@bleedingdev/modern-js-plugin-i18n 3.5.0-ultramodern.4 → 3.5.0-ultramodern.41

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 (214) hide show
  1. package/dist/cjs/cli/index.js +2 -1
  2. package/dist/cjs/cli/locales.js +15 -15
  3. package/dist/cjs/runtime/Link.js +8 -89
  4. package/dist/cjs/runtime/context.js +18 -82
  5. package/dist/cjs/runtime/contextHelpers.js +137 -0
  6. package/dist/cjs/runtime/core.js +29 -132
  7. package/dist/cjs/runtime/i18n/backend/defaults.node.js +7 -7
  8. package/dist/cjs/runtime/i18n/detection/cache.js +150 -0
  9. package/dist/cjs/runtime/i18n/detection/detector.js +100 -0
  10. package/dist/cjs/runtime/i18n/detection/index.js +8 -263
  11. package/dist/cjs/runtime/i18n/detection/initOptions.js +69 -0
  12. package/dist/cjs/runtime/i18n/detection/language.js +62 -0
  13. package/dist/cjs/runtime/i18n/detection/path.js +49 -0
  14. package/dist/cjs/runtime/i18n/detection/priority.js +66 -0
  15. package/dist/cjs/runtime/i18n/detection/ssr.js +70 -0
  16. package/dist/cjs/runtime/i18n/detection/types.js +18 -0
  17. package/dist/cjs/runtime/i18n/instance.js +7 -7
  18. package/dist/cjs/runtime/linkHelpers.js +148 -0
  19. package/dist/cjs/runtime/pluginSetup.js +102 -0
  20. package/dist/cjs/runtime/providerComposition.js +114 -0
  21. package/dist/cjs/runtime/reactI18next.js +43 -0
  22. package/dist/cjs/runtime/utils.js +1 -9
  23. package/dist/cjs/server/apiPrefix.js +72 -0
  24. package/dist/cjs/server/detectorOptions.js +97 -0
  25. package/dist/cjs/server/index.js +17 -144
  26. package/dist/cjs/server/redirectPolicy.js +96 -0
  27. package/dist/cjs/shared/detection.js +2 -104
  28. package/dist/cjs/shared/localisedUrls/config.js +49 -0
  29. package/dist/cjs/shared/localisedUrls/index.js +91 -0
  30. package/dist/cjs/shared/localisedUrls/normalise.js +87 -0
  31. package/dist/cjs/shared/localisedUrls/pathname.js +68 -0
  32. package/dist/cjs/shared/localisedUrls/patterns.js +130 -0
  33. package/dist/cjs/shared/localisedUrls/redirect.js +95 -0
  34. package/dist/cjs/shared/localisedUrls/resolve.js +88 -0
  35. package/dist/cjs/shared/localisedUrls/routes.js +129 -0
  36. package/dist/cjs/shared/localisedUrls/types.js +18 -0
  37. package/dist/cjs/shared/localisedUrls.js +28 -294
  38. package/dist/cjs/shared/utils.js +2 -8
  39. package/dist/esm/cli/index.mjs +2 -1
  40. package/dist/esm/cli/locales.mjs +13 -13
  41. package/dist/esm/runtime/Link.mjs +1 -82
  42. package/dist/esm/runtime/context.mjs +18 -82
  43. package/dist/esm/runtime/contextHelpers.mjs +84 -0
  44. package/dist/esm/runtime/core.mjs +30 -133
  45. package/dist/esm/runtime/i18n/backend/defaults.node.mjs +5 -5
  46. package/dist/esm/runtime/i18n/detection/cache.mjs +103 -0
  47. package/dist/esm/runtime/i18n/detection/detector.mjs +62 -0
  48. package/dist/esm/runtime/i18n/detection/index.mjs +4 -260
  49. package/dist/esm/runtime/i18n/detection/initOptions.mjs +28 -0
  50. package/dist/esm/runtime/i18n/detection/language.mjs +21 -0
  51. package/dist/esm/runtime/i18n/detection/path.mjs +11 -0
  52. package/dist/esm/runtime/i18n/detection/priority.mjs +28 -0
  53. package/dist/esm/runtime/i18n/detection/ssr.mjs +26 -0
  54. package/dist/esm/runtime/i18n/detection/types.mjs +0 -0
  55. package/dist/esm/runtime/i18n/instance.mjs +8 -5
  56. package/dist/esm/runtime/linkHelpers.mjs +98 -0
  57. package/dist/esm/runtime/pluginSetup.mjs +64 -0
  58. package/dist/esm/runtime/providerComposition.mjs +76 -0
  59. package/dist/esm/runtime/reactI18next.mjs +5 -0
  60. package/dist/esm/runtime/utils.mjs +2 -10
  61. package/dist/esm/server/apiPrefix.mjs +31 -0
  62. package/dist/esm/server/detectorOptions.mjs +59 -0
  63. package/dist/esm/server/index.mjs +8 -135
  64. package/dist/esm/server/redirectPolicy.mjs +46 -0
  65. package/dist/esm/shared/detection.mjs +1 -71
  66. package/dist/esm/shared/localisedUrls/config.mjs +11 -0
  67. package/dist/esm/shared/localisedUrls/index.mjs +7 -0
  68. package/dist/esm/shared/localisedUrls/normalise.mjs +37 -0
  69. package/dist/esm/shared/localisedUrls/pathname.mjs +24 -0
  70. package/dist/esm/shared/localisedUrls/patterns.mjs +86 -0
  71. package/dist/esm/shared/localisedUrls/redirect.mjs +48 -0
  72. package/dist/esm/shared/localisedUrls/resolve.mjs +47 -0
  73. package/dist/esm/shared/localisedUrls/routes.mjs +88 -0
  74. package/dist/esm/shared/localisedUrls/types.mjs +0 -0
  75. package/dist/esm/shared/localisedUrls.mjs +1 -283
  76. package/dist/esm/shared/utils.mjs +1 -1
  77. package/dist/esm-node/cli/index.mjs +2 -1
  78. package/dist/esm-node/cli/locales.mjs +13 -13
  79. package/dist/esm-node/runtime/Link.mjs +1 -82
  80. package/dist/esm-node/runtime/context.mjs +18 -82
  81. package/dist/esm-node/runtime/contextHelpers.mjs +85 -0
  82. package/dist/esm-node/runtime/core.mjs +30 -133
  83. package/dist/esm-node/runtime/i18n/backend/defaults.node.mjs +5 -5
  84. package/dist/esm-node/runtime/i18n/detection/cache.mjs +104 -0
  85. package/dist/esm-node/runtime/i18n/detection/detector.mjs +63 -0
  86. package/dist/esm-node/runtime/i18n/detection/index.mjs +4 -260
  87. package/dist/esm-node/runtime/i18n/detection/initOptions.mjs +29 -0
  88. package/dist/esm-node/runtime/i18n/detection/language.mjs +22 -0
  89. package/dist/esm-node/runtime/i18n/detection/path.mjs +12 -0
  90. package/dist/esm-node/runtime/i18n/detection/priority.mjs +29 -0
  91. package/dist/esm-node/runtime/i18n/detection/ssr.mjs +27 -0
  92. package/dist/esm-node/runtime/i18n/detection/types.mjs +1 -0
  93. package/dist/esm-node/runtime/i18n/instance.mjs +8 -5
  94. package/dist/esm-node/runtime/linkHelpers.mjs +99 -0
  95. package/dist/esm-node/runtime/pluginSetup.mjs +65 -0
  96. package/dist/esm-node/runtime/providerComposition.mjs +77 -0
  97. package/dist/esm-node/runtime/reactI18next.mjs +6 -0
  98. package/dist/esm-node/runtime/utils.mjs +2 -10
  99. package/dist/esm-node/server/apiPrefix.mjs +32 -0
  100. package/dist/esm-node/server/detectorOptions.mjs +60 -0
  101. package/dist/esm-node/server/index.mjs +8 -135
  102. package/dist/esm-node/server/redirectPolicy.mjs +47 -0
  103. package/dist/esm-node/shared/detection.mjs +1 -71
  104. package/dist/esm-node/shared/localisedUrls/config.mjs +12 -0
  105. package/dist/esm-node/shared/localisedUrls/index.mjs +8 -0
  106. package/dist/esm-node/shared/localisedUrls/normalise.mjs +38 -0
  107. package/dist/esm-node/shared/localisedUrls/pathname.mjs +25 -0
  108. package/dist/esm-node/shared/localisedUrls/patterns.mjs +87 -0
  109. package/dist/esm-node/shared/localisedUrls/redirect.mjs +49 -0
  110. package/dist/esm-node/shared/localisedUrls/resolve.mjs +48 -0
  111. package/dist/esm-node/shared/localisedUrls/routes.mjs +89 -0
  112. package/dist/esm-node/shared/localisedUrls/types.mjs +1 -0
  113. package/dist/esm-node/shared/localisedUrls.mjs +1 -283
  114. package/dist/esm-node/shared/utils.mjs +1 -1
  115. package/dist/types/cli/locales.d.ts +1 -1
  116. package/dist/types/runtime/Link.d.ts +3 -7
  117. package/dist/types/runtime/context.d.ts +6 -5
  118. package/dist/types/runtime/contextHelpers.d.ts +28 -0
  119. package/dist/types/runtime/core.d.ts +3 -7
  120. package/dist/types/runtime/hooks.d.ts +5 -0
  121. package/dist/types/runtime/i18n/backend/middleware.common.d.ts +2 -1
  122. package/dist/types/runtime/i18n/detection/cache.d.ts +14 -0
  123. package/dist/types/runtime/i18n/detection/detector.d.ts +8 -0
  124. package/dist/types/runtime/i18n/detection/index.d.ts +5 -50
  125. package/dist/types/runtime/i18n/detection/initOptions.d.ts +10 -0
  126. package/dist/types/runtime/i18n/detection/language.d.ts +11 -0
  127. package/dist/types/runtime/i18n/detection/middleware.d.ts +4 -4
  128. package/dist/types/runtime/i18n/detection/middleware.node.d.ts +4 -4
  129. package/dist/types/runtime/i18n/detection/path.d.ts +5 -0
  130. package/dist/types/runtime/i18n/detection/priority.d.ts +10 -0
  131. package/dist/types/runtime/i18n/detection/ssr.d.ts +9 -0
  132. package/dist/types/runtime/i18n/detection/types.d.ts +32 -0
  133. package/dist/types/runtime/i18n/instance.d.ts +28 -26
  134. package/dist/types/runtime/i18n/react-i18next.d.ts +1 -6
  135. package/dist/types/runtime/i18n/utils.d.ts +14 -5
  136. package/dist/types/runtime/linkHelpers.d.ts +17 -0
  137. package/dist/types/runtime/pluginSetup.d.ts +32 -0
  138. package/dist/types/runtime/providerComposition.d.ts +16 -0
  139. package/dist/types/runtime/reactI18next.d.ts +7 -0
  140. package/dist/types/runtime/routerAdapter.d.ts +7 -6
  141. package/dist/types/runtime/utils.d.ts +7 -2
  142. package/dist/types/server/apiPrefix.d.ts +7 -0
  143. package/dist/types/server/detectorOptions.d.ts +21 -0
  144. package/dist/types/server/index.d.ts +1 -6
  145. package/dist/types/server/redirectPolicy.d.ts +22 -0
  146. package/dist/types/shared/detection.d.ts +1 -11
  147. package/dist/types/shared/localisedUrls/config.d.ts +9 -0
  148. package/dist/types/shared/localisedUrls/index.d.ts +9 -0
  149. package/dist/types/shared/localisedUrls/normalise.d.ts +10 -0
  150. package/dist/types/shared/localisedUrls/pathname.d.ts +4 -0
  151. package/dist/types/shared/localisedUrls/patterns.d.ts +5 -0
  152. package/dist/types/shared/localisedUrls/redirect.d.ts +11 -0
  153. package/dist/types/shared/localisedUrls/resolve.d.ts +7 -0
  154. package/dist/types/shared/localisedUrls/routes.d.ts +3 -0
  155. package/dist/types/shared/localisedUrls/types.d.ts +14 -0
  156. package/dist/types/shared/localisedUrls.d.ts +2 -36
  157. package/dist/types/shared/utils.d.ts +0 -2
  158. package/package.json +16 -18
  159. package/rstest.config.mts +1 -0
  160. package/src/cli/index.ts +5 -4
  161. package/src/cli/locales.ts +3 -3
  162. package/src/runtime/Link.tsx +11 -138
  163. package/src/runtime/context.tsx +45 -199
  164. package/src/runtime/contextHelpers.ts +232 -0
  165. package/src/runtime/core.tsx +43 -274
  166. package/src/runtime/hooks.ts +13 -2
  167. package/src/runtime/i18n/backend/defaults.node.ts +2 -2
  168. package/src/runtime/i18n/backend/middleware.common.ts +5 -4
  169. package/src/runtime/i18n/detection/cache.ts +189 -0
  170. package/src/runtime/i18n/detection/detector.ts +166 -0
  171. package/src/runtime/i18n/detection/index.ts +10 -641
  172. package/src/runtime/i18n/detection/initOptions.ts +74 -0
  173. package/src/runtime/i18n/detection/language.ts +64 -0
  174. package/src/runtime/i18n/detection/middleware.node.ts +26 -7
  175. package/src/runtime/i18n/detection/middleware.ts +15 -7
  176. package/src/runtime/i18n/detection/path.ts +41 -0
  177. package/src/runtime/i18n/detection/priority.ts +82 -0
  178. package/src/runtime/i18n/detection/ssr.ts +47 -0
  179. package/src/runtime/i18n/detection/types.ts +41 -0
  180. package/src/runtime/i18n/instance.ts +56 -31
  181. package/src/runtime/i18n/react-i18next.ts +1 -6
  182. package/src/runtime/i18n/utils.ts +18 -9
  183. package/src/runtime/linkHelpers.ts +174 -0
  184. package/src/runtime/pluginSetup.ts +189 -0
  185. package/src/runtime/providerComposition.tsx +144 -0
  186. package/src/runtime/reactI18next.ts +20 -0
  187. package/src/runtime/routerAdapter.tsx +6 -6
  188. package/src/runtime/utils.ts +13 -35
  189. package/src/server/apiPrefix.ts +67 -0
  190. package/src/server/detectorOptions.ts +105 -0
  191. package/src/server/index.ts +14 -319
  192. package/src/server/redirectPolicy.ts +127 -0
  193. package/src/shared/detection.ts +1 -1
  194. package/src/shared/localisedUrls/config.ts +18 -0
  195. package/src/shared/localisedUrls/index.ts +24 -0
  196. package/src/shared/localisedUrls/normalise.ts +67 -0
  197. package/src/shared/localisedUrls/pathname.ts +60 -0
  198. package/src/shared/localisedUrls/patterns.ts +160 -0
  199. package/src/shared/localisedUrls/redirect.ts +91 -0
  200. package/src/shared/localisedUrls/resolve.ts +135 -0
  201. package/src/shared/localisedUrls/routes.ts +226 -0
  202. package/src/shared/localisedUrls/types.ts +18 -0
  203. package/src/shared/localisedUrls.ts +26 -623
  204. package/src/shared/utils.ts +2 -2
  205. package/tests/fixtures/localised-routes.golden.json +156 -0
  206. package/tests/link.test.tsx +111 -0
  207. package/tests/linkTypes.test.ts +15 -4
  208. package/tests/localisedUrlRewriteMatrix.fork.test.ts +147 -0
  209. package/tests/localisedUrls.test.ts +129 -2
  210. package/tests/reactI18nextRuntimeBoundary.test.ts +6 -3
  211. package/tests/redirectPolicy.test.ts +43 -0
  212. package/tests/routerAdapter.test.tsx +85 -0
  213. package/tests/type-fixture/fixture-globals.d.ts +11 -0
  214. package/tests/type-fixture/tsconfig.json +1 -0
