@vaadin/hilla-react-i18n 24.7.0-alpha9 → 24.7.0-beta3

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.
package/FormatCache.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { IntlMessageFormat } from 'intl-messageformat';
1
+ import { IntlMessageFormat } from "intl-messageformat";
2
2
  export declare class FormatCache {
3
- #private;
4
- constructor(language: string);
5
- getFormat(translation: string): IntlMessageFormat;
3
+ #private;
4
+ constructor(language: string);
5
+ getFormat(translation: string): IntlMessageFormat;
6
6
  }
7
- //# sourceMappingURL=FormatCache.d.ts.map
package/FormatCache.js CHANGED
@@ -1,25 +1,21 @@
1
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
- }
2
+ export class FormatCache {
3
+ #language;
4
+ #formats = new Map();
5
+ constructor(language) {
6
+ let supportedLocales = [];
7
+ try {
8
+ supportedLocales = Intl.NumberFormat.supportedLocalesOf(language);
9
+ } catch {}
10
+ this.#language = supportedLocales.length > 0 ? supportedLocales[0] : navigator.language;
11
+ }
12
+ getFormat(translation) {
13
+ let format = this.#formats.get(translation);
14
+ if (!format) {
15
+ format = new IntlMessageFormat(translation, this.#language);
16
+ this.#formats.set(translation, format);
17
+ }
18
+ return format;
19
+ }
21
20
  }
22
- export {
23
- FormatCache
24
- };
25
- //# sourceMappingURL=FormatCache.js.map
21
+ //# sourceMappingURL=./FormatCache.js.map
@@ -1,7 +1 @@
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
- }
1
+ {"mappings":"AAAA,SAAS,6CAA8C;AAEvD,OAAO,MAAM,YAAY;CACvB,AAASA;CACT,AAASC,WAAW,IAAI;CAExB,YAAYC,UAAkB;EAG5B,IAAIC,mBAA6B,CAAE;AACnC,MAAI;AACF,sBAAmB,KAAK,aAAa,mBAAmB,SAAS;EAClE,QAAO,CAAE;AACV,OAAKH,YAAY,iBAAiB,SAAS,IAAI,iBAAiB,KAAK,UAAU;CAChF;CAED,UAAUI,aAAwC;EAChD,IAAI,SAAS,KAAKH,SAAS,IAAI,YAAY;AAC3C,OAAK,QAAQ;AACX,YAAS,IAAI,kBAAkB,aAAa,KAAKD;AACjD,QAAKC,SAAS,IAAI,aAAa,OAAO;EACvC;AACD,SAAO;CACR;AACF","names":["#language","#formats","language: string","supportedLocales: string[]","translation: string"],"sources":["/opt/agent/work/1af72d8adc613024/hilla/packages/ts/react-i18n/src/FormatCache.ts"],"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 {}\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"],"version":3}
package/backend.d.ts CHANGED
@@ -1,8 +1,7 @@
1
- import type { TranslationsResult } from './types.js';
1
+ import type { TranslationsResult } from "./types.js";
2
2
  export interface I18nBackend {
3
- loadTranslations(language: string, chunks?: readonly string[]): Promise<TranslationsResult>;
3
+ loadTranslations(language: string, chunks?: readonly string[]): Promise<TranslationsResult>;
4
4
  }
5
5
  export declare class DefaultBackend implements I18nBackend {
6
- loadTranslations(language: string, chunks?: readonly string[]): Promise<TranslationsResult>;
6
+ loadTranslations(language: string, chunks?: readonly string[]): Promise<TranslationsResult>;
7
7
  }
