@bleedingdev/modern-js-plugin-i18n 3.5.0-ultramodern.9 → 3.5.0-ultramodern.90

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.
Files changed (221) hide show
  1. package/dist/cjs/cli/index.js +2 -1
  2. package/dist/cjs/cli/locales.js +15 -15
  3. package/dist/cjs/runtime/Link.js +8 -89
  4. package/dist/cjs/runtime/context.js +78 -91
  5. package/dist/cjs/runtime/contextHelpers.js +143 -0
  6. package/dist/cjs/runtime/core.js +32 -132
  7. package/dist/cjs/runtime/hooks.js +54 -21
  8. package/dist/cjs/runtime/i18n/backend/defaults.node.js +7 -7
  9. package/dist/cjs/runtime/i18n/detection/cache.js +150 -0
  10. package/dist/cjs/runtime/i18n/detection/detector.js +100 -0
  11. package/dist/cjs/runtime/i18n/detection/index.js +8 -263
  12. package/dist/cjs/runtime/i18n/detection/initOptions.js +69 -0
  13. package/dist/cjs/runtime/i18n/detection/language.js +62 -0
  14. package/dist/cjs/runtime/i18n/detection/path.js +49 -0
  15. package/dist/cjs/runtime/i18n/detection/priority.js +66 -0
  16. package/dist/cjs/runtime/i18n/detection/ssr.js +70 -0
  17. package/dist/cjs/runtime/i18n/detection/types.js +18 -0
  18. package/dist/cjs/runtime/i18n/instance.js +34 -10
  19. package/dist/cjs/runtime/linkHelpers.js +148 -0
  20. package/dist/cjs/runtime/pluginSetup.js +102 -0
  21. package/dist/cjs/runtime/providerComposition.js +115 -0
  22. package/dist/cjs/runtime/reactI18next.js +43 -0
  23. package/dist/cjs/runtime/routerAdapter.js +16 -8
  24. package/dist/cjs/runtime/utils.js +1 -9
  25. package/dist/cjs/server/apiPrefix.js +72 -0
  26. package/dist/cjs/server/detectorOptions.js +97 -0
  27. package/dist/cjs/server/index.js +17 -144
  28. package/dist/cjs/server/redirectPolicy.js +96 -0
  29. package/dist/cjs/shared/detection.js +2 -104
  30. package/dist/cjs/shared/localisedUrls/config.js +49 -0
  31. package/dist/cjs/shared/localisedUrls/index.js +91 -0
  32. package/dist/cjs/shared/localisedUrls/normalise.js +87 -0
  33. package/dist/cjs/shared/localisedUrls/pathname.js +68 -0
  34. package/dist/cjs/shared/localisedUrls/patterns.js +130 -0
  35. package/dist/cjs/shared/localisedUrls/redirect.js +103 -0
  36. package/dist/cjs/shared/localisedUrls/resolve.js +88 -0
  37. package/dist/cjs/shared/localisedUrls/routes.js +130 -0
  38. package/dist/cjs/shared/localisedUrls/types.js +18 -0
  39. package/dist/cjs/shared/localisedUrls.js +28 -294
  40. package/dist/cjs/shared/utils.js +2 -8
  41. package/dist/esm/cli/index.mjs +2 -1
  42. package/dist/esm/cli/locales.mjs +13 -13
  43. package/dist/esm/runtime/Link.mjs +1 -82
  44. package/dist/esm/runtime/context.mjs +76 -92
  45. package/dist/esm/runtime/contextHelpers.mjs +87 -0
  46. package/dist/esm/runtime/core.mjs +30 -133
  47. package/dist/esm/runtime/hooks.mjs +56 -23
  48. package/dist/esm/runtime/i18n/backend/defaults.node.mjs +5 -5
  49. package/dist/esm/runtime/i18n/detection/cache.mjs +103 -0
  50. package/dist/esm/runtime/i18n/detection/detector.mjs +62 -0
  51. package/dist/esm/runtime/i18n/detection/index.mjs +4 -260
  52. package/dist/esm/runtime/i18n/detection/initOptions.mjs +28 -0
  53. package/dist/esm/runtime/i18n/detection/language.mjs +21 -0
  54. package/dist/esm/runtime/i18n/detection/path.mjs +11 -0
  55. package/dist/esm/runtime/i18n/detection/priority.mjs +28 -0
  56. package/dist/esm/runtime/i18n/detection/ssr.mjs +26 -0
  57. package/dist/esm/runtime/i18n/detection/types.mjs +0 -0
  58. package/dist/esm/runtime/i18n/instance.mjs +35 -8
  59. package/dist/esm/runtime/linkHelpers.mjs +98 -0
  60. package/dist/esm/runtime/pluginSetup.mjs +64 -0
  61. package/dist/esm/runtime/providerComposition.mjs +77 -0
  62. package/dist/esm/runtime/reactI18next.mjs +5 -0
  63. package/dist/esm/runtime/routerAdapter.mjs +16 -8
  64. package/dist/esm/runtime/utils.mjs +2 -10
  65. package/dist/esm/server/apiPrefix.mjs +31 -0
  66. package/dist/esm/server/detectorOptions.mjs +59 -0
  67. package/dist/esm/server/index.mjs +8 -135
  68. package/dist/esm/server/redirectPolicy.mjs +46 -0
  69. package/dist/esm/shared/detection.mjs +1 -71
  70. package/dist/esm/shared/localisedUrls/config.mjs +11 -0
  71. package/dist/esm/shared/localisedUrls/index.mjs +7 -0
  72. package/dist/esm/shared/localisedUrls/normalise.mjs +37 -0
  73. package/dist/esm/shared/localisedUrls/pathname.mjs +24 -0
  74. package/dist/esm/shared/localisedUrls/patterns.mjs +86 -0
  75. package/dist/esm/shared/localisedUrls/redirect.mjs +56 -0
  76. package/dist/esm/shared/localisedUrls/resolve.mjs +47 -0
  77. package/dist/esm/shared/localisedUrls/routes.mjs +89 -0
  78. package/dist/esm/shared/localisedUrls/types.mjs +0 -0
  79. package/dist/esm/shared/localisedUrls.mjs +1 -283
  80. package/dist/esm/shared/utils.mjs +1 -1
  81. package/dist/esm-node/cli/index.mjs +2 -1
  82. package/dist/esm-node/cli/locales.mjs +13 -13
  83. package/dist/esm-node/runtime/Link.mjs +1 -82
  84. package/dist/esm-node/runtime/context.mjs +76 -92
  85. package/dist/esm-node/runtime/contextHelpers.mjs +88 -0
  86. package/dist/esm-node/runtime/core.mjs +30 -133
  87. package/dist/esm-node/runtime/hooks.mjs +56 -23
  88. package/dist/esm-node/runtime/i18n/backend/defaults.node.mjs +5 -5
  89. package/dist/esm-node/runtime/i18n/detection/cache.mjs +104 -0
  90. package/dist/esm-node/runtime/i18n/detection/detector.mjs +63 -0
  91. package/dist/esm-node/runtime/i18n/detection/index.mjs +4 -260
  92. package/dist/esm-node/runtime/i18n/detection/initOptions.mjs +29 -0
  93. package/dist/esm-node/runtime/i18n/detection/language.mjs +22 -0
  94. package/dist/esm-node/runtime/i18n/detection/path.mjs +12 -0
  95. package/dist/esm-node/runtime/i18n/detection/priority.mjs +29 -0
  96. package/dist/esm-node/runtime/i18n/detection/ssr.mjs +27 -0
  97. package/dist/esm-node/runtime/i18n/detection/types.mjs +1 -0
  98. package/dist/esm-node/runtime/i18n/instance.mjs +35 -8
  99. package/dist/esm-node/runtime/linkHelpers.mjs +99 -0
  100. package/dist/esm-node/runtime/pluginSetup.mjs +65 -0
  101. package/dist/esm-node/runtime/providerComposition.mjs +78 -0
  102. package/dist/esm-node/runtime/reactI18next.mjs +6 -0
  103. package/dist/esm-node/runtime/routerAdapter.mjs +16 -8
  104. package/dist/esm-node/runtime/utils.mjs +2 -10
  105. package/dist/esm-node/server/apiPrefix.mjs +32 -0
  106. package/dist/esm-node/server/detectorOptions.mjs +60 -0
  107. package/dist/esm-node/server/index.mjs +8 -135
  108. package/dist/esm-node/server/redirectPolicy.mjs +47 -0
  109. package/dist/esm-node/shared/detection.mjs +1 -71
  110. package/dist/esm-node/shared/localisedUrls/config.mjs +12 -0
  111. package/dist/esm-node/shared/localisedUrls/index.mjs +8 -0
  112. package/dist/esm-node/shared/localisedUrls/normalise.mjs +38 -0
  113. package/dist/esm-node/shared/localisedUrls/pathname.mjs +25 -0
  114. package/dist/esm-node/shared/localisedUrls/patterns.mjs +87 -0
  115. package/dist/esm-node/shared/localisedUrls/redirect.mjs +57 -0
  116. package/dist/esm-node/shared/localisedUrls/resolve.mjs +48 -0
  117. package/dist/esm-node/shared/localisedUrls/routes.mjs +90 -0
  118. package/dist/esm-node/shared/localisedUrls/types.mjs +1 -0
  119. package/dist/esm-node/shared/localisedUrls.mjs +1 -283
  120. package/dist/esm-node/shared/utils.mjs +1 -1
  121. package/dist/types/cli/locales.d.ts +1 -1
  122. package/dist/types/runtime/Link.d.ts +3 -7
  123. package/dist/types/runtime/context.d.ts +21 -5
  124. package/dist/types/runtime/contextHelpers.d.ts +29 -0
  125. package/dist/types/runtime/core.d.ts +4 -8
  126. package/dist/types/runtime/hooks.d.ts +3 -7
  127. package/dist/types/runtime/i18n/backend/middleware.common.d.ts +2 -1
  128. package/dist/types/runtime/i18n/detection/cache.d.ts +14 -0
  129. package/dist/types/runtime/i18n/detection/detector.d.ts +8 -0
  130. package/dist/types/runtime/i18n/detection/index.d.ts +5 -50
  131. package/dist/types/runtime/i18n/detection/initOptions.d.ts +10 -0
  132. package/dist/types/runtime/i18n/detection/language.d.ts +11 -0
  133. package/dist/types/runtime/i18n/detection/middleware.d.ts +4 -4
  134. package/dist/types/runtime/i18n/detection/middleware.node.d.ts +4 -4
  135. package/dist/types/runtime/i18n/detection/path.d.ts +5 -0
  136. package/dist/types/runtime/i18n/detection/priority.d.ts +10 -0
  137. package/dist/types/runtime/i18n/detection/ssr.d.ts +9 -0
  138. package/dist/types/runtime/i18n/detection/types.d.ts +32 -0
  139. package/dist/types/runtime/i18n/instance.d.ts +31 -27
  140. package/dist/types/runtime/i18n/react-i18next.d.ts +1 -6
  141. package/dist/types/runtime/i18n/utils.d.ts +14 -5
  142. package/dist/types/runtime/linkHelpers.d.ts +17 -0
  143. package/dist/types/runtime/pluginSetup.d.ts +32 -0
  144. package/dist/types/runtime/providerComposition.d.ts +16 -0
  145. package/dist/types/runtime/reactI18next.d.ts +7 -0
  146. package/dist/types/runtime/routerAdapter.d.ts +7 -6
  147. package/dist/types/runtime/utils.d.ts +7 -2
  148. package/dist/types/server/apiPrefix.d.ts +7 -0
  149. package/dist/types/server/detectorOptions.d.ts +21 -0
  150. package/dist/types/server/index.d.ts +1 -6
  151. package/dist/types/server/redirectPolicy.d.ts +22 -0
  152. package/dist/types/shared/detection.d.ts +1 -11
  153. package/dist/types/shared/localisedUrls/config.d.ts +9 -0
  154. package/dist/types/shared/localisedUrls/index.d.ts +9 -0
  155. package/dist/types/shared/localisedUrls/normalise.d.ts +10 -0
  156. package/dist/types/shared/localisedUrls/pathname.d.ts +4 -0
  157. package/dist/types/shared/localisedUrls/patterns.d.ts +5 -0
  158. package/dist/types/shared/localisedUrls/redirect.d.ts +11 -0
  159. package/dist/types/shared/localisedUrls/resolve.d.ts +7 -0
  160. package/dist/types/shared/localisedUrls/routes.d.ts +3 -0
  161. package/dist/types/shared/localisedUrls/types.d.ts +14 -0
  162. package/dist/types/shared/localisedUrls.d.ts +2 -36
  163. package/dist/types/shared/utils.d.ts +0 -2
  164. package/package.json +17 -20
  165. package/rstest.config.mts +8 -1
  166. package/src/cli/index.ts +5 -4
  167. package/src/cli/locales.ts +3 -3
  168. package/src/runtime/Link.tsx +11 -138
  169. package/src/runtime/context.tsx +170 -207
  170. package/src/runtime/contextHelpers.ts +242 -0
  171. package/src/runtime/core.tsx +48 -275
  172. package/src/runtime/hooks.ts +86 -24
  173. package/src/runtime/i18n/backend/defaults.node.ts +2 -2
  174. package/src/runtime/i18n/backend/middleware.common.ts +5 -4
  175. package/src/runtime/i18n/detection/cache.ts +189 -0
  176. package/src/runtime/i18n/detection/detector.ts +166 -0
  177. package/src/runtime/i18n/detection/index.ts +10 -641
  178. package/src/runtime/i18n/detection/initOptions.ts +74 -0
  179. package/src/runtime/i18n/detection/language.ts +64 -0
  180. package/src/runtime/i18n/detection/middleware.node.ts +26 -7
  181. package/src/runtime/i18n/detection/middleware.ts +15 -7
  182. package/src/runtime/i18n/detection/path.ts +41 -0
  183. package/src/runtime/i18n/detection/priority.ts +82 -0
  184. package/src/runtime/i18n/detection/ssr.ts +47 -0
  185. package/src/runtime/i18n/detection/types.ts +41 -0
  186. package/src/runtime/i18n/instance.ts +98 -34
  187. package/src/runtime/i18n/react-i18next.ts +1 -6
  188. package/src/runtime/i18n/utils.ts +18 -9
  189. package/src/runtime/linkHelpers.ts +174 -0
  190. package/src/runtime/pluginSetup.ts +189 -0
  191. package/src/runtime/providerComposition.tsx +148 -0
  192. package/src/runtime/reactI18next.ts +20 -0
  193. package/src/runtime/routerAdapter.tsx +36 -15
  194. package/src/runtime/utils.ts +13 -35
  195. package/src/server/apiPrefix.ts +67 -0
  196. package/src/server/detectorOptions.ts +105 -0
  197. package/src/server/index.ts +14 -319
  198. package/src/server/redirectPolicy.ts +127 -0
  199. package/src/shared/detection.ts +1 -1
  200. package/src/shared/localisedUrls/config.ts +18 -0
  201. package/src/shared/localisedUrls/index.ts +24 -0
  202. package/src/shared/localisedUrls/normalise.ts +67 -0
  203. package/src/shared/localisedUrls/pathname.ts +60 -0
  204. package/src/shared/localisedUrls/patterns.ts +160 -0
  205. package/src/shared/localisedUrls/redirect.ts +93 -0
  206. package/src/shared/localisedUrls/resolve.ts +135 -0
  207. package/src/shared/localisedUrls/routes.ts +234 -0
  208. package/src/shared/localisedUrls/types.ts +18 -0
  209. package/src/shared/localisedUrls.ts +26 -623
  210. package/src/shared/utils.ts +2 -2
  211. package/tests/federatedI18nBoundary.client.test.tsx +112 -0
  212. package/tests/federatedI18nBoundary.test.tsx +120 -0
  213. package/tests/fixtures/localised-routes.golden.json +156 -0
  214. package/tests/link.test.tsx +111 -0
  215. package/tests/localisedUrlRewriteMatrix.fork.test.ts +147 -0
  216. package/tests/localisedUrls.test.ts +148 -2
  217. package/tests/reactI18nextRuntimeBoundary.test.ts +6 -3
  218. package/tests/redirectPolicy.test.ts +45 -0
  219. package/tests/routerAdapter.test.tsx +603 -0
  220. package/tests/type-fixture/fixture-globals.d.ts +11 -0
  221. package/tests/type-fixture/tsconfig.json +1 -0
