@aidc-toolkit/core 0.9.7-beta → 0.9.9-beta

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/LICENSE CHANGED
@@ -172,30 +172,3 @@
172
172
  defend, and hold each Contributor harmless for any liability
173
173
  incurred by, or claims asserted against, such Contributor by reason
174
174
  of your accepting any such warranty or additional liability.
175
-
176
- END OF TERMS AND CONDITIONS
177
-
178
- APPENDIX: How to apply the Apache License to your work.
179
-
180
- To apply the Apache License to your work, attach the following
181
- boilerplate notice, with the fields enclosed by brackets "[]"
182
- replaced with your own identifying information. (Don't include
183
- the brackets!) The text should be enclosed in the appropriate
184
- comment syntax for the file format. We also recommend that a
185
- file or class name and description of purpose be included on the
186
- same "printed page" as the copyright notice for easier
187
- identification within third-party archives.
188
-
189
- Copyright © 2024 Dolphin Data Development Ltd.
190
-
191
- Licensed under the Apache License, Version 2.0 (the "License");
192
- you may not use this file except in compliance with the License.
193
- You may obtain a copy of the License at
194
-
195
- http://www.apache.org/licenses/LICENSE-2.0
196
-
197
- Unless required by applicable law or agreed to in writing, software
198
- distributed under the License is distributed on an "AS IS" BASIS,
199
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
- See the License for the specific language governing permissions and
201
- limitations under the License.
package/README.md CHANGED
@@ -1,3 +1,20 @@
1
+ # Core Package
2
+
3
+ **Copyright © 2024-2025 Dolphin Data Development Ltd. and AIDC Toolkit contributors**
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
6
+ License. You may obtain a copy of the License at
7
+
8
+ https://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
11
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
12
+
13
+ ## Overview
14
+
15
+ ⚠️ **This software is in beta**, with production release is scheduled for 2024Q4. To follow the status of that and other
16
+ projects, go to the AIDC Toolkit [projects](https://github.com/orgs/aidc-toolkit/projects) page.
17
+
1
18
  The AIDC Toolkit `core` package contains artefacts to support other AIDC Toolkit packages; it does not itself provide
2
19
  any of the functionality of the AIDC Toolkit. It is a required dependency for all AIDC Toolkit packages.
3
20
 
@@ -5,29 +22,81 @@ any of the functionality of the AIDC Toolkit. It is a required dependency for al
5
22
 
6
23
  All AIDC Toolkit packages require localization. The localization functionality in this package simplifies initialization
7
24
  and allows packages to share a common internationalization engine, whose initialization is the responsibility of the
8
- client application. Packages install their resources as follows in `i18n.ts` or similar:
25
+ client application. Each package requires its own internationalization object and each is responsible for initializing
26
+ those of its dependencies.
27
+
28
+ Packages install their resources as follows in `i18n.ts` or similar:
9
29
 
10
30
  ```typescript
11
- import { i18nAddResourceBundle, i18next } from "@aidc-toolkit/core";
12
- import { localeStrings as enLocaleStrings } from "./en/locale_strings.js";
31
+ import { i18nAssertValidResources, i18nCoreInit, type I18NEnvironment } from "@aidc-toolkit/core";
32
+ import { i18nDependency1Init, dependency1Resources } from "@aidc-toolkit/dependency1";
33
+ import { i18nDependency2Init, dependency2Resources } from "@aidc-toolkit/dependency2";
34
+ import i18next from "i18next";
35
+ import { localeStrings as enLocaleStrings } from "./en/locale-strings.js";
36
+ import { localeStrings as frLocaleStrings } from "./fr/locale-strings.js";
13
37
 
14
38
  export const packageNS = "aidct_package";
15
39
 
16
- i18nAddResourceBundle("en", packageNS, enLocaleStrings);
40
+ /**
41
+ * Locale strings type is extracted from the English locale strings object.
42
+ */
43
+ export type PackageLocaleStrings = typeof enLocaleStrings;
44
+
45
+ i18nAssertValidResources(enLocaleStrings, "fr", frLocaleStrings);
46
+
47
+ /**
48
+ * Package resources.
49
+ */
50
+ export const packageResources = {
51
+ en: {
52
+ aidct_package: enLocaleStrings
53
+ },
54
+ fr: {
55
+ aidct_package: frLocaleStrings
56
+ }
57
+ };
58
+
59
+ export const i18nextPackage = i18next.createInstance();
17
60
 
18
- export default i18next;
61
+ /**
62
+ * Initialize internationalization.
63
+ *
64
+ * @param environment
65
+ * Environment in which the application is running.
66
+ *
67
+ * @param debug
68
+ * Debug setting.
69
+ *
70
+ * @returns
71
+ * Void promise.
72
+ */
73
+ export async function i18nPackageInit(environment: I18NEnvironment, debug = false): Promise<void> {
74
+ await i18nDependency1Init(environment, debug);
75
+ await i18nDependency2Init(environment, debug);
76
+ await i18nCoreInit(i18nextPackage, environment, debug, packageNS, dependency1Resources, dependency2Resources, packageResources);
77
+ }
19
78
  ```
20
79
 
21
80
  The resource types are declared in `i18next.d.ts` or similar:
22
81
 
23
82
  ```typescript
24
- import type { localeStrings } from "./en/locale_strings.js";
83
+ import type { Dependency1LocaleStrings } from "@aidc-toolkit/dependency1";
84
+ import type { Dependency2LocaleStrings } from "@aidc-toolkit/dependency2";
85
+ import type { PackageLocaleStrings } from "./i18n.js";
25
86
 
87
+ /**
88
+ * Internationalization module.
89
+ */
26
90
  declare module "i18next" {
91
+ /**
92
+ * Custom type options for this package.
93
+ */
27
94
  interface CustomTypeOptions {
95
+ defaultNS: "aidct_package";
28
96
  resources: {
29
- // Extract the type from the English locale strings object.
30
- aidct_package: typeof localeStrings;
97
+ aidct_dependency1: Dependency1LocaleStrings;
98
+ aidct_dependency2: Dependency2LocaleStrings;
99
+ aidct_package: PackageLocaleStrings;
31
100
  };
32
101
  }
33
102
  }
package/dist/index.cjs CHANGED
@@ -28,74 +28,23 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
 
30
30
  // src/index.ts
31
- var src_exports = {};
32
- __export(src_exports, {
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
33
  I18NEnvironment: () => I18NEnvironment,
34
- i18nAddResourceBundle: () => i18nAddResourceBundle,
35
34
  i18nAssertValidResources: () => i18nAssertValidResources,
36
- i18nInit: () => i18nInit,
37
- i18next: () => i18n_default
35
+ i18nCoreInit: () => i18nCoreInit
38
36
  });
39
- module.exports = __toCommonJS(src_exports);
37
+ module.exports = __toCommonJS(index_exports);
40
38
 
41
39
  // src/locale/i18n.ts
42
- var import_i18next = __toESM(require("i18next"), 1);
43
40
  var import_i18next_browser_languagedetector = __toESM(require("i18next-browser-languagedetector"), 1);
44
41
  var import_i18next_cli_language_detector = __toESM(require("i18next-cli-language-detector"), 1);
45
- var i18n_default = import_i18next.default;
46
- var pendingResourceBundles = [];
47
42
  var I18NEnvironment = /* @__PURE__ */ ((I18NEnvironment2) => {
48
43
  I18NEnvironment2[I18NEnvironment2["CLI"] = 0] = "CLI";
49
44
  I18NEnvironment2[I18NEnvironment2["Server"] = 1] = "Server";
50
45
  I18NEnvironment2[I18NEnvironment2["Browser"] = 2] = "Browser";
51
46
  return I18NEnvironment2;
52
47
  })(I18NEnvironment || {});
53
- function toLowerCase(s) {
54
- return s.split(" ").map((word) => /[a-z]/.test(word) ? word.toLowerCase() : word).join(" ");
55
- }
56
- async function i18nInit(environment, debug = false) {
57
- let initialized;
58
- if (pendingResourceBundles !== void 0) {
59
- initialized = true;
60
- let module2;
61
- switch (environment) {
62
- case 0 /* CLI */:
63
- module2 = import_i18next_cli_language_detector.default;
64
- break;
65
- case 2 /* Browser */:
66
- module2 = import_i18next_browser_languagedetector.default;
67
- break;
68
- default:
69
- throw new Error("Not supported");
70
- }
71
- const initResourceBundles = pendingResourceBundles;
72
- pendingResourceBundles = void 0;
73
- await import_i18next.default.use(module2).init({
74
- fallbackLng: "en",
75
- debug,
76
- resources: {}
77
- }).then(() => {
78
- import_i18next.default.services.formatter?.add("toLowerCase", (value) => typeof value === "string" ? toLowerCase(value) : String(value));
79
- for (const initResourceBundle of initResourceBundles) {
80
- i18nAddResourceBundle(initResourceBundle.lng, initResourceBundle.ns, initResourceBundle.resources);
81
- }
82
- });
83
- } else {
84
- initialized = false;
85
- }
86
- return initialized;
87
- }
88
- function i18nAddResourceBundle(lng, ns, resources) {
89
- if (pendingResourceBundles !== void 0) {
90
- pendingResourceBundles.push({
91
- lng,
92
- ns,
93
- resources
94
- });
95
- } else {
96
- import_i18next.default.addResourceBundle(lng, ns, resources);
97
- }
98
- }
99
48
  function i18nAssertValidResources(enResources, lng, lngResources, parent) {
100
49
  const enResourcesMap = new Map(Object.entries(enResources));
101
50
  const lngResourcesMap = new Map(Object.entries(lngResources));
@@ -121,11 +70,63 @@ function i18nAssertValidResources(enResources, lng, lngResources, parent) {
121
70
  }
122
71
  }
123
72
  }
73
+ function toLowerCase(s) {
74
+ return s.split(" ").map((word) => /[a-z]/.test(word) ? word.toLowerCase() : word).join(" ");
75
+ }
76
+ async function i18nCoreInit(i18next, environment, debug, defaultNS, ...resources) {
77
+ if (!i18next.isInitialized) {
78
+ const mergedResource = {};
79
+ for (const resource of resources) {
80
+ for (const [language, resourceLanguage] of Object.entries(resource)) {
81
+ if (!(language in mergedResource)) {
82
+ mergedResource[language] = {};
83
+ }
84
+ const mergedResourceLanguage = mergedResource[language];
85
+ for (const [namespace, resourceKey] of Object.entries(resourceLanguage)) {
86
+ mergedResourceLanguage[namespace] = resourceKey;
87
+ }
88
+ }
89
+ }
90
+ let module2;
91
+ switch (environment) {
92
+ case 0 /* CLI */:
93
+ module2 = import_i18next_cli_language_detector.default;
94
+ break;
95
+ case 2 /* Browser */:
96
+ module2 = import_i18next_browser_languagedetector.default;
97
+ break;
98
+ default:
99
+ throw new Error("Not supported");
100
+ }
101
+ await i18next.use(module2).init({
102
+ debug,
103
+ resources: mergedResource,
104
+ fallbackLng: "en",
105
+ defaultNS
106
+ }).then(() => {
107
+ i18next.services.formatter?.add("toLowerCase", (value) => typeof value === "string" ? toLowerCase(value) : String(value));
108
+ });
109
+ }
110
+ }
124
111
  // Annotate the CommonJS export names for ESM import in node:
125
112
  0 && (module.exports = {
126
113
  I18NEnvironment,
127
- i18nAddResourceBundle,
128
114
  i18nAssertValidResources,
129
- i18nInit,
130
- i18next
115
+ i18nCoreInit
131
116
  });
117
+ /*!
118
+ * Copyright © 2024-2025 Dolphin Data Development Ltd. and AIDC Toolkit
119
+ * contributors
120
+ *
121
+ * Licensed under the Apache License, Version 2.0 (the "License");
122
+ * you may not use this file except in compliance with the License.
123
+ * You may obtain a copy of the License at
124
+ *
125
+ * https://www.apache.org/licenses/LICENSE-2.0
126
+ *
127
+ * Unless required by applicable law or agreed to in writing, software
128
+ * distributed under the License is distributed on an "AS IS" BASIS,
129
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130
+ * See the License for the specific language governing permissions and
131
+ * limitations under the License.
132
+ */
package/dist/index.d.cts CHANGED
@@ -1,5 +1,4 @@
1
- import i18next from 'i18next';
2
- export { default as i18next } from 'i18next';
1
+ import { i18n, Resource } from 'i18next';
3
2
 
4
3
  /**
5
4
  * Internationalization operating environment.
@@ -18,32 +17,6 @@ declare enum I18NEnvironment {
18
17
  */
19
18
  Browser = 2
20
19
  }
21
- /**
22
- * Initialize internationalization.
23
- *
24
- * @param environment
25
- * Environment in which the application is running.
26
- *
27
- * @param debug
28
- * Debug setting.
29
- *
30
- * @returns
31
- * True if initialization was completed, false if skipped (already initialized).
32
- */
33
- declare function i18nInit(environment: I18NEnvironment, debug?: boolean): Promise<boolean>;
34
- /**
35
- * Add a resource bundle.
36
- *
37
- * @param lng
38
- * Language.
39
- *
40
- * @param ns
41
- * Namespace.
42
- *
43
- * @param resources
44
- * Resources.
45
- */
46
- declare function i18nAddResourceBundle(lng: string, ns: string, resources: object): void;
47
20
  /**
48
21
  * Assert that language resources are a type match for English (default) resources.
49
22
  *
@@ -60,5 +33,28 @@ declare function i18nAddResourceBundle(lng: string, ns: string, resources: objec
60
33
  * Parent key name (set recursively).
61
34
  */
62
35
  declare function i18nAssertValidResources(enResources: object, lng: string, lngResources: object, parent?: string): void;
36
+ /**
37
+ * Initialize internationalization.
38
+ *
39
+ * @param i18next
40
+ * Internationalization object. As multiple objects exists, this parameter represents the one for the module for which
41
+ * internationalization is being initialized.
42
+ *
43
+ * @param environment
44
+ * Environment in which the application is running.
45
+ *
46
+ * @param debug
47
+ * Debug setting.
48
+ *
49
+ * @param defaultNS
50
+ * Default namespace.
51
+ *
52
+ * @param resources
53
+ * Resources.
54
+ *
55
+ * @returns
56
+ * Void promise.
57
+ */
58
+ declare function i18nCoreInit(i18next: i18n, environment: I18NEnvironment, debug: boolean, defaultNS: string, ...resources: Resource[]): Promise<void>;
63
59
 
64
- export { I18NEnvironment, i18nAddResourceBundle, i18nAssertValidResources, i18nInit };
60
+ export { I18NEnvironment, i18nAssertValidResources, i18nCoreInit };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import i18next from 'i18next';
2
- export { default as i18next } from 'i18next';
1
+ import { i18n, Resource } from 'i18next';
3
2
 
4
3
  /**
5
4
  * Internationalization operating environment.
@@ -18,32 +17,6 @@ declare enum I18NEnvironment {
18
17
  */
19
18
  Browser = 2
20
19
  }
21
- /**
22
- * Initialize internationalization.
23
- *
24
- * @param environment
25
- * Environment in which the application is running.
26
- *
27
- * @param debug
28
- * Debug setting.
29
- *
30
- * @returns
31
- * True if initialization was completed, false if skipped (already initialized).
32
- */
33
- declare function i18nInit(environment: I18NEnvironment, debug?: boolean): Promise<boolean>;
34
- /**
35
- * Add a resource bundle.
36
- *
37
- * @param lng
38
- * Language.
39
- *
40
- * @param ns
41
- * Namespace.
42
- *
43
- * @param resources
44
- * Resources.
45
- */
46
- declare function i18nAddResourceBundle(lng: string, ns: string, resources: object): void;
47
20
  /**
48
21
  * Assert that language resources are a type match for English (default) resources.
49
22
  *
@@ -60,5 +33,28 @@ declare function i18nAddResourceBundle(lng: string, ns: string, resources: objec
60
33
  * Parent key name (set recursively).
61
34
  */
62
35
  declare function i18nAssertValidResources(enResources: object, lng: string, lngResources: object, parent?: string): void;
36
+ /**
37
+ * Initialize internationalization.
38
+ *
39
+ * @param i18next
40
+ * Internationalization object. As multiple objects exists, this parameter represents the one for the module for which
41
+ * internationalization is being initialized.
42
+ *
43
+ * @param environment
44
+ * Environment in which the application is running.
45
+ *
46
+ * @param debug
47
+ * Debug setting.
48
+ *
49
+ * @param defaultNS
50
+ * Default namespace.
51
+ *
52
+ * @param resources
53
+ * Resources.
54
+ *
55
+ * @returns
56
+ * Void promise.
57
+ */
58
+ declare function i18nCoreInit(i18next: i18n, environment: I18NEnvironment, debug: boolean, defaultNS: string, ...resources: Resource[]): Promise<void>;
63
59
 
64
- export { I18NEnvironment, i18nAddResourceBundle, i18nAssertValidResources, i18nInit };
60
+ export { I18NEnvironment, i18nAssertValidResources, i18nCoreInit };
package/dist/index.js CHANGED
@@ -1,61 +1,12 @@
1
1
  // src/locale/i18n.ts
2
- import i18next from "i18next";
3
2
  import I18nextBrowserLanguageDetector from "i18next-browser-languagedetector";
4
3
  import I18nextCLILanguageDetector from "i18next-cli-language-detector";
5
- var i18n_default = i18next;
6
- var pendingResourceBundles = [];
7
4
  var I18NEnvironment = /* @__PURE__ */ ((I18NEnvironment2) => {
8
5
  I18NEnvironment2[I18NEnvironment2["CLI"] = 0] = "CLI";
9
6
  I18NEnvironment2[I18NEnvironment2["Server"] = 1] = "Server";
10
7
  I18NEnvironment2[I18NEnvironment2["Browser"] = 2] = "Browser";
11
8
  return I18NEnvironment2;
12
9
  })(I18NEnvironment || {});
13
- function toLowerCase(s) {
14
- return s.split(" ").map((word) => /[a-z]/.test(word) ? word.toLowerCase() : word).join(" ");
15
- }
16
- async function i18nInit(environment, debug = false) {
17
- let initialized;
18
- if (pendingResourceBundles !== void 0) {
19
- initialized = true;
20
- let module;
21
- switch (environment) {
22
- case 0 /* CLI */:
23
- module = I18nextCLILanguageDetector;
24
- break;
25
- case 2 /* Browser */:
26
- module = I18nextBrowserLanguageDetector;
27
- break;
28
- default:
29
- throw new Error("Not supported");
30
- }
31
- const initResourceBundles = pendingResourceBundles;
32
- pendingResourceBundles = void 0;
33
- await i18next.use(module).init({
34
- fallbackLng: "en",
35
- debug,
36
- resources: {}
37
- }).then(() => {
38
- i18next.services.formatter?.add("toLowerCase", (value) => typeof value === "string" ? toLowerCase(value) : String(value));
39
- for (const initResourceBundle of initResourceBundles) {
40
- i18nAddResourceBundle(initResourceBundle.lng, initResourceBundle.ns, initResourceBundle.resources);
41
- }
42
- });
43
- } else {
44
- initialized = false;
45
- }
46
- return initialized;
47
- }
48
- function i18nAddResourceBundle(lng, ns, resources) {
49
- if (pendingResourceBundles !== void 0) {
50
- pendingResourceBundles.push({
51
- lng,
52
- ns,
53
- resources
54
- });
55
- } else {
56
- i18next.addResourceBundle(lng, ns, resources);
57
- }
58
- }
59
10
  function i18nAssertValidResources(enResources, lng, lngResources, parent) {
60
11
  const enResourcesMap = new Map(Object.entries(enResources));
61
12
  const lngResourcesMap = new Map(Object.entries(lngResources));
@@ -81,10 +32,62 @@ function i18nAssertValidResources(enResources, lng, lngResources, parent) {
81
32
  }
82
33
  }
83
34
  }
35
+ function toLowerCase(s) {
36
+ return s.split(" ").map((word) => /[a-z]/.test(word) ? word.toLowerCase() : word).join(" ");
37
+ }
38
+ async function i18nCoreInit(i18next, environment, debug, defaultNS, ...resources) {
39
+ if (!i18next.isInitialized) {
40
+ const mergedResource = {};
41
+ for (const resource of resources) {
42
+ for (const [language, resourceLanguage] of Object.entries(resource)) {
43
+ if (!(language in mergedResource)) {
44
+ mergedResource[language] = {};
45
+ }
46
+ const mergedResourceLanguage = mergedResource[language];
47
+ for (const [namespace, resourceKey] of Object.entries(resourceLanguage)) {
48
+ mergedResourceLanguage[namespace] = resourceKey;
49
+ }
50
+ }
51
+ }
52
+ let module;
53
+ switch (environment) {
54
+ case 0 /* CLI */:
55
+ module = I18nextCLILanguageDetector;
56
+ break;
57
+ case 2 /* Browser */:
58
+ module = I18nextBrowserLanguageDetector;
59
+ break;
60
+ default:
61
+ throw new Error("Not supported");
62
+ }
63
+ await i18next.use(module).init({
64
+ debug,
65
+ resources: mergedResource,
66
+ fallbackLng: "en",
67
+ defaultNS
68
+ }).then(() => {
69
+ i18next.services.formatter?.add("toLowerCase", (value) => typeof value === "string" ? toLowerCase(value) : String(value));
70
+ });
71
+ }
72
+ }
84
73
  export {
85
74
  I18NEnvironment,
86
- i18nAddResourceBundle,
87
75
  i18nAssertValidResources,
88
- i18nInit,
89
- i18n_default as i18next
76
+ i18nCoreInit
90
77
  };
78
+ /*!
79
+ * Copyright © 2024-2025 Dolphin Data Development Ltd. and AIDC Toolkit
80
+ * contributors
81
+ *
82
+ * Licensed under the Apache License, Version 2.0 (the "License");
83
+ * you may not use this file except in compliance with the License.
84
+ * You may obtain a copy of the License at
85
+ *
86
+ * https://www.apache.org/licenses/LICENSE-2.0
87
+ *
88
+ * Unless required by applicable law or agreed to in writing, software
89
+ * distributed under the License is distributed on an "AS IS" BASIS,
90
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
91
+ * See the License for the specific language governing permissions and
92
+ * limitations under the License.
93
+ */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aidc-toolkit/core",
3
- "version": "0.9.7-beta",
3
+ "version": "0.9.9-beta",
4
4
  "description": "Core functionality for AIDC Toolkit",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -20,19 +20,19 @@
20
20
  },
21
21
  "scripts": {
22
22
  "lint": "eslint .",
23
- "build": "tsup src/index.ts --clean --format cjs,esm --dts",
24
- "build-doc": "npm run build && tsc src/index.ts --outDir dist --target esnext --moduleResolution nodenext --module nodenext --emitDeclarationOnly --declaration --declarationMap"
23
+ "build-dist": "tsup src/index.ts --clean --format cjs,esm --dts",
24
+ "build-doc": "npm run build-dist && tsc src/index.ts --outDir dist --target esnext --moduleResolution nodenext --module nodenext --emitDeclarationOnly --declaration --declarationMap"
25
25
  },
26
26
  "devDependencies": {
27
- "@aidc-toolkit/dev": "^0.9.7-beta",
28
- "eslint": "^9.16.0",
27
+ "@aidc-toolkit/dev": "^0.9.9-beta",
28
+ "eslint": "^9.17.0",
29
29
  "ts-node": "^10.9.2",
30
30
  "tsup": "^8.3.5",
31
31
  "typescript": "^5.7.2"
32
32
  },
33
33
  "dependencies": {
34
- "@rollup/rollup-linux-x64-gnu": "^4.28.1",
35
- "i18next": "^24.0.5",
34
+ "@rollup/rollup-linux-x64-gnu": "^4.29.1",
35
+ "i18next": "^24.2.0",
36
36
  "i18next-browser-languagedetector": "^8.0.2",
37
37
  "i18next-cli-language-detector": "^1.1.8"
38
38
  }
package/src/index.ts CHANGED
@@ -1,2 +1,17 @@
1
+ /*!
2
+ * Copyright © 2024-2025 Dolphin Data Development Ltd. and AIDC Toolkit
3
+ * contributors
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * https://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
1
17
  export * from "./locale/i18n.js";
2
- export { default as i18next } from "./locale/i18n.js";
@@ -1,23 +1,7 @@
1
- import i18next, { type LanguageDetectorModule } from "i18next";
1
+ import type { i18n, LanguageDetectorModule, Resource } from "i18next";
2
2
  import I18nextBrowserLanguageDetector from "i18next-browser-languagedetector";
3
3
  import I18nextCLILanguageDetector from "i18next-cli-language-detector";
4
4
 
5
- export default i18next;
6
-
7
- /**
8
- * Internal type to maintain resource bundles added before initialization.
9
- */
10
- interface ResourceBundle {
11
- lng: string;
12
- ns: string;
13
- resources: object;
14
- }
15
-
16
- /**
17
- * Internal array to maintain resource bundles added before initialization.
18
- */
19
- let pendingResourceBundles: ResourceBundle[] | undefined = [];
20
-
21
5
  /**
22
6
  * Internationalization operating environment.
23
7
  */
@@ -38,6 +22,54 @@ export enum I18NEnvironment {
38
22
  Browser
39
23
  }
40
24
 
25
+ /**
26
+ * Assert that language resources are a type match for English (default) resources.
27
+ *
28
+ * @param enResources
29
+ * English resources.
30
+ *
31
+ * @param lng
32
+ * Language.
33
+ *
34
+ * @param lngResources
35
+ * Language resources.
36
+ *
37
+ * @param parent
38
+ * Parent key name (set recursively).
39
+ */
40
+ export function i18nAssertValidResources(enResources: object, lng: string, lngResources: object, parent?: string): void {
41
+ const enResourcesMap = new Map<string, object>(Object.entries(enResources));
42
+ const lngResourcesMap = new Map<string, object>(Object.entries(lngResources));
43
+
44
+ const isLocale = lng.includes("-");
45
+
46
+ for (const [enKey, enValue] of enResourcesMap) {
47
+ const lngValue = lngResourcesMap.get(enKey);
48
+
49
+ if (lngValue !== undefined) {
50
+ const enValueType = typeof enValue;
51
+ const lngValueType = typeof lngValue;
52
+
53
+ if (lngValueType !== enValueType) {
54
+ throw new Error(`Invalid value type ${lngValueType} for key ${parent === undefined ? "" : `${parent}.`}${enKey} in ${lng} resources`);
55
+ }
56
+
57
+ if (enValueType === "object") {
58
+ i18nAssertValidResources(enValue, lng, lngValue, `${parent === undefined ? "" : `${parent}.`}${enKey}`);
59
+ }
60
+ // Locale falls back to raw language so ignore if missing.
61
+ } else if (!isLocale) {
62
+ throw new Error(`Missing key ${parent === undefined ? "" : `${parent}.`}${enKey} from ${lng} resources`);
63
+ }
64
+ }
65
+
66
+ for (const [lngKey] of lngResourcesMap) {
67
+ if (!enResourcesMap.has(lngKey)) {
68
+ throw new Error(`Extraneous key ${parent === undefined ? "" : `${parent}.`}${lngKey} in ${lng} resources`);
69
+ }
70
+ }
71
+ }
72
+
41
73
  /**
42
74
  * Convert a string to lower case, skipping words that are all upper case.
43
75
  *
@@ -55,27 +87,53 @@ function toLowerCase(s: string): string {
55
87
  /**
56
88
  * Initialize internationalization.
57
89
  *
90
+ * @param i18next
91
+ * Internationalization object. As multiple objects exists, this parameter represents the one for the module for which
92
+ * internationalization is being initialized.
93
+ *
58
94
  * @param environment
59
95
  * Environment in which the application is running.
60
96
  *
61
97
  * @param debug
62
98
  * Debug setting.
63
99
  *
100
+ * @param defaultNS
101
+ * Default namespace.
102
+ *
103
+ * @param resources
104
+ * Resources.
105
+ *
64
106
  * @returns
65
- * True if initialization was completed, false if skipped (already initialized).
107
+ * Void promise.
66
108
  */
67
- export async function i18nInit(environment: I18NEnvironment, debug = false): Promise<boolean> {
68
- let initialized: boolean;
109
+ export async function i18nCoreInit(i18next: i18n, environment: I18NEnvironment, debug: boolean, defaultNS: string, ...resources: Resource[]): Promise<void> {
110
+ // Initialization may be called more than once.
111
+ if (!i18next.isInitialized) {
112
+ const mergedResource: Resource = {};
113
+
114
+ // Merge resources.
115
+ for (const resource of resources) {
116
+ // Merge languages.
117
+ for (const [language, resourceLanguage] of Object.entries(resource)) {
118
+ if (!(language in mergedResource)) {
119
+ mergedResource[language] = {};
120
+ }
121
+
122
+ const mergedResourceLanguage = mergedResource[language];
123
+
124
+ // Merge namespaces.
125
+ for (const [namespace, resourceKey] of Object.entries(resourceLanguage)) {
126
+ mergedResourceLanguage[namespace] = resourceKey;
127
+ }
128
+ }
129
+ }
69
130
 
70
- // Skip if initialization is not pending.
71
- if (pendingResourceBundles !== undefined) {
72
- initialized = true;
73
-
74
131
  let module: Parameters<typeof i18next.use>[0];
75
132
 
76
133
  switch (environment) {
77
134
  case I18NEnvironment.CLI:
78
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Pending resolution of https://github.com/neet/i18next-cli-language-detector/issues/281.
135
+ // TODO Refactor when https://github.com/neet/i18next-cli-language-detector/issues/281 resolved.
136
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Per above.
79
137
  module = I18nextCLILanguageDetector as unknown as LanguageDetectorModule;
80
138
  break;
81
139
 
@@ -87,100 +145,14 @@ export async function i18nInit(environment: I18NEnvironment, debug = false): Pro
87
145
  throw new Error("Not supported");
88
146
  }
89
147
 
90
- const initResourceBundles = pendingResourceBundles;
91
-
92
- // No need to manage pending resource bundles past this point.
93
- pendingResourceBundles = undefined;
94
-
95
148
  await i18next.use(module).init({
96
- fallbackLng: "en",
97
149
  debug,
98
- resources: {}
150
+ resources: mergedResource,
151
+ fallbackLng: "en",
152
+ defaultNS
99
153
  }).then(() => {
100
154
  // Add toLowerCase function.
101
155
  i18next.services.formatter?.add("toLowerCase", value => typeof value === "string" ? toLowerCase(value) : String(value));
102
-
103
- // Add pending resource bundles.
104
- for (const initResourceBundle of initResourceBundles) {
105
- i18nAddResourceBundle(initResourceBundle.lng, initResourceBundle.ns, initResourceBundle.resources);
106
- }
107
- });
108
- } else {
109
- initialized = false;
110
- }
111
-
112
- return initialized;
113
- }
114
-
115
- /**
116
- * Add a resource bundle.
117
- *
118
- * @param lng
119
- * Language.
120
- *
121
- * @param ns
122
- * Namespace.
123
- *
124
- * @param resources
125
- * Resources.
126
- */
127
- export function i18nAddResourceBundle(lng: string, ns: string, resources: object): void {
128
- if (pendingResourceBundles !== undefined) {
129
- pendingResourceBundles.push({
130
- lng,
131
- ns,
132
- resources
133
156
  });
134
- } else {
135
- // Already initialized; add resource bundle directly.
136
- i18next.addResourceBundle(lng, ns, resources);
137
- }
138
- }
139
-
140
- /**
141
- * Assert that language resources are a type match for English (default) resources.
142
- *
143
- * @param enResources
144
- * English resources.
145
- *
146
- * @param lng
147
- * Language.
148
- *
149
- * @param lngResources
150
- * Language resources.
151
- *
152
- * @param parent
153
- * Parent key name (set recursively).
154
- */
155
- export function i18nAssertValidResources(enResources: object, lng: string, lngResources: object, parent?: string): void {
156
- const enResourcesMap = new Map<string, object>(Object.entries(enResources));
157
- const lngResourcesMap = new Map<string, object>(Object.entries(lngResources));
158
-
159
- const isLocale = lng.includes("-");
160
-
161
- for (const [enKey, enValue] of enResourcesMap) {
162
- const lngValue = lngResourcesMap.get(enKey);
163
-
164
- if (lngValue !== undefined) {
165
- const enValueType = typeof enValue;
166
- const lngValueType = typeof lngValue;
167
-
168
- if (lngValueType !== enValueType) {
169
- throw new Error(`Invalid value type ${lngValueType} for key ${parent === undefined ? "" : `${parent}.`}${enKey} in ${lng} resources`);
170
- }
171
-
172
- if (enValueType === "object") {
173
- i18nAssertValidResources(enValue, lng, lngValue, `${parent === undefined ? "" : `${parent}.`}${enKey}`);
174
- }
175
- // Locale falls back to raw language so ignore if missing.
176
- } else if (!isLocale) {
177
- throw new Error(`Missing key ${parent === undefined ? "" : `${parent}.`}${enKey} from ${lng} resources`);
178
- }
179
- }
180
-
181
- for (const [lngKey] of lngResourcesMap) {
182
- if (!enResourcesMap.has(lngKey)) {
183
- throw new Error(`Extraneous key ${parent === undefined ? "" : `${parent}.`}${lngKey} in ${lng} resources`);
184
- }
185
157
  }
186
158
  }
File without changes