@bleedingdev/modern-js-plugin-i18n 3.2.0-ultramodern.2 → 3.2.0-ultramodern.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/cli/index.js +22 -0
- package/dist/cjs/runtime/I18nLink.js +4 -12
- package/dist/cjs/runtime/context.js +32 -5
- package/dist/cjs/runtime/hooks.js +8 -5
- package/dist/cjs/runtime/i18n/backend/defaults.js +1 -1
- package/dist/cjs/runtime/i18n/backend/middleware.node.js +4 -4
- package/dist/cjs/runtime/i18n/instance.js +4 -2
- package/dist/cjs/runtime/index.js +7 -6
- package/dist/cjs/runtime/routerAdapter.js +163 -0
- package/dist/cjs/runtime/utils.js +63 -94
- package/dist/cjs/server/index.js +64 -8
- package/dist/cjs/shared/localisedUrls.js +237 -0
- package/dist/esm/cli/index.mjs +22 -0
- package/dist/esm/runtime/I18nLink.mjs +4 -12
- package/dist/esm/runtime/context.mjs +34 -7
- package/dist/esm/runtime/hooks.mjs +9 -6
- package/dist/esm/runtime/i18n/backend/defaults.mjs +1 -1
- package/dist/esm/runtime/i18n/backend/middleware.node.mjs +3 -3
- package/dist/esm/runtime/i18n/instance.mjs +4 -2
- package/dist/esm/runtime/index.mjs +7 -6
- package/dist/esm/runtime/routerAdapter.mjs +129 -0
- package/dist/esm/runtime/utils.mjs +11 -30
- package/dist/esm/server/index.mjs +57 -7
- package/dist/esm/shared/localisedUrls.mjs +191 -0
- package/dist/esm-node/cli/index.mjs +22 -0
- package/dist/esm-node/runtime/I18nLink.mjs +4 -12
- package/dist/esm-node/runtime/context.mjs +34 -7
- package/dist/esm-node/runtime/hooks.mjs +9 -6
- package/dist/esm-node/runtime/i18n/backend/defaults.mjs +1 -1
- package/dist/esm-node/runtime/i18n/backend/middleware.node.mjs +3 -3
- package/dist/esm-node/runtime/i18n/instance.mjs +4 -2
- package/dist/esm-node/runtime/index.mjs +7 -6
- package/dist/esm-node/runtime/routerAdapter.mjs +130 -0
- package/dist/esm-node/runtime/utils.mjs +11 -30
- package/dist/esm-node/server/index.mjs +57 -7
- package/dist/esm-node/shared/localisedUrls.mjs +192 -0
- package/dist/types/cli/index.d.ts +21 -0
- package/dist/types/runtime/I18nLink.d.ts +23 -0
- package/dist/types/runtime/context.d.ts +41 -0
- package/dist/types/runtime/hooks.d.ts +30 -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 +53 -0
- package/dist/types/runtime/i18n/backend/sdk-event.d.ts +9 -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 +96 -0
- package/dist/types/runtime/i18n/utils.d.ts +29 -0
- package/dist/types/runtime/index.d.ts +21 -0
- package/dist/types/runtime/routerAdapter.d.ts +26 -0
- package/dist/types/runtime/types.d.ts +15 -0
- package/dist/types/runtime/utils.d.ts +28 -0
- package/dist/types/server/index.d.ts +14 -0
- package/dist/types/shared/deepMerge.d.ts +1 -0
- package/dist/types/shared/detection.d.ts +11 -0
- package/dist/types/shared/localisedUrls.d.ts +13 -0
- package/dist/types/shared/type.d.ts +168 -0
- package/dist/types/shared/utils.d.ts +5 -0
- package/package.json +15 -15
- package/rstest.config.mts +39 -0
- package/src/cli/index.ts +43 -1
- package/src/runtime/I18nLink.tsx +10 -16
- package/src/runtime/context.tsx +45 -7
- package/src/runtime/hooks.ts +13 -4
- package/src/runtime/i18n/backend/defaults.ts +3 -1
- package/src/runtime/i18n/backend/middleware.node.ts +1 -1
- package/src/runtime/i18n/instance.ts +14 -5
- package/src/runtime/index.tsx +10 -2
- package/src/runtime/routerAdapter.tsx +333 -0
- package/src/runtime/utils.ts +22 -34
- package/src/server/index.ts +135 -10
- package/src/shared/localisedUrls.ts +393 -0
- package/src/shared/type.ts +12 -0
- package/tests/localisedUrls.test.ts +278 -0
- package/tests/routerAdapter.test.tsx +278 -0
- package/dist/esm/rslib-runtime.mjs +0 -18
- package/dist/esm-node/rslib-runtime.mjs +0 -19
package/dist/cjs/server/index.js
CHANGED
|
@@ -24,13 +24,46 @@ var __webpack_require__ = {};
|
|
|
24
24
|
var __webpack_exports__ = {};
|
|
25
25
|
__webpack_require__.r(__webpack_exports__);
|
|
26
26
|
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
collectApiPrefixes: ()=>collectApiPrefixes,
|
|
27
28
|
default: ()=>server,
|
|
28
|
-
i18nServerPlugin: ()=>i18nServerPlugin
|
|
29
|
+
i18nServerPlugin: ()=>i18nServerPlugin,
|
|
30
|
+
matchesApiPrefix: ()=>matchesApiPrefix
|
|
29
31
|
});
|
|
30
32
|
const hono_namespaceObject = require("@modern-js/server-core/hono");
|
|
31
33
|
const config_js_namespaceObject = require("../runtime/i18n/detection/config.js");
|
|
34
|
+
const localisedUrls_js_namespaceObject = require("../shared/localisedUrls.js");
|
|
32
35
|
const utils_js_namespaceObject = require("../shared/utils.js");
|
|
33
36
|
const { languageDetector } = hono_namespaceObject;
|
|
37
|
+
const normalizeApiPrefix = (prefix)=>{
|
|
38
|
+
const trimmedPrefix = prefix.trim();
|
|
39
|
+
if (!trimmedPrefix) return null;
|
|
40
|
+
const prefixedPath = trimmedPrefix.startsWith('/') ? trimmedPrefix : `/${trimmedPrefix}`;
|
|
41
|
+
const withoutWildcard = prefixedPath.replace(/\/\*$/, '');
|
|
42
|
+
const normalizedPrefix = withoutWildcard.length > 1 ? withoutWildcard.replace(/\/+$/, '') : withoutWildcard;
|
|
43
|
+
return '/' === normalizedPrefix ? null : normalizedPrefix;
|
|
44
|
+
};
|
|
45
|
+
const collectApiPrefixes = (routes, bffPrefix)=>{
|
|
46
|
+
const prefixes = new Set();
|
|
47
|
+
for (const route of routes){
|
|
48
|
+
if (!route.isApi || !route.urlPath) continue;
|
|
49
|
+
const normalizedPrefix = normalizeApiPrefix(route.urlPath);
|
|
50
|
+
if (normalizedPrefix) prefixes.add(normalizedPrefix);
|
|
51
|
+
}
|
|
52
|
+
const bffPrefixes = Array.isArray(bffPrefix) ? bffPrefix : bffPrefix ? [
|
|
53
|
+
bffPrefix
|
|
54
|
+
] : [];
|
|
55
|
+
for (const prefix of bffPrefixes){
|
|
56
|
+
const normalizedPrefix = normalizeApiPrefix(prefix);
|
|
57
|
+
if (normalizedPrefix) prefixes.add(normalizedPrefix);
|
|
58
|
+
}
|
|
59
|
+
return [
|
|
60
|
+
...prefixes
|
|
61
|
+
];
|
|
62
|
+
};
|
|
63
|
+
const matchesApiPrefix = (pathname, apiPrefixes)=>{
|
|
64
|
+
const normalizedPathname = pathname.startsWith('/') ? pathname : `/${pathname}`;
|
|
65
|
+
return apiPrefixes.some((prefix)=>normalizedPathname === prefix || normalizedPathname.startsWith(`${prefix}/`));
|
|
66
|
+
};
|
|
34
67
|
const convertToHonoLanguageDetectorOptions = (languages, fallbackLanguage, detectionOptions)=>{
|
|
35
68
|
const mergedDetection = detectionOptions ? (0, config_js_namespaceObject.mergeDetectionOptions)(detectionOptions) : config_js_namespaceObject.DEFAULT_I18NEXT_DETECTION_OPTIONS;
|
|
36
69
|
const order = (mergedDetection.order || []).filter((item)=>![
|
|
@@ -120,15 +153,20 @@ const getLanguageFromPath = (req, urlPath, languages)=>{
|
|
|
120
153
|
if (languages.includes(firstSegment)) return firstSegment;
|
|
121
154
|
return null;
|
|
122
155
|
};
|
|
123
|
-
const buildLocalizedUrl = (req, urlPath, language, languages)=>{
|
|
156
|
+
const buildLocalizedUrl = (req, urlPath, language, languages, localisedUrls)=>{
|
|
124
157
|
const url = new URL(req.url);
|
|
125
158
|
const pathname = url.pathname;
|
|
126
159
|
const basePath = urlPath.replace('/*', '');
|
|
127
160
|
const remainingPath = pathname.startsWith(basePath) ? pathname.slice(basePath.length) : pathname;
|
|
128
161
|
const segments = remainingPath.split('/').filter(Boolean);
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
162
|
+
const localisedUrlsConfig = (0, localisedUrls_js_namespaceObject.resolveLocalisedUrlsConfig)(localisedUrls);
|
|
163
|
+
const pathWithoutLanguage = segments.length > 0 && languages.includes(segments[0]) ? `/${segments.slice(1).join('/')}` : remainingPath;
|
|
164
|
+
const resolvedPath = localisedUrlsConfig.enabled ? (0, localisedUrls_js_namespaceObject.resolveLocalisedPath)(pathWithoutLanguage, language, languages, localisedUrlsConfig.map) : pathWithoutLanguage;
|
|
165
|
+
const resolvedSegments = resolvedPath.split('/').filter(Boolean);
|
|
166
|
+
const newPathname = `/${[
|
|
167
|
+
language,
|
|
168
|
+
...resolvedSegments
|
|
169
|
+
].join('/')}`;
|
|
132
170
|
const suffix = `${url.search}${url.hash}`;
|
|
133
171
|
const localizedUrl = '/' === basePath ? newPathname + suffix : basePath + newPathname + suffix;
|
|
134
172
|
return localizedUrl;
|
|
@@ -138,6 +176,9 @@ const i18nServerPlugin = (options)=>({
|
|
|
138
176
|
setup: (api)=>{
|
|
139
177
|
api.onPrepare(()=>{
|
|
140
178
|
const { middlewares, routes } = api.getServerContext();
|
|
179
|
+
const serverConfig = api.getServerConfig();
|
|
180
|
+
const bffPrefix = serverConfig?.bff ? serverConfig.bff.prefix ?? '/api' : void 0;
|
|
181
|
+
const apiPrefixes = collectApiPrefixes(routes, bffPrefix);
|
|
141
182
|
const entryPaths = new Set();
|
|
142
183
|
routes.forEach((route)=>{
|
|
143
184
|
if (route.entryName && route.urlPath && '/' !== route.urlPath) {
|
|
@@ -149,7 +190,7 @@ const i18nServerPlugin = (options)=>({
|
|
|
149
190
|
const { entryName } = route;
|
|
150
191
|
if (!entryName) return;
|
|
151
192
|
if (!options.localeDetection) return;
|
|
152
|
-
const { localePathRedirect, i18nextDetector = true, languages = [], fallbackLanguage = 'en', detection, ignoreRedirectRoutes } = (0, utils_js_namespaceObject.getLocaleDetectionOptions)(entryName, options.localeDetection);
|
|
193
|
+
const { localePathRedirect, i18nextDetector = true, languages = [], fallbackLanguage = 'en', detection, ignoreRedirectRoutes, localisedUrls } = (0, utils_js_namespaceObject.getLocaleDetectionOptions)(entryName, options.localeDetection);
|
|
153
194
|
const staticRoutePrefixes = options.staticRoutePrefixes;
|
|
154
195
|
const originUrlPath = route.urlPath;
|
|
155
196
|
const urlPath = originUrlPath.endsWith('/') ? `${originUrlPath}*` : `${originUrlPath}/*`;
|
|
@@ -163,6 +204,7 @@ const i18nServerPlugin = (options)=>({
|
|
|
163
204
|
handler: async (c, next)=>{
|
|
164
205
|
const url = new URL(c.req.url);
|
|
165
206
|
const pathname = url.pathname;
|
|
207
|
+
if (matchesApiPrefix(pathname, apiPrefixes)) return await next();
|
|
166
208
|
if (isStaticResourceRequest(pathname, staticRoutePrefixes, languages)) return await next();
|
|
167
209
|
if ('/' === originUrlPath) {
|
|
168
210
|
const pathSegments = pathname.split('/').filter(Boolean);
|
|
@@ -181,6 +223,7 @@ const i18nServerPlugin = (options)=>({
|
|
|
181
223
|
handler: async (c, next)=>{
|
|
182
224
|
const url = new URL(c.req.url);
|
|
183
225
|
const pathname = url.pathname;
|
|
226
|
+
if (matchesApiPrefix(pathname, apiPrefixes)) return await next();
|
|
184
227
|
if (isStaticResourceRequest(pathname, staticRoutePrefixes, languages)) return await next();
|
|
185
228
|
if (shouldIgnoreRedirect(pathname, urlPath, ignoreRedirectRoutes)) return await next();
|
|
186
229
|
if ('/' === originUrlPath) {
|
|
@@ -195,9 +238,18 @@ const i18nServerPlugin = (options)=>({
|
|
|
195
238
|
let detectedLanguage = null;
|
|
196
239
|
if (i18nextDetector) detectedLanguage = c.get('language') || null;
|
|
197
240
|
const targetLanguage = detectedLanguage || fallbackLanguage;
|
|
198
|
-
const localizedUrl = buildLocalizedUrl(c.req, originUrlPath, targetLanguage, languages);
|
|
241
|
+
const localizedUrl = buildLocalizedUrl(c.req, originUrlPath, targetLanguage, languages, localisedUrls);
|
|
199
242
|
return c.redirect(localizedUrl);
|
|
200
243
|
}
|
|
244
|
+
const localisedUrlsConfig = (0, localisedUrls_js_namespaceObject.resolveLocalisedUrlsConfig)(localisedUrls);
|
|
245
|
+
if (localisedUrlsConfig.enabled) {
|
|
246
|
+
const basePath = originUrlPath.replace('/*', '');
|
|
247
|
+
const remainingPath = pathname.startsWith(basePath) ? pathname.slice(basePath.length) : pathname;
|
|
248
|
+
const pathWithoutLanguage = remainingPath.split('/').filter(Boolean).slice(1).join('/');
|
|
249
|
+
const canonicalLocalizedPath = (0, localisedUrls_js_namespaceObject.resolveLocalisedPath)(`/${pathWithoutLanguage}`, language, languages, localisedUrlsConfig.map);
|
|
250
|
+
const expectedPathname = '/' === basePath ? `/${language}${'/' === canonicalLocalizedPath ? '' : canonicalLocalizedPath}` : `${basePath}/${language}${'/' === canonicalLocalizedPath ? '' : canonicalLocalizedPath}`;
|
|
251
|
+
if (expectedPathname !== pathname) return c.redirect(`${expectedPathname}${url.search}${url.hash}`);
|
|
252
|
+
}
|
|
201
253
|
await next();
|
|
202
254
|
}
|
|
203
255
|
});
|
|
@@ -207,11 +259,15 @@ const i18nServerPlugin = (options)=>({
|
|
|
207
259
|
}
|
|
208
260
|
});
|
|
209
261
|
const server = i18nServerPlugin;
|
|
262
|
+
exports.collectApiPrefixes = __webpack_exports__.collectApiPrefixes;
|
|
210
263
|
exports["default"] = __webpack_exports__["default"];
|
|
211
264
|
exports.i18nServerPlugin = __webpack_exports__.i18nServerPlugin;
|
|
265
|
+
exports.matchesApiPrefix = __webpack_exports__.matchesApiPrefix;
|
|
212
266
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
267
|
+
"collectApiPrefixes",
|
|
213
268
|
"default",
|
|
214
|
-
"i18nServerPlugin"
|
|
269
|
+
"i18nServerPlugin",
|
|
270
|
+
"matchesApiPrefix"
|
|
215
271
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
216
272
|
Object.defineProperty(exports, '__esModule', {
|
|
217
273
|
value: true
|
|
@@ -0,0 +1,237 @@
|
|
|
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
|
+
applyLocalisedUrlsToRoutes: ()=>applyLocalisedUrlsToRoutes,
|
|
28
|
+
normalisePathPattern: ()=>normalisePathPattern,
|
|
29
|
+
resolveLocalisedPath: ()=>resolveLocalisedPath,
|
|
30
|
+
resolveLocalisedUrlsConfig: ()=>resolveLocalisedUrlsConfig,
|
|
31
|
+
validateLocalisedUrls: ()=>validateLocalisedUrls
|
|
32
|
+
});
|
|
33
|
+
const LOCALE_PARAM_NAMES = new Set([
|
|
34
|
+
'lang',
|
|
35
|
+
'locale',
|
|
36
|
+
'language'
|
|
37
|
+
]);
|
|
38
|
+
const normalisePathPattern = (path)=>{
|
|
39
|
+
const withoutDuplicateSlashes = path.replace(/\/+/g, '/');
|
|
40
|
+
const withLeadingSlash = withoutDuplicateSlashes.startsWith('/') ? withoutDuplicateSlashes : `/${withoutDuplicateSlashes}`;
|
|
41
|
+
const withoutTrailingSlash = withLeadingSlash.length > 1 ? withLeadingSlash.replace(/\/+$/, '') : withLeadingSlash;
|
|
42
|
+
return withoutTrailingSlash.replace(/\[(.+?)\]/g, ':$1');
|
|
43
|
+
};
|
|
44
|
+
const normaliseRoutePath = (path)=>{
|
|
45
|
+
const normalized = normalisePathPattern(path);
|
|
46
|
+
return '/' === normalized ? '' : normalized.slice(1);
|
|
47
|
+
};
|
|
48
|
+
const getLocaleParamSegment = (segment)=>{
|
|
49
|
+
if (!segment.startsWith(':')) return null;
|
|
50
|
+
const paramName = segment.slice(1).replace(/\?$/, '');
|
|
51
|
+
return LOCALE_PARAM_NAMES.has(paramName) ? segment : null;
|
|
52
|
+
};
|
|
53
|
+
const splitPathSegments = (path)=>{
|
|
54
|
+
if (!path) return [];
|
|
55
|
+
return normalisePathPattern(path).split('/').filter(Boolean);
|
|
56
|
+
};
|
|
57
|
+
const stripLeadingLocaleParam = (path)=>{
|
|
58
|
+
const segments = splitPathSegments(path);
|
|
59
|
+
const leadingLocaleParam = getLocaleParamSegment(segments[0] || '');
|
|
60
|
+
if (!leadingLocaleParam) return path;
|
|
61
|
+
const remainingPath = segments.slice(1).join('/');
|
|
62
|
+
return remainingPath ? `/${remainingPath}` : void 0;
|
|
63
|
+
};
|
|
64
|
+
const getLeadingLocaleParam = (path)=>{
|
|
65
|
+
const segments = splitPathSegments(path);
|
|
66
|
+
return getLocaleParamSegment(segments[0] || '');
|
|
67
|
+
};
|
|
68
|
+
const resolveLocalisedUrlsConfig = (option)=>{
|
|
69
|
+
if (false === option) return {
|
|
70
|
+
enabled: false,
|
|
71
|
+
map: {}
|
|
72
|
+
};
|
|
73
|
+
if (option && 'object' == typeof option) return {
|
|
74
|
+
enabled: true,
|
|
75
|
+
map: option
|
|
76
|
+
};
|
|
77
|
+
return {
|
|
78
|
+
enabled: true,
|
|
79
|
+
map: {}
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
const isLocalisableRoutePath = (path)=>{
|
|
83
|
+
const pathWithoutLocale = stripLeadingLocaleParam(path);
|
|
84
|
+
if (!pathWithoutLocale || '/' === pathWithoutLocale || '*' === pathWithoutLocale) return false;
|
|
85
|
+
return true;
|
|
86
|
+
};
|
|
87
|
+
const joinPath = (parentPath, routePath)=>{
|
|
88
|
+
if (!isLocalisableRoutePath(routePath)) return parentPath;
|
|
89
|
+
const segment = normaliseRoutePath(stripLeadingLocaleParam(routePath) || '');
|
|
90
|
+
return normalisePathPattern(`${parentPath}/${segment}`);
|
|
91
|
+
};
|
|
92
|
+
const ensureLocalisedUrlsForPath = (canonicalPath, languages, localisedUrls)=>{
|
|
93
|
+
const entry = localisedUrls[canonicalPath];
|
|
94
|
+
if (!entry) throw new Error(`localisedUrls is enabled, but route "${canonicalPath}" does not define localised URLs for languages: ${languages.join(', ')}. Add localisedUrls["${canonicalPath}"] or set localeDetection.localisedUrls to false.`);
|
|
95
|
+
const missingLanguages = languages.filter((language)=>!entry[language]);
|
|
96
|
+
if (missingLanguages.length > 0) throw new Error(`localisedUrls["${canonicalPath}"] is missing languages: ${missingLanguages.join(', ')}. Every configured language must have a localised URL.`);
|
|
97
|
+
return entry;
|
|
98
|
+
};
|
|
99
|
+
const validateLocalisedUrls = (routes, languages, localisedUrls)=>{
|
|
100
|
+
const visit = (route, parentPath)=>{
|
|
101
|
+
const canonicalPath = joinPath(parentPath, route.path);
|
|
102
|
+
if (isLocalisableRoutePath(route.path)) ensureLocalisedUrlsForPath(canonicalPath, languages, localisedUrls);
|
|
103
|
+
if ('children' in route && route.children) route.children.forEach((child)=>visit(child, canonicalPath));
|
|
104
|
+
};
|
|
105
|
+
routes.forEach((route)=>visit(route, ''));
|
|
106
|
+
};
|
|
107
|
+
const getLocalisedRoutePaths = (canonicalPath, parentLocalisedPaths, languages, entry)=>{
|
|
108
|
+
const paths = languages.map((language)=>{
|
|
109
|
+
const fullPath = normalisePathPattern(entry[language]);
|
|
110
|
+
const parentPath = normalisePathPattern(parentLocalisedPaths[language] || '/');
|
|
111
|
+
if ('/' === parentPath) return normaliseRoutePath(fullPath) || void 0;
|
|
112
|
+
const parentPrefix = `${parentPath}/`;
|
|
113
|
+
if (!fullPath.startsWith(parentPrefix)) throw new Error(`localisedUrls["${canonicalPath}"].${language} must be nested under "${parentPath}" because its parent route is localised there.`);
|
|
114
|
+
return normaliseRoutePath(fullPath.slice(parentPath.length));
|
|
115
|
+
});
|
|
116
|
+
return Array.from(new Set(paths.filter(Boolean)));
|
|
117
|
+
};
|
|
118
|
+
const transformLocalisedRoute = (route, parentCanonicalPath, parentLocalisedPaths, languages, localisedUrls)=>{
|
|
119
|
+
const canonicalPath = joinPath(parentCanonicalPath, route.path);
|
|
120
|
+
const localisedUrlEntry = isLocalisableRoutePath(route.path) ? ensureLocalisedUrlsForPath(canonicalPath, languages, localisedUrls) : void 0;
|
|
121
|
+
const routeLocalisedPaths = localisedUrlEntry ? languages.reduce((acc, language)=>{
|
|
122
|
+
acc[language] = normalisePathPattern(localisedUrlEntry[language]);
|
|
123
|
+
return acc;
|
|
124
|
+
}, {}) : parentLocalisedPaths;
|
|
125
|
+
const children = 'children' in route && route.children ? route.children.flatMap((child)=>transformLocalisedRoute(child, canonicalPath, routeLocalisedPaths, languages, localisedUrls)) : void 0;
|
|
126
|
+
const baseRoute = {
|
|
127
|
+
...route,
|
|
128
|
+
...children ? {
|
|
129
|
+
children
|
|
130
|
+
} : {}
|
|
131
|
+
};
|
|
132
|
+
if (!localisedUrlEntry) return [
|
|
133
|
+
baseRoute
|
|
134
|
+
];
|
|
135
|
+
return getLocalisedRoutePaths(canonicalPath, parentLocalisedPaths, languages, localisedUrlEntry).map((localisedPath, index)=>cloneRouteWithLocalisedPath(baseRoute, localisedPath, index));
|
|
136
|
+
};
|
|
137
|
+
const legalRouteIdPart = (value)=>value.replace(/[^a-zA-Z0-9_$-]+/g, '_').replace(/^_+|_+$/g, '') || 'index';
|
|
138
|
+
const suffixRouteIds = (route, suffix)=>{
|
|
139
|
+
const children = 'children' in route && route.children ? route.children.map((child)=>suffixRouteIds(child, suffix)) : void 0;
|
|
140
|
+
return {
|
|
141
|
+
...route,
|
|
142
|
+
...route.id ? {
|
|
143
|
+
id: `${route.id}__localised_${suffix}`
|
|
144
|
+
} : {},
|
|
145
|
+
...children ? {
|
|
146
|
+
children
|
|
147
|
+
} : {}
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
const cloneRouteWithLocalisedPath = (route, path, index)=>{
|
|
151
|
+
const leadingLocaleParam = getLeadingLocaleParam(route.path);
|
|
152
|
+
const localisedPath = leadingLocaleParam ? normaliseRoutePath(`${leadingLocaleParam}/${path}`) : path;
|
|
153
|
+
const routeWithPath = {
|
|
154
|
+
...route,
|
|
155
|
+
path: localisedPath
|
|
156
|
+
};
|
|
157
|
+
return 0 === index ? routeWithPath : suffixRouteIds(routeWithPath, legalRouteIdPart(localisedPath));
|
|
158
|
+
};
|
|
159
|
+
const applyLocalisedUrlsToRoutes = (routes, languages, localisedUrls)=>{
|
|
160
|
+
const rootLocalisedPaths = languages.reduce((acc, language)=>{
|
|
161
|
+
acc[language] = '/';
|
|
162
|
+
return acc;
|
|
163
|
+
}, {});
|
|
164
|
+
validateLocalisedUrls(routes, languages, localisedUrls);
|
|
165
|
+
return routes.flatMap((route)=>transformLocalisedRoute(route, '', rootLocalisedPaths, languages, localisedUrls));
|
|
166
|
+
};
|
|
167
|
+
const escapeRegExp = (value)=>value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
168
|
+
const getParamName = (segment)=>segment.slice(1).replace(/\?$/, '');
|
|
169
|
+
const compilePathPattern = (pattern)=>{
|
|
170
|
+
const names = [];
|
|
171
|
+
const segments = normalisePathPattern(pattern).split('/').filter(Boolean);
|
|
172
|
+
const source = segments.map((segment)=>{
|
|
173
|
+
if (segment.startsWith(':')) {
|
|
174
|
+
names.push(getParamName(segment));
|
|
175
|
+
const paramPattern = '([^/]+)';
|
|
176
|
+
return segment.endsWith('?') ? `(?:/${paramPattern})?` : `/${paramPattern}`;
|
|
177
|
+
}
|
|
178
|
+
if ('*' === segment) {
|
|
179
|
+
names.push('*');
|
|
180
|
+
return '/(.*)';
|
|
181
|
+
}
|
|
182
|
+
return `/${escapeRegExp(segment)}`;
|
|
183
|
+
}).join('');
|
|
184
|
+
return {
|
|
185
|
+
names,
|
|
186
|
+
regexp: new RegExp(`^${source || '/'}$`)
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
const matchPathPattern = (pathname, pattern)=>{
|
|
190
|
+
const { names, regexp } = compilePathPattern(pattern);
|
|
191
|
+
const match = regexp.exec(normalisePathPattern(pathname));
|
|
192
|
+
if (!match) return null;
|
|
193
|
+
return names.reduce((params, name, index)=>{
|
|
194
|
+
params[name] = decodeURIComponent(match[index + 1] || '');
|
|
195
|
+
return params;
|
|
196
|
+
}, {});
|
|
197
|
+
};
|
|
198
|
+
const buildPathFromPattern = (pattern, params)=>{
|
|
199
|
+
const segments = normalisePathPattern(pattern).split('/').filter(Boolean);
|
|
200
|
+
const path = segments.map((segment)=>{
|
|
201
|
+
if (segment.startsWith(':')) {
|
|
202
|
+
const param = params[getParamName(segment)];
|
|
203
|
+
return param ? encodeURIComponent(param) : '';
|
|
204
|
+
}
|
|
205
|
+
if ('*' === segment) return params['*'] || '';
|
|
206
|
+
return segment;
|
|
207
|
+
}).filter(Boolean).join('/');
|
|
208
|
+
return `/${path}`;
|
|
209
|
+
};
|
|
210
|
+
const resolveLocalisedPath = (pathname, targetLanguage, languages, localisedUrls)=>{
|
|
211
|
+
const normalizedPathname = normalisePathPattern(pathname);
|
|
212
|
+
for (const localisedUrlEntry of Object.values(localisedUrls)){
|
|
213
|
+
const targetPattern = localisedUrlEntry[targetLanguage];
|
|
214
|
+
if (targetPattern) for (const language of languages){
|
|
215
|
+
const sourcePattern = localisedUrlEntry[language];
|
|
216
|
+
if (!sourcePattern) continue;
|
|
217
|
+
const params = matchPathPattern(normalizedPathname, sourcePattern);
|
|
218
|
+
if (params) return buildPathFromPattern(targetPattern, params);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return normalizedPathname;
|
|
222
|
+
};
|
|
223
|
+
exports.applyLocalisedUrlsToRoutes = __webpack_exports__.applyLocalisedUrlsToRoutes;
|
|
224
|
+
exports.normalisePathPattern = __webpack_exports__.normalisePathPattern;
|
|
225
|
+
exports.resolveLocalisedPath = __webpack_exports__.resolveLocalisedPath;
|
|
226
|
+
exports.resolveLocalisedUrlsConfig = __webpack_exports__.resolveLocalisedUrlsConfig;
|
|
227
|
+
exports.validateLocalisedUrls = __webpack_exports__.validateLocalisedUrls;
|
|
228
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
229
|
+
"applyLocalisedUrlsToRoutes",
|
|
230
|
+
"normalisePathPattern",
|
|
231
|
+
"resolveLocalisedPath",
|
|
232
|
+
"resolveLocalisedUrlsConfig",
|
|
233
|
+
"validateLocalisedUrls"
|
|
234
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
235
|
+
Object.defineProperty(exports, '__esModule', {
|
|
236
|
+
value: true
|
|
237
|
+
});
|
package/dist/esm/cli/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getPublicDirRoutePrefixes } from "@modern-js/server-core";
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import path from "path";
|
|
4
|
+
import { applyLocalisedUrlsToRoutes, resolveLocalisedUrlsConfig } from "../shared/localisedUrls.mjs";
|
|
4
5
|
import { getBackendOptions, getLocaleDetectionOptions } from "../shared/utils.mjs";
|
|
5
6
|
function hasJsonFiles(dirPath) {
|
|
6
7
|
try {
|
|
@@ -80,6 +81,27 @@ const i18nPlugin = (options = {})=>({
|
|
|
80
81
|
plugins
|
|
81
82
|
};
|
|
82
83
|
});
|
|
84
|
+
api.modifyFileSystemRoutes(({ entrypoint, routes })=>{
|
|
85
|
+
if (!localeDetection) return {
|
|
86
|
+
entrypoint,
|
|
87
|
+
routes
|
|
88
|
+
};
|
|
89
|
+
const localeDetectionOptions = getLocaleDetectionOptions(entrypoint.entryName, localeDetection);
|
|
90
|
+
const { localePathRedirect, languages = [], localisedUrls } = localeDetectionOptions;
|
|
91
|
+
if (!localePathRedirect || 0 === languages.length) return {
|
|
92
|
+
entrypoint,
|
|
93
|
+
routes
|
|
94
|
+
};
|
|
95
|
+
const localisedUrlsConfig = resolveLocalisedUrlsConfig(localisedUrls);
|
|
96
|
+
if (!localisedUrlsConfig.enabled) return {
|
|
97
|
+
entrypoint,
|
|
98
|
+
routes
|
|
99
|
+
};
|
|
100
|
+
return {
|
|
101
|
+
entrypoint,
|
|
102
|
+
routes: applyLocalisedUrlsToRoutes(routes, languages, localisedUrlsConfig.map)
|
|
103
|
+
};
|
|
104
|
+
});
|
|
83
105
|
api._internalServerPlugins(({ plugins })=>{
|
|
84
106
|
const { serverRoutes, metaName } = api.getAppContext();
|
|
85
107
|
const normalizedConfig = api.getNormalizedConfig();
|
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Link as router_Link, useInRouterContext, useParams } from "@modern-js/runtime/router";
|
|
3
2
|
import { useModernI18n } from "./context.mjs";
|
|
3
|
+
import { useI18nRouterAdapter } from "./routerAdapter.mjs";
|
|
4
4
|
import { buildLocalizedUrl } from "./utils.mjs";
|
|
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
5
|
const I18nLink = ({ to, children, ...props })=>{
|
|
14
|
-
const { Link, params, hasRouter } =
|
|
15
|
-
const { language, supportedLanguages } = useModernI18n();
|
|
6
|
+
const { Link, params, hasRouter } = useI18nRouterAdapter();
|
|
7
|
+
const { language, supportedLanguages, localisedUrls } = useModernI18n();
|
|
16
8
|
const currentLang = language;
|
|
17
|
-
const localizedTo = buildLocalizedUrl(to, currentLang, supportedLanguages);
|
|
9
|
+
const localizedTo = buildLocalizedUrl(to, currentLang, supportedLanguages, localisedUrls);
|
|
18
10
|
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
11
|
if (!hasRouter || !Link) return /*#__PURE__*/ jsx("a", {
|
|
20
12
|
href: localizedTo,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { isBrowser } from "@modern-js/runtime";
|
|
3
|
-
import { createContext, useCallback, useContext, useMemo } from "react";
|
|
3
|
+
import { createContext, useCallback, useContext, useEffect, useMemo } from "react";
|
|
4
4
|
import { cacheUserLanguage } from "./i18n/detection/index.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { useI18nRouterAdapter } from "./routerAdapter.mjs";
|
|
6
|
+
import { buildLocalizedUrl, detectLanguageFromPath, getEntryPath, shouldIgnoreRedirect } from "./utils.mjs";
|
|
6
7
|
const ModernI18nContext = /*#__PURE__*/ createContext(null);
|
|
7
8
|
const ModernI18nProvider = ({ children, value })=>/*#__PURE__*/ jsx(ModernI18nContext.Provider, {
|
|
8
9
|
value: value,
|
|
@@ -11,9 +12,33 @@ const ModernI18nProvider = ({ children, value })=>/*#__PURE__*/ jsx(ModernI18nCo
|
|
|
11
12
|
const useModernI18n = ()=>{
|
|
12
13
|
const context = useContext(ModernI18nContext);
|
|
13
14
|
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 } =
|
|
16
|
-
const
|
|
15
|
+
const { language: contextLanguage, i18nInstance, languages, localePathRedirect, ignoreRedirectRoutes, localisedUrls, updateLanguage } = context;
|
|
16
|
+
const { navigate, location, hasRouter } = useI18nRouterAdapter();
|
|
17
|
+
const pathLanguage = useMemo(()=>{
|
|
18
|
+
if (!localePathRedirect || !location?.pathname) return;
|
|
19
|
+
const detected = detectLanguageFromPath(location.pathname, languages || [], localePathRedirect);
|
|
20
|
+
return detected.detected ? detected.language : void 0;
|
|
21
|
+
}, [
|
|
22
|
+
languages,
|
|
23
|
+
localePathRedirect,
|
|
24
|
+
location?.pathname
|
|
25
|
+
]);
|
|
26
|
+
const currentLanguage = pathLanguage || contextLanguage;
|
|
27
|
+
useEffect(()=>{
|
|
28
|
+
if (!pathLanguage || pathLanguage === contextLanguage) return;
|
|
29
|
+
updateLanguage?.(pathLanguage);
|
|
30
|
+
i18nInstance?.setLang?.(pathLanguage);
|
|
31
|
+
i18nInstance?.changeLanguage?.(pathLanguage);
|
|
32
|
+
if (isBrowser()) {
|
|
33
|
+
const detectionOptions = i18nInstance.options?.detection;
|
|
34
|
+
cacheUserLanguage(i18nInstance, pathLanguage, detectionOptions);
|
|
35
|
+
}
|
|
36
|
+
}, [
|
|
37
|
+
contextLanguage,
|
|
38
|
+
i18nInstance,
|
|
39
|
+
pathLanguage,
|
|
40
|
+
updateLanguage
|
|
41
|
+
]);
|
|
17
42
|
const changeLanguage = useCallback(async (newLang)=>{
|
|
18
43
|
try {
|
|
19
44
|
if (!newLang || 'string' != typeof newLang) throw new Error('Language must be a non-empty string');
|
|
@@ -30,7 +55,7 @@ const useModernI18n = ()=>{
|
|
|
30
55
|
const pathLanguage = detectLanguageFromPath(currentPath, languages || [], localePathRedirect);
|
|
31
56
|
if (pathLanguage.detected && pathLanguage.language === newLang) return;
|
|
32
57
|
if (!shouldIgnoreRedirect(relativePath, languages || [], ignoreRedirectRoutes)) {
|
|
33
|
-
const newPath = buildLocalizedUrl(relativePath, newLang, languages || []);
|
|
58
|
+
const newPath = buildLocalizedUrl(relativePath, newLang, languages || [], localisedUrls);
|
|
34
59
|
const newUrl = entryPath + newPath + location.search + location.hash;
|
|
35
60
|
await navigate(newUrl, {
|
|
36
61
|
replace: true
|
|
@@ -43,7 +68,7 @@ const useModernI18n = ()=>{
|
|
|
43
68
|
const pathLanguage = detectLanguageFromPath(currentPath, languages || [], localePathRedirect);
|
|
44
69
|
if (pathLanguage.detected && pathLanguage.language === newLang) return;
|
|
45
70
|
if (!shouldIgnoreRedirect(relativePath, languages || [], ignoreRedirectRoutes)) {
|
|
46
|
-
const newPath = buildLocalizedUrl(relativePath, newLang, languages || []);
|
|
71
|
+
const newPath = buildLocalizedUrl(relativePath, newLang, languages || [], localisedUrls);
|
|
47
72
|
const newUrl = entryPath + newPath + window.location.search + window.location.hash;
|
|
48
73
|
window.history.pushState(null, '', newUrl);
|
|
49
74
|
}
|
|
@@ -58,6 +83,7 @@ const useModernI18n = ()=>{
|
|
|
58
83
|
updateLanguage,
|
|
59
84
|
localePathRedirect,
|
|
60
85
|
ignoreRedirectRoutes,
|
|
86
|
+
localisedUrls,
|
|
61
87
|
languages,
|
|
62
88
|
hasRouter,
|
|
63
89
|
navigate,
|
|
@@ -98,6 +124,7 @@ const useModernI18n = ()=>{
|
|
|
98
124
|
changeLanguage,
|
|
99
125
|
i18nInstance,
|
|
100
126
|
supportedLanguages: languages || [],
|
|
127
|
+
localisedUrls,
|
|
101
128
|
isLanguageSupported,
|
|
102
129
|
isResourcesReady
|
|
103
130
|
};
|
|
@@ -2,7 +2,8 @@ import { isBrowser } from "@modern-js/runtime";
|
|
|
2
2
|
import { useEffect, useRef } from "react";
|
|
3
3
|
import { I18N_SDK_RESOURCES_LOADED_EVENT, getI18nSdkBackendId } from "./i18n/backend/sdk-event.mjs";
|
|
4
4
|
import { cacheUserLanguage } from "./i18n/detection/index.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { useI18nRouterAdapter } from "./routerAdapter.mjs";
|
|
6
|
+
import { buildLocalizedUrl, detectLanguageFromPath, getEntryPath, getPathname, shouldIgnoreRedirect } from "./utils.mjs";
|
|
6
7
|
function createMinimalI18nInstance(language) {
|
|
7
8
|
const minimalInstance = {
|
|
8
9
|
language,
|
|
@@ -14,7 +15,7 @@ function createMinimalI18nInstance(language) {
|
|
|
14
15
|
};
|
|
15
16
|
return minimalInstance;
|
|
16
17
|
}
|
|
17
|
-
function createContextValue(lang, i18nInstance, entryName, languages, localePathRedirect, ignoreRedirectRoutes, setLang) {
|
|
18
|
+
function createContextValue(lang, i18nInstance, entryName, languages, localePathRedirect, ignoreRedirectRoutes, localisedUrls, setLang) {
|
|
18
19
|
const instance = i18nInstance || createMinimalI18nInstance(lang);
|
|
19
20
|
return {
|
|
20
21
|
language: lang,
|
|
@@ -23,6 +24,7 @@ function createContextValue(lang, i18nInstance, entryName, languages, localePath
|
|
|
23
24
|
languages,
|
|
24
25
|
localePathRedirect,
|
|
25
26
|
ignoreRedirectRoutes,
|
|
27
|
+
localisedUrls,
|
|
26
28
|
updateLanguage: setLang
|
|
27
29
|
};
|
|
28
30
|
}
|
|
@@ -72,9 +74,9 @@ function useSdkResourcesLoader(i18nInstance, setForceUpdate) {
|
|
|
72
74
|
setForceUpdate
|
|
73
75
|
]);
|
|
74
76
|
}
|
|
75
|
-
function useClientSideRedirect(i18nInstance, localePathRedirect, languages, fallbackLanguage, ignoreRedirectRoutes) {
|
|
77
|
+
function useClientSideRedirect(i18nInstance, localePathRedirect, languages, fallbackLanguage, ignoreRedirectRoutes, localisedUrls) {
|
|
76
78
|
const hasRedirectedRef = useRef(false);
|
|
77
|
-
const { navigate, location, hasRouter } =
|
|
79
|
+
const { navigate, location, hasRouter } = useI18nRouterAdapter();
|
|
78
80
|
useEffect(()=>{
|
|
79
81
|
if ('browser' !== process.env.MODERN_TARGET) return;
|
|
80
82
|
if (!localePathRedirect || !i18nInstance) return;
|
|
@@ -93,7 +95,7 @@ function useClientSideRedirect(i18nInstance, localePathRedirect, languages, fall
|
|
|
93
95
|
const pathDetection = detectLanguageFromPath(currentPathname, languages, localePathRedirect);
|
|
94
96
|
if (pathDetection.detected) return;
|
|
95
97
|
const targetLanguage = i18nInstance.language || fallbackLanguage || languages[0] || 'en';
|
|
96
|
-
const newPath = buildLocalizedUrl(relativePath, targetLanguage, languages);
|
|
98
|
+
const newPath = buildLocalizedUrl(relativePath, targetLanguage, languages, localisedUrls);
|
|
97
99
|
const newUrl = entryPath + newPath + currentSearch + currentHash;
|
|
98
100
|
if (newUrl !== currentPathname + currentSearch + currentHash) {
|
|
99
101
|
hasRedirectedRef.current = true;
|
|
@@ -110,7 +112,8 @@ function useClientSideRedirect(i18nInstance, localePathRedirect, languages, fall
|
|
|
110
112
|
i18nInstance,
|
|
111
113
|
languages,
|
|
112
114
|
fallbackLanguage,
|
|
113
|
-
ignoreRedirectRoutes
|
|
115
|
+
ignoreRedirectRoutes,
|
|
116
|
+
localisedUrls
|
|
114
117
|
]);
|
|
115
118
|
}
|
|
116
119
|
function useLanguageSync(i18nInstance, localePathRedirect, languages, runtimeContextRef, prevLangRef, setLang) {
|
|
@@ -4,7 +4,7 @@ const DEFAULT_I18NEXT_BACKEND_OPTIONS = {
|
|
|
4
4
|
};
|
|
5
5
|
function convertPath(path) {
|
|
6
6
|
if (!path) return path;
|
|
7
|
-
if (path.startsWith('/')) return `${window.__assetPrefix__ || ''}${path}`;
|
|
7
|
+
if (path.startsWith('/')) return "u" < typeof window ? path : `${window.__assetPrefix__ || ''}${path}`;
|
|
8
8
|
return path;
|
|
9
9
|
}
|
|
10
10
|
function convertBackendOptions(options) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import cjs from "i18next-fs-backend/cjs";
|
|
2
2
|
import { useI18nextBackendCommon } from "./middleware.common.mjs";
|
|
3
|
-
class FsBackendWithSave extends
|
|
3
|
+
class FsBackendWithSave extends cjs {
|
|
4
4
|
save(_language, _namespace, _data) {}
|
|
5
5
|
}
|
|
6
6
|
const HttpBackendWithSave = FsBackendWithSave;
|
|
7
|
-
const useI18nextBackend = (i18nInstance, backend)=>useI18nextBackendCommon(i18nInstance, FsBackendWithSave,
|
|
7
|
+
const useI18nextBackend = (i18nInstance, backend)=>useI18nextBackendCommon(i18nInstance, FsBackendWithSave, cjs, backend);
|
|
8
8
|
export { FsBackendWithSave, HttpBackendWithSave, useI18nextBackend };
|
|
@@ -40,10 +40,12 @@ async function createI18nextInstance() {
|
|
|
40
40
|
return null;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
+
function getOptionalReactI18nextPackageName() {
|
|
44
|
+
return "react-i18next";
|
|
45
|
+
}
|
|
43
46
|
async function tryImportReactI18next() {
|
|
44
47
|
try {
|
|
45
|
-
|
|
46
|
-
return reactI18next;
|
|
48
|
+
return await import(getOptionalReactI18nextPackageName());
|
|
47
49
|
} catch (error) {
|
|
48
50
|
return null;
|
|
49
51
|
}
|