@bleedingdev/modern-js-plugin-i18n 3.5.0-ultramodern.6 → 3.5.0-ultramodern.75
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/dist/cjs/cli/index.js +2 -1
- package/dist/cjs/cli/locales.js +15 -15
- package/dist/cjs/runtime/Link.js +8 -89
- package/dist/cjs/runtime/context.js +78 -91
- package/dist/cjs/runtime/contextHelpers.js +143 -0
- package/dist/cjs/runtime/core.js +32 -132
- package/dist/cjs/runtime/hooks.js +54 -21
- package/dist/cjs/runtime/i18n/backend/defaults.node.js +7 -7
- package/dist/cjs/runtime/i18n/detection/cache.js +150 -0
- package/dist/cjs/runtime/i18n/detection/detector.js +100 -0
- package/dist/cjs/runtime/i18n/detection/index.js +8 -263
- package/dist/cjs/runtime/i18n/detection/initOptions.js +69 -0
- package/dist/cjs/runtime/i18n/detection/language.js +62 -0
- package/dist/cjs/runtime/i18n/detection/path.js +49 -0
- package/dist/cjs/runtime/i18n/detection/priority.js +66 -0
- package/dist/cjs/runtime/i18n/detection/ssr.js +70 -0
- package/dist/cjs/runtime/i18n/detection/types.js +18 -0
- package/dist/cjs/runtime/i18n/instance.js +34 -10
- package/dist/cjs/runtime/linkHelpers.js +148 -0
- package/dist/cjs/runtime/pluginSetup.js +102 -0
- package/dist/cjs/runtime/providerComposition.js +115 -0
- package/dist/cjs/runtime/reactI18next.js +43 -0
- package/dist/cjs/runtime/routerAdapter.js +16 -8
- package/dist/cjs/runtime/utils.js +1 -9
- package/dist/cjs/server/apiPrefix.js +72 -0
- package/dist/cjs/server/detectorOptions.js +97 -0
- package/dist/cjs/server/index.js +17 -144
- package/dist/cjs/server/redirectPolicy.js +96 -0
- package/dist/cjs/shared/detection.js +2 -104
- package/dist/cjs/shared/localisedUrls/config.js +49 -0
- package/dist/cjs/shared/localisedUrls/index.js +91 -0
- package/dist/cjs/shared/localisedUrls/normalise.js +87 -0
- package/dist/cjs/shared/localisedUrls/pathname.js +68 -0
- package/dist/cjs/shared/localisedUrls/patterns.js +130 -0
- package/dist/cjs/shared/localisedUrls/redirect.js +103 -0
- package/dist/cjs/shared/localisedUrls/resolve.js +88 -0
- package/dist/cjs/shared/localisedUrls/routes.js +130 -0
- package/dist/cjs/shared/localisedUrls/types.js +18 -0
- package/dist/cjs/shared/localisedUrls.js +28 -294
- package/dist/cjs/shared/utils.js +2 -8
- package/dist/esm/cli/index.mjs +2 -1
- package/dist/esm/cli/locales.mjs +13 -13
- package/dist/esm/runtime/Link.mjs +1 -82
- package/dist/esm/runtime/context.mjs +76 -92
- package/dist/esm/runtime/contextHelpers.mjs +87 -0
- package/dist/esm/runtime/core.mjs +30 -133
- package/dist/esm/runtime/hooks.mjs +56 -23
- package/dist/esm/runtime/i18n/backend/defaults.node.mjs +5 -5
- package/dist/esm/runtime/i18n/detection/cache.mjs +103 -0
- package/dist/esm/runtime/i18n/detection/detector.mjs +62 -0
- package/dist/esm/runtime/i18n/detection/index.mjs +4 -260
- package/dist/esm/runtime/i18n/detection/initOptions.mjs +28 -0
- package/dist/esm/runtime/i18n/detection/language.mjs +21 -0
- package/dist/esm/runtime/i18n/detection/path.mjs +11 -0
- package/dist/esm/runtime/i18n/detection/priority.mjs +28 -0
- package/dist/esm/runtime/i18n/detection/ssr.mjs +26 -0
- package/dist/esm/runtime/i18n/detection/types.mjs +0 -0
- package/dist/esm/runtime/i18n/instance.mjs +35 -8
- package/dist/esm/runtime/linkHelpers.mjs +98 -0
- package/dist/esm/runtime/pluginSetup.mjs +64 -0
- package/dist/esm/runtime/providerComposition.mjs +77 -0
- package/dist/esm/runtime/reactI18next.mjs +5 -0
- package/dist/esm/runtime/routerAdapter.mjs +16 -8
- package/dist/esm/runtime/utils.mjs +2 -10
- package/dist/esm/server/apiPrefix.mjs +31 -0
- package/dist/esm/server/detectorOptions.mjs +59 -0
- package/dist/esm/server/index.mjs +8 -135
- package/dist/esm/server/redirectPolicy.mjs +46 -0
- package/dist/esm/shared/detection.mjs +1 -71
- package/dist/esm/shared/localisedUrls/config.mjs +11 -0
- package/dist/esm/shared/localisedUrls/index.mjs +7 -0
- package/dist/esm/shared/localisedUrls/normalise.mjs +37 -0
- package/dist/esm/shared/localisedUrls/pathname.mjs +24 -0
- package/dist/esm/shared/localisedUrls/patterns.mjs +86 -0
- package/dist/esm/shared/localisedUrls/redirect.mjs +56 -0
- package/dist/esm/shared/localisedUrls/resolve.mjs +47 -0
- package/dist/esm/shared/localisedUrls/routes.mjs +89 -0
- package/dist/esm/shared/localisedUrls/types.mjs +0 -0
- package/dist/esm/shared/localisedUrls.mjs +1 -283
- package/dist/esm/shared/utils.mjs +1 -1
- package/dist/esm-node/cli/index.mjs +2 -1
- package/dist/esm-node/cli/locales.mjs +13 -13
- package/dist/esm-node/runtime/Link.mjs +1 -82
- package/dist/esm-node/runtime/context.mjs +76 -92
- package/dist/esm-node/runtime/contextHelpers.mjs +88 -0
- package/dist/esm-node/runtime/core.mjs +30 -133
- package/dist/esm-node/runtime/hooks.mjs +56 -23
- package/dist/esm-node/runtime/i18n/backend/defaults.node.mjs +5 -5
- package/dist/esm-node/runtime/i18n/detection/cache.mjs +104 -0
- package/dist/esm-node/runtime/i18n/detection/detector.mjs +63 -0
- package/dist/esm-node/runtime/i18n/detection/index.mjs +4 -260
- package/dist/esm-node/runtime/i18n/detection/initOptions.mjs +29 -0
- package/dist/esm-node/runtime/i18n/detection/language.mjs +22 -0
- package/dist/esm-node/runtime/i18n/detection/path.mjs +12 -0
- package/dist/esm-node/runtime/i18n/detection/priority.mjs +29 -0
- package/dist/esm-node/runtime/i18n/detection/ssr.mjs +27 -0
- package/dist/esm-node/runtime/i18n/detection/types.mjs +1 -0
- package/dist/esm-node/runtime/i18n/instance.mjs +35 -8
- package/dist/esm-node/runtime/linkHelpers.mjs +99 -0
- package/dist/esm-node/runtime/pluginSetup.mjs +65 -0
- package/dist/esm-node/runtime/providerComposition.mjs +78 -0
- package/dist/esm-node/runtime/reactI18next.mjs +6 -0
- package/dist/esm-node/runtime/routerAdapter.mjs +16 -8
- package/dist/esm-node/runtime/utils.mjs +2 -10
- package/dist/esm-node/server/apiPrefix.mjs +32 -0
- package/dist/esm-node/server/detectorOptions.mjs +60 -0
- package/dist/esm-node/server/index.mjs +8 -135
- package/dist/esm-node/server/redirectPolicy.mjs +47 -0
- package/dist/esm-node/shared/detection.mjs +1 -71
- package/dist/esm-node/shared/localisedUrls/config.mjs +12 -0
- package/dist/esm-node/shared/localisedUrls/index.mjs +8 -0
- package/dist/esm-node/shared/localisedUrls/normalise.mjs +38 -0
- package/dist/esm-node/shared/localisedUrls/pathname.mjs +25 -0
- package/dist/esm-node/shared/localisedUrls/patterns.mjs +87 -0
- package/dist/esm-node/shared/localisedUrls/redirect.mjs +57 -0
- package/dist/esm-node/shared/localisedUrls/resolve.mjs +48 -0
- package/dist/esm-node/shared/localisedUrls/routes.mjs +90 -0
- package/dist/esm-node/shared/localisedUrls/types.mjs +1 -0
- package/dist/esm-node/shared/localisedUrls.mjs +1 -283
- package/dist/esm-node/shared/utils.mjs +1 -1
- package/dist/types/cli/locales.d.ts +1 -1
- package/dist/types/runtime/Link.d.ts +3 -7
- package/dist/types/runtime/context.d.ts +21 -5
- package/dist/types/runtime/contextHelpers.d.ts +29 -0
- package/dist/types/runtime/core.d.ts +4 -8
- package/dist/types/runtime/hooks.d.ts +3 -7
- package/dist/types/runtime/i18n/backend/middleware.common.d.ts +2 -1
- package/dist/types/runtime/i18n/detection/cache.d.ts +14 -0
- package/dist/types/runtime/i18n/detection/detector.d.ts +8 -0
- package/dist/types/runtime/i18n/detection/index.d.ts +5 -50
- package/dist/types/runtime/i18n/detection/initOptions.d.ts +10 -0
- package/dist/types/runtime/i18n/detection/language.d.ts +11 -0
- package/dist/types/runtime/i18n/detection/middleware.d.ts +4 -4
- package/dist/types/runtime/i18n/detection/middleware.node.d.ts +4 -4
- package/dist/types/runtime/i18n/detection/path.d.ts +5 -0
- package/dist/types/runtime/i18n/detection/priority.d.ts +10 -0
- package/dist/types/runtime/i18n/detection/ssr.d.ts +9 -0
- package/dist/types/runtime/i18n/detection/types.d.ts +32 -0
- package/dist/types/runtime/i18n/instance.d.ts +31 -27
- package/dist/types/runtime/i18n/react-i18next.d.ts +1 -6
- package/dist/types/runtime/i18n/utils.d.ts +14 -5
- package/dist/types/runtime/linkHelpers.d.ts +17 -0
- package/dist/types/runtime/pluginSetup.d.ts +32 -0
- package/dist/types/runtime/providerComposition.d.ts +16 -0
- package/dist/types/runtime/reactI18next.d.ts +7 -0
- package/dist/types/runtime/routerAdapter.d.ts +7 -6
- package/dist/types/runtime/utils.d.ts +7 -2
- package/dist/types/server/apiPrefix.d.ts +7 -0
- package/dist/types/server/detectorOptions.d.ts +21 -0
- package/dist/types/server/index.d.ts +1 -6
- package/dist/types/server/redirectPolicy.d.ts +22 -0
- package/dist/types/shared/detection.d.ts +1 -11
- package/dist/types/shared/localisedUrls/config.d.ts +9 -0
- package/dist/types/shared/localisedUrls/index.d.ts +9 -0
- package/dist/types/shared/localisedUrls/normalise.d.ts +10 -0
- package/dist/types/shared/localisedUrls/pathname.d.ts +4 -0
- package/dist/types/shared/localisedUrls/patterns.d.ts +5 -0
- package/dist/types/shared/localisedUrls/redirect.d.ts +11 -0
- package/dist/types/shared/localisedUrls/resolve.d.ts +7 -0
- package/dist/types/shared/localisedUrls/routes.d.ts +3 -0
- package/dist/types/shared/localisedUrls/types.d.ts +14 -0
- package/dist/types/shared/localisedUrls.d.ts +2 -36
- package/dist/types/shared/utils.d.ts +0 -2
- package/package.json +17 -20
- package/rstest.config.mts +8 -1
- package/src/cli/index.ts +5 -4
- package/src/cli/locales.ts +3 -3
- package/src/runtime/Link.tsx +11 -138
- package/src/runtime/context.tsx +170 -207
- package/src/runtime/contextHelpers.ts +242 -0
- package/src/runtime/core.tsx +48 -275
- package/src/runtime/hooks.ts +86 -24
- package/src/runtime/i18n/backend/defaults.node.ts +2 -2
- package/src/runtime/i18n/backend/middleware.common.ts +5 -4
- package/src/runtime/i18n/detection/cache.ts +189 -0
- package/src/runtime/i18n/detection/detector.ts +166 -0
- package/src/runtime/i18n/detection/index.ts +10 -641
- package/src/runtime/i18n/detection/initOptions.ts +74 -0
- package/src/runtime/i18n/detection/language.ts +64 -0
- package/src/runtime/i18n/detection/middleware.node.ts +26 -7
- package/src/runtime/i18n/detection/middleware.ts +15 -7
- package/src/runtime/i18n/detection/path.ts +41 -0
- package/src/runtime/i18n/detection/priority.ts +82 -0
- package/src/runtime/i18n/detection/ssr.ts +47 -0
- package/src/runtime/i18n/detection/types.ts +41 -0
- package/src/runtime/i18n/instance.ts +98 -34
- package/src/runtime/i18n/react-i18next.ts +1 -6
- package/src/runtime/i18n/utils.ts +18 -9
- package/src/runtime/linkHelpers.ts +174 -0
- package/src/runtime/pluginSetup.ts +189 -0
- package/src/runtime/providerComposition.tsx +148 -0
- package/src/runtime/reactI18next.ts +20 -0
- package/src/runtime/routerAdapter.tsx +36 -15
- package/src/runtime/utils.ts +13 -35
- package/src/server/apiPrefix.ts +67 -0
- package/src/server/detectorOptions.ts +105 -0
- package/src/server/index.ts +14 -319
- package/src/server/redirectPolicy.ts +127 -0
- package/src/shared/detection.ts +1 -1
- package/src/shared/localisedUrls/config.ts +18 -0
- package/src/shared/localisedUrls/index.ts +24 -0
- package/src/shared/localisedUrls/normalise.ts +67 -0
- package/src/shared/localisedUrls/pathname.ts +60 -0
- package/src/shared/localisedUrls/patterns.ts +160 -0
- package/src/shared/localisedUrls/redirect.ts +93 -0
- package/src/shared/localisedUrls/resolve.ts +135 -0
- package/src/shared/localisedUrls/routes.ts +234 -0
- package/src/shared/localisedUrls/types.ts +18 -0
- package/src/shared/localisedUrls.ts +26 -623
- package/src/shared/utils.ts +2 -2
- package/tests/federatedI18nBoundary.client.test.tsx +112 -0
- package/tests/federatedI18nBoundary.test.tsx +120 -0
- package/tests/fixtures/localised-routes.golden.json +156 -0
- package/tests/link.test.tsx +111 -0
- package/tests/localisedUrlRewriteMatrix.fork.test.ts +147 -0
- package/tests/localisedUrls.test.ts +148 -2
- package/tests/reactI18nextRuntimeBoundary.test.ts +6 -3
- package/tests/redirectPolicy.test.ts +45 -0
- package/tests/routerAdapter.test.tsx +603 -0
- package/tests/type-fixture/fixture-globals.d.ts +11 -0
- package/tests/type-fixture/tsconfig.json +1 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
I18nInitOptions,
|
|
3
|
+
I18nInstance,
|
|
4
|
+
LanguageDetectorOptions,
|
|
5
|
+
} from '../instance';
|
|
6
|
+
|
|
7
|
+
interface DetectorCacheEntry {
|
|
8
|
+
instance: I18nInstance;
|
|
9
|
+
isTemporary: boolean;
|
|
10
|
+
configKey: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type DetectorCleanupInstance = I18nInstance & {
|
|
14
|
+
removeAllListeners?: () => void;
|
|
15
|
+
off?: (event: string) => void;
|
|
16
|
+
services?: I18nInstance['services'] & {
|
|
17
|
+
backendConnector?: {
|
|
18
|
+
backend?: {
|
|
19
|
+
stop?: () => void;
|
|
20
|
+
close?: () => void;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const detectorInstanceCache = new WeakMap<
|
|
27
|
+
I18nInstance,
|
|
28
|
+
DetectorCacheEntry
|
|
29
|
+
>();
|
|
30
|
+
|
|
31
|
+
const DETECTOR_SAFE_OPTION_KEYS: string[] = [
|
|
32
|
+
'lowerCaseLng',
|
|
33
|
+
'nonExplicitSupportedLngs',
|
|
34
|
+
'load',
|
|
35
|
+
'partialBundledLanguages',
|
|
36
|
+
'returnNull',
|
|
37
|
+
'returnEmptyString',
|
|
38
|
+
'returnObjects',
|
|
39
|
+
'joinArrays',
|
|
40
|
+
'keySeparator',
|
|
41
|
+
'nsSeparator',
|
|
42
|
+
'pluralSeparator',
|
|
43
|
+
'contextSeparator',
|
|
44
|
+
'fallbackNS',
|
|
45
|
+
'ns',
|
|
46
|
+
'defaultNS',
|
|
47
|
+
'debug',
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Stable stringify that sorts object keys to ensure consistent output
|
|
52
|
+
* regardless of property order
|
|
53
|
+
*/
|
|
54
|
+
const stableStringify = (value: unknown): string => {
|
|
55
|
+
if (value === null || value === undefined) {
|
|
56
|
+
return JSON.stringify(value);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (typeof value !== 'object') {
|
|
60
|
+
return JSON.stringify(value);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (Array.isArray(value)) {
|
|
64
|
+
// Arrays maintain their order
|
|
65
|
+
return `[${value.map(item => stableStringify(item)).join(',')}]`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// For objects, sort keys and recursively stringify values
|
|
69
|
+
const record = value as Record<string, unknown>;
|
|
70
|
+
const sortedKeys = Object.keys(record).sort();
|
|
71
|
+
const sortedEntries = sortedKeys.map(key => {
|
|
72
|
+
const stringifiedValue = stableStringify(record[key]);
|
|
73
|
+
return `${JSON.stringify(key)}:${stringifiedValue}`;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return `{${sortedEntries.join(',')}}`;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const buildDetectorConfigKey = (
|
|
80
|
+
languages: string[],
|
|
81
|
+
fallbackLanguage: string,
|
|
82
|
+
mergedDetection: LanguageDetectorOptions,
|
|
83
|
+
): string => {
|
|
84
|
+
return stableStringify({
|
|
85
|
+
languages,
|
|
86
|
+
fallbackLanguage,
|
|
87
|
+
detection: mergedDetection,
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const pickSafeDetectionOptions = (
|
|
92
|
+
userInitOptions?: I18nInitOptions,
|
|
93
|
+
): Partial<I18nInitOptions> & Record<string, unknown> => {
|
|
94
|
+
if (!userInitOptions) {
|
|
95
|
+
return {};
|
|
96
|
+
}
|
|
97
|
+
const safeOptions: Partial<I18nInitOptions> & Record<string, unknown> = {};
|
|
98
|
+
for (const key of DETECTOR_SAFE_OPTION_KEYS) {
|
|
99
|
+
const value = (userInitOptions as Record<string, unknown>)[key];
|
|
100
|
+
if (value !== undefined) {
|
|
101
|
+
safeOptions[key] = value;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (userInitOptions.interpolation) {
|
|
105
|
+
safeOptions.interpolation = { ...userInitOptions.interpolation };
|
|
106
|
+
}
|
|
107
|
+
return safeOptions;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const cleanupDetectorCacheEntry = (entry?: DetectorCacheEntry) => {
|
|
111
|
+
if (!entry || !entry.isTemporary) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const instance = entry.instance as DetectorCleanupInstance;
|
|
115
|
+
try {
|
|
116
|
+
instance?.removeAllListeners?.();
|
|
117
|
+
} catch (error) {
|
|
118
|
+
void error;
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
instance?.off?.('*');
|
|
122
|
+
} catch (error) {
|
|
123
|
+
void error;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
instance?.services?.backendConnector?.backend?.stop?.();
|
|
127
|
+
} catch (error) {
|
|
128
|
+
void error;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
instance?.services?.backendConnector?.backend?.close?.();
|
|
132
|
+
} catch (error) {
|
|
133
|
+
void error;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export const createDetectorInstance = (
|
|
138
|
+
baseInstance: I18nInstance,
|
|
139
|
+
configKey: string,
|
|
140
|
+
): { instance: I18nInstance; isTemporary: boolean } => {
|
|
141
|
+
const cached = detectorInstanceCache.get(baseInstance);
|
|
142
|
+
if (cached && cached.configKey === configKey) {
|
|
143
|
+
return { instance: cached.instance, isTemporary: cached.isTemporary };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (cached) {
|
|
147
|
+
cleanupDetectorCacheEntry(cached);
|
|
148
|
+
detectorInstanceCache.delete(baseInstance);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const createNewInstance = (): {
|
|
152
|
+
instance: I18nInstance;
|
|
153
|
+
isTemporary: boolean;
|
|
154
|
+
} => {
|
|
155
|
+
if (typeof baseInstance.createInstance === 'function') {
|
|
156
|
+
try {
|
|
157
|
+
const created = baseInstance.createInstance();
|
|
158
|
+
if (created) {
|
|
159
|
+
return { instance: created, isTemporary: true };
|
|
160
|
+
}
|
|
161
|
+
} catch (error) {
|
|
162
|
+
void error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (typeof baseInstance.cloneInstance === 'function') {
|
|
167
|
+
try {
|
|
168
|
+
const cloned = baseInstance.cloneInstance();
|
|
169
|
+
if (cloned) {
|
|
170
|
+
return { instance: cloned, isTemporary: true };
|
|
171
|
+
}
|
|
172
|
+
} catch (error) {
|
|
173
|
+
void error;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return { instance: baseInstance, isTemporary: false };
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const created = createNewInstance();
|
|
181
|
+
if (created.isTemporary) {
|
|
182
|
+
detectorInstanceCache.set(baseInstance, {
|
|
183
|
+
instance: created.instance,
|
|
184
|
+
isTemporary: true,
|
|
185
|
+
configKey,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
return created;
|
|
189
|
+
};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { isBrowser } from '@modern-js/runtime';
|
|
2
|
+
import type { I18nInitOptions, I18nInstance } from '../instance';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
buildDetectorConfigKey,
|
|
6
|
+
createDetectorInstance,
|
|
7
|
+
detectorInstanceCache,
|
|
8
|
+
pickSafeDetectionOptions,
|
|
9
|
+
} from './cache';
|
|
10
|
+
|
|
11
|
+
import { mergeDetectionOptions } from './initOptions';
|
|
12
|
+
|
|
13
|
+
import { getSupportedLanguage, isLanguageSupported } from './language';
|
|
14
|
+
|
|
15
|
+
import { detectLanguage, useI18nextLanguageDetector } from './middleware';
|
|
16
|
+
|
|
17
|
+
import type {
|
|
18
|
+
BaseLanguageDetectionOptions,
|
|
19
|
+
LanguageDetectionSsrContext,
|
|
20
|
+
} from './types';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Initialize i18n instance for detector if needed
|
|
24
|
+
*/
|
|
25
|
+
interface DetectorInitResult {
|
|
26
|
+
detectorInstance: I18nInstance;
|
|
27
|
+
isTemporary: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type MutableDetectorInstanceState = Omit<I18nInstance, 'language'> & {
|
|
31
|
+
language?: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const initializeI18nForDetector = async (
|
|
35
|
+
i18nInstance: I18nInstance,
|
|
36
|
+
options: BaseLanguageDetectionOptions,
|
|
37
|
+
): Promise<DetectorInitResult> => {
|
|
38
|
+
const mergedDetection = mergeDetectionOptions(
|
|
39
|
+
options.i18nextDetector,
|
|
40
|
+
options.detection,
|
|
41
|
+
options.localePathRedirect,
|
|
42
|
+
options.userInitOptions,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const configKey = buildDetectorConfigKey(
|
|
46
|
+
options.languages,
|
|
47
|
+
options.fallbackLanguage,
|
|
48
|
+
mergedDetection,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const { instance, isTemporary } = createDetectorInstance(
|
|
52
|
+
i18nInstance,
|
|
53
|
+
configKey,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const safeUserOptions = pickSafeDetectionOptions(options.userInitOptions);
|
|
57
|
+
|
|
58
|
+
// Only initialize detection capability, don't load resources to avoid conflicts with subsequent backend initialization
|
|
59
|
+
const initOptions: I18nInitOptions = {
|
|
60
|
+
...safeUserOptions,
|
|
61
|
+
fallbackLng: options.fallbackLanguage,
|
|
62
|
+
supportedLngs: options.languages,
|
|
63
|
+
detection: mergedDetection,
|
|
64
|
+
initImmediate: true,
|
|
65
|
+
interpolation: {
|
|
66
|
+
...(safeUserOptions?.interpolation || {}),
|
|
67
|
+
escapeValue: safeUserOptions?.interpolation?.escapeValue ?? false,
|
|
68
|
+
},
|
|
69
|
+
react: {
|
|
70
|
+
useSuspense: false,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Ensure the detector instance has the language detection plugin loaded
|
|
75
|
+
useI18nextLanguageDetector(instance);
|
|
76
|
+
|
|
77
|
+
if (!instance.isInitialized) {
|
|
78
|
+
await instance.init(initOptions);
|
|
79
|
+
} else if (isTemporary) {
|
|
80
|
+
await instance.init(initOptions);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return { detectorInstance: instance, isTemporary };
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Priority 3: Detect language using i18next detector
|
|
88
|
+
*/
|
|
89
|
+
export const detectLanguageFromI18nextDetector = async (
|
|
90
|
+
i18nInstance: I18nInstance,
|
|
91
|
+
options: BaseLanguageDetectionOptions & {
|
|
92
|
+
ssrContext?: LanguageDetectionSsrContext;
|
|
93
|
+
},
|
|
94
|
+
): Promise<string | undefined> => {
|
|
95
|
+
if (!options.i18nextDetector) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Merge detection options to pass to detector
|
|
100
|
+
const mergedDetection = mergeDetectionOptions(
|
|
101
|
+
options.i18nextDetector,
|
|
102
|
+
options.detection,
|
|
103
|
+
options.localePathRedirect,
|
|
104
|
+
options.userInitOptions,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const { detectorInstance, isTemporary } = await initializeI18nForDetector(
|
|
108
|
+
i18nInstance,
|
|
109
|
+
options,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
const request = options.ssrContext?.request;
|
|
114
|
+
if (!isBrowser() && !request) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const detectorLang = detectLanguage(
|
|
119
|
+
detectorInstance,
|
|
120
|
+
request,
|
|
121
|
+
mergedDetection,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
// Use getSupportedLanguage to get the matching supported language
|
|
125
|
+
// This handles both exact match and base language code match (e.g., 'zh-CN' -> 'zh')
|
|
126
|
+
if (detectorLang) {
|
|
127
|
+
const supportedLang = getSupportedLanguage(
|
|
128
|
+
detectorLang,
|
|
129
|
+
options.languages,
|
|
130
|
+
);
|
|
131
|
+
if (supportedLang) {
|
|
132
|
+
return supportedLang;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Fallback to instance's current language if detector didn't detect
|
|
137
|
+
if (detectorInstance.isInitialized && detectorInstance.language) {
|
|
138
|
+
const currentLang = detectorInstance.language;
|
|
139
|
+
if (isLanguageSupported(currentLang, options.languages)) {
|
|
140
|
+
return currentLang;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
} catch (error) {
|
|
144
|
+
// Silently ignore errors
|
|
145
|
+
} finally {
|
|
146
|
+
// Clean up temporary instance to avoid affecting subsequent formal initialization
|
|
147
|
+
if (isTemporary && detectorInstance !== i18nInstance) {
|
|
148
|
+
// Temporary instance is saved in cache for reuse
|
|
149
|
+
detectorInstanceCache.set(i18nInstance, {
|
|
150
|
+
instance: detectorInstance,
|
|
151
|
+
isTemporary: true,
|
|
152
|
+
configKey: buildDetectorConfigKey(
|
|
153
|
+
options.languages,
|
|
154
|
+
options.fallbackLanguage,
|
|
155
|
+
mergedDetection,
|
|
156
|
+
),
|
|
157
|
+
});
|
|
158
|
+
} else if (detectorInstance === i18nInstance) {
|
|
159
|
+
// As a fallback, prevent i18nInstance from being polluted by detector init
|
|
160
|
+
i18nInstance.isInitialized = false;
|
|
161
|
+
delete (i18nInstance as unknown as MutableDetectorInstanceState).language;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return undefined;
|
|
166
|
+
};
|