@@ -0,0 +1,103 @@
1
+ const detectorInstanceCache = new WeakMap();
2
+ const DETECTOR_SAFE_OPTION_KEYS = [
3
+ 'lowerCaseLng',
4
+ 'nonExplicitSupportedLngs',
5
+ 'load',
6
+ 'partialBundledLanguages',
7
+ 'returnNull',
8
+ 'returnEmptyString',
9
+ 'returnObjects',
10
+ 'joinArrays',
11
+ 'keySeparator',
12
+ 'nsSeparator',
13
+ 'pluralSeparator',
14
+ 'contextSeparator',
15
+ 'fallbackNS',
16
+ 'ns',
17
+ 'defaultNS',
18
+ 'debug'
19
+ ];
20
+ const stableStringify = (value)=>{
21
+ if (null == value) return JSON.stringify(value);
22
+ if ('object' != typeof value) return JSON.stringify(value);
23
+ if (Array.isArray(value)) return `[${value.map((item)=>stableStringify(item)).join(',')}]`;
24
+ const record = value;
25
+ const sortedKeys = Object.keys(record).sort();
26
+ const sortedEntries = sortedKeys.map((key)=>{
27
+ const stringifiedValue = stableStringify(record[key]);
28
+ return `${JSON.stringify(key)}:${stringifiedValue}`;
29
+ });
30
+ return `{${sortedEntries.join(',')}}`;
31
+ };
32
+ const buildDetectorConfigKey = (languages, fallbackLanguage, mergedDetection)=>stableStringify({
33
+ languages,
34
+ fallbackLanguage,
35
+ detection: mergedDetection
36
+ });
37
+ const pickSafeDetectionOptions = (userInitOptions)=>{
38
+ if (!userInitOptions) return {};
39
+ const safeOptions = {};
40
+ for (const key of DETECTOR_SAFE_OPTION_KEYS){
41
+ const value = userInitOptions[key];
42
+ if (void 0 !== value) safeOptions[key] = value;
43
+ }
44
+ if (userInitOptions.interpolation) safeOptions.interpolation = {
45
+ ...userInitOptions.interpolation
46
+ };
47
+ return safeOptions;
48
+ };
49
+ const cleanupDetectorCacheEntry = (entry)=>{
50
+ if (!entry || !entry.isTemporary) return;
51
+ const instance = entry.instance;
52
+ try {
53
+ instance?.removeAllListeners?.();
54
+ } catch (error) {}
55
+ try {
56
+ instance?.off?.('*');
57
+ } catch (error) {}
58
+ try {
59
+ instance?.services?.backendConnector?.backend?.stop?.();
60
+ } catch (error) {}
61
+ try {
62
+ instance?.services?.backendConnector?.backend?.close?.();
63
+ } catch (error) {}
64
+ };
65
+ const createDetectorInstance = (baseInstance, configKey)=>{
66
+ const cached = detectorInstanceCache.get(baseInstance);
67
+ if (cached && cached.configKey === configKey) return {
68
+ instance: cached.instance,
69
+ isTemporary: cached.isTemporary
70
+ };
71
+ if (cached) {
72
+ cleanupDetectorCacheEntry(cached);
73
+ detectorInstanceCache.delete(baseInstance);
74
+ }
75
+ const createNewInstance = ()=>{
76
+ if ('function' == typeof baseInstance.createInstance) try {
77
+ const created = baseInstance.createInstance();
78
+ if (created) return {
79
+ instance: created,
80
+ isTemporary: true
81
+ };
82
+ } catch (error) {}
83
+ if ('function' == typeof baseInstance.cloneInstance) try {
84
+ const cloned = baseInstance.cloneInstance();
85
+ if (cloned) return {
86
+ instance: cloned,
87
+ isTemporary: true
88
+ };
89
+ } catch (error) {}
90
+ return {
91
+ instance: baseInstance,
92
+ isTemporary: false
93
+ };
94
+ };
95
+ const created = createNewInstance();
96
+ if (created.isTemporary) detectorInstanceCache.set(baseInstance, {
97
+ instance: created.instance,
98
+ isTemporary: true,
99
+ configKey
100
+ });
101
+ return created;
102
+ };
103
+ export { buildDetectorConfigKey, createDetectorInstance, detectorInstanceCache, pickSafeDetectionOptions };
@@ -0,0 +1,62 @@
1
+ import { isBrowser } from "@modern-js/runtime";
2
+ import { buildDetectorConfigKey, createDetectorInstance, detectorInstanceCache, pickSafeDetectionOptions } from "./cache.mjs";
3
+ import { mergeDetectionOptions } from "./initOptions.mjs";
4
+ import { getSupportedLanguage, isLanguageSupported } from "./language.mjs";
5
+ import { detectLanguage, useI18nextLanguageDetector } from "./middleware.mjs";
6
+ const initializeI18nForDetector = async (i18nInstance, options)=>{
7
+ const mergedDetection = mergeDetectionOptions(options.i18nextDetector, options.detection, options.localePathRedirect, options.userInitOptions);
8
+ const configKey = buildDetectorConfigKey(options.languages, options.fallbackLanguage, mergedDetection);
9
+ const { instance, isTemporary } = createDetectorInstance(i18nInstance, configKey);
10
+ const safeUserOptions = pickSafeDetectionOptions(options.userInitOptions);
11
+ const initOptions = {
12
+ ...safeUserOptions,
13
+ fallbackLng: options.fallbackLanguage,
14
+ supportedLngs: options.languages,
15
+ detection: mergedDetection,
16
+ initImmediate: true,
17
+ interpolation: {
18
+ ...safeUserOptions?.interpolation || {},
19
+ escapeValue: safeUserOptions?.interpolation?.escapeValue ?? false
20
+ },
21
+ react: {
22
+ useSuspense: false
23
+ }
24
+ };
25
+ useI18nextLanguageDetector(instance);
26
+ if (instance.isInitialized) {
27
+ if (isTemporary) await instance.init(initOptions);
28
+ } else await instance.init(initOptions);
29
+ return {
30
+ detectorInstance: instance,
31
+ isTemporary
32
+ };
33
+ };
34
+ const detectLanguageFromI18nextDetector = async (i18nInstance, options)=>{
35
+ if (!options.i18nextDetector) return;
36
+ const mergedDetection = mergeDetectionOptions(options.i18nextDetector, options.detection, options.localePathRedirect, options.userInitOptions);
37
+ const { detectorInstance, isTemporary } = await initializeI18nForDetector(i18nInstance, options);
38
+ try {
39
+ const request = options.ssrContext?.request;
40
+ if (!isBrowser() && !request) return;
41
+ const detectorLang = detectLanguage(detectorInstance, request, mergedDetection);
42
+ if (detectorLang) {
43
+ const supportedLang = getSupportedLanguage(detectorLang, options.languages);
44
+ if (supportedLang) return supportedLang;
45
+ }
46
+ if (detectorInstance.isInitialized && detectorInstance.language) {
47
+ const currentLang = detectorInstance.language;
48
+ if (isLanguageSupported(currentLang, options.languages)) return currentLang;
49
+ }
50
+ } catch (error) {} finally{
51
+ if (isTemporary && detectorInstance !== i18nInstance) detectorInstanceCache.set(i18nInstance, {
52
+ instance: detectorInstance,
53
+ isTemporary: true,
54
+ configKey: buildDetectorConfigKey(options.languages, options.fallbackLanguage, mergedDetection)
55
+ });
56
+ else if (detectorInstance === i18nInstance) {
57
+ i18nInstance.isInitialized = false;
58
+ delete i18nInstance.language;
59
+ }
60
+ }
61
+ };
62
+ export { detectLanguageFromI18nextDetector };
@@ -1,260 +1,4 @@
1
- import { isBrowser } from "@modern-js/runtime";
2
- import { detectLanguageFromPath } from "../../utils.mjs";
3
- import { isI18nWrapperInstance } from "../instance.mjs";
4
- import { mergeDetectionOptions } from "./config.mjs";
5
- import { cacheUserLanguage, detectLanguage, readLanguageFromStorage, useI18nextLanguageDetector } from "./middleware.mjs";
6
- const detectorInstanceCache = new WeakMap();
7
- const DETECTOR_SAFE_OPTION_KEYS = [
8
- 'lowerCaseLng',
9
- 'nonExplicitSupportedLngs',
10
- 'load',
11
- 'partialBundledLanguages',
12
- 'returnNull',
13
- 'returnEmptyString',
14
- 'returnObjects',
15
- 'joinArrays',
16
- 'keySeparator',
17
- 'nsSeparator',
18
- 'pluralSeparator',
19
- 'contextSeparator',
20
- 'fallbackNS',
21
- 'ns',
22
- 'defaultNS',
23
- 'debug'
24
- ];
25
- const stableStringify = (value)=>{
26
- if (null == value) return JSON.stringify(value);
27
- if ('object' != typeof value) return JSON.stringify(value);
28
- if (Array.isArray(value)) return `[${value.map((item)=>stableStringify(item)).join(',')}]`;
29
- const sortedKeys = Object.keys(value).sort();
30
- const sortedEntries = sortedKeys.map((key)=>{
31
- const stringifiedValue = stableStringify(value[key]);
32
- return `${JSON.stringify(key)}:${stringifiedValue}`;
33
- });
34
- return `{${sortedEntries.join(',')}}`;
35
- };
36
- const buildDetectorConfigKey = (languages, fallbackLanguage, mergedDetection)=>stableStringify({
37
- languages,
38
- fallbackLanguage,
39
- detection: mergedDetection
40
- });
41
- const pickSafeDetectionOptions = (userInitOptions)=>{
42
- if (!userInitOptions) return {};
43
- const safeOptions = {};
44
- for (const key of DETECTOR_SAFE_OPTION_KEYS){
45
- const value = userInitOptions[key];
46
- if (void 0 !== value) safeOptions[key] = value;
47
- }
48
- if (userInitOptions.interpolation) safeOptions.interpolation = {
49
- ...userInitOptions.interpolation
50
- };
51
- return safeOptions;
52
- };
53
- const cleanupDetectorCacheEntry = (entry)=>{
54
- if (!entry || !entry.isTemporary) return;
55
- const instance = entry.instance;
56
- try {
57
- instance?.removeAllListeners?.();
58
- } catch (error) {}
59
- try {
60
- instance?.off?.('*');
61
- } catch (error) {}
62
- try {
63
- instance?.services?.backendConnector?.backend?.stop?.();
64
- } catch (error) {}
65
- try {
66
- instance?.services?.backendConnector?.backend?.close?.();
67
- } catch (error) {}
68
- };
69
- function exportServerLngToWindow(context, lng) {
70
- context.__i18nData__ = {
71
- lng
72
- };
73
- }
74
- const getLanguageFromSSRData = (window1)=>{
75
- try {
76
- const ssrData = window1._SSR_DATA;
77
- if (!ssrData || !ssrData.data || !ssrData.data.i18nData) return;
78
- const lng = ssrData.data.i18nData.lng;
79
- return 'string' == typeof lng && '' !== lng.trim() ? lng : void 0;
80
- } catch (error) {
81
- return;
82
- }
83
- };
84
- const normalizeLanguageCode = (language)=>{
85
- if (!language) return language;
86
- const baseLang = language.split('-')[0];
87
- return baseLang;
88
- };
89
- const isLanguageSupported = (language, supportedLanguages)=>{
90
- if (!language) return false;
91
- if (0 === supportedLanguages.length) return true;
92
- if (supportedLanguages.includes(language)) return true;
93
- const baseLang = normalizeLanguageCode(language);
94
- if (baseLang !== language && supportedLanguages.includes(baseLang)) return true;
95
- return false;
96
- };
97
- const getSupportedLanguage = (language, supportedLanguages)=>{
98
- if (!language) return;
99
- if (0 === supportedLanguages.length) return language;
100
- if (supportedLanguages.includes(language)) return language;
101
- const baseLang = normalizeLanguageCode(language);
102
- if (baseLang !== language && supportedLanguages.includes(baseLang)) return baseLang;
103
- };
104
- const detectLanguageFromSSR = (languages)=>{
105
- if (!isBrowser()) return;
106
- try {
107
- const ssrLanguage = getLanguageFromSSRData(window);
108
- if (ssrLanguage && isLanguageSupported(ssrLanguage, languages)) return ssrLanguage;
109
- } catch (error) {}
110
- };
111
- const detectLanguageFromPathPriority = (pathname, languages, localePathRedirect)=>{
112
- if (!localePathRedirect) return;
113
- if (!languages || 0 === languages.length) return;
114
- if (!pathname || '' === pathname.trim()) return;
115
- try {
116
- const pathDetection = detectLanguageFromPath(pathname, languages, localePathRedirect);
117
- if (true === pathDetection.detected && pathDetection.language) return pathDetection.language;
118
- } catch (error) {}
119
- };
120
- const createDetectorInstance = (baseInstance, configKey)=>{
121
- const cached = detectorInstanceCache.get(baseInstance);
122
- if (cached && cached.configKey === configKey) return {
123
- instance: cached.instance,
124
- isTemporary: cached.isTemporary
125
- };
126
- if (cached) {
127
- cleanupDetectorCacheEntry(cached);
128
- detectorInstanceCache.delete(baseInstance);
129
- }
130
- const createNewInstance = ()=>{
131
- if ('function' == typeof baseInstance.createInstance) try {
132
- const created = baseInstance.createInstance();
133
- if (created) return {
134
- instance: created,
135
- isTemporary: true
136
- };
137
- } catch (error) {}
138
- if ('function' == typeof baseInstance.cloneInstance) try {
139
- const cloned = baseInstance.cloneInstance();
140
- if (cloned) return {
141
- instance: cloned,
142
- isTemporary: true
143
- };
144
- } catch (error) {}
145
- return {
146
- instance: baseInstance,
147
- isTemporary: false
148
- };
149
- };
150
- const created = createNewInstance();
151
- if (created.isTemporary) detectorInstanceCache.set(baseInstance, {
152
- instance: created.instance,
153
- isTemporary: true,
154
- configKey
155
- });
156
- return created;
157
- };
158
- const initializeI18nForDetector = async (i18nInstance, options)=>{
159
- const mergedDetection = detection_mergeDetectionOptions(options.i18nextDetector, options.detection, options.localePathRedirect, options.userInitOptions);
160
- const configKey = buildDetectorConfigKey(options.languages, options.fallbackLanguage, mergedDetection);
161
- const { instance, isTemporary } = createDetectorInstance(i18nInstance, configKey);
162
- const safeUserOptions = pickSafeDetectionOptions(options.userInitOptions);
163
- const initOptions = {
164
- ...safeUserOptions,
165
- fallbackLng: options.fallbackLanguage,
166
- supportedLngs: options.languages,
167
- detection: mergedDetection,
168
- initImmediate: true,
169
- interpolation: {
170
- ...safeUserOptions?.interpolation || {},
171
- escapeValue: safeUserOptions?.interpolation?.escapeValue ?? false
172
- },
173
- react: {
174
- useSuspense: false
175
- }
176
- };
177
- useI18nextLanguageDetector(instance);
178
- if (instance.isInitialized) {
179
- if (isTemporary) await instance.init(initOptions);
180
- } else await instance.init(initOptions);
181
- return {
182
- detectorInstance: instance,
183
- isTemporary
184
- };
185
- };
186
- const detectLanguageFromI18nextDetector = async (i18nInstance, options)=>{
187
- if (!options.i18nextDetector) return;
188
- const mergedDetection = detection_mergeDetectionOptions(options.i18nextDetector, options.detection, options.localePathRedirect, options.userInitOptions);
189
- const { detectorInstance, isTemporary } = await initializeI18nForDetector(i18nInstance, options);
190
- try {
191
- const request = options.ssrContext?.request;
192
- if (!isBrowser() && !request) return;
193
- const detectorLang = detectLanguage(detectorInstance, request, mergedDetection);
194
- if (detectorLang) {
195
- const supportedLang = getSupportedLanguage(detectorLang, options.languages);
196
- if (supportedLang) return supportedLang;
197
- }
198
- if (detectorInstance.isInitialized && detectorInstance.language) {
199
- const currentLang = detectorInstance.language;
200
- if (isLanguageSupported(currentLang, options.languages)) return currentLang;
201
- }
202
- } catch (error) {} finally{
203
- if (isTemporary && detectorInstance !== i18nInstance) detectorInstanceCache.set(i18nInstance, {
204
- instance: detectorInstance,
205
- isTemporary: true,
206
- configKey: buildDetectorConfigKey(options.languages, options.fallbackLanguage, mergedDetection)
207
- });
208
- else if (detectorInstance === i18nInstance) {
209
- i18nInstance.isInitialized = false;
210
- delete i18nInstance.language;
211
- }
212
- }
213
- };
214
- const detectLanguageWithPriority = async (i18nInstance, options)=>{
215
- const { languages, fallbackLanguage, localePathRedirect, i18nextDetector, detection, userInitOptions, pathname, ssrContext } = options;
216
- let detectedLanguage;
217
- detectedLanguage = detectLanguageFromSSR(languages);
218
- if (!detectedLanguage) detectedLanguage = detectLanguageFromPathPriority(pathname, languages, localePathRedirect);
219
- if (!detectedLanguage && i18nextDetector) detectedLanguage = isI18nWrapperInstance(i18nInstance) ? readLanguageFromStorage(detection_mergeDetectionOptions(i18nextDetector, detection, localePathRedirect, userInitOptions)) : await detectLanguageFromI18nextDetector(i18nInstance, {
220
- languages,
221
- fallbackLanguage,
222
- localePathRedirect,
223
- i18nextDetector,
224
- detection,
225
- userInitOptions,
226
- mergedBackend: options.mergedBackend,
227
- ssrContext
228
- });
229
- const finalLanguage = detectedLanguage || userInitOptions?.lng || fallbackLanguage;
230
- return {
231
- detectedLanguage,
232
- finalLanguage
233
- };
234
- };
235
- const buildInitOptions = (params)=>{
236
- const { finalLanguage, fallbackLanguage, languages, userInitOptions, mergedDetection, mergeBackend } = params;
237
- return {
238
- ...userInitOptions || {},
239
- lng: finalLanguage,
240
- fallbackLng: fallbackLanguage,
241
- supportedLngs: languages,
242
- detection: mergedDetection,
243
- backend: mergeBackend,
244
- interpolation: {
245
- ...userInitOptions?.interpolation || {},
246
- escapeValue: userInitOptions?.interpolation?.escapeValue ?? false
247
- },
248
- react: {
249
- useSuspense: isBrowser()
250
- }
251
- };
252
- };
253
- const detection_mergeDetectionOptions = (i18nextDetector, detection, localePathRedirect, userInitOptions)=>{
254
- let mergedDetection;
255
- mergedDetection = i18nextDetector ? mergeDetectionOptions(detection, userInitOptions?.detection) : userInitOptions?.detection || {};
256
- if (!mergedDetection || 'object' != typeof mergedDetection) mergedDetection = {};
257
- if (localePathRedirect && mergedDetection.order) mergedDetection.order = mergedDetection.order.filter((item)=>'path' !== item);
258
- return mergedDetection;
259
- };
260
- export { buildInitOptions, cacheUserLanguage, detectLanguageWithPriority, detection_mergeDetectionOptions as mergeDetectionOptions, exportServerLngToWindow, getLanguageFromSSRData };
1
+ export { buildInitOptions, mergeDetectionOptions } from "./initOptions.mjs";
2
+ export { cacheUserLanguage } from "./middleware.mjs";
3
+ export { detectLanguageWithPriority } from "./priority.mjs";
4
+ export { exportServerLngToWindow, getLanguageFromSSRData } from "./ssr.mjs";
@@ -0,0 +1,28 @@
1
+ import { isBrowser } from "@modern-js/runtime";
2
+ import { mergeDetectionOptions } from "./config.mjs";
3
+ const buildInitOptions = (params)=>{
4
+ const { finalLanguage, fallbackLanguage, languages, userInitOptions, mergedDetection, mergeBackend } = params;
5
+ return {
6
+ ...userInitOptions || {},
7
+ lng: finalLanguage,
8
+ fallbackLng: fallbackLanguage,
9
+ supportedLngs: languages,
10
+ detection: mergedDetection,
11
+ backend: mergeBackend,
12
+ interpolation: {
13
+ ...userInitOptions?.interpolation || {},
14
+ escapeValue: userInitOptions?.interpolation?.escapeValue ?? false
15
+ },
16
+ react: {
17
+ useSuspense: isBrowser()
18
+ }
19
+ };
20
+ };
21
+ const initOptions_mergeDetectionOptions = (i18nextDetector, detection, localePathRedirect, userInitOptions)=>{
22
+ let mergedDetection;
23
+ mergedDetection = i18nextDetector ? mergeDetectionOptions(detection, userInitOptions?.detection) : userInitOptions?.detection || {};
24
+ if (!mergedDetection || 'object' != typeof mergedDetection) mergedDetection = {};
25
+ if (localePathRedirect && mergedDetection.order) mergedDetection.order = mergedDetection.order.filter((item)=>'path' !== item);
26
+ return mergedDetection;
27
+ };
28
+ export { buildInitOptions, initOptions_mergeDetectionOptions as mergeDetectionOptions };
@@ -0,0 +1,21 @@
1
+ const normalizeLanguageCode = (language)=>{
2
+ if (!language) return language;
3
+ const baseLang = language.split('-')[0];
4
+ return baseLang;
5
+ };
6
+ const isLanguageSupported = (language, supportedLanguages)=>{
7
+ if (!language) return false;
8
+ if (0 === supportedLanguages.length) return true;
9
+ if (supportedLanguages.includes(language)) return true;
10
+ const baseLang = normalizeLanguageCode(language);
11
+ if (baseLang !== language && supportedLanguages.includes(baseLang)) return true;
12
+ return false;
13
+ };
14
+ const getSupportedLanguage = (language, supportedLanguages)=>{
15
+ if (!language) return;
16
+ if (0 === supportedLanguages.length) return language;
17
+ if (supportedLanguages.includes(language)) return language;
18
+ const baseLang = normalizeLanguageCode(language);
19
+ if (baseLang !== language && supportedLanguages.includes(baseLang)) return baseLang;
20
+ };
21
+ export { getSupportedLanguage, isLanguageSupported };
@@ -0,0 +1,11 @@
1
+ import { detectLanguageFromPath } from "../../utils.mjs";
2
+ const detectLanguageFromPathPriority = (pathname, languages, localePathRedirect)=>{
3
+ if (!localePathRedirect) return;
4
+ if (!languages || 0 === languages.length) return;
5
+ if (!pathname || '' === pathname.trim()) return;
6
+ try {
7
+ const pathDetection = detectLanguageFromPath(pathname, languages, localePathRedirect);
8
+ if (true === pathDetection.detected && pathDetection.language) return pathDetection.language;
9
+ } catch (error) {}
10
+ };
11
+ export { detectLanguageFromPathPriority };
@@ -0,0 +1,28 @@
1
+ import { isI18nWrapperInstance } from "../instance.mjs";
2
+ import { detectLanguageFromI18nextDetector } from "./detector.mjs";
3
+ import { mergeDetectionOptions } from "./initOptions.mjs";
4
+ import { readLanguageFromStorage } from "./middleware.mjs";
5
+ import { detectLanguageFromPathPriority } from "./path.mjs";
6
+ import { detectLanguageFromSSR } from "./ssr.mjs";
7
+ const detectLanguageWithPriority = async (i18nInstance, options)=>{
8
+ const { languages, fallbackLanguage, localePathRedirect, i18nextDetector, detection, userInitOptions, pathname, ssrContext } = options;
9
+ let detectedLanguage;
10
+ detectedLanguage = detectLanguageFromPathPriority(pathname, languages, localePathRedirect);
11
+ if (!detectedLanguage) detectedLanguage = detectLanguageFromSSR(languages);
12
+ if (!detectedLanguage && i18nextDetector) detectedLanguage = isI18nWrapperInstance(i18nInstance) ? readLanguageFromStorage(mergeDetectionOptions(i18nextDetector, detection, localePathRedirect, userInitOptions)) : await detectLanguageFromI18nextDetector(i18nInstance, {
13
+ languages,
14
+ fallbackLanguage,
15
+ localePathRedirect,
16
+ i18nextDetector,
17
+ detection,
18
+ userInitOptions,
19
+ mergedBackend: options.mergedBackend,
20
+ ssrContext
21
+ });
22
+ const finalLanguage = detectedLanguage || userInitOptions?.lng || fallbackLanguage;
23
+ return {
24
+ detectedLanguage,
25
+ finalLanguage
26
+ };
27
+ };
28
+ export { detectLanguageWithPriority };
@@ -0,0 +1,26 @@
1
+ import { isBrowser } from "@modern-js/runtime";
2
+ import { getSupportedLanguage } from "./language.mjs";
3
+ function exportServerLngToWindow(context, lng) {
4
+ context.__i18nData__ = {
5
+ lng
6
+ };
7
+ }
8
+ const getLanguageFromSSRData = (window1)=>{
9
+ try {
10
+ const ssrData = window1._SSR_DATA;
11
+ if (!ssrData || !ssrData.data || !ssrData.data.i18nData) return;
12
+ const lng = ssrData.data.i18nData.lng;
13
+ return 'string' == typeof lng && '' !== lng.trim() ? lng : void 0;
14
+ } catch (error) {
15
+ return;
16
+ }
17
+ };
18
+ const detectLanguageFromSSR = (languages)=>{
19
+ if (!isBrowser()) return;
20
+ try {
21
+ const ssrLanguage = getLanguageFromSSRData(window);
22
+ const supportedLanguage = getSupportedLanguage(ssrLanguage, languages);
23
+ if (supportedLanguage) return supportedLanguage;
24
+ } catch (error) {}
25
+ };
26
+ export { detectLanguageFromSSR, exportServerLngToWindow, getLanguageFromSSRData };
File without changes
@@ -1,8 +1,10 @@
1
1
  function isI18nWrapperInstance(obj) {
2
2
  if (!obj || 'object' != typeof obj) return false;
3
- if (!obj.i18nInstance || 'object' != typeof obj.i18nInstance) return false;
4
- if (!obj.i18nInstance.instance) return false;
5
- if ('function' != typeof obj.init || 'function' != typeof obj.use) return false;
3
+ const candidate = obj;
4
+ if (!candidate.i18nInstance || 'object' != typeof candidate.i18nInstance) return false;
5
+ const wrapper = candidate.i18nInstance;
6
+ if (!wrapper.instance) return false;
7
+ if ('function' != typeof candidate.init || 'function' != typeof candidate.use) return false;
6
8
  return true;
7
9
  }
