@bleedingdev/modern-js-plugin-i18n 3.5.0-ultramodern.36 → 3.5.0-ultramodern.38

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 (91) hide show
  1. package/dist/cjs/cli/locales.js +15 -15
  2. package/dist/cjs/runtime/i18n/backend/defaults.node.js +7 -7
  3. package/dist/cjs/runtime/i18n/detection/cache.js +3 -2
  4. package/dist/cjs/runtime/i18n/instance.js +7 -7
  5. package/dist/cjs/runtime/utils.js +1 -9
  6. package/dist/cjs/server/redirectPolicy.js +14 -20
  7. package/dist/cjs/shared/detection.js +2 -104
  8. package/dist/cjs/shared/localisedUrls/index.js +16 -0
  9. package/dist/cjs/shared/localisedUrls/pathname.js +5 -2
  10. package/dist/cjs/shared/localisedUrls/redirect.js +95 -0
  11. package/dist/cjs/shared/localisedUrls/resolve.js +29 -32
  12. package/dist/cjs/shared/localisedUrls.js +15 -0
  13. package/dist/cjs/shared/utils.js +2 -8
  14. package/dist/esm/cli/locales.mjs +13 -13
  15. package/dist/esm/runtime/i18n/backend/defaults.node.mjs +5 -5
  16. package/dist/esm/runtime/i18n/detection/cache.mjs +3 -2
  17. package/dist/esm/runtime/i18n/instance.mjs +8 -5
  18. package/dist/esm/runtime/utils.mjs +2 -10
  19. package/dist/esm/server/index.mjs +2 -2
  20. package/dist/esm/server/redirectPolicy.mjs +15 -21
  21. package/dist/esm/shared/detection.mjs +1 -71
  22. package/dist/esm/shared/localisedUrls/index.mjs +2 -1
  23. package/dist/esm/shared/localisedUrls/pathname.mjs +1 -1
  24. package/dist/esm/shared/localisedUrls/redirect.mjs +48 -0
  25. package/dist/esm/shared/localisedUrls/resolve.mjs +29 -32
  26. package/dist/esm/shared/localisedUrls.mjs +1 -1
  27. package/dist/esm/shared/utils.mjs +1 -1
  28. package/dist/esm-node/cli/locales.mjs +13 -13
  29. package/dist/esm-node/runtime/i18n/backend/defaults.node.mjs +5 -5
  30. package/dist/esm-node/runtime/i18n/detection/cache.mjs +3 -2
  31. package/dist/esm-node/runtime/i18n/instance.mjs +8 -5
  32. package/dist/esm-node/runtime/utils.mjs +2 -10
  33. package/dist/esm-node/server/index.mjs +2 -2
  34. package/dist/esm-node/server/redirectPolicy.mjs +15 -21
  35. package/dist/esm-node/shared/detection.mjs +1 -71
  36. package/dist/esm-node/shared/localisedUrls/index.mjs +2 -1
  37. package/dist/esm-node/shared/localisedUrls/pathname.mjs +1 -1
  38. package/dist/esm-node/shared/localisedUrls/redirect.mjs +49 -0
  39. package/dist/esm-node/shared/localisedUrls/resolve.mjs +29 -32
  40. package/dist/esm-node/shared/localisedUrls.mjs +1 -1
  41. package/dist/esm-node/shared/utils.mjs +1 -1
  42. package/dist/types/cli/locales.d.ts +1 -1
  43. package/dist/types/runtime/context.d.ts +4 -3
  44. package/dist/types/runtime/contextHelpers.d.ts +2 -2
  45. package/dist/types/runtime/i18n/backend/middleware.common.d.ts +2 -1
  46. package/dist/types/runtime/i18n/detection/cache.d.ts +1 -1
  47. package/dist/types/runtime/i18n/detection/detector.d.ts +2 -2
  48. package/dist/types/runtime/i18n/detection/middleware.d.ts +4 -4
  49. package/dist/types/runtime/i18n/detection/middleware.node.d.ts +4 -4
  50. package/dist/types/runtime/i18n/detection/types.d.ts +8 -5
  51. package/dist/types/runtime/i18n/instance.d.ts +28 -26
  52. package/dist/types/runtime/i18n/utils.d.ts +14 -5
  53. package/dist/types/runtime/pluginSetup.d.ts +1 -1
  54. package/dist/types/runtime/providerComposition.d.ts +2 -1
  55. package/dist/types/runtime/routerAdapter.d.ts +7 -6
  56. package/dist/types/server/redirectPolicy.d.ts +14 -8
  57. package/dist/types/shared/detection.d.ts +1 -11
  58. package/dist/types/shared/localisedUrls/index.d.ts +3 -1
  59. package/dist/types/shared/localisedUrls/pathname.d.ts +1 -0
  60. package/dist/types/shared/localisedUrls/redirect.d.ts +11 -0
  61. package/dist/types/shared/localisedUrls/resolve.d.ts +2 -3
  62. package/dist/types/shared/localisedUrls.d.ts +2 -2
  63. package/dist/types/shared/utils.d.ts +0 -2
  64. package/package.json +16 -18
  65. package/src/cli/index.ts +2 -2
  66. package/src/cli/locales.ts +3 -3
  67. package/src/runtime/context.tsx +3 -3
  68. package/src/runtime/contextHelpers.ts +3 -3
  69. package/src/runtime/hooks.ts +10 -2
  70. package/src/runtime/i18n/backend/defaults.node.ts +2 -2
  71. package/src/runtime/i18n/backend/middleware.common.ts +5 -4
  72. package/src/runtime/i18n/detection/cache.ts +23 -9
  73. package/src/runtime/i18n/detection/detector.ts +15 -6
  74. package/src/runtime/i18n/detection/middleware.node.ts +26 -7
  75. package/src/runtime/i18n/detection/middleware.ts +15 -7
  76. package/src/runtime/i18n/detection/types.ts +13 -5
  77. package/src/runtime/i18n/instance.ts +56 -31
  78. package/src/runtime/i18n/utils.ts +18 -9
  79. package/src/runtime/pluginSetup.ts +1 -1
  80. package/src/runtime/providerComposition.tsx +1 -1
  81. package/src/runtime/routerAdapter.tsx +6 -6
  82. package/src/runtime/utils.ts +5 -29
  83. package/src/server/redirectPolicy.ts +51 -76
  84. package/src/shared/detection.ts +1 -1
  85. package/src/shared/localisedUrls/index.ts +12 -1
  86. package/src/shared/localisedUrls/pathname.ts +4 -1
  87. package/src/shared/localisedUrls/redirect.ts +91 -0
  88. package/src/shared/localisedUrls/resolve.ts +91 -74
  89. package/src/shared/localisedUrls.ts +6 -0
  90. package/src/shared/utils.ts +2 -2
  91. package/tests/redirectPolicy.test.ts +43 -0