@@ -1,6 +1,6 @@
1
1
  import { isBrowser } from "@modern-js/runtime";
2
2
  import { getGlobalBasename } from "@modern-js/runtime/context";
3
- import { localiseTargetPathname } from "../shared/localisedUrls.mjs";
3
+ import { localiseTargetPathname, shouldSkipLocaleRedirect } from "../shared/localisedUrls.mjs";
4
4
  const getPathname = (context)=>{
5
5
  if (isBrowser()) return window.location.pathname;
6
6
  return context.ssrContext?.request?.pathname || '/';
@@ -51,13 +51,5 @@ const detectLanguageFromPath = (pathname, languages, localePathRedirect)=>{
51
51
  detected: false
52
52
  };
53
53
  };
54
- const shouldIgnoreRedirect = (pathname, languages, ignoreRedirectRoutes)=>{
55
- if (!ignoreRedirectRoutes) return false;
56
- const segments = pathname.split('/').filter(Boolean);
57
- let pathWithoutLang = pathname;
58
- if (segments.length > 0 && languages.includes(segments[0])) pathWithoutLang = `/${segments.slice(1).join('/')}`;
59
- const normalizedPath = pathWithoutLang.startsWith('/') ? pathWithoutLang : `/${pathWithoutLang}`;
60
- if ('function' == typeof ignoreRedirectRoutes) return ignoreRedirectRoutes(normalizedPath);
61
- return ignoreRedirectRoutes.some((pattern)=>normalizedPath === pattern || normalizedPath.startsWith(`${pattern}/`));
62
- };
54
+ const shouldIgnoreRedirect = (pathname, languages, ignoreRedirectRoutes)=>shouldSkipLocaleRedirect(pathname, languages, ignoreRedirectRoutes);
63
55
  export { buildLocalizedUrl, detectLanguageFromPath, getEntryPath, getLanguageFromPath, getPathname, shouldIgnoreRedirect, splitUrlTarget };
