@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.
Files changed (59) hide show
  1. package/dist/cjs/cli/index.js +11 -0
  2. package/dist/cjs/cli/localisedRoutes.js +46 -0
  3. package/dist/cjs/runtime/core.js +4 -1
  4. package/dist/cjs/runtime/utils.js +6 -2
  5. package/dist/cjs/server/defaultUrlStrategy.js +48 -0
  6. package/dist/cjs/server/index.js +3 -2
  7. package/dist/cjs/server/mappedUrlStrategy.js +43 -0
  8. package/dist/cjs/server/redirectPolicy.js +13 -9
  9. package/dist/cjs/shared/mappedUrlStrategy.js +63 -0
  10. package/dist/cjs/shared/urlStrategy.js +31 -1
  11. package/dist/esm/cli/index.mjs +11 -0
  12. package/dist/esm/cli/localisedRoutes.mjs +8 -0
  13. package/dist/esm/runtime/core.mjs +4 -1
  14. package/dist/esm/runtime/utils.mjs +6 -2
  15. package/dist/esm/server/defaultUrlStrategy.mjs +7 -0
  16. package/dist/esm/server/index.mjs +3 -2
  17. package/dist/esm/server/mappedUrlStrategy.mjs +1 -0
  18. package/dist/esm/server/redirectPolicy.mjs +6 -2
  19. package/dist/esm/shared/mappedUrlStrategy.mjs +19 -0
  20. package/dist/esm/shared/urlStrategy.mjs +7 -0
  21. package/dist/esm-node/cli/index.mjs +11 -0
  22. package/dist/esm-node/cli/localisedRoutes.mjs +9 -0
  23. package/dist/esm-node/runtime/core.mjs +4 -1
  24. package/dist/esm-node/runtime/utils.mjs +6 -2
  25. package/dist/esm-node/server/defaultUrlStrategy.mjs +8 -0
  26. package/dist/esm-node/server/index.mjs +3 -2
  27. package/dist/esm-node/server/mappedUrlStrategy.mjs +2 -0
  28. package/dist/esm-node/server/redirectPolicy.mjs +6 -2
  29. package/dist/esm-node/shared/mappedUrlStrategy.mjs +20 -0
  30. package/dist/esm-node/shared/urlStrategy.mjs +7 -0
  31. package/dist/types/cli/localisedRoutes.d.ts +18 -0
  32. package/dist/types/runtime/core.d.ts +1 -1
  33. package/dist/types/runtime/utils.d.ts +1 -1
  34. package/dist/types/server/defaultUrlStrategy.d.ts +11 -0
  35. package/dist/types/server/index.d.ts +1 -1
  36. package/dist/types/server/mappedUrlStrategy.d.ts +7 -0
  37. package/dist/types/server/redirectPolicy.d.ts +1 -1
  38. package/dist/types/shared/mappedUrlStrategy.d.ts +27 -0
  39. package/dist/types/shared/type.d.ts +10 -0
  40. package/dist/types/shared/urlStrategy.d.ts +11 -0
  41. package/package.json +11 -10
  42. package/rstest.config.mts +4 -0
  43. package/src/cli/index.ts +42 -1
  44. package/src/cli/localisedRoutes.ts +43 -0
  45. package/src/runtime/core.tsx +18 -2
  46. package/src/runtime/utils.ts +13 -4
  47. package/src/server/defaultUrlStrategy.ts +27 -0
  48. package/src/server/index.ts +7 -12
  49. package/src/server/mappedUrlStrategy.ts +10 -0
  50. package/src/server/redirectPolicy.ts +19 -4
  51. package/src/shared/mappedUrlStrategy.ts +59 -0
  52. package/src/shared/type.ts +10 -0
  53. package/src/shared/urlStrategy.ts +27 -0
  54. package/tests/defaultUrlStrategy.test.ts +107 -0
  55. package/tests/localisedRouteGeneration.test.ts +159 -0
  56. package/tests/localisedUrls.test.ts +21 -4
  57. package/tests/mappedUrlStrategyDerivation.test.ts +62 -0
  58. package/tests/redirectPolicy.test.ts +53 -0
  59. package/tests/urlStrategySerialization.test.ts +122 -0
@@ -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
+ });
@@ -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
- if (urlStrategy) return `${urlStrategy.localizePathname(pathname, language, languages)}${search}${hash}`;
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 (urlStrategy?.shouldSkipRedirect?.(pathname, languages)) return true;
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
+ });
@@ -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 (urlStrategy?.shouldSkipRedirect?.(remainingPath, languages)) return true;
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 pathname = urlStrategy ? urlStrategy.localizePathname(remainingPath, language, languages) : `/${segments.join('/')}`;
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
- for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
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
  });
@@ -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
- if (urlStrategy) return `${urlStrategy.localizePathname(pathname, language, languages)}${search}${hash}`;
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 (urlStrategy?.shouldSkipRedirect?.(pathname, languages)) return true;
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 (urlStrategy?.shouldSkipRedirect?.(remainingPath, languages)) return true;
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 pathname = urlStrategy ? urlStrategy.localizePathname(remainingPath, language, languages) : `/${segments.join('/')}`;
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 };