8
10
  function getI18nWrapperI18nextInstance(wrapperInstance) {
@@ -19,7 +21,8 @@ function getActualI18nextInstance(instance) {
19
21
  function isI18nInstance(obj) {
20
22
  if (!obj || 'object' != typeof obj) return false;
21
23
  if (isI18nWrapperInstance(obj)) return true;
22
- return 'function' == typeof obj.init && 'function' == typeof obj.use;
24
+ const candidate = obj;
25
+ return 'function' == typeof candidate.init && 'function' == typeof candidate.use;
23
26
  }
24
27
  async function tryImportI18next() {
25
28
  try {
@@ -40,12 +43,36 @@ async function createI18nextInstance() {
40
43
  return null;
41
44
  }
42
45
  }
43
- function getI18nextInstanceForProvider(instance) {
46
+ function getI18nextInstanceForProvider(instance, language) {
47
+ let providerInstance = instance;
44
48
  if (isI18nWrapperInstance(instance)) {
45
49
  const i18nextInstance = getI18nWrapperI18nextInstance(instance);
46
- if (i18nextInstance) return i18nextInstance;
50
+ if (i18nextInstance) providerInstance = i18nextInstance;
47
51
  }
48
- return instance;
52
+ if (!language || "u" < typeof Proxy) return providerInstance;
53
+ return new Proxy(providerInstance, {
54
+ get (target, property, receiver) {
55
+ if ('language' === property || 'resolvedLanguage' === property) return language;
56
+ if ('languages' === property) {
57
+ const languages = Reflect.get(target, property, receiver);
58
+ return Array.isArray(languages) ? [
59
+ language,
60
+ ...languages.filter((item)=>item !== language)
61
+ ] : [
62
+ language
63
+ ];
64
+ }
65
+ if ('t' === property && 'function' == typeof target.getFixedT) return target.getFixedT(language);
66
+ if ('hasLoadedNamespace' === property && 'function' == typeof target.hasLoadedNamespace) {
67
+ const hasLoadedNamespace = target.hasLoadedNamespace;
68
+ return (namespace, options)=>hasLoadedNamespace.call(target, namespace, {
69
+ ...options,
70
+ lng: options?.lng || language
71
+ });
72
+ }
73
+ return Reflect.get(target, property, receiver);
74
+ }
75
+ });
49
76
  }
50
77
  async function getI18nInstance(userInstance) {
51
78
  if (userInstance) {
@@ -56,4 +83,4 @@ async function getI18nInstance(userInstance) {
56
83
  if (i18nextInstance) return i18nextInstance;
57
84
  throw new Error('No i18n instance found');
58
85
  }
59
- export { getActualI18nextInstance, getI18nInstance, getI18nWrapperI18nextInstance, getI18nextInstanceForProvider, isI18nInstance, isI18nWrapperInstance };
86
+ export { getActualI18nextInstance, getI18nInstance, getI18nextInstanceForProvider, isI18nInstance, isI18nWrapperInstance };