@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.
Files changed (39) hide show
  1. package/dist/cjs/runtime/i18n/detection/index.js +1 -1
  2. package/dist/cjs/runtime/i18n/detection/middleware.js +1 -1
  3. package/dist/cjs/runtime/i18n/detection/middleware.node.js +6 -1
  4. package/dist/cjs/runtime/i18n/instance.js +12 -14
  5. package/dist/cjs/runtime/utils.js +72 -88
  6. package/dist/cjs/shared/detection.js +20 -6
  7. package/dist/esm/runtime/i18n/detection/index.mjs +1 -1
  8. package/dist/esm/runtime/i18n/detection/middleware.mjs +1 -1
  9. package/dist/esm/runtime/i18n/detection/middleware.node.mjs +6 -1
  10. package/dist/esm/runtime/i18n/instance.mjs +12 -14
  11. package/dist/esm/runtime/utils.mjs +1 -7
  12. package/dist/esm/shared/detection.mjs +20 -6
  13. package/dist/esm-node/runtime/i18n/detection/index.mjs +1 -1
  14. package/dist/esm-node/runtime/i18n/detection/middleware.mjs +1 -1
  15. package/dist/esm-node/runtime/i18n/detection/middleware.node.mjs +6 -1
  16. package/dist/esm-node/runtime/i18n/instance.mjs +12 -14
  17. package/dist/esm-node/runtime/utils.mjs +1 -9
  18. package/dist/esm-node/shared/detection.mjs +20 -6
  19. package/dist/types/runtime/i18n/detection/middleware.d.ts +3 -2
  20. package/dist/types/runtime/i18n/detection/middleware.node.d.ts +3 -6
  21. package/dist/types/runtime/i18n/index.d.ts +1 -1
  22. package/dist/types/runtime/utils.d.ts +8 -3
  23. package/dist/types/shared/detection.d.ts +2 -2
  24. package/package.json +19 -20
  25. package/rstack.config.mts +4 -0
  26. package/rstest.config.mts +7 -0
  27. package/src/runtime/i18n/backend/sdk-event.ts +1 -2
  28. package/src/runtime/i18n/detection/index.ts +2 -0
  29. package/src/runtime/i18n/detection/middleware.node.ts +15 -8
  30. package/src/runtime/i18n/detection/middleware.ts +5 -2
  31. package/src/runtime/i18n/index.ts +1 -4
  32. package/src/runtime/i18n/instance.ts +10 -17
  33. package/src/runtime/utils.ts +1 -5
  34. package/src/shared/detection.ts +33 -9
  35. package/src/shared/utils.ts +1 -2
  36. package/tests/detection.test.ts +37 -0
  37. package/dist/esm/rslib-runtime~1.mjs +0 -18
  38. package/dist/esm-node/rslib-runtime~0.mjs +0 -19
  39. 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 = (_detectionOptions)=>{};
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
- const reactI18nextModule = await tryImportReactI18next();
98
- if (reactI18nextModule) return reactI18nextModule.initReactI18next;
99
- return null;
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
- const reactI18nextModule = await tryImportReactI18next();
103
- if (reactI18nextModule) return reactI18nextModule.I18nextProvider;
104
- return null;
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 __webpack_modules__ = {
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
- __webpack_require__.r(__webpack_exports__);
45
- __webpack_require__.d(__webpack_exports__, {
46
- buildLocalizedUrl: ()=>buildLocalizedUrl,
47
- detectLanguageFromPath: ()=>detectLanguageFromPath,
48
- getEntryPath: ()=>getEntryPath,
49
- getLanguageFromPath: ()=>getLanguageFromPath,
50
- getPathname: ()=>getPathname,
51
- shouldIgnoreRedirect: ()=>shouldIgnoreRedirect,
52
- useRouterHooks: ()=>useRouterHooks
53
- });
54
- const runtime_namespaceObject = require("@modern-js/runtime");
55
- const context_namespaceObject = require("@modern-js/runtime/context");
56
- const getPathname = (context)=>{
57
- if ((0, runtime_namespaceObject.isBrowser)()) return window.location.pathname;
58
- return context.ssrContext?.request?.pathname || '/';
59
- };
60
- const getEntryPath = ()=>{
61
- const basename = (0, context_namespaceObject.getGlobalBasename)();
62
- if (basename) return '/' === basename ? '' : basename;
63
- return '';
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 getLanguageFromPath = (pathname, languages, fallbackLanguage)=>{
66
- const segments = pathname.split('/').filter(Boolean);
67
- const firstSegment = segments[0];
68
- if (languages.includes(firstSegment)) return firstSegment;
69
- return fallbackLanguage;
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
- const buildLocalizedUrl = (pathname, language, languages)=>{
72
- const segments = pathname.split('/').filter(Boolean);
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
- const detectLanguageFromPath = (pathname, languages, localePathRedirect)=>{
78
- if (!localePathRedirect) return {
79
- detected: false
80
- };
81
- const entryPath = getEntryPath();
82
- const relativePath = pathname.replace(entryPath, '');
83
- const segments = relativePath.split('/').filter(Boolean);
84
- const segmentsToCheck = !entryPath && segments.length > 1 && segments[0] && !languages.includes(segments[0]) ? segments.slice(1) : segments;
85
- const firstSegment = segmentsToCheck[0];
86
- if (firstSegment && languages.includes(firstSegment)) return {
87
- detected: true,
88
- language: firstSegment
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
- detected: false
99
+ navigate: null,
100
+ location: null,
101
+ params: {},
102
+ hasRouter: false
92
103
  };
93
- };
94
- const shouldIgnoreRedirect = (pathname, languages, ignoreRedirectRoutes)=>{
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, Headers;
51
- return req.headers.get(name);
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: code.split('-')[0],
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)if (0 === languages.length || languages.includes(lang.code)) {
91
- detectedLang = lang.code;
92
- break;
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 = (_detectionOptions)=>{};
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
- const reactI18nextModule = await tryImportReactI18next();
69
- if (reactI18nextModule) return reactI18nextModule.initReactI18next;
70
- return null;
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
- const reactI18nextModule = await tryImportReactI18next();
74
- if (reactI18nextModule) return reactI18nextModule.I18nextProvider;
75
- return null;
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 { __webpack_require__ } from "../rslib-runtime~1.mjs";
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, Headers;
19
- return req.headers.get(name);
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: code.split('-')[0],
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)if (0 === languages.length || languages.includes(lang.code)) {
59
- detectedLang = lang.code;
60
- break;
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 = (_detectionOptions)=>{};
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
- const reactI18nextModule = await tryImportReactI18next();
70
- if (reactI18nextModule) return reactI18nextModule.initReactI18next;
71
- return null;
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
- const reactI18nextModule = await tryImportReactI18next();
75
- if (reactI18nextModule) return reactI18nextModule.I18nextProvider;
76
- return null;
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 { __webpack_require__ } from "../rslib-runtime~0.mjs";
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, Headers;
20
- return req.headers.get(name);
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: code.split('-')[0],
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)if (0 === languages.length || languages.includes(lang.code)) {
60
- detectedLang = lang.code;
61
- break;
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 { I18nInstance } from '../instance.js';
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?: any) => string | undefined;
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 type { I18nInstance } from '../instance.js';
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
@@ -1,3 +1,3 @@
1
- export type { I18nInstance, I18nInitOptions, } from './instance.js';
1
+ export type { I18nInstance, I18nInitOptions } from './instance.js';
2
2
  export { isI18nInstance, getI18nInstance } from './instance.js';
3
3
  export { assertI18nInstance } from './utils.js';
@@ -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: any;
30
- location: any;
31
- params: any;
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
- } | Headers;
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.9.2",
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.7",
86
- "i18next-http-backend": "^3.0.6",
87
- "i18next-http-middleware": "^3.9.8",
88
- "@modern-js/plugin": "3.9.2",
89
- "@modern-js/runtime-utils": "3.9.2",
90
- "@modern-js/server-core": "3.9.2",
91
- "@modern-js/server-runtime": "3.9.2",
92
- "@modern-js/types": "3.9.2",
93
- "@modern-js/utils": "3.9.2"
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.7.4",
98
+ "i18next": ">=25.10.10",
99
99
  "react-i18next": ">=15.7.4",
100
- "@modern-js/runtime": "^3.9.2"
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.7.4",
114
- "react": "^19.2.8",
115
- "react-dom": "^19.2.8",
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.9.2"
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": "rslib build --watch",
130
- "build": "rslib build",
128
+ "dev": "rs lib --watch",
129
+ "build": "rs lib",
131
130
  "test": "rstest"
132
131
  }
133
132
  }
@@ -0,0 +1,4 @@
1
+ import { rslibConfig } from '@modern-js/rslib';
2
+ import { define } from 'rstack';
3
+
4
+ define.lib(rslibConfig);
@@ -0,0 +1,7 @@
1
+ import { withTestPreset } from '@scripts/rstest-config';
2
+
3
+ export default withTestPreset({
4
+ root: __dirname,
5
+ testEnvironment: 'node',
6
+ globals: true,
7
+ });
@@ -34,6 +34,5 @@ export function getI18nSdkBackendId(target: unknown): string | undefined {
34
34
  }
35
35
 
36
36
  return (target as Record<string, unknown>)[I18N_SDK_BACKEND_ID_KEY] as
37
- | string
38
- | undefined;
37
+ string | undefined;
39
38
  }