@@ -0,0 +1,31 @@
1
+ const normalizeApiPrefix = (prefix)=>{
2
+ const trimmedPrefix = prefix.trim();
3
+ if (!trimmedPrefix) return null;
4
+ const prefixedPath = trimmedPrefix.startsWith('/') ? trimmedPrefix : `/${trimmedPrefix}`;
5
+ const withoutWildcard = prefixedPath.replace(/\/\*$/, '');
6
+ const normalizedPrefix = withoutWildcard.length > 1 ? withoutWildcard.replace(/\/+$/, '') : withoutWildcard;
7
+ return '/' === normalizedPrefix ? null : normalizedPrefix;
8
+ };
9
+ const collectApiPrefixes = (routes, bffPrefix)=>{
10
+ const prefixes = new Set();
11
+ for (const route of routes){
12
+ if (!route.isApi || !route.urlPath) continue;
13
+ const normalizedPrefix = normalizeApiPrefix(route.urlPath);
14
+ if (normalizedPrefix) prefixes.add(normalizedPrefix);
15
+ }
16
+ const bffPrefixes = Array.isArray(bffPrefix) ? bffPrefix : bffPrefix ? [
17
+ bffPrefix
18
+ ] : [];
19
+ for (const prefix of bffPrefixes){
20
+ const normalizedPrefix = normalizeApiPrefix(prefix);
21
+ if (normalizedPrefix) prefixes.add(normalizedPrefix);
22
+ }
23
+ return [
24
+ ...prefixes
25
+ ];
26
+ };
27
+ const matchesApiPrefix = (pathname, apiPrefixes)=>{
28
+ const normalizedPathname = pathname.startsWith('/') ? pathname : `/${pathname}`;
29
+ return apiPrefixes.some((prefix)=>normalizedPathname === prefix || normalizedPathname.startsWith(`${prefix}/`));
30
+ };
31
+ export { collectApiPrefixes, matchesApiPrefix };
@@ -0,0 +1,59 @@
1
+ import { DEFAULT_I18NEXT_DETECTION_OPTIONS, mergeDetectionOptions } from "../runtime/i18n/detection/config.mjs";
2
+ const convertToHonoLanguageDetectorOptions = (languages, fallbackLanguage, detectionOptions)=>{
3
+ const mergedDetection = detectionOptions ? mergeDetectionOptions(detectionOptions) : DEFAULT_I18NEXT_DETECTION_OPTIONS;
4
+ const order = (mergedDetection.order || []).filter((item)=>![
5
+ 'path',
6
+ 'localStorage',
7
+ 'navigator',
8
+ 'htmlTag',
9
+ 'subdomain'
10
+ ].includes(item));
11
+ const detectionOrder = order.length > 0 ? order : [
12
+ 'querystring',
13
+ 'cookie',
14
+ 'header'
15
+ ];
16
+ const honoOrder = detectionOrder.map((item)=>{
17
+ if ('querystring' === item) return 'querystring';
18
+ if ('cookie' === item) return 'cookie';
19
+ if ('header' === item) return 'header';
20
+ return item;
21
+ });
22
+ const caches = false === mergedDetection.caches ? false : Array.isArray(mergedDetection.caches) && !mergedDetection.caches.includes('cookie') ? false : [
23
+ 'cookie'
24
+ ];
25
+ const cookieMinutes = mergedDetection.cookieMinutes;
26
+ const cookieMaxAge = 'number' == typeof cookieMinutes && Number.isFinite(cookieMinutes) ? Math.max(0, Math.floor(60 * cookieMinutes)) : 60 * DEFAULT_I18NEXT_DETECTION_OPTIONS.cookieMinutes;
27
+ const cookieDomain = mergedDetection.cookieDomain;
28
+ const cookieSecure = mergedDetection.cookieSecure;
29
+ const cookieHttpOnly = mergedDetection.cookieHttpOnly;
30
+ const cookieSameSite = mergedDetection.cookieSameSite;
31
+ const normalizedCookieDomain = 'string' == typeof cookieDomain ? cookieDomain : void 0;
32
+ const cookieOptions = {
33
+ maxAge: cookieMaxAge,
34
+ sameSite: 'None' === cookieSameSite || 'none' === cookieSameSite ? 'None' : 'Lax' === cookieSameSite || 'lax' === cookieSameSite ? 'Lax' : 'Strict',
35
+ secure: 'boolean' == typeof cookieSecure ? cookieSecure : false,
36
+ httpOnly: 'boolean' == typeof cookieHttpOnly ? cookieHttpOnly : false,
37
+ ...normalizedCookieDomain ? {
38
+ domain: normalizedCookieDomain
39
+ } : {}
40
+ };
41
+ return {
42
+ supportedLanguages: languages.length > 0 ? languages : [
43
+ fallbackLanguage
44
+ ],
45
+ fallbackLanguage,
46
+ order: honoOrder,
47
+ lookupQueryString: mergedDetection.lookupQuerystring || DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupQuerystring || 'lng',
48
+ lookupCookie: mergedDetection.lookupCookie || DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupCookie || 'i18next',
49
+ lookupFromHeaderKey: mergedDetection.lookupHeader || DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupHeader || 'accept-language',
50
+ ...void 0 !== caches && {
51
+ caches
52
+ },
53
+ ...false !== caches && {
54
+ cookieOptions
55
+ },
56
+ ignoreCase: true
57
+ };
58
+ };
59
+ export { convertToHonoLanguageDetectorOptions };
@@ -1,137 +1,10 @@
1
- import { DEFAULT_I18NEXT_DETECTION_OPTIONS, mergeDetectionOptions } from "../runtime/i18n/detection/config.mjs";
2
- import { localiseTargetPathname, resolveLocalisedUrlsConfig } from "../shared/localisedUrls.mjs";
1
+ import { resolveLocalisedUrlsConfig } from "../shared/localisedUrls.mjs";
3
2
  import { getLocaleDetectionOptions } from "../shared/utils.mjs";
