@vaadin/hilla-react-i18n 24.4.0-alpha14 → 24.4.0-alpha16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ import { IntlMessageFormat } from 'intl-messageformat';
2
+ export declare class FormatCache {
3
+ #private;
4
+ constructor(language: string);
5
+ getFormat(translation: string): IntlMessageFormat;
6
+ }
7
+ //# sourceMappingURL=FormatCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormatCache.d.ts","sourceRoot":"","sources":["src/FormatCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,qBAAa,WAAW;;gBAIV,QAAQ,EAAE,MAAM;IAU5B,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,iBAAiB;CAQlD"}
package/FormatCache.js ADDED
@@ -0,0 +1,25 @@
1
+ import { IntlMessageFormat } from "intl-messageformat";
2
+ class FormatCache {
3
+ #language;
4
+ #formats = /* @__PURE__ */ new Map();
5
+ constructor(language) {
6
+ let supportedLocales = [];
7
+ try {
8
+ supportedLocales = Intl.NumberFormat.supportedLocalesOf(language);
9
+ } catch (e) {
10
+ }
11
+ this.#language = supportedLocales.length > 0 ? supportedLocales[0] : navigator.language;
12
+ }
13
+ getFormat(translation) {
14
+ let format = this.#formats.get(translation);
15
+ if (!format) {
16
+ format = new IntlMessageFormat(translation, this.#language);
17
+ this.#formats.set(translation, format);
18
+ }
19
+ return format;
20
+ }
21
+ }
22
+ export {
23
+ FormatCache
24
+ };
25
+ //# sourceMappingURL=FormatCache.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["src/FormatCache.ts"],
4
+ "sourcesContent": ["import { IntlMessageFormat } from 'intl-messageformat';\n\nexport class FormatCache {\n readonly #language: string;\n readonly #formats = new Map<string, IntlMessageFormat>();\n\n constructor(language: string) {\n // Ensure that the language is supported by Intl.NumberFormat, which IntlMessageFormat uses internally\n // Fall back to navigator.language if the given language is not supported\n let supportedLocales: string[] = [];\n try {\n supportedLocales = Intl.NumberFormat.supportedLocalesOf(language);\n } catch (e) {}\n this.#language = supportedLocales.length > 0 ? supportedLocales[0] : navigator.language;\n }\n\n getFormat(translation: string): IntlMessageFormat {\n let format = this.#formats.get(translation);\n if (!format) {\n format = new IntlMessageFormat(translation, this.#language);\n this.#formats.set(translation, format);\n }\n return format;\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,yBAAyB;AAE3B,MAAM,YAAY;AAAA,EACd;AAAA,EACA,WAAW,oBAAI,IAA+B;AAAA,EAEvD,YAAY,UAAkB;AAG5B,QAAI,mBAA6B,CAAC;AAClC,QAAI;AACF,yBAAmB,KAAK,aAAa,mBAAmB,QAAQ;AAAA,IAClE,SAAS,GAAG;AAAA,IAAC;AACb,SAAK,YAAY,iBAAiB,SAAS,IAAI,iBAAiB,CAAC,IAAI,UAAU;AAAA,EACjF;AAAA,EAEA,UAAU,aAAwC;AAChD,QAAI,SAAS,KAAK,SAAS,IAAI,WAAW;AAC1C,QAAI,CAAC,QAAQ;AACX,eAAS,IAAI,kBAAkB,aAAa,KAAK,SAAS;AAC1D,WAAK,SAAS,IAAI,aAAa,MAAM;AAAA,IACvC;AACA,WAAO;AAAA,EACT;AACF;",
6
+ "names": []
7
+ }
package/index.d.ts CHANGED
@@ -1,14 +1,16 @@
1
- import { type Signal } from '@vaadin/hilla-react-signals';
1
+ import { type ReadonlySignal } from '@vaadin/hilla-react-signals';
2
2
  import type { I18nOptions } from './types.js';
3
3
  export declare class I18n {
4
4
  #private;
5
- get language(): Signal<string | undefined>;
6
- get resolvedLanguage(): Signal<string | undefined>;
5
+ constructor();
6
+ get initialized(): ReadonlySignal<boolean>;
7
+ get language(): ReadonlySignal<string | undefined>;
8
+ get resolvedLanguage(): ReadonlySignal<string | undefined>;
7
9
  configure(options?: I18nOptions): Promise<void>;
8
10
  setLanguage(newLanguage: string): Promise<void>;
9
11
  private updateLanguage;
10
- translate(key: string): string;
12
+ translate(key: string, params?: Record<string, unknown>): string;
11
13
  }
12
14
  export declare const i18n: I18n;
13
- export declare function translate(key: string): string;
15
+ export declare function translate(key: string, params?: Record<string, unknown>): string;
14
16
  //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,6BAA6B,CAAC;AAGzE,OAAO,KAAK,EAAE,WAAW,EAAoC,MAAM,YAAY,CAAC;AAgBhF,qBAAa,IAAI;;IAOf,IAAI,QAAQ,IAAI,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC,CAEzC;IAED,IAAI,gBAAgB,IAAI,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC,CAEjD;IAEK,SAAS,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/C,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAIvC,cAAc;IA2B5B,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;CAG/B;AAED,eAAO,MAAM,IAAI,MAAa,CAAC;AAE/B,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE7C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,cAAc,EAAuB,MAAM,6BAA6B,CAAC;AAI9F,OAAO,KAAK,EAAE,WAAW,EAAoC,MAAM,YAAY,CAAC;AAgBhF,qBAAa,IAAI;;;IAmBf,IAAI,WAAW,IAAI,cAAc,CAAC,OAAO,CAAC,CAEzC;IAED,IAAI,QAAQ,IAAI,cAAc,CAAC,MAAM,GAAG,SAAS,CAAC,CAEjD;IAED,IAAI,gBAAgB,IAAI,cAAc,CAAC,MAAM,GAAG,SAAS,CAAC,CAEzD;IAEK,SAAS,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/C,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAIvC,cAAc;IA6B5B,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM;CAQjE;AAED,eAAO,MAAM,IAAI,MAAa,CAAC;AAE/B,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAE/E"}
package/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { batch, signal } from "@vaadin/hilla-react-signals";
2
2
  import { DefaultBackend } from "./backend.js";
3
+ import { FormatCache } from "./FormatCache.js";
3
4
  import { getLanguageSettings, updateLanguageSettings } from "./settings.js";
4
5
  function determineInitialLanguage(options) {
5
6
  if (options?.language) {
@@ -13,9 +14,21 @@ function determineInitialLanguage(options) {
13
14
  }
14
15
  class I18n {
15
16
  #backend = new DefaultBackend();
17
+ #initialized = signal(false);
16
18
  #language = signal(void 0);
17
19
  #translations = signal({});
18
20
  #resolvedLanguage = signal(void 0);
21
+ #formatCache = new FormatCache(navigator.language);
22
+ constructor() {
23
+ if (!window.Vaadin?.featureFlags?.hillaI18n) {
24
+ throw new Error(
25
+ `The Hilla I18n API is currently considered experimental and may change in the future. To use it you need to explicitly enable it in Copilot or by adding com.vaadin.experimental.hillaI18n=true to vaadin-featureflags.properties`
26
+ );
27
+ }
28
+ }
29
+ get initialized() {
30
+ return this.#initialized;
31
+ }
19
32
  get language() {
20
33
  return this.#language;
21
34
  }
@@ -44,6 +57,8 @@ class I18n {
44
57
  this.#translations.value = translationsResult.translations;
45
58
  this.#language.value = newLanguage;
46
59
  this.#resolvedLanguage.value = translationsResult.resolvedLanguage;
60
+ this.#formatCache = new FormatCache(newLanguage);
61
+ this.#initialized.value = true;
47
62
  if (updateSettings) {
48
63
  updateLanguageSettings({
49
64
  language: newLanguage
@@ -51,13 +66,18 @@ class I18n {
51
66
  }
52
67
  });
53
68
  }
54
- translate(key) {
55
- return this.#translations.value[key] || key;
69
+ translate(key, params) {
70
+ const translation = this.#translations.value[key];
71
+ if (!translation) {
72
+ return key;
73
+ }
74
+ const format = this.#formatCache.getFormat(translation);
75
+ return format.format(params);
56
76
  }
57
77
  }
58
78
  const i18n = new I18n();
59
- function translate(key) {
60
- return i18n.translate(key);
79
+ function translate(key, params) {
80
+ return i18n.translate(key, params);
61
81
  }
62
82
  export {
63
83
  I18n,
package/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["src/index.ts"],
4
- "sourcesContent": ["import { batch, signal, type Signal } from '@vaadin/hilla-react-signals';\nimport { DefaultBackend, type I18nBackend } from './backend.js';\nimport { getLanguageSettings, updateLanguageSettings } from './settings.js';\nimport type { I18nOptions, Translations, TranslationsResult } from './types.js';\n\nfunction determineInitialLanguage(options?: I18nOptions): string {\n // Use explicitly configured language if defined\n if (options?.language) {\n return options.language;\n }\n // Use last used language as fallback\n const settings = getLanguageSettings();\n if (settings?.language) {\n return settings.language;\n }\n // Otherwise use browser language\n return navigator.language;\n}\n\nexport class I18n {\n readonly #backend: I18nBackend = new DefaultBackend();\n\n readonly #language: Signal<string | undefined> = signal(undefined);\n readonly #translations: Signal<Translations> = signal({});\n readonly #resolvedLanguage: Signal<string | undefined> = signal(undefined);\n\n get language(): Signal<string | undefined> {\n return this.#language;\n }\n\n get resolvedLanguage(): Signal<string | undefined> {\n return this.#resolvedLanguage;\n }\n\n async configure(options?: I18nOptions): Promise<void> {\n const initialLanguage = determineInitialLanguage(options);\n await this.updateLanguage(initialLanguage);\n }\n\n async setLanguage(newLanguage: string): Promise<void> {\n await this.updateLanguage(newLanguage, true);\n }\n\n private async updateLanguage(newLanguage: string, updateSettings = false) {\n if (this.#language.value === newLanguage) {\n return;\n }\n\n let translationsResult: TranslationsResult;\n try {\n translationsResult = await this.#backend.loadTranslations(newLanguage);\n } catch (e) {\n console.error(`Failed to load translations for language: ${newLanguage}`, e);\n return;\n }\n\n // Update all signals together to avoid triggering side effects multiple times\n batch(() => {\n this.#translations.value = translationsResult.translations;\n this.#language.value = newLanguage;\n this.#resolvedLanguage.value = translationsResult.resolvedLanguage;\n\n if (updateSettings) {\n updateLanguageSettings({\n language: newLanguage,\n });\n }\n });\n }\n\n translate(key: string): string {\n return this.#translations.value[key] || key;\n }\n}\n\nexport const i18n = new I18n();\n\nexport function translate(key: string): string {\n return i18n.translate(key);\n}\n"],
5
- "mappings": "AAAA,SAAS,OAAO,cAA2B;AAC3C,SAAS,sBAAwC;AACjD,SAAS,qBAAqB,8BAA8B;AAG5D,SAAS,yBAAyB,SAA+B;AAE/D,MAAI,SAAS,UAAU;AACrB,WAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,WAAW,oBAAoB;AACrC,MAAI,UAAU,UAAU;AACtB,WAAO,SAAS;AAAA,EAClB;AAEA,SAAO,UAAU;AACnB;AAEO,MAAM,KAAK;AAAA,EACP,WAAwB,IAAI,eAAe;AAAA,EAE3C,YAAwC,OAAO,MAAS;AAAA,EACxD,gBAAsC,OAAO,CAAC,CAAC;AAAA,EAC/C,oBAAgD,OAAO,MAAS;AAAA,EAEzE,IAAI,WAAuC;AACzC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,mBAA+C;AACjD,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,UAAU,SAAsC;AACpD,UAAM,kBAAkB,yBAAyB,OAAO;AACxD,UAAM,KAAK,eAAe,eAAe;AAAA,EAC3C;AAAA,EAEA,MAAM,YAAY,aAAoC;AACpD,UAAM,KAAK,eAAe,aAAa,IAAI;AAAA,EAC7C;AAAA,EAEA,MAAc,eAAe,aAAqB,iBAAiB,OAAO;AACxE,QAAI,KAAK,UAAU,UAAU,aAAa;AACxC;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,2BAAqB,MAAM,KAAK,SAAS,iBAAiB,WAAW;AAAA,IACvE,SAAS,GAAG;AACV,cAAQ,MAAM,6CAA6C,WAAW,IAAI,CAAC;AAC3E;AAAA,IACF;AAGA,UAAM,MAAM;AACV,WAAK,cAAc,QAAQ,mBAAmB;AAC9C,WAAK,UAAU,QAAQ;AACvB,WAAK,kBAAkB,QAAQ,mBAAmB;AAElD,UAAI,gBAAgB;AAClB,+BAAuB;AAAA,UACrB,UAAU;AAAA,QACZ,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,UAAU,KAAqB;AAC7B,WAAO,KAAK,cAAc,MAAM,GAAG,KAAK;AAAA,EAC1C;AACF;AAEO,MAAM,OAAO,IAAI,KAAK;AAEtB,SAAS,UAAU,KAAqB;AAC7C,SAAO,KAAK,UAAU,GAAG;AAC3B;",
4
+ "sourcesContent": ["import { batch, type ReadonlySignal, signal, type Signal } from '@vaadin/hilla-react-signals';\nimport { DefaultBackend, type I18nBackend } from './backend.js';\nimport { FormatCache } from './FormatCache.js';\nimport { getLanguageSettings, updateLanguageSettings } from './settings.js';\nimport type { I18nOptions, Translations, TranslationsResult } from './types.js';\n\nfunction determineInitialLanguage(options?: I18nOptions): string {\n // Use explicitly configured language if defined\n if (options?.language) {\n return options.language;\n }\n // Use last used language as fallback\n const settings = getLanguageSettings();\n if (settings?.language) {\n return settings.language;\n }\n // Otherwise use browser language\n return navigator.language;\n}\n\nexport class I18n {\n readonly #backend: I18nBackend = new DefaultBackend();\n\n readonly #initialized: Signal<boolean> = signal(false);\n readonly #language: Signal<string | undefined> = signal(undefined);\n readonly #translations: Signal<Translations> = signal({});\n readonly #resolvedLanguage: Signal<string | undefined> = signal(undefined);\n #formatCache: FormatCache = new FormatCache(navigator.language);\n\n constructor() {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n if (!(window as any).Vaadin?.featureFlags?.hillaI18n) {\n // Remove when removing feature flag\n throw new Error(\n `The Hilla I18n API is currently considered experimental and may change in the future. To use it you need to explicitly enable it in Copilot or by adding com.vaadin.experimental.hillaI18n=true to vaadin-featureflags.properties`,\n );\n }\n }\n\n get initialized(): ReadonlySignal<boolean> {\n return this.#initialized;\n }\n\n get language(): ReadonlySignal<string | undefined> {\n return this.#language;\n }\n\n get resolvedLanguage(): ReadonlySignal<string | undefined> {\n return this.#resolvedLanguage;\n }\n\n async configure(options?: I18nOptions): Promise<void> {\n const initialLanguage = determineInitialLanguage(options);\n await this.updateLanguage(initialLanguage);\n }\n\n async setLanguage(newLanguage: string): Promise<void> {\n await this.updateLanguage(newLanguage, true);\n }\n\n private async updateLanguage(newLanguage: string, updateSettings = false) {\n if (this.#language.value === newLanguage) {\n return;\n }\n\n let translationsResult: TranslationsResult;\n try {\n translationsResult = await this.#backend.loadTranslations(newLanguage);\n } catch (e) {\n console.error(`Failed to load translations for language: ${newLanguage}`, e);\n return;\n }\n\n // Update all signals together to avoid triggering side effects multiple times\n batch(() => {\n this.#translations.value = translationsResult.translations;\n this.#language.value = newLanguage;\n this.#resolvedLanguage.value = translationsResult.resolvedLanguage;\n this.#formatCache = new FormatCache(newLanguage);\n this.#initialized.value = true;\n\n if (updateSettings) {\n updateLanguageSettings({\n language: newLanguage,\n });\n }\n });\n }\n\n translate(key: string, params?: Record<string, unknown>): string {\n const translation = this.#translations.value[key];\n if (!translation) {\n return key;\n }\n const format = this.#formatCache.getFormat(translation);\n return format.format(params) as string;\n }\n}\n\nexport const i18n = new I18n();\n\nexport function translate(key: string, params?: Record<string, unknown>): string {\n return i18n.translate(key, params);\n}\n"],
5
+ "mappings": "AAAA,SAAS,OAA4B,cAA2B;AAChE,SAAS,sBAAwC;AACjD,SAAS,mBAAmB;AAC5B,SAAS,qBAAqB,8BAA8B;AAG5D,SAAS,yBAAyB,SAA+B;AAE/D,MAAI,SAAS,UAAU;AACrB,WAAO,QAAQ;AAAA,EACjB;AAEA,QAAM,WAAW,oBAAoB;AACrC,MAAI,UAAU,UAAU;AACtB,WAAO,SAAS;AAAA,EAClB;AAEA,SAAO,UAAU;AACnB;AAEO,MAAM,KAAK;AAAA,EACP,WAAwB,IAAI,eAAe;AAAA,EAE3C,eAAgC,OAAO,KAAK;AAAA,EAC5C,YAAwC,OAAO,MAAS;AAAA,EACxD,gBAAsC,OAAO,CAAC,CAAC;AAAA,EAC/C,oBAAgD,OAAO,MAAS;AAAA,EACzE,eAA4B,IAAI,YAAY,UAAU,QAAQ;AAAA,EAE9D,cAAc;AAEZ,QAAI,CAAE,OAAe,QAAQ,cAAc,WAAW;AAEpD,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,IAAI,cAAuC;AACzC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,WAA+C;AACjD,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,mBAAuD;AACzD,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,UAAU,SAAsC;AACpD,UAAM,kBAAkB,yBAAyB,OAAO;AACxD,UAAM,KAAK,eAAe,eAAe;AAAA,EAC3C;AAAA,EAEA,MAAM,YAAY,aAAoC;AACpD,UAAM,KAAK,eAAe,aAAa,IAAI;AAAA,EAC7C;AAAA,EAEA,MAAc,eAAe,aAAqB,iBAAiB,OAAO;AACxE,QAAI,KAAK,UAAU,UAAU,aAAa;AACxC;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,2BAAqB,MAAM,KAAK,SAAS,iBAAiB,WAAW;AAAA,IACvE,SAAS,GAAG;AACV,cAAQ,MAAM,6CAA6C,WAAW,IAAI,CAAC;AAC3E;AAAA,IACF;AAGA,UAAM,MAAM;AACV,WAAK,cAAc,QAAQ,mBAAmB;AAC9C,WAAK,UAAU,QAAQ;AACvB,WAAK,kBAAkB,QAAQ,mBAAmB;AAClD,WAAK,eAAe,IAAI,YAAY,WAAW;AAC/C,WAAK,aAAa,QAAQ;AAE1B,UAAI,gBAAgB;AAClB,+BAAuB;AAAA,UACrB,UAAU;AAAA,QACZ,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,UAAU,KAAa,QAA0C;AAC/D,UAAM,cAAc,KAAK,cAAc,MAAM,GAAG;AAChD,QAAI,CAAC,aAAa;AAChB,aAAO;AAAA,IACT;AACA,UAAM,SAAS,KAAK,aAAa,UAAU,WAAW;AACtD,WAAO,OAAO,OAAO,MAAM;AAAA,EAC7B;AACF;AAEO,MAAM,OAAO,IAAI,KAAK;AAEtB,SAAS,UAAU,KAAa,QAA0C;AAC/E,SAAO,KAAK,UAAU,KAAK,MAAM;AACnC;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/hilla-react-i18n",
3
- "version": "24.4.0-alpha14",
3
+ "version": "24.4.0-alpha16",
4
4
  "description": "Hilla I18n utils for React",
5
5
  "main": "index.js",
6
6
  "module": "index.js",
@@ -46,8 +46,9 @@
46
46
  "access": "public"
47
47
  },
48
48
  "dependencies": {
49
- "@vaadin/hilla-frontend": "24.4.0-alpha14",
50
- "@vaadin/hilla-react-signals": "24.4.0-alpha14"
49
+ "@vaadin/hilla-frontend": "24.4.0-alpha16",
50
+ "@vaadin/hilla-react-signals": "24.4.0-alpha16",
51
+ "intl-messageformat": "^10.5.11"
51
52
  },
52
53
  "peerDependencies": {
53
54
  "react": "^18"