8
- //# sourceMappingURL=backend.d.ts.map
package/backend.js CHANGED
@@ -1,23 +1,20 @@
1
- class DefaultBackend {
2
- async loadTranslations(language, chunks) {
3
- const params = new URLSearchParams([
4
- ["v-r", "i18n"],
5
- ["langtag", language],
6
- ...(chunks ?? []).map((chunk) => ["chunks", chunk])
7
- ]);
8
- const response = await fetch(`./?${params.toString()}`);
9
- if (!response.ok) {
10
- throw new Error("Failed fetching translations.");
11
- }
12
- const retrievedLocale = response.headers.get("X-Vaadin-Retrieved-Locale");
13
- const translations = await response.json();
14
- return {
15
- translations,
16
- resolvedLanguage: retrievedLocale ?? void 0
17
- };
18
- }
1
+ export class DefaultBackend {
2
+ async loadTranslations(language, chunks) {
3
+ const params = new URLSearchParams([
4
+ ["v-r", "i18n"],
5
+ ["langtag", language],
6
+ ...(chunks ?? []).map((chunk) => ["chunks", chunk])
7
+ ]);
8
+ const response = await fetch(`./?${params.toString()}`);
9
+ if (!response.ok) {
10
+ throw new Error("Failed fetching translations.");
11
+ }
12
+ const retrievedLocale = response.headers.get("X-Vaadin-Retrieved-Locale");
13
+ const translations = await response.json();
14
+ return {
15
+ translations,
16
+ resolvedLanguage: retrievedLocale ?? undefined
17
+ };
18
+ }
19
19
  }
20
- export {
21
- DefaultBackend
22
- };
23
- //# sourceMappingURL=backend.js.map
20
+ //# sourceMappingURL=./backend.js.map
package/backend.js.map CHANGED
@@ -1,7 +1 @@
1
- {
2
- "version": 3,
3
- "sources": ["src/backend.ts"],
4
- "sourcesContent": ["import type { Translations, TranslationsResult } from './types.js';\n\nexport interface I18nBackend {\n loadTranslations(language: string, chunks?: readonly string[]): Promise<TranslationsResult>;\n}\n\nexport class DefaultBackend implements I18nBackend {\n async loadTranslations(language: string, chunks?: readonly string[]): Promise<TranslationsResult> {\n const params = new URLSearchParams([\n ['v-r', 'i18n'],\n ['langtag', language],\n ...(chunks ?? []).map((chunk) => ['chunks', chunk]),\n ]);\n const response = await fetch(`./?${params.toString()}`);\n if (!response.ok) {\n throw new Error('Failed fetching translations.');\n }\n const retrievedLocale = response.headers.get('X-Vaadin-Retrieved-Locale');\n const translations: Translations = await response.json();\n return {\n translations,\n resolvedLanguage: retrievedLocale ?? undefined,\n };\n }\n}\n"],
5
- "mappings": "AAMO,MAAM,eAAsC;AAAA,EACjD,MAAM,iBAAiB,UAAkB,QAAyD;AAChG,UAAM,SAAS,IAAI,gBAAgB;AAAA,MACjC,CAAC,OAAO,MAAM;AAAA,MACd,CAAC,WAAW,QAAQ;AAAA,MACpB,IAAI,UAAU,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,KAAK,CAAC;AAAA,IACpD,CAAC;AACD,UAAM,WAAW,MAAM,MAAM,MAAM,OAAO,SAAS,CAAC,EAAE;AACtD,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,+BAA+B;AAAA,IACjD;AACA,UAAM,kBAAkB,SAAS,QAAQ,IAAI,2BAA2B;AACxE,UAAM,eAA6B,MAAM,SAAS,KAAK;AACvD,WAAO;AAAA,MACL;AAAA,MACA,kBAAkB,mBAAmB;AAAA,IACvC;AAAA,EACF;AACF;",
6
- "names": []
7
- }
1
+ {"mappings":"AAMA,OAAO,MAAM,eAAsC;CACjD,MAAM,iBAAiBA,UAAkBC,QAAyD;EAChG,MAAM,SAAS,IAAI,gBAAgB;GACjC,CAAC,OAAO,MAAO;GACf,CAAC,WAAW,QAAS;GACrB,GAAG,CAAC,UAAU,CAAE,GAAE,IAAI,CAAC,UAAU,CAAC,UAAU,KAAM,EAAC;EACpD;EACD,MAAM,WAAW,MAAM,OAAO,KAAK,OAAO,UAAU,CAAC,EAAE;AACvD,OAAK,SAAS,IAAI;AAChB,SAAM,IAAI,MAAM;EACjB;EACD,MAAM,kBAAkB,SAAS,QAAQ,IAAI,4BAA4B;EACzE,MAAMC,eAA6B,MAAM,SAAS,MAAM;AACxD,SAAO;GACL;GACA,kBAAkB,mBAAmB;EACtC;CACF;AACF","names":["language: string","chunks?: readonly string[]","translations: Translations"],"sources":["/opt/agent/work/1af72d8adc613024/hilla/packages/ts/react-i18n/src/backend.ts"],"sourcesContent":["import type { Translations, TranslationsResult } from './types.js';\n\nexport interface I18nBackend {\n loadTranslations(language: string, chunks?: readonly string[]): Promise<TranslationsResult>;\n}\n\nexport class DefaultBackend implements I18nBackend {\n async loadTranslations(language: string, chunks?: readonly string[]): Promise<TranslationsResult> {\n const params = new URLSearchParams([\n ['v-r', 'i18n'],\n ['langtag', language],\n ...(chunks ?? []).map((chunk) => ['chunks', chunk]),\n ]);\n const response = await fetch(`./?${params.toString()}`);\n if (!response.ok) {\n throw new Error('Failed fetching translations.');\n }\n const retrievedLocale = response.headers.get('X-Vaadin-Retrieved-Locale');\n const translations: Translations = await response.json();\n return {\n translations,\n resolvedLanguage: retrievedLocale ?? undefined,\n };\n }\n}\n"],"version":3}
package/index.d.ts CHANGED
@@ -1,144 +1,134 @@
1
- import { type ReadonlySignal } from '@vaadin/hilla-react-signals';
2
- import type { I18nOptions } from './types.js';
1
+ import { type ReadonlySignal } from "@vaadin/hilla-react-signals";
2
+ import type { I18nOptions } from "./types.js";
3
3
  export declare class I18n {
4
- #private;
5
- constructor();
6
- /**
7
- * Returns a signal indicating whether the I18n instance has been initialized.
8
- * The instance is considered initialized after `configure` has been called
9
- * and translations for the initial language have been loaded. Can be used to
10
- * show a placeholder or loading indicator until the translations are ready.
11
- *
12
- * Subscribers to this signal will be notified when initialization is complete
13
- * and translations are ready to be used.
14
- */
15
- get initialized(): ReadonlySignal<boolean>;
16
- /**
17
- * Returns a signal with the currently configured language.
18
- *
19
- * Subscribers to this signal will be notified when the language has changed
20
- * and new translations have been loaded.
21
- */
22
- get language(): ReadonlySignal<string | undefined>;
23
- /**
24
- * Returns a signal with the resolved language. The resolved language is the
25
- * language that was actually used to load translations. It may differ from
26
- * the configured language if there are no translations available for the
27
- * configured language. For example, when setting the language to "de-DE" but
28
- * translations are only available for "de", the resolved language will be
29
- * "de".
30
- */
31
- get resolvedLanguage(): ReadonlySignal<string | undefined>;
32
- /**
33
- * Initializes the I18n instance. This method should be called once to load
34
- * translations for the initial language. The `translate` API will not return
35
- * properly translated strings until the initializations has completed.
36
- *
37
- * The initialization runs asynchronously as translations are loaded from the
38
- * backend. The method returns a promise that resolves when the translations
39
- * have been loaded, after which the `translate` API can safely be used.
40
- *
41
- * The initial language is determined as follows:
42
- * 1. If a user opens the app for the first time, the browser language is used.
43
- * 2. If the language has been changed in a previous usage of the app using
44
- * `setLanguage`, the last used language is used. The last used language is
45
- * automatically stored in local storage.
46
- *
47
- * Alternatively, the initial language can be explicitly configured using the
48
- * `language` option. The language should be a valid IETF BCP 47 language tag,
49
- * such as `en` or `en-US`.
50
- *
51
- * @param options - Optional options object to specify the initial language.
52
- */
53
- configure(options?: I18nOptions): Promise<void>;
54
- /**
55
- * Changes the current language and loads translations for the new language.
56
- * Components using the `translate` API will automatically re-render, and
57
- * subscribers to the `language` signal will be notified, when the new
58
- * translations have been loaded.
59
- *
60
- * The language should be a valid IETF BCP 47 language tag, such as `en` or
61
- * `en-US`.
62
- *
63
- * If there is no translation file for that specific language tag, the backend
64
- * will try to load the translation file for the parent language tag. For
65
- * example, if there is no translation file for `en-US`, the backend will try
66
- * to load the translation file for `en`. Otherwise, it will fall back to the
67
- * default translation file `translations.properties`.
68
- *
69
- * Changing the language is an asynchronous operation. The method returns a
70
- * promise that resolves when the translations for the new language have been
71
- * loaded.
72
- *
73
- * @param newLanguage - a valid IETF BCP 47 language tag, such as `en` or `en-US`
74
- */
75
- setLanguage(newLanguage: string): Promise<void>;
76
- /**
77
- * Registers the chunk name for loading translations, and loads the
78
- * translations for the specified chunk.
79
- *
80
- * @internal only for automatic internal calls from production JS bundles
81
- *
82
- * @param chunkName - the production JS bundle chunk name
83
- */
84
- registerChunk(chunkName: string): Promise<void>;
85
- private updateLanguage;
86
- /**
87
- * Reloads all translations for the current language. This method should only
88
- * be used for HMR in development mode.
89
- */
90
- private reloadTranslations;
91
- /**
92
- * Returns a translated string for the given translation key. The key should
93
- * match a key in the loaded translations. If no translation is found for the
94
- * key, the key itself is returned.
95
- *
96
- * Translations may contain placeholders, following the ICU MessageFormat
97
- * syntax. They can be replaced by passing a `params` object with placeholder
98
- * values, where keys correspond to the placeholder names and values are the
99
- * replacement value. Values should be basic types such as strings, numbers,
100
- * or dates that match the placeholder format configured in the translation
101
- * string. For example, when using a placeholder `{count, number}`, the value
102
- * should be a number, when using `{date, date}`, the value should be a Date
103
- * object, and so on.
104
- *
105
- * This method internally accesses a signal, meaning that React components
106
- * that use it will automatically re-render when translations change.
107
- * Likewise, signal effects automatically subscribe to translation changes
108
- * when calling this method.
109
- *
110
- * @param key - The translation key to translate
111
- * @param params - Optional object with placeholder values
112
- */
113
- translate(key: string, params?: Record<string, unknown>): string;
4
+ #private;
5
+ constructor();
6
+ /**
7
+ * Returns a signal indicating whether the I18n instance has been initialized.
8
+ * The instance is considered initialized after `configure` has been called
9
+ * and translations for the initial language have been loaded. Can be used to
10
+ * show a placeholder or loading indicator until the translations are ready.
11
+ *
12
+ * Subscribers to this signal will be notified when initialization is complete
13
+ * and translations are ready to be used.
14
+ */
15
+ get initialized(): ReadonlySignal<boolean>;
16
+ /**
17
+ * Returns a signal with the currently configured language.
18
+ *
19
+ * Subscribers to this signal will be notified when the language has changed
20
+ * and new translations have been loaded.
21
+ */
22
+ get language(): ReadonlySignal<string | undefined>;
23
+ /**
24
+ * Returns a signal with the resolved language. The resolved language is the
25
+ * language that was actually used to load translations. It may differ from
26
+ * the configured language if there are no translations available for the
27
+ * configured language. For example, when setting the language to "de-DE" but
28
+ * translations are only available for "de", the resolved language will be
29
+ * "de".
30
+ */
31
+ get resolvedLanguage(): ReadonlySignal<string | undefined>;
32
+ /**
33
+ * Initializes the I18n instance. This method should be called once to load
34
+ * translations for the initial language. The `translate` API will not return
35
+ * properly translated strings until the initializations has completed.
36
+ *
37
+ * The initialization runs asynchronously as translations are loaded from the
38
+ * backend. The method returns a promise that resolves when the translations
39
+ * have been loaded, after which the `translate` API can safely be used.
40
+ *
41
+ * The initial language is determined as follows:
42
+ * 1. If a user opens the app for the first time, the browser language is used.
43
+ * 2. If the language has been changed in a previous usage of the app using
44
+ * `setLanguage`, the last used language is used. The last used language is
45
+ * automatically stored in local storage.
46
+ *
47
+ * Alternatively, the initial language can be explicitly configured using the
48
+ * `language` option. The language should be a valid IETF BCP 47 language tag,
49
+ * such as `en` or `en-US`.
50
+ *
51
+ * @param options - Optional options object to specify the initial language.
52
+ */
53
+ configure(options?: I18nOptions): Promise<void>;
54
+ /**
55
+ * Changes the current language and loads translations for the new language.
56
+ * Components using the `translate` API will automatically re-render, and
57
+ * subscribers to the `language` signal will be notified, when the new
58
+ * translations have been loaded.
59
+ *
60
+ * The language should be a valid IETF BCP 47 language tag, such as `en` or
61
+ * `en-US`.
62
+ *
63
+ * If there is no translation file for that specific language tag, the backend
64
+ * will try to load the translation file for the parent language tag. For
65
+ * example, if there is no translation file for `en-US`, the backend will try
66
+ * to load the translation file for `en`. Otherwise, it will fall back to the
67
+ * default translation file `translations.properties`.
68
+ *
69
+ * Changing the language is an asynchronous operation. The method returns a
70
+ * promise that resolves when the translations for the new language have been
71
+ * loaded.
72
+ *
73
+ * @param newLanguage - a valid IETF BCP 47 language tag, such as `en` or `en-US`
74
+ */
75
+ setLanguage(newLanguage: string): Promise<void>;
76
+ private updateLanguage;
77
+ /**
78
+ * Reloads all translations for the current language. This method should only
79
+ * be used for HMR in development mode.
80
+ */
81
+ private reloadTranslations;
82
+ /**
83
+ * Returns a translated string for the given translation key. The key should
84
+ * match a key in the loaded translations. If no translation is found for the
85
+ * key, the key itself is returned.
86
+ *
87
+ * Translations may contain placeholders, following the ICU MessageFormat
88
+ * syntax. They can be replaced by passing a `params` object with placeholder
89
+ * values, where keys correspond to the placeholder names and values are the
90
+ * replacement value. Values should be basic types such as strings, numbers,
91
+ * or dates that match the placeholder format configured in the translation
92
+ * string. For example, when using a placeholder `{count, number}`, the value
93
+ * should be a number, when using `{date, date}`, the value should be a Date
94
+ * object, and so on.
95
+ *
96
+ * This method internally accesses a signal, meaning that React components
97
+ * that use it will automatically re-render when translations change.
98
+ * Likewise, signal effects automatically subscribe to translation changes
99
+ * when calling this method.
100
+ *
101
+ * @param key - The translation key to translate
102
+ * @param params - Optional object with placeholder values
103
+ */
104
+ translate(key: string, params?: Record<string, unknown>): string;
114
105
  }
115
106
  /**
116
- * The global I18n instance that is used to initialize translations, change the
117
- * current language, and translate strings.
118
- */
107
+ * The global I18n instance that is used to initialize translations, change the
108
+ * current language, and translate strings.
109
+ */
119
110
  export declare const i18n: I18n;
120
111
  /**
121
- * Returns a translated string for the given translation key. The key should
122
- * match a key in the loaded translations. If no translation is found for the
123
- * key, the key itself is returned.
124
- *
125
- * Translations may contain placeholders, following the ICU MessageFormat
126
- * syntax. They can be replaced by passing a `params` object with placeholder
127
- * values, where keys correspond to the placeholder names and values are the
128
- * replacement value. Values should be basic types such as strings, numbers,
129
- * or dates that match the placeholder format configured in the translation
130
- * string. For example, when using a placeholder `{count, number}`, the value
131
- * should be a number, when using `{date, date}`, the value should be a Date
132
- * object, and so on.
133
- *
134
- * This method internally accesses a signal, meaning that React components
135
- * that use it will automatically re-render when translations change.
136
- * Likewise, signal effects automatically subscribe to translation changes
137
- * when calling this method.
138
- *
139
- * This function is a shorthand for `i18n.translate` of the global I18n instance.
140
- *
141
- * @param key - The translation key to translate
142
- * @param params - Optional object with placeholder values
143
- */ export declare function translate(key: string, params?: Record<string, unknown>): string;
144
- //# sourceMappingURL=index.d.ts.map
112
+ * Returns a translated string for the given translation key. The key should
113
+ * match a key in the loaded translations. If no translation is found for the
114
+ * key, the key itself is returned.
115
+ *
116
+ * Translations may contain placeholders, following the ICU MessageFormat
117
+ * syntax. They can be replaced by passing a `params` object with placeholder
118
+ * values, where keys correspond to the placeholder names and values are the
119
+ * replacement value. Values should be basic types such as strings, numbers,
120
+ * or dates that match the placeholder format configured in the translation
121
+ * string. For example, when using a placeholder `{count, number}`, the value
122
+ * should be a number, when using `{date, date}`, the value should be a Date
123
+ * object, and so on.
124
+ *
125
+ * This method internally accesses a signal, meaning that React components
126
+ * that use it will automatically re-render when translations change.
127
+ * Likewise, signal effects automatically subscribe to translation changes
128
+ * when calling this method.
129
+ *
130
+ * This function is a shorthand for `i18n.translate` of the global I18n instance.
131
+ *
132
+ * @param key - The translation key to translate
133
+ * @param params - Optional object with placeholder values
134
+ */ export declare function translate(key: string, params?: Record<string, unknown>): string;
package/index.js CHANGED
@@ -3,218 +3,239 @@ import { DefaultBackend } from "./backend.js";
3
3
  import { FormatCache } from "./FormatCache.js";
4
4
  import { getLanguageSettings, updateLanguageSettings } from "./settings.js";
5
5
  function determineInitialLanguage(options) {
6
- if (options?.language) {
7
- return options.language;
8
- }
9
- const settings = getLanguageSettings();
10
- if (settings?.language) {
11
- return settings.language;
12
- }
13
- return navigator.language;
6
+ if (options?.language) {
7
+ return options.language;
8
+ }
9
+ const settings = getLanguageSettings();
10
+ if (settings?.language) {
11
+ return settings.language;
12
+ }
13
+ return navigator.language;
14
14
  }
15
- class I18n {
16
- #backend = new DefaultBackend();
17
- #initialized = signal(false);
18
- #language = signal(void 0);
19
- #translations = signal({});
20
- #resolvedLanguage = signal(void 0);
21
- #chunks = /* @__PURE__ */ new Set();
22
- #formatCache = new FormatCache(navigator.language);
23
- constructor() {
24
- if (!globalThis.Vaadin?.featureFlags?.hillaI18n) {
25
- throw new Error(
26
- `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`
27
- );
28
- }
29
- if (import.meta.hot) {
30
- import.meta.hot.on("translations-update", () => {
31
- this.reloadTranslations();
32
- });
33
- }
34
- }
35
- /**
36
- * Returns a signal indicating whether the I18n instance has been initialized.
37
- * The instance is considered initialized after `configure` has been called
38
- * and translations for the initial language have been loaded. Can be used to
39
- * show a placeholder or loading indicator until the translations are ready.
40
- *
41
- * Subscribers to this signal will be notified when initialization is complete
42
- * and translations are ready to be used.
43
- */
44
- get initialized() {
45
- return this.#initialized;
46
- }
47
- /**
48
- * Returns a signal with the currently configured language.
49
- *
50
- * Subscribers to this signal will be notified when the language has changed
51
- * and new translations have been loaded.
52
- */
53
- get language() {
54
- return this.#language;
55
- }
56
- /**
57
- * Returns a signal with the resolved language. The resolved language is the
58
- * language that was actually used to load translations. It may differ from
59
- * the configured language if there are no translations available for the
60
- * configured language. For example, when setting the language to "de-DE" but
61
- * translations are only available for "de", the resolved language will be
62
- * "de".
63
- */
64
- get resolvedLanguage() {
65
- return this.#resolvedLanguage;
66
- }
67
- /**
68
- * Initializes the I18n instance. This method should be called once to load
69
- * translations for the initial language. The `translate` API will not return
70
- * properly translated strings until the initializations has completed.
71
- *
72
- * The initialization runs asynchronously as translations are loaded from the
73
- * backend. The method returns a promise that resolves when the translations
74
- * have been loaded, after which the `translate` API can safely be used.
75
- *
76
- * The initial language is determined as follows:
77
- * 1. If a user opens the app for the first time, the browser language is used.
78
- * 2. If the language has been changed in a previous usage of the app using
79
- * `setLanguage`, the last used language is used. The last used language is
80
- * automatically stored in local storage.
81
- *
82
- * Alternatively, the initial language can be explicitly configured using the
83
- * `language` option. The language should be a valid IETF BCP 47 language tag,
84
- * such as `en` or `en-US`.
85
- *
86
- * @param options - Optional options object to specify the initial language.
87
- */
88
- async configure(options) {
89
- const initialLanguage = determineInitialLanguage(options);
90
- await this.updateLanguage(initialLanguage);
91
- }
92
- /**
93
- * Changes the current language and loads translations for the new language.
94
- * Components using the `translate` API will automatically re-render, and
95
- * subscribers to the `language` signal will be notified, when the new
96
- * translations have been loaded.
97
- *
98
- * The language should be a valid IETF BCP 47 language tag, such as `en` or
99
- * `en-US`.
100
- *
101
- * If there is no translation file for that specific language tag, the backend
102
- * will try to load the translation file for the parent language tag. For
103
- * example, if there is no translation file for `en-US`, the backend will try
104
- * to load the translation file for `en`. Otherwise, it will fall back to the
105
- * default translation file `translations.properties`.
106
- *
107
- * Changing the language is an asynchronous operation. The method returns a
108
- * promise that resolves when the translations for the new language have been
109
- * loaded.
110
- *
111
- * @param newLanguage - a valid IETF BCP 47 language tag, such as `en` or `en-US`
112
- */
113
- async setLanguage(newLanguage) {
114
- await this.updateLanguage(newLanguage, true);
115
- }
116
- /**
117
- * Registers the chunk name for loading translations, and loads the
118
- * translations for the specified chunk.
119
- *
120
- * @internal only for automatic internal calls from production JS bundles
121
- *
122
- * @param chunkName - the production JS bundle chunk name
123
- */
124
- async registerChunk(chunkName) {
125
- if (this.#chunks.has(chunkName)) {
126
- return;
127
- }
128
- this.#chunks.add(chunkName);
129
- if (this.#language.value) {
130
- await this.updateLanguage(this.#language.value, false, chunkName);
131
- }
132
- }
133
- async updateLanguage(newLanguage, updateSettings = false, newChunk) {
134
- if (this.#language.value === newLanguage && !newChunk) {
135
- return;
136
- }
137
- const chunks = newChunk ? [newChunk] : this.#chunks.size > 0 ? [...this.#chunks.values()] : void 0;
138
- let translationsResult;
139
- try {
140
- translationsResult = await this.#backend.loadTranslations(newLanguage, chunks);
141
- } catch (e) {
142
- console.error(`Failed to load translations for language: ${newLanguage}`, e);
143
- return;
144
- }
145
- batch(() => {
146
- this.#translations.value = newChunk ? { ...this.#translations.value, ...translationsResult.translations } : translationsResult.translations;
147
- this.#language.value = newLanguage;
148
- this.#resolvedLanguage.value = translationsResult.resolvedLanguage;
149
- this.#formatCache = new FormatCache(newLanguage);
150
- this.#initialized.value = true;
151
- if (updateSettings) {
152
- updateLanguageSettings({
153
- language: newLanguage
154
- });
155
- }
156
- });
157
- }
158
- /**
159
- * Reloads all translations for the current language. This method should only
160
- * be used for HMR in development mode.
161
- */
162
- async reloadTranslations() {
163
- const currentLanguage = this.#language.value;
164
- if (!currentLanguage) {
165
- return;
166
- }
167
- let translationsResult;
168
- try {
169
- translationsResult = await this.#backend.loadTranslations(currentLanguage);
170
- } catch (e) {
171
- console.error(`Failed to reload translations for language: ${currentLanguage}`, e);
172
- return;
173
- }
174
- batch(() => {
175
- this.#translations.value = translationsResult.translations;
176
- this.#resolvedLanguage.value = translationsResult.resolvedLanguage;
177
- this.#formatCache = new FormatCache(currentLanguage);
178
- });
179
- }
180
- /**
181
- * Returns a translated string for the given translation key. The key should
182
- * match a key in the loaded translations. If no translation is found for the
183
- * key, the key itself is returned.
184
- *
185
- * Translations may contain placeholders, following the ICU MessageFormat
186
- * syntax. They can be replaced by passing a `params` object with placeholder
187
- * values, where keys correspond to the placeholder names and values are the
188
- * replacement value. Values should be basic types such as strings, numbers,
189
- * or dates that match the placeholder format configured in the translation
190
- * string. For example, when using a placeholder `{count, number}`, the value
191
- * should be a number, when using `{date, date}`, the value should be a Date
192
- * object, and so on.
193
- *
194
- * This method internally accesses a signal, meaning that React components
195
- * that use it will automatically re-render when translations change.
196
- * Likewise, signal effects automatically subscribe to translation changes
197
- * when calling this method.
198
- *
199
- * @param key - The translation key to translate
200
- * @param params - Optional object with placeholder values
201
- */
202
- translate(key, params) {
203
- const translation = this.#translations.value[key];
204
- if (!translation) {
205
- return key;
206
- }
207
- const format = this.#formatCache.getFormat(translation);
208
- return format.format(params);
209
- }
15
+ export class I18n {
16
+ #backend = new DefaultBackend();
17
+ #initialized = signal(false);
18
+ #language = signal(undefined);
19
+ #translations = signal({});
20
+ #resolvedLanguage = signal(undefined);
21
+ #chunks = new Set();
22
+ #formatCache = new FormatCache(navigator.language);
23
+ constructor() {
24
+ if (!globalThis.Vaadin?.featureFlags?.hillaI18n) {
25
+ throw new Error(`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
+ if (import.meta.hot) {
28
+ import.meta.hot.on("translations-update", () => {
29
+ this.reloadTranslations();
30
+ });
31
+ }
32
+ }
33
+ /**
34
+ * Returns a signal indicating whether the I18n instance has been initialized.
35
+ * The instance is considered initialized after `configure` has been called
36
+ * and translations for the initial language have been loaded. Can be used to
37
+ * show a placeholder or loading indicator until the translations are ready.
38
+ *
39
+ * Subscribers to this signal will be notified when initialization is complete
40
+ * and translations are ready to be used.
41
+ */
42
+ get initialized() {
43
+ return this.#initialized;
44
+ }
45
+ /**
46
+ * Returns a signal with the currently configured language.
47
+ *
48
+ * Subscribers to this signal will be notified when the language has changed
49
+ * and new translations have been loaded.
50
+ */
51
+ get language() {
52
+ return this.#language;
53
+ }
54
+ /**
55
+ * Returns a signal with the resolved language. The resolved language is the
56
+ * language that was actually used to load translations. It may differ from
57
+ * the configured language if there are no translations available for the
58
+ * configured language. For example, when setting the language to "de-DE" but
59
+ * translations are only available for "de", the resolved language will be
60
+ * "de".
61
+ */
62
+ get resolvedLanguage() {
63
+ return this.#resolvedLanguage;
64
+ }
65
+ /**
66
+ * Initializes the I18n instance. This method should be called once to load
67
+ * translations for the initial language. The `translate` API will not return
68
+ * properly translated strings until the initializations has completed.
69
+ *
70
+ * The initialization runs asynchronously as translations are loaded from the
71
+ * backend. The method returns a promise that resolves when the translations
72
+ * have been loaded, after which the `translate` API can safely be used.
73
+ *
74
+ * The initial language is determined as follows:
75
+ * 1. If a user opens the app for the first time, the browser language is used.
76
+ * 2. If the language has been changed in a previous usage of the app using
77
+ * `setLanguage`, the last used language is used. The last used language is
78
+ * automatically stored in local storage.
79
+ *
80
+ * Alternatively, the initial language can be explicitly configured using the
81
+ * `language` option. The language should be a valid IETF BCP 47 language tag,
82
+ * such as `en` or `en-US`.
83
+ *
84
+ * @param options - Optional options object to specify the initial language.
85
+ */
86
+ async configure(options) {
87
+ const initialLanguage = determineInitialLanguage(options);
88
+ await this.updateLanguage(initialLanguage);
89
+ }
90
+ /**
91
+ * Changes the current language and loads translations for the new language.
92
+ * Components using the `translate` API will automatically re-render, and
93
+ * subscribers to the `language` signal will be notified, when the new
94
+ * translations have been loaded.
95
+ *
96
+ * The language should be a valid IETF BCP 47 language tag, such as `en` or
97
+ * `en-US`.
98
+ *
99
+ * If there is no translation file for that specific language tag, the backend
100
+ * will try to load the translation file for the parent language tag. For
101
+ * example, if there is no translation file for `en-US`, the backend will try
102
+ * to load the translation file for `en`. Otherwise, it will fall back to the
103
+ * default translation file `translations.properties`.
104
+ *
105
+ * Changing the language is an asynchronous operation. The method returns a
106
+ * promise that resolves when the translations for the new language have been
107
+ * loaded.
108
+ *
109
+ * @param newLanguage - a valid IETF BCP 47 language tag, such as `en` or `en-US`
110
+ */
111
+ async setLanguage(newLanguage) {
112
+ await this.updateLanguage(newLanguage, true);
113
+ }
114
+ /**
115
+ * Registers the chunk name for loading translations, and loads the
116
+ * translations for the specified chunk.
117
+ *
118
+ * @internal only for automatic internal calls from production JS bundles
119
+ *
120
+ * @param chunkName - the production JS bundle chunk name
121
+ */
122
+ async registerChunk(chunkName) {
123
+ if (this.#chunks.has(chunkName)) {
124
+ return;
125
+ }
126
+ this.#chunks.add(chunkName);
127
+ if (this.#language.value) {
128
+ await this.updateLanguage(this.#language.value, false, chunkName);
129
+ }
130
+ }
131
+ async updateLanguage(newLanguage, updateSettings = false, newChunk) {
132
+ if (this.#language.value === newLanguage && !newChunk) {
133
+ return;
134
+ }
135
+ const chunks = newChunk ? [newChunk] : this.#chunks.size > 0 ? [...this.#chunks.values()] : undefined;
136
+ let translationsResult;
137
+ try {
138
+ translationsResult = await this.#backend.loadTranslations(newLanguage, chunks);
139
+ } catch (e) {
140
+ console.error(`Failed to load translations for language: ${newLanguage}`, e);
141
+ return;
142
+ }
143
+ batch(() => {
144
+ this.#translations.value = newChunk ? {
145
+ ...this.#translations.value,
146
+ ...translationsResult.translations
147
+ } : translationsResult.translations;
148
+ this.#language.value = newLanguage;
149
+ this.#resolvedLanguage.value = translationsResult.resolvedLanguage;
150
+ this.#formatCache = new FormatCache(newLanguage);
151
+ this.#initialized.value = true;
152
+ if (updateSettings) {
153
+ updateLanguageSettings({ language: newLanguage });
154
+ }
155
+ });
156
+ }
157
+ /**
158
+ * Reloads all translations for the current language. This method should only
159
+ * be used for HMR in development mode.
160
+ */
161
+ async reloadTranslations() {
162
+ const currentLanguage = this.#language.value;
163
+ if (!currentLanguage) {
164
+ return;
165
+ }
166
+ let translationsResult;
167
+ try {
168
+ translationsResult = await this.#backend.loadTranslations(currentLanguage);
169
+ } catch (e) {
170
+ console.error(`Failed to reload translations for language: ${currentLanguage}`, e);
171
+ return;
172
+ }
173
+ batch(() => {
174
+ this.#translations.value = translationsResult.translations;
175
+ this.#resolvedLanguage.value = translationsResult.resolvedLanguage;
176
+ this.#formatCache = new FormatCache(currentLanguage);
177
+ });
178
+ }
179
+ /**
180
+ * Returns a translated string for the given translation key. The key should
181
+ * match a key in the loaded translations. If no translation is found for the
182
+ * key, the key itself is returned.
183
+ *
184
+ * Translations may contain placeholders, following the ICU MessageFormat
185
+ * syntax. They can be replaced by passing a `params` object with placeholder
186
+ * values, where keys correspond to the placeholder names and values are the
187
+ * replacement value. Values should be basic types such as strings, numbers,
188
+ * or dates that match the placeholder format configured in the translation
189
+ * string. For example, when using a placeholder `{count, number}`, the value
190
+ * should be a number, when using `{date, date}`, the value should be a Date
191
+ * object, and so on.
192
+ *
193
+ * This method internally accesses a signal, meaning that React components
194
+ * that use it will automatically re-render when translations change.
195
+ * Likewise, signal effects automatically subscribe to translation changes
196
+ * when calling this method.
197
+ *
198
+ * @param key - The translation key to translate
199
+ * @param params - Optional object with placeholder values
200
+ */
201
+ translate(key, params) {
202
+ const translation = this.#translations.value[key];
203
+ if (!translation) {
204
+ return key;
205
+ }
206
+ const format = this.#formatCache.getFormat(translation);
207
+ return format.format(params);
208
+ }
210
209
  }
211
- const i18n = new I18n();
212
- function translate(key, params) {
213
- return i18n.translate(key, params);
210
+ /**
211
+ * The global I18n instance that is used to initialize translations, change the
212
+ * current language, and translate strings.
213
+ */
214
+ export const i18n = new I18n();
215
+ /**
216
+ * Returns a translated string for the given translation key. The key should
217
+ * match a key in the loaded translations. If no translation is found for the
218
+ * key, the key itself is returned.
219
+ *
220
+ * Translations may contain placeholders, following the ICU MessageFormat
221
+ * syntax. They can be replaced by passing a `params` object with placeholder
222
+ * values, where keys correspond to the placeholder names and values are the
223
+ * replacement value. Values should be basic types such as strings, numbers,
224
+ * or dates that match the placeholder format configured in the translation
225
+ * string. For example, when using a placeholder `{count, number}`, the value
226
+ * should be a number, when using `{date, date}`, the value should be a Date
227
+ * object, and so on.
228
+ *
229
+ * This method internally accesses a signal, meaning that React components
230
+ * that use it will automatically re-render when translations change.
231
+ * Likewise, signal effects automatically subscribe to translation changes
232
+ * when calling this method.
233
+ *
234
+ * This function is a shorthand for `i18n.translate` of the global I18n instance.
235
+ *
236
+ * @param key - The translation key to translate
237
+ * @param params - Optional object with placeholder values
238
+ */ export function translate(key, params) {
239
+ return i18n.translate(key, params);
214
240
  }
215
- export {
216
- I18n,
217
- i18n,
218
- translate
219
- };
220
- //# sourceMappingURL=index.js.map
241
+ //# sourceMappingURL=./index.js.map
package/index.js.map CHANGED
@@ -1,7 +1 @@
1
- {
2
- "version": 3,
3
- "sources": ["src/index.ts"],
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\ninterface VaadinGlobal {\n Vaadin?: {\n featureFlags?: {\n hillaI18n?: boolean;\n };\n };\n}\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 readonly #chunks = new Set<string>();\n\n #formatCache: FormatCache = new FormatCache(navigator.language);\n\n constructor() {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n if (!(globalThis as VaadinGlobal).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 // @ts-expect-error import.meta.hot does not have TS definitions\n if (import.meta.hot) {\n // @ts-expect-error import.meta.hot does not have TS definitions\n // eslint-disable-next-line\n import.meta.hot.on('translations-update', () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.reloadTranslations();\n });\n }\n }\n\n /**\n * Returns a signal indicating whether the I18n instance has been initialized.\n * The instance is considered initialized after `configure` has been called\n * and translations for the initial language have been loaded. Can be used to\n * show a placeholder or loading indicator until the translations are ready.\n *\n * Subscribers to this signal will be notified when initialization is complete\n * and translations are ready to be used.\n */\n get initialized(): ReadonlySignal<boolean> {\n return this.#initialized;\n }\n\n /**\n * Returns a signal with the currently configured language.\n *\n * Subscribers to this signal will be notified when the language has changed\n * and new translations have been loaded.\n */\n get language(): ReadonlySignal<string | undefined> {\n return this.#language;\n }\n\n /**\n * Returns a signal with the resolved language. The resolved language is the\n * language that was actually used to load translations. It may differ from\n * the configured language if there are no translations available for the\n * configured language. For example, when setting the language to \"de-DE\" but\n * translations are only available for \"de\", the resolved language will be\n * \"de\".\n */\n get resolvedLanguage(): ReadonlySignal<string | undefined> {\n return this.#resolvedLanguage;\n }\n\n /**\n * Initializes the I18n instance. This method should be called once to load\n * translations for the initial language. The `translate` API will not return\n * properly translated strings until the initializations has completed.\n *\n * The initialization runs asynchronously as translations are loaded from the\n * backend. The method returns a promise that resolves when the translations\n * have been loaded, after which the `translate` API can safely be used.\n *\n * The initial language is determined as follows:\n * 1. If a user opens the app for the first time, the browser language is used.\n * 2. If the language has been changed in a previous usage of the app using\n * `setLanguage`, the last used language is used. The last used language is\n * automatically stored in local storage.\n *\n * Alternatively, the initial language can be explicitly configured using the\n * `language` option. The language should be a valid IETF BCP 47 language tag,\n * such as `en` or `en-US`.\n *\n * @param options - Optional options object to specify the initial language.\n */\n async configure(options?: I18nOptions): Promise<void> {\n const initialLanguage = determineInitialLanguage(options);\n await this.updateLanguage(initialLanguage);\n }\n\n /**\n * Changes the current language and loads translations for the new language.\n * Components using the `translate` API will automatically re-render, and\n * subscribers to the `language` signal will be notified, when the new\n * translations have been loaded.\n *\n * The language should be a valid IETF BCP 47 language tag, such as `en` or\n * `en-US`.\n *\n * If there is no translation file for that specific language tag, the backend\n * will try to load the translation file for the parent language tag. For\n * example, if there is no translation file for `en-US`, the backend will try\n * to load the translation file for `en`. Otherwise, it will fall back to the\n * default translation file `translations.properties`.\n *\n * Changing the language is an asynchronous operation. The method returns a\n * promise that resolves when the translations for the new language have been\n * loaded.\n *\n * @param newLanguage - a valid IETF BCP 47 language tag, such as `en` or `en-US`\n */\n async setLanguage(newLanguage: string): Promise<void> {\n await this.updateLanguage(newLanguage, true);\n }\n\n /**\n * Registers the chunk name for loading translations, and loads the\n * translations for the specified chunk.\n *\n * @internal only for automatic internal calls from production JS bundles\n *\n * @param chunkName - the production JS bundle chunk name\n */\n async registerChunk(chunkName: string): Promise<void> {\n if (this.#chunks.has(chunkName)) {\n return;\n }\n\n this.#chunks.add(chunkName);\n\n if (this.#language.value) {\n await this.updateLanguage(this.#language.value, false, chunkName);\n }\n }\n\n private async updateLanguage(newLanguage: string, updateSettings = false, newChunk?: string) {\n if (this.#language.value === newLanguage && !newChunk) {\n return;\n }\n\n const chunks = newChunk\n ? [newChunk] // New chunk is registered, load only that\n : this.#chunks.size > 0\n ? [...this.#chunks.values()] // Load the new language for all chunks registered so far\n : undefined; // Load the new language without specifying chunks, assuming dev. mode\n\n let translationsResult: TranslationsResult;\n try {\n translationsResult = await this.#backend.loadTranslations(newLanguage, chunks);\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 = newChunk\n ? { ...this.#translations.value, ...translationsResult.translations }\n : 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 /**\n * Reloads all translations for the current language. This method should only\n * be used for HMR in development mode.\n */\n private async reloadTranslations() {\n const currentLanguage = this.#language.value;\n if (!currentLanguage) {\n return;\n }\n\n let translationsResult: TranslationsResult;\n try {\n translationsResult = await this.#backend.loadTranslations(currentLanguage);\n } catch (e) {\n console.error(`Failed to reload translations for language: ${currentLanguage}`, 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.#resolvedLanguage.value = translationsResult.resolvedLanguage;\n this.#formatCache = new FormatCache(currentLanguage);\n });\n }\n\n /**\n * Returns a translated string for the given translation key. The key should\n * match a key in the loaded translations. If no translation is found for the\n * key, the key itself is returned.\n *\n * Translations may contain placeholders, following the ICU MessageFormat\n * syntax. They can be replaced by passing a `params` object with placeholder\n * values, where keys correspond to the placeholder names and values are the\n * replacement value. Values should be basic types such as strings, numbers,\n * or dates that match the placeholder format configured in the translation\n * string. For example, when using a placeholder `{count, number}`, the value\n * should be a number, when using `{date, date}`, the value should be a Date\n * object, and so on.\n *\n * This method internally accesses a signal, meaning that React components\n * that use it will automatically re-render when translations change.\n * Likewise, signal effects automatically subscribe to translation changes\n * when calling this method.\n *\n * @param key - The translation key to translate\n * @param params - Optional object with placeholder values\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\n/**\n * The global I18n instance that is used to initialize translations, change the\n * current language, and translate strings.\n */\nexport const i18n = new I18n();\n\n/**\n * Returns a translated string for the given translation key. The key should\n * match a key in the loaded translations. If no translation is found for the\n * key, the key itself is returned.\n *\n * Translations may contain placeholders, following the ICU MessageFormat\n * syntax. They can be replaced by passing a `params` object with placeholder\n * values, where keys correspond to the placeholder names and values are the\n * replacement value. Values should be basic types such as strings, numbers,\n * or dates that match the placeholder format configured in the translation\n * string. For example, when using a placeholder `{count, number}`, the value\n * should be a number, when using `{date, date}`, the value should be a Date\n * object, and so on.\n *\n * This method internally accesses a signal, meaning that React components\n * that use it will automatically re-render when translations change.\n * Likewise, signal effects automatically subscribe to translation changes\n * when calling this method.\n *\n * This function is a shorthand for `i18n.translate` of the global I18n instance.\n *\n * @param key - The translation key to translate\n * @param params - Optional object with placeholder values\n */ export 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;AAW5D,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,EAChE,UAAU,oBAAI,IAAY;AAAA,EAEnC,eAA4B,IAAI,YAAY,UAAU,QAAQ;AAAA,EAE9D,cAAc;AAEZ,QAAI,CAAE,WAA4B,QAAQ,cAAc,WAAW;AAEjE,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,QAAI,YAAY,KAAK;AAGnB,kBAAY,IAAI,GAAG,uBAAuB,MAAM;AAE9C,aAAK,mBAAmB;AAAA,MAC1B,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,IAAI,cAAuC;AACzC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAI,WAA+C;AACjD,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,IAAI,mBAAuD;AACzD,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,MAAM,UAAU,SAAsC;AACpD,UAAM,kBAAkB,yBAAyB,OAAO;AACxD,UAAM,KAAK,eAAe,eAAe;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,MAAM,YAAY,aAAoC;AACpD,UAAM,KAAK,eAAe,aAAa,IAAI;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,cAAc,WAAkC;AACpD,QAAI,KAAK,QAAQ,IAAI,SAAS,GAAG;AAC/B;AAAA,IACF;AAEA,SAAK,QAAQ,IAAI,SAAS;AAE1B,QAAI,KAAK,UAAU,OAAO;AACxB,YAAM,KAAK,eAAe,KAAK,UAAU,OAAO,OAAO,SAAS;AAAA,IAClE;AAAA,EACF;AAAA,EAEA,MAAc,eAAe,aAAqB,iBAAiB,OAAO,UAAmB;AAC3F,QAAI,KAAK,UAAU,UAAU,eAAe,CAAC,UAAU;AACrD;AAAA,IACF;AAEA,UAAM,SAAS,WACX,CAAC,QAAQ,IACT,KAAK,QAAQ,OAAO,IAClB,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,IACzB;AAEN,QAAI;AACJ,QAAI;AACF,2BAAqB,MAAM,KAAK,SAAS,iBAAiB,aAAa,MAAM;AAAA,IAC/E,SAAS,GAAG;AACV,cAAQ,MAAM,6CAA6C,WAAW,IAAI,CAAC;AAC3E;AAAA,IACF;AAGA,UAAM,MAAM;AACV,WAAK,cAAc,QAAQ,WACvB,EAAE,GAAG,KAAK,cAAc,OAAO,GAAG,mBAAmB,aAAa,IAClE,mBAAmB;AACvB,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;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,qBAAqB;AACjC,UAAM,kBAAkB,KAAK,UAAU;AACvC,QAAI,CAAC,iBAAiB;AACpB;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,2BAAqB,MAAM,KAAK,SAAS,iBAAiB,eAAe;AAAA,IAC3E,SAAS,GAAG;AACV,cAAQ,MAAM,+CAA+C,eAAe,IAAI,CAAC;AACjF;AAAA,IACF;AAGA,UAAM,MAAM;AACV,WAAK,cAAc,QAAQ,mBAAmB;AAC9C,WAAK,kBAAkB,QAAQ,mBAAmB;AAClD,WAAK,eAAe,IAAI,YAAY,eAAe;AAAA,IACrD,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBA,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;AAMO,MAAM,OAAO,IAAI,KAAK;AAyBlB,SAAS,UAAU,KAAa,QAA0C;AACnF,SAAO,KAAK,UAAU,KAAK,MAAM;AACnC;",
6
- "names": []
7
- }
1
+ {"mappings":"AAAA,SAAS,OAA4B,2CAAyD;AAC9F,SAAS,oCAAuD;AAChE,SAAS,qCAAsC;AAC/C,SAAS,qBAAqB,6CAA8C;AAW5E,SAAS,yBAAyBA,SAA+B;AAE/D,KAAI,SAAS,UAAU;AACrB,SAAO,QAAQ;CAChB;CAED,MAAM,WAAW,qBAAqB;AACtC,KAAI,UAAU,UAAU;AACtB,SAAO,SAAS;CACjB;AAED,QAAO,UAAU;AAClB;AAED,OAAO,MAAM,KAAK;CAChB,AAASC,WAAwB,IAAI;CAErC,AAASC,eAAgC,OAAO,MAAM;CACtD,AAASC,YAAwC,OAAO,UAAU;CAClE,AAASC,gBAAsC,OAAO,CAAE,EAAC;CACzD,AAASC,oBAAgD,OAAO,UAAU;CAC1E,AAASC,UAAU,IAAI;CAEvB,eAA4B,IAAI,YAAY,UAAU;CAEtD,cAAc;AAEZ,OAAM,WAA4B,QAAQ,cAAc,WAAW;AAEjE,SAAM,IAAI,OACP;EAEJ;AAED,MAAI,OAAO,KAAK,KAAK;AAGnB,UAAO,KAAK,IAAI,GAAG,uBAAuB,MAAM;AAE9C,SAAK,oBAAoB;GAC1B,EAAC;EACH;CACF;;;;;;;;;;CAWD,IAAI,cAAuC;AACzC,SAAO,KAAKJ;CACb;;;;;;;CAQD,IAAI,WAA+C;AACjD,SAAO,KAAKC;CACb;;;;;;;;;CAUD,IAAI,mBAAuD;AACzD,SAAO,KAAKE;CACb;;;;;;;;;;;;;;;;;;;;;;CAuBD,MAAM,UAAUL,SAAsC;EACpD,MAAM,kBAAkB,yBAAyB,QAAQ;AACzD,QAAM,KAAK,eAAe,gBAAgB;CAC3C;;;;;;;;;;;;;;;;;;;;;;CAuBD,MAAM,YAAYO,aAAoC;AACpD,QAAM,KAAK,eAAe,aAAa,KAAK;CAC7C;;;;;;;;;CAUD,MAAM,cAAcC,WAAkC;AACpD,MAAI,KAAKF,QAAQ,IAAI,UAAU,EAAE;AAC/B;EACD;AAED,OAAKA,QAAQ,IAAI,UAAU;AAE3B,MAAI,KAAKH,UAAU,OAAO;AACxB,SAAM,KAAK,eAAe,KAAKA,UAAU,OAAO,OAAO,UAAU;EAClE;CACF;CAED,MAAc,eAAeI,aAAqB,iBAAiB,OAAOE,UAAmB;AAC3F,MAAI,KAAKN,UAAU,UAAU,gBAAgB,UAAU;AACrD;EACD;EAED,MAAM,SAAS,WACX,CAAC,QAAS,IACV,KAAKG,QAAQ,OAAO,IAClB,CAAC,GAAG,KAAKA,QAAQ,QAAQ,AAAC,IAC1B;EAEN,IAAII;AACJ,MAAI;AACF,wBAAqB,MAAM,KAAKT,SAAS,iBAAiB,aAAa,OAAO;EAC/E,SAAQ,GAAG;AACV,WAAQ,OAAO,4CAA4C,YAAY,GAAG,EAAE;AAC5E;EACD;AAGD,QAAM,MAAM;AACV,QAAKG,cAAc,QAAQ,WACvB;IAAE,GAAG,KAAKA,cAAc;IAAO,GAAG,mBAAmB;GAAc,IACnE,mBAAmB;AACvB,QAAKD,UAAU,QAAQ;AACvB,QAAKE,kBAAkB,QAAQ,mBAAmB;AAClD,QAAKM,eAAe,IAAI,YAAY;AACpC,QAAKT,aAAa,QAAQ;AAE1B,OAAI,gBAAgB;AAClB,2BAAuB,EACrB,UAAU,YACX,EAAC;GACH;EACF,EAAC;CACH;;;;;CAMD,MAAc,qBAAqB;EACjC,MAAM,kBAAkB,KAAKC,UAAU;AACvC,OAAK,iBAAiB;AACpB;EACD;EAED,IAAIO;AACJ,MAAI;AACF,wBAAqB,MAAM,KAAKT,SAAS,iBAAiB,gBAAgB;EAC3E,SAAQ,GAAG;AACV,WAAQ,OAAO,8CAA8C,gBAAgB,GAAG,EAAE;AAClF;EACD;AAGD,QAAM,MAAM;AACV,QAAKG,cAAc,QAAQ,mBAAmB;AAC9C,QAAKC,kBAAkB,QAAQ,mBAAmB;AAClD,QAAKM,eAAe,IAAI,YAAY;EACrC,EAAC;CACH;;;;;;;;;;;;;;;;;;;;;;;CAwBD,UAAUC,KAAaC,QAA0C;EAC/D,MAAM,cAAc,KAAKT,cAAc,MAAM;AAC7C,OAAK,aAAa;AAChB,UAAO;EACR;EACD,MAAM,SAAS,KAAKO,aAAa,UAAU,YAAY;AACvD,SAAO,OAAO,OAAO,OAAO;CAC7B;AACF;;;;;AAMD,OAAO,MAAMG,OAAa,IAAI;;;;;;;;;;;;;;;;;;;;;;;;EAyB1B,QAAO,SAAS,UAAUF,KAAaC,QAA0C;AACnF,QAAO,KAAK,UAAU,KAAK,OAAO;AACnC","names":["options?: I18nOptions","#backend","#initialized","#language","#translations","#resolvedLanguage","#chunks","newLanguage: string","chunkName: string","newChunk?: string","translationsResult: TranslationsResult","#formatCache","key: string","params?: Record<string, unknown>","i18n: I18n"],"sources":["/opt/agent/work/1af72d8adc613024/hilla/packages/ts/react-i18n/src/index.ts"],"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\ninterface VaadinGlobal {\n Vaadin?: {\n featureFlags?: {\n hillaI18n?: boolean;\n };\n };\n}\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 readonly #chunks = new Set<string>();\n\n #formatCache: FormatCache = new FormatCache(navigator.language);\n\n constructor() {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n if (!(globalThis as VaadinGlobal).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 // @ts-expect-error import.meta.hot does not have TS definitions\n if (import.meta.hot) {\n // @ts-expect-error import.meta.hot does not have TS definitions\n // eslint-disable-next-line\n import.meta.hot.on('translations-update', () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.reloadTranslations();\n });\n }\n }\n\n /**\n * Returns a signal indicating whether the I18n instance has been initialized.\n * The instance is considered initialized after `configure` has been called\n * and translations for the initial language have been loaded. Can be used to\n * show a placeholder or loading indicator until the translations are ready.\n *\n * Subscribers to this signal will be notified when initialization is complete\n * and translations are ready to be used.\n */\n get initialized(): ReadonlySignal<boolean> {\n return this.#initialized;\n }\n\n /**\n * Returns a signal with the currently configured language.\n *\n * Subscribers to this signal will be notified when the language has changed\n * and new translations have been loaded.\n */\n get language(): ReadonlySignal<string | undefined> {\n return this.#language;\n }\n\n /**\n * Returns a signal with the resolved language. The resolved language is the\n * language that was actually used to load translations. It may differ from\n * the configured language if there are no translations available for the\n * configured language. For example, when setting the language to \"de-DE\" but\n * translations are only available for \"de\", the resolved language will be\n * \"de\".\n */\n get resolvedLanguage(): ReadonlySignal<string | undefined> {\n return this.#resolvedLanguage;\n }\n\n /**\n * Initializes the I18n instance. This method should be called once to load\n * translations for the initial language. The `translate` API will not return\n * properly translated strings until the initializations has completed.\n *\n * The initialization runs asynchronously as translations are loaded from the\n * backend. The method returns a promise that resolves when the translations\n * have been loaded, after which the `translate` API can safely be used.\n *\n * The initial language is determined as follows:\n * 1. If a user opens the app for the first time, the browser language is used.\n * 2. If the language has been changed in a previous usage of the app using\n * `setLanguage`, the last used language is used. The last used language is\n * automatically stored in local storage.\n *\n * Alternatively, the initial language can be explicitly configured using the\n * `language` option. The language should be a valid IETF BCP 47 language tag,\n * such as `en` or `en-US`.\n *\n * @param options - Optional options object to specify the initial language.\n */\n async configure(options?: I18nOptions): Promise<void> {\n const initialLanguage = determineInitialLanguage(options);\n await this.updateLanguage(initialLanguage);\n }\n\n /**\n * Changes the current language and loads translations for the new language.\n * Components using the `translate` API will automatically re-render, and\n * subscribers to the `language` signal will be notified, when the new\n * translations have been loaded.\n *\n * The language should be a valid IETF BCP 47 language tag, such as `en` or\n * `en-US`.\n *\n * If there is no translation file for that specific language tag, the backend\n * will try to load the translation file for the parent language tag. For\n * example, if there is no translation file for `en-US`, the backend will try\n * to load the translation file for `en`. Otherwise, it will fall back to the\n * default translation file `translations.properties`.\n *\n * Changing the language is an asynchronous operation. The method returns a\n * promise that resolves when the translations for the new language have been\n * loaded.\n *\n * @param newLanguage - a valid IETF BCP 47 language tag, such as `en` or `en-US`\n */\n async setLanguage(newLanguage: string): Promise<void> {\n await this.updateLanguage(newLanguage, true);\n }\n\n /**\n * Registers the chunk name for loading translations, and loads the\n * translations for the specified chunk.\n *\n * @internal only for automatic internal calls from production JS bundles\n *\n * @param chunkName - the production JS bundle chunk name\n */\n async registerChunk(chunkName: string): Promise<void> {\n if (this.#chunks.has(chunkName)) {\n return;\n }\n\n this.#chunks.add(chunkName);\n\n if (this.#language.value) {\n await this.updateLanguage(this.#language.value, false, chunkName);\n }\n }\n\n private async updateLanguage(newLanguage: string, updateSettings = false, newChunk?: string) {\n if (this.#language.value === newLanguage && !newChunk) {\n return;\n }\n\n const chunks = newChunk\n ? [newChunk] // New chunk is registered, load only that\n : this.#chunks.size > 0\n ? [...this.#chunks.values()] // Load the new language for all chunks registered so far\n : undefined; // Load the new language without specifying chunks, assuming dev. mode\n\n let translationsResult: TranslationsResult;\n try {\n translationsResult = await this.#backend.loadTranslations(newLanguage, chunks);\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 = newChunk\n ? { ...this.#translations.value, ...translationsResult.translations }\n : 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 /**\n * Reloads all translations for the current language. This method should only\n * be used for HMR in development mode.\n */\n private async reloadTranslations() {\n const currentLanguage = this.#language.value;\n if (!currentLanguage) {\n return;\n }\n\n let translationsResult: TranslationsResult;\n try {\n translationsResult = await this.#backend.loadTranslations(currentLanguage);\n } catch (e) {\n console.error(`Failed to reload translations for language: ${currentLanguage}`, 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.#resolvedLanguage.value = translationsResult.resolvedLanguage;\n this.#formatCache = new FormatCache(currentLanguage);\n });\n }\n\n /**\n * Returns a translated string for the given translation key. The key should\n * match a key in the loaded translations. If no translation is found for the\n * key, the key itself is returned.\n *\n * Translations may contain placeholders, following the ICU MessageFormat\n * syntax. They can be replaced by passing a `params` object with placeholder\n * values, where keys correspond to the placeholder names and values are the\n * replacement value. Values should be basic types such as strings, numbers,\n * or dates that match the placeholder format configured in the translation\n * string. For example, when using a placeholder `{count, number}`, the value\n * should be a number, when using `{date, date}`, the value should be a Date\n * object, and so on.\n *\n * This method internally accesses a signal, meaning that React components\n * that use it will automatically re-render when translations change.\n * Likewise, signal effects automatically subscribe to translation changes\n * when calling this method.\n *\n * @param key - The translation key to translate\n * @param params - Optional object with placeholder values\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\n/**\n * The global I18n instance that is used to initialize translations, change the\n * current language, and translate strings.\n */\nexport const i18n: I18n = new I18n();\n\n/**\n * Returns a translated string for the given translation key. The key should\n * match a key in the loaded translations. If no translation is found for the\n * key, the key itself is returned.\n *\n * Translations may contain placeholders, following the ICU MessageFormat\n * syntax. They can be replaced by passing a `params` object with placeholder\n * values, where keys correspond to the placeholder names and values are the\n * replacement value. Values should be basic types such as strings, numbers,\n * or dates that match the placeholder format configured in the translation\n * string. For example, when using a placeholder `{count, number}`, the value\n * should be a number, when using `{date, date}`, the value should be a Date\n * object, and so on.\n *\n * This method internally accesses a signal, meaning that React components\n * that use it will automatically re-render when translations change.\n * Likewise, signal effects automatically subscribe to translation changes\n * when calling this method.\n *\n * This function is a shorthand for `i18n.translate` of the global I18n instance.\n *\n * @param key - The translation key to translate\n * @param params - Optional object with placeholder values\n */ export function translate(key: string, params?: Record<string, unknown>): string {\n return i18n.translate(key, params);\n}\n"],"version":3}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/hilla-react-i18n",
3
- "version": "24.7.0-alpha9",
3
+ "version": "24.7.0-beta3",
4
4
  "description": "Hilla I18n utils for React",
5
5
  "main": "index.js",
6
6
  "module": "index.js",
@@ -17,15 +17,13 @@
17
17
  ],
18
18
  "scripts": {
19
19
  "clean:build": "git clean -fx . -e .vite -e node_modules",
20
- "build": "concurrently npm:build:*",
21
- "build:esbuild": "tsx ../../../scripts/build.ts",
22
- "build:dts": "tsc --isolatedModules -p tsconfig.build.json",
23
- "build:copy": "cd src && copyfiles **/*.d.ts ..",
20
+ "build": "tsx ../../../scripts/fast-build.ts",
24
21
  "lint": "eslint src test",
25
22
  "lint:fix": "eslint src test --fix",
26
- "test": "karma start ../../../karma.config.cjs --port 9878",
27
- "test:coverage": "npm run test -- --coverage",
28
- "test:watch": "npm run test -- --watch",
23
+ "test": "vitest --run",
24
+ "test:coverage": "vitest --run --coverage",
25
+ "test:react": "npm run test",
26
+ "test:watch": "vitest",
29
27
  "typecheck": "tsc --noEmit"
30
28
  },
31
29
  "exports": {
@@ -46,33 +44,12 @@
46
44
  "access": "public"
47
45
  },
48
46
  "dependencies": {
49
- "@vaadin/hilla-frontend": "24.7.0-alpha9",
50
- "@vaadin/hilla-react-signals": "24.7.0-alpha9",
51
- "intl-messageformat": "^10.7.11"
47
+ "@vaadin/hilla-frontend": "24.7.0-beta3",
48
+ "@vaadin/hilla-react-signals": "24.7.0-beta3",
49
+ "intl-messageformat": "10.7.11"
52
50
  },
53
51
  "peerDependencies": {
54
52
  "react": "18 || 19",
55
53
  "react-dom": "18 || 19"
56
- },
57
- "devDependencies": {
58
- "@testing-library/dom": "^10.4.0",
59
- "@testing-library/react": "^16.1.0",
60
- "@testing-library/user-event": "^14.5.2",
61
- "@types/chai": "^4.3.20",
62
- "@types/chai-as-promised": "^7.1.8",
63
- "@types/chai-dom": "^1.11.3",
64
- "@types/mocha": "^10.0.10",
65
- "@types/react": "^18.3.18",
66
- "@types/react-dom": "^18",
67
- "@types/sinon": "^10.0.20",
68
- "@types/sinon-chai": "^3.2.12",
69
- "@types/validator": "^13.12.2",
70
- "chai": "^5.1.2",
71
- "chai-as-promised": "^7.1.2",
72
- "chai-dom": "^1.12.0",
73
- "fetch-mock": "^9.11.0",
74
- "sinon": "^16.1.3",
75
- "sinon-chai": "^3.7.0",
76
- "typescript": "5.7.3"
77
54
  }
78
55
  }
package/settings.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  export declare const VAADIN_LANGUAGE_SETTINGS_COOKIE_NAME = "vaadinLanguageSettings";
2
2
  export interface LanguageSettings {
3
- language?: string;
3
+ language?: string;
4
4
  }
5
5
  export declare function getLanguageSettings(): LanguageSettings | undefined;
6
6
  export declare function updateLanguageSettings(updates: Partial<LanguageSettings>): void;
7
- //# sourceMappingURL=settings.d.ts.map
package/settings.js CHANGED
@@ -1,26 +1,23 @@
1
1
  import CookieManager from "@vaadin/hilla-frontend/CookieManager.js";
2
- const VAADIN_LANGUAGE_SETTINGS_COOKIE_NAME = "vaadinLanguageSettings";
3
- function getLanguageSettings() {
4
- const cookie = CookieManager.get(VAADIN_LANGUAGE_SETTINGS_COOKIE_NAME);
5
- if (!cookie) return void 0;
6
- try {
7
- return JSON.parse(cookie);
8
- } catch (e) {
9
- return void 0;
10
- }
2
+ export const VAADIN_LANGUAGE_SETTINGS_COOKIE_NAME = "vaadinLanguageSettings";
3
+ export function getLanguageSettings() {
4
+ const cookie = CookieManager.get(VAADIN_LANGUAGE_SETTINGS_COOKIE_NAME);
5
+ if (!cookie) {
6
+ return undefined;
7
+ }
8
+ try {
9
+ return JSON.parse(cookie);
10
+ } catch {
11
+ return undefined;
12
+ }
11
13
  }
12
- function updateLanguageSettings(updates) {
13
- const settings = getLanguageSettings() ?? {};
14
- const newSettings = {
15
- ...settings,
16
- ...updates
17
- };
18
- const json = JSON.stringify(newSettings);
19
- CookieManager.set(VAADIN_LANGUAGE_SETTINGS_COOKIE_NAME, json);
14
+ export function updateLanguageSettings(updates) {
15
+ const settings = getLanguageSettings() ?? {};
16
+ const newSettings = {
17
+ ...settings,
18
+ ...updates
19
+ };
20
+ const json = JSON.stringify(newSettings);
21
+ CookieManager.set(VAADIN_LANGUAGE_SETTINGS_COOKIE_NAME, json);
20
22
  }
21
- export {
22
- VAADIN_LANGUAGE_SETTINGS_COOKIE_NAME,
23
- getLanguageSettings,
24
- updateLanguageSettings
25
- };
26
- //# sourceMappingURL=settings.js.map
23
+ //# sourceMappingURL=./settings.js.map
package/settings.js.map CHANGED
@@ -1,7 +1 @@
1
- {
2
- "version": 3,
3
- "sources": ["src/settings.ts"],
4
- "sourcesContent": ["import CookieManager from '@vaadin/hilla-frontend/CookieManager.js';\n\nexport const VAADIN_LANGUAGE_SETTINGS_COOKIE_NAME = 'vaadinLanguageSettings';\n\nexport interface LanguageSettings {\n language?: string;\n}\n\nexport function getLanguageSettings(): LanguageSettings | undefined {\n const cookie = CookieManager.get(VAADIN_LANGUAGE_SETTINGS_COOKIE_NAME);\n if (!cookie) return undefined;\n\n try {\n return JSON.parse(cookie);\n } catch (e) {\n // Ignore\n return undefined;\n }\n}\n\nexport function updateLanguageSettings(updates: Partial<LanguageSettings>): void {\n const settings = getLanguageSettings() ?? {};\n const newSettings = {\n ...settings,\n ...updates,\n };\n const json = JSON.stringify(newSettings);\n CookieManager.set(VAADIN_LANGUAGE_SETTINGS_COOKIE_NAME, json);\n}\n"],
5
- "mappings": "AAAA,OAAO,mBAAmB;AAEnB,MAAM,uCAAuC;AAM7C,SAAS,sBAAoD;AAClE,QAAM,SAAS,cAAc,IAAI,oCAAoC;AACrE,MAAI,CAAC,OAAQ,QAAO;AAEpB,MAAI;AACF,WAAO,KAAK,MAAM,MAAM;AAAA,EAC1B,SAAS,GAAG;AAEV,WAAO;AAAA,EACT;AACF;AAEO,SAAS,uBAAuB,SAA0C;AAC/E,QAAM,WAAW,oBAAoB,KAAK,CAAC;AAC3C,QAAM,cAAc;AAAA,IAClB,GAAG;AAAA,IACH,GAAG;AAAA,EACL;AACA,QAAM,OAAO,KAAK,UAAU,WAAW;AACvC,gBAAc,IAAI,sCAAsC,IAAI;AAC9D;",
6
- "names": []
7
- }
1
+ {"mappings":"AAAA,OAAO,4DAA6D;AAEpE,OAAO,MAAM,uCAAuC;AAMpD,OAAO,SAAS,sBAAoD;CAClE,MAAM,SAAS,cAAc,IAAI,qCAAqC;AACtE,MAAK,QAAQ;AACX,SAAO;CACR;AAED,KAAI;AACF,SAAO,KAAK,MAAM,OAAO;CAC1B,QAAO;AAEN,SAAO;CACR;AACF;AAED,OAAO,SAAS,uBAAuBA,SAA0C;CAC/E,MAAM,WAAW,qBAAqB,IAAI,CAAE;CAC5C,MAAM,cAAc;EAClB,GAAG;EACH,GAAG;CACJ;CACD,MAAM,OAAO,KAAK,UAAU,YAAY;AACxC,eAAc,IAAI,sCAAsC,KAAK;AAC9D","names":["updates: Partial<LanguageSettings>"],"sources":["/opt/agent/work/1af72d8adc613024/hilla/packages/ts/react-i18n/src/settings.ts"],"sourcesContent":["import CookieManager from '@vaadin/hilla-frontend/CookieManager.js';\n\nexport const VAADIN_LANGUAGE_SETTINGS_COOKIE_NAME = 'vaadinLanguageSettings';\n\nexport interface LanguageSettings {\n language?: string;\n}\n\nexport function getLanguageSettings(): LanguageSettings | undefined {\n const cookie = CookieManager.get(VAADIN_LANGUAGE_SETTINGS_COOKIE_NAME);\n if (!cookie) {\n return undefined;\n }\n\n try {\n return JSON.parse(cookie);\n } catch {\n // Ignore\n return undefined;\n }\n}\n\nexport function updateLanguageSettings(updates: Partial<LanguageSettings>): void {\n const settings = getLanguageSettings() ?? {};\n const newSettings = {\n ...settings,\n ...updates,\n };\n const json = JSON.stringify(newSettings);\n CookieManager.set(VAADIN_LANGUAGE_SETTINGS_COOKIE_NAME, json);\n}\n"],"version":3}
package/types.d.ts CHANGED
@@ -1,13 +1,12 @@
1
1
  export type Translations = Readonly<Record<string, string>>;
2
2
  export type TranslationsResult = Readonly<{
3
- translations: Translations;
4
- resolvedLanguage?: string;
3
+ translations: Translations
4
+ resolvedLanguage?: string
5
5
  }>;
6
6
  export interface I18nOptions {
7
- /**
8
- * Allows to explicitly set the initial language. Should be a valid
9
- * IETF BCP 47 language tag, such as `en` or `en-US`
10
- */
11
- language?: string;
7
+ /**
8
+ * Allows to explicitly set the initial language. Should be a valid
9
+ * IETF BCP 47 language tag, such as `en` or `en-US`
10
+ */
11
+ language?: string;
12
12
  }
13
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
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/backend.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["src/backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAgB,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEnE,MAAM,WAAW,WAAW;IAC1B,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;CAC7F;AAED,qBAAa,cAAe,YAAW,WAAW;IAC1C,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAiBlG"}
package/index.d.ts.map DELETED
@@ -1 +0,0 @@
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;AAwBhF,qBAAa,IAAI;;;IA8Bf;;;;;;;;OAQG;IACH,IAAI,WAAW,IAAI,cAAc,CAAC,OAAO,CAAC,CAEzC;IAED;;;;;OAKG;IACH,IAAI,QAAQ,IAAI,cAAc,CAAC,MAAM,GAAG,SAAS,CAAC,CAEjD;IAED;;;;;;;OAOG;IACH,IAAI,gBAAgB,IAAI,cAAc,CAAC,MAAM,GAAG,SAAS,CAAC,CAEzD;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,SAAS,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrD;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrD;;;;;;;OAOG;IACG,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAYvC,cAAc;IAqC5B;;;OAGG;YACW,kBAAkB;IAsBhC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM;CAQjE;AAED;;;GAGG;AACH,eAAO,MAAM,IAAI,MAAa,CAAC;AAE/B;;;;;;;;;;;;;;;;;;;;;;;GAuBG,CAAC,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAEnF"}
package/settings.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["src/settings.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,oCAAoC,2BAA2B,CAAC;AAE7E,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,SAAS,CAUlE;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAQ/E"}
package/types.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAE5D,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,YAAY,EAAE,YAAY,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC,CAAC;AAEH,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB"}
package/types.js DELETED
@@ -1 +0,0 @@
1
- //# sourceMappingURL=types.js.map
package/types.js.map DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": [],
4
- "sourcesContent": [],
5
- "mappings": "",
6
- "names": []
7
- }