4
- import * as __rspack_external__modern_js_server_core_hono_a76ca254 from "@modern-js/server-core/hono";
5
- const { languageDetector: languageDetector } = __rspack_external__modern_js_server_core_hono_a76ca254;
6
- const normalizeApiPrefix = (prefix)=>{
7
- const trimmedPrefix = prefix.trim();
8
- if (!trimmedPrefix) return null;
9
- const prefixedPath = trimmedPrefix.startsWith('/') ? trimmedPrefix : `/${trimmedPrefix}`;
10
- const withoutWildcard = prefixedPath.replace(/\/\*$/, '');
11
- const normalizedPrefix = withoutWildcard.length > 1 ? withoutWildcard.replace(/\/+$/, '') : withoutWildcard;
12
- return '/' === normalizedPrefix ? null : normalizedPrefix;
13
- };
14
- const collectApiPrefixes = (routes, bffPrefix)=>{
15
- const prefixes = new Set();
16
- for (const route of routes){
17
- if (!route.isApi || !route.urlPath) continue;
18
- const normalizedPrefix = normalizeApiPrefix(route.urlPath);
19
- if (normalizedPrefix) prefixes.add(normalizedPrefix);
20
- }
21
- const bffPrefixes = Array.isArray(bffPrefix) ? bffPrefix : bffPrefix ? [
22
- bffPrefix
23
- ] : [];
24
- for (const prefix of bffPrefixes){
25
- const normalizedPrefix = normalizeApiPrefix(prefix);
26
- if (normalizedPrefix) prefixes.add(normalizedPrefix);
27
- }
28
- return [
29
- ...prefixes
30
- ];
31
- };
32
- const matchesApiPrefix = (pathname, apiPrefixes)=>{
33
- const normalizedPathname = pathname.startsWith('/') ? pathname : `/${pathname}`;
34
- return apiPrefixes.some((prefix)=>normalizedPathname === prefix || normalizedPathname.startsWith(`${prefix}/`));
35
- };
36
- const convertToHonoLanguageDetectorOptions = (languages, fallbackLanguage, detectionOptions)=>{
37
- const mergedDetection = detectionOptions ? mergeDetectionOptions(detectionOptions) : DEFAULT_I18NEXT_DETECTION_OPTIONS;
38
- const order = (mergedDetection.order || []).filter((item)=>![
39
- 'path',
40
- 'localStorage',
41
- 'navigator',
42
- 'htmlTag',
43
- 'subdomain'
44
- ].includes(item));
45
- const detectionOrder = order.length > 0 ? order : [
46
- 'querystring',
47
- 'cookie',
48
- 'header'
49
- ];
50
- const honoOrder = detectionOrder.map((item)=>{
51
- if ('querystring' === item) return 'querystring';
52
- if ('cookie' === item) return 'cookie';
53
- if ('header' === item) return 'header';
54
- return item;
55
- });
56
- const caches = false === mergedDetection.caches ? false : Array.isArray(mergedDetection.caches) && !mergedDetection.caches.includes('cookie') ? false : [
57
- 'cookie'
58
- ];
59
- const cookieMinutes = mergedDetection.cookieMinutes;
60
- const cookieMaxAge = 'number' == typeof cookieMinutes && Number.isFinite(cookieMinutes) ? Math.max(0, Math.floor(60 * cookieMinutes)) : 60 * DEFAULT_I18NEXT_DETECTION_OPTIONS.cookieMinutes;
61
- const cookieDomain = mergedDetection.cookieDomain;
62
- const cookieSecure = mergedDetection.cookieSecure;
63
- const cookieHttpOnly = mergedDetection.cookieHttpOnly;
64
- const cookieSameSite = mergedDetection.cookieSameSite;
65
- const normalizedCookieDomain = 'string' == typeof cookieDomain ? cookieDomain : void 0;
66
- const cookieOptions = {
67
- maxAge: cookieMaxAge,
68
- sameSite: 'None' === cookieSameSite || 'none' === cookieSameSite ? 'None' : 'Lax' === cookieSameSite || 'lax' === cookieSameSite ? 'Lax' : 'Strict',
69
- secure: 'boolean' == typeof cookieSecure ? cookieSecure : false,
70
- httpOnly: 'boolean' == typeof cookieHttpOnly ? cookieHttpOnly : false,
71
- ...normalizedCookieDomain ? {
72
- domain: normalizedCookieDomain
73
- } : {}
74
- };
75
- return {
76
- supportedLanguages: languages.length > 0 ? languages : [
77
- fallbackLanguage
78
- ],
79
- fallbackLanguage,
80
- order: honoOrder,
81
- lookupQueryString: mergedDetection.lookupQuerystring || DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupQuerystring || 'lng',
82
- lookupCookie: mergedDetection.lookupCookie || DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupCookie || 'i18next',
83
- lookupFromHeaderKey: mergedDetection.lookupHeader || DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupHeader || 'accept-language',
84
- ...void 0 !== caches && {
85
- caches
86
- },
87
- ...false !== caches && {
88
- cookieOptions
89
- },
90
- ignoreCase: true
91
- };
92
- };
93
- const shouldIgnoreRedirect = (pathname, urlPath, ignoreRedirectRoutes)=>{
94
- if (!ignoreRedirectRoutes) return false;
95
- const basePath = urlPath.replace('/*', '');
96
- const remainingPath = pathname.startsWith(basePath) ? pathname.slice(basePath.length) : pathname;
97
- const normalizedPath = remainingPath.startsWith('/') ? remainingPath : `/${remainingPath}`;
98
- if ('function' == typeof ignoreRedirectRoutes) return ignoreRedirectRoutes(normalizedPath);
99
- return ignoreRedirectRoutes.some((pattern)=>normalizedPath === pattern || normalizedPath.startsWith(`${pattern}/`));
100
- };
101
- const isStaticResourceRequest = (pathname, staticRoutePrefixes, languages = [])=>{
102
- if (staticRoutePrefixes.some((prefix)=>pathname.startsWith(`${prefix}/`) || pathname === prefix)) return true;
103
- const standardStaticPrefixes = [
104
- '/static/',
105
- '/upload/'
106
- ];
107
- if (standardStaticPrefixes.some((prefix)=>pathname.startsWith(prefix))) return true;
108
- const pathSegments = pathname.split('/').filter(Boolean);
109
- if (pathSegments.length > 0 && languages.includes(pathSegments[0])) {
110
- const pathWithoutLang = '/' + pathSegments.slice(1).join('/');
111
- if (standardStaticPrefixes.some((prefix)=>pathWithoutLang.startsWith(prefix)) || staticRoutePrefixes.some((prefix)=>pathWithoutLang.startsWith(`${prefix}/`) || pathWithoutLang === prefix)) return true;
112
- }
113
- return false;
114
- };
115
- const getLanguageFromPath = (req, urlPath, languages)=>{
116
- const url = new URL(req.url, `http://${req.header().host}`);
117
- const pathname = url.pathname;
118
- const basePath = urlPath.replace('/*', '');
119
- const remainingPath = pathname.startsWith(basePath) ? pathname.slice(basePath.length) : pathname;
120
- const segments = remainingPath.split('/').filter(Boolean);
121
- const firstSegment = segments[0];
122
- if (languages.includes(firstSegment)) return firstSegment;
123
- return null;
124
- };
125
- const buildLocalizedUrl = (req, urlPath, language, languages, localisedUrls)=>{
126
- const url = new URL(req.url);
127
- const pathname = url.pathname;
128
- const basePath = urlPath.replace('/*', '');
129
- const remainingPath = pathname.startsWith(basePath) ? pathname.slice(basePath.length) : pathname;
130
- const newPathname = localiseTargetPathname(remainingPath, language, languages, localisedUrls);
131
- const suffix = `${url.search}${url.hash}`;
132
- const localizedUrl = '/' === basePath ? newPathname + suffix : basePath + newPathname + suffix;
133
- return localizedUrl;
134
- };
3
+ import { collectApiPrefixes, matchesApiPrefix } from "./apiPrefix.mjs";
4
+ import { convertToHonoLanguageDetectorOptions } from "./detectorOptions.mjs";
5
+ import { buildLocalizedUrl, createLocaleRedirectResponse, getLanguageFromPath, isStaticResourceRequest, shouldIgnoreRedirect } from "./redirectPolicy.mjs";
6
+ import * as __rspack_external__modern_js_server_core_hono_0af3baa7 from "@modern-js/server-core/hono";
7
+ const { languageDetector: languageDetector } = __rspack_external__modern_js_server_core_hono_0af3baa7;
135
8
  const i18nServerPlugin = (options)=>({
136
9
  name: '@modern-js/plugin-i18n/server',
137
10
  setup: (api)=>{
@@ -200,12 +73,12 @@ const i18nServerPlugin = (options)=>({
200
73
  if (i18nextDetector) detectedLanguage = c.get('language') || null;
201
74
  const targetLanguage = detectedLanguage || fallbackLanguage;
202
75
  const localizedUrl = buildLocalizedUrl(c.req, originUrlPath, targetLanguage, languages, localisedUrls);
203
- return c.redirect(localizedUrl);
76
+ return createLocaleRedirectResponse(localizedUrl);
204
77
  }
205
78
  const localisedUrlsConfig = resolveLocalisedUrlsConfig(localisedUrls);
206
79
  if (localisedUrlsConfig.enabled) {
207
80
  const expectedUrl = buildLocalizedUrl(c.req, originUrlPath, language, languages, localisedUrls);
208
- if (expectedUrl !== `${pathname}${url.search}${url.hash}`) return c.redirect(expectedUrl);
81
+ if (expectedUrl !== `${pathname}${url.search}${url.hash}`) return createLocaleRedirectResponse(expectedUrl);
209
82
  }
210
83
  await next();
211
84
  }
@@ -0,0 +1,46 @@
1
+ import { isDefaultLocaleRedirectSkipPath, localiseTargetPathname, matchesPathPrefix, shouldSkipLocaleRedirect, stripLanguagePrefix } from "../shared/localisedUrls.mjs";
2
+ const stripUrlPathPrefix = (pathname, urlPath)=>{
3
+ const basePath = urlPath.replace('/*', '');
4
+ if (!basePath || '/' === basePath) return pathname;
5
+ const remainingPath = pathname.startsWith(basePath) ? pathname.slice(basePath.length) : pathname;
6
+ return remainingPath || '/';
7
+ };
8
+ const shouldIgnoreRedirect = (pathname, urlPath, ignoreRedirectRoutes)=>{
9
+ const remainingPath = stripUrlPathPrefix(pathname, urlPath);
10
+ return shouldSkipLocaleRedirect(remainingPath, [], ignoreRedirectRoutes);
11
+ };
12
+ const isStaticResourceRequest = (pathname, staticRoutePrefixes, languages = [])=>{
13
+ if (isDefaultLocaleRedirectSkipPath(pathname, languages)) return true;
14
+ const matchesStaticRoutePrefix = (targetPathname)=>staticRoutePrefixes.some((prefix)=>matchesPathPrefix(targetPathname, prefix));
15
+ if (matchesStaticRoutePrefix(pathname)) return true;
16
+ const pathWithoutLanguage = stripLanguagePrefix(pathname, languages);
17
+ return pathWithoutLanguage !== pathname && matchesStaticRoutePrefix(pathWithoutLanguage);
18
+ };
19
+ const getLanguageFromPath = (req, urlPath, languages)=>{
20
+ const url = new URL(req.url, `http://${req.header().host}`);
21
+ const pathname = url.pathname;
22
+ const remainingPath = stripUrlPathPrefix(pathname, urlPath);
23
+ const segments = remainingPath.split('/').filter(Boolean);
24
+ const firstSegment = segments[0];
25
+ if (languages.includes(firstSegment)) return firstSegment;
26
+ return null;
27
+ };
28
+ const buildLocalizedUrl = (req, urlPath, language, languages, localisedUrls)=>{
29
+ const url = new URL(req.url);
30
+ const pathname = url.pathname;
31
+ const basePath = urlPath.replace('/*', '');
32
+ const remainingPath = stripUrlPathPrefix(pathname, urlPath);
33
+ const newPathname = localiseTargetPathname(remainingPath, language, languages, localisedUrls);
34
+ const suffix = `${url.search}${url.hash}`;
35
+ const localizedUrl = '/' === basePath ? newPathname + suffix : basePath + newPathname + suffix;
36
+ return localizedUrl;
37
+ };
38
+ const createLocaleRedirectResponse = (location)=>new Response(null, {
39
+ status: 302,
40
+ headers: {
41
+ 'Cache-Control': 'private, no-store',
42
+ Location: location,
43
+ Vary: 'Accept-Language, Cookie'
44
+ }
45
+ });
46
+ export { buildLocalizedUrl, createLocaleRedirectResponse, getLanguageFromPath, isStaticResourceRequest, shouldIgnoreRedirect };
@@ -1,71 +1 @@
1
- import { DEFAULT_I18NEXT_DETECTION_OPTIONS, mergeDetectionOptions } from "../runtime/i18n/detection/config.mjs";
2
- function detectLanguageFromRequest(req, languages, detectionOptions) {
3
- try {
4
- const mergedDetection = detectionOptions ? mergeDetectionOptions(detectionOptions) : DEFAULT_I18NEXT_DETECTION_OPTIONS;
5
- const order = (mergedDetection.order || []).filter((item)=>![
6
- 'path',
7
- 'localStorage',
8
- 'navigator',
9
- 'htmlTag',
10
- 'subdomain'
11
- ].includes(item));
12
- const detectionOrder = order.length > 0 ? order : [
13
- 'querystring',
14
- 'cookie',
15
- 'header'
16
- ];
17
- const getHeader = (name)=>{
18
- req.headers, Headers;
19
- return req.headers.get(name);
20
- };
21
- for (const method of detectionOrder){
22
- let detectedLang = null;
23
- switch(method){
24
- case 'querystring':
25
- {
26
- const lookupKey = mergedDetection.lookupQuerystring || DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupQuerystring || 'lng';
27
- const host = getHeader('host') || 'localhost';
28
- const url = new URL(req.url, `http://${host}`);
29
- detectedLang = url.searchParams.get(lookupKey);
30
- break;
31
- }
32
- case 'cookie':
33
- {
34
- const lookupKey = mergedDetection.lookupCookie || DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupCookie || 'i18next';
35
- const cookieHeader = getHeader('Cookie');
36
- if (cookieHeader) {
37
- const cookies = cookieHeader.split(';').reduce((acc, item)=>{
38
- const [key, value] = item.trim().split('=');
39
- if (key && value) acc[key] = value;
40
- return acc;
41
- }, {});
42
- detectedLang = cookies[lookupKey] || null;
43
- }
44
- break;
45
- }
46
- case 'header':
47
- {
48
- const lookupKey = mergedDetection.lookupHeader || DEFAULT_I18NEXT_DETECTION_OPTIONS.lookupHeader || 'accept-language';
49
- const acceptLanguage = getHeader(lookupKey);
50
- if (acceptLanguage) {
51
- const languagesList = acceptLanguage.split(',').map((lang)=>{
52
- const [code, q] = lang.trim().split(';');
53
- return {
54
- code: code.split('-')[0],
55
- quality: q ? parseFloat(q.split('=')[1]) : 1.0
56
- };
57
- }).sort((a, b)=>b.quality - a.quality);
58
- for (const lang of languagesList)if (0 === languages.length || languages.includes(lang.code)) {
59
- detectedLang = lang.code;
60
- break;
61
- }
62
- }
63
- break;
64
- }
65
- }
66
- if (detectedLang && (0 === languages.length || languages.includes(detectedLang))) return detectedLang;
67
- }
68
- } catch (error) {}
69
- return null;
70
- }
71
- export { detectLanguageFromRequest };
1
+ import "../runtime/i18n/detection/config.mjs";
@@ -0,0 +1,11 @@
1
+ const resolveLocalisedUrlsConfig = (option)=>{
2
+ if (option && 'object' == typeof option && Object.keys(option).length > 0) return {
3
+ enabled: true,
4
+ map: option
5
+ };
6
+ return {
7
+ enabled: false,
8
+ map: {}
9
+ };
10
+ };
11
+ export { resolveLocalisedUrlsConfig };
@@ -0,0 +1,7 @@
1
+ export { resolveLocalisedUrlsConfig } from "./config.mjs";
2
+ export { normalisePathPattern, normalisePathname } from "./normalise.mjs";
3
+ export { canonicalTargetPathname, localiseTargetPathname, stripLanguagePrefix } from "./pathname.mjs";
4
+ export { buildPathFromPattern, matchPathPattern } from "./patterns.mjs";
5
+ export { DEFAULT_LOCALE_REDIRECT_SKIP_RULES, isDefaultLocaleRedirectSkipPath, matchesPathPrefix, shouldSkipLocaleRedirect } from "./redirect.mjs";
6
+ export { resolveCanonicalLocalisedPath, resolveLocalisedPath } from "./resolve.mjs";
7
+ export { applyLocalisedUrlsToRoutes, validateLocalisedUrls } from "./routes.mjs";
@@ -0,0 +1,37 @@
1
+ const LOCALE_PARAM_NAMES = new Set([
2
+ 'lang',
3
+ 'locale',
4
+ 'language'
5
+ ]);
6
+ const normaliseSlashes = (path)=>{
7
+ const withoutDuplicateSlashes = path.replace(/\/+/g, '/');
8
+ const withLeadingSlash = withoutDuplicateSlashes.startsWith('/') ? withoutDuplicateSlashes : `/${withoutDuplicateSlashes}`;
9
+ return withLeadingSlash.length > 1 ? withLeadingSlash.replace(/\/+$/, '') : withLeadingSlash;
10
+ };
11
+ const normalisePathPattern = (path)=>normaliseSlashes(path).replace(/\[(.+?)\]/g, ':$1');
12
+ const normalisePathname = (pathname)=>normaliseSlashes(pathname);
13
+ const normaliseRoutePath = (path)=>{
14
+ const normalized = normalisePathPattern(path);
15
+ return '/' === normalized ? '' : normalized.slice(1);
16
+ };
17
+ const getLocaleParamSegment = (segment)=>{
18
+ if (!segment.startsWith(':')) return null;
19
+ const paramName = segment.slice(1).replace(/\?$/, '');
20
+ return LOCALE_PARAM_NAMES.has(paramName) ? segment : null;
21
+ };
22
+ const splitPathSegments = (path)=>{
23
+ if (!path) return [];
24
+ return normalisePathPattern(path).split('/').filter(Boolean);
25
+ };
26
+ const stripLeadingLocaleParam = (path)=>{
27
+ const segments = splitPathSegments(path);
28
+ const leadingLocaleParam = getLocaleParamSegment(segments[0] || '');
29
+ if (!leadingLocaleParam) return path;
30
+ const remainingPath = segments.slice(1).join('/');
31
+ return remainingPath ? `/${remainingPath}` : void 0;
32
+ };
33
+ const getLeadingLocaleParam = (path)=>{
34
+ const segments = splitPathSegments(path);
35
+ return getLocaleParamSegment(segments[0] || '');
36
+ };
37
+ export { getLeadingLocaleParam, normalisePathPattern, normalisePathname, normaliseRoutePath, stripLeadingLocaleParam };
@@ -0,0 +1,24 @@
1
+ import { resolveLocalisedUrlsConfig } from "./config.mjs";
2
+ import { resolveCanonicalLocalisedPath, resolveLocalisedPath } from "./resolve.mjs";
3
+ const stripLanguagePrefix = (pathname, languages)=>{
4
+ const segments = pathname.split('/').filter(Boolean);
5
+ const firstSegment = segments[0]?.toLowerCase();
6
+ if (firstSegment && languages.some((language)=>language.toLowerCase() === firstSegment)) return `/${segments.slice(1).join('/')}`;
7
+ return pathname || '/';
8
+ };
9
+ const localiseTargetPathname = (pathname, language, languages, localisedUrls)=>{
10
+ const pathWithoutLanguage = stripLanguagePrefix(pathname, languages);
11
+ const localisedUrlsConfig = resolveLocalisedUrlsConfig(localisedUrls);
12
+ const resolvedPath = localisedUrlsConfig.enabled ? resolveLocalisedPath(pathWithoutLanguage, language, languages, localisedUrlsConfig.map) : pathWithoutLanguage;
13
+ const resolvedSegments = resolvedPath.split('/').filter(Boolean);
14
+ return `/${[
15
+ language,
16
+ ...resolvedSegments
17
+ ].join('/')}`;
18
+ };
19
+ const canonicalTargetPathname = (pathname, languages, localisedUrls)=>{
20
+ const pathWithoutLanguage = stripLanguagePrefix(pathname, languages);
21
+ const localisedUrlsConfig = resolveLocalisedUrlsConfig(localisedUrls);
22
+ return localisedUrlsConfig.enabled ? resolveCanonicalLocalisedPath(pathWithoutLanguage, languages, localisedUrlsConfig.map) : pathWithoutLanguage;
23
+ };
24
+ export { canonicalTargetPathname, localiseTargetPathname, stripLanguagePrefix };
@@ -0,0 +1,86 @@
1
+ import { normalisePathPattern, normalisePathname } from "./normalise.mjs";
2
+ const escapeRegExp = (value)=>value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
3
+ const getParamName = (segment)=>segment.slice(1).replace(/\?$/, '');
4
+ const compiledPathPatternCache = new Map();
5
+ const compilePathPattern = (pattern)=>{
6
+ const normalizedPattern = normalisePathPattern(pattern);
7
+ const cached = compiledPathPatternCache.get(normalizedPattern);
8
+ if (cached) return cached;
9
+ const names = [];
10
+ const segments = normalizedPattern.split('/').filter(Boolean);
11
+ const source = segments.map((segment)=>{
12
+ if (segment.startsWith(':')) {
13
+ names.push(getParamName(segment));
14
+ const paramPattern = '([^/]+)';
15
+ return segment.endsWith('?') ? `(?:/${paramPattern})?` : `/${paramPattern}`;
16
+ }
17
+ if ('*' === segment) {
18
+ names.push('*');
19
+ return '/(.*)';
20
+ }
21
+ return `/${escapeRegExp(segment)}`;
22
+ }).join('');
23
+ const compiled = {
24
+ names,
25
+ regexp: new RegExp(`^${source || '/'}$`)
26
+ };
27
+ compiledPathPatternCache.set(normalizedPattern, compiled);
28
+ return compiled;
29
+ };
30
+ const getPatternSpecificity = (pattern)=>{
31
+ const segments = normalisePathPattern(pattern).split('/').filter(Boolean);
32
+ let staticSegments = 0;
33
+ let dynamicSegments = 0;
34
+ let splatSegments = 0;
35
+ for (const segment of segments)if ('*' === segment) splatSegments++;
36
+ else if (segment.startsWith(':')) dynamicSegments++;
37
+ else staticSegments++;
38
+ return {
39
+ staticSegments,
40
+ dynamicSegments,
41
+ splatSegments,
42
+ totalSegments: segments.length
43
+ };
44
+ };
45
+ const comparePatternSpecificity = (left, right)=>{
46
+ const a = getPatternSpecificity(left);
47
+ const b = getPatternSpecificity(right);
48
+ return b.staticSegments - a.staticSegments || b.totalSegments - a.totalSegments || a.splatSegments - b.splatSegments || a.dynamicSegments - b.dynamicSegments;
49
+ };
50
+ const sortPatternsBySpecificity = (patterns)=>patterns.map((pattern, index)=>({
51
+ pattern,
52
+ index
53
+ })).sort((left, right)=>comparePatternSpecificity(left.pattern.pattern, right.pattern.pattern) || left.index - right.index).map(({ pattern })=>pattern);
54
+ const decodePathParam = (value)=>{
55
+ try {
56
+ return decodeURIComponent(value);
57
+ } catch {
58
+ return null;
59
+ }
60
+ };
61
+ const encodeSplatParam = (value)=>value.split('/').map(encodeURIComponent).join('/');
62
+ const matchPathPattern = (pathname, pattern)=>{
63
+ const { names, regexp } = compilePathPattern(pattern);
64
+ const match = regexp.exec(normalisePathname(pathname));
65
+ if (!match) return null;
66
+ const params = {};
67
+ for(let index = 0; index < names.length; index++){
68
+ const decoded = decodePathParam(match[index + 1] || '');
69
+ if (null === decoded) return null;
70
+ params[names[index]] = decoded;
71
+ }
72
+ return params;
73
+ };
74
+ const buildPathFromPattern = (pattern, params)=>{
75
+ const segments = normalisePathPattern(pattern).split('/').filter(Boolean);
76
+ const path = segments.map((segment)=>{
77
+ if (segment.startsWith(':')) {
78
+ const param = params[getParamName(segment)];
79
+ return param ? encodeURIComponent(param) : '';
80
+ }
81
+ if ('*' === segment) return encodeSplatParam(params['*'] || '');
82
+ return segment;
83
+ }).filter(Boolean).join('/');
84
+ return `/${path}`;
85
+ };
86
+ export { buildPathFromPattern, matchPathPattern, sortPatternsBySpecificity };
@@ -0,0 +1,48 @@
1
+ import { normalisePathname } from "./normalise.mjs";
2
+ import { stripLanguagePrefix } from "./pathname.mjs";
3
+ const DEFAULT_LOCALE_REDIRECT_SKIP_RULES = [
4
+ {
5
+ type: 'exact',
6
+ path: '/mf-manifest.json'
7
+ },
8
+ {
9
+ type: 'exact',
10
+ path: '/mf-stats.json'
11
+ },
12
+ {
13
+ type: 'exact',
14
+ path: '/remoteEntry.js'
15
+ },
16
+ {
17
+ type: 'prefix',
18
+ path: '/static/'
19
+ },
20
+ {
21
+ type: 'prefix',
22
+ path: '/upload/'
23
+ }
24
+ ];
25
+ const stripTrailingSlash = (pathname)=>pathname.length > 1 ? pathname.replace(/\/+$/, '') : pathname;
26
+ const matchesPathPrefix = (pathname, prefix)=>{
27
+ const normalizedPathname = normalisePathname(pathname);
28
+ const normalizedPrefix = stripTrailingSlash(normalisePathname(prefix));
29
+ return normalizedPathname === normalizedPrefix || normalizedPathname.startsWith(`${normalizedPrefix}/`);
30
+ };
31
+ const matchesSkipRule = (pathname, rule)=>{
32
+ if ('exact' === rule.type) return normalisePathname(pathname) === normalisePathname(rule.path);
33
+ return matchesPathPrefix(pathname, rule.path);
34
+ };
35
+ const isDefaultLocaleRedirectSkipPath = (pathname, languages = [])=>{
36
+ const normalizedPathname = normalisePathname(pathname);
37
+ const pathWithoutLanguage = stripLanguagePrefix(normalizedPathname, languages);
38
+ return DEFAULT_LOCALE_REDIRECT_SKIP_RULES.some((rule)=>matchesSkipRule(pathWithoutLanguage, rule));
39
+ };
40
+ const shouldSkipLocaleRedirect = (pathname, languages = [], ignoreRedirectRoutes)=>{
41
+ const normalizedPathname = normalisePathname(pathname);
42
+ const pathWithoutLanguage = stripLanguagePrefix(normalizedPathname, languages);
43
+ if (isDefaultLocaleRedirectSkipPath(pathWithoutLanguage)) return true;
44
+ if (!ignoreRedirectRoutes) return false;
45
+ if ('function' == typeof ignoreRedirectRoutes) return ignoreRedirectRoutes(pathWithoutLanguage);
46
+ return ignoreRedirectRoutes.some((pattern)=>matchesPathPrefix(pathWithoutLanguage, pattern));
47
+ };
48
+ export { DEFAULT_LOCALE_REDIRECT_SKIP_RULES, isDefaultLocaleRedirectSkipPath, matchesPathPrefix, shouldSkipLocaleRedirect };