@@ -538,6 +538,8 @@ export const detectLanguageWithPriority = async (
538
538
  localePathRedirect,
539
539
  userInitOptions,
540
540
  ),
541
+ ssrContext?.request,
542
+ languages,
541
543
  );
542
544
  } else {
543
545
  detectedLanguage = await detectLanguageFromI18nextDetector(i18nInstance, {
@@ -1,5 +1,6 @@
1
1
  import { LanguageDetector } from 'i18next-http-middleware';
2
- import type { I18nInstance } from '../instance';
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
- _detectionOptions?: any,
14
+ detectionOptions?: LanguageDetectorOptions,
15
+ request?: Parameters<typeof detectLanguageFromRequest>[0],
16
+ languages: string[] = [],
18
17
  ): string | undefined => {
19
- // In Node.js environment, storage-based detection is not available
20
- return undefined;
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 { I18nInstance } from '../instance';
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?: any,
31
+ detectionOptions?: LanguageDetectorOptions,
32
+ _request?: Parameters<typeof detectLanguageFromRequest>[0],
33
+ _languages?: string[],
31
34
  ): string | undefined => {
32
35
  try {
33
36
  const options = detectionOptions || {};
@@ -1,7 +1,4 @@
1
- export type {
2
- I18nInstance,
3
- I18nInitOptions,
4
- } from './instance';
1
+ export type { I18nInstance, I18nInitOptions } from './instance';
5
2
 
6
3
  export { isI18nInstance, getI18nInstance } from './instance';
7
4
 
@@ -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
- const reactI18nextModule = await tryImportReactI18next();
215
- if (reactI18nextModule) {
216
- return reactI18nextModule.initReactI18next;
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
- const reactI18nextModule = await tryImportReactI18next();
223
- if (reactI18nextModule) {
224
- return reactI18nextModule.I18nextProvider;
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
  }
@@ -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(),
@@ -12,10 +12,9 @@ export function detectLanguageFromRequest(
12
12
  req: {
13
13
  url: string;
14
14
  headers:
15
- | {
16
- get: (name: string) => string | null;
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
- if (req.headers instanceof Headers) {
44
- return req.headers.get(name);
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 req.headers.get(name);
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: code.split('-')[0], // Extract base language 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
@@ -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
- | Record<string, T>
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
@@ -1,4 +0,0 @@
1
- import { rslibConfig } from '@modern-js/rslib';
2
- import { defineConfig } from '@rslib/core';
3
-
4
- export default defineConfig(rslibConfig);