@@ -22,9 +22,10 @@ const stableStringify = (value)=>{
22
22
  if (null == value) return JSON.stringify(value);
23
23
  if ('object' != typeof value) return JSON.stringify(value);
24
24
  if (Array.isArray(value)) return `[${value.map((item)=>stableStringify(item)).join(',')}]`;
25
- const sortedKeys = Object.keys(value).sort();
25
+ const record = value;
26
+ const sortedKeys = Object.keys(record).sort();
26
27
  const sortedEntries = sortedKeys.map((key)=>{
27
- const stringifiedValue = stableStringify(value[key]);
28
+ const stringifiedValue = stableStringify(record[key]);
28
29
  return `${JSON.stringify(key)}:${stringifiedValue}`;
29
30
  });
30
31
  return `{${sortedEntries.join(',')}}`;
@@ -1,9 +1,11 @@
1
1
  import "node:module";
2
2
  function isI18nWrapperInstance(obj) {
3
3
  if (!obj || 'object' != typeof obj) return false;
4
- if (!obj.i18nInstance || 'object' != typeof obj.i18nInstance) return false;
5
- if (!obj.i18nInstance.instance) return false;
6
- if ('function' != typeof obj.init || 'function' != typeof obj.use) return false;
4
+ const candidate = obj;
5
+ if (!candidate.i18nInstance || 'object' != typeof candidate.i18nInstance) return false;
6
+ const wrapper = candidate.i18nInstance;
7
+ if (!wrapper.instance) return false;
8
+ if ('function' != typeof candidate.init || 'function' != typeof candidate.use) return false;
7
9
  return true;
8
10
  }
9
11
  function getI18nWrapperI18nextInstance(wrapperInstance) {
@@ -20,7 +22,8 @@ function getActualI18nextInstance(instance) {
20
22
  function isI18nInstance(obj) {
21
23
  if (!obj || 'object' != typeof obj) return false;
22
24
  if (isI18nWrapperInstance(obj)) return true;
23
- return 'function' == typeof obj.init && 'function' == typeof obj.use;
25
+ const candidate = obj;
26
+ return 'function' == typeof candidate.init && 'function' == typeof candidate.use;
24
27
  }
25
28
  async function tryImportI18next() {
26
29
  try {
@@ -57,4 +60,4 @@ async function getI18nInstance(userInstance) {
57
60
  if (i18nextInstance) return i18nextInstance;
58
61
  throw new Error('No i18n instance found');
59
62
  }
60
- export { getActualI18nextInstance, getI18nInstance, getI18nWrapperI18nextInstance, getI18nextInstanceForProvider, isI18nInstance, isI18nWrapperInstance };
63
+ export { getActualI18nextInstance, getI18nInstance, getI18nextInstanceForProvider, isI18nInstance, isI18nWrapperInstance };
@@ -1,7 +1,7 @@
1
1
  import "node:module";
2
2
  import { isBrowser } from "@modern-js/runtime";
3
3
  import { getGlobalBasename } from "@modern-js/runtime/context";
4
- import { localiseTargetPathname } from "../shared/localisedUrls.mjs";
4
+ import { localiseTargetPathname, shouldSkipLocaleRedirect } from "../shared/localisedUrls.mjs";
5
5
  const getPathname = (context)=>{
6
6
  if (isBrowser()) return window.location.pathname;
7
7
  return context.ssrContext?.request?.pathname || '/';
@@ -52,13 +52,5 @@ const detectLanguageFromPath = (pathname, languages, localePathRedirect)=>{
52
52
  detected: false
53
53
  };
54
54
  };
55
- const shouldIgnoreRedirect = (pathname, languages, ignoreRedirectRoutes)=>{
56
- if (!ignoreRedirectRoutes) return false;
57
- const segments = pathname.split('/').filter(Boolean);
58
- let pathWithoutLang = pathname;
59
- if (segments.length > 0 && languages.includes(segments[0])) pathWithoutLang = `/${segments.slice(1).join('/')}`;
60
- const normalizedPath = pathWithoutLang.startsWith('/') ? pathWithoutLang : `/${pathWithoutLang}`;
61
- if ('function' == typeof ignoreRedirectRoutes) return ignoreRedirectRoutes(normalizedPath);
62
- return ignoreRedirectRoutes.some((pattern)=>normalizedPath === pattern || normalizedPath.startsWith(`${pattern}/`));
63
- };
55
+ const shouldIgnoreRedirect = (pathname, languages, ignoreRedirectRoutes)=>shouldSkipLocaleRedirect(pathname, languages, ignoreRedirectRoutes);
64
56
  export { buildLocalizedUrl, detectLanguageFromPath, getEntryPath, getLanguageFromPath, getPathname, shouldIgnoreRedirect, splitUrlTarget };
@@ -4,8 +4,8 @@ import { getLocaleDetectionOptions } from "../shared/utils.mjs";
4
4
  import { collectApiPrefixes, matchesApiPrefix } from "./apiPrefix.mjs";
5
5
  import { convertToHonoLanguageDetectorOptions } from "./detectorOptions.mjs";
6
6
  import { buildLocalizedUrl, createLocaleRedirectResponse, getLanguageFromPath, isStaticResourceRequest, shouldIgnoreRedirect } from "./redirectPolicy.mjs";
7
- import * as __rspack_external__modern_js_server_core_hono_a76ca254 from "@modern-js/server-core/hono";
8
- const { languageDetector: languageDetector } = __rspack_external__modern_js_server_core_hono_a76ca254;
7
+ import * as __rspack_external__modern_js_server_core_hono_0af3baa7 from "@modern-js/server-core/hono";
8
+ const { languageDetector: languageDetector } = __rspack_external__modern_js_server_core_hono_0af3baa7;
9
9
  const i18nServerPlugin = (options)=>({
10
10
  name: '@modern-js/plugin-i18n/server',
11
11
  setup: (api)=>{
@@ -1,32 +1,26 @@
1
1
  import "node:module";
2
- import { localiseTargetPathname } from "../shared/localisedUrls.mjs";
3
- const shouldIgnoreRedirect = (pathname, urlPath, ignoreRedirectRoutes)=>{
4
- if (!ignoreRedirectRoutes) return false;
2
+ import { isDefaultLocaleRedirectSkipPath, localiseTargetPathname, matchesPathPrefix, shouldSkipLocaleRedirect, stripLanguagePrefix } from "../shared/localisedUrls.mjs";
3
+ const stripUrlPathPrefix = (pathname, urlPath)=>{
5
4
  const basePath = urlPath.replace('/*', '');
5
+ if (!basePath || '/' === basePath) return pathname;
6
6
  const remainingPath = pathname.startsWith(basePath) ? pathname.slice(basePath.length) : pathname;
7
- const normalizedPath = remainingPath.startsWith('/') ? remainingPath : `/${remainingPath}`;
8
- if ('function' == typeof ignoreRedirectRoutes) return ignoreRedirectRoutes(normalizedPath);
9
- return ignoreRedirectRoutes.some((pattern)=>normalizedPath === pattern || normalizedPath.startsWith(`${pattern}/`));
7
+ return remainingPath || '/';
8
+ };
9
+ const shouldIgnoreRedirect = (pathname, urlPath, ignoreRedirectRoutes)=>{
10
+ const remainingPath = stripUrlPathPrefix(pathname, urlPath);
11
+ return shouldSkipLocaleRedirect(remainingPath, [], ignoreRedirectRoutes);
10
12
  };
11
13
  const isStaticResourceRequest = (pathname, staticRoutePrefixes, languages = [])=>{
12
- if (staticRoutePrefixes.some((prefix)=>pathname.startsWith(`${prefix}/`) || pathname === prefix)) return true;
13
- const standardStaticPrefixes = [
14
- '/static/',
15
- '/upload/'
16
- ];
17
- if (standardStaticPrefixes.some((prefix)=>pathname.startsWith(prefix))) return true;
18
- const pathSegments = pathname.split('/').filter(Boolean);
19
- if (pathSegments.length > 0 && languages.includes(pathSegments[0])) {
20
- const pathWithoutLang = '/' + pathSegments.slice(1).join('/');
21
- if (standardStaticPrefixes.some((prefix)=>pathWithoutLang.startsWith(prefix)) || staticRoutePrefixes.some((prefix)=>pathWithoutLang.startsWith(`${prefix}/`) || pathWithoutLang === prefix)) return true;
22
- }
23
- return false;
14
+ if (isDefaultLocaleRedirectSkipPath(pathname, languages)) return true;
15
+ const matchesStaticRoutePrefix = (targetPathname)=>staticRoutePrefixes.some((prefix)=>matchesPathPrefix(targetPathname, prefix));
16
+ if (matchesStaticRoutePrefix(pathname)) return true;
17
+ const pathWithoutLanguage = stripLanguagePrefix(pathname, languages);
18
+ return pathWithoutLanguage !== pathname && matchesStaticRoutePrefix(pathWithoutLanguage);
24
19
  };
25
20
  const getLanguageFromPath = (req, urlPath, languages)=>{
26
21
  const url = new URL(req.url, `http://${req.header().host}`);
27
22
  const pathname = url.pathname;
28
- const basePath = urlPath.replace('/*', '');
29
- const remainingPath = pathname.startsWith(basePath) ? pathname.slice(basePath.length) : pathname;
23
+ const remainingPath = stripUrlPathPrefix(pathname, urlPath);
30
24
  const segments = remainingPath.split('/').filter(Boolean);
31
25
  const firstSegment = segments[0];
32
26
  if (languages.includes(firstSegment)) return firstSegment;
@@ -36,7 +30,7 @@ const buildLocalizedUrl = (req, urlPath, language, languages, localisedUrls)=>{
36
30
  const url = new URL(req.url);
37
31
  const pathname = url.pathname;
38
32
  const basePath = urlPath.replace('/*', '');
39
- const remainingPath = pathname.startsWith(basePath) ? pathname.slice(basePath.length) : pathname;
33
+ const remainingPath = stripUrlPathPrefix(pathname, urlPath);
40
34
  const newPathname = localiseTargetPathname(remainingPath, language, languages, localisedUrls);
41
35
  const suffix = `${url.search}${url.hash}`;
42
36
  const localizedUrl = '/' === basePath ? newPathname + suffix : basePath + newPathname + suffix;
@@ -1,72 +1,2 @@
1
1
  import "node:module";
2
- import { DEFAULT_I18NEXT_DETECTION_OPTIONS, mergeDetectionOptions } from "../runtime/i18n/detection/config.mjs";
3
- function detectLanguageFromRequest(req, languages, detectionOptions) {
4
- try {
5
- const mergedDetection = detectionOptions ? mergeDetectionOptions(detectionOptions) : DEFAULT_I18NEXT_DETECTION_OPTIONS;
6
- const order = (mergedDetection.order || []).filter((item)=>![
7
- 'path',
8
- 'localStorage',
9
- 'navigator',
10
- 'htmlTag',
11
- 'subdomain'
12
- ].includes(item));
13
- const detectionOrder = order.length > 0 ? order : [
14
- 'querystring',
15
- 'cookie',
16
- 'header'
17
- ];
18
- const getHeader = (name)=>{
19
- req.headers, Headers;
20
- return req.headers.get(name);
21
- };
22
- for (const method of detectionOrder){
23
- let detectedLang = null;
24
- switch(method){
25
- case 'querystring':
26
- {
27
- const lookupKey = mergedDetection.lookupQuerystring || DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupQuerystring || 'lng';
28
- const host = getHeader('host') || 'localhost';
29
- const url = new URL(req.url, `http://${host}`);
30
- detectedLang = url.searchParams.get(lookupKey);
31
- break;
32
- }
33
- case 'cookie':
34
- {
35
- const lookupKey = mergedDetection.lookupCookie || DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupCookie || 'i18next';
36
- const cookieHeader = getHeader('Cookie');
37
- if (cookieHeader) {
38
- const cookies = cookieHeader.split(';').reduce((acc, item)=>{
39
- const [key, value] = item.trim().split('=');
40
- if (key && value) acc[key] = value;
41
- return acc;
42
- }, {});
43
- detectedLang = cookies[lookupKey] || null;
44
- }
45
- break;
46
- }
47
- case 'header':
48
- {
49
- const lookupKey = mergedDetection.lookupHeader || DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupHeader || 'accept-language';
50
- const acceptLanguage = getHeader(lookupKey);
51
- if (acceptLanguage) {
52
- const languagesList = acceptLanguage.split(',').map((lang)=>{
53
- const [code, q] = lang.trim().split(';');
54
- return {
55
- code: code.split('-')[0],
56
- quality: q ? parseFloat(q.split('=')[1]) : 1.0
57
- };
58
- }).sort((a, b)=>b.quality - a.quality);
59
- for (const lang of languagesList)if (0 === languages.length || languages.includes(lang.code)) {
60
- detectedLang = lang.code;
61
- break;
62
- }
63
- }
64
- break;
65
- }
66
- }
67
- if (detectedLang && (0 === languages.length || languages.includes(detectedLang))) return detectedLang;
68
- }
69
- } catch (error) {}
70
- return null;
71
- }
72
- export { detectLanguageFromRequest };
2
+ import "../runtime/i18n/detection/config.mjs";
@@ -1,7 +1,8 @@
1
1
  import "node:module";
2
2
  export { resolveLocalisedUrlsConfig } from "./config.mjs";
3
3
  export { normalisePathPattern, normalisePathname } from "./normalise.mjs";
4
- export { canonicalTargetPathname, localiseTargetPathname } from "./pathname.mjs";
4
+ export { canonicalTargetPathname, localiseTargetPathname, stripLanguagePrefix } from "./pathname.mjs";
5
5
  export { buildPathFromPattern, matchPathPattern } from "./patterns.mjs";
6
+ export { DEFAULT_LOCALE_REDIRECT_SKIP_RULES, isDefaultLocaleRedirectSkipPath, matchesPathPrefix, shouldSkipLocaleRedirect } from "./redirect.mjs";
6
7
  export { resolveCanonicalLocalisedPath, resolveLocalisedPath } from "./resolve.mjs";
7
8
  export { applyLocalisedUrlsToRoutes, validateLocalisedUrls } from "./routes.mjs";
@@ -22,4 +22,4 @@ const canonicalTargetPathname = (pathname, languages, localisedUrls)=>{
22
22
  const localisedUrlsConfig = resolveLocalisedUrlsConfig(localisedUrls);
23
23
  return localisedUrlsConfig.enabled ? resolveCanonicalLocalisedPath(pathWithoutLanguage, languages, localisedUrlsConfig.map) : pathWithoutLanguage;
24
24
  };
25
- export { canonicalTargetPathname, localiseTargetPathname };
25
+ export { canonicalTargetPathname, localiseTargetPathname, stripLanguagePrefix };
@@ -0,0 +1,49 @@
1
+ import "node:module";
2
+ import { normalisePathname } from "./normalise.mjs";
3
+ import { stripLanguagePrefix } from "./pathname.mjs";
4
+ const DEFAULT_LOCALE_REDIRECT_SKIP_RULES = [
5
+ {
6
+ type: 'exact',
7
+ path: '/mf-manifest.json'
8
+ },
9
+ {
10
+ type: 'exact',
11
+ path: '/mf-stats.json'
12
+ },
13
+ {
14
+ type: 'exact',
15
+ path: '/remoteEntry.js'
16
+ },
17
+ {
18
+ type: 'prefix',
19
+ path: '/static/'
20
+ },
21
+ {
22
+ type: 'prefix',
23
+ path: '/upload/'
24
+ }
25
+ ];
26
+ const stripTrailingSlash = (pathname)=>pathname.length > 1 ? pathname.replace(/\/+$/, '') : pathname;
27
+ const matchesPathPrefix = (pathname, prefix)=>{
28
+ const normalizedPathname = normalisePathname(pathname);
29
+ const normalizedPrefix = stripTrailingSlash(normalisePathname(prefix));
30
+ return normalizedPathname === normalizedPrefix || normalizedPathname.startsWith(`${normalizedPrefix}/`);
31
+ };
32
+ const matchesSkipRule = (pathname, rule)=>{
33
+ if ('exact' === rule.type) return normalisePathname(pathname) === normalisePathname(rule.path);
34
+ return matchesPathPrefix(pathname, rule.path);
35
+ };
36
+ const isDefaultLocaleRedirectSkipPath = (pathname, languages = [])=>{
37
+ const normalizedPathname = normalisePathname(pathname);
38
+ const pathWithoutLanguage = stripLanguagePrefix(normalizedPathname, languages);
39
+ return DEFAULT_LOCALE_REDIRECT_SKIP_RULES.some((rule)=>matchesSkipRule(pathWithoutLanguage, rule));
40
+ };
41
+ const shouldSkipLocaleRedirect = (pathname, languages = [], ignoreRedirectRoutes)=>{
42
+ const normalizedPathname = normalisePathname(pathname);
43
+ const pathWithoutLanguage = stripLanguagePrefix(normalizedPathname, languages);
44
+ if (isDefaultLocaleRedirectSkipPath(pathWithoutLanguage)) return true;
45
+ if (!ignoreRedirectRoutes) return false;
46
+ if ('function' == typeof ignoreRedirectRoutes) return ignoreRedirectRoutes(pathWithoutLanguage);
47
+ return ignoreRedirectRoutes.some((pattern)=>matchesPathPrefix(pathWithoutLanguage, pattern));
48
+ };
49
+ export { DEFAULT_LOCALE_REDIRECT_SKIP_RULES, isDefaultLocaleRedirectSkipPath, matchesPathPrefix, shouldSkipLocaleRedirect };
@@ -1,51 +1,48 @@
1
1
  import "node:module";
2
2
  import { normalisePathname } from "./normalise.mjs";
3
3
  import { buildPathFromPattern, matchPathPattern, sortPatternsBySpecificity } from "./patterns.mjs";
4
- const resolveLocalisedPath = (pathname, targetLanguage, languages, localisedUrls)=>{
5
- const normalizedPathname = normalisePathname(pathname);
6
- const canonicalCandidates = sortPatternsBySpecificity(Object.entries(localisedUrls).map(([canonicalPattern, localisedUrlEntry])=>({
4
+ const getCanonicalCandidates = (localisedUrls)=>sortPatternsBySpecificity(Object.entries(localisedUrls).map(([canonicalPattern, localisedUrlEntry])=>({
7
5
  pattern: canonicalPattern,
8
6
  canonicalPattern,
9
7
  localisedUrlEntry
10
8
  })));
11
- for (const { canonicalPattern, localisedUrlEntry } of canonicalCandidates){
12
- const targetPattern = localisedUrlEntry[targetLanguage];
13
- if (!targetPattern) continue;
14
- const params = matchPathPattern(normalizedPathname, canonicalPattern);
15
- if (params) return buildPathFromPattern(targetPattern, params);
16
- }
17
- const localisedCandidates = sortPatternsBySpecificity(Object.values(localisedUrls).flatMap((localisedUrlEntry)=>{
18
- const targetPattern = localisedUrlEntry[targetLanguage];
19
- if (!targetPattern) return [];
20
- return languages.map((language)=>localisedUrlEntry[language]).filter((sourcePattern)=>Boolean(sourcePattern)).map((sourcePattern)=>({
21
- pattern: sourcePattern,
22
- sourcePattern,
23
- targetPattern
24
- }));
25
- }));
26
- for (const { sourcePattern, targetPattern } of localisedCandidates){
27
- const params = matchPathPattern(normalizedPathname, sourcePattern);
28
- if (params) return buildPathFromPattern(targetPattern, params);
29
- }
30
- return normalizedPathname;
31
- };
32
- const resolveCanonicalLocalisedPath = (pathname, languages, localisedUrls)=>{
9
+ const resolveLocalisedUrlPath = (pathname, languages, localisedUrls, options)=>{
33
10
  const normalizedPathname = normalisePathname(pathname);
34
- const canonicalCandidates = sortPatternsBySpecificity(Object.entries(localisedUrls).map(([canonicalPattern, localisedUrlEntry])=>({
35
- pattern: canonicalPattern,
36
- canonicalPattern,
37
- localisedUrlEntry
38
- })));
11
+ const canonicalCandidates = getCanonicalCandidates(localisedUrls);
39
12
  for (const { canonicalPattern, localisedUrlEntry } of canonicalCandidates){
13
+ const targetPattern = 'localised' === options.mode ? localisedUrlEntry[options.targetLanguage] : canonicalPattern;
14
+ if (!targetPattern) continue;
40
15
  const canonicalParams = matchPathPattern(normalizedPathname, canonicalPattern);
41
- if (canonicalParams) return buildPathFromPattern(canonicalPattern, canonicalParams);
42
- for (const language of languages){
16
+ if (canonicalParams) return buildPathFromPattern(targetPattern, canonicalParams);
17
+ if ('canonical' === options.mode) for (const language of languages){
43
18
  const sourcePattern = localisedUrlEntry[language];
44
19
  if (!sourcePattern) continue;
45
20
  const params = matchPathPattern(normalizedPathname, sourcePattern);
46
21
  if (params) return buildPathFromPattern(canonicalPattern, params);
47
22
  }
48
23
  }
24
+ if ('localised' === options.mode) {
25
+ const localisedCandidates = sortPatternsBySpecificity(Object.values(localisedUrls).flatMap((localisedUrlEntry)=>{
26
+ const targetPattern = localisedUrlEntry[options.targetLanguage];
27
+ if (!targetPattern) return [];
28
+ return languages.map((language)=>localisedUrlEntry[language]).filter((sourcePattern)=>Boolean(sourcePattern)).map((sourcePattern)=>({
29
+ pattern: sourcePattern,
30
+ sourcePattern,
31
+ targetPattern
32
+ }));
33
+ }));
34
+ for (const { sourcePattern, targetPattern } of localisedCandidates){
35
+ const params = matchPathPattern(normalizedPathname, sourcePattern);
36
+ if (params) return buildPathFromPattern(targetPattern, params);
37
+ }
38
+ }
49
39
  return normalizedPathname;
50
40
  };
41
+ const resolveLocalisedPath = (pathname, targetLanguage, languages, localisedUrls)=>resolveLocalisedUrlPath(pathname, languages, localisedUrls, {
42
+ mode: 'localised',
43
+ targetLanguage
44
+ });
45
+ const resolveCanonicalLocalisedPath = (pathname, languages, localisedUrls)=>resolveLocalisedUrlPath(pathname, languages, localisedUrls, {
46
+ mode: 'canonical'
47
+ });
51
48
  export { resolveCanonicalLocalisedPath, resolveLocalisedPath };
@@ -1,2 +1,2 @@
1
1
  import "node:module";
2
- export { applyLocalisedUrlsToRoutes, buildPathFromPattern, canonicalTargetPathname, localiseTargetPathname, matchPathPattern, normalisePathPattern, normalisePathname, resolveCanonicalLocalisedPath, resolveLocalisedPath, resolveLocalisedUrlsConfig, validateLocalisedUrls } from "./localisedUrls/index.mjs";
2
+ export { DEFAULT_LOCALE_REDIRECT_SKIP_RULES, applyLocalisedUrlsToRoutes, buildPathFromPattern, canonicalTargetPathname, isDefaultLocaleRedirectSkipPath, localiseTargetPathname, matchPathPattern, matchesPathPrefix, normalisePathPattern, normalisePathname, resolveCanonicalLocalisedPath, resolveLocalisedPath, resolveLocalisedUrlsConfig, shouldSkipLocaleRedirect, stripLanguagePrefix, validateLocalisedUrls } from "./localisedUrls/index.mjs";
@@ -33,4 +33,4 @@ function getBackendOptions(entryName, backend) {
33
33
  if ('backendOptionsByEntry' in fullConfig) return removeEntryConfigKey(fullConfig, 'backendOptionsByEntry');
34
34
  return backend;
35
35
  }
36
- export { getBackendOptions, getEntryConfig, getLocaleDetectionOptions, removeEntryConfigKey };
36
+ export { getBackendOptions, getLocaleDetectionOptions };
@@ -4,7 +4,7 @@ interface NormalizedConfigForLocales {
4
4
  publicDir?: string | string[];
5
5
  };
6
6
  }
7
- export interface DetectedLocalesDirectory {
7
+ interface DetectedLocalesDirectory {
8
8
  loadPath: string;
9
9
  addPath: string;
10
10
  serverLoadPath: string;
@@ -1,7 +1,7 @@
1
1
  import type { FC, ReactNode } from 'react';
2
2
  import type { LocalisedUrlsOption } from '../shared/localisedUrls';
3
3
  import type { I18nInstance } from './i18n';
4
- export interface ModernI18nContextValue {
4
+ interface ModernI18nContextValue {
5
5
  language: string;
6
6
  i18nInstance: I18nInstance;
7
7
  entryName?: string;
@@ -11,12 +11,12 @@ export interface ModernI18nContextValue {
11
11
  localisedUrls?: LocalisedUrlsOption;
12
12
  updateLanguage?: (newLang: string) => void;
13
13
  }
14
- export interface ModernI18nProviderProps {
14
+ interface ModernI18nProviderProps {
15
15
  children: ReactNode;
16
16
  value: ModernI18nContextValue;
17
17
  }
18
18
  export declare const ModernI18nProvider: FC<ModernI18nProviderProps>;
19
- export interface UseModernI18nReturn {
19
+ interface UseModernI18nReturn {
20
20
  language: string;
21
21
  changeLanguage: (newLang: string) => Promise<void>;
22
22
  t: (key: string | string[], ...args: any[]) => string;
@@ -40,3 +40,4 @@ export interface UseModernI18nReturn {
40
40
  * @returns Object containing i18n functionality and utilities
41
41
  */
42
42
  export declare const useModernI18n: () => UseModernI18nReturn;
43
+ export {};
@@ -10,7 +10,7 @@ type LocationLike = {
10
10
  };
11
11
  export declare function getPathLanguage(pathname: string | undefined, languages: string[] | undefined, localePathRedirect: boolean | undefined): string | undefined;
12
12
  export declare function cacheI18nLanguage(i18nInstance: I18nInstance, language: string): void;
13
- export interface ChangeModernI18nLanguageOptions {
13
+ interface ChangeModernI18nLanguageOptions {
14
14
  i18nInstance: I18nInstance;
15
15
  updateLanguage?: (newLang: string) => void;
16
16
  localePathRedirect?: boolean;
@@ -22,7 +22,7 @@ export interface ChangeModernI18nLanguageOptions {
22
22
  location?: LocationLike | null;
23
23
  }
24
24
  export declare function changeModernI18nLanguage(newLang: string, options: ChangeModernI18nLanguageOptions): Promise<void>;
25
- export declare function translateI18n(i18nInstance: I18nInstance, key: string | string[], ...args: any[]): string;
25
+ export declare function translateI18n(i18nInstance: I18nInstance, key: string | string[], ...args: unknown[]): string;
26
26
  export declare function isI18nLanguageSupported(languages: string[] | undefined, lang: string): boolean;
27
27
  export declare function isI18nResourcesReady(i18nInstance: I18nInstance, currentLanguage: string): boolean;
28
28
  export {};
@@ -1,6 +1,7 @@
1
1
  import type { BaseBackendOptions, ChainedBackendConfig } from '../../../shared/type';
2
2
  import type { I18nInstance } from '../instance';
3
3
  type BackendConfigWithChained = BaseBackendOptions & Partial<ChainedBackendConfig>;
4
+ type BackendConstructor = new (...args: never[]) => unknown;
4
5
  /**
5
6
  * Common logic for using i18next backend
6
7
  * This function handles the backend selection and chained backend configuration
@@ -10,5 +11,5 @@ type BackendConfigWithChained = BaseBackendOptions & Partial<ChainedBackendConfi
10
11
  * @param BackendBase - The base backend class (for non-chained use)
11
12
  * @param backend - Optional backend configuration
12
13
  */
13
- export declare function useI18nextBackendCommon(i18nInstance: I18nInstance, BackendWithSave: new (...args: any[]) => any, BackendBase: new (...args: any[]) => any, backend?: BackendConfigWithChained): void;
14
+ export declare function useI18nextBackendCommon(i18nInstance: I18nInstance, BackendWithSave: BackendConstructor, BackendBase: BackendConstructor, backend?: BackendConfigWithChained): void;
14
15
  export {};
@@ -6,7 +6,7 @@ interface DetectorCacheEntry {
6
6
  }
7
7
  export declare const detectorInstanceCache: WeakMap<I18nInstance, DetectorCacheEntry>;
8
8
  export declare const buildDetectorConfigKey: (languages: string[], fallbackLanguage: string, mergedDetection: LanguageDetectorOptions) => string;
9
- export declare const pickSafeDetectionOptions: (userInitOptions?: I18nInitOptions) => Partial<I18nInitOptions> & Record<string, any>;
9
+ export declare const pickSafeDetectionOptions: (userInitOptions?: I18nInitOptions) => Partial<I18nInitOptions> & Record<string, unknown>;
10
10
  export declare const createDetectorInstance: (baseInstance: I18nInstance, configKey: string) => {
11
11
  instance: I18nInstance;
12
12
  isTemporary: boolean;
@@ -1,8 +1,8 @@
1
1
  import type { I18nInstance } from '../instance';
2
- import type { BaseLanguageDetectionOptions } from './types';
2
+ import type { BaseLanguageDetectionOptions, LanguageDetectionSsrContext } from './types';
3
3
  /**
4
4
  * Priority 3: Detect language using i18next detector
5
5
  */
6
6
  export declare const detectLanguageFromI18nextDetector: (i18nInstance: I18nInstance, options: BaseLanguageDetectionOptions & {
7
- ssrContext?: any;
7
+ ssrContext?: LanguageDetectionSsrContext;
8
8
  }) => Promise<string | undefined>;
@@ -1,4 +1,4 @@
1
- import type { I18nInstance } from '../instance';
1
+ import type { I18nInstance, LanguageDetectorOptions } from '../instance';
2
2
  /**
3
3
  * Register LanguageDetector plugin to i18n instance
4
4
  * Must be called before init() to properly register the detector
@@ -9,16 +9,16 @@ export declare const useI18nextLanguageDetector: (i18nInstance: I18nInstance) =>
9
9
  * Read language directly from localStorage/cookie
10
10
  * Fallback when detector is not available in services
11
11
  */
12
- export declare const readLanguageFromStorage: (detectionOptions?: any) => string | undefined;
12
+ export declare const readLanguageFromStorage: (detectionOptions?: LanguageDetectorOptions) => string | undefined;
13
13
  /**
14
14
  * Detect language using i18next-browser-languagedetector
15
15
  * For initialized instances without detector in services, manually create a detector instance
16
16
  * For wrapper instances, access the underlying i18next instance's services
17
17
  */
18
- export declare const detectLanguage: (i18nInstance: I18nInstance, _request?: any, detectionOptions?: any) => string | undefined;
18
+ export declare const detectLanguage: (i18nInstance: I18nInstance, _request?: unknown, detectionOptions?: LanguageDetectorOptions) => string | undefined;
19
19
  /**
20
20
  * Cache user language to localStorage/cookie
21
21
  * Uses LanguageDetector's cacheUserLanguage method when available
22
22
  * For wrapper instances, access the underlying i18next instance's services
23
23
  */
24
- export declare const cacheUserLanguage: (i18nInstance: I18nInstance, language: string, detectionOptions?: any) => void;
24
+ export declare const cacheUserLanguage: (i18nInstance: I18nInstance, language: string, detectionOptions?: unknown) => void;
@@ -1,10 +1,10 @@
1
- import type { I18nInstance } from '../instance';
2
- export declare const cacheUserLanguage: (_i18nInstance: I18nInstance, _language: string, _detectionOptions?: any) => void;
1
+ import type { I18nInstance, LanguageDetectorOptions } from '../instance';
2
+ export declare const cacheUserLanguage: (_i18nInstance: I18nInstance, _language: string, _detectionOptions?: unknown) => void;
3
3
  /**
4
4
  * Read language directly from storage (localStorage/cookie)
5
5
  * Not available in Node.js environment, returns undefined
6
6
  */
7
- export declare const readLanguageFromStorage: (_detectionOptions?: any) => string | undefined;
7
+ export declare const readLanguageFromStorage: (_detectionOptions?: LanguageDetectorOptions) => string | undefined;
8
8
  /**
9
9
  * Register LanguageDetector plugin to i18n instance
10
10
  * Must be called before init() to properly register the detector
@@ -14,4 +14,4 @@ export declare const useI18nextLanguageDetector: (i18nInstance: I18nInstance) =>
14
14
  * Detect language using i18next-http-middleware LanguageDetector
15
15
  * For initialized instances without detector in services, manually create a detector instance
16
16
  */
17
- export declare const detectLanguage: (i18nInstance: I18nInstance, request?: any, detectionOptions?: any) => string | undefined;
17
+ export declare const detectLanguage: (i18nInstance: I18nInstance, request?: unknown, detectionOptions?: LanguageDetectorOptions) => string | undefined;
@@ -1,4 +1,7 @@
1
- import type { I18nInitOptions, LanguageDetectorOptions } from '../instance';
1
+ import type { BackendOptions, I18nInitOptions, LanguageDetectorOptions } from '../instance';
2
+ export interface LanguageDetectionSsrContext {
3
+ request?: unknown;
4
+ }
2
5
  export interface BaseLanguageDetectionOptions {
3
6
  languages: string[];
4
7
  fallbackLanguage: string;
@@ -6,11 +9,11 @@ export interface BaseLanguageDetectionOptions {
6
9
  i18nextDetector: boolean;
7
10
  detection?: LanguageDetectorOptions;
8
11
  userInitOptions?: I18nInitOptions;
9
- mergedBackend?: any;
12
+ mergedBackend?: BackendOptions;
10
13
  }
11
14
  export interface LanguageDetectionOptions extends BaseLanguageDetectionOptions {
12
15
  pathname: string;
13
- ssrContext?: any;
16
+ ssrContext?: LanguageDetectionSsrContext;
14
17
  }
15
18
  export interface LanguageDetectionResult {
16
19
  detectedLanguage?: string;
@@ -24,6 +27,6 @@ export interface BuildInitOptionsParams {
24
27
  fallbackLanguage: string;
25
28
  languages: string[];
26
29
  userInitOptions?: I18nInitOptions;
27
- mergedDetection?: any;
28
- mergeBackend?: any;
30
+ mergedDetection?: LanguageDetectorOptions;
31
+ mergeBackend?: BackendOptions;
29
32
  }