@bleedingdev/modern-js-plugin-i18n 3.9.0-ultramodern.7 → 3.9.0-ultramodern.9
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 +11 -0
- package/dist/cjs/cli/localisedRoutes.js +46 -0
- package/dist/cjs/runtime/core.js +4 -1
- package/dist/cjs/runtime/utils.js +6 -2
- package/dist/cjs/server/defaultUrlStrategy.js +48 -0
- package/dist/cjs/server/index.js +3 -2
- package/dist/cjs/server/mappedUrlStrategy.js +43 -0
- package/dist/cjs/server/redirectPolicy.js +13 -9
- package/dist/cjs/shared/mappedUrlStrategy.js +63 -0
- package/dist/cjs/shared/urlStrategy.js +31 -1
- package/dist/esm/cli/index.mjs +11 -0
- package/dist/esm/cli/localisedRoutes.mjs +8 -0
- package/dist/esm/runtime/core.mjs +4 -1
- package/dist/esm/runtime/utils.mjs +6 -2
- package/dist/esm/server/defaultUrlStrategy.mjs +7 -0
- package/dist/esm/server/index.mjs +3 -2
- package/dist/esm/server/mappedUrlStrategy.mjs +1 -0
- package/dist/esm/server/redirectPolicy.mjs +6 -2
- package/dist/esm/shared/mappedUrlStrategy.mjs +19 -0
- package/dist/esm/shared/urlStrategy.mjs +7 -0
- package/dist/esm-node/cli/index.mjs +11 -0
- package/dist/esm-node/cli/localisedRoutes.mjs +9 -0
- package/dist/esm-node/runtime/core.mjs +4 -1
- package/dist/esm-node/runtime/utils.mjs +6 -2
- package/dist/esm-node/server/defaultUrlStrategy.mjs +8 -0
- package/dist/esm-node/server/index.mjs +3 -2
- package/dist/esm-node/server/mappedUrlStrategy.mjs +2 -0
- package/dist/esm-node/server/redirectPolicy.mjs +6 -2
- package/dist/esm-node/shared/mappedUrlStrategy.mjs +20 -0
- package/dist/esm-node/shared/urlStrategy.mjs +7 -0
- package/dist/types/cli/localisedRoutes.d.ts +18 -0
- package/dist/types/runtime/core.d.ts +1 -1
- package/dist/types/runtime/utils.d.ts +1 -1
- package/dist/types/server/defaultUrlStrategy.d.ts +11 -0
- package/dist/types/server/index.d.ts +1 -1
- package/dist/types/server/mappedUrlStrategy.d.ts +7 -0
- package/dist/types/server/redirectPolicy.d.ts +1 -1
- package/dist/types/shared/mappedUrlStrategy.d.ts +27 -0
- package/dist/types/shared/type.d.ts +10 -0
- package/dist/types/shared/urlStrategy.d.ts +11 -0
- package/package.json +11 -10
- package/rstest.config.mts +4 -0
- package/src/cli/index.ts +42 -1
- package/src/cli/localisedRoutes.ts +43 -0
- package/src/runtime/core.tsx +18 -2
- package/src/runtime/utils.ts +13 -4
- package/src/server/defaultUrlStrategy.ts +27 -0
- package/src/server/index.ts +7 -12
- package/src/server/mappedUrlStrategy.ts +10 -0
- package/src/server/redirectPolicy.ts +19 -4
- package/src/shared/mappedUrlStrategy.ts +59 -0
- package/src/shared/type.ts +10 -0
- package/src/shared/urlStrategy.ts +27 -0
- package/tests/defaultUrlStrategy.test.ts +107 -0
- package/tests/localisedRouteGeneration.test.ts +159 -0
- package/tests/localisedUrls.test.ts +21 -4
- package/tests/mappedUrlStrategyDerivation.test.ts +62 -0
- package/tests/redirectPolicy.test.ts +53 -0
- package/tests/urlStrategySerialization.test.ts +122 -0
package/dist/cjs/cli/index.js
CHANGED
|
@@ -32,8 +32,11 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
32
32
|
i18nPlugin: ()=>i18nPlugin
|
|
33
33
|
});
|
|
34
34
|
const server_core_namespaceObject = require("@modern-js/server-core");
|
|
35
|
+
const utils_namespaceObject = require("@modern-js/utils");
|
|
36
|
+
const urlStrategy_js_namespaceObject = require("../shared/urlStrategy.js");
|
|
35
37
|
const utils_js_namespaceObject = require("../shared/utils.js");
|
|
36
38
|
const external_locales_js_namespaceObject = require("./locales.js");
|
|
39
|
+
const external_localisedRoutes_js_namespaceObject = require("./localisedRoutes.js");
|
|
37
40
|
require("../runtime/types.js");
|
|
38
41
|
const i18nPlugin = (options = {})=>({
|
|
39
42
|
name: '@modern-js/plugin-i18n',
|
|
@@ -63,6 +66,10 @@ const i18nPlugin = (options = {})=>({
|
|
|
63
66
|
backend: backendOptions,
|
|
64
67
|
...extendedConfig
|
|
65
68
|
};
|
|
69
|
+
if ((0, urlStrategy_js_namespaceObject.isUnusableI18nUrlStrategy)(config.urlStrategy)) {
|
|
70
|
+
delete config.urlStrategy;
|
|
71
|
+
utils_namespaceObject.logger.warn("[i18n] `urlStrategy` cannot be passed through `modern.config.ts`: the generated runtime registration carries plugin options as JSON, which drops its methods. Configure `localeDetection.localisedUrls` instead — the runtime and the server both derive the URL policy from it — or register a runtime plugin that supplies the strategy directly.");
|
|
72
|
+
}
|
|
66
73
|
const { reactI18next } = extendedConfig;
|
|
67
74
|
const runtimePluginPath = customPlugin?.runtime?.path || (false === reactI18next ? `@${metaName}/plugin-i18n/runtime/no-react-i18next` : `@${metaName}/plugin-i18n/runtime`);
|
|
68
75
|
plugins.push({
|
|
@@ -75,6 +82,10 @@ const i18nPlugin = (options = {})=>({
|
|
|
75
82
|
plugins
|
|
76
83
|
};
|
|
77
84
|
});
|
|
85
|
+
api.modifyFileSystemRoutes(({ entrypoint, routes })=>({
|
|
86
|
+
entrypoint,
|
|
87
|
+
routes: (0, external_localisedRoutes_js_namespaceObject.applyLocalisedRoutes)(routes, localeDetection ? (0, utils_js_namespaceObject.getLocaleDetectionOptions)(entrypoint.entryName, localeDetection) : void 0)
|
|
88
|
+
}));
|
|
78
89
|
api._internalServerPlugins(({ plugins })=>{
|
|
79
90
|
const { serverRoutes, metaName } = api.getAppContext();
|
|
80
91
|
const normalizedConfig = api.getNormalizedConfig();
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
|
+
var define = (defs, kind)=>{
|
|
6
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
[kind]: defs[key]
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
define(getters, "get");
|
|
12
|
+
define(values, "value");
|
|
13
|
+
};
|
|
14
|
+
})();
|
|
15
|
+
(()=>{
|
|
16
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
17
|
+
})();
|
|
18
|
+
(()=>{
|
|
19
|
+
__webpack_require__.r = (exports1)=>{
|
|
20
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
21
|
+
value: 'Module'
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
24
|
+
value: true
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
})();
|
|
28
|
+
var __webpack_exports__ = {};
|
|
29
|
+
__webpack_require__.r(__webpack_exports__);
|
|
30
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
31
|
+
applyLocalisedRoutes: ()=>applyLocalisedRoutes
|
|
32
|
+
});
|
|
33
|
+
const i18n_runtime_extensions_namespaceObject = require("@modern-js/i18n-runtime-extensions");
|
|
34
|
+
const applyLocalisedRoutes = (routes, localeDetection)=>{
|
|
35
|
+
const { localePathRedirect, languages = [], localisedUrls } = localeDetection ?? {};
|
|
36
|
+
const resolved = (0, i18n_runtime_extensions_namespaceObject.resolveLocalisedUrlsConfig)(localisedUrls);
|
|
37
|
+
if (!localePathRedirect || !languages.length || !resolved.enabled) return routes;
|
|
38
|
+
return (0, i18n_runtime_extensions_namespaceObject.applyLocalisedUrlsToRoutes)(routes, languages, resolved.map);
|
|
39
|
+
};
|
|
40
|
+
exports.applyLocalisedRoutes = __webpack_exports__.applyLocalisedRoutes;
|
|
41
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
42
|
+
"applyLocalisedRoutes"
|
|
43
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
44
|
+
Object.defineProperty(exports, '__esModule', {
|
|
45
|
+
value: true
|
|
46
|
+
});
|
package/dist/cjs/runtime/core.js
CHANGED
|
@@ -40,6 +40,8 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
40
40
|
useModernI18n: ()=>external_context_js_namespaceObject.useModernI18n,
|
|
41
41
|
useNativeI18nRouterAdapter: ()=>external_routerAdapter_js_namespaceObject.useNativeI18nRouterAdapter
|
|
42
42
|
});
|
|
43
|
+
const mappedUrlStrategy_js_namespaceObject = require("../shared/mappedUrlStrategy.js");
|
|
44
|
+
const urlStrategy_js_namespaceObject = require("../shared/urlStrategy.js");
|
|
43
45
|
const external_pluginSetup_js_namespaceObject = require("./pluginSetup.js");
|
|
44
46
|
const external_providerComposition_js_namespaceObject = require("./providerComposition.js");
|
|
45
47
|
const external_reactI18next_js_namespaceObject = require("./reactI18next.js");
|
|
@@ -56,6 +58,7 @@ const createI18nPlugin = (loadReactI18nextIntegration)=>(options)=>({
|
|
|
56
58
|
const { entryName, i18nInstance: userI18nInstance, initOptions, localeDetection, backend, htmlLangAttr = false, reactI18next = true, urlStrategy, NavigationProvider, LanguageSynchronization } = options;
|
|
57
59
|
const { localePathRedirect = false, i18nextDetector = true, languages = [], fallbackLanguage = 'en', detection, ignoreRedirectRoutes } = localeDetection || {};
|
|
58
60
|
const { enabled: backendEnabled = false } = backend || {};
|
|
61
|
+
const resolvedUrlStrategy = (0, urlStrategy_js_namespaceObject.asI18nUrlStrategy)(urlStrategy) ?? (0, mappedUrlStrategy_js_namespaceObject.resolveMappedUrlStrategy)(localeDetection);
|
|
59
62
|
let latestI18nInstance;
|
|
60
63
|
let I18nextProvider;
|
|
61
64
|
api.onBeforeRender(async (context)=>{
|
|
@@ -83,7 +86,7 @@ const createI18nPlugin = (loadReactI18nextIntegration)=>(options)=>({
|
|
|
83
86
|
languages,
|
|
84
87
|
fallbackLanguage,
|
|
85
88
|
ignoreRedirectRoutes,
|
|
86
|
-
urlStrategy,
|
|
89
|
+
urlStrategy: resolvedUrlStrategy,
|
|
87
90
|
NavigationProvider,
|
|
88
91
|
LanguageSynchronization,
|
|
89
92
|
getLatestI18nInstance: ()=>latestI18nInstance,
|
|
@@ -39,6 +39,8 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
39
39
|
const runtime_namespaceObject = require("@modern-js/runtime");
|
|
40
40
|
const context_namespaceObject = require("@modern-js/runtime/context");
|
|
41
41
|
const url_namespaceObject = require("@modern-js/runtime-utils/url");
|
|
42
|
+
const mappedUrlStrategy_js_namespaceObject = require("../shared/mappedUrlStrategy.js");
|
|
43
|
+
const urlStrategy_js_namespaceObject = require("../shared/urlStrategy.js");
|
|
42
44
|
const getPathname = (context)=>{
|
|
43
45
|
if ((0, runtime_namespaceObject.isBrowser)()) return window.location.pathname;
|
|
44
46
|
return context.ssrContext?.request?.pathname || '/';
|
|
@@ -56,7 +58,8 @@ const getLanguageFromPath = (pathname, languages, fallbackLanguage)=>{
|
|
|
56
58
|
};
|
|
57
59
|
const buildLocalizedUrl = (target, language, languages, urlStrategy)=>{
|
|
58
60
|
const { pathname, search, hash } = (0, url_namespaceObject.splitUrlTarget)(target);
|
|
59
|
-
|
|
61
|
+
const strategy = (0, urlStrategy_js_namespaceObject.asI18nUrlStrategy)(urlStrategy);
|
|
62
|
+
if (strategy) return `${strategy.localizePathname(pathname, language, languages)}${search}${hash}`;
|
|
60
63
|
const segments = pathname.split('/').filter(Boolean);
|
|
61
64
|
if (segments[0] && languages.some((item)=>item.toLowerCase() === segments[0].toLowerCase())) segments[0] = language;
|
|
62
65
|
else segments.unshift(language);
|
|
@@ -81,7 +84,8 @@ const detectLanguageFromPath = (pathname, languages, localePathRedirect)=>{
|
|
|
81
84
|
};
|
|
82
85
|
};
|
|
83
86
|
const shouldIgnoreRedirect = (pathname, languages, ignoreRedirectRoutes, urlStrategy)=>{
|
|
84
|
-
if (
|
|
87
|
+
if ((0, mappedUrlStrategy_js_namespaceObject.isDefaultLocaleRedirectSkipPath)(pathname, languages)) return true;
|
|
88
|
+
if ((0, urlStrategy_js_namespaceObject.asI18nUrlStrategy)(urlStrategy)?.shouldSkipRedirect?.(pathname, languages)) return true;
|
|
85
89
|
if (!ignoreRedirectRoutes) return false;
|
|
86
90
|
const segments = pathname.split('/').filter(Boolean);
|
|
87
91
|
if (segments[0] && languages.some((item)=>item.toLowerCase() === segments[0].toLowerCase())) segments.shift();
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
|
+
var define = (defs, kind)=>{
|
|
6
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
[kind]: defs[key]
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
define(getters, "get");
|
|
12
|
+
define(values, "value");
|
|
13
|
+
};
|
|
14
|
+
})();
|
|
15
|
+
(()=>{
|
|
16
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
17
|
+
})();
|
|
18
|
+
(()=>{
|
|
19
|
+
__webpack_require__.r = (exports1)=>{
|
|
20
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
21
|
+
value: 'Module'
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
24
|
+
value: true
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
})();
|
|
28
|
+
var __webpack_exports__ = {};
|
|
29
|
+
__webpack_require__.r(__webpack_exports__);
|
|
30
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
31
|
+
createDefaultUrlStrategy: ()=>createDefaultUrlStrategy,
|
|
32
|
+
resolveDefaultUrlStrategy: ()=>resolveDefaultUrlStrategy
|
|
33
|
+
});
|
|
34
|
+
const external_mappedUrlStrategy_js_namespaceObject = require("./mappedUrlStrategy.js");
|
|
35
|
+
const createDefaultUrlStrategy = (localisedUrls)=>(0, external_mappedUrlStrategy_js_namespaceObject.createMappedUrlStrategy)(localisedUrls);
|
|
36
|
+
const resolveDefaultUrlStrategy = (localisedUrls)=>{
|
|
37
|
+
if (!localisedUrls || 'object' != typeof localisedUrls || 0 === Object.keys(localisedUrls).length) return;
|
|
38
|
+
return createDefaultUrlStrategy(localisedUrls);
|
|
39
|
+
};
|
|
40
|
+
exports.createDefaultUrlStrategy = __webpack_exports__.createDefaultUrlStrategy;
|
|
41
|
+
exports.resolveDefaultUrlStrategy = __webpack_exports__.resolveDefaultUrlStrategy;
|
|
42
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
43
|
+
"createDefaultUrlStrategy",
|
|
44
|
+
"resolveDefaultUrlStrategy"
|
|
45
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
46
|
+
Object.defineProperty(exports, '__esModule', {
|
|
47
|
+
value: true
|
|
48
|
+
});
|
package/dist/cjs/server/index.js
CHANGED
|
@@ -34,8 +34,10 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
34
34
|
matchesApiPrefix: ()=>external_apiPrefix_js_namespaceObject.matchesApiPrefix
|
|
35
35
|
});
|
|
36
36
|
const hono_namespaceObject = require("@modern-js/server-core/hono");
|
|
37
|
+
const urlStrategy_js_namespaceObject = require("../shared/urlStrategy.js");
|
|
37
38
|
const utils_js_namespaceObject = require("../shared/utils.js");
|
|
38
39
|
const external_apiPrefix_js_namespaceObject = require("./apiPrefix.js");
|
|
40
|
+
const external_defaultUrlStrategy_js_namespaceObject = require("./defaultUrlStrategy.js");
|
|
39
41
|
const external_detectorOptions_js_namespaceObject = require("./detectorOptions.js");
|
|
40
42
|
const external_redirectPolicy_js_namespaceObject = require("./redirectPolicy.js");
|
|
41
43
|
const { languageDetector } = hono_namespaceObject;
|
|
@@ -61,8 +63,7 @@ const i18nServerPlugin = (options)=>({
|
|
|
61
63
|
const entryLocaleDetection = (0, utils_js_namespaceObject.getLocaleDetectionOptions)(entryName, options.localeDetection);
|
|
62
64
|
const { localePathRedirect, i18nextDetector = true, languages = [], fallbackLanguage = 'en', detection, ignoreRedirectRoutes } = entryLocaleDetection;
|
|
63
65
|
const legacyLocalisedUrls = Reflect.get(entryLocaleDetection, 'localisedUrls');
|
|
64
|
-
const urlStrategy = options.resolveUrlStrategy?.(entryName);
|
|
65
|
-
if (legacyLocalisedUrls && 'object' == typeof legacyLocalisedUrls && Object.keys(legacyLocalisedUrls).length > 0 && !urlStrategy) throw new Error('Mapped locale URLs require a URL strategy. Use the i18n integration server plugin or supply resolveUrlStrategy.');
|
|
66
|
+
const urlStrategy = (0, urlStrategy_js_namespaceObject.asI18nUrlStrategy)(options.resolveUrlStrategy?.(entryName)) ?? (0, external_defaultUrlStrategy_js_namespaceObject.resolveDefaultUrlStrategy)(legacyLocalisedUrls);
|
|
66
67
|
const staticRoutePrefixes = options.staticRoutePrefixes;
|
|
67
68
|
const originUrlPath = route.urlPath;
|
|
68
69
|
const urlPath = originUrlPath.endsWith('/') ? `${originUrlPath}*` : `${originUrlPath}/*`;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
|
+
var define = (defs, kind)=>{
|
|
6
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
[kind]: defs[key]
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
define(getters, "get");
|
|
12
|
+
define(values, "value");
|
|
13
|
+
};
|
|
14
|
+
})();
|
|
15
|
+
(()=>{
|
|
16
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
17
|
+
})();
|
|
18
|
+
(()=>{
|
|
19
|
+
__webpack_require__.r = (exports1)=>{
|
|
20
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
21
|
+
value: 'Module'
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
24
|
+
value: true
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
})();
|
|
28
|
+
var __webpack_exports__ = {};
|
|
29
|
+
__webpack_require__.r(__webpack_exports__);
|
|
30
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
31
|
+
createMappedUrlStrategy: ()=>mappedUrlStrategy_js_namespaceObject.createMappedUrlStrategy,
|
|
32
|
+
resolveMappedUrlStrategy: ()=>mappedUrlStrategy_js_namespaceObject.resolveMappedUrlStrategy
|
|
33
|
+
});
|
|
34
|
+
const mappedUrlStrategy_js_namespaceObject = require("../shared/mappedUrlStrategy.js");
|
|
35
|
+
exports.createMappedUrlStrategy = __webpack_exports__.createMappedUrlStrategy;
|
|
36
|
+
exports.resolveMappedUrlStrategy = __webpack_exports__.resolveMappedUrlStrategy;
|
|
37
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
38
|
+
"createMappedUrlStrategy",
|
|
39
|
+
"resolveMappedUrlStrategy"
|
|
40
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
41
|
+
Object.defineProperty(exports, '__esModule', {
|
|
42
|
+
value: true
|
|
43
|
+
});
|
|
@@ -27,6 +27,15 @@ var __webpack_require__ = {};
|
|
|
27
27
|
})();
|
|
28
28
|
var __webpack_exports__ = {};
|
|
29
29
|
__webpack_require__.r(__webpack_exports__);
|
|
30
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
31
|
+
buildLocalizedUrl: ()=>buildLocalizedUrl,
|
|
32
|
+
createLocaleRedirectResponse: ()=>createLocaleRedirectResponse,
|
|
33
|
+
getLanguageFromPath: ()=>getLanguageFromPath,
|
|
34
|
+
isStaticResourceRequest: ()=>isStaticResourceRequest,
|
|
35
|
+
shouldIgnoreRedirect: ()=>shouldIgnoreRedirect
|
|
36
|
+
});
|
|
37
|
+
const mappedUrlStrategy_js_namespaceObject = require("../shared/mappedUrlStrategy.js");
|
|
38
|
+
const urlStrategy_js_namespaceObject = require("../shared/urlStrategy.js");
|
|
30
39
|
const stripUrlPathPrefix = (pathname, urlPath)=>{
|
|
31
40
|
const basePath = urlPath.replace('/*', '');
|
|
32
41
|
if (!basePath || '/' === basePath) return pathname;
|
|
@@ -34,7 +43,8 @@ const stripUrlPathPrefix = (pathname, urlPath)=>{
|
|
|
34
43
|
};
|
|
35
44
|
const shouldIgnoreRedirect = (pathname, urlPath, ignoreRedirectRoutes, urlStrategy, languages = [])=>{
|
|
36
45
|
const remainingPath = stripUrlPathPrefix(pathname, urlPath);
|
|
37
|
-
if (
|
|
46
|
+
if ((0, mappedUrlStrategy_js_namespaceObject.isDefaultLocaleRedirectSkipPath)(remainingPath, languages)) return true;
|
|
47
|
+
if ((0, urlStrategy_js_namespaceObject.asI18nUrlStrategy)(urlStrategy)?.shouldSkipRedirect?.(remainingPath, languages)) return true;
|
|
38
48
|
if (!ignoreRedirectRoutes) return false;
|
|
39
49
|
const segments = remainingPath.split('/').filter(Boolean);
|
|
40
50
|
if (segments[0] && languages.some((language)=>language.toLowerCase() === segments[0].toLowerCase())) segments.shift();
|
|
@@ -66,7 +76,8 @@ const buildLocalizedUrl = (req, urlPath, language, languages, urlStrategy)=>{
|
|
|
66
76
|
const segments = remainingPath.split('/').filter(Boolean);
|
|
67
77
|
if (segments[0] && languages.some((item)=>item.toLowerCase() === segments[0].toLowerCase())) segments[0] = language;
|
|
68
78
|
else segments.unshift(language);
|
|
69
|
-
const
|
|
79
|
+
const strategy = (0, urlStrategy_js_namespaceObject.asI18nUrlStrategy)(urlStrategy);
|
|
80
|
+
const pathname = strategy ? strategy.localizePathname(remainingPath, language, languages) : `/${segments.join('/')}`;
|
|
70
81
|
return `${'/' === basePath ? '' : basePath}${pathname}${url.search}${url.hash}`;
|
|
71
82
|
};
|
|
72
83
|
const createLocaleRedirectResponse = (location)=>new Response(null, {
|
|
@@ -77,13 +88,6 @@ const createLocaleRedirectResponse = (location)=>new Response(null, {
|
|
|
77
88
|
Vary: 'Accept-Language, Cookie'
|
|
78
89
|
}
|
|
79
90
|
});
|
|
80
|
-
__webpack_require__.d(__webpack_exports__, {}, {
|
|
81
|
-
buildLocalizedUrl: buildLocalizedUrl,
|
|
82
|
-
createLocaleRedirectResponse: createLocaleRedirectResponse,
|
|
83
|
-
getLanguageFromPath: getLanguageFromPath,
|
|
84
|
-
isStaticResourceRequest: isStaticResourceRequest,
|
|
85
|
-
shouldIgnoreRedirect: shouldIgnoreRedirect
|
|
86
|
-
});
|
|
87
91
|
exports.buildLocalizedUrl = __webpack_exports__.buildLocalizedUrl;
|
|
88
92
|
exports.createLocaleRedirectResponse = __webpack_exports__.createLocaleRedirectResponse;
|
|
89
93
|
exports.getLanguageFromPath = __webpack_exports__.getLanguageFromPath;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
|
+
var define = (defs, kind)=>{
|
|
6
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
[kind]: defs[key]
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
define(getters, "get");
|
|
12
|
+
define(values, "value");
|
|
13
|
+
};
|
|
14
|
+
})();
|
|
15
|
+
(()=>{
|
|
16
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
17
|
+
})();
|
|
18
|
+
(()=>{
|
|
19
|
+
__webpack_require__.r = (exports1)=>{
|
|
20
|
+
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
21
|
+
value: 'Module'
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
24
|
+
value: true
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
})();
|
|
28
|
+
var __webpack_exports__ = {};
|
|
29
|
+
__webpack_require__.r(__webpack_exports__);
|
|
30
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
31
|
+
createMappedUrlStrategy: ()=>createMappedUrlStrategy,
|
|
32
|
+
isDefaultLocaleRedirectSkipPath: ()=>i18n_runtime_extensions_namespaceObject.isDefaultLocaleRedirectSkipPath,
|
|
33
|
+
resolveMappedUrlStrategy: ()=>resolveMappedUrlStrategy
|
|
34
|
+
});
|
|
35
|
+
const i18n_runtime_extensions_namespaceObject = require("@modern-js/i18n-runtime-extensions");
|
|
36
|
+
const createMappedUrlStrategy = (localisedUrls)=>({
|
|
37
|
+
localizePathname: (pathname, language, languages)=>(0, i18n_runtime_extensions_namespaceObject.localiseTargetPathname)(pathname, language, [
|
|
38
|
+
...languages
|
|
39
|
+
], localisedUrls),
|
|
40
|
+
canonicalPathname: (pathname, languages)=>(0, i18n_runtime_extensions_namespaceObject.canonicalTargetPathname)(pathname, [
|
|
41
|
+
...languages
|
|
42
|
+
], localisedUrls),
|
|
43
|
+
shouldSkipRedirect: (pathname, languages)=>(0, i18n_runtime_extensions_namespaceObject.isDefaultLocaleRedirectSkipPath)(pathname, [
|
|
44
|
+
...languages
|
|
45
|
+
])
|
|
46
|
+
});
|
|
47
|
+
const resolveMappedUrlStrategy = (localeDetection)=>{
|
|
48
|
+
if (!localeDetection || 'object' != typeof localeDetection) return;
|
|
49
|
+
const localisedUrls = Reflect.get(localeDetection, 'localisedUrls');
|
|
50
|
+
if (!localisedUrls || 'object' != typeof localisedUrls || 0 === Object.keys(localisedUrls).length) return;
|
|
51
|
+
return createMappedUrlStrategy(localisedUrls);
|
|
52
|
+
};
|
|
53
|
+
exports.createMappedUrlStrategy = __webpack_exports__.createMappedUrlStrategy;
|
|
54
|
+
exports.isDefaultLocaleRedirectSkipPath = __webpack_exports__.isDefaultLocaleRedirectSkipPath;
|
|
55
|
+
exports.resolveMappedUrlStrategy = __webpack_exports__.resolveMappedUrlStrategy;
|
|
56
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
57
|
+
"createMappedUrlStrategy",
|
|
58
|
+
"isDefaultLocaleRedirectSkipPath",
|
|
59
|
+
"resolveMappedUrlStrategy"
|
|
60
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
61
|
+
Object.defineProperty(exports, '__esModule', {
|
|
62
|
+
value: true
|
|
63
|
+
});
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
|
+
var define = (defs, kind)=>{
|
|
6
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
[kind]: defs[key]
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
define(getters, "get");
|
|
12
|
+
define(values, "value");
|
|
13
|
+
};
|
|
14
|
+
})();
|
|
15
|
+
(()=>{
|
|
16
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
17
|
+
})();
|
|
3
18
|
(()=>{
|
|
4
19
|
__webpack_require__.r = (exports1)=>{
|
|
5
20
|
if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
@@ -12,7 +27,22 @@ var __webpack_require__ = {};
|
|
|
12
27
|
})();
|
|
13
28
|
var __webpack_exports__ = {};
|
|
14
29
|
__webpack_require__.r(__webpack_exports__);
|
|
15
|
-
|
|
30
|
+
const asI18nUrlStrategy = (candidate)=>{
|
|
31
|
+
if (!candidate || 'object' != typeof candidate) return;
|
|
32
|
+
const strategy = candidate;
|
|
33
|
+
return 'function' == typeof strategy.localizePathname && 'function' == typeof strategy.canonicalPathname ? strategy : void 0;
|
|
34
|
+
};
|
|
35
|
+
const isUnusableI18nUrlStrategy = (candidate)=>Boolean(candidate) && 'object' == typeof candidate && void 0 === asI18nUrlStrategy(candidate);
|
|
36
|
+
__webpack_require__.d(__webpack_exports__, {}, {
|
|
37
|
+
asI18nUrlStrategy: asI18nUrlStrategy,
|
|
38
|
+
isUnusableI18nUrlStrategy: isUnusableI18nUrlStrategy
|
|
39
|
+
});
|
|
40
|
+
exports.asI18nUrlStrategy = __webpack_exports__.asI18nUrlStrategy;
|
|
41
|
+
exports.isUnusableI18nUrlStrategy = __webpack_exports__.isUnusableI18nUrlStrategy;
|
|
42
|
+
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
43
|
+
"asI18nUrlStrategy",
|
|
44
|
+
"isUnusableI18nUrlStrategy"
|
|
45
|
+
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
16
46
|
Object.defineProperty(exports, '__esModule', {
|
|
17
47
|
value: true
|
|
18
48
|
});
|
package/dist/esm/cli/index.mjs
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { getPublicDirRoutePrefixes } from "@modern-js/server-core";
|
|
2
|
+
import { logger } from "@modern-js/utils";
|
|
3
|
+
import { isUnusableI18nUrlStrategy } from "../shared/urlStrategy.mjs";
|
|
2
4
|
import { getBackendOptions, getLocaleDetectionOptions } from "../shared/utils.mjs";
|
|
3
5
|
import { applyDetectedBackendPaths, detectLocalesDirectory } from "./locales.mjs";
|
|
6
|
+
import { applyLocalisedRoutes } from "./localisedRoutes.mjs";
|
|
4
7
|
import "../runtime/types.mjs";
|
|
5
8
|
const i18nPlugin = (options = {})=>({
|
|
6
9
|
name: '@modern-js/plugin-i18n',
|
|
@@ -30,6 +33,10 @@ const i18nPlugin = (options = {})=>({
|
|
|
30
33
|
backend: backendOptions,
|
|
31
34
|
...extendedConfig
|
|
32
35
|
};
|
|
36
|
+
if (isUnusableI18nUrlStrategy(config.urlStrategy)) {
|
|
37
|
+
delete config.urlStrategy;
|
|
38
|
+
logger.warn("[i18n] `urlStrategy` cannot be passed through `modern.config.ts`: the generated runtime registration carries plugin options as JSON, which drops its methods. Configure `localeDetection.localisedUrls` instead — the runtime and the server both derive the URL policy from it — or register a runtime plugin that supplies the strategy directly.");
|
|
39
|
+
}
|
|
33
40
|
const { reactI18next } = extendedConfig;
|
|
34
41
|
const runtimePluginPath = customPlugin?.runtime?.path || (false === reactI18next ? `@${metaName}/plugin-i18n/runtime/no-react-i18next` : `@${metaName}/plugin-i18n/runtime`);
|
|
35
42
|
plugins.push({
|
|
@@ -42,6 +49,10 @@ const i18nPlugin = (options = {})=>({
|
|
|
42
49
|
plugins
|
|
43
50
|
};
|
|
44
51
|
});
|
|
52
|
+
api.modifyFileSystemRoutes(({ entrypoint, routes })=>({
|
|
53
|
+
entrypoint,
|
|
54
|
+
routes: applyLocalisedRoutes(routes, localeDetection ? getLocaleDetectionOptions(entrypoint.entryName, localeDetection) : void 0)
|
|
55
|
+
}));
|
|
45
56
|
api._internalServerPlugins(({ plugins })=>{
|
|
46
57
|
const { serverRoutes, metaName } = api.getAppContext();
|
|
47
58
|
const normalizedConfig = api.getNormalizedConfig();
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { applyLocalisedUrlsToRoutes, resolveLocalisedUrlsConfig } from "@modern-js/i18n-runtime-extensions";
|
|
2
|
+
const applyLocalisedRoutes = (routes, localeDetection)=>{
|
|
3
|
+
const { localePathRedirect, languages = [], localisedUrls } = localeDetection ?? {};
|
|
4
|
+
const resolved = resolveLocalisedUrlsConfig(localisedUrls);
|
|
5
|
+
if (!localePathRedirect || !languages.length || !resolved.enabled) return routes;
|
|
6
|
+
return applyLocalisedUrlsToRoutes(routes, languages, resolved.map);
|
|
7
|
+
};
|
|
8
|
+
export { applyLocalisedRoutes };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { resolveMappedUrlStrategy } from "../shared/mappedUrlStrategy.mjs";
|
|
2
|
+
import { asI18nUrlStrategy } from "../shared/urlStrategy.mjs";
|
|
1
3
|
import { setupI18nBeforeRender } from "./pluginSetup.mjs";
|
|
2
4
|
import { createI18nRootWrapper } from "./providerComposition.mjs";
|
|
3
5
|
import { resolveReactI18nextIntegration } from "./reactI18next.mjs";
|
|
@@ -8,6 +10,7 @@ const createI18nPlugin = (loadReactI18nextIntegration)=>(options)=>({
|
|
|
8
10
|
const { entryName, i18nInstance: userI18nInstance, initOptions, localeDetection, backend, htmlLangAttr = false, reactI18next = true, urlStrategy, NavigationProvider, LanguageSynchronization } = options;
|
|
9
11
|
const { localePathRedirect = false, i18nextDetector = true, languages = [], fallbackLanguage = 'en', detection, ignoreRedirectRoutes } = localeDetection || {};
|
|
10
12
|
const { enabled: backendEnabled = false } = backend || {};
|
|
13
|
+
const resolvedUrlStrategy = asI18nUrlStrategy(urlStrategy) ?? resolveMappedUrlStrategy(localeDetection);
|
|
11
14
|
let latestI18nInstance;
|
|
12
15
|
let I18nextProvider;
|
|
13
16
|
api.onBeforeRender(async (context)=>{
|
|
@@ -35,7 +38,7 @@ const createI18nPlugin = (loadReactI18nextIntegration)=>(options)=>({
|
|
|
35
38
|
languages,
|
|
36
39
|
fallbackLanguage,
|
|
37
40
|
ignoreRedirectRoutes,
|
|
38
|
-
urlStrategy,
|
|
41
|
+
urlStrategy: resolvedUrlStrategy,
|
|
39
42
|
NavigationProvider,
|
|
40
43
|
LanguageSynchronization,
|
|
41
44
|
getLatestI18nInstance: ()=>latestI18nInstance,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { isBrowser } from "@modern-js/runtime";
|
|
2
2
|
import { getGlobalBasename } from "@modern-js/runtime/context";
|
|
3
3
|
import { splitUrlTarget } from "@modern-js/runtime-utils/url";
|
|
4
|
+
import { isDefaultLocaleRedirectSkipPath } from "../shared/mappedUrlStrategy.mjs";
|
|
5
|
+
import { asI18nUrlStrategy } from "../shared/urlStrategy.mjs";
|
|
4
6
|
const getPathname = (context)=>{
|
|
5
7
|
if (isBrowser()) return window.location.pathname;
|
|
6
8
|
return context.ssrContext?.request?.pathname || '/';
|
|
@@ -18,7 +20,8 @@ const getLanguageFromPath = (pathname, languages, fallbackLanguage)=>{
|
|
|
18
20
|
};
|
|
19
21
|
const buildLocalizedUrl = (target, language, languages, urlStrategy)=>{
|
|
20
22
|
const { pathname, search, hash } = splitUrlTarget(target);
|
|
21
|
-
|
|
23
|
+
const strategy = asI18nUrlStrategy(urlStrategy);
|
|
24
|
+
if (strategy) return `${strategy.localizePathname(pathname, language, languages)}${search}${hash}`;
|
|
22
25
|
const segments = pathname.split('/').filter(Boolean);
|
|
23
26
|
if (segments[0] && languages.some((item)=>item.toLowerCase() === segments[0].toLowerCase())) segments[0] = language;
|
|
24
27
|
else segments.unshift(language);
|
|
@@ -43,7 +46,8 @@ const detectLanguageFromPath = (pathname, languages, localePathRedirect)=>{
|
|
|
43
46
|
};
|
|
44
47
|
};
|
|
45
48
|
const shouldIgnoreRedirect = (pathname, languages, ignoreRedirectRoutes, urlStrategy)=>{
|
|
46
|
-
if (
|
|
49
|
+
if (isDefaultLocaleRedirectSkipPath(pathname, languages)) return true;
|
|
50
|
+
if (asI18nUrlStrategy(urlStrategy)?.shouldSkipRedirect?.(pathname, languages)) return true;
|
|
47
51
|
if (!ignoreRedirectRoutes) return false;
|
|
48
52
|
const segments = pathname.split('/').filter(Boolean);
|
|
49
53
|
if (segments[0] && languages.some((item)=>item.toLowerCase() === segments[0].toLowerCase())) segments.shift();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createMappedUrlStrategy } from "./mappedUrlStrategy.mjs";
|
|
2
|
+
const createDefaultUrlStrategy = (localisedUrls)=>createMappedUrlStrategy(localisedUrls);
|
|
3
|
+
const resolveDefaultUrlStrategy = (localisedUrls)=>{
|
|
4
|
+
if (!localisedUrls || 'object' != typeof localisedUrls || 0 === Object.keys(localisedUrls).length) return;
|
|
5
|
+
return createDefaultUrlStrategy(localisedUrls);
|
|
6
|
+
};
|
|
7
|
+
export { createDefaultUrlStrategy, resolveDefaultUrlStrategy };
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { asI18nUrlStrategy } from "../shared/urlStrategy.mjs";
|
|
1
2
|
import { getLocaleDetectionOptions } from "../shared/utils.mjs";
|
|
2
3
|
import { collectApiPrefixes, matchesApiPrefix } from "./apiPrefix.mjs";
|
|
4
|
+
import { resolveDefaultUrlStrategy } from "./defaultUrlStrategy.mjs";
|
|
3
5
|
import { convertToHonoLanguageDetectorOptions } from "./detectorOptions.mjs";
|
|
4
6
|
import { buildLocalizedUrl, createLocaleRedirectResponse, getLanguageFromPath, isStaticResourceRequest, shouldIgnoreRedirect } from "./redirectPolicy.mjs";
|
|
5
7
|
import * as __rspack_external__modern_js_server_core_hono_0af3baa7 from "@modern-js/server-core/hono";
|
|
@@ -26,8 +28,7 @@ const i18nServerPlugin = (options)=>({
|
|
|
26
28
|
const entryLocaleDetection = getLocaleDetectionOptions(entryName, options.localeDetection);
|
|
27
29
|
const { localePathRedirect, i18nextDetector = true, languages = [], fallbackLanguage = 'en', detection, ignoreRedirectRoutes } = entryLocaleDetection;
|
|
28
30
|
const legacyLocalisedUrls = Reflect.get(entryLocaleDetection, 'localisedUrls');
|
|
29
|
-
const urlStrategy = options.resolveUrlStrategy?.(entryName);
|
|
30
|
-
if (legacyLocalisedUrls && 'object' == typeof legacyLocalisedUrls && Object.keys(legacyLocalisedUrls).length > 0 && !urlStrategy) throw new Error('Mapped locale URLs require a URL strategy. Use the i18n integration server plugin or supply resolveUrlStrategy.');
|
|
31
|
+
const urlStrategy = asI18nUrlStrategy(options.resolveUrlStrategy?.(entryName)) ?? resolveDefaultUrlStrategy(legacyLocalisedUrls);
|
|
31
32
|
const staticRoutePrefixes = options.staticRoutePrefixes;
|
|
32
33
|
const originUrlPath = route.urlPath;
|
|
33
34
|
const urlPath = originUrlPath.endsWith('/') ? `${originUrlPath}*` : `${originUrlPath}/*`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createMappedUrlStrategy, resolveMappedUrlStrategy } from "../shared/mappedUrlStrategy.mjs";
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isDefaultLocaleRedirectSkipPath } from "../shared/mappedUrlStrategy.mjs";
|
|
2
|
+
import { asI18nUrlStrategy } from "../shared/urlStrategy.mjs";
|
|
1
3
|
const stripUrlPathPrefix = (pathname, urlPath)=>{
|
|
2
4
|
const basePath = urlPath.replace('/*', '');
|
|
3
5
|
if (!basePath || '/' === basePath) return pathname;
|
|
@@ -5,7 +7,8 @@ const stripUrlPathPrefix = (pathname, urlPath)=>{
|
|
|
5
7
|
};
|
|
6
8
|
const shouldIgnoreRedirect = (pathname, urlPath, ignoreRedirectRoutes, urlStrategy, languages = [])=>{
|
|
7
9
|
const remainingPath = stripUrlPathPrefix(pathname, urlPath);
|
|
8
|
-
if (
|
|
10
|
+
if (isDefaultLocaleRedirectSkipPath(remainingPath, languages)) return true;
|
|
11
|
+
if (asI18nUrlStrategy(urlStrategy)?.shouldSkipRedirect?.(remainingPath, languages)) return true;
|
|
9
12
|
if (!ignoreRedirectRoutes) return false;
|
|
10
13
|
const segments = remainingPath.split('/').filter(Boolean);
|
|
11
14
|
if (segments[0] && languages.some((language)=>language.toLowerCase() === segments[0].toLowerCase())) segments.shift();
|
|
@@ -37,7 +40,8 @@ const buildLocalizedUrl = (req, urlPath, language, languages, urlStrategy)=>{
|
|
|
37
40
|
const segments = remainingPath.split('/').filter(Boolean);
|
|
38
41
|
if (segments[0] && languages.some((item)=>item.toLowerCase() === segments[0].toLowerCase())) segments[0] = language;
|
|
39
42
|
else segments.unshift(language);
|
|
40
|
-
const
|
|
43
|
+
const strategy = asI18nUrlStrategy(urlStrategy);
|
|
44
|
+
const pathname = strategy ? strategy.localizePathname(remainingPath, language, languages) : `/${segments.join('/')}`;
|
|
41
45
|
return `${'/' === basePath ? '' : basePath}${pathname}${url.search}${url.hash}`;
|
|
42
46
|
};
|
|
43
47
|
const createLocaleRedirectResponse = (location)=>new Response(null, {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { canonicalTargetPathname, isDefaultLocaleRedirectSkipPath, localiseTargetPathname } from "@modern-js/i18n-runtime-extensions";
|
|
2
|
+
const createMappedUrlStrategy = (localisedUrls)=>({
|
|
3
|
+
localizePathname: (pathname, language, languages)=>localiseTargetPathname(pathname, language, [
|
|
4
|
+
...languages
|
|
5
|
+
], localisedUrls),
|
|
6
|
+
canonicalPathname: (pathname, languages)=>canonicalTargetPathname(pathname, [
|
|
7
|
+
...languages
|
|
8
|
+
], localisedUrls),
|
|
9
|
+
shouldSkipRedirect: (pathname, languages)=>isDefaultLocaleRedirectSkipPath(pathname, [
|
|
10
|
+
...languages
|
|
11
|
+
])
|
|
12
|
+
});
|
|
13
|
+
const resolveMappedUrlStrategy = (localeDetection)=>{
|
|
14
|
+
if (!localeDetection || 'object' != typeof localeDetection) return;
|
|
15
|
+
const localisedUrls = Reflect.get(localeDetection, 'localisedUrls');
|
|
16
|
+
if (!localisedUrls || 'object' != typeof localisedUrls || 0 === Object.keys(localisedUrls).length) return;
|
|
17
|
+
return createMappedUrlStrategy(localisedUrls);
|
|
18
|
+
};
|
|
19
|
+
export { createMappedUrlStrategy, isDefaultLocaleRedirectSkipPath, resolveMappedUrlStrategy };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
const asI18nUrlStrategy = (candidate)=>{
|
|
2
|
+
if (!candidate || 'object' != typeof candidate) return;
|
|
3
|
+
const strategy = candidate;
|
|
4
|
+
return 'function' == typeof strategy.localizePathname && 'function' == typeof strategy.canonicalPathname ? strategy : void 0;
|
|
5
|
+
};
|
|
6
|
+
const isUnusableI18nUrlStrategy = (candidate)=>Boolean(candidate) && 'object' == typeof candidate && void 0 === asI18nUrlStrategy(candidate);
|
|
7
|
+
export { asI18nUrlStrategy, isUnusableI18nUrlStrategy };
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import { getPublicDirRoutePrefixes } from "@modern-js/server-core";
|
|
3
|
+
import { logger } from "@modern-js/utils";
|
|
4
|
+
import { isUnusableI18nUrlStrategy } from "../shared/urlStrategy.mjs";
|
|
3
5
|
import { getBackendOptions, getLocaleDetectionOptions } from "../shared/utils.mjs";
|
|
4
6
|
import { applyDetectedBackendPaths, detectLocalesDirectory } from "./locales.mjs";
|
|
7
|
+
import { applyLocalisedRoutes } from "./localisedRoutes.mjs";
|
|
5
8
|
import "../runtime/types.mjs";
|
|
6
9
|
const i18nPlugin = (options = {})=>({
|
|
7
10
|
name: '@modern-js/plugin-i18n',
|
|
@@ -31,6 +34,10 @@ const i18nPlugin = (options = {})=>({
|
|
|
31
34
|
backend: backendOptions,
|
|
32
35
|
...extendedConfig
|
|
33
36
|
};
|
|
37
|
+
if (isUnusableI18nUrlStrategy(config.urlStrategy)) {
|
|
38
|
+
delete config.urlStrategy;
|
|
39
|
+
logger.warn("[i18n] `urlStrategy` cannot be passed through `modern.config.ts`: the generated runtime registration carries plugin options as JSON, which drops its methods. Configure `localeDetection.localisedUrls` instead — the runtime and the server both derive the URL policy from it — or register a runtime plugin that supplies the strategy directly.");
|
|
40
|
+
}
|
|
34
41
|
const { reactI18next } = extendedConfig;
|
|
35
42
|
const runtimePluginPath = customPlugin?.runtime?.path || (false === reactI18next ? `@${metaName}/plugin-i18n/runtime/no-react-i18next` : `@${metaName}/plugin-i18n/runtime`);
|
|
36
43
|
plugins.push({
|
|
@@ -43,6 +50,10 @@ const i18nPlugin = (options = {})=>({
|
|
|
43
50
|
plugins
|
|
44
51
|
};
|
|
45
52
|
});
|
|
53
|
+
api.modifyFileSystemRoutes(({ entrypoint, routes })=>({
|
|
54
|
+
entrypoint,
|
|
55
|
+
routes: applyLocalisedRoutes(routes, localeDetection ? getLocaleDetectionOptions(entrypoint.entryName, localeDetection) : void 0)
|
|
56
|
+
}));
|
|
46
57
|
api._internalServerPlugins(({ plugins })=>{
|
|
47
58
|
const { serverRoutes, metaName } = api.getAppContext();
|
|
48
59
|
const normalizedConfig = api.getNormalizedConfig();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
import { applyLocalisedUrlsToRoutes, resolveLocalisedUrlsConfig } from "@modern-js/i18n-runtime-extensions";
|
|
3
|
+
const applyLocalisedRoutes = (routes, localeDetection)=>{
|
|
4
|
+
const { localePathRedirect, languages = [], localisedUrls } = localeDetection ?? {};
|
|
5
|
+
const resolved = resolveLocalisedUrlsConfig(localisedUrls);
|
|
6
|
+
if (!localePathRedirect || !languages.length || !resolved.enabled) return routes;
|
|
7
|
+
return applyLocalisedUrlsToRoutes(routes, languages, resolved.map);
|
|
8
|
+
};
|
|
9
|
+
export { applyLocalisedRoutes };
|