@modern-js/plugin-i18n 3.9.2 → 3.10.0
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/runtime/i18n/detection/index.js +1 -1
- package/dist/cjs/runtime/i18n/detection/middleware.js +1 -1
- package/dist/cjs/runtime/i18n/detection/middleware.node.js +6 -1
- package/dist/cjs/runtime/i18n/instance.js +12 -14
- package/dist/cjs/runtime/utils.js +72 -88
- package/dist/cjs/shared/detection.js +20 -6
- package/dist/esm/runtime/i18n/detection/index.mjs +1 -1
- package/dist/esm/runtime/i18n/detection/middleware.mjs +1 -1
- package/dist/esm/runtime/i18n/detection/middleware.node.mjs +6 -1
- package/dist/esm/runtime/i18n/instance.mjs +12 -14
- package/dist/esm/runtime/utils.mjs +1 -7
- package/dist/esm/shared/detection.mjs +20 -6
- package/dist/esm-node/runtime/i18n/detection/index.mjs +1 -1
- package/dist/esm-node/runtime/i18n/detection/middleware.mjs +1 -1
- package/dist/esm-node/runtime/i18n/detection/middleware.node.mjs +6 -1
- package/dist/esm-node/runtime/i18n/instance.mjs +12 -14
- package/dist/esm-node/runtime/utils.mjs +1 -9
- package/dist/esm-node/shared/detection.mjs +20 -6
- package/dist/types/runtime/i18n/detection/middleware.d.ts +3 -2
- package/dist/types/runtime/i18n/detection/middleware.node.d.ts +3 -6
- package/dist/types/runtime/i18n/index.d.ts +1 -1
- package/dist/types/runtime/utils.d.ts +8 -3
- package/dist/types/shared/detection.d.ts +2 -2
- package/package.json +19 -20
- package/rstack.config.mts +4 -0
- package/rstest.config.mts +7 -0
- package/src/runtime/i18n/backend/sdk-event.ts +1 -2
- package/src/runtime/i18n/detection/index.ts +2 -0
- package/src/runtime/i18n/detection/middleware.node.ts +15 -8
- package/src/runtime/i18n/detection/middleware.ts +5 -2
- package/src/runtime/i18n/index.ts +1 -4
- package/src/runtime/i18n/instance.ts +10 -17
- package/src/runtime/utils.ts +1 -5
- package/src/shared/detection.ts +33 -9
- package/src/shared/utils.ts +1 -2
- package/tests/detection.test.ts +37 -0
- package/dist/esm/rslib-runtime~1.mjs +0 -18
- package/dist/esm-node/rslib-runtime~0.mjs +0 -19
- package/rslib.config.mts +0 -4
|
@@ -253,7 +253,7 @@ const detectLanguageWithPriority = async (i18nInstance, options)=>{
|
|
|
253
253
|
let detectedLanguage;
|
|
254
254
|
detectedLanguage = detectLanguageFromSSR(languages);
|
|
255
255
|
if (!detectedLanguage) detectedLanguage = detectLanguageFromPathPriority(pathname, languages, localePathRedirect);
|
|
256
|
-
if (!detectedLanguage && i18nextDetector) detectedLanguage = (0, external_instance_js_namespaceObject.isI18nWrapperInstance)(i18nInstance) ? (0, external_middleware_js_namespaceObject.readLanguageFromStorage)(mergeDetectionOptions(i18nextDetector, detection, localePathRedirect, userInitOptions)) : await detectLanguageFromI18nextDetector(i18nInstance, {
|
|
256
|
+
if (!detectedLanguage && i18nextDetector) detectedLanguage = (0, external_instance_js_namespaceObject.isI18nWrapperInstance)(i18nInstance) ? (0, external_middleware_js_namespaceObject.readLanguageFromStorage)(mergeDetectionOptions(i18nextDetector, detection, localePathRedirect, userInitOptions), ssrContext?.request, languages) : await detectLanguageFromI18nextDetector(i18nInstance, {
|
|
257
257
|
languages,
|
|
258
258
|
fallbackLanguage,
|
|
259
259
|
localePathRedirect,
|
|
@@ -56,7 +56,7 @@ const useI18nextLanguageDetector = (i18nInstance)=>{
|
|
|
56
56
|
}
|
|
57
57
|
return i18nInstance;
|
|
58
58
|
};
|
|
59
|
-
const readLanguageFromStorage = (detectionOptions)=>{
|
|
59
|
+
const readLanguageFromStorage = (detectionOptions, _request, _languages)=>{
|
|
60
60
|
try {
|
|
61
61
|
const options = detectionOptions || {};
|
|
62
62
|
const order = options.order || [
|
|
@@ -34,8 +34,13 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
34
34
|
useI18nextLanguageDetector: ()=>useI18nextLanguageDetector
|
|
35
35
|
});
|
|
36
36
|
const external_i18next_http_middleware_namespaceObject = require("i18next-http-middleware");
|
|
37
|
+
const detection_js_namespaceObject = require("../../../shared/detection.js");
|
|
37
38
|
const cacheUserLanguage = (_i18nInstance, _language, _detectionOptions)=>{};
|
|
38
|
-
const readLanguageFromStorage = (
|
|
39
|
+
const readLanguageFromStorage = (detectionOptions, request, languages = [])=>{
|
|
40
|
+
if (!request) return;
|
|
41
|
+
const detectedLanguage = (0, detection_js_namespaceObject.detectLanguageFromRequest)(request, languages, detectionOptions);
|
|
42
|
+
return detectedLanguage ?? void 0;
|
|
43
|
+
};
|
|
39
44
|
const useI18nextLanguageDetector = (i18nInstance)=>{
|
|
40
45
|
if (!i18nInstance.isInitialized) return i18nInstance.use(external_i18next_http_middleware_namespaceObject.LanguageDetector);
|
|
41
46
|
return i18nInstance;
|
|
@@ -69,14 +69,6 @@ async function createI18nextInstance() {
|
|
|
69
69
|
return null;
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
-
async function tryImportReactI18next() {
|
|
73
|
-
try {
|
|
74
|
-
const reactI18next = await import("react-i18next");
|
|
75
|
-
return reactI18next;
|
|
76
|
-
} catch (error) {
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
72
|
function getI18nextInstanceForProvider(instance) {
|
|
81
73
|
if (isI18nWrapperInstance(instance)) {
|
|
82
74
|
const i18nextInstance = getI18nWrapperI18nextInstance(instance);
|
|
@@ -94,14 +86,20 @@ async function getI18nInstance(userInstance) {
|
|
|
94
86
|
throw new Error('No i18n instance found');
|
|
95
87
|
}
|
|
96
88
|
async function getInitReactI18next() {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
89
|
+
try {
|
|
90
|
+
const { initReactI18next } = await import("react-i18next");
|
|
91
|
+
return initReactI18next;
|
|
92
|
+
} catch (error) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
100
95
|
}
|
|
101
96
|
async function getI18nextProvider() {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
97
|
+
try {
|
|
98
|
+
const { I18nextProvider } = await import("react-i18next");
|
|
99
|
+
return I18nextProvider;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
105
103
|
}
|
|
106
104
|
__webpack_require__.d(__webpack_exports__, {
|
|
107
105
|
getActualI18nextInstance: ()=>getActualI18nextInstance,
|
|
@@ -1,19 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
"@modern-js/runtime/router" (module) {
|
|
4
|
-
module.exports = require("@modern-js/runtime/router");
|
|
5
|
-
}
|
|
6
|
-
};
|
|
7
|
-
var __webpack_module_cache__ = {};
|
|
8
|
-
function __webpack_require__(moduleId) {
|
|
9
|
-
var cachedModule = __webpack_module_cache__[moduleId];
|
|
10
|
-
if (void 0 !== cachedModule) return cachedModule.exports;
|
|
11
|
-
var module = __webpack_module_cache__[moduleId] = {
|
|
12
|
-
exports: {}
|
|
13
|
-
};
|
|
14
|
-
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
15
|
-
return module.exports;
|
|
16
|
-
}
|
|
2
|
+
var __webpack_require__ = {};
|
|
17
3
|
(()=>{
|
|
18
4
|
__webpack_require__.d = (exports1, getters, values)=>{
|
|
19
5
|
var define = (defs, kind)=>{
|
|
@@ -40,85 +26,83 @@ function __webpack_require__(moduleId) {
|
|
|
40
26
|
};
|
|
41
27
|
})();
|
|
42
28
|
var __webpack_exports__ = {};
|
|
43
|
-
(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
29
|
+
__webpack_require__.r(__webpack_exports__);
|
|
30
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
31
|
+
buildLocalizedUrl: ()=>buildLocalizedUrl,
|
|
32
|
+
detectLanguageFromPath: ()=>detectLanguageFromPath,
|
|
33
|
+
getEntryPath: ()=>getEntryPath,
|
|
34
|
+
getLanguageFromPath: ()=>getLanguageFromPath,
|
|
35
|
+
getPathname: ()=>getPathname,
|
|
36
|
+
shouldIgnoreRedirect: ()=>shouldIgnoreRedirect,
|
|
37
|
+
useRouterHooks: ()=>useRouterHooks
|
|
38
|
+
});
|
|
39
|
+
const runtime_namespaceObject = require("@modern-js/runtime");
|
|
40
|
+
const context_namespaceObject = require("@modern-js/runtime/context");
|
|
41
|
+
const router_namespaceObject = require("@modern-js/runtime/router");
|
|
42
|
+
const getPathname = (context)=>{
|
|
43
|
+
if ((0, runtime_namespaceObject.isBrowser)()) return window.location.pathname;
|
|
44
|
+
return context.ssrContext?.request?.pathname || '/';
|
|
45
|
+
};
|
|
46
|
+
const getEntryPath = ()=>{
|
|
47
|
+
const basename = (0, context_namespaceObject.getGlobalBasename)();
|
|
48
|
+
if (basename) return '/' === basename ? '' : basename;
|
|
49
|
+
return '';
|
|
50
|
+
};
|
|
51
|
+
const getLanguageFromPath = (pathname, languages, fallbackLanguage)=>{
|
|
52
|
+
const segments = pathname.split('/').filter(Boolean);
|
|
53
|
+
const firstSegment = segments[0];
|
|
54
|
+
if (languages.includes(firstSegment)) return firstSegment;
|
|
55
|
+
return fallbackLanguage;
|
|
56
|
+
};
|
|
57
|
+
const buildLocalizedUrl = (pathname, language, languages)=>{
|
|
58
|
+
const segments = pathname.split('/').filter(Boolean);
|
|
59
|
+
if (segments.length > 0 && languages.includes(segments[0])) segments[0] = language;
|
|
60
|
+
else segments.unshift(language);
|
|
61
|
+
return `/${segments.join('/')}`;
|
|
62
|
+
};
|
|
63
|
+
const detectLanguageFromPath = (pathname, languages, localePathRedirect)=>{
|
|
64
|
+
if (!localePathRedirect) return {
|
|
65
|
+
detected: false
|
|
64
66
|
};
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
const entryPath = getEntryPath();
|
|
68
|
+
const relativePath = pathname.replace(entryPath, '');
|
|
69
|
+
const segments = relativePath.split('/').filter(Boolean);
|
|
70
|
+
const segmentsToCheck = !entryPath && segments.length > 1 && segments[0] && !languages.includes(segments[0]) ? segments.slice(1) : segments;
|
|
71
|
+
const firstSegment = segmentsToCheck[0];
|
|
72
|
+
if (firstSegment && languages.includes(firstSegment)) return {
|
|
73
|
+
detected: true,
|
|
74
|
+
language: firstSegment
|
|
70
75
|
};
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (segments.length > 0 && languages.includes(segments[0])) segments[0] = language;
|
|
74
|
-
else segments.unshift(language);
|
|
75
|
-
return `/${segments.join('/')}`;
|
|
76
|
+
return {
|
|
77
|
+
detected: false
|
|
76
78
|
};
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
79
|
+
};
|
|
80
|
+
const shouldIgnoreRedirect = (pathname, languages, ignoreRedirectRoutes)=>{
|
|
81
|
+
if (!ignoreRedirectRoutes) return false;
|
|
82
|
+
const segments = pathname.split('/').filter(Boolean);
|
|
83
|
+
let pathWithoutLang = pathname;
|
|
84
|
+
if (segments.length > 0 && languages.includes(segments[0])) pathWithoutLang = `/${segments.slice(1).join('/')}`;
|
|
85
|
+
const normalizedPath = pathWithoutLang.startsWith('/') ? pathWithoutLang : `/${pathWithoutLang}`;
|
|
86
|
+
if ('function' == typeof ignoreRedirectRoutes) return ignoreRedirectRoutes(normalizedPath);
|
|
87
|
+
return ignoreRedirectRoutes.some((pattern)=>normalizedPath === pattern || normalizedPath.startsWith(`${pattern}/`));
|
|
88
|
+
};
|
|
89
|
+
const useRouterHooks = ()=>{
|
|
90
|
+
try {
|
|
91
|
+
return {
|
|
92
|
+
navigate: (0, router_namespaceObject.useNavigate)(),
|
|
93
|
+
location: (0, router_namespaceObject.useLocation)(),
|
|
94
|
+
params: (0, router_namespaceObject.useParams)(),
|
|
95
|
+
hasRouter: true
|
|
89
96
|
};
|
|
97
|
+
} catch (error) {
|
|
90
98
|
return {
|
|
91
|
-
|
|
99
|
+
navigate: null,
|
|
100
|
+
location: null,
|
|
101
|
+
params: {},
|
|
102
|
+
hasRouter: false
|
|
92
103
|
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (!ignoreRedirectRoutes) return false;
|
|
96
|
-
const segments = pathname.split('/').filter(Boolean);
|
|
97
|
-
let pathWithoutLang = pathname;
|
|
98
|
-
if (segments.length > 0 && languages.includes(segments[0])) pathWithoutLang = `/${segments.slice(1).join('/')}`;
|
|
99
|
-
const normalizedPath = pathWithoutLang.startsWith('/') ? pathWithoutLang : `/${pathWithoutLang}`;
|
|
100
|
-
if ('function' == typeof ignoreRedirectRoutes) return ignoreRedirectRoutes(normalizedPath);
|
|
101
|
-
return ignoreRedirectRoutes.some((pattern)=>normalizedPath === pattern || normalizedPath.startsWith(`${pattern}/`));
|
|
102
|
-
};
|
|
103
|
-
const useRouterHooks = ()=>{
|
|
104
|
-
try {
|
|
105
|
-
const { useLocation, useNavigate, useParams } = __webpack_require__("@modern-js/runtime/router");
|
|
106
|
-
return {
|
|
107
|
-
navigate: useNavigate(),
|
|
108
|
-
location: useLocation(),
|
|
109
|
-
params: useParams(),
|
|
110
|
-
hasRouter: true
|
|
111
|
-
};
|
|
112
|
-
} catch (error) {
|
|
113
|
-
return {
|
|
114
|
-
navigate: null,
|
|
115
|
-
location: null,
|
|
116
|
-
params: {},
|
|
117
|
-
hasRouter: false
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
})();
|
|
104
|
+
}
|
|
105
|
+
};
|
|
122
106
|
exports.buildLocalizedUrl = __webpack_exports__.buildLocalizedUrl;
|
|
123
107
|
exports.detectLanguageFromPath = __webpack_exports__.detectLanguageFromPath;
|
|
124
108
|
exports.getEntryPath = __webpack_exports__.getEntryPath;
|
|
@@ -47,8 +47,11 @@ function detectLanguageFromRequest(req, languages, detectionOptions) {
|
|
|
47
47
|
'header'
|
|
48
48
|
];
|
|
49
49
|
const getHeader = (name)=>{
|
|
50
|
-
req.headers
|
|
51
|
-
return
|
|
50
|
+
const headers = req.headers;
|
|
51
|
+
if ('function' == typeof headers?.get) return headers.get(name);
|
|
52
|
+
const value = headers[name.toLowerCase()];
|
|
53
|
+
if (Array.isArray(value)) return value[0] ?? null;
|
|
54
|
+
return value ?? null;
|
|
52
55
|
};
|
|
53
56
|
for (const method of detectionOrder){
|
|
54
57
|
let detectedLang = null;
|
|
@@ -83,19 +86,30 @@ function detectLanguageFromRequest(req, languages, detectionOptions) {
|
|
|
83
86
|
const languagesList = acceptLanguage.split(',').map((lang)=>{
|
|
84
87
|
const [code, q] = lang.trim().split(';');
|
|
85
88
|
return {
|
|
86
|
-
code
|
|
89
|
+
code,
|
|
90
|
+
baseCode: code.split('-')[0],
|
|
87
91
|
quality: q ? parseFloat(q.split('=')[1]) : 1.0
|
|
88
92
|
};
|
|
89
93
|
}).sort((a, b)=>b.quality - a.quality);
|
|
90
|
-
for (const lang of languagesList)
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
for (const lang of languagesList){
|
|
95
|
+
if (0 === languages.length || languages.includes(lang.code)) {
|
|
96
|
+
detectedLang = lang.code;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
if (languages.includes(lang.baseCode)) {
|
|
100
|
+
detectedLang = lang.baseCode;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
93
103
|
}
|
|
94
104
|
}
|
|
95
105
|
break;
|
|
96
106
|
}
|
|
97
107
|
}
|
|
98
108
|
if (detectedLang && (0 === languages.length || languages.includes(detectedLang))) return detectedLang;
|
|
109
|
+
if (detectedLang) {
|
|
110
|
+
const baseLang = detectedLang.split('-')[0];
|
|
111
|
+
if (languages.includes(baseLang)) return baseLang;
|
|
112
|
+
}
|
|
99
113
|
}
|
|
100
114
|
} catch (error) {}
|
|
101
115
|
return null;
|
|
@@ -216,7 +216,7 @@ const detectLanguageWithPriority = async (i18nInstance, options)=>{
|
|
|
216
216
|
let detectedLanguage;
|
|
217
217
|
detectedLanguage = detectLanguageFromSSR(languages);
|
|
218
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, {
|
|
219
|
+
if (!detectedLanguage && i18nextDetector) detectedLanguage = isI18nWrapperInstance(i18nInstance) ? readLanguageFromStorage(detection_mergeDetectionOptions(i18nextDetector, detection, localePathRedirect, userInitOptions), ssrContext?.request, languages) : await detectLanguageFromI18nextDetector(i18nInstance, {
|
|
220
220
|
languages,
|
|
221
221
|
fallbackLanguage,
|
|
222
222
|
localePathRedirect,
|
|
@@ -11,7 +11,7 @@ const useI18nextLanguageDetector = (i18nInstance)=>{
|
|
|
11
11
|
}
|
|
12
12
|
return i18nInstance;
|
|
13
13
|
};
|
|
14
|
-
const readLanguageFromStorage = (detectionOptions)=>{
|
|
14
|
+
const readLanguageFromStorage = (detectionOptions, _request, _languages)=>{
|
|
15
15
|
try {
|
|
16
16
|
const options = detectionOptions || {};
|
|
17
17
|
const order = options.order || [
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { LanguageDetector } from "i18next-http-middleware";
|
|
2
|
+
import { detectLanguageFromRequest } from "../../../shared/detection.mjs";
|
|
2
3
|
const cacheUserLanguage = (_i18nInstance, _language, _detectionOptions)=>{};
|
|
3
|
-
const readLanguageFromStorage = (
|
|
4
|
+
const readLanguageFromStorage = (detectionOptions, request, languages = [])=>{
|
|
5
|
+
if (!request) return;
|
|
6
|
+
const detectedLanguage = detectLanguageFromRequest(request, languages, detectionOptions);
|
|
7
|
+
return detectedLanguage ?? void 0;
|
|
8
|
+
};
|
|
4
9
|
const useI18nextLanguageDetector = (i18nInstance)=>{
|
|
5
10
|
if (!i18nInstance.isInitialized) return i18nInstance.use(LanguageDetector);
|
|
6
11
|
return i18nInstance;
|
|
@@ -40,14 +40,6 @@ async function createI18nextInstance() {
|
|
|
40
40
|
return null;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
async function tryImportReactI18next() {
|
|
44
|
-
try {
|
|
45
|
-
const reactI18next = await import("react-i18next");
|
|
46
|
-
return reactI18next;
|
|
47
|
-
} catch (error) {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
43
|
function getI18nextInstanceForProvider(instance) {
|
|
52
44
|
if (isI18nWrapperInstance(instance)) {
|
|
53
45
|
const i18nextInstance = getI18nWrapperI18nextInstance(instance);
|
|
@@ -65,13 +57,19 @@ async function getI18nInstance(userInstance) {
|
|
|
65
57
|
throw new Error('No i18n instance found');
|
|
66
58
|
}
|
|
67
59
|
async function getInitReactI18next() {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
60
|
+
try {
|
|
61
|
+
const { initReactI18next } = await import("react-i18next");
|
|
62
|
+
return initReactI18next;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
71
66
|
}
|
|
72
67
|
async function getI18nextProvider() {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
68
|
+
try {
|
|
69
|
+
const { I18nextProvider } = await import("react-i18next");
|
|
70
|
+
return I18nextProvider;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
76
74
|
}
|
|
77
75
|
export { getActualI18nextInstance, getI18nInstance, getI18nWrapperI18nextInstance, getI18nextInstanceForProvider, getI18nextProvider, getInitReactI18next, isI18nInstance, isI18nWrapperInstance };
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { isBrowser } from "@modern-js/runtime";
|
|
2
2
|
import { getGlobalBasename } from "@modern-js/runtime/context";
|
|
3
|
-
import {
|
|
4
|
-
__webpack_require__.add({
|
|
5
|
-
4734 (module) {
|
|
6
|
-
module.exports = require("@modern-js/runtime/router");
|
|
7
|
-
}
|
|
8
|
-
});
|
|
3
|
+
import { useLocation, useNavigate, useParams } from "@modern-js/runtime/router";
|
|
9
4
|
const getPathname = (context)=>{
|
|
10
5
|
if (isBrowser()) return window.location.pathname;
|
|
11
6
|
return context.ssrContext?.request?.pathname || '/';
|
|
@@ -55,7 +50,6 @@ const shouldIgnoreRedirect = (pathname, languages, ignoreRedirectRoutes)=>{
|
|
|
55
50
|
};
|
|
56
51
|
const useRouterHooks = ()=>{
|
|
57
52
|
try {
|
|
58
|
-
const { useLocation, useNavigate, useParams } = __webpack_require__(4734);
|
|
59
53
|
return {
|
|
60
54
|
navigate: useNavigate(),
|
|
61
55
|
location: useLocation(),
|
|
@@ -15,8 +15,11 @@ function detectLanguageFromRequest(req, languages, detectionOptions) {
|
|
|
15
15
|
'header'
|
|
16
16
|
];
|
|
17
17
|
const getHeader = (name)=>{
|
|
18
|
-
req.headers
|
|
19
|
-
return
|
|
18
|
+
const headers = req.headers;
|
|
19
|
+
if ('function' == typeof headers?.get) return headers.get(name);
|
|
20
|
+
const value = headers[name.toLowerCase()];
|
|
21
|
+
if (Array.isArray(value)) return value[0] ?? null;
|
|
22
|
+
return value ?? null;
|
|
20
23
|
};
|
|
21
24
|
for (const method of detectionOrder){
|
|
22
25
|
let detectedLang = null;
|
|
@@ -51,19 +54,30 @@ function detectLanguageFromRequest(req, languages, detectionOptions) {
|
|
|
51
54
|
const languagesList = acceptLanguage.split(',').map((lang)=>{
|
|
52
55
|
const [code, q] = lang.trim().split(';');
|
|
53
56
|
return {
|
|
54
|
-
code
|
|
57
|
+
code,
|
|
58
|
+
baseCode: code.split('-')[0],
|
|
55
59
|
quality: q ? parseFloat(q.split('=')[1]) : 1.0
|
|
56
60
|
};
|
|
57
61
|
}).sort((a, b)=>b.quality - a.quality);
|
|
58
|
-
for (const lang of languagesList)
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
for (const lang of languagesList){
|
|
63
|
+
if (0 === languages.length || languages.includes(lang.code)) {
|
|
64
|
+
detectedLang = lang.code;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
if (languages.includes(lang.baseCode)) {
|
|
68
|
+
detectedLang = lang.baseCode;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
61
71
|
}
|
|
62
72
|
}
|
|
63
73
|
break;
|
|
64
74
|
}
|
|
65
75
|
}
|
|
66
76
|
if (detectedLang && (0 === languages.length || languages.includes(detectedLang))) return detectedLang;
|
|
77
|
+
if (detectedLang) {
|
|
78
|
+
const baseLang = detectedLang.split('-')[0];
|
|
79
|
+
if (languages.includes(baseLang)) return baseLang;
|
|
80
|
+
}
|
|
67
81
|
}
|
|
68
82
|
} catch (error) {}
|
|
69
83
|
return null;
|
|
@@ -217,7 +217,7 @@ const detectLanguageWithPriority = async (i18nInstance, options)=>{
|
|
|
217
217
|
let detectedLanguage;
|
|
218
218
|
detectedLanguage = detectLanguageFromSSR(languages);
|
|
219
219
|
if (!detectedLanguage) detectedLanguage = detectLanguageFromPathPriority(pathname, languages, localePathRedirect);
|
|
220
|
-
if (!detectedLanguage && i18nextDetector) detectedLanguage = isI18nWrapperInstance(i18nInstance) ? readLanguageFromStorage(detection_mergeDetectionOptions(i18nextDetector, detection, localePathRedirect, userInitOptions)) : await detectLanguageFromI18nextDetector(i18nInstance, {
|
|
220
|
+
if (!detectedLanguage && i18nextDetector) detectedLanguage = isI18nWrapperInstance(i18nInstance) ? readLanguageFromStorage(detection_mergeDetectionOptions(i18nextDetector, detection, localePathRedirect, userInitOptions), ssrContext?.request, languages) : await detectLanguageFromI18nextDetector(i18nInstance, {
|
|
221
221
|
languages,
|
|
222
222
|
fallbackLanguage,
|
|
223
223
|
localePathRedirect,
|
|
@@ -12,7 +12,7 @@ const useI18nextLanguageDetector = (i18nInstance)=>{
|
|
|
12
12
|
}
|
|
13
13
|
return i18nInstance;
|
|
14
14
|
};
|
|
15
|
-
const readLanguageFromStorage = (detectionOptions)=>{
|
|
15
|
+
const readLanguageFromStorage = (detectionOptions, _request, _languages)=>{
|
|
16
16
|
try {
|
|
17
17
|
const options = detectionOptions || {};
|
|
18
18
|
const order = options.order || [
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { LanguageDetector } from "i18next-http-middleware";
|
|
3
|
+
import { detectLanguageFromRequest } from "../../../shared/detection.mjs";
|
|
3
4
|
const cacheUserLanguage = (_i18nInstance, _language, _detectionOptions)=>{};
|
|
4
|
-
const readLanguageFromStorage = (
|
|
5
|
+
const readLanguageFromStorage = (detectionOptions, request, languages = [])=>{
|
|
6
|
+
if (!request) return;
|
|
7
|
+
const detectedLanguage = detectLanguageFromRequest(request, languages, detectionOptions);
|
|
8
|
+
return detectedLanguage ?? void 0;
|
|
9
|
+
};
|
|
5
10
|
const useI18nextLanguageDetector = (i18nInstance)=>{
|
|
6
11
|
if (!i18nInstance.isInitialized) return i18nInstance.use(LanguageDetector);
|
|
7
12
|
return i18nInstance;
|
|
@@ -41,14 +41,6 @@ async function createI18nextInstance() {
|
|
|
41
41
|
return null;
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
async function tryImportReactI18next() {
|
|
45
|
-
try {
|
|
46
|
-
const reactI18next = await import("react-i18next");
|
|
47
|
-
return reactI18next;
|
|
48
|
-
} catch (error) {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
44
|
function getI18nextInstanceForProvider(instance) {
|
|
53
45
|
if (isI18nWrapperInstance(instance)) {
|
|
54
46
|
const i18nextInstance = getI18nWrapperI18nextInstance(instance);
|
|
@@ -66,13 +58,19 @@ async function getI18nInstance(userInstance) {
|
|
|
66
58
|
throw new Error('No i18n instance found');
|
|
67
59
|
}
|
|
68
60
|
async function getInitReactI18next() {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
61
|
+
try {
|
|
62
|
+
const { initReactI18next } = await import("react-i18next");
|
|
63
|
+
return initReactI18next;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
72
67
|
}
|
|
73
68
|
async function getI18nextProvider() {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
69
|
+
try {
|
|
70
|
+
const { I18nextProvider } = await import("react-i18next");
|
|
71
|
+
return I18nextProvider;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
77
75
|
}
|
|
78
76
|
export { getActualI18nextInstance, getI18nInstance, getI18nWrapperI18nextInstance, getI18nextInstanceForProvider, getI18nextProvider, getInitReactI18next, isI18nInstance, isI18nWrapperInstance };
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { isBrowser } from "@modern-js/runtime";
|
|
3
3
|
import { getGlobalBasename } from "@modern-js/runtime/context";
|
|
4
|
-
import {
|
|
5
|
-
import { createRequire as __rspack_createRequire } from "node:module";
|
|
6
|
-
const __rspack_createRequire_require = __rspack_createRequire(import.meta.url);
|
|
7
|
-
__webpack_require__.add({
|
|
8
|
-
"@modern-js/runtime/router?a7f7" (module) {
|
|
9
|
-
module.exports = __rspack_createRequire_require("@modern-js/runtime/router");
|
|
10
|
-
}
|
|
11
|
-
});
|
|
4
|
+
import { useLocation, useNavigate, useParams } from "@modern-js/runtime/router";
|
|
12
5
|
const getPathname = (context)=>{
|
|
13
6
|
if (isBrowser()) return window.location.pathname;
|
|
14
7
|
return context.ssrContext?.request?.pathname || '/';
|
|
@@ -58,7 +51,6 @@ const shouldIgnoreRedirect = (pathname, languages, ignoreRedirectRoutes)=>{
|
|
|
58
51
|
};
|
|
59
52
|
const useRouterHooks = ()=>{
|
|
60
53
|
try {
|
|
61
|
-
const { useLocation, useNavigate, useParams } = __webpack_require__("@modern-js/runtime/router?a7f7");
|
|
62
54
|
return {
|
|
63
55
|
navigate: useNavigate(),
|
|
64
56
|
location: useLocation(),
|
|
@@ -16,8 +16,11 @@ function detectLanguageFromRequest(req, languages, detectionOptions) {
|
|
|
16
16
|
'header'
|
|
17
17
|
];
|
|
18
18
|
const getHeader = (name)=>{
|
|
19
|
-
req.headers
|
|
20
|
-
return
|
|
19
|
+
const headers = req.headers;
|
|
20
|
+
if ('function' == typeof headers?.get) return headers.get(name);
|
|
21
|
+
const value = headers[name.toLowerCase()];
|
|
22
|
+
if (Array.isArray(value)) return value[0] ?? null;
|
|
23
|
+
return value ?? null;
|
|
21
24
|
};
|
|
22
25
|
for (const method of detectionOrder){
|
|
23
26
|
let detectedLang = null;
|
|
@@ -52,19 +55,30 @@ function detectLanguageFromRequest(req, languages, detectionOptions) {
|
|
|
52
55
|
const languagesList = acceptLanguage.split(',').map((lang)=>{
|
|
53
56
|
const [code, q] = lang.trim().split(';');
|
|
54
57
|
return {
|
|
55
|
-
code
|
|
58
|
+
code,
|
|
59
|
+
baseCode: code.split('-')[0],
|
|
56
60
|
quality: q ? parseFloat(q.split('=')[1]) : 1.0
|
|
57
61
|
};
|
|
58
62
|
}).sort((a, b)=>b.quality - a.quality);
|
|
59
|
-
for (const lang of languagesList)
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
for (const lang of languagesList){
|
|
64
|
+
if (0 === languages.length || languages.includes(lang.code)) {
|
|
65
|
+
detectedLang = lang.code;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
if (languages.includes(lang.baseCode)) {
|
|
69
|
+
detectedLang = lang.baseCode;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
62
72
|
}
|
|
63
73
|
}
|
|
64
74
|
break;
|
|
65
75
|
}
|
|
66
76
|
}
|
|
67
77
|
if (detectedLang && (0 === languages.length || languages.includes(detectedLang))) return detectedLang;
|
|
78
|
+
if (detectedLang) {
|
|
79
|
+
const baseLang = detectedLang.split('-')[0];
|
|
80
|
+
if (languages.includes(baseLang)) return baseLang;
|
|
81
|
+
}
|
|
68
82
|
}
|
|
69
83
|
} catch (error) {}
|
|
70
84
|
return null;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { detectLanguageFromRequest } from '../../../shared/detection.js';
|
|
2
|
+
import type { I18nInstance, LanguageDetectorOptions } from '../instance.js';
|
|
2
3
|
/**
|
|
3
4
|
* Register LanguageDetector plugin to i18n instance
|
|
4
5
|
* Must be called before init() to properly register the detector
|
|
@@ -9,7 +10,7 @@ export declare const useI18nextLanguageDetector: (i18nInstance: I18nInstance) =>
|
|
|
9
10
|
* Read language directly from localStorage/cookie
|
|
10
11
|
* Fallback when detector is not available in services
|
|
11
12
|
*/
|
|
12
|
-
export declare const readLanguageFromStorage: (detectionOptions?:
|
|
13
|
+
export declare const readLanguageFromStorage: (detectionOptions?: LanguageDetectorOptions, _request?: Parameters<typeof detectLanguageFromRequest>[0], _languages?: string[]) => string | undefined;
|
|
13
14
|
/**
|
|
14
15
|
* Detect language using i18next-browser-languagedetector
|
|
15
16
|
* For initialized instances without detector in services, manually create a detector instance
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { detectLanguageFromRequest } from '../../../shared/detection.js';
|
|
2
|
+
import type { I18nInstance, LanguageDetectorOptions } from '../instance.js';
|
|
2
3
|
export declare const cacheUserLanguage: (_i18nInstance: I18nInstance, _language: string, _detectionOptions?: any) => void;
|
|
3
|
-
|
|
4
|
-
* Read language directly from storage (localStorage/cookie)
|
|
5
|
-
* Not available in Node.js environment, returns undefined
|
|
6
|
-
*/
|
|
7
|
-
export declare const readLanguageFromStorage: (_detectionOptions?: any) => string | undefined;
|
|
4
|
+
export declare const readLanguageFromStorage: (detectionOptions?: LanguageDetectorOptions, request?: Parameters<typeof detectLanguageFromRequest>[0], languages?: string[]) => string | undefined;
|
|
8
5
|
/**
|
|
9
6
|
* Register LanguageDetector plugin to i18n instance
|
|
10
7
|
* Must be called before init() to properly register the detector
|
|
@@ -26,8 +26,13 @@ export declare const detectLanguageFromPath: (pathname: string, languages: strin
|
|
|
26
26
|
*/
|
|
27
27
|
export declare const shouldIgnoreRedirect: (pathname: string, languages: string[], ignoreRedirectRoutes?: string[] | ((pathname: string) => boolean)) => boolean;
|
|
28
28
|
export declare const useRouterHooks: () => {
|
|
29
|
-
navigate:
|
|
30
|
-
location: any
|
|
31
|
-
params:
|
|
29
|
+
navigate: import("@modern-js/runtime/router").NavigateFunction;
|
|
30
|
+
location: import("@modern-js/runtime/router").Location<any>;
|
|
31
|
+
params: Readonly<import("@modern-js/runtime/router").Params<string>>;
|
|
32
|
+
hasRouter: boolean;
|
|
33
|
+
} | {
|
|
34
|
+
navigate: null;
|
|
35
|
+
location: null;
|
|
36
|
+
params: {};
|
|
32
37
|
hasRouter: boolean;
|
|
33
38
|
};
|
|
@@ -5,7 +5,7 @@ import type { LanguageDetectorOptions } from '../runtime/i18n/instance.js';
|
|
|
5
5
|
*/
|
|
6
6
|
export declare function detectLanguageFromRequest(req: {
|
|
7
7
|
url: string;
|
|
8
|
-
headers: {
|
|
8
|
+
headers: Headers | {
|
|
9
9
|
get: (name: string) => string | null;
|
|
10
|
-
} |
|
|
10
|
+
} | Record<string, string | string[] | undefined>;
|
|
11
11
|
}, languages: string[], detectionOptions?: LanguageDetectorOptions): string | null;
|
package/package.json
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"modern",
|
|
16
16
|
"modern.js"
|
|
17
17
|
],
|
|
18
|
-
"version": "3.
|
|
18
|
+
"version": "3.10.0",
|
|
19
19
|
"engines": {
|
|
20
20
|
"node": ">=20"
|
|
21
21
|
},
|
|
@@ -82,22 +82,22 @@
|
|
|
82
82
|
"@swc/helpers": "^0.5.17",
|
|
83
83
|
"i18next-browser-languagedetector": "^8.2.1",
|
|
84
84
|
"i18next-chained-backend": "^4.6.3",
|
|
85
|
-
"i18next-fs-backend": "^2.6.
|
|
86
|
-
"i18next-http-backend": "^
|
|
87
|
-
"i18next-http-middleware": "^3.9.
|
|
88
|
-
"@modern-js/plugin": "3.
|
|
89
|
-
"@modern-js/
|
|
90
|
-
"@modern-js/
|
|
91
|
-
"@modern-js/server-runtime": "3.
|
|
92
|
-
"@modern-js/types": "3.
|
|
93
|
-
"@modern-js/utils": "3.
|
|
85
|
+
"i18next-fs-backend": "^2.6.8",
|
|
86
|
+
"i18next-http-backend": "^4.0.2",
|
|
87
|
+
"i18next-http-middleware": "^3.9.9",
|
|
88
|
+
"@modern-js/plugin": "3.10.0",
|
|
89
|
+
"@modern-js/server-core": "3.10.0",
|
|
90
|
+
"@modern-js/runtime-utils": "3.10.0",
|
|
91
|
+
"@modern-js/server-runtime": "3.10.0",
|
|
92
|
+
"@modern-js/types": "3.10.0",
|
|
93
|
+
"@modern-js/utils": "3.10.0"
|
|
94
94
|
},
|
|
95
95
|
"peerDependencies": {
|
|
96
96
|
"react": ">=18.3.1",
|
|
97
97
|
"react-dom": ">=18.3.1",
|
|
98
|
-
"i18next": ">=25.
|
|
98
|
+
"i18next": ">=25.10.10",
|
|
99
99
|
"react-i18next": ">=15.7.4",
|
|
100
|
-
"@modern-js/runtime": "^3.
|
|
100
|
+
"@modern-js/runtime": "^3.10.0"
|
|
101
101
|
},
|
|
102
102
|
"peerDependenciesMeta": {
|
|
103
103
|
"i18next": {
|
|
@@ -108,17 +108,16 @@
|
|
|
108
108
|
}
|
|
109
109
|
},
|
|
110
110
|
"devDependencies": {
|
|
111
|
-
"@rslib/core": "1.0.0",
|
|
112
111
|
"@types/node": "^20",
|
|
113
|
-
"i18next": "25.
|
|
114
|
-
"react": "^19.
|
|
115
|
-
"react-dom": "^19.
|
|
112
|
+
"i18next": "25.10.10",
|
|
113
|
+
"react": "^19.3.0",
|
|
114
|
+
"react-dom": "^19.3.0",
|
|
116
115
|
"react-i18next": "15.7.4",
|
|
117
116
|
"ts-node": "^10.9.2",
|
|
118
117
|
"typescript": "^5",
|
|
119
|
-
"@modern-js/app-tools": "3.9.2",
|
|
120
118
|
"@modern-js/rslib": "2.68.10",
|
|
121
|
-
"@modern-js/runtime": "3.
|
|
119
|
+
"@modern-js/runtime": "3.10.0",
|
|
120
|
+
"@modern-js/app-tools": "3.10.0"
|
|
122
121
|
},
|
|
123
122
|
"sideEffects": false,
|
|
124
123
|
"publishConfig": {
|
|
@@ -126,8 +125,8 @@
|
|
|
126
125
|
"access": "public"
|
|
127
126
|
},
|
|
128
127
|
"scripts": {
|
|
129
|
-
"dev": "
|
|
130
|
-
"build": "
|
|
128
|
+
"dev": "rs lib --watch",
|
|
129
|
+
"build": "rs lib",
|
|
131
130
|
"test": "rstest"
|
|
132
131
|
}
|
|
133
132
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { LanguageDetector } from 'i18next-http-middleware';
|
|
2
|
-
import
|
|
2
|
+
import { detectLanguageFromRequest } from '../../../shared/detection.js';
|
|
3
|
+
import type { I18nInstance, LanguageDetectorOptions } from '../instance';
|
|
3
4
|
|
|
4
5
|
export const cacheUserLanguage = (
|
|
5
6
|
_i18nInstance: I18nInstance,
|
|
@@ -9,15 +10,21 @@ export const cacheUserLanguage = (
|
|
|
9
10
|
return;
|
|
10
11
|
};
|
|
11
12
|
|
|
12
|
-
/**
|
|
13
|
-
* Read language directly from storage (localStorage/cookie)
|
|
14
|
-
* Not available in Node.js environment, returns undefined
|
|
15
|
-
*/
|
|
16
13
|
export const readLanguageFromStorage = (
|
|
17
|
-
|
|
14
|
+
detectionOptions?: LanguageDetectorOptions,
|
|
15
|
+
request?: Parameters<typeof detectLanguageFromRequest>[0],
|
|
16
|
+
languages: string[] = [],
|
|
18
17
|
): string | undefined => {
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
if (!request) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const detectedLanguage = detectLanguageFromRequest(
|
|
23
|
+
request,
|
|
24
|
+
languages,
|
|
25
|
+
detectionOptions,
|
|
26
|
+
);
|
|
27
|
+
return detectedLanguage ?? undefined;
|
|
21
28
|
};
|
|
22
29
|
/**
|
|
23
30
|
* Register LanguageDetector plugin to i18n instance
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isBrowser } from '@modern-js/runtime';
|
|
2
2
|
import LanguageDetector from 'i18next-browser-languagedetector';
|
|
3
|
-
import type {
|
|
3
|
+
import type { detectLanguageFromRequest } from '../../../shared/detection.js';
|
|
4
|
+
import type { I18nInstance, LanguageDetectorOptions } from '../instance';
|
|
4
5
|
import { getActualI18nextInstance, isI18nWrapperInstance } from '../instance';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -27,7 +28,9 @@ export const useI18nextLanguageDetector = (i18nInstance: I18nInstance) => {
|
|
|
27
28
|
* Fallback when detector is not available in services
|
|
28
29
|
*/
|
|
29
30
|
export const readLanguageFromStorage = (
|
|
30
|
-
detectionOptions?:
|
|
31
|
+
detectionOptions?: LanguageDetectorOptions,
|
|
32
|
+
_request?: Parameters<typeof detectLanguageFromRequest>[0],
|
|
33
|
+
_languages?: string[],
|
|
31
34
|
): string | undefined => {
|
|
32
35
|
try {
|
|
33
36
|
const options = detectionOptions || {};
|
|
@@ -167,15 +167,6 @@ async function createI18nextInstance(): Promise<I18nInstance | null> {
|
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
async function tryImportReactI18next() {
|
|
171
|
-
try {
|
|
172
|
-
const reactI18next = await import('react-i18next');
|
|
173
|
-
return reactI18next;
|
|
174
|
-
} catch (error) {
|
|
175
|
-
return null;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
170
|
export function getI18nextInstanceForProvider(
|
|
180
171
|
instance: I18nInstance | any,
|
|
181
172
|
): any {
|
|
@@ -211,17 +202,19 @@ export async function getI18nInstance(
|
|
|
211
202
|
}
|
|
212
203
|
|
|
213
204
|
export async function getInitReactI18next() {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
return
|
|
205
|
+
try {
|
|
206
|
+
const { initReactI18next } = await import('react-i18next');
|
|
207
|
+
return initReactI18next;
|
|
208
|
+
} catch (error) {
|
|
209
|
+
return null;
|
|
217
210
|
}
|
|
218
|
-
return null;
|
|
219
211
|
}
|
|
220
212
|
|
|
221
213
|
export async function getI18nextProvider() {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return
|
|
214
|
+
try {
|
|
215
|
+
const { I18nextProvider } = await import('react-i18next');
|
|
216
|
+
return I18nextProvider;
|
|
217
|
+
} catch (error) {
|
|
218
|
+
return null;
|
|
225
219
|
}
|
|
226
|
-
return null;
|
|
227
220
|
}
|
package/src/runtime/utils.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
type TInternalRuntimeContext,
|
|
4
4
|
getGlobalBasename,
|
|
5
5
|
} from '@modern-js/runtime/context';
|
|
6
|
+
import { useLocation, useNavigate, useParams } from '@modern-js/runtime/router';
|
|
6
7
|
|
|
7
8
|
export const getPathname = (context: TInternalRuntimeContext): string => {
|
|
8
9
|
if (isBrowser()) {
|
|
@@ -141,11 +142,6 @@ export const shouldIgnoreRedirect = (
|
|
|
141
142
|
// Safe hook wrapper to handle cases where router context is not available
|
|
142
143
|
export const useRouterHooks = () => {
|
|
143
144
|
try {
|
|
144
|
-
const {
|
|
145
|
-
useLocation,
|
|
146
|
-
useNavigate,
|
|
147
|
-
useParams,
|
|
148
|
-
} = require('@modern-js/runtime/router');
|
|
149
145
|
return {
|
|
150
146
|
navigate: useNavigate(),
|
|
151
147
|
location: useLocation(),
|
package/src/shared/detection.ts
CHANGED
|
@@ -12,10 +12,9 @@ export function detectLanguageFromRequest(
|
|
|
12
12
|
req: {
|
|
13
13
|
url: string;
|
|
14
14
|
headers:
|
|
15
|
-
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
| Headers;
|
|
15
|
+
| Headers
|
|
16
|
+
| { get: (name: string) => string | null }
|
|
17
|
+
| Record<string, string | string[] | undefined>;
|
|
19
18
|
},
|
|
20
19
|
languages: string[],
|
|
21
20
|
detectionOptions?: LanguageDetectorOptions,
|
|
@@ -39,11 +38,21 @@ export function detectLanguageFromRequest(
|
|
|
39
38
|
order.length > 0 ? order : ['querystring', 'cookie', 'header'];
|
|
40
39
|
|
|
41
40
|
// Helper to get header value
|
|
41
|
+
// req.headers is a Fetch API Headers instance when the caller passes the raw
|
|
42
|
+
// Request (e.g. ssrContext.request.raw), but modern.js's own SSR request object
|
|
43
|
+
// (ssrContext.request) carries headers as a plain, lower-cased key/value object.
|
|
42
44
|
const getHeader = (name: string): string | null => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
const headers = req.headers;
|
|
46
|
+
if (typeof (headers as Headers)?.get === 'function') {
|
|
47
|
+
return (headers as Headers).get(name);
|
|
48
|
+
}
|
|
49
|
+
const value = (headers as Record<string, string | string[] | undefined>)[
|
|
50
|
+
name.toLowerCase()
|
|
51
|
+
];
|
|
52
|
+
if (Array.isArray(value)) {
|
|
53
|
+
return value[0] ?? null;
|
|
45
54
|
}
|
|
46
|
-
return
|
|
55
|
+
return value ?? null;
|
|
47
56
|
};
|
|
48
57
|
|
|
49
58
|
// Try each detection method in order
|
|
@@ -94,7 +103,8 @@ export function detectLanguageFromRequest(
|
|
|
94
103
|
.map((lang: string) => {
|
|
95
104
|
const [code, q] = lang.trim().split(';');
|
|
96
105
|
return {
|
|
97
|
-
code
|
|
106
|
+
code,
|
|
107
|
+
baseCode: code.split('-')[0],
|
|
98
108
|
quality: q ? parseFloat(q.split('=')[1]) : 1.0,
|
|
99
109
|
};
|
|
100
110
|
})
|
|
@@ -103,12 +113,17 @@ export function detectLanguageFromRequest(
|
|
|
103
113
|
b.quality - a.quality,
|
|
104
114
|
);
|
|
105
115
|
|
|
106
|
-
// Find first matching language
|
|
116
|
+
// Find first matching language, preferring an exact tag match
|
|
117
|
+
// (e.g. 'zh-Hant-TW') over the base language code (e.g. 'zh')
|
|
107
118
|
for (const lang of languagesList) {
|
|
108
119
|
if (languages.length === 0 || languages.includes(lang.code)) {
|
|
109
120
|
detectedLang = lang.code;
|
|
110
121
|
break;
|
|
111
122
|
}
|
|
123
|
+
if (languages.includes(lang.baseCode)) {
|
|
124
|
+
detectedLang = lang.baseCode;
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
112
127
|
}
|
|
113
128
|
}
|
|
114
129
|
break;
|
|
@@ -122,6 +137,15 @@ export function detectLanguageFromRequest(
|
|
|
122
137
|
) {
|
|
123
138
|
return detectedLang;
|
|
124
139
|
}
|
|
140
|
+
|
|
141
|
+
// Match query and cookie values like header values: exact tag first,
|
|
142
|
+
// then the supported base language (e.g. 'zh-CN' -> 'zh').
|
|
143
|
+
if (detectedLang) {
|
|
144
|
+
const baseLang = detectedLang.split('-')[0];
|
|
145
|
+
if (languages.includes(baseLang)) {
|
|
146
|
+
return baseLang;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
125
149
|
}
|
|
126
150
|
} catch (error) {
|
|
127
151
|
// Silently ignore errors
|
package/src/shared/utils.ts
CHANGED
|
@@ -11,8 +11,7 @@ export function getEntryConfig<T extends Record<string, any>>(
|
|
|
11
11
|
entryKey: string,
|
|
12
12
|
): T | undefined {
|
|
13
13
|
const entryConfigMap = (config as any)[entryKey] as
|
|
14
|
-
|
|
15
|
-
| undefined;
|
|
14
|
+
Record<string, T> | undefined;
|
|
16
15
|
return entryConfigMap?.[entryName];
|
|
17
16
|
}
|
|
18
17
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { detectLanguageFromRequest } from '../src/shared/detection';
|
|
2
|
+
|
|
3
|
+
// A later English header must not override a supported query/cookie language.
|
|
4
|
+
const requests = [
|
|
5
|
+
{
|
|
6
|
+
source: 'query',
|
|
7
|
+
url: '/?lng=zh-Hant-TW',
|
|
8
|
+
headers: { 'accept-language': 'en' },
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
source: 'cookie',
|
|
12
|
+
url: '/',
|
|
13
|
+
headers: { cookie: 'i18next=zh-Hant-TW', 'accept-language': 'en' },
|
|
14
|
+
},
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
test.each(requests)('$source falls back to a supported base language', req => {
|
|
18
|
+
expect(detectLanguageFromRequest(req, ['en', 'zh'])).toBe('zh');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test.each(requests)(
|
|
22
|
+
'$source prefers the full tag when both are supported',
|
|
23
|
+
req => {
|
|
24
|
+
expect(detectLanguageFromRequest(req, ['en', 'zh', 'zh-Hant-TW'])).toBe(
|
|
25
|
+
'zh-Hant-TW',
|
|
26
|
+
);
|
|
27
|
+
},
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
test('skips an unsupported query language and matches the cookie base language', () => {
|
|
31
|
+
expect(
|
|
32
|
+
detectLanguageFromRequest(
|
|
33
|
+
{ url: '/?lng=fr-FR', headers: { cookie: 'i18next=zh-CN' } },
|
|
34
|
+
['en', 'zh'],
|
|
35
|
+
),
|
|
36
|
+
).toBe('zh');
|
|
37
|
+
});
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
var __webpack_modules__ = {};
|
|
2
|
-
var __webpack_module_cache__ = {};
|
|
3
|
-
function __webpack_require__(moduleId) {
|
|
4
|
-
var cachedModule = __webpack_module_cache__[moduleId];
|
|
5
|
-
if (void 0 !== cachedModule) return cachedModule.exports;
|
|
6
|
-
var module = __webpack_module_cache__[moduleId] = {
|
|
7
|
-
exports: {}
|
|
8
|
-
};
|
|
9
|
-
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
10
|
-
return module.exports;
|
|
11
|
-
}
|
|
12
|
-
__webpack_require__.m = __webpack_modules__;
|
|
13
|
-
(()=>{
|
|
14
|
-
__webpack_require__.add = function(modules) {
|
|
15
|
-
Object.assign(__webpack_require__.m, modules);
|
|
16
|
-
};
|
|
17
|
-
})();
|
|
18
|
-
export { __webpack_require__ };
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import "node:module";
|
|
2
|
-
var __webpack_modules__ = {};
|
|
3
|
-
var __webpack_module_cache__ = {};
|
|
4
|
-
function __webpack_require__(moduleId) {
|
|
5
|
-
var cachedModule = __webpack_module_cache__[moduleId];
|
|
6
|
-
if (void 0 !== cachedModule) return cachedModule.exports;
|
|
7
|
-
var module = __webpack_module_cache__[moduleId] = {
|
|
8
|
-
exports: {}
|
|
9
|
-
};
|
|
10
|
-
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
11
|
-
return module.exports;
|
|
12
|
-
}
|
|
13
|
-
__webpack_require__.m = __webpack_modules__;
|
|
14
|
-
(()=>{
|
|
15
|
-
__webpack_require__.add = function(modules) {
|
|
16
|
-
Object.assign(__webpack_require__.m, modules);
|
|
17
|
-
};
|
|
18
|
-
})();
|
|
19
|
-
export { __webpack_require__ };
|
package/rslib.config.mts
DELETED