@modern-js/plugin-i18n 2.69.5 → 3.0.0-alpha.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/README.md +6 -0
- package/dist/cjs/cli/index.cjs +154 -0
- package/dist/cjs/runtime/I18nLink.cjs +68 -0
- package/dist/cjs/runtime/context.cjs +138 -0
- package/dist/cjs/runtime/hooks.cjs +189 -0
- package/dist/cjs/runtime/i18n/backend/config.cjs +39 -0
- package/dist/cjs/runtime/i18n/backend/defaults.cjs +56 -0
- package/dist/cjs/runtime/i18n/backend/defaults.node.cjs +56 -0
- package/dist/cjs/runtime/i18n/backend/index.cjs +108 -0
- package/dist/cjs/runtime/i18n/backend/middleware.cjs +54 -0
- package/dist/cjs/runtime/i18n/backend/middleware.common.cjs +105 -0
- package/dist/cjs/runtime/i18n/backend/middleware.node.cjs +58 -0
- package/dist/cjs/runtime/i18n/backend/sdk-backend.cjs +171 -0
- package/dist/cjs/runtime/i18n/detection/config.cjs +63 -0
- package/dist/cjs/runtime/i18n/detection/index.cjs +309 -0
- package/dist/cjs/runtime/i18n/detection/middleware.cjs +185 -0
- package/dist/cjs/runtime/i18n/detection/middleware.node.cjs +74 -0
- package/dist/cjs/runtime/i18n/index.cjs +43 -0
- package/dist/cjs/runtime/i18n/instance.cjs +132 -0
- package/dist/cjs/runtime/i18n/utils.cjs +185 -0
- package/dist/cjs/runtime/index.cjs +162 -0
- package/dist/cjs/runtime/types.cjs +18 -0
- package/dist/cjs/runtime/utils.cjs +134 -0
- package/dist/cjs/server/index.cjs +178 -0
- package/dist/cjs/shared/deepMerge.cjs +54 -0
- package/dist/cjs/shared/detection.cjs +105 -0
- package/dist/cjs/shared/type.cjs +18 -0
- package/dist/cjs/shared/utils.cjs +78 -0
- package/dist/esm/cli/index.js +106 -0
- package/dist/esm/runtime/I18nLink.js +31 -0
- package/dist/esm/runtime/context.js +101 -0
- package/dist/esm/runtime/hooks.js +146 -0
- package/dist/esm/runtime/i18n/backend/config.js +5 -0
- package/dist/esm/runtime/i18n/backend/defaults.js +19 -0
- package/dist/esm/runtime/i18n/backend/defaults.node.js +19 -0
- package/dist/esm/runtime/i18n/backend/index.js +74 -0
- package/dist/esm/runtime/i18n/backend/middleware.common.js +61 -0
- package/dist/esm/runtime/i18n/backend/middleware.js +7 -0
- package/dist/esm/runtime/i18n/backend/middleware.node.js +8 -0
- package/dist/esm/runtime/i18n/backend/sdk-backend.js +137 -0
- package/dist/esm/runtime/i18n/detection/config.js +26 -0
- package/dist/esm/runtime/i18n/detection/index.js +260 -0
- package/dist/esm/runtime/i18n/detection/middleware.js +132 -0
- package/dist/esm/runtime/i18n/detection/middleware.node.js +31 -0
- package/dist/esm/runtime/i18n/index.js +3 -0
- package/dist/esm/runtime/i18n/instance.js +77 -0
- package/dist/esm/runtime/i18n/utils.js +136 -0
- package/dist/esm/runtime/index.js +119 -0
- package/dist/esm/runtime/types.js +0 -0
- package/dist/esm/runtime/utils.js +82 -0
- package/dist/esm/server/index.js +168 -0
- package/dist/esm/shared/deepMerge.js +20 -0
- package/dist/esm/shared/detection.js +71 -0
- package/dist/esm/shared/type.js +0 -0
- package/dist/esm/shared/utils.js +35 -0
- package/dist/esm-node/cli/index.js +106 -0
- package/dist/esm-node/runtime/I18nLink.js +31 -0
- package/dist/esm-node/runtime/context.js +101 -0
- package/dist/esm-node/runtime/hooks.js +146 -0
- package/dist/esm-node/runtime/i18n/backend/config.js +5 -0
- package/dist/esm-node/runtime/i18n/backend/defaults.js +19 -0
- package/dist/esm-node/runtime/i18n/backend/defaults.node.js +19 -0
- package/dist/esm-node/runtime/i18n/backend/index.js +74 -0
- package/dist/esm-node/runtime/i18n/backend/middleware.common.js +61 -0
- package/dist/esm-node/runtime/i18n/backend/middleware.js +7 -0
- package/dist/esm-node/runtime/i18n/backend/middleware.node.js +8 -0
- package/dist/esm-node/runtime/i18n/backend/sdk-backend.js +137 -0
- package/dist/esm-node/runtime/i18n/detection/config.js +26 -0
- package/dist/esm-node/runtime/i18n/detection/index.js +260 -0
- package/dist/esm-node/runtime/i18n/detection/middleware.js +132 -0
- package/dist/esm-node/runtime/i18n/detection/middleware.node.js +31 -0
- package/dist/esm-node/runtime/i18n/index.js +3 -0
- package/dist/esm-node/runtime/i18n/instance.js +77 -0
- package/dist/esm-node/runtime/i18n/utils.js +136 -0
- package/dist/esm-node/runtime/index.js +119 -0
- package/dist/esm-node/runtime/types.js +0 -0
- package/dist/esm-node/runtime/utils.js +82 -0
- package/dist/esm-node/server/index.js +168 -0
- package/dist/esm-node/shared/deepMerge.js +20 -0
- package/dist/esm-node/shared/detection.js +71 -0
- package/dist/esm-node/shared/type.js +0 -0
- package/dist/esm-node/shared/utils.js +35 -0
- package/dist/types/cli/index.d.ts +21 -0
- package/dist/types/runtime/I18nLink.d.ts +8 -0
- package/dist/types/runtime/context.d.ts +38 -0
- package/dist/types/runtime/hooks.d.ts +28 -0
- package/dist/types/runtime/i18n/backend/config.d.ts +2 -0
- package/dist/types/runtime/i18n/backend/defaults.d.ts +13 -0
- package/dist/types/runtime/i18n/backend/defaults.node.d.ts +8 -0
- package/dist/types/runtime/i18n/backend/index.d.ts +3 -0
- package/dist/types/runtime/i18n/backend/middleware.common.d.ts +14 -0
- package/dist/types/runtime/i18n/backend/middleware.d.ts +12 -0
- package/dist/types/runtime/i18n/backend/middleware.node.d.ts +13 -0
- package/dist/types/runtime/i18n/backend/sdk-backend.d.ts +52 -0
- package/dist/types/runtime/i18n/detection/config.d.ts +11 -0
- package/dist/types/runtime/i18n/detection/index.d.ts +50 -0
- package/dist/types/runtime/i18n/detection/middleware.d.ts +24 -0
- package/dist/types/runtime/i18n/detection/middleware.node.d.ts +17 -0
- package/dist/types/runtime/i18n/index.d.ts +3 -0
- package/dist/types/runtime/i18n/instance.d.ts +93 -0
- package/dist/types/runtime/i18n/utils.d.ts +29 -0
- package/dist/types/runtime/index.d.ts +19 -0
- package/dist/types/runtime/types.d.ts +15 -0
- package/dist/types/runtime/utils.d.ts +33 -0
- package/dist/types/server/index.d.ts +8 -0
- package/dist/types/shared/deepMerge.d.ts +1 -0
- package/dist/types/shared/detection.d.ts +11 -0
- package/dist/types/shared/type.d.ts +156 -0
- package/dist/types/shared/utils.d.ts +5 -0
- package/package.json +100 -34
- package/rslib.config.mts +4 -0
- package/src/cli/index.ts +245 -0
- package/src/runtime/I18nLink.tsx +76 -0
- package/src/runtime/context.tsx +256 -0
- package/src/runtime/hooks.ts +274 -0
- package/src/runtime/i18n/backend/config.ts +10 -0
- package/src/runtime/i18n/backend/defaults.node.ts +31 -0
- package/src/runtime/i18n/backend/defaults.ts +37 -0
- package/src/runtime/i18n/backend/index.ts +181 -0
- package/src/runtime/i18n/backend/middleware.common.ts +116 -0
- package/src/runtime/i18n/backend/middleware.node.ts +32 -0
- package/src/runtime/i18n/backend/middleware.ts +28 -0
- package/src/runtime/i18n/backend/sdk-backend.ts +292 -0
- package/src/runtime/i18n/detection/config.ts +32 -0
- package/src/runtime/i18n/detection/index.ts +641 -0
- package/src/runtime/i18n/detection/middleware.node.ts +84 -0
- package/src/runtime/i18n/detection/middleware.ts +251 -0
- package/src/runtime/i18n/index.ts +8 -0
- package/src/runtime/i18n/instance.ts +227 -0
- package/src/runtime/i18n/utils.ts +333 -0
- package/src/runtime/index.tsx +275 -0
- package/src/runtime/types.ts +17 -0
- package/src/runtime/utils.ts +151 -0
- package/src/server/index.ts +336 -0
- package/src/shared/deepMerge.ts +38 -0
- package/src/shared/detection.ts +131 -0
- package/src/shared/type.ts +170 -0
- package/src/shared/utils.ts +82 -0
- package/tsconfig.json +12 -0
- package/dist/cjs/index.js +0 -73
- package/dist/cjs/languageDetector.js +0 -51
- package/dist/cjs/utils/index.js +0 -39
- package/dist/esm/index.js +0 -61
- package/dist/esm/languageDetector.js +0 -33
- package/dist/esm/utils/index.js +0 -16
- package/dist/esm-node/index.js +0 -49
- package/dist/esm-node/languageDetector.js +0 -26
- package/dist/esm-node/utils/index.js +0 -15
- package/dist/types/index.d.ts +0 -34
- package/dist/types/languageDetector.d.ts +0 -6
- package/dist/types/utils/index.d.ts +0 -5
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
i18nServerPlugin: ()=>i18nServerPlugin,
|
|
28
|
+
default: ()=>server
|
|
29
|
+
});
|
|
30
|
+
const hono_namespaceObject = require("@modern-js/server-core/hono");
|
|
31
|
+
const config_cjs_namespaceObject = require("../runtime/i18n/detection/config.cjs");
|
|
32
|
+
const utils_cjs_namespaceObject = require("../shared/utils.cjs");
|
|
33
|
+
const { languageDetector } = hono_namespaceObject;
|
|
34
|
+
const convertToHonoLanguageDetectorOptions = (languages, fallbackLanguage, detectionOptions)=>{
|
|
35
|
+
const mergedDetection = detectionOptions ? (0, config_cjs_namespaceObject.mergeDetectionOptions)(detectionOptions) : config_cjs_namespaceObject.DEFAULT_I18NEXT_DETECTION_OPTIONS;
|
|
36
|
+
const order = (mergedDetection.order || []).filter((item)=>![
|
|
37
|
+
'path',
|
|
38
|
+
'localStorage',
|
|
39
|
+
'navigator',
|
|
40
|
+
'htmlTag',
|
|
41
|
+
'subdomain'
|
|
42
|
+
].includes(item));
|
|
43
|
+
const detectionOrder = order.length > 0 ? order : [
|
|
44
|
+
'querystring',
|
|
45
|
+
'cookie',
|
|
46
|
+
'header'
|
|
47
|
+
];
|
|
48
|
+
const honoOrder = detectionOrder.map((item)=>{
|
|
49
|
+
if ('querystring' === item) return 'querystring';
|
|
50
|
+
if ('cookie' === item) return 'cookie';
|
|
51
|
+
if ('header' === item) return 'header';
|
|
52
|
+
return item;
|
|
53
|
+
});
|
|
54
|
+
const caches = false === mergedDetection.caches ? false : Array.isArray(mergedDetection.caches) && !mergedDetection.caches.includes('cookie') ? false : [
|
|
55
|
+
'cookie'
|
|
56
|
+
];
|
|
57
|
+
return {
|
|
58
|
+
supportedLanguages: languages.length > 0 ? languages : [
|
|
59
|
+
fallbackLanguage
|
|
60
|
+
],
|
|
61
|
+
fallbackLanguage,
|
|
62
|
+
order: honoOrder,
|
|
63
|
+
lookupQueryString: mergedDetection.lookupQuerystring || config_cjs_namespaceObject.DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupQuerystring || 'lng',
|
|
64
|
+
lookupCookie: mergedDetection.lookupCookie || config_cjs_namespaceObject.DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupCookie || 'i18next',
|
|
65
|
+
lookupFromHeaderKey: mergedDetection.lookupHeader || config_cjs_namespaceObject.DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupHeader || 'accept-language',
|
|
66
|
+
...void 0 !== caches && {
|
|
67
|
+
caches
|
|
68
|
+
},
|
|
69
|
+
ignoreCase: true
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
const shouldIgnoreRedirect = (pathname, urlPath, ignoreRedirectRoutes)=>{
|
|
73
|
+
if (!ignoreRedirectRoutes) return false;
|
|
74
|
+
const basePath = urlPath.replace('/*', '');
|
|
75
|
+
const remainingPath = pathname.startsWith(basePath) ? pathname.slice(basePath.length) : pathname;
|
|
76
|
+
const normalizedPath = remainingPath.startsWith('/') ? remainingPath : `/${remainingPath}`;
|
|
77
|
+
if ('function' == typeof ignoreRedirectRoutes) return ignoreRedirectRoutes(normalizedPath);
|
|
78
|
+
return ignoreRedirectRoutes.some((pattern)=>normalizedPath === pattern || normalizedPath.startsWith(`${pattern}/`));
|
|
79
|
+
};
|
|
80
|
+
const isStaticResourceRequest = (pathname, staticRoutePrefixes, languages = [])=>{
|
|
81
|
+
if (staticRoutePrefixes.some((prefix)=>pathname.startsWith(`${prefix}/`) || pathname === prefix)) return true;
|
|
82
|
+
const standardStaticPrefixes = [
|
|
83
|
+
'/static/',
|
|
84
|
+
'/upload/'
|
|
85
|
+
];
|
|
86
|
+
if (standardStaticPrefixes.some((prefix)=>pathname.startsWith(prefix))) return true;
|
|
87
|
+
const pathSegments = pathname.split('/').filter(Boolean);
|
|
88
|
+
if (pathSegments.length > 0 && languages.includes(pathSegments[0])) {
|
|
89
|
+
const pathWithoutLang = '/' + pathSegments.slice(1).join('/');
|
|
90
|
+
if (standardStaticPrefixes.some((prefix)=>pathWithoutLang.startsWith(prefix)) || staticRoutePrefixes.some((prefix)=>pathWithoutLang.startsWith(`${prefix}/`) || pathWithoutLang === prefix)) return true;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
};
|
|
94
|
+
const getLanguageFromPath = (req, urlPath, languages)=>{
|
|
95
|
+
const url = new URL(req.url, `http://${req.header().host}`);
|
|
96
|
+
const pathname = url.pathname;
|
|
97
|
+
const basePath = urlPath.replace('/*', '');
|
|
98
|
+
const remainingPath = pathname.startsWith(basePath) ? pathname.slice(basePath.length) : pathname;
|
|
99
|
+
const segments = remainingPath.split('/').filter(Boolean);
|
|
100
|
+
const firstSegment = segments[0];
|
|
101
|
+
if (languages.includes(firstSegment)) return firstSegment;
|
|
102
|
+
return null;
|
|
103
|
+
};
|
|
104
|
+
const buildLocalizedUrl = (req, urlPath, language, languages)=>{
|
|
105
|
+
const url = new URL(req.url);
|
|
106
|
+
const pathname = url.pathname;
|
|
107
|
+
const basePath = urlPath.replace('/*', '');
|
|
108
|
+
const remainingPath = pathname.startsWith(basePath) ? pathname.slice(basePath.length) : pathname;
|
|
109
|
+
const segments = remainingPath.split('/').filter(Boolean);
|
|
110
|
+
if (segments.length > 0 && languages.includes(segments[0])) segments[0] = language;
|
|
111
|
+
else segments.unshift(language);
|
|
112
|
+
const newPathname = `/${segments.join('/')}`;
|
|
113
|
+
const suffix = `${url.search}${url.hash}`;
|
|
114
|
+
const localizedUrl = '/' === basePath ? newPathname + suffix : basePath + newPathname + suffix;
|
|
115
|
+
return localizedUrl;
|
|
116
|
+
};
|
|
117
|
+
const i18nServerPlugin = (options)=>({
|
|
118
|
+
name: '@modern-js/plugin-i18n/server',
|
|
119
|
+
setup: (api)=>{
|
|
120
|
+
api.onPrepare(()=>{
|
|
121
|
+
const { middlewares, routes } = api.getServerContext();
|
|
122
|
+
routes.map((route)=>{
|
|
123
|
+
const { entryName } = route;
|
|
124
|
+
if (!entryName) return;
|
|
125
|
+
if (!options.localeDetection) return;
|
|
126
|
+
const { localePathRedirect, i18nextDetector = true, languages = [], fallbackLanguage = 'en', detection, ignoreRedirectRoutes } = (0, utils_cjs_namespaceObject.getLocaleDetectionOptions)(entryName, options.localeDetection);
|
|
127
|
+
const staticRoutePrefixes = options.staticRoutePrefixes;
|
|
128
|
+
const originUrlPath = route.urlPath;
|
|
129
|
+
const urlPath = originUrlPath.endsWith('/') ? `${originUrlPath}*` : `${originUrlPath}/*`;
|
|
130
|
+
if (localePathRedirect) {
|
|
131
|
+
if (i18nextDetector) {
|
|
132
|
+
const detectorOptions = convertToHonoLanguageDetectorOptions(languages, fallbackLanguage, detection);
|
|
133
|
+
const detectorHandler = languageDetector(detectorOptions);
|
|
134
|
+
middlewares.push({
|
|
135
|
+
name: 'i18n-language-detector',
|
|
136
|
+
path: urlPath,
|
|
137
|
+
handler: async (c, next)=>{
|
|
138
|
+
const url = new URL(c.req.url);
|
|
139
|
+
const pathname = url.pathname;
|
|
140
|
+
if (isStaticResourceRequest(pathname, staticRoutePrefixes, languages)) return await next();
|
|
141
|
+
return detectorHandler(c, next);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
middlewares.push({
|
|
146
|
+
name: 'i18n-server-middleware',
|
|
147
|
+
path: urlPath,
|
|
148
|
+
handler: async (c, next)=>{
|
|
149
|
+
const url = new URL(c.req.url);
|
|
150
|
+
const pathname = url.pathname;
|
|
151
|
+
if (isStaticResourceRequest(pathname, staticRoutePrefixes, languages)) return await next();
|
|
152
|
+
if (shouldIgnoreRedirect(pathname, urlPath, ignoreRedirectRoutes)) return await next();
|
|
153
|
+
const language = getLanguageFromPath(c.req, urlPath, languages);
|
|
154
|
+
if (!language) {
|
|
155
|
+
let detectedLanguage = null;
|
|
156
|
+
if (i18nextDetector) detectedLanguage = c.get('language') || null;
|
|
157
|
+
const targetLanguage = detectedLanguage || fallbackLanguage;
|
|
158
|
+
const localizedUrl = buildLocalizedUrl(c.req, originUrlPath, targetLanguage, languages);
|
|
159
|
+
return c.redirect(localizedUrl);
|
|
160
|
+
}
|
|
161
|
+
await next();
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
const server = i18nServerPlugin;
|
|
170
|
+
exports["default"] = __webpack_exports__["default"];
|
|
171
|
+
exports.i18nServerPlugin = __webpack_exports__.i18nServerPlugin;
|
|
172
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
173
|
+
"default",
|
|
174
|
+
"i18nServerPlugin"
|
|
175
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
176
|
+
Object.defineProperty(exports, '__esModule', {
|
|
177
|
+
value: true
|
|
178
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
deepMerge: ()=>deepMerge
|
|
28
|
+
});
|
|
29
|
+
function isPlainObject(value) {
|
|
30
|
+
return null !== value && 'object' == typeof value && !Array.isArray(value) && !(value instanceof Date);
|
|
31
|
+
}
|
|
32
|
+
function deepMerge(defaultOptions, userOptions) {
|
|
33
|
+
if (!userOptions) return defaultOptions;
|
|
34
|
+
const merged = {
|
|
35
|
+
...defaultOptions
|
|
36
|
+
};
|
|
37
|
+
for(const key in userOptions){
|
|
38
|
+
const userValue = userOptions[key];
|
|
39
|
+
if (void 0 === userValue) continue;
|
|
40
|
+
const defaultValue = merged[key];
|
|
41
|
+
const isUserValueObject = isPlainObject(userValue);
|
|
42
|
+
const isDefaultValueObject = isPlainObject(defaultValue);
|
|
43
|
+
if (isUserValueObject && isDefaultValueObject) merged[key] = deepMerge(defaultValue, userValue);
|
|
44
|
+
else merged[key] = userValue;
|
|
45
|
+
}
|
|
46
|
+
return merged;
|
|
47
|
+
}
|
|
48
|
+
exports.deepMerge = __webpack_exports__.deepMerge;
|
|
49
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
50
|
+
"deepMerge"
|
|
51
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
52
|
+
Object.defineProperty(exports, '__esModule', {
|
|
53
|
+
value: true
|
|
54
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
detectLanguageFromRequest: ()=>detectLanguageFromRequest
|
|
28
|
+
});
|
|
29
|
+
const config_cjs_namespaceObject = require("../runtime/i18n/detection/config.cjs");
|
|
30
|
+
function detectLanguageFromRequest(req, languages, detectionOptions) {
|
|
31
|
+
try {
|
|
32
|
+
const mergedDetection = detectionOptions ? (0, config_cjs_namespaceObject.mergeDetectionOptions)(detectionOptions) : config_cjs_namespaceObject.DEFAULT_I18NEXT_DETECTION_OPTIONS;
|
|
33
|
+
const order = (mergedDetection.order || []).filter((item)=>![
|
|
34
|
+
'path',
|
|
35
|
+
'localStorage',
|
|
36
|
+
'navigator',
|
|
37
|
+
'htmlTag',
|
|
38
|
+
'subdomain'
|
|
39
|
+
].includes(item));
|
|
40
|
+
const detectionOrder = order.length > 0 ? order : [
|
|
41
|
+
'querystring',
|
|
42
|
+
'cookie',
|
|
43
|
+
'header'
|
|
44
|
+
];
|
|
45
|
+
const getHeader = (name)=>{
|
|
46
|
+
req.headers, Headers;
|
|
47
|
+
return req.headers.get(name);
|
|
48
|
+
};
|
|
49
|
+
for (const method of detectionOrder){
|
|
50
|
+
let detectedLang = null;
|
|
51
|
+
switch(method){
|
|
52
|
+
case 'querystring':
|
|
53
|
+
{
|
|
54
|
+
const lookupKey = mergedDetection.lookupQuerystring || config_cjs_namespaceObject.DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupQuerystring || 'lng';
|
|
55
|
+
const host = getHeader('host') || 'localhost';
|
|
56
|
+
const url = new URL(req.url, `http://${host}`);
|
|
57
|
+
detectedLang = url.searchParams.get(lookupKey);
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
case 'cookie':
|
|
61
|
+
{
|
|
62
|
+
const lookupKey = mergedDetection.lookupCookie || config_cjs_namespaceObject.DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupCookie || 'i18next';
|
|
63
|
+
const cookieHeader = getHeader('Cookie');
|
|
64
|
+
if (cookieHeader) {
|
|
65
|
+
const cookies = cookieHeader.split(';').reduce((acc, item)=>{
|
|
66
|
+
const [key, value] = item.trim().split('=');
|
|
67
|
+
if (key && value) acc[key] = value;
|
|
68
|
+
return acc;
|
|
69
|
+
}, {});
|
|
70
|
+
detectedLang = cookies[lookupKey] || null;
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case 'header':
|
|
75
|
+
{
|
|
76
|
+
const lookupKey = mergedDetection.lookupHeader || config_cjs_namespaceObject.DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupHeader || 'accept-language';
|
|
77
|
+
const acceptLanguage = getHeader(lookupKey);
|
|
78
|
+
if (acceptLanguage) {
|
|
79
|
+
const languagesList = acceptLanguage.split(',').map((lang)=>{
|
|
80
|
+
const [code, q] = lang.trim().split(';');
|
|
81
|
+
return {
|
|
82
|
+
code: code.split('-')[0],
|
|
83
|
+
quality: q ? parseFloat(q.split('=')[1]) : 1.0
|
|
84
|
+
};
|
|
85
|
+
}).sort((a, b)=>b.quality - a.quality);
|
|
86
|
+
for (const lang of languagesList)if (0 === languages.length || languages.includes(lang.code)) {
|
|
87
|
+
detectedLang = lang.code;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (detectedLang && (0 === languages.length || languages.includes(detectedLang))) return detectedLang;
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
exports.detectLanguageFromRequest = __webpack_exports__.detectLanguageFromRequest;
|
|
100
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
101
|
+
"detectLanguageFromRequest"
|
|
102
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
103
|
+
Object.defineProperty(exports, '__esModule', {
|
|
104
|
+
value: true
|
|
105
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.r = (exports1)=>{
|
|
5
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
6
|
+
value: 'Module'
|
|
7
|
+
});
|
|
8
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
9
|
+
value: true
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
})();
|
|
13
|
+
var __webpack_exports__ = {};
|
|
14
|
+
__webpack_require__.r(__webpack_exports__);
|
|
15
|
+
for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
16
|
+
Object.defineProperty(exports, '__esModule', {
|
|
17
|
+
value: true
|
|
18
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
getBackendOptions: ()=>getBackendOptions,
|
|
28
|
+
getEntryConfig: ()=>getEntryConfig,
|
|
29
|
+
getLocaleDetectionOptions: ()=>getLocaleDetectionOptions,
|
|
30
|
+
removeEntryConfigKey: ()=>removeEntryConfigKey
|
|
31
|
+
});
|
|
32
|
+
function getEntryConfig(entryName, config, entryKey) {
|
|
33
|
+
const entryConfigMap = config[entryKey];
|
|
34
|
+
return entryConfigMap?.[entryName];
|
|
35
|
+
}
|
|
36
|
+
function removeEntryConfigKey(config, entryKey) {
|
|
37
|
+
const { [entryKey]: _, ...rest } = config;
|
|
38
|
+
return rest;
|
|
39
|
+
}
|
|
40
|
+
function getLocaleDetectionOptions(entryName, localeDetection) {
|
|
41
|
+
const fullConfig = localeDetection;
|
|
42
|
+
const entryConfig = getEntryConfig(entryName, fullConfig, 'localeDetectionByEntry');
|
|
43
|
+
if (entryConfig) {
|
|
44
|
+
const globalConfig = removeEntryConfigKey(fullConfig, 'localeDetectionByEntry');
|
|
45
|
+
return {
|
|
46
|
+
...globalConfig,
|
|
47
|
+
...entryConfig
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if ('localeDetectionByEntry' in fullConfig) return removeEntryConfigKey(fullConfig, 'localeDetectionByEntry');
|
|
51
|
+
return localeDetection;
|
|
52
|
+
}
|
|
53
|
+
function getBackendOptions(entryName, backend) {
|
|
54
|
+
const fullConfig = backend;
|
|
55
|
+
const entryConfig = getEntryConfig(entryName, fullConfig, 'backendOptionsByEntry');
|
|
56
|
+
if (entryConfig) {
|
|
57
|
+
const globalConfig = removeEntryConfigKey(fullConfig, 'backendOptionsByEntry');
|
|
58
|
+
return {
|
|
59
|
+
...globalConfig,
|
|
60
|
+
...entryConfig
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if ('backendOptionsByEntry' in fullConfig) return removeEntryConfigKey(fullConfig, 'backendOptionsByEntry');
|
|
64
|
+
return backend;
|
|
65
|
+
}
|
|
66
|
+
exports.getBackendOptions = __webpack_exports__.getBackendOptions;
|
|
67
|
+
exports.getEntryConfig = __webpack_exports__.getEntryConfig;
|
|
68
|
+
exports.getLocaleDetectionOptions = __webpack_exports__.getLocaleDetectionOptions;
|
|
69
|
+
exports.removeEntryConfigKey = __webpack_exports__.removeEntryConfigKey;
|
|
70
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
71
|
+
"getBackendOptions",
|
|
72
|
+
"getEntryConfig",
|
|
73
|
+
"getLocaleDetectionOptions",
|
|
74
|
+
"removeEntryConfigKey"
|
|
75
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
76
|
+
Object.defineProperty(exports, '__esModule', {
|
|
77
|
+
value: true
|
|
78
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { getPublicDirRoutePrefixes } from "@modern-js/server-core";
|
|
4
|
+
import { getBackendOptions, getLocaleDetectionOptions } from "../shared/utils";
|
|
5
|
+
function hasJsonFiles(dirPath) {
|
|
6
|
+
try {
|
|
7
|
+
if (!fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory()) return false;
|
|
8
|
+
const entries = fs.readdirSync(dirPath);
|
|
9
|
+
for (const entry of entries){
|
|
10
|
+
const entryPath = path.join(dirPath, entry);
|
|
11
|
+
const stat = fs.statSync(entryPath);
|
|
12
|
+
if (stat.isFile() && entry.endsWith('.json')) return true;
|
|
13
|
+
if (stat.isDirectory()) {
|
|
14
|
+
if (hasJsonFiles(entryPath)) return true;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
} catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function detectLocalesDirectory(appDirectory, normalizedConfig) {
|
|
23
|
+
const rootLocalesPath = path.join(appDirectory, 'locales');
|
|
24
|
+
if (hasJsonFiles(rootLocalesPath)) return true;
|
|
25
|
+
const configPublicPath = path.join(appDirectory, 'config', 'public', 'locales');
|
|
26
|
+
if (hasJsonFiles(configPublicPath)) return true;
|
|
27
|
+
const publicDir = normalizedConfig?.server?.publicDir;
|
|
28
|
+
if (publicDir) {
|
|
29
|
+
const publicDirPath = Array.isArray(publicDir) ? publicDir[0] : publicDir;
|
|
30
|
+
const localesPath = path.isAbsolute(publicDirPath) ? path.join(publicDirPath, 'locales') : path.join(appDirectory, publicDirPath, 'locales');
|
|
31
|
+
if (hasJsonFiles(localesPath)) return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const i18nPlugin = (options = {})=>({
|
|
36
|
+
name: '@modern-js/plugin-i18n',
|
|
37
|
+
setup: (api)=>{
|
|
38
|
+
const { localeDetection, backend, transformRuntimeConfig, customPlugin, ...restOptions } = options;
|
|
39
|
+
api._internalRuntimePlugins(({ entrypoint, plugins })=>{
|
|
40
|
+
const localeDetectionOptions = localeDetection ? getLocaleDetectionOptions(entrypoint.entryName, localeDetection) : void 0;
|
|
41
|
+
let backendOptions;
|
|
42
|
+
const { appDirectory } = api.getAppContext();
|
|
43
|
+
const normalizedConfig = api.getNormalizedConfig();
|
|
44
|
+
if (backend) {
|
|
45
|
+
const entryBackendOptions = getBackendOptions(entrypoint.entryName, backend);
|
|
46
|
+
if (entryBackendOptions?.enabled === false) backendOptions = entryBackendOptions;
|
|
47
|
+
else if (entryBackendOptions?.loadPath || entryBackendOptions?.addPath) backendOptions = {
|
|
48
|
+
...entryBackendOptions,
|
|
49
|
+
enabled: true
|
|
50
|
+
};
|
|
51
|
+
else if (entryBackendOptions?.enabled !== true) {
|
|
52
|
+
const hasLocales = detectLocalesDirectory(appDirectory, normalizedConfig);
|
|
53
|
+
backendOptions = hasLocales ? {
|
|
54
|
+
...entryBackendOptions,
|
|
55
|
+
enabled: true
|
|
56
|
+
} : entryBackendOptions;
|
|
57
|
+
} else backendOptions = entryBackendOptions;
|
|
58
|
+
} else {
|
|
59
|
+
const hasLocales = detectLocalesDirectory(appDirectory, normalizedConfig);
|
|
60
|
+
if (hasLocales) backendOptions = getBackendOptions(entrypoint.entryName, {
|
|
61
|
+
enabled: true
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
const { metaName } = api.getAppContext();
|
|
65
|
+
let extendedConfig = restOptions;
|
|
66
|
+
if (transformRuntimeConfig) extendedConfig = transformRuntimeConfig(restOptions, entrypoint);
|
|
67
|
+
const config = {
|
|
68
|
+
entryName: entrypoint.entryName,
|
|
69
|
+
localeDetection: localeDetectionOptions,
|
|
70
|
+
backend: backendOptions,
|
|
71
|
+
...extendedConfig
|
|
72
|
+
};
|
|
73
|
+
plugins.push({
|
|
74
|
+
name: customPlugin?.runtime?.name || 'i18n',
|
|
75
|
+
path: customPlugin?.runtime?.path || `@${metaName}/plugin-i18n/runtime`,
|
|
76
|
+
config
|
|
77
|
+
});
|
|
78
|
+
return {
|
|
79
|
+
entrypoint,
|
|
80
|
+
plugins
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
api._internalServerPlugins(({ plugins })=>{
|
|
84
|
+
const { serverRoutes, metaName } = api.getAppContext();
|
|
85
|
+
const normalizedConfig = api.getNormalizedConfig();
|
|
86
|
+
let staticRoutePrefixes = [];
|
|
87
|
+
if (serverRoutes && Array.isArray(serverRoutes)) staticRoutePrefixes = serverRoutes.filter((route)=>!route.entryName && route.entryPath.startsWith('public')).map((route)=>route.urlPath).filter(Boolean);
|
|
88
|
+
const publicDirPrefixes = getPublicDirRoutePrefixes(normalizedConfig?.server?.publicDir);
|
|
89
|
+
publicDirPrefixes.forEach((prefix)=>{
|
|
90
|
+
if (!staticRoutePrefixes.includes(prefix)) staticRoutePrefixes.push(prefix);
|
|
91
|
+
});
|
|
92
|
+
plugins.push({
|
|
93
|
+
name: customPlugin?.server?.name || `@${metaName}/plugin-i18n/server`,
|
|
94
|
+
options: {
|
|
95
|
+
localeDetection,
|
|
96
|
+
staticRoutePrefixes
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
return {
|
|
100
|
+
plugins
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
const cli = i18nPlugin;
|
|
106
|
+
export { cli as default, i18nPlugin };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Link as router_Link, useInRouterContext, useParams } from "@modern-js/runtime/router";
|
|
3
|
+
import { useModernI18n } from "./context";
|
|
4
|
+
import { buildLocalizedUrl } from "./utils";
|
|
5
|
+
const useRouterHooks = ()=>{
|
|
6
|
+
const inRouter = useInRouterContext();
|
|
7
|
+
return {
|
|
8
|
+
Link: inRouter ? router_Link : null,
|
|
9
|
+
params: inRouter ? useParams() : {},
|
|
10
|
+
hasRouter: inRouter
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
const I18nLink = ({ to, children, ...props })=>{
|
|
14
|
+
const { Link, params, hasRouter } = useRouterHooks();
|
|
15
|
+
const { language, supportedLanguages } = useModernI18n();
|
|
16
|
+
const currentLang = language;
|
|
17
|
+
const localizedTo = buildLocalizedUrl(to, currentLang, supportedLanguages);
|
|
18
|
+
if ('development' === process.env.NODE_ENV && hasRouter && !params.lang) console.warn("I18nLink is being used outside of a :lang dynamic route context. This may cause unexpected behavior. Please ensure I18nLink is used within a route that has a :lang parameter.");
|
|
19
|
+
if (!hasRouter || !Link) return /*#__PURE__*/ jsx("a", {
|
|
20
|
+
href: localizedTo,
|
|
21
|
+
...props,
|
|
22
|
+
children: children
|
|
23
|
+
});
|
|
24
|
+
return /*#__PURE__*/ jsx(Link, {
|
|
25
|
+
to: localizedTo,
|
|
26
|
+
...props,
|
|
27
|
+
children: children
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
const runtime_I18nLink = I18nLink;
|
|
31
|
+
export { I18nLink, runtime_I18nLink as default };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { isBrowser } from "@modern-js/runtime";
|
|
3
|
+
import { createContext, useCallback, useContext, useMemo } from "react";
|
|
4
|
+
import { cacheUserLanguage } from "./i18n/detection";
|
|
5
|
+
import { buildLocalizedUrl, getEntryPath, shouldIgnoreRedirect, useRouterHooks } from "./utils";
|
|
6
|
+
const ModernI18nContext = /*#__PURE__*/ createContext(null);
|
|
7
|
+
const ModernI18nProvider = ({ children, value })=>/*#__PURE__*/ jsx(ModernI18nContext.Provider, {
|
|
8
|
+
value: value,
|
|
9
|
+
children: children
|
|
10
|
+
});
|
|
11
|
+
const useModernI18n = ()=>{
|
|
12
|
+
const context = useContext(ModernI18nContext);
|
|
13
|
+
if (!context) throw new Error('useModernI18n must be used within a ModernI18nProvider');
|
|
14
|
+
const { language: contextLanguage, i18nInstance, languages, localePathRedirect, ignoreRedirectRoutes, updateLanguage } = context;
|
|
15
|
+
const { navigate, location, hasRouter } = useRouterHooks();
|
|
16
|
+
const currentLanguage = contextLanguage;
|
|
17
|
+
const changeLanguage = useCallback(async (newLang)=>{
|
|
18
|
+
try {
|
|
19
|
+
if (!newLang || 'string' != typeof newLang) throw new Error('Language must be a non-empty string');
|
|
20
|
+
await i18nInstance?.setLang?.(newLang);
|
|
21
|
+
await i18nInstance?.changeLanguage?.(newLang);
|
|
22
|
+
if (isBrowser()) {
|
|
23
|
+
const detectionOptions = i18nInstance.options?.detection;
|
|
24
|
+
cacheUserLanguage(i18nInstance, newLang, detectionOptions);
|
|
25
|
+
}
|
|
26
|
+
if (localePathRedirect && isBrowser() && hasRouter && navigate && location) {
|
|
27
|
+
const currentPath = location.pathname;
|
|
28
|
+
const entryPath = getEntryPath();
|
|
29
|
+
const relativePath = currentPath.replace(entryPath, '');
|
|
30
|
+
if (!shouldIgnoreRedirect(relativePath, languages || [], ignoreRedirectRoutes)) {
|
|
31
|
+
const newPath = buildLocalizedUrl(relativePath, newLang, languages || []);
|
|
32
|
+
const newUrl = entryPath + newPath + location.search + location.hash;
|
|
33
|
+
await navigate(newUrl, {
|
|
34
|
+
replace: true
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
} else if (localePathRedirect && isBrowser() && !hasRouter) {
|
|
38
|
+
const currentPath = window.location.pathname;
|
|
39
|
+
const entryPath = getEntryPath();
|
|
40
|
+
const relativePath = currentPath.replace(entryPath, '');
|
|
41
|
+
if (!shouldIgnoreRedirect(relativePath, languages || [], ignoreRedirectRoutes)) {
|
|
42
|
+
const newPath = buildLocalizedUrl(relativePath, newLang, languages || []);
|
|
43
|
+
const newUrl = entryPath + newPath + window.location.search + window.location.hash;
|
|
44
|
+
window.history.pushState(null, '', newUrl);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (updateLanguage) updateLanguage(newLang);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('Failed to change language:', error);
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}, [
|
|
53
|
+
i18nInstance,
|
|
54
|
+
updateLanguage,
|
|
55
|
+
localePathRedirect,
|
|
56
|
+
ignoreRedirectRoutes,
|
|
57
|
+
languages,
|
|
58
|
+
hasRouter,
|
|
59
|
+
navigate,
|
|
60
|
+
location
|
|
61
|
+
]);
|
|
62
|
+
const isLanguageSupported = useCallback((lang)=>languages?.includes(lang) || false, [
|
|
63
|
+
languages
|
|
64
|
+
]);
|
|
65
|
+
const isResourcesReady = useMemo(()=>{
|
|
66
|
+
if (!i18nInstance?.isInitialized) return false;
|
|
67
|
+
const backend = i18nInstance?.services?.backend;
|
|
68
|
+
if (backend && 'function' == typeof backend.isLoading) {
|
|
69
|
+
const loadingResources = backend.getLoadingResources();
|
|
70
|
+
const isCurrentLanguageLoading = loadingResources.some(({ language })=>language === currentLanguage);
|
|
71
|
+
if (isCurrentLanguageLoading) return false;
|
|
72
|
+
}
|
|
73
|
+
const store = i18nInstance.store;
|
|
74
|
+
if (!store?.data) return false;
|
|
75
|
+
const langData = store.data[currentLanguage];
|
|
76
|
+
if (!langData || 'object' != typeof langData) return false;
|
|
77
|
+
const options = i18nInstance.options;
|
|
78
|
+
const namespaces = options?.ns || options?.defaultNS || [
|
|
79
|
+
'translation'
|
|
80
|
+
];
|
|
81
|
+
const requiredNamespaces = Array.isArray(namespaces) ? namespaces : [
|
|
82
|
+
namespaces
|
|
83
|
+
];
|
|
84
|
+
return requiredNamespaces.every((ns)=>{
|
|
85
|
+
const nsData = langData[ns];
|
|
86
|
+
return nsData && 'object' == typeof nsData && Object.keys(nsData).length > 0;
|
|
87
|
+
});
|
|
88
|
+
}, [
|
|
89
|
+
currentLanguage,
|
|
90
|
+
i18nInstance
|
|
91
|
+
]);
|
|
92
|
+
return {
|
|
93
|
+
language: currentLanguage,
|
|
94
|
+
changeLanguage,
|
|
95
|
+
i18nInstance,
|
|
96
|
+
supportedLanguages: languages || [],
|
|
97
|
+
isLanguageSupported,
|
|
98
|
+
isResourcesReady
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
export { ModernI18nProvider, useModernI18n };
|