@itwin/core-i18n 4.0.0-dev.48 → 4.0.0-dev.50

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.
@@ -1,100 +1,100 @@
1
- /** @packageDocumentation
2
- * @module Localization
3
- */
4
- import { i18n, InitOptions, Module, TOptionsBase } from "i18next";
5
- import { DetectorOptions } from "i18next-browser-languagedetector";
6
- import { BackendOptions } from "i18next-http-backend";
7
- import type { Localization } from "@itwin/core-common";
8
- /** Options for ITwinLocalization
9
- * @public
10
- */
11
- export interface LocalizationOptions {
12
- urlTemplate?: string;
13
- backendPlugin?: Module;
14
- detectorPlugin?: Module;
15
- initOptions?: InitOptions;
16
- backendHttpOptions?: BackendOptions;
17
- detectorOptions?: DetectorOptions;
18
- }
19
- /** Supplies localizations for iTwin.js
20
- * @note this class uses the [i18next](https://www.i18next.com/) package.
21
- * @public
22
- */
23
- export declare class ITwinLocalization implements Localization {
24
- i18next: i18n;
25
- private readonly _initOptions;
26
- private readonly _backendOptions;
27
- private readonly _detectionOptions;
28
- private readonly _namespaces;
29
- constructor(options?: LocalizationOptions);
30
- initialize(namespaces: string[]): Promise<void>;
31
- /** Replace all instances of `%{key}` within a string with the translations of those keys.
32
- * For example:
33
- * ``` ts
34
- * "MyKeys": {
35
- * "Key1": "First value",
36
- * "Key2": "Second value"
37
- * }
38
- * ```
39
- *
40
- * ``` ts
41
- * i18.translateKeys("string with %{MyKeys.Key1} followed by %{MyKeys.Key2}!"") // returns "string with First Value followed by Second Value!"
42
- * ```
43
- * @param line The input line, potentially containing %{keys}.
44
- * @returns The line with all %{keys} translated
45
- * @public
46
- */
47
- getLocalizedKeys(line: string): string;
48
- /** Return the translated value of a key.
49
- * @param key - the key that matches a property in the JSON localization file.
50
- * @note The key includes the namespace, which identifies the particular localization file that contains the property,
51
- * followed by a colon, followed by the property in the JSON file.
52
- * For example:
53
- * ``` ts
54
- * const dataString: string = IModelApp.localization.getLocalizedString("iModelJs:BackgroundMap.BingDataAttribution");
55
- * ```
56
- * assigns to dataString the string with property BackgroundMap.BingDataAttribution from the iModelJs.json localization file.
57
- * @returns The string corresponding to the first key that resolves.
58
- * @throws Error if no keys resolve to a string.
59
- * @public
60
- */
61
- getLocalizedString(key: string | string[], options?: TOptionsBase): string;
62
- /** Similar to `getLocalizedString` but the default namespace is a separate parameter and the key does not need
63
- * to include a namespace. If a key includes a namespace, that namespace will be used for interpolating that key.
64
- * @param namespace - the namespace that identifies the particular localization file that contains the property.
65
- * @param key - the key that matches a property in the JSON localization file.
66
- * @returns The string corresponding to the first key that resolves.
67
- * @throws Error if no keys resolve to a string.
68
- * @internal
69
- */
70
- getLocalizedStringWithNamespace(namespace: string, key: string | string[], options?: TOptionsBase): string;
71
- /** Gets the English translation.
72
- * @param namespace - the namespace that identifies the particular localization file that contains the property.
73
- * @param key - the key that matches a property in the JSON localization file.
74
- * @returns The string corresponding to the first key that resolves.
75
- * @throws Error if no keys resolve to a string.
76
- * @internal
77
- */
78
- getEnglishString(namespace: string, key: string | string[], options?: TOptionsBase): string;
79
- /** Get the promise for an already registered Namespace.
80
- * @param name - the name of the namespace
81
- * @public
82
- */
83
- getNamespacePromise(name: string): Promise<void> | undefined;
84
- /** @internal */
85
- getLanguageList(): readonly string[];
86
- /** override the language detected in the browser */
87
- changeLanguage(language: string): Promise<void>;
88
- /** Register a new Namespace and return it. If the namespace is already registered, it will be returned.
89
- * @param name - the name of the namespace, which is the base name of the JSON file that contains the localization properties.
90
- * @note - The registerNamespace method starts fetching the appropriate version of the JSON localization file from the server,
91
- * based on the current locale. To make sure that fetch is complete before performing translations from this namespace, await
92
- * fulfillment of the readPromise Promise property of the returned LocalizationNamespace.
93
- * @see [Localization in iTwin.js]($docs/learning/frontend/Localization.md)
94
- * @public
95
- */
96
- registerNamespace(name: string): Promise<void>;
97
- /** @internal */
98
- unregisterNamespace(name: string): void;
99
- }
1
+ /** @packageDocumentation
2
+ * @module Localization
3
+ */
4
+ import { i18n, InitOptions, Module, TOptionsBase } from "i18next";
5
+ import { DetectorOptions } from "i18next-browser-languagedetector";
6
+ import { BackendOptions } from "i18next-http-backend";
7
+ import type { Localization } from "@itwin/core-common";
8
+ /** Options for ITwinLocalization
9
+ * @public
10
+ */
11
+ export interface LocalizationOptions {
12
+ urlTemplate?: string;
13
+ backendPlugin?: Module;
14
+ detectorPlugin?: Module;
15
+ initOptions?: InitOptions;
16
+ backendHttpOptions?: BackendOptions;
17
+ detectorOptions?: DetectorOptions;
18
+ }
19
+ /** Supplies localizations for iTwin.js
20
+ * @note this class uses the [i18next](https://www.i18next.com/) package.
21
+ * @public
22
+ */
23
+ export declare class ITwinLocalization implements Localization {
24
+ i18next: i18n;
25
+ private readonly _initOptions;
26
+ private readonly _backendOptions;
27
+ private readonly _detectionOptions;
28
+ private readonly _namespaces;
29
+ constructor(options?: LocalizationOptions);
30
+ initialize(namespaces: string[]): Promise<void>;
31
+ /** Replace all instances of `%{key}` within a string with the translations of those keys.
32
+ * For example:
33
+ * ``` ts
34
+ * "MyKeys": {
35
+ * "Key1": "First value",
36
+ * "Key2": "Second value"
37
+ * }
38
+ * ```
39
+ *
40
+ * ``` ts
41
+ * i18.translateKeys("string with %{MyKeys.Key1} followed by %{MyKeys.Key2}!"") // returns "string with First Value followed by Second Value!"
42
+ * ```
43
+ * @param line The input line, potentially containing %{keys}.
44
+ * @returns The line with all %{keys} translated
45
+ * @public
46
+ */
47
+ getLocalizedKeys(line: string): string;
48
+ /** Return the translated value of a key.
49
+ * @param key - the key that matches a property in the JSON localization file.
50
+ * @note The key includes the namespace, which identifies the particular localization file that contains the property,
51
+ * followed by a colon, followed by the property in the JSON file.
52
+ * For example:
53
+ * ``` ts
54
+ * const dataString: string = IModelApp.localization.getLocalizedString("iModelJs:BackgroundMap.BingDataAttribution");
55
+ * ```
56
+ * assigns to dataString the string with property BackgroundMap.BingDataAttribution from the iModelJs.json localization file.
57
+ * @returns The string corresponding to the first key that resolves.
58
+ * @throws Error if no keys resolve to a string.
59
+ * @public
60
+ */
61
+ getLocalizedString(key: string | string[], options?: TOptionsBase): string;
62
+ /** Similar to `getLocalizedString` but the default namespace is a separate parameter and the key does not need
63
+ * to include a namespace. If a key includes a namespace, that namespace will be used for interpolating that key.
64
+ * @param namespace - the namespace that identifies the particular localization file that contains the property.
65
+ * @param key - the key that matches a property in the JSON localization file.
66
+ * @returns The string corresponding to the first key that resolves.
67
+ * @throws Error if no keys resolve to a string.
68
+ * @internal
69
+ */
70
+ getLocalizedStringWithNamespace(namespace: string, key: string | string[], options?: TOptionsBase): string;
71
+ /** Gets the English translation.
72
+ * @param namespace - the namespace that identifies the particular localization file that contains the property.
73
+ * @param key - the key that matches a property in the JSON localization file.
74
+ * @returns The string corresponding to the first key that resolves.
75
+ * @throws Error if no keys resolve to a string.
76
+ * @internal
77
+ */
78
+ getEnglishString(namespace: string, key: string | string[], options?: TOptionsBase): string;
79
+ /** Get the promise for an already registered Namespace.
80
+ * @param name - the name of the namespace
81
+ * @public
82
+ */
83
+ getNamespacePromise(name: string): Promise<void> | undefined;
84
+ /** @internal */
85
+ getLanguageList(): readonly string[];
86
+ /** override the language detected in the browser */
87
+ changeLanguage(language: string): Promise<void>;
88
+ /** Register a new Namespace and return it. If the namespace is already registered, it will be returned.
89
+ * @param name - the name of the namespace, which is the base name of the JSON file that contains the localization properties.
90
+ * @note - The registerNamespace method starts fetching the appropriate version of the JSON localization file from the server,
91
+ * based on the current locale. To make sure that fetch is complete before performing translations from this namespace, await
92
+ * fulfillment of the readPromise Promise property of the returned LocalizationNamespace.
93
+ * @see [Localization in iTwin.js]($docs/learning/frontend/Localization.md)
94
+ * @public
95
+ */
96
+ registerNamespace(name: string): Promise<void>;
97
+ /** @internal */
98
+ unregisterNamespace(name: string): void;
99
+ }
100
100
  //# sourceMappingURL=ITwinLocalization.d.ts.map
@@ -1,223 +1,223 @@
1
- /*---------------------------------------------------------------------------------------------
2
- * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
- * See LICENSE.md in the project root for license terms and full copyright notice.
4
- *--------------------------------------------------------------------------------------------*/
5
- /** @packageDocumentation
6
- * @module Localization
7
- */
8
- import i18next from "i18next";
9
- import i18nextBrowserLanguageDetector from "i18next-browser-languagedetector";
10
- import Backend from "i18next-http-backend";
11
- import { Logger } from "@itwin/core-bentley";
12
- const DEFAULT_MAX_RETRIES = 1; // a low number prevents wasted time and potential timeouts when requesting localization files throws an error
13
- /** Supplies localizations for iTwin.js
14
- * @note this class uses the [i18next](https://www.i18next.com/) package.
15
- * @public
16
- */
17
- export class ITwinLocalization {
18
- constructor(options) {
19
- this._namespaces = new Map();
20
- this.i18next = i18next.createInstance();
21
- this._backendOptions = {
22
- loadPath: options?.urlTemplate ?? "locales/{{lng}}/{{ns}}.json",
23
- crossDomain: true,
24
- ...options?.backendHttpOptions,
25
- };
26
- this._detectionOptions = {
27
- order: ["querystring", "navigator", "htmlTag"],
28
- lookupQuerystring: "lng",
29
- caches: [],
30
- ...options?.detectorOptions,
31
- };
32
- this._initOptions = {
33
- interpolation: { escapeValue: true },
34
- fallbackLng: "en",
35
- maxRetries: DEFAULT_MAX_RETRIES,
36
- backend: this._backendOptions,
37
- detection: this._detectionOptions,
38
- ...options?.initOptions,
39
- };
40
- this.i18next
41
- .use(options?.detectorPlugin ?? i18nextBrowserLanguageDetector)
42
- .use(options?.backendPlugin ?? Backend)
43
- .use(TranslationLogger);
44
- }
45
- async initialize(namespaces) {
46
- // Also consider namespaces passed into constructor
47
- const initNamespaces = [this._initOptions.ns || []].flat();
48
- const combinedNamespaces = new Set([...namespaces, ...initNamespaces]); // without duplicates
49
- const defaultNamespace = this._initOptions.defaultNS ?? namespaces[0];
50
- if (defaultNamespace)
51
- combinedNamespaces.add(defaultNamespace); // Make sure default namespace is in namespaces list
52
- const initOptions = {
53
- ...this._initOptions,
54
- defaultNS: defaultNamespace,
55
- ns: [...combinedNamespaces],
56
- };
57
- // if in a development environment, set debugging
58
- if (process.env.NODE_ENV === "development")
59
- initOptions.debug = true;
60
- const initPromise = this.i18next.init(initOptions);
61
- for (const ns of namespaces)
62
- this._namespaces.set(ns, initPromise);
63
- return initPromise;
64
- }
65
- /** Replace all instances of `%{key}` within a string with the translations of those keys.
66
- * For example:
67
- * ``` ts
68
- * "MyKeys": {
69
- * "Key1": "First value",
70
- * "Key2": "Second value"
71
- * }
72
- * ```
73
- *
74
- * ``` ts
75
- * i18.translateKeys("string with %{MyKeys.Key1} followed by %{MyKeys.Key2}!"") // returns "string with First Value followed by Second Value!"
76
- * ```
77
- * @param line The input line, potentially containing %{keys}.
78
- * @returns The line with all %{keys} translated
79
- * @public
80
- */
81
- getLocalizedKeys(line) {
82
- return line.replace(/\%\{(.+?)\}/g, (_match, tag) => this.getLocalizedString(tag));
83
- }
84
- /** Return the translated value of a key.
85
- * @param key - the key that matches a property in the JSON localization file.
86
- * @note The key includes the namespace, which identifies the particular localization file that contains the property,
87
- * followed by a colon, followed by the property in the JSON file.
88
- * For example:
89
- * ``` ts
90
- * const dataString: string = IModelApp.localization.getLocalizedString("iModelJs:BackgroundMap.BingDataAttribution");
91
- * ```
92
- * assigns to dataString the string with property BackgroundMap.BingDataAttribution from the iModelJs.json localization file.
93
- * @returns The string corresponding to the first key that resolves.
94
- * @throws Error if no keys resolve to a string.
95
- * @public
96
- */
97
- getLocalizedString(key, options) {
98
- if (options?.returnDetails || options?.returnObjects) {
99
- throw new Error("Translation key must map to a string, but the given options will result in an object");
100
- }
101
- const value = this.i18next.t(key, options);
102
- if (typeof value !== "string") {
103
- throw new Error("Translation key(s) string not found");
104
- }
105
- return value;
106
- }
107
- /** Similar to `getLocalizedString` but the default namespace is a separate parameter and the key does not need
108
- * to include a namespace. If a key includes a namespace, that namespace will be used for interpolating that key.
109
- * @param namespace - the namespace that identifies the particular localization file that contains the property.
110
- * @param key - the key that matches a property in the JSON localization file.
111
- * @returns The string corresponding to the first key that resolves.
112
- * @throws Error if no keys resolve to a string.
113
- * @internal
114
- */
115
- getLocalizedStringWithNamespace(namespace, key, options) {
116
- let fullKey = "";
117
- if (typeof key === "string") {
118
- fullKey = `${namespace}:${key}`;
119
- }
120
- else {
121
- fullKey = key.map((subKey) => {
122
- return `${namespace}:${subKey}`;
123
- });
124
- }
125
- return this.getLocalizedString(fullKey, options);
126
- }
127
- /** Gets the English translation.
128
- * @param namespace - the namespace that identifies the particular localization file that contains the property.
129
- * @param key - the key that matches a property in the JSON localization file.
130
- * @returns The string corresponding to the first key that resolves.
131
- * @throws Error if no keys resolve to a string.
132
- * @internal
133
- */
134
- getEnglishString(namespace, key, options) {
135
- if (options?.returnDetails || options?.returnObjects) {
136
- throw new Error("Translation key must map to a string, but the given options will result in an object");
137
- }
138
- options = {
139
- ...options,
140
- ns: namespace, // ensure namespace argument is used
141
- };
142
- const en = this.i18next.getFixedT("en", namespace);
143
- const str = en(key, options);
144
- if (typeof str !== "string")
145
- throw new Error("Translation key(s) not found");
146
- return str;
147
- }
148
- /** Get the promise for an already registered Namespace.
149
- * @param name - the name of the namespace
150
- * @public
151
- */
152
- getNamespacePromise(name) {
153
- return this._namespaces.get(name);
154
- }
155
- /** @internal */
156
- getLanguageList() {
157
- return this.i18next.languages;
158
- }
159
- /** override the language detected in the browser */
160
- async changeLanguage(language) {
161
- return this.i18next.changeLanguage(language);
162
- }
163
- /** Register a new Namespace and return it. If the namespace is already registered, it will be returned.
164
- * @param name - the name of the namespace, which is the base name of the JSON file that contains the localization properties.
165
- * @note - The registerNamespace method starts fetching the appropriate version of the JSON localization file from the server,
166
- * based on the current locale. To make sure that fetch is complete before performing translations from this namespace, await
167
- * fulfillment of the readPromise Promise property of the returned LocalizationNamespace.
168
- * @see [Localization in iTwin.js]($docs/learning/frontend/Localization.md)
169
- * @public
170
- */
171
- async registerNamespace(name) {
172
- const existing = this._namespaces.get(name);
173
- if (existing !== undefined)
174
- return existing;
175
- const theReadPromise = new Promise((resolve) => {
176
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
177
- this.i18next.loadNamespaces(name, (err) => {
178
- if (!err)
179
- return resolve();
180
- // Here we got a non-null err object.
181
- // This method is called when the system has attempted to load the resources for the namespaces for each possible locale.
182
- // For example 'fr-ca' might be the most specific locale, in which case 'fr' and 'en' are fallback locales.
183
- // Using Backend from i18next-http-backend, err will be an array of strings of each namespace it tried to read and its locale.
184
- // There might be errs for some other namespaces as well as this one. We resolve the promise unless there's an error for each possible locale.
185
- let locales = this.getLanguageList().map((thisLocale) => `/${thisLocale}/`);
186
- try {
187
- for (const thisError of err) {
188
- if (typeof thisError === "string")
189
- locales = locales.filter((thisLocale) => !thisError.includes(thisLocale));
190
- }
191
- }
192
- catch (e) {
193
- locales = [];
194
- }
195
- // if we removed every locale from the array, it wasn't loaded.
196
- if (locales.length === 0)
197
- Logger.logError("i18n", `No resources for namespace ${name} could be loaded`);
198
- resolve();
199
- });
200
- });
201
- this._namespaces.set(name, theReadPromise);
202
- return theReadPromise;
203
- }
204
- /** @internal */
205
- unregisterNamespace(name) {
206
- this._namespaces.delete(name);
207
- }
208
- }
209
- class TranslationLogger {
210
- log(args) { Logger.logInfo("i18n", this.createLogMessage(args)); }
211
- warn(args) { Logger.logWarning("i18n", this.createLogMessage(args)); }
212
- error(args) { Logger.logError("i18n", this.createLogMessage(args)); }
213
- createLogMessage(args) {
214
- let message = args[0];
215
- for (let i = 1; i < args.length; ++i) {
216
- if (typeof args[i] === "string")
217
- message += `\n ${args[i]}`;
218
- }
219
- return message;
220
- }
221
- }
222
- TranslationLogger.type = "logger";
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
+ * See LICENSE.md in the project root for license terms and full copyright notice.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ /** @packageDocumentation
6
+ * @module Localization
7
+ */
8
+ import i18next from "i18next";
9
+ import i18nextBrowserLanguageDetector from "i18next-browser-languagedetector";
10
+ import Backend from "i18next-http-backend";
11
+ import { Logger } from "@itwin/core-bentley";
12
+ const DEFAULT_MAX_RETRIES = 1; // a low number prevents wasted time and potential timeouts when requesting localization files throws an error
13
+ /** Supplies localizations for iTwin.js
14
+ * @note this class uses the [i18next](https://www.i18next.com/) package.
15
+ * @public
16
+ */
17
+ export class ITwinLocalization {
18
+ constructor(options) {
19
+ this._namespaces = new Map();
20
+ this.i18next = i18next.createInstance();
21
+ this._backendOptions = {
22
+ loadPath: options?.urlTemplate ?? "locales/{{lng}}/{{ns}}.json",
23
+ crossDomain: true,
24
+ ...options?.backendHttpOptions,
25
+ };
26
+ this._detectionOptions = {
27
+ order: ["querystring", "navigator", "htmlTag"],
28
+ lookupQuerystring: "lng",
29
+ caches: [],
30
+ ...options?.detectorOptions,
31
+ };
32
+ this._initOptions = {
33
+ interpolation: { escapeValue: true },
34
+ fallbackLng: "en",
35
+ maxRetries: DEFAULT_MAX_RETRIES,
36
+ backend: this._backendOptions,
37
+ detection: this._detectionOptions,
38
+ ...options?.initOptions,
39
+ };
40
+ this.i18next
41
+ .use(options?.detectorPlugin ?? i18nextBrowserLanguageDetector)
42
+ .use(options?.backendPlugin ?? Backend)
43
+ .use(TranslationLogger);
44
+ }
45
+ async initialize(namespaces) {
46
+ // Also consider namespaces passed into constructor
47
+ const initNamespaces = [this._initOptions.ns || []].flat();
48
+ const combinedNamespaces = new Set([...namespaces, ...initNamespaces]); // without duplicates
49
+ const defaultNamespace = this._initOptions.defaultNS ?? namespaces[0];
50
+ if (defaultNamespace)
51
+ combinedNamespaces.add(defaultNamespace); // Make sure default namespace is in namespaces list
52
+ const initOptions = {
53
+ ...this._initOptions,
54
+ defaultNS: defaultNamespace,
55
+ ns: [...combinedNamespaces],
56
+ };
57
+ // if in a development environment, set debugging
58
+ if (process.env.NODE_ENV === "development")
59
+ initOptions.debug = true;
60
+ const initPromise = this.i18next.init(initOptions);
61
+ for (const ns of namespaces)
62
+ this._namespaces.set(ns, initPromise);
63
+ return initPromise;
64
+ }
65
+ /** Replace all instances of `%{key}` within a string with the translations of those keys.
66
+ * For example:
67
+ * ``` ts
68
+ * "MyKeys": {
69
+ * "Key1": "First value",
70
+ * "Key2": "Second value"
71
+ * }
72
+ * ```
73
+ *
74
+ * ``` ts
75
+ * i18.translateKeys("string with %{MyKeys.Key1} followed by %{MyKeys.Key2}!"") // returns "string with First Value followed by Second Value!"
76
+ * ```
77
+ * @param line The input line, potentially containing %{keys}.
78
+ * @returns The line with all %{keys} translated
79
+ * @public
80
+ */
81
+ getLocalizedKeys(line) {
82
+ return line.replace(/\%\{(.+?)\}/g, (_match, tag) => this.getLocalizedString(tag));
83
+ }
84
+ /** Return the translated value of a key.
85
+ * @param key - the key that matches a property in the JSON localization file.
86
+ * @note The key includes the namespace, which identifies the particular localization file that contains the property,
87
+ * followed by a colon, followed by the property in the JSON file.
88
+ * For example:
89
+ * ``` ts
90
+ * const dataString: string = IModelApp.localization.getLocalizedString("iModelJs:BackgroundMap.BingDataAttribution");
91
+ * ```
92
+ * assigns to dataString the string with property BackgroundMap.BingDataAttribution from the iModelJs.json localization file.
93
+ * @returns The string corresponding to the first key that resolves.
94
+ * @throws Error if no keys resolve to a string.
95
+ * @public
96
+ */
97
+ getLocalizedString(key, options) {
98
+ if (options?.returnDetails || options?.returnObjects) {
99
+ throw new Error("Translation key must map to a string, but the given options will result in an object");
100
+ }
101
+ const value = this.i18next.t(key, options);
102
+ if (typeof value !== "string") {
103
+ throw new Error("Translation key(s) string not found");
104
+ }
105
+ return value;
106
+ }
107
+ /** Similar to `getLocalizedString` but the default namespace is a separate parameter and the key does not need
108
+ * to include a namespace. If a key includes a namespace, that namespace will be used for interpolating that key.
109
+ * @param namespace - the namespace that identifies the particular localization file that contains the property.
110
+ * @param key - the key that matches a property in the JSON localization file.
111
+ * @returns The string corresponding to the first key that resolves.
112
+ * @throws Error if no keys resolve to a string.
113
+ * @internal
114
+ */
115
+ getLocalizedStringWithNamespace(namespace, key, options) {
116
+ let fullKey = "";
117
+ if (typeof key === "string") {
118
+ fullKey = `${namespace}:${key}`;
119
+ }
120
+ else {
121
+ fullKey = key.map((subKey) => {
122
+ return `${namespace}:${subKey}`;
123
+ });
124
+ }
125
+ return this.getLocalizedString(fullKey, options);
126
+ }
127
+ /** Gets the English translation.
128
+ * @param namespace - the namespace that identifies the particular localization file that contains the property.
129
+ * @param key - the key that matches a property in the JSON localization file.
130
+ * @returns The string corresponding to the first key that resolves.
131
+ * @throws Error if no keys resolve to a string.
132
+ * @internal
133
+ */
134
+ getEnglishString(namespace, key, options) {
135
+ if (options?.returnDetails || options?.returnObjects) {
136
+ throw new Error("Translation key must map to a string, but the given options will result in an object");
137
+ }
138
+ options = {
139
+ ...options,
140
+ ns: namespace, // ensure namespace argument is used
141
+ };
142
+ const en = this.i18next.getFixedT("en", namespace);
143
+ const str = en(key, options);
144
+ if (typeof str !== "string")
145
+ throw new Error("Translation key(s) not found");
146
+ return str;
147
+ }
148
+ /** Get the promise for an already registered Namespace.
149
+ * @param name - the name of the namespace
150
+ * @public
151
+ */
152
+ getNamespacePromise(name) {
153
+ return this._namespaces.get(name);
154
+ }
155
+ /** @internal */
156
+ getLanguageList() {
157
+ return this.i18next.languages;
158
+ }
159
+ /** override the language detected in the browser */
160
+ async changeLanguage(language) {
161
+ return this.i18next.changeLanguage(language);
162
+ }
163
+ /** Register a new Namespace and return it. If the namespace is already registered, it will be returned.
164
+ * @param name - the name of the namespace, which is the base name of the JSON file that contains the localization properties.
165
+ * @note - The registerNamespace method starts fetching the appropriate version of the JSON localization file from the server,
166
+ * based on the current locale. To make sure that fetch is complete before performing translations from this namespace, await
167
+ * fulfillment of the readPromise Promise property of the returned LocalizationNamespace.
168
+ * @see [Localization in iTwin.js]($docs/learning/frontend/Localization.md)
169
+ * @public
170
+ */
171
+ async registerNamespace(name) {
172
+ const existing = this._namespaces.get(name);
173
+ if (existing !== undefined)
174
+ return existing;
175
+ const theReadPromise = new Promise((resolve) => {
176
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
177
+ this.i18next.loadNamespaces(name, (err) => {
178
+ if (!err)
179
+ return resolve();
180
+ // Here we got a non-null err object.
181
+ // This method is called when the system has attempted to load the resources for the namespaces for each possible locale.
182
+ // For example 'fr-ca' might be the most specific locale, in which case 'fr' and 'en' are fallback locales.
183
+ // Using Backend from i18next-http-backend, err will be an array of strings of each namespace it tried to read and its locale.
184
+ // There might be errs for some other namespaces as well as this one. We resolve the promise unless there's an error for each possible locale.
185
+ let locales = this.getLanguageList().map((thisLocale) => `/${thisLocale}/`);
186
+ try {
187
+ for (const thisError of err) {
188
+ if (typeof thisError === "string")
189
+ locales = locales.filter((thisLocale) => !thisError.includes(thisLocale));
190
+ }
191
+ }
192
+ catch (e) {
193
+ locales = [];
194
+ }
195
+ // if we removed every locale from the array, it wasn't loaded.
196
+ if (locales.length === 0)
197
+ Logger.logError("i18n", `No resources for namespace ${name} could be loaded`);
198
+ resolve();
199
+ });
200
+ });
201
+ this._namespaces.set(name, theReadPromise);
202
+ return theReadPromise;
203
+ }
204
+ /** @internal */
205
+ unregisterNamespace(name) {
206
+ this._namespaces.delete(name);
207
+ }
208
+ }
209
+ class TranslationLogger {
210
+ log(args) { Logger.logInfo("i18n", this.createLogMessage(args)); }
211
+ warn(args) { Logger.logWarning("i18n", this.createLogMessage(args)); }
212
+ error(args) { Logger.logError("i18n", this.createLogMessage(args)); }
213
+ createLogMessage(args) {
214
+ let message = args[0];
215
+ for (let i = 1; i < args.length; ++i) {
216
+ if (typeof args[i] === "string")
217
+ message += `\n ${args[i]}`;
218
+ }
219
+ return message;
220
+ }
221
+ }
222
+ TranslationLogger.type = "logger";
223
223
  //# sourceMappingURL=ITwinLocalization.js.map