From d92ba2b9c1f1ae9d61186a9bb80b961a3c422b79 Mon Sep 17 00:00:00 2001 From: deylak Date: Tue, 22 Jul 2025 03:25:28 +0300 Subject: [PATCH] feat(core): Add global formats options for setupI18n --- packages/core/src/i18n.test.ts | 71 ++++++++++++++++++++++++++++++++++ packages/core/src/i18n.ts | 9 ++++- website/docs/ref/core.md | 19 +++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/packages/core/src/i18n.test.ts b/packages/core/src/i18n.test.ts index b107f99c7..b145a9e40 100644 --- a/packages/core/src/i18n.test.ts +++ b/packages/core/src/i18n.test.ts @@ -476,6 +476,77 @@ describe("I18n", () => { `) }) + describe("global custom formats", () => { + const i18n = setupI18n({ + locale: "fr", + formats: { + myDateStyle: { + day: "numeric", + }, + myNumberStyle: { + maximumFractionDigits: 2, + }, + }, + messages: { fr: {} }, + }) + + const date = new Date("2014-12-06") + + const number = 123.4567 + + it("._ should respect global custom date formats", () => { + expect( + i18n._("It starts on {someDate, date, myDateStyle}", { + someDate: date, + }) + ).toMatchInlineSnapshot(`"It starts on 6"`) + }) + + it("._ should respect global custom number formats", () => { + expect( + i18n._("Number is {someNumber, number, myNumberStyle}", { + someNumber: number, + }) + ).toMatchInlineSnapshot(`"Number is 123,46"`) + }) + + it("._ should override global custom date formats with local ones", () => { + expect( + i18n._( + "It starts on {someDate, date, myDateStyle}", + { + someDate: date, + }, + { + formats: { + myDateStyle: { + day: "2-digit", + }, + }, + } + ) + ).toMatchInlineSnapshot(`"It starts on 06"`) + }) + + it("._ should override global custom number formats with local ones", () => { + expect( + i18n._( + "Number is {someNumber, number, myNumberStyle}", + { + someNumber: number, + }, + { + formats: { + myNumberStyle: { + maximumFractionDigits: 3, + }, + }, + } + ) + ).toMatchInlineSnapshot(`"Number is 123,457"`) + }) + }) + describe("ICU date format", () => { const i18n = setupI18n({ locale: "fr", diff --git a/packages/core/src/i18n.ts b/packages/core/src/i18n.ts index bbb716086..7e0dfcd2c 100644 --- a/packages/core/src/i18n.ts +++ b/packages/core/src/i18n.ts @@ -65,6 +65,11 @@ export type I18nProps = { */ localeData?: AllLocaleData missing?: MissingHandler + /** + * Global custom formats to be used in all `i18n._` calls. + * Additional passed formats in the `i18n._` call will be merged with the global ones. + */ + formats?: Formats } type Events = { @@ -89,6 +94,7 @@ export class I18n extends EventEmitter { private _localeData: AllLocaleData = {} private _messages: AllMessages = {} private _missing?: MissingHandler + private _formats: Formats = {} private _messageCompiler?: MessageCompiler constructor(params: I18nProps) { @@ -104,6 +110,7 @@ export class I18n extends EventEmitter { if (typeof params.locale === "string" || params.locales) { this.activate(params.locale ?? defaultLocale, params.locales) } + if (params.formats != null) this._formats = params.formats } get locale() { @@ -304,7 +311,7 @@ Please compile your catalog first. translation, this._locale, this._locales - )(values, options?.formats) + )(values, { ...this._formats, ...options?.formats }) } /** diff --git a/website/docs/ref/core.md b/website/docs/ref/core.md index fc18380f2..74a3d1a11 100644 --- a/website/docs/ref/core.md +++ b/website/docs/ref/core.md @@ -364,6 +364,25 @@ const i18n = setupI18n({ missing }); i18n._("missing translation"); // Triggers an alert ``` +### `options.formats` + +Custom format definitions for number, dates and times. An object where keys are format names and values are either `Intl.DateTimeFormatOptions` or `Intl.NumberFormatOptions`. + +```tsx +import { setupI18n } from "@lingui/core"; + +const i18n = setupI18n({ + formats: { + myDateStyle: { + day: "numeric", + }, + } +}); +i18n._("It starts on {someDate, date, myDateStyle}", { + someDate: new Date("2014-12-06"), +}) === "It starts on 6"; +``` + ## AllMessages The `AllMessages` parameter in the [`I18n.load`](#i18n.load) method is of the following type: