@intlayer/core 8.6.2 → 8.6.4

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 (125) hide show
  1. package/dist/cjs/deepTransformPlugins/getFilterMissingTranslationsContent.cjs +1 -2
  2. package/dist/cjs/deepTransformPlugins/getFilterMissingTranslationsContent.cjs.map +1 -1
  3. package/dist/cjs/deepTransformPlugins/getFilterTranslationsOnlyContent.cjs +2 -3
  4. package/dist/cjs/deepTransformPlugins/getFilterTranslationsOnlyContent.cjs.map +1 -1
  5. package/dist/cjs/deepTransformPlugins/getMissingLocalesContent.cjs +2 -3
  6. package/dist/cjs/deepTransformPlugins/getMissingLocalesContent.cjs.map +1 -1
  7. package/dist/cjs/dictionaryManipulator/mergeDictionaries.cjs +1 -2
  8. package/dist/cjs/dictionaryManipulator/mergeDictionaries.cjs.map +1 -1
  9. package/dist/cjs/dictionaryManipulator/orderDictionaries.cjs +2 -4
  10. package/dist/cjs/dictionaryManipulator/orderDictionaries.cjs.map +1 -1
  11. package/dist/cjs/formatters/compact.cjs +1 -2
  12. package/dist/cjs/formatters/compact.cjs.map +1 -1
  13. package/dist/cjs/formatters/currency.cjs +1 -2
  14. package/dist/cjs/formatters/currency.cjs.map +1 -1
  15. package/dist/cjs/formatters/date.cjs +1 -2
  16. package/dist/cjs/formatters/date.cjs.map +1 -1
  17. package/dist/cjs/formatters/list.cjs +1 -2
  18. package/dist/cjs/formatters/list.cjs.map +1 -1
  19. package/dist/cjs/formatters/number.cjs +1 -2
  20. package/dist/cjs/formatters/number.cjs.map +1 -1
  21. package/dist/cjs/formatters/percentage.cjs +1 -2
  22. package/dist/cjs/formatters/percentage.cjs.map +1 -1
  23. package/dist/cjs/formatters/relativeTime.cjs +1 -2
  24. package/dist/cjs/formatters/relativeTime.cjs.map +1 -1
  25. package/dist/cjs/formatters/units.cjs +1 -2
  26. package/dist/cjs/formatters/units.cjs.map +1 -1
  27. package/dist/cjs/interpreter/getContent/getContent.cjs +2 -3
  28. package/dist/cjs/interpreter/getContent/getContent.cjs.map +1 -1
  29. package/dist/cjs/interpreter/getContent/plugins.cjs +35 -7
  30. package/dist/cjs/interpreter/getContent/plugins.cjs.map +1 -1
  31. package/dist/cjs/interpreter/getIntlayer.cjs +1 -2
  32. package/dist/cjs/interpreter/getIntlayer.cjs.map +1 -1
  33. package/dist/cjs/localization/getBrowserLocale.cjs +4 -8
  34. package/dist/cjs/localization/getBrowserLocale.cjs.map +1 -1
  35. package/dist/cjs/localization/getLocale.cjs +2 -3
  36. package/dist/cjs/localization/getLocale.cjs.map +1 -1
  37. package/dist/cjs/localization/getLocalizedUrl.cjs +11 -5
  38. package/dist/cjs/localization/getLocalizedUrl.cjs.map +1 -1
  39. package/dist/cjs/localization/getPathWithoutLocale.cjs +12 -5
  40. package/dist/cjs/localization/getPathWithoutLocale.cjs.map +1 -1
  41. package/dist/cjs/localization/getPrefix.cjs +13 -13
  42. package/dist/cjs/localization/getPrefix.cjs.map +1 -1
  43. package/dist/cjs/localization/localeMapper.cjs +3 -4
  44. package/dist/cjs/localization/localeMapper.cjs.map +1 -1
  45. package/dist/cjs/localization/localeResolver.cjs +1 -2
  46. package/dist/cjs/localization/localeResolver.cjs.map +1 -1
  47. package/dist/cjs/localization/rewriteUtils.cjs +9 -6
  48. package/dist/cjs/localization/rewriteUtils.cjs.map +1 -1
  49. package/dist/cjs/localization/validatePrefix.cjs +9 -3
  50. package/dist/cjs/localization/validatePrefix.cjs.map +1 -1
  51. package/dist/cjs/utils/intl.cjs +1 -2
  52. package/dist/cjs/utils/intl.cjs.map +1 -1
  53. package/dist/cjs/utils/localeStorage.cjs +43 -35
  54. package/dist/cjs/utils/localeStorage.cjs.map +1 -1
  55. package/dist/esm/deepTransformPlugins/getFilterMissingTranslationsContent.mjs +2 -2
  56. package/dist/esm/deepTransformPlugins/getFilterMissingTranslationsContent.mjs.map +1 -1
  57. package/dist/esm/deepTransformPlugins/getFilterTranslationsOnlyContent.mjs +3 -3
  58. package/dist/esm/deepTransformPlugins/getFilterTranslationsOnlyContent.mjs.map +1 -1
  59. package/dist/esm/deepTransformPlugins/getMissingLocalesContent.mjs +3 -3
  60. package/dist/esm/deepTransformPlugins/getMissingLocalesContent.mjs.map +1 -1
  61. package/dist/esm/dictionaryManipulator/mergeDictionaries.mjs +2 -2
  62. package/dist/esm/dictionaryManipulator/mergeDictionaries.mjs.map +1 -1
  63. package/dist/esm/dictionaryManipulator/orderDictionaries.mjs +2 -3
  64. package/dist/esm/dictionaryManipulator/orderDictionaries.mjs.map +1 -1
  65. package/dist/esm/formatters/compact.mjs +2 -2
  66. package/dist/esm/formatters/compact.mjs.map +1 -1
  67. package/dist/esm/formatters/currency.mjs +2 -2
  68. package/dist/esm/formatters/currency.mjs.map +1 -1
  69. package/dist/esm/formatters/date.mjs +2 -2
  70. package/dist/esm/formatters/date.mjs.map +1 -1
  71. package/dist/esm/formatters/list.mjs +2 -2
  72. package/dist/esm/formatters/list.mjs.map +1 -1
  73. package/dist/esm/formatters/number.mjs +2 -2
  74. package/dist/esm/formatters/number.mjs.map +1 -1
  75. package/dist/esm/formatters/percentage.mjs +2 -2
  76. package/dist/esm/formatters/percentage.mjs.map +1 -1
  77. package/dist/esm/formatters/relativeTime.mjs +2 -2
  78. package/dist/esm/formatters/relativeTime.mjs.map +1 -1
  79. package/dist/esm/formatters/units.mjs +2 -2
  80. package/dist/esm/formatters/units.mjs.map +1 -1
  81. package/dist/esm/interpreter/getContent/getContent.mjs +3 -3
  82. package/dist/esm/interpreter/getContent/getContent.mjs.map +1 -1
  83. package/dist/esm/interpreter/getContent/plugins.mjs +35 -7
  84. package/dist/esm/interpreter/getContent/plugins.mjs.map +1 -1
  85. package/dist/esm/interpreter/getIntlayer.mjs +2 -2
  86. package/dist/esm/interpreter/getIntlayer.mjs.map +1 -1
  87. package/dist/esm/localization/getBrowserLocale.mjs +1 -4
  88. package/dist/esm/localization/getBrowserLocale.mjs.map +1 -1
  89. package/dist/esm/localization/getLocale.mjs +3 -3
  90. package/dist/esm/localization/getLocale.mjs.map +1 -1
  91. package/dist/esm/localization/getLocalizedUrl.mjs +10 -3
  92. package/dist/esm/localization/getLocalizedUrl.mjs.map +1 -1
  93. package/dist/esm/localization/getPathWithoutLocale.mjs +11 -3
  94. package/dist/esm/localization/getPathWithoutLocale.mjs.map +1 -1
  95. package/dist/esm/localization/getPrefix.mjs +13 -12
  96. package/dist/esm/localization/getPrefix.mjs.map +1 -1
  97. package/dist/esm/localization/localeMapper.mjs +4 -4
  98. package/dist/esm/localization/localeMapper.mjs.map +1 -1
  99. package/dist/esm/localization/localeResolver.mjs +2 -2
  100. package/dist/esm/localization/localeResolver.mjs.map +1 -1
  101. package/dist/esm/localization/rewriteUtils.mjs +5 -2
  102. package/dist/esm/localization/rewriteUtils.mjs.map +1 -1
  103. package/dist/esm/localization/validatePrefix.mjs +8 -1
  104. package/dist/esm/localization/validatePrefix.mjs.map +1 -1
  105. package/dist/esm/utils/intl.mjs +2 -2
  106. package/dist/esm/utils/intl.mjs.map +1 -1
  107. package/dist/esm/utils/localeStorage.mjs +35 -26
  108. package/dist/esm/utils/localeStorage.mjs.map +1 -1
  109. package/dist/types/deepTransformPlugins/getFilterMissingTranslationsContent.d.ts.map +1 -1
  110. package/dist/types/deepTransformPlugins/getFilterTranslationsOnlyContent.d.ts.map +1 -1
  111. package/dist/types/deepTransformPlugins/getMissingLocalesContent.d.ts.map +1 -1
  112. package/dist/types/dictionaryManipulator/mergeDictionaries.d.ts.map +1 -1
  113. package/dist/types/dictionaryManipulator/orderDictionaries.d.ts +1 -2
  114. package/dist/types/dictionaryManipulator/orderDictionaries.d.ts.map +1 -1
  115. package/dist/types/interpreter/getContent/plugins.d.ts.map +1 -1
  116. package/dist/types/localization/getBrowserLocale.d.ts.map +1 -1
  117. package/dist/types/localization/getLocalizedUrl.d.ts.map +1 -1
  118. package/dist/types/localization/getPathWithoutLocale.d.ts.map +1 -1
  119. package/dist/types/localization/getPrefix.d.ts.map +1 -1
  120. package/dist/types/localization/localeResolver.d.ts.map +1 -1
  121. package/dist/types/localization/rewriteUtils.d.ts.map +1 -1
  122. package/dist/types/localization/validatePrefix.d.ts.map +1 -1
  123. package/dist/types/utils/intl.d.ts.map +1 -1
  124. package/dist/types/utils/localeStorage.d.ts.map +1 -1
  125. package/package.json +6 -6
@@ -1 +1 @@
1
- {"version":3,"file":"getPathWithoutLocale.mjs","names":[],"sources":["../../../src/localization/getPathWithoutLocale.ts"],"sourcesContent":["import configuration from '@intlayer/config/built';\nimport {\n TREE_SHAKE_PREFIX_MODES,\n TREE_SHAKE_SEARCH_PARAMS,\n} from '@intlayer/config/envVars';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { checkIsURLAbsolute } from '../utils/checkIsURLAbsolute';\n\n/**\n * Removes the locale segment from the given URL or pathname if present.\n * Also removes locale from search parameters if present.\n *\n * This function get the locales from the configuration if not provided.\n *\n * Example:\n *\n * ```ts\n * getPathWithoutLocale('/en/dashboard') // Returns '/dashboard'\n * getPathWithoutLocale('/fr/dashboard') // Returns '/dashboard'\n * getPathWithoutLocale('/dashboard') // Returns '/dashboard'\n * getPathWithoutLocale('dashboard') // Returns 'dashboard'\n * getPathWithoutLocale('/dashboard?locale=fr') // Returns '/dashboard'\n * getPathWithoutLocale('https://example.com/en/dashboard') // Returns 'https://example.com/dashboard'\n * getPathWithoutLocale('https://example.com/fr/dashboard') // Returns 'https://example.com/dashboard'\n * getPathWithoutLocale('https://example.com/dashboard') // Returns 'https://example.com/dashboard'\n * getPathWithoutLocale('https://example.com/dashboard?locale=fr') // Returns 'https://example.com/dashboard'\n * ```\n *\n * @param inputUrl - The complete URL string or pathname to process.\n * @param locales - Optional array of supported locales. Defaults to `localesDefault`.\n * @returns The URL string or pathname without the locale segment or locale search parameter.\n */\nexport const getPathWithoutLocale = (\n inputUrl: string,\n locales: LocalesValues[] = configuration?.internationalization?.locales\n): string => {\n // Determine if the original URL is absolute (includes protocol)\n const isAbsoluteUrl = checkIsURLAbsolute(inputUrl);\n\n let fixedInputUrl = inputUrl;\n\n if (inputUrl?.endsWith('/')) {\n fixedInputUrl = inputUrl.slice(0, -1);\n }\n\n // Initialize a URL object if the URL is absolute\n // For relative URLs, use a dummy base to leverage the URL API\n const url = isAbsoluteUrl\n ? new URL(fixedInputUrl)\n : new URL(fixedInputUrl, 'http://example.com');\n\n const pathname = url.pathname;\n\n // Ensure the pathname starts with '/'\n if (!pathname.startsWith('/')) {\n // If not, return the URL as is\n url.pathname = `/${pathname}`;\n }\n\n // Only strip locale path prefix in prefix-based routing modes\n if (!TREE_SHAKE_PREFIX_MODES) {\n // Split the pathname to extract the first segment\n const pathSegments = pathname.split('/');\n const firstSegment = pathSegments[1]; // The segment after the first '/'\n\n // Check if the first segment is a supported locale\n if (locales?.includes(firstSegment as LocalesValues)) {\n // Remove the locale segment from the pathname\n pathSegments.splice(1, 1); // Remove the first segment\n\n // Reconstruct the pathname\n const newPathname = pathSegments.join('/') ?? '/';\n url.pathname = newPathname;\n }\n }\n\n // Only strip locale from search parameters in search-params routing mode\n if (!TREE_SHAKE_SEARCH_PARAMS) {\n const searchParams = new URLSearchParams(url.search);\n if (searchParams.has('locale')) {\n searchParams.delete('locale');\n url.search = searchParams.toString();\n }\n }\n\n if (isAbsoluteUrl) {\n // Return the modified URL as a string\n return url.toString();\n }\n\n // Return the modified URL as a string\n return url.toString().replace('http://example.com', '');\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAa,wBACX,UACA,UAA2B,eAAe,sBAAsB,YACrD;CAEX,MAAM,gBAAgB,mBAAmB,SAAS;CAElD,IAAI,gBAAgB;AAEpB,KAAI,UAAU,SAAS,IAAI,CACzB,iBAAgB,SAAS,MAAM,GAAG,GAAG;CAKvC,MAAM,MAAM,gBACR,IAAI,IAAI,cAAc,GACtB,IAAI,IAAI,eAAe,qBAAqB;CAEhD,MAAM,WAAW,IAAI;AAGrB,KAAI,CAAC,SAAS,WAAW,IAAI,CAE3B,KAAI,WAAW,IAAI;AAIrB,KAAI,CAAC,yBAAyB;EAE5B,MAAM,eAAe,SAAS,MAAM,IAAI;EACxC,MAAM,eAAe,aAAa;AAGlC,MAAI,SAAS,SAAS,aAA8B,EAAE;AAEpD,gBAAa,OAAO,GAAG,EAAE;AAIzB,OAAI,WADgB,aAAa,KAAK,IAAI,IAAI;;;AAMlD,KAAI,CAAC,0BAA0B;EAC7B,MAAM,eAAe,IAAI,gBAAgB,IAAI,OAAO;AACpD,MAAI,aAAa,IAAI,SAAS,EAAE;AAC9B,gBAAa,OAAO,SAAS;AAC7B,OAAI,SAAS,aAAa,UAAU;;;AAIxC,KAAI,cAEF,QAAO,IAAI,UAAU;AAIvB,QAAO,IAAI,UAAU,CAAC,QAAQ,sBAAsB,GAAG"}
1
+ {"version":3,"file":"getPathWithoutLocale.mjs","names":[],"sources":["../../../src/localization/getPathWithoutLocale.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\n\n\n// ── Tree-shake constants ──────────────────────────────────────────────────────\n// When these env vars are injected at build time, bundlers eliminate the\n// branches guarded by these constants.\n\n/**\n * True when the build-time routing mode is known and is not a prefix-based\n * mode (neither 'prefix-all' nor 'prefix-no-default').\n */\nconst TREE_SHAKE_PREFIX_MODES =\n process.env['INTLAYER_ROUTING_MODE'] &&\n process.env['INTLAYER_ROUTING_MODE'] !== 'prefix-all' &&\n process.env['INTLAYER_ROUTING_MODE'] !== 'prefix-no-default';\n\n/**\n * True when the build-time routing mode is known and is NOT 'search-params'.\n */\nconst TREE_SHAKE_SEARCH_PARAMS =\n process.env['INTLAYER_ROUTING_MODE'] &&\n process.env['INTLAYER_ROUTING_MODE'] !== 'search-params';\n\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { checkIsURLAbsolute } from '../utils/checkIsURLAbsolute';\n\n/**\n * Removes the locale segment from the given URL or pathname if present.\n * Also removes locale from search parameters if present.\n *\n * This function get the locales from the configuration if not provided.\n *\n * Example:\n *\n * ```ts\n * getPathWithoutLocale('/en/dashboard') // Returns '/dashboard'\n * getPathWithoutLocale('/fr/dashboard') // Returns '/dashboard'\n * getPathWithoutLocale('/dashboard') // Returns '/dashboard'\n * getPathWithoutLocale('dashboard') // Returns 'dashboard'\n * getPathWithoutLocale('/dashboard?locale=fr') // Returns '/dashboard'\n * getPathWithoutLocale('https://example.com/en/dashboard') // Returns 'https://example.com/dashboard'\n * getPathWithoutLocale('https://example.com/fr/dashboard') // Returns 'https://example.com/dashboard'\n * getPathWithoutLocale('https://example.com/dashboard') // Returns 'https://example.com/dashboard'\n * getPathWithoutLocale('https://example.com/dashboard?locale=fr') // Returns 'https://example.com/dashboard'\n * ```\n *\n * @param inputUrl - The complete URL string or pathname to process.\n * @param locales - Optional array of supported locales. Defaults to `localesDefault`.\n * @returns The URL string or pathname without the locale segment or locale search parameter.\n */\nexport const getPathWithoutLocale = (\n inputUrl: string,\n locales: LocalesValues[] = internationalization?.locales\n\n): string => {\n // Determine if the original URL is absolute (includes protocol)\n const isAbsoluteUrl = checkIsURLAbsolute(inputUrl);\n\n let fixedInputUrl = inputUrl;\n\n if (inputUrl?.endsWith('/')) {\n fixedInputUrl = inputUrl.slice(0, -1);\n }\n\n // Initialize a URL object if the URL is absolute\n // For relative URLs, use a dummy base to leverage the URL API\n const url = isAbsoluteUrl\n ? new URL(fixedInputUrl)\n : new URL(fixedInputUrl, 'http://example.com');\n\n const pathname = url.pathname;\n\n // Ensure the pathname starts with '/'\n if (!pathname.startsWith('/')) {\n // If not, return the URL as is\n url.pathname = `/${pathname}`;\n }\n\n // Only strip locale path prefix in prefix-based routing modes\n if (!TREE_SHAKE_PREFIX_MODES) {\n // Split the pathname to extract the first segment\n const pathSegments = pathname.split('/');\n const firstSegment = pathSegments[1]; // The segment after the first '/'\n\n // Check if the first segment is a supported locale\n if (locales?.includes(firstSegment as LocalesValues)) {\n // Remove the locale segment from the pathname\n pathSegments.splice(1, 1); // Remove the first segment\n\n // Reconstruct the pathname\n const newPathname = pathSegments.join('/') ?? '/';\n url.pathname = newPathname;\n }\n }\n\n // Only strip locale from search parameters in search-params routing mode\n if (!TREE_SHAKE_SEARCH_PARAMS) {\n const searchParams = new URLSearchParams(url.search);\n if (searchParams.has('locale')) {\n searchParams.delete('locale');\n url.search = searchParams.toString();\n }\n }\n\n if (isAbsoluteUrl) {\n // Return the modified URL as a string\n return url.toString();\n }\n\n // Return the modified URL as a string\n return url.toString().replace('http://example.com', '');\n};\n"],"mappings":";;;;;;;;AAWA,MAAM,0BACJ,QAAQ,IAAI,4BACZ,QAAQ,IAAI,6BAA6B,gBACzC,QAAQ,IAAI,6BAA6B;;;;AAK3C,MAAM,2BACJ,QAAQ,IAAI,4BACZ,QAAQ,IAAI,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;AA6B3C,MAAa,wBACX,UACA,UAA2B,sBAAsB,YAEtC;CAEX,MAAM,gBAAgB,mBAAmB,SAAS;CAElD,IAAI,gBAAgB;AAEpB,KAAI,UAAU,SAAS,IAAI,CACzB,iBAAgB,SAAS,MAAM,GAAG,GAAG;CAKvC,MAAM,MAAM,gBACR,IAAI,IAAI,cAAc,GACtB,IAAI,IAAI,eAAe,qBAAqB;CAEhD,MAAM,WAAW,IAAI;AAGrB,KAAI,CAAC,SAAS,WAAW,IAAI,CAE3B,KAAI,WAAW,IAAI;AAIrB,KAAI,CAAC,yBAAyB;EAE5B,MAAM,eAAe,SAAS,MAAM,IAAI;EACxC,MAAM,eAAe,aAAa;AAGlC,MAAI,SAAS,SAAS,aAA8B,EAAE;AAEpD,gBAAa,OAAO,GAAG,EAAE;AAIzB,OAAI,WADgB,aAAa,KAAK,IAAI,IAAI;;;AAMlD,KAAI,CAAC,0BAA0B;EAC7B,MAAM,eAAe,IAAI,gBAAgB,IAAI,OAAO;AACpD,MAAI,aAAa,IAAI,SAAS,EAAE;AAC9B,gBAAa,OAAO,SAAS;AAC7B,OAAI,SAAS,aAAa,UAAU;;;AAIxC,KAAI,cAEF,QAAO,IAAI,UAAU;AAIvB,QAAO,IAAI,UAAU,CAAC,QAAQ,sBAAsB,GAAG"}
@@ -1,22 +1,23 @@
1
- import configuration from "@intlayer/config/built";
2
- import { TREE_SHAKE_PREFIX_MODES } from "@intlayer/config/envVars";
1
+ import { internationalization, routing } from "@intlayer/config/built";
3
2
  import { DEFAULT_LOCALE, LOCALES, ROUTING_MODE } from "@intlayer/config/defaultValues";
4
3
 
5
4
  //#region src/localization/getPrefix.ts
6
5
  /**
6
+ * True when the build-time routing mode is known and is not a prefix-based
7
+ * mode (neither 'prefix-all' nor 'prefix-no-default').
8
+ */
9
+ const TREE_SHAKE_PREFIX_MODES = process.env["INTLAYER_ROUTING_MODE"] && process.env["INTLAYER_ROUTING_MODE"] !== "prefix-all" && process.env["INTLAYER_ROUTING_MODE"] !== "prefix-no-default";
10
+ /**
7
11
  * Resolves routing configuration by merging provided options with configuration defaults.
8
12
  * Single source of truth for default routing config resolution across all localization functions.
9
13
  */
10
- const resolveRoutingConfig = (options = {}) => {
11
- const { internationalization, routing } = configuration ?? {};
12
- return {
13
- defaultLocale: internationalization?.defaultLocale ?? DEFAULT_LOCALE,
14
- mode: routing?.mode ?? ROUTING_MODE,
15
- locales: internationalization?.locales ?? LOCALES,
16
- rewrite: routing?.rewrite,
17
- ...options
18
- };
19
- };
14
+ const resolveRoutingConfig = (options = {}) => ({
15
+ defaultLocale: internationalization?.defaultLocale ?? DEFAULT_LOCALE,
16
+ mode: routing?.mode ?? ROUTING_MODE,
17
+ locales: internationalization?.locales ?? LOCALES,
18
+ rewrite: routing?.rewrite,
19
+ ...options
20
+ });
20
21
  /**
21
22
  * Determines the URL prefix for a given locale based on the routing mode configuration.
22
23
  *
@@ -1 +1 @@
1
- {"version":3,"file":"getPrefix.mjs","names":[],"sources":["../../../src/localization/getPrefix.ts"],"sourcesContent":["import configuration from '@intlayer/config/built';\nimport {\n DEFAULT_LOCALE,\n LOCALES,\n ROUTING_MODE,\n} from '@intlayer/config/defaultValues';\nimport { TREE_SHAKE_PREFIX_MODES } from '@intlayer/config/envVars';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { RoutingConfig } from '@intlayer/types/config';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\n/**\n * Shared routing options used across all URL localization functions.\n */\nexport type RoutingOptions = {\n locales?: LocalesValues[];\n defaultLocale?: LocalesValues;\n mode?: RoutingConfig['mode'];\n rewrite?: RoutingConfig['rewrite'];\n};\n\n/**\n * Resolves routing configuration by merging provided options with configuration defaults.\n * Single source of truth for default routing config resolution across all localization functions.\n */\nexport const resolveRoutingConfig = (options: RoutingOptions = {}) => {\n const { internationalization, routing } = configuration ?? {};\n return {\n defaultLocale: internationalization?.defaultLocale ?? DEFAULT_LOCALE,\n mode: routing?.mode ?? ROUTING_MODE,\n locales: internationalization?.locales ?? LOCALES,\n rewrite: routing?.rewrite,\n ...options,\n };\n};\n\nexport type GetPrefixOptions = {\n defaultLocale?: LocalesValues;\n mode?: RoutingConfig['mode'];\n};\n\nexport type GetPrefixResult = {\n /**\n * The complete base URL path with leading and trailing slashes.\n *\n * @example\n * // https://example.com/fr/about -> '/fr'\n * // https://example.com/about -> ''\n */\n prefix: string;\n /**\n * The locale identifier without slashes.\n *\n * @example\n * // https://example.com/fr/about -> 'fr'\n * // https://example.com/about -> undefined\n */\n localePrefix: Locale | undefined;\n};\n\n/**\n * Determines the URL prefix for a given locale based on the routing mode configuration.\n *\n * Example:\n *\n * ```ts\n * // prefix-no-default mode with default locale\n * getPrefix('en', { defaultLocale: 'en', mode: 'prefix-no-default' })\n * // Returns { prefix: '', localePrefix: undefined }\n *\n * // prefix-no-default mode with non-default locale\n * getPrefix('fr', { defaultLocale: 'en', mode: 'prefix-no-default' })\n * // Returns { prefix: '/fr', localePrefix: 'fr' }\n *\n * // prefix-all mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'prefix-all' })\n * // Returns { prefix: '/en', localePrefix: locale }\n *\n * // search-params mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'search-params' })\n * // Returns { prefix: '', localePrefix: undefined }\n *\n * // no-prefix mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'no-prefix' })\n * // Returns { prefix: '', localePrefix: undefined }\n * ```\n *\n * @param locale - The locale to check for prefix. If not provided, uses configured default locale.\n * @param options - Configuration options\n * @param options.defaultLocale - The default locale. Defaults to configured default locale.\n * @param options.mode - URL routing mode for locale handling. Defaults to configured mode.\n * @returns An object containing pathPrefix, prefix, and localePrefix for the given locale.\n */\nexport const getPrefix = (\n locale: LocalesValues | undefined,\n options: RoutingOptions = {}\n): GetPrefixResult => {\n const { defaultLocale, mode, locales } = resolveRoutingConfig(options);\n\n if (TREE_SHAKE_PREFIX_MODES || !locale || !locales.includes(locale)) {\n return {\n prefix: '',\n localePrefix: undefined,\n };\n }\n\n // Handle prefix-based modes (prefix-all or prefix-no-default)\n const shouldPrefix =\n mode === 'prefix-all' ||\n (mode === 'prefix-no-default' && defaultLocale !== locale);\n\n if (shouldPrefix) {\n return {\n prefix: `${locale}/`,\n localePrefix: locale as Locale,\n };\n }\n\n return {\n prefix: '',\n localePrefix: undefined,\n };\n};\n"],"mappings":";;;;;;;;;AAyBA,MAAa,wBAAwB,UAA0B,EAAE,KAAK;CACpE,MAAM,EAAE,sBAAsB,YAAY,iBAAiB,EAAE;AAC7D,QAAO;EACL,eAAe,sBAAsB,iBAAiB;EACtD,MAAM,SAAS,QAAQ;EACvB,SAAS,sBAAsB,WAAW;EAC1C,SAAS,SAAS;EAClB,GAAG;EACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DH,MAAa,aACX,QACA,UAA0B,EAAE,KACR;CACpB,MAAM,EAAE,eAAe,MAAM,YAAY,qBAAqB,QAAQ;AAEtE,KAAI,2BAA2B,CAAC,UAAU,CAAC,QAAQ,SAAS,OAAO,CACjE,QAAO;EACL,QAAQ;EACR,cAAc;EACf;AAQH,KAHE,SAAS,gBACR,SAAS,uBAAuB,kBAAkB,OAGnD,QAAO;EACL,QAAQ,GAAG,OAAO;EAClB,cAAc;EACf;AAGH,QAAO;EACL,QAAQ;EACR,cAAc;EACf"}
1
+ {"version":3,"file":"getPrefix.mjs","names":[],"sources":["../../../src/localization/getPrefix.ts"],"sourcesContent":["import { internationalization, routing } from '@intlayer/config/built';\nimport {\n DEFAULT_LOCALE,\n LOCALES,\n ROUTING_MODE,\n} from '@intlayer/config/defaultValues';\n\n// ── Tree-shake constants ──────────────────────────────────────────────────────\n// When these env vars are injected at build time, bundlers eliminate the\n// branches guarded by these constants.\n\n/**\n * True when the build-time routing mode is known and is not a prefix-based\n * mode (neither 'prefix-all' nor 'prefix-no-default').\n */\nconst TREE_SHAKE_PREFIX_MODES =\n process.env['INTLAYER_ROUTING_MODE'] &&\n process.env['INTLAYER_ROUTING_MODE'] !== 'prefix-all' &&\n process.env['INTLAYER_ROUTING_MODE'] !== 'prefix-no-default';\n\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { RoutingConfig } from '@intlayer/types/config';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\n/**\n * Shared routing options used across all URL localization functions.\n */\nexport type RoutingOptions = {\n locales?: LocalesValues[];\n defaultLocale?: LocalesValues;\n mode?: RoutingConfig['mode'];\n rewrite?: RoutingConfig['rewrite'];\n};\n\n/**\n * Resolves routing configuration by merging provided options with configuration defaults.\n * Single source of truth for default routing config resolution across all localization functions.\n */\nexport const resolveRoutingConfig = (options: RoutingOptions = {}) => ({\n defaultLocale: internationalization?.defaultLocale ?? DEFAULT_LOCALE,\n mode: routing?.mode ?? ROUTING_MODE,\n locales: internationalization?.locales ?? LOCALES,\n rewrite: routing?.rewrite,\n ...options,\n});\n\nexport type GetPrefixOptions = {\n defaultLocale?: LocalesValues;\n mode?: RoutingConfig['mode'];\n};\n\nexport type GetPrefixResult = {\n /**\n * The complete base URL path with leading and trailing slashes.\n *\n * @example\n * // https://example.com/fr/about -> '/fr'\n * // https://example.com/about -> ''\n */\n prefix: string;\n /**\n * The locale identifier without slashes.\n *\n * @example\n * // https://example.com/fr/about -> 'fr'\n * // https://example.com/about -> undefined\n */\n localePrefix: Locale | undefined;\n};\n\n/**\n * Determines the URL prefix for a given locale based on the routing mode configuration.\n *\n * Example:\n *\n * ```ts\n * // prefix-no-default mode with default locale\n * getPrefix('en', { defaultLocale: 'en', mode: 'prefix-no-default' })\n * // Returns { prefix: '', localePrefix: undefined }\n *\n * // prefix-no-default mode with non-default locale\n * getPrefix('fr', { defaultLocale: 'en', mode: 'prefix-no-default' })\n * // Returns { prefix: '/fr', localePrefix: 'fr' }\n *\n * // prefix-all mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'prefix-all' })\n * // Returns { prefix: '/en', localePrefix: locale }\n *\n * // search-params mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'search-params' })\n * // Returns { prefix: '', localePrefix: undefined }\n *\n * // no-prefix mode\n * getPrefix('en', { defaultLocale: 'en', mode: 'no-prefix' })\n * // Returns { prefix: '', localePrefix: undefined }\n * ```\n *\n * @param locale - The locale to check for prefix. If not provided, uses configured default locale.\n * @param options - Configuration options\n * @param options.defaultLocale - The default locale. Defaults to configured default locale.\n * @param options.mode - URL routing mode for locale handling. Defaults to configured mode.\n * @returns An object containing pathPrefix, prefix, and localePrefix for the given locale.\n */\nexport const getPrefix = (\n locale: LocalesValues | undefined,\n options: RoutingOptions = {}\n): GetPrefixResult => {\n const { defaultLocale, mode, locales } = resolveRoutingConfig(options);\n\n if (TREE_SHAKE_PREFIX_MODES || !locale || !locales.includes(locale)) {\n return {\n prefix: '',\n localePrefix: undefined,\n };\n }\n\n // Handle prefix-based modes (prefix-all or prefix-no-default)\n const shouldPrefix =\n mode === 'prefix-all' ||\n (mode === 'prefix-no-default' && defaultLocale !== locale);\n\n if (shouldPrefix) {\n return {\n prefix: `${locale}/`,\n localePrefix: locale as Locale,\n };\n }\n\n return {\n prefix: '',\n localePrefix: undefined,\n };\n};\n"],"mappings":";;;;;;;;AAeA,MAAM,0BACJ,QAAQ,IAAI,4BACZ,QAAQ,IAAI,6BAA6B,gBACzC,QAAQ,IAAI,6BAA6B;;;;;AAoB3C,MAAa,wBAAwB,UAA0B,EAAE,MAAM;CACrE,eAAe,sBAAsB,iBAAiB;CACtD,MAAM,SAAS,QAAQ;CACvB,SAAS,sBAAsB,WAAW;CAC1C,SAAS,SAAS;CAClB,GAAG;CACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2DD,MAAa,aACX,QACA,UAA0B,EAAE,KACR;CACpB,MAAM,EAAE,eAAe,MAAM,YAAY,qBAAqB,QAAQ;AAEtE,KAAI,2BAA2B,CAAC,UAAU,CAAC,QAAQ,SAAS,OAAO,CACjE,QAAO;EACL,QAAQ;EACR,cAAc;EACf;AAQH,KAHE,SAAS,gBACR,SAAS,uBAAuB,kBAAkB,OAGnD,QAAO;EACL,QAAQ,GAAG,OAAO;EAClB,cAAc;EACf;AAGH,QAAO;EACL,QAAQ;EACR,cAAc;EACf"}
@@ -1,5 +1,5 @@
1
1
  import { getPrefix } from "./getPrefix.mjs";
2
- import configuration from "@intlayer/config/built";
2
+ import { internationalization, routing } from "@intlayer/config/built";
3
3
  import { DEFAULT_LOCALE } from "@intlayer/config/defaultValues";
4
4
 
5
5
  //#region src/localization/localeMapper.ts
@@ -29,7 +29,7 @@ import { DEFAULT_LOCALE } from "@intlayer/config/defaultValues";
29
29
  * @param mapper - The mapper function that returns an object
30
30
  * @returns An array of objects
31
31
  */
32
- const localeMap = (mapper, locales = configuration?.internationalization.locales ?? [], defaultLocale = configuration?.internationalization.defaultLocale ?? DEFAULT_LOCALE, mode = configuration?.routing?.mode ?? "prefix-no-default") => (locales ?? []).map((locale) => mapper({
32
+ const localeMap = (mapper, locales = internationalization.locales ?? [], defaultLocale = internationalization.defaultLocale ?? DEFAULT_LOCALE, mode = routing?.mode ?? "prefix-no-default") => (locales ?? []).map((locale) => mapper({
33
33
  locale,
34
34
  defaultLocale,
35
35
  locales,
@@ -66,7 +66,7 @@ const localeMap = (mapper, locales = configuration?.internationalization.locales
66
66
  * @param mapper - The mapper function that returns an array of objects
67
67
  * @returns An array of objects
68
68
  */
69
- const localeFlatMap = (mapper, locales = configuration?.internationalization.locales ?? [], defaultLocale = configuration?.internationalization.defaultLocale ?? DEFAULT_LOCALE, mode = configuration?.routing?.mode ?? "prefix-no-default") => locales.flatMap((locale) => mapper({
69
+ const localeFlatMap = (mapper, locales = internationalization.locales ?? [], defaultLocale = internationalization.defaultLocale ?? DEFAULT_LOCALE, mode = routing?.mode ?? "prefix-no-default") => locales.flatMap((locale) => mapper({
70
70
  locale,
71
71
  defaultLocale,
72
72
  locales,
@@ -100,7 +100,7 @@ const localeFlatMap = (mapper, locales = configuration?.internationalization.loc
100
100
  * @param mode - URL routing mode for locale handling (defaults to configured value)
101
101
  * @returns Record mapping locale codes to mapped values
102
102
  */
103
- const localeRecord = (mapper, locales = configuration?.internationalization.locales ?? [], defaultLocale = configuration?.internationalization.defaultLocale ?? DEFAULT_LOCALE, mode = configuration?.routing?.mode ?? "prefix-no-default") => (locales ?? []).reduce((acc, locale) => {
103
+ const localeRecord = (mapper, locales = internationalization.locales ?? [], defaultLocale = internationalization.defaultLocale ?? DEFAULT_LOCALE, mode = routing?.mode ?? "prefix-no-default") => (locales ?? []).reduce((acc, locale) => {
104
104
  acc[locale] = mapper({
105
105
  locale,
106
106
  defaultLocale,
@@ -1 +1 @@
1
- {"version":3,"file":"localeMapper.mjs","names":[],"sources":["../../../src/localization/localeMapper.ts"],"sourcesContent":["import configuration from '@intlayer/config/built';\nimport { DEFAULT_LOCALE } from '@intlayer/config/defaultValues';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { getPrefix } from './getPrefix';\n\nexport type LocaleData = {\n locale: Locale;\n defaultLocale: Locale;\n isDefault: boolean;\n locales: Locale[];\n urlPrefix: string;\n};\n\n/**\n * Map the locale data to an array of objects\n *\n * @example\n * ```ts\n * const routes = localeMap((localizedData) =>\n * ({\n * path: localizedData.urlPrefix,\n * name: localizedData.locale,\n * isDefault: localizedData.isDefault,\n * locales: localizedData.locales,\n * defaultLocale: localizedData.defaultLocale,\n * }),\n * );\n *\n * // Result\n * [\n * { path: '/', name: 'en', isDefault: true, locales: ['en'], defaultLocale: 'en', urlPrefix: '' },\n * { path: '/fr', name: 'fr', isDefault: false, locales: ['fr'], defaultLocale: 'en', urlPrefix: '/fr' },\n * { path: '/es', name: 'es', isDefault: false, locales: ['es'], defaultLocale: 'en', urlPrefix: '/es' },\n * ]\n * ```\n *\n * @param mapper - The mapper function that returns an object\n * @returns An array of objects\n */\nexport const localeMap = <T>(\n mapper: (locale: LocaleData) => T,\n locales: LocalesValues[] = configuration?.internationalization.locales ?? [],\n defaultLocale: LocalesValues = configuration?.internationalization\n .defaultLocale ?? DEFAULT_LOCALE,\n mode:\n | 'prefix-no-default'\n | 'prefix-all'\n | 'no-prefix'\n | 'search-params' = configuration?.routing?.mode ?? 'prefix-no-default'\n): T[] =>\n (locales ?? []).map((locale) =>\n mapper({\n locale,\n defaultLocale,\n locales,\n isDefault: locale === defaultLocale,\n urlPrefix: getPrefix(locale, { defaultLocale, mode, locales })\n .localePrefix\n ? `/${locale}`\n : '',\n } as LocaleData)\n );\n\n/**\n * Flatten the locale map into a single array of objects\n *\n * @example\n * ```ts\n * const routes = localeMap((localizedData) =>\n * [{\n * path: localizedData.urlPrefix,\n * name: localizedData.locale,\n * isDefault: localizedData.isDefault,\n * locales: localizedData.locales,\n * defaultLocale: localizedData.defaultLocale,\n * }],\n * );\n *\n * // Result\n * [\n * path: '/', name: 'en', isDefault: true, locales: ['en'], defaultLocale: 'en', urlPrefix: '' ,\n * path: '/fr', name: 'fr', isDefault: false, locales: ['fr'], defaultLocale: 'en', urlPrefix: '/fr' ,\n * path: '/es', name: 'es', isDefault: false, locales: ['es'], defaultLocale: 'en', urlPrefix: '/es' ,\n * ]\n * ```\n *\n * @param mapper - The mapper function that returns an array of objects\n * @returns An array of objects\n */\nexport const localeFlatMap = <T>(\n mapper: (locale: LocaleData) => T[],\n locales: LocalesValues[] = configuration?.internationalization.locales ?? [],\n defaultLocale: LocalesValues = configuration?.internationalization\n .defaultLocale ?? DEFAULT_LOCALE,\n mode:\n | 'prefix-no-default'\n | 'prefix-all'\n | 'no-prefix'\n | 'search-params' = configuration?.routing?.mode ?? 'prefix-no-default'\n): T[] =>\n locales.flatMap((locale) =>\n mapper({\n locale,\n defaultLocale,\n locales,\n isDefault: locale === defaultLocale,\n urlPrefix: getPrefix(locale, { defaultLocale, mode, locales })\n .localePrefix\n ? `/${locale}`\n : '',\n } as LocaleData)\n );\n\n/**\n * Creates a record object mapping locales to values transformed by the mapper function\n *\n * @example\n * ```ts\n * const translations = localeRecord(({ locale }) =>\n * require(`./translations/${locale}.json`)\n * );\n *\n * // Result\n *\n * en: ... , // Content of translations/en.json\n * fr: ... , // Content of translations/fr.json\n * es: ...\n *\n * ```\n *\n * @param mapper - Function that takes locale data and returns a value for that locale\n * @param locales - Array of locale codes to map over (defaults to configured locales)\n * @param defaultLocale - The default locale (defaults to configured default)\n * @param mode - URL routing mode for locale handling (defaults to configured value)\n * @returns Record mapping locale codes to mapped values\n */\nexport const localeRecord = <T>(\n mapper: (locale: LocaleData) => T,\n locales: LocalesValues[] = configuration?.internationalization.locales ?? [],\n defaultLocale: LocalesValues = configuration?.internationalization\n .defaultLocale ?? DEFAULT_LOCALE,\n mode:\n | 'prefix-no-default'\n | 'prefix-all'\n | 'no-prefix'\n | 'search-params' = configuration?.routing?.mode ?? 'prefix-no-default'\n): Record<LocalesValues, T> =>\n (locales ?? []).reduce(\n (acc, locale) => {\n acc[locale] = mapper({\n locale,\n defaultLocale,\n locales,\n isDefault: locale === defaultLocale,\n urlPrefix: getPrefix(locale, { defaultLocale, mode, locales })\n .localePrefix\n ? `/${locale}`\n : '',\n } as LocaleData);\n return acc;\n },\n {} as Record<LocalesValues, T>\n );\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,MAAa,aACX,QACA,UAA2B,eAAe,qBAAqB,WAAW,EAAE,EAC5E,gBAA+B,eAAe,qBAC3C,iBAAiB,gBACpB,OAIsB,eAAe,SAAS,QAAQ,yBAErD,WAAW,EAAE,EAAE,KAAK,WACnB,OAAO;CACL;CACA;CACA;CACA,WAAW,WAAW;CACtB,WAAW,UAAU,QAAQ;EAAE;EAAe;EAAM;EAAS,CAAC,CAC3D,eACC,IAAI,WACJ;CACL,CAAe,CACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BH,MAAa,iBACX,QACA,UAA2B,eAAe,qBAAqB,WAAW,EAAE,EAC5E,gBAA+B,eAAe,qBAC3C,iBAAiB,gBACpB,OAIsB,eAAe,SAAS,QAAQ,wBAEtD,QAAQ,SAAS,WACf,OAAO;CACL;CACA;CACA;CACA,WAAW,WAAW;CACtB,WAAW,UAAU,QAAQ;EAAE;EAAe;EAAM;EAAS,CAAC,CAC3D,eACC,IAAI,WACJ;CACL,CAAe,CACjB;;;;;;;;;;;;;;;;;;;;;;;;AAyBH,MAAa,gBACX,QACA,UAA2B,eAAe,qBAAqB,WAAW,EAAE,EAC5E,gBAA+B,eAAe,qBAC3C,iBAAiB,gBACpB,OAIsB,eAAe,SAAS,QAAQ,yBAErD,WAAW,EAAE,EAAE,QACb,KAAK,WAAW;AACf,KAAI,UAAU,OAAO;EACnB;EACA;EACA;EACA,WAAW,WAAW;EACtB,WAAW,UAAU,QAAQ;GAAE;GAAe;GAAM;GAAS,CAAC,CAC3D,eACC,IAAI,WACJ;EACL,CAAe;AAChB,QAAO;GAET,EAAE,CACH"}
1
+ {"version":3,"file":"localeMapper.mjs","names":[],"sources":["../../../src/localization/localeMapper.ts"],"sourcesContent":["import { internationalization, routing } from '@intlayer/config/built';\nimport { DEFAULT_LOCALE } from '@intlayer/config/defaultValues';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { getPrefix } from './getPrefix';\n\nexport type LocaleData = {\n locale: Locale;\n defaultLocale: Locale;\n isDefault: boolean;\n locales: Locale[];\n urlPrefix: string;\n};\n\n/**\n * Map the locale data to an array of objects\n *\n * @example\n * ```ts\n * const routes = localeMap((localizedData) =>\n * ({\n * path: localizedData.urlPrefix,\n * name: localizedData.locale,\n * isDefault: localizedData.isDefault,\n * locales: localizedData.locales,\n * defaultLocale: localizedData.defaultLocale,\n * }),\n * );\n *\n * // Result\n * [\n * { path: '/', name: 'en', isDefault: true, locales: ['en'], defaultLocale: 'en', urlPrefix: '' },\n * { path: '/fr', name: 'fr', isDefault: false, locales: ['fr'], defaultLocale: 'en', urlPrefix: '/fr' },\n * { path: '/es', name: 'es', isDefault: false, locales: ['es'], defaultLocale: 'en', urlPrefix: '/es' },\n * ]\n * ```\n *\n * @param mapper - The mapper function that returns an object\n * @returns An array of objects\n */\nexport const localeMap = <T>(\n mapper: (locale: LocaleData) => T,\n locales: LocalesValues[] = internationalization.locales ?? [],\n defaultLocale: LocalesValues = internationalization.defaultLocale ??\n DEFAULT_LOCALE,\n mode:\n | 'prefix-no-default'\n | 'prefix-all'\n | 'no-prefix'\n | 'search-params' = routing?.mode ?? 'prefix-no-default'\n): T[] =>\n (locales ?? []).map((locale) =>\n mapper({\n locale,\n defaultLocale,\n locales,\n isDefault: locale === defaultLocale,\n urlPrefix: getPrefix(locale, { defaultLocale, mode, locales })\n .localePrefix\n ? `/${locale}`\n : '',\n } as LocaleData)\n );\n\n/**\n * Flatten the locale map into a single array of objects\n *\n * @example\n * ```ts\n * const routes = localeMap((localizedData) =>\n * [{\n * path: localizedData.urlPrefix,\n * name: localizedData.locale,\n * isDefault: localizedData.isDefault,\n * locales: localizedData.locales,\n * defaultLocale: localizedData.defaultLocale,\n * }],\n * );\n *\n * // Result\n * [\n * path: '/', name: 'en', isDefault: true, locales: ['en'], defaultLocale: 'en', urlPrefix: '' ,\n * path: '/fr', name: 'fr', isDefault: false, locales: ['fr'], defaultLocale: 'en', urlPrefix: '/fr' ,\n * path: '/es', name: 'es', isDefault: false, locales: ['es'], defaultLocale: 'en', urlPrefix: '/es' ,\n * ]\n * ```\n *\n * @param mapper - The mapper function that returns an array of objects\n * @returns An array of objects\n */\nexport const localeFlatMap = <T>(\n mapper: (locale: LocaleData) => T[],\n locales: LocalesValues[] = internationalization.locales ?? [],\n defaultLocale: LocalesValues = internationalization.defaultLocale ??\n DEFAULT_LOCALE,\n mode:\n | 'prefix-no-default'\n | 'prefix-all'\n | 'no-prefix'\n | 'search-params' = routing?.mode ?? 'prefix-no-default'\n): T[] =>\n locales.flatMap((locale) =>\n mapper({\n locale,\n defaultLocale,\n locales,\n isDefault: locale === defaultLocale,\n urlPrefix: getPrefix(locale, { defaultLocale, mode, locales })\n .localePrefix\n ? `/${locale}`\n : '',\n } as LocaleData)\n );\n\n/**\n * Creates a record object mapping locales to values transformed by the mapper function\n *\n * @example\n * ```ts\n * const translations = localeRecord(({ locale }) =>\n * require(`./translations/${locale}.json`)\n * );\n *\n * // Result\n *\n * en: ... , // Content of translations/en.json\n * fr: ... , // Content of translations/fr.json\n * es: ...\n *\n * ```\n *\n * @param mapper - Function that takes locale data and returns a value for that locale\n * @param locales - Array of locale codes to map over (defaults to configured locales)\n * @param defaultLocale - The default locale (defaults to configured default)\n * @param mode - URL routing mode for locale handling (defaults to configured value)\n * @returns Record mapping locale codes to mapped values\n */\nexport const localeRecord = <T>(\n mapper: (locale: LocaleData) => T,\n locales: LocalesValues[] = internationalization.locales ?? [],\n defaultLocale: LocalesValues = internationalization.defaultLocale ??\n DEFAULT_LOCALE,\n mode:\n | 'prefix-no-default'\n | 'prefix-all'\n | 'no-prefix'\n | 'search-params' = routing?.mode ?? 'prefix-no-default'\n): Record<LocalesValues, T> =>\n (locales ?? []).reduce(\n (acc, locale) => {\n acc[locale] = mapper({\n locale,\n defaultLocale,\n locales,\n isDefault: locale === defaultLocale,\n urlPrefix: getPrefix(locale, { defaultLocale, mode, locales })\n .localePrefix\n ? `/${locale}`\n : '',\n } as LocaleData);\n return acc;\n },\n {} as Record<LocalesValues, T>\n );\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,MAAa,aACX,QACA,UAA2B,qBAAqB,WAAW,EAAE,EAC7D,gBAA+B,qBAAqB,iBAClD,gBACF,OAIsB,SAAS,QAAQ,yBAEtC,WAAW,EAAE,EAAE,KAAK,WACnB,OAAO;CACL;CACA;CACA;CACA,WAAW,WAAW;CACtB,WAAW,UAAU,QAAQ;EAAE;EAAe;EAAM;EAAS,CAAC,CAC3D,eACC,IAAI,WACJ;CACL,CAAe,CACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BH,MAAa,iBACX,QACA,UAA2B,qBAAqB,WAAW,EAAE,EAC7D,gBAA+B,qBAAqB,iBAClD,gBACF,OAIsB,SAAS,QAAQ,wBAEvC,QAAQ,SAAS,WACf,OAAO;CACL;CACA;CACA;CACA,WAAW,WAAW;CACtB,WAAW,UAAU,QAAQ;EAAE;EAAe;EAAM;EAAS,CAAC,CAC3D,eACC,IAAI,WACJ;CACL,CAAe,CACjB;;;;;;;;;;;;;;;;;;;;;;;;AAyBH,MAAa,gBACX,QACA,UAA2B,qBAAqB,WAAW,EAAE,EAC7D,gBAA+B,qBAAqB,iBAClD,gBACF,OAIsB,SAAS,QAAQ,yBAEtC,WAAW,EAAE,EAAE,QACb,KAAK,WAAW;AACf,KAAI,UAAU,OAAO;EACnB;EACA;EACA;EACA,WAAW,WAAW;EACtB,WAAW,UAAU,QAAQ;GAAE;GAAe;GAAM;GAAS,CAAC,CAC3D,eACC,IAAI,WACJ;EACL,CAAe;AAChB,QAAO;GAET,EAAE,CACH"}
@@ -1,11 +1,11 @@
1
- import configuration from "@intlayer/config/built";
1
+ import { internationalization } from "@intlayer/config/built";
2
2
 
3
3
  //#region src/localization/localeResolver.ts
4
4
  /**
5
5
  * Resolves the most specific locale from a user-provided list,
6
6
  * or falls back to the default locale if no match is found.
7
7
  */
8
- const localeResolver = (selectedLocale, locales = configuration?.internationalization?.locales, defaultLocale = configuration?.internationalization?.defaultLocale) => {
8
+ const localeResolver = (selectedLocale, locales = internationalization?.locales, defaultLocale = internationalization?.defaultLocale) => {
9
9
  const requestedLocales = [selectedLocale].flat();
10
10
  const normalize = (locale) => locale.trim().toLowerCase();
11
11
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"localeResolver.mjs","names":[],"sources":["../../../src/localization/localeResolver.ts"],"sourcesContent":["import configuration from '@intlayer/config/built';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\n/**\n * Resolves the most specific locale from a user-provided list,\n * or falls back to the default locale if no match is found.\n */\nexport const localeResolver = (\n selectedLocale: LocalesValues | LocalesValues[],\n locales: LocalesValues[] = configuration?.internationalization?.locales,\n defaultLocale: LocalesValues = configuration?.internationalization\n ?.defaultLocale\n): Locale => {\n // Ensure we can handle both a single locale or an array of locales uniformly\n const requestedLocales = [selectedLocale].flat();\n\n // Simple helper to normalize locale strings (e.g. \"en-US\" => \"en-us\")\n const normalize = (locale: string): string => locale.trim().toLowerCase();\n\n try {\n // Check each requested locale in order\n for (const requested of requestedLocales) {\n const normalizedRequested = normalize(requested);\n\n // Attempt exact match\n const exactMatch = locales.find(\n (locale) => normalize(locale) === normalizedRequested\n );\n if (exactMatch) {\n return exactMatch as Locale;\n }\n\n // Attempt partial match on language subtag\n // e.g. if requested is \"en-US\" and not found,\n // see if \"en\" is available among locales\n const [requestedLang] = normalizedRequested.split('-');\n const partialMatch = locales.find(\n (locale) => normalize(locale).split('-')[0] === requestedLang\n );\n if (partialMatch) {\n return partialMatch as Locale;\n }\n }\n } catch {\n // If anything unexpected happened, fall back to default\n }\n\n // If no match was found, return the default\n return defaultLocale as Locale;\n};\n"],"mappings":";;;;;;;AAQA,MAAa,kBACX,gBACA,UAA2B,eAAe,sBAAsB,SAChE,gBAA+B,eAAe,sBAC1C,kBACO;CAEX,MAAM,mBAAmB,CAAC,eAAe,CAAC,MAAM;CAGhD,MAAM,aAAa,WAA2B,OAAO,MAAM,CAAC,aAAa;AAEzE,KAAI;AAEF,OAAK,MAAM,aAAa,kBAAkB;GACxC,MAAM,sBAAsB,UAAU,UAAU;GAGhD,MAAM,aAAa,QAAQ,MACxB,WAAW,UAAU,OAAO,KAAK,oBACnC;AACD,OAAI,WACF,QAAO;GAMT,MAAM,CAAC,iBAAiB,oBAAoB,MAAM,IAAI;GACtD,MAAM,eAAe,QAAQ,MAC1B,WAAW,UAAU,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,cACjD;AACD,OAAI,aACF,QAAO;;SAGL;AAKR,QAAO"}
1
+ {"version":3,"file":"localeResolver.mjs","names":[],"sources":["../../../src/localization/localeResolver.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\n/**\n * Resolves the most specific locale from a user-provided list,\n * or falls back to the default locale if no match is found.\n */\nexport const localeResolver = (\n selectedLocale: LocalesValues | LocalesValues[],\n locales: LocalesValues[] = internationalization?.locales,\n defaultLocale: LocalesValues = internationalization?.defaultLocale\n): Locale => {\n // Ensure we can handle both a single locale or an array of locales uniformly\n const requestedLocales = [selectedLocale].flat();\n\n // Simple helper to normalize locale strings (e.g. \"en-US\" => \"en-us\")\n const normalize = (locale: string): string => locale.trim().toLowerCase();\n\n try {\n // Check each requested locale in order\n for (const requested of requestedLocales) {\n const normalizedRequested = normalize(requested);\n\n // Attempt exact match\n const exactMatch = locales.find(\n (locale) => normalize(locale) === normalizedRequested\n );\n if (exactMatch) {\n return exactMatch as Locale;\n }\n\n // Attempt partial match on language subtag\n // e.g. if requested is \"en-US\" and not found,\n // see if \"en\" is available among locales\n const [requestedLang] = normalizedRequested.split('-');\n const partialMatch = locales.find(\n (locale) => normalize(locale).split('-')[0] === requestedLang\n );\n if (partialMatch) {\n return partialMatch as Locale;\n }\n }\n } catch {\n // If anything unexpected happened, fall back to default\n }\n\n // If no match was found, return the default\n return defaultLocale as Locale;\n};\n"],"mappings":";;;;;;;AAQA,MAAa,kBACX,gBACA,UAA2B,sBAAsB,SACjD,gBAA+B,sBAAsB,kBAC1C;CAEX,MAAM,mBAAmB,CAAC,eAAe,CAAC,MAAM;CAGhD,MAAM,aAAa,WAA2B,OAAO,MAAM,CAAC,aAAa;AAEzE,KAAI;AAEF,OAAK,MAAM,aAAa,kBAAkB;GACxC,MAAM,sBAAsB,UAAU,UAAU;GAGhD,MAAM,aAAa,QAAQ,MACxB,WAAW,UAAU,OAAO,KAAK,oBACnC;AACD,OAAI,WACF,QAAO;GAMT,MAAM,CAAC,iBAAiB,oBAAoB,MAAM,IAAI;GACtD,MAAM,eAAe,QAAQ,MAC1B,WAAW,UAAU,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,cACjD;AACD,OAAI,aACF,QAAO;;SAGL;AAKR,QAAO"}
@@ -1,7 +1,10 @@
1
- import { TREE_SHAKE_REWRITE } from "@intlayer/config/envVars";
2
-
3
1
  //#region src/localization/rewriteUtils.ts
4
2
  /**
3
+ * True when rewrite rules are explicitly disabled at build time
4
+ * (INTLAYER_ROUTING_REWRITE_RULES === 'false').
5
+ */
6
+ const TREE_SHAKE_REWRITE = process.env["INTLAYER_ROUTING_REWRITE_RULES"] === "false";
7
+ /**
5
8
  * Normalizes legacy Record format or extracts specialized rules from RewriteObject.
6
9
  */
7
10
  const getRewriteRules = (rewrite, context = "url") => {
@@ -1 +1 @@
1
- {"version":3,"file":"rewriteUtils.mjs","names":[],"sources":["../../../src/localization/rewriteUtils.ts"],"sourcesContent":["import { TREE_SHAKE_REWRITE } from '@intlayer/config/envVars';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type {\n RewriteObject,\n RewriteRules,\n RoutingConfig,\n} from '@intlayer/types/config';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\nexport type LocalizedPathResult = {\n path: string;\n isRewritten: boolean;\n};\n\n/**\n * Normalizes legacy Record format or extracts specialized rules from RewriteObject.\n */\nexport const getRewriteRules = (\n rewrite: RoutingConfig['rewrite'],\n context: keyof RewriteObject = 'url'\n): RewriteRules | undefined => {\n if (!rewrite || TREE_SHAKE_REWRITE) return undefined;\n\n if ('url' in rewrite) {\n return (rewrite as RewriteObject)[context];\n }\n\n // Normalize legacy format\n return {\n rules: Object.entries(rewrite).map(([canonical, localized]) => ({\n // Normalize canonical path\n canonical: canonical.startsWith('/')\n ? canonical.replace(/\\[([^\\]]+)\\]/g, ':$1')\n : `/${canonical.replace(/\\[([^\\]]+)\\]/g, ':$1')}`,\n\n // Normalize localized path\n localized: Object.fromEntries(\n Object.entries(localized).map(([locale, pattern]) => {\n const normalizedPattern = pattern?.startsWith('/')\n ? pattern.replace(/\\[([^\\]]+)\\]/g, ':$1')\n : `/${(pattern || '').replace(/\\[([^\\]]+)\\]/g, ':$1')}`;\n return [locale, normalizedPattern];\n })\n ),\n })),\n };\n};\n\n/**\n * Converts normalized pattern to Regex.\n * Internal syntax supports:\n * - :param -> ([^/]+) (one segment)\n * - :param* -> (.*) (zero or more segments)\n * - :param+ -> (.+) (one or more segments)\n * - :param? -> ([^/]*) (zero or one segment)\n */\nconst patternToRegex = (pattern: string) => {\n const regexString = pattern\n .replace(/\\//g, '\\\\/') // Escape slashes\n .replace(/\\\\\\/:(?:[^/\\\\*+?]+)\\*/g, '(?:\\\\/(.*))?') // /:param*\n .replace(/\\\\\\/:(?:[^/\\\\*+?]+)\\?/g, '(?:\\\\/([^\\\\/]+))?') // /:param?\n .replace(/:([^/\\\\*+?]+)\\*/g, '(.*)') // :param* (if no leading slash)\n .replace(/:([^/\\\\*+?]+)\\?/g, '([^\\\\/]*)') // :param? (if no leading slash)\n .replace(/:([^/\\\\*+?]+)\\+/g, '(.+)') // :param+\n .replace(/:([^/\\\\*+?]+)/g, '([^\\\\/]+)'); // :param\n\n return new RegExp(`^${regexString}$`);\n};\n\n/**\n * Replaces route parameters in a path with provided values.\n */\nconst fillPath = (pattern: string, params: string[]) => {\n let index = 0;\n return (\n pattern\n .replace(/:([^/\\\\*+?]+)[*+?]?/g, () => params[index++] ?? '')\n .replace(/\\/+/g, '/')\n .replace(/\\/$/, '') || '/'\n );\n};\n\n/**\n * Extract values from a URL based on a pattern.\n */\nconst extractParams = (url: string, pattern: string): string[] | null => {\n const regex = patternToRegex(pattern);\n const match = url.match(regex);\n return match ? match.slice(1) : null;\n};\n\n/**\n * Given a localized URL (e.g., \"/produits/123\"), finds the canonical internal path (e.g., \"/products/123\").\n * If locale is provided, only check for that locale. Otherwise, check for all locales.\n */\nexport const getCanonicalPath = (\n localizedPath: string,\n locale?: Locale,\n rewriteRules?: RewriteRules\n): string => {\n if (!rewriteRules || TREE_SHAKE_REWRITE) return localizedPath;\n\n for (const rule of rewriteRules.rules) {\n const { canonical, localized } = rule;\n const localesToCheck = locale ? [locale] : Object.keys(localized);\n\n for (const loc of localesToCheck) {\n const localizedPattern = localized[loc as keyof typeof localized];\n\n if (!localizedPattern) continue;\n\n const params = extractParams(localizedPath, localizedPattern);\n\n if (params) {\n return fillPath(canonical, params);\n }\n }\n }\n\n return localizedPath;\n};\n\n/**\n * Given a canonical path (e.g., \"/products/123\"), finds the localized URL pattern (e.g., \"/produits/123\").\n */\nexport const getLocalizedPath = (\n canonicalPath: string,\n locale: LocalesValues,\n rewriteRules?: RewriteRules\n): LocalizedPathResult => {\n if (!rewriteRules || TREE_SHAKE_REWRITE)\n return { path: canonicalPath, isRewritten: false };\n\n for (const rule of rewriteRules.rules) {\n const { canonical, localized } = rule;\n\n // Check if the input path matches a configured canonical pattern\n const params = extractParams(canonicalPath, canonical);\n\n if (params) {\n const targetPattern = localized[locale as keyof typeof localized];\n\n if (targetPattern) {\n return {\n path: fillPath(targetPattern, params),\n isRewritten: true,\n };\n }\n }\n }\n\n return { path: canonicalPath, isRewritten: false };\n};\n\n/**\n * Returns the internal path for a given canonical path and locale.\n * Ensures the locale prefix is present exactly once.\n */\nexport const getInternalPath = (\n canonicalPath: string,\n locale: Locale\n): string => {\n const pathWithLeadingSlash = canonicalPath.startsWith('/')\n ? canonicalPath\n : `/${canonicalPath}`;\n\n if (\n pathWithLeadingSlash.startsWith(`/${locale}/`) ||\n pathWithLeadingSlash === `/${locale}`\n ) {\n return pathWithLeadingSlash;\n }\n\n return `/${locale}${pathWithLeadingSlash === '/' ? '' : pathWithLeadingSlash}`;\n};\n\n/**\n * Given a current pathname and locale, returns the pretty localized path if a rewrite rule exists and the path is not already localized.\n */\nexport const getRewritePath = (\n pathname: string,\n locale: Locale,\n rewrite?: RoutingConfig['rewrite']\n): string | undefined => {\n if (TREE_SHAKE_REWRITE) return undefined;\n const rules = getRewriteRules(rewrite, 'url');\n if (!rules) return undefined;\n\n // Identify canonical path (relative to root, no locale prefix expected in 'url' context)\n const canonicalPath = getCanonicalPath(pathname, undefined, rules);\n\n // Get the localized path for the current locale\n const { path: localizedPath, isRewritten } = getLocalizedPath(\n canonicalPath,\n locale,\n rules\n );\n\n if (isRewritten && localizedPath !== pathname) {\n return localizedPath;\n }\n\n return undefined;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"rewriteUtils.mjs","names":[],"sources":["../../../src/localization/rewriteUtils.ts"],"sourcesContent":["// ── Tree-shake constants ──────────────────────────────────────────────────────\n// When these env vars are injected at build time, bundlers eliminate the\n// branches guarded by these constants.\n\n/**\n * True when rewrite rules are explicitly disabled at build time\n * (INTLAYER_ROUTING_REWRITE_RULES === 'false').\n */\nconst TREE_SHAKE_REWRITE =\n process.env['INTLAYER_ROUTING_REWRITE_RULES'] === 'false';\n\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type {\n RewriteObject,\n RewriteRules,\n RoutingConfig,\n} from '@intlayer/types/config';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\nexport type LocalizedPathResult = {\n path: string;\n isRewritten: boolean;\n};\n\n/**\n * Normalizes legacy Record format or extracts specialized rules from RewriteObject.\n */\nexport const getRewriteRules = (\n rewrite: RoutingConfig['rewrite'],\n context: keyof RewriteObject = 'url'\n): RewriteRules | undefined => {\n if (!rewrite || TREE_SHAKE_REWRITE) return undefined;\n\n if ('url' in rewrite) {\n return (rewrite as RewriteObject)[context];\n }\n\n // Normalize legacy format\n return {\n rules: Object.entries(rewrite).map(([canonical, localized]) => ({\n // Normalize canonical path\n canonical: canonical.startsWith('/')\n ? canonical.replace(/\\[([^\\]]+)\\]/g, ':$1')\n : `/${canonical.replace(/\\[([^\\]]+)\\]/g, ':$1')}`,\n\n // Normalize localized path\n localized: Object.fromEntries(\n Object.entries(localized).map(([locale, pattern]) => {\n const normalizedPattern = pattern?.startsWith('/')\n ? pattern.replace(/\\[([^\\]]+)\\]/g, ':$1')\n : `/${(pattern || '').replace(/\\[([^\\]]+)\\]/g, ':$1')}`;\n return [locale, normalizedPattern];\n })\n ),\n })),\n };\n};\n\n/**\n * Converts normalized pattern to Regex.\n * Internal syntax supports:\n * - :param -> ([^/]+) (one segment)\n * - :param* -> (.*) (zero or more segments)\n * - :param+ -> (.+) (one or more segments)\n * - :param? -> ([^/]*) (zero or one segment)\n */\nconst patternToRegex = (pattern: string) => {\n const regexString = pattern\n .replace(/\\//g, '\\\\/') // Escape slashes\n .replace(/\\\\\\/:(?:[^/\\\\*+?]+)\\*/g, '(?:\\\\/(.*))?') // /:param*\n .replace(/\\\\\\/:(?:[^/\\\\*+?]+)\\?/g, '(?:\\\\/([^\\\\/]+))?') // /:param?\n .replace(/:([^/\\\\*+?]+)\\*/g, '(.*)') // :param* (if no leading slash)\n .replace(/:([^/\\\\*+?]+)\\?/g, '([^\\\\/]*)') // :param? (if no leading slash)\n .replace(/:([^/\\\\*+?]+)\\+/g, '(.+)') // :param+\n .replace(/:([^/\\\\*+?]+)/g, '([^\\\\/]+)'); // :param\n\n return new RegExp(`^${regexString}$`);\n};\n\n/**\n * Replaces route parameters in a path with provided values.\n */\nconst fillPath = (pattern: string, params: string[]) => {\n let index = 0;\n return (\n pattern\n .replace(/:([^/\\\\*+?]+)[*+?]?/g, () => params[index++] ?? '')\n .replace(/\\/+/g, '/')\n .replace(/\\/$/, '') || '/'\n );\n};\n\n/**\n * Extract values from a URL based on a pattern.\n */\nconst extractParams = (url: string, pattern: string): string[] | null => {\n const regex = patternToRegex(pattern);\n const match = url.match(regex);\n return match ? match.slice(1) : null;\n};\n\n/**\n * Given a localized URL (e.g., \"/produits/123\"), finds the canonical internal path (e.g., \"/products/123\").\n * If locale is provided, only check for that locale. Otherwise, check for all locales.\n */\nexport const getCanonicalPath = (\n localizedPath: string,\n locale?: Locale,\n rewriteRules?: RewriteRules\n): string => {\n if (!rewriteRules || TREE_SHAKE_REWRITE) return localizedPath;\n\n for (const rule of rewriteRules.rules) {\n const { canonical, localized } = rule;\n const localesToCheck = locale ? [locale] : Object.keys(localized);\n\n for (const loc of localesToCheck) {\n const localizedPattern = localized[loc as keyof typeof localized];\n\n if (!localizedPattern) continue;\n\n const params = extractParams(localizedPath, localizedPattern);\n\n if (params) {\n return fillPath(canonical, params);\n }\n }\n }\n\n return localizedPath;\n};\n\n/**\n * Given a canonical path (e.g., \"/products/123\"), finds the localized URL pattern (e.g., \"/produits/123\").\n */\nexport const getLocalizedPath = (\n canonicalPath: string,\n locale: LocalesValues,\n rewriteRules?: RewriteRules\n): LocalizedPathResult => {\n if (!rewriteRules || TREE_SHAKE_REWRITE)\n return { path: canonicalPath, isRewritten: false };\n\n for (const rule of rewriteRules.rules) {\n const { canonical, localized } = rule;\n\n // Check if the input path matches a configured canonical pattern\n const params = extractParams(canonicalPath, canonical);\n\n if (params) {\n const targetPattern = localized[locale as keyof typeof localized];\n\n if (targetPattern) {\n return {\n path: fillPath(targetPattern, params),\n isRewritten: true,\n };\n }\n }\n }\n\n return { path: canonicalPath, isRewritten: false };\n};\n\n/**\n * Returns the internal path for a given canonical path and locale.\n * Ensures the locale prefix is present exactly once.\n */\nexport const getInternalPath = (\n canonicalPath: string,\n locale: Locale\n): string => {\n const pathWithLeadingSlash = canonicalPath.startsWith('/')\n ? canonicalPath\n : `/${canonicalPath}`;\n\n if (\n pathWithLeadingSlash.startsWith(`/${locale}/`) ||\n pathWithLeadingSlash === `/${locale}`\n ) {\n return pathWithLeadingSlash;\n }\n\n return `/${locale}${pathWithLeadingSlash === '/' ? '' : pathWithLeadingSlash}`;\n};\n\n/**\n * Given a current pathname and locale, returns the pretty localized path if a rewrite rule exists and the path is not already localized.\n */\nexport const getRewritePath = (\n pathname: string,\n locale: Locale,\n rewrite?: RoutingConfig['rewrite']\n): string | undefined => {\n if (TREE_SHAKE_REWRITE) return undefined;\n const rules = getRewriteRules(rewrite, 'url');\n if (!rules) return undefined;\n\n // Identify canonical path (relative to root, no locale prefix expected in 'url' context)\n const canonicalPath = getCanonicalPath(pathname, undefined, rules);\n\n // Get the localized path for the current locale\n const { path: localizedPath, isRewritten } = getLocalizedPath(\n canonicalPath,\n locale,\n rules\n );\n\n if (isRewritten && localizedPath !== pathname) {\n return localizedPath;\n }\n\n return undefined;\n};\n"],"mappings":"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"}
@@ -1,8 +1,15 @@
1
1
  import { getPrefix, resolveRoutingConfig } from "./getPrefix.mjs";
2
- import { TREE_SHAKE_NO_PREFIX, TREE_SHAKE_SEARCH_PARAMS } from "@intlayer/config/envVars";
3
2
 
4
3
  //#region src/localization/validatePrefix.ts
5
4
  /**
5
+ * True when the build-time routing mode is known and is NOT 'no-prefix'.
6
+ */
7
+ const TREE_SHAKE_NO_PREFIX = process.env["INTLAYER_ROUTING_MODE"] && process.env["INTLAYER_ROUTING_MODE"] !== "no-prefix";
8
+ /**
9
+ * True when the build-time routing mode is known and is NOT 'search-params'.
10
+ */
11
+ const TREE_SHAKE_SEARCH_PARAMS = process.env["INTLAYER_ROUTING_MODE"] && process.env["INTLAYER_ROUTING_MODE"] !== "search-params";
12
+ /**
6
13
  * Checks whether a given locale is valid based on the configured locales.
7
14
  *
8
15
  * @param locale - The locale value to validate. Can be `undefined` or `null`.
@@ -1 +1 @@
1
- {"version":3,"file":"validatePrefix.mjs","names":[],"sources":["../../../src/localization/validatePrefix.ts"],"sourcesContent":["import {\n TREE_SHAKE_NO_PREFIX,\n TREE_SHAKE_SEARCH_PARAMS,\n} from '@intlayer/config/envVars';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport {\n getPrefix,\n type RoutingOptions,\n resolveRoutingConfig,\n} from './getPrefix';\n\nexport type ValidatePrefixResult = {\n isValid: boolean;\n localePrefix: string | undefined;\n};\n\n/**\n * Checks whether a given locale is valid based on the configured locales.\n *\n * @param locale - The locale value to validate. Can be `undefined` or `null`.\n * @param options - Optional configuration to override default settings.\n * @param options.locales - Array of valid locales. Defaults to the configured internationalization locales.\n * @param options.defaultLocale - The default locale to use as fallback. Defaults to the configured default locale.\n * @param options.mode - The routing mode (`'prefix'`, `'prefix-all'`, or `'no-prefix'`). Defaults to the configured routing mode.\n * @returns An object containing the validation result and the locale prefix.\n *\n * @example\n * // Check if 'en' is a valid locale\n * const { isValid, localePrefix } = validatePrefix('en');\n *\n * @example\n * // Check with custom options\n * const { isValid, localePrefix } = validatePrefix('fr', {\n * locales: ['en', 'fr', 'es'],\n * defaultLocale: 'en',\n * mode: 'prefix-all',\n * });\n */\nexport const validatePrefix = (\n locale: LocalesValues | undefined | null,\n options?: RoutingOptions\n): ValidatePrefixResult => {\n const { defaultLocale, mode, locales } = resolveRoutingConfig(options);\n\n if (\n (!TREE_SHAKE_NO_PREFIX && mode === 'no-prefix') ||\n (!TREE_SHAKE_SEARCH_PARAMS && mode === 'search-params')\n ) {\n return { isValid: true, localePrefix: undefined };\n }\n\n const { localePrefix } = getPrefix(locale || defaultLocale, {\n mode,\n locales,\n defaultLocale,\n });\n\n if (localePrefix === locale && locale === undefined) {\n return { isValid: true, localePrefix: undefined };\n }\n\n const isValid = locales.some((localeEl) => localeEl === locale);\n\n return { isValid, localePrefix };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,MAAa,kBACX,QACA,YACyB;CACzB,MAAM,EAAE,eAAe,MAAM,YAAY,qBAAqB,QAAQ;AAEtE,KACG,CAAC,wBAAwB,SAAS,eAClC,CAAC,4BAA4B,SAAS,gBAEvC,QAAO;EAAE,SAAS;EAAM,cAAc;EAAW;CAGnD,MAAM,EAAE,iBAAiB,UAAU,UAAU,eAAe;EAC1D;EACA;EACA;EACD,CAAC;AAEF,KAAI,iBAAiB,UAAU,WAAW,OACxC,QAAO;EAAE,SAAS;EAAM,cAAc;EAAW;AAKnD,QAAO;EAAE,SAFO,QAAQ,MAAM,aAAa,aAAa,OAAO;EAE7C;EAAc"}
1
+ {"version":3,"file":"validatePrefix.mjs","names":[],"sources":["../../../src/localization/validatePrefix.ts"],"sourcesContent":["// ── Tree-shake constants ──────────────────────────────────────────────────────\n// When these env vars are injected at build time, bundlers eliminate the\n// branches guarded by these constants.\n\n/**\n * True when the build-time routing mode is known and is NOT 'no-prefix'.\n */\nconst TREE_SHAKE_NO_PREFIX =\n process.env['INTLAYER_ROUTING_MODE'] &&\n process.env['INTLAYER_ROUTING_MODE'] !== 'no-prefix';\n\n/**\n * True when the build-time routing mode is known and is NOT 'search-params'.\n */\nconst TREE_SHAKE_SEARCH_PARAMS =\n process.env['INTLAYER_ROUTING_MODE'] &&\n process.env['INTLAYER_ROUTING_MODE'] !== 'search-params';\n\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport {\n getPrefix,\n type RoutingOptions,\n resolveRoutingConfig,\n} from './getPrefix';\n\nexport type ValidatePrefixResult = {\n isValid: boolean;\n localePrefix: string | undefined;\n};\n\n/**\n * Checks whether a given locale is valid based on the configured locales.\n *\n * @param locale - The locale value to validate. Can be `undefined` or `null`.\n * @param options - Optional configuration to override default settings.\n * @param options.locales - Array of valid locales. Defaults to the configured internationalization locales.\n * @param options.defaultLocale - The default locale to use as fallback. Defaults to the configured default locale.\n * @param options.mode - The routing mode (`'prefix'`, `'prefix-all'`, or `'no-prefix'`). Defaults to the configured routing mode.\n * @returns An object containing the validation result and the locale prefix.\n *\n * @example\n * // Check if 'en' is a valid locale\n * const { isValid, localePrefix } = validatePrefix('en');\n *\n * @example\n * // Check with custom options\n * const { isValid, localePrefix } = validatePrefix('fr', {\n * locales: ['en', 'fr', 'es'],\n * defaultLocale: 'en',\n * mode: 'prefix-all',\n * });\n */\nexport const validatePrefix = (\n locale: LocalesValues | undefined | null,\n options?: RoutingOptions\n): ValidatePrefixResult => {\n const { defaultLocale, mode, locales } = resolveRoutingConfig(options);\n\n if (\n (!TREE_SHAKE_NO_PREFIX && mode === 'no-prefix') ||\n (!TREE_SHAKE_SEARCH_PARAMS && mode === 'search-params')\n ) {\n return { isValid: true, localePrefix: undefined };\n }\n\n const { localePrefix } = getPrefix(locale || defaultLocale, {\n mode,\n locales,\n defaultLocale,\n });\n\n if (localePrefix === locale && locale === undefined) {\n return { isValid: true, localePrefix: undefined };\n }\n\n const isValid = locales.some((localeEl) => localeEl === locale);\n\n return { isValid, localePrefix };\n};\n"],"mappings":";;;;;;AAOA,MAAM,uBACJ,QAAQ,IAAI,4BACZ,QAAQ,IAAI,6BAA6B;;;;AAK3C,MAAM,2BACJ,QAAQ,IAAI,4BACZ,QAAQ,IAAI,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;AAoC3C,MAAa,kBACX,QACA,YACyB;CACzB,MAAM,EAAE,eAAe,MAAM,YAAY,qBAAqB,QAAQ;AAEtE,KACG,CAAC,wBAAwB,SAAS,eAClC,CAAC,4BAA4B,SAAS,gBAEvC,QAAO;EAAE,SAAS;EAAM,cAAc;EAAW;CAGnD,MAAM,EAAE,iBAAiB,UAAU,UAAU,eAAe;EAC1D;EACA;EACA;EACD,CAAC;AAEF,KAAI,iBAAiB,UAAU,WAAW,OACxC,QAAO;EAAE,SAAS;EAAM,cAAc;EAAW;AAKnD,QAAO;EAAE,SAFO,QAAQ,MAAM,aAAa,aAAa,OAAO;EAE7C;EAAc"}
@@ -1,4 +1,4 @@
1
- import configuration from "@intlayer/config/built";
1
+ import { internationalization } from "@intlayer/config/built";
2
2
 
3
3
  //#region src/utils/intl.ts
4
4
  /**
@@ -32,7 +32,7 @@ const cache = /* @__PURE__ */ new Map();
32
32
  * Generic caching instantiator for Intl constructors.
33
33
  */
34
34
  const getCachedIntl = (Ctor, locale, options) => {
35
- const resLoc = locale ?? configuration?.internationalization?.defaultLocale;
35
+ const resLoc = locale ?? internationalization?.defaultLocale;
36
36
  const key = `${resLoc}|${options ? JSON.stringify(options) : ""}`;
37
37
  let ctorCache = cache.get(Ctor);
38
38
  if (!ctorCache) {
@@ -1 +1 @@
1
- {"version":3,"file":"intl.mjs","names":[],"sources":["../../../src/utils/intl.ts"],"sourcesContent":["/**\n * Cached Intl helper – drop‑in replacement for the global `Intl` object.\n * ‑‑‑\n * • Uses a `Proxy` to lazily wrap every *constructor* hanging off `Intl` (NumberFormat, DateTimeFormat, …).\n * • Each wrapped constructor keeps an in‑memory cache keyed by `[locales, options]` so that identical requests\n * reuse the same heavy instance instead of reparsing CLDR data every time.\n * • A polyfill warning for `Intl.DisplayNames` is emitted only once and only in dev.\n * • The public API is fully type‑safe and mirrors the native `Intl` surface exactly –\n * you can treat `CachedIntl` just like the built‑in `Intl`.\n *\n * Usage @example:\n * ---------------\n * ```ts\n * import { CachedIntl } from \"./cached-intl\";\n *\n * const nf = CachedIntl.NumberFormat(\"en-US\", { style: \"currency\", currency: \"USD\" });\n * console.log(nf.format(1234));\n *\n * const dn = CachedIntl.DisplayNames([\"fr\"], { type: \"language\" });\n * console.log(dn.of(\"en\")); * → \"anglais\"\n *\n * You can also spin up an isolated instance with its own caches (handy in test suites):\n * const TestIntl = createCachedIntl();\n * ```\n */\n\nimport configuration from '@intlayer/config/built';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\nconst MAX_CACHE_SIZE = 50;\nconst cache = new Map<any, Map<string, any>>();\n\ntype IntlConstructors = {\n [K in keyof typeof Intl as (typeof Intl)[K] extends new (\n ...args: any\n ) => any\n ? K\n : never]: (typeof Intl)[K];\n};\n\ntype ReplaceLocaleWithLocalesValues<T> = T extends new (\n locales: any,\n options?: infer Options\n) => infer Instance\n ? {\n new (locales?: LocalesValues, options?: Options): Instance;\n new (options?: Options & { locale?: LocalesValues }): Instance;\n (locales?: LocalesValues, options?: Options): Instance;\n (options?: Options & { locale?: LocalesValues }): Instance;\n }\n : T extends new (\n locales: any\n ) => infer Instance\n ? {\n new (locales?: LocalesValues): Instance;\n new (options?: { locale?: LocalesValues }): Instance;\n (locales?: LocalesValues): Instance;\n (options?: { locale?: LocalesValues }): Instance;\n }\n : T;\n\nexport type WrappedIntl = {\n [K in keyof typeof Intl]: K extends keyof IntlConstructors\n ? ReplaceLocaleWithLocalesValues<(typeof Intl)[K]>\n : (typeof Intl)[K];\n};\n\n/**\n * Generic caching instantiator for Intl constructors.\n */\nexport const getCachedIntl = <T extends new (...args: any[]) => any>(\n Ctor: T,\n locale?: LocalesValues | string,\n options?: any\n): InstanceType<T> => {\n const resLoc = locale ?? configuration?.internationalization?.defaultLocale;\n const optKey = options ? JSON.stringify(options) : '';\n const key = `${resLoc}|${optKey}`;\n\n let ctorCache = cache.get(Ctor);\n\n if (!ctorCache) {\n ctorCache = new Map();\n cache.set(Ctor, ctorCache);\n }\n\n let instance = ctorCache.get(key);\n\n if (!instance) {\n if (ctorCache.size > MAX_CACHE_SIZE) ctorCache.clear();\n instance = new Ctor(resLoc, options);\n ctorCache.set(key, instance);\n }\n return instance;\n};\n\n/**\n * Optional: Keep bindIntl if your library exports it publicly.\n * It now uses the much smaller getCachedIntl under the hood.\n */\nexport const bindIntl = (boundLocale: LocalesValues): WrappedIntl => {\n const bindWrap = (Ctor: any) =>\n // function is used as a constructor, do not change in arrow function\n function intlConstructor(locales?: any, options?: any) {\n const isOptsFirst =\n locales !== null &&\n typeof locales === 'object' &&\n !Array.isArray(locales);\n const resOpts = isOptsFirst ? locales : options;\n const resLoc = isOptsFirst\n ? (resOpts as any).locale || boundLocale\n : locales || boundLocale;\n\n return getCachedIntl(Ctor, resLoc, resOpts);\n };\n\n return {\n ...Intl,\n Collator: bindWrap(Intl.Collator),\n DateTimeFormat: bindWrap(Intl.DateTimeFormat),\n DisplayNames: bindWrap(Intl.DisplayNames),\n ListFormat: bindWrap(Intl.ListFormat),\n NumberFormat: bindWrap(Intl.NumberFormat),\n PluralRules: bindWrap(Intl.PluralRules),\n RelativeTimeFormat: bindWrap(Intl.RelativeTimeFormat),\n Locale: bindWrap(Intl.Locale),\n Segmenter: bindWrap((Intl as any).Segmenter),\n } as unknown as WrappedIntl;\n};\n\n// Add this to the bottom of utils/intl.ts ONLY if required for public API compatibility.\nexport const CachedIntl = {\n // function is used as a constructor, do not change in arrow function\n Collator: function Collator(locales?: any, options?: any) {\n return getCachedIntl(Intl.Collator, locales, options);\n },\n DateTimeFormat: function DateTimeFormat(locales?: any, options?: any) {\n return getCachedIntl(Intl.DateTimeFormat, locales, options);\n },\n DisplayNames: function DisplayNames(locales?: any, options?: any) {\n return getCachedIntl(Intl.DisplayNames, locales, options);\n },\n ListFormat: function ListFormat(locales?: any, options?: any) {\n return getCachedIntl(Intl.ListFormat as any, locales, options);\n },\n NumberFormat: function NumberFormat(locales?: any, options?: any) {\n return getCachedIntl(Intl.NumberFormat, locales, options);\n },\n PluralRules: function PluralRules(locales?: any, options?: any) {\n return getCachedIntl(Intl.PluralRules, locales, options);\n },\n RelativeTimeFormat: function RelativeTimeFormat(\n locales?: any,\n options?: any\n ) {\n return getCachedIntl(Intl.RelativeTimeFormat, locales, options);\n },\n Segmenter: function Segmenter(locales?: any, options?: any) {\n return getCachedIntl((Intl as any).Segmenter, locales, options);\n },\n} as any; // Cast to 'any' internally to avoid TS readonly errors\n\nexport { CachedIntl as Intl };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAM,iBAAiB;AACvB,MAAM,wBAAQ,IAAI,KAA4B;;;;AAwC9C,MAAa,iBACX,MACA,QACA,YACoB;CACpB,MAAM,SAAS,UAAU,eAAe,sBAAsB;CAE9D,MAAM,MAAM,GAAG,OAAO,GADP,UAAU,KAAK,UAAU,QAAQ,GAAG;CAGnD,IAAI,YAAY,MAAM,IAAI,KAAK;AAE/B,KAAI,CAAC,WAAW;AACd,8BAAY,IAAI,KAAK;AACrB,QAAM,IAAI,MAAM,UAAU;;CAG5B,IAAI,WAAW,UAAU,IAAI,IAAI;AAEjC,KAAI,CAAC,UAAU;AACb,MAAI,UAAU,OAAO,eAAgB,WAAU,OAAO;AACtD,aAAW,IAAI,KAAK,QAAQ,QAAQ;AACpC,YAAU,IAAI,KAAK,SAAS;;AAE9B,QAAO;;;;;;AAOT,MAAa,YAAY,gBAA4C;CACnE,MAAM,YAAY,SAEhB,SAAS,gBAAgB,SAAe,SAAe;EACrD,MAAM,cACJ,YAAY,QACZ,OAAO,YAAY,YACnB,CAAC,MAAM,QAAQ,QAAQ;EACzB,MAAM,UAAU,cAAc,UAAU;AAKxC,SAAO,cAAc,MAJN,cACV,QAAgB,UAAU,cAC3B,WAAW,aAEoB,QAAQ;;AAG/C,QAAO;EACL,GAAG;EACH,UAAU,SAAS,KAAK,SAAS;EACjC,gBAAgB,SAAS,KAAK,eAAe;EAC7C,cAAc,SAAS,KAAK,aAAa;EACzC,YAAY,SAAS,KAAK,WAAW;EACrC,cAAc,SAAS,KAAK,aAAa;EACzC,aAAa,SAAS,KAAK,YAAY;EACvC,oBAAoB,SAAS,KAAK,mBAAmB;EACrD,QAAQ,SAAS,KAAK,OAAO;EAC7B,WAAW,SAAU,KAAa,UAAU;EAC7C;;AAIH,MAAa,aAAa;CAExB,UAAU,SAAS,SAAS,SAAe,SAAe;AACxD,SAAO,cAAc,KAAK,UAAU,SAAS,QAAQ;;CAEvD,gBAAgB,SAAS,eAAe,SAAe,SAAe;AACpE,SAAO,cAAc,KAAK,gBAAgB,SAAS,QAAQ;;CAE7D,cAAc,SAAS,aAAa,SAAe,SAAe;AAChE,SAAO,cAAc,KAAK,cAAc,SAAS,QAAQ;;CAE3D,YAAY,SAAS,WAAW,SAAe,SAAe;AAC5D,SAAO,cAAc,KAAK,YAAmB,SAAS,QAAQ;;CAEhE,cAAc,SAAS,aAAa,SAAe,SAAe;AAChE,SAAO,cAAc,KAAK,cAAc,SAAS,QAAQ;;CAE3D,aAAa,SAAS,YAAY,SAAe,SAAe;AAC9D,SAAO,cAAc,KAAK,aAAa,SAAS,QAAQ;;CAE1D,oBAAoB,SAAS,mBAC3B,SACA,SACA;AACA,SAAO,cAAc,KAAK,oBAAoB,SAAS,QAAQ;;CAEjE,WAAW,SAAS,UAAU,SAAe,SAAe;AAC1D,SAAO,cAAe,KAAa,WAAW,SAAS,QAAQ;;CAElE"}
1
+ {"version":3,"file":"intl.mjs","names":[],"sources":["../../../src/utils/intl.ts"],"sourcesContent":["/**\n * Cached Intl helper – drop‑in replacement for the global `Intl` object.\n * ‑‑‑\n * • Uses a `Proxy` to lazily wrap every *constructor* hanging off `Intl` (NumberFormat, DateTimeFormat, …).\n * • Each wrapped constructor keeps an in‑memory cache keyed by `[locales, options]` so that identical requests\n * reuse the same heavy instance instead of reparsing CLDR data every time.\n * • A polyfill warning for `Intl.DisplayNames` is emitted only once and only in dev.\n * • The public API is fully type‑safe and mirrors the native `Intl` surface exactly –\n * you can treat `CachedIntl` just like the built‑in `Intl`.\n *\n * Usage @example:\n * ---------------\n * ```ts\n * import { CachedIntl } from \"./cached-intl\";\n *\n * const nf = CachedIntl.NumberFormat(\"en-US\", { style: \"currency\", currency: \"USD\" });\n * console.log(nf.format(1234));\n *\n * const dn = CachedIntl.DisplayNames([\"fr\"], { type: \"language\" });\n * console.log(dn.of(\"en\")); * → \"anglais\"\n *\n * You can also spin up an isolated instance with its own caches (handy in test suites):\n * const TestIntl = createCachedIntl();\n * ```\n */\n\nimport { internationalization } from '@intlayer/config/built';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\nconst MAX_CACHE_SIZE = 50;\nconst cache = new Map<any, Map<string, any>>();\n\ntype IntlConstructors = {\n [K in keyof typeof Intl as (typeof Intl)[K] extends new (\n ...args: any\n ) => any\n ? K\n : never]: (typeof Intl)[K];\n};\n\ntype ReplaceLocaleWithLocalesValues<T> = T extends new (\n locales: any,\n options?: infer Options\n) => infer Instance\n ? {\n new (locales?: LocalesValues, options?: Options): Instance;\n new (options?: Options & { locale?: LocalesValues }): Instance;\n (locales?: LocalesValues, options?: Options): Instance;\n (options?: Options & { locale?: LocalesValues }): Instance;\n }\n : T extends new (\n locales: any\n ) => infer Instance\n ? {\n new (locales?: LocalesValues): Instance;\n new (options?: { locale?: LocalesValues }): Instance;\n (locales?: LocalesValues): Instance;\n (options?: { locale?: LocalesValues }): Instance;\n }\n : T;\n\nexport type WrappedIntl = {\n [K in keyof typeof Intl]: K extends keyof IntlConstructors\n ? ReplaceLocaleWithLocalesValues<(typeof Intl)[K]>\n : (typeof Intl)[K];\n};\n\n/**\n * Generic caching instantiator for Intl constructors.\n */\nexport const getCachedIntl = <T extends new (...args: any[]) => any>(\n Ctor: T,\n locale?: LocalesValues | string,\n options?: any\n): InstanceType<T> => {\n const resLoc = locale ?? internationalization?.defaultLocale;\n\n const optKey = options ? JSON.stringify(options) : '';\n const key = `${resLoc}|${optKey}`;\n\n let ctorCache = cache.get(Ctor);\n\n if (!ctorCache) {\n ctorCache = new Map();\n cache.set(Ctor, ctorCache);\n }\n\n let instance = ctorCache.get(key);\n\n if (!instance) {\n if (ctorCache.size > MAX_CACHE_SIZE) ctorCache.clear();\n instance = new Ctor(resLoc, options);\n ctorCache.set(key, instance);\n }\n return instance;\n};\n\n/**\n * Optional: Keep bindIntl if your library exports it publicly.\n * It now uses the much smaller getCachedIntl under the hood.\n */\nexport const bindIntl = (boundLocale: LocalesValues): WrappedIntl => {\n const bindWrap = (Ctor: any) =>\n // function is used as a constructor, do not change in arrow function\n function intlConstructor(locales?: any, options?: any) {\n const isOptsFirst =\n locales !== null &&\n typeof locales === 'object' &&\n !Array.isArray(locales);\n const resOpts = isOptsFirst ? locales : options;\n const resLoc = isOptsFirst\n ? (resOpts as any).locale || boundLocale\n : locales || boundLocale;\n\n return getCachedIntl(Ctor, resLoc, resOpts);\n };\n\n return {\n ...Intl,\n Collator: bindWrap(Intl.Collator),\n DateTimeFormat: bindWrap(Intl.DateTimeFormat),\n DisplayNames: bindWrap(Intl.DisplayNames),\n ListFormat: bindWrap(Intl.ListFormat),\n NumberFormat: bindWrap(Intl.NumberFormat),\n PluralRules: bindWrap(Intl.PluralRules),\n RelativeTimeFormat: bindWrap(Intl.RelativeTimeFormat),\n Locale: bindWrap(Intl.Locale),\n Segmenter: bindWrap((Intl as any).Segmenter),\n } as unknown as WrappedIntl;\n};\n\n// Add this to the bottom of utils/intl.ts ONLY if required for public API compatibility.\nexport const CachedIntl = {\n // function is used as a constructor, do not change in arrow function\n Collator: function Collator(locales?: any, options?: any) {\n return getCachedIntl(Intl.Collator, locales, options);\n },\n DateTimeFormat: function DateTimeFormat(locales?: any, options?: any) {\n return getCachedIntl(Intl.DateTimeFormat, locales, options);\n },\n DisplayNames: function DisplayNames(locales?: any, options?: any) {\n return getCachedIntl(Intl.DisplayNames, locales, options);\n },\n ListFormat: function ListFormat(locales?: any, options?: any) {\n return getCachedIntl(Intl.ListFormat as any, locales, options);\n },\n NumberFormat: function NumberFormat(locales?: any, options?: any) {\n return getCachedIntl(Intl.NumberFormat, locales, options);\n },\n PluralRules: function PluralRules(locales?: any, options?: any) {\n return getCachedIntl(Intl.PluralRules, locales, options);\n },\n RelativeTimeFormat: function RelativeTimeFormat(\n locales?: any,\n options?: any\n ) {\n return getCachedIntl(Intl.RelativeTimeFormat, locales, options);\n },\n Segmenter: function Segmenter(locales?: any, options?: any) {\n return getCachedIntl((Intl as any).Segmenter, locales, options);\n },\n} as any; // Cast to 'any' internally to avoid TS readonly errors\n\nexport { CachedIntl as Intl };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAM,iBAAiB;AACvB,MAAM,wBAAQ,IAAI,KAA4B;;;;AAwC9C,MAAa,iBACX,MACA,QACA,YACoB;CACpB,MAAM,SAAS,UAAU,sBAAsB;CAG/C,MAAM,MAAM,GAAG,OAAO,GADP,UAAU,KAAK,UAAU,QAAQ,GAAG;CAGnD,IAAI,YAAY,MAAM,IAAI,KAAK;AAE/B,KAAI,CAAC,WAAW;AACd,8BAAY,IAAI,KAAK;AACrB,QAAM,IAAI,MAAM,UAAU;;CAG5B,IAAI,WAAW,UAAU,IAAI,IAAI;AAEjC,KAAI,CAAC,UAAU;AACb,MAAI,UAAU,OAAO,eAAgB,WAAU,OAAO;AACtD,aAAW,IAAI,KAAK,QAAQ,QAAQ;AACpC,YAAU,IAAI,KAAK,SAAS;;AAE9B,QAAO;;;;;;AAOT,MAAa,YAAY,gBAA4C;CACnE,MAAM,YAAY,SAEhB,SAAS,gBAAgB,SAAe,SAAe;EACrD,MAAM,cACJ,YAAY,QACZ,OAAO,YAAY,YACnB,CAAC,MAAM,QAAQ,QAAQ;EACzB,MAAM,UAAU,cAAc,UAAU;AAKxC,SAAO,cAAc,MAJN,cACV,QAAgB,UAAU,cAC3B,WAAW,aAEoB,QAAQ;;AAG/C,QAAO;EACL,GAAG;EACH,UAAU,SAAS,KAAK,SAAS;EACjC,gBAAgB,SAAS,KAAK,eAAe;EAC7C,cAAc,SAAS,KAAK,aAAa;EACzC,YAAY,SAAS,KAAK,WAAW;EACrC,cAAc,SAAS,KAAK,aAAa;EACzC,aAAa,SAAS,KAAK,YAAY;EACvC,oBAAoB,SAAS,KAAK,mBAAmB;EACrD,QAAQ,SAAS,KAAK,OAAO;EAC7B,WAAW,SAAU,KAAa,UAAU;EAC7C;;AAIH,MAAa,aAAa;CAExB,UAAU,SAAS,SAAS,SAAe,SAAe;AACxD,SAAO,cAAc,KAAK,UAAU,SAAS,QAAQ;;CAEvD,gBAAgB,SAAS,eAAe,SAAe,SAAe;AACpE,SAAO,cAAc,KAAK,gBAAgB,SAAS,QAAQ;;CAE7D,cAAc,SAAS,aAAa,SAAe,SAAe;AAChE,SAAO,cAAc,KAAK,cAAc,SAAS,QAAQ;;CAE3D,YAAY,SAAS,WAAW,SAAe,SAAe;AAC5D,SAAO,cAAc,KAAK,YAAmB,SAAS,QAAQ;;CAEhE,cAAc,SAAS,aAAa,SAAe,SAAe;AAChE,SAAO,cAAc,KAAK,cAAc,SAAS,QAAQ;;CAE3D,aAAa,SAAS,YAAY,SAAe,SAAe;AAC9D,SAAO,cAAc,KAAK,aAAa,SAAS,QAAQ;;CAE1D,oBAAoB,SAAS,mBAC3B,SACA,SACA;AACA,SAAO,cAAc,KAAK,oBAAoB,SAAS,QAAQ;;CAEjE,WAAW,SAAS,UAAU,SAAe,SAAe;AAC1D,SAAO,cAAe,KAAa,WAAW,SAAS,QAAQ;;CAElE"}
@@ -1,8 +1,23 @@
1
1
  import { getCookie } from "./getCookie.mjs";
2
- import configuration from "@intlayer/config/built";
3
- import { TREE_SHAKE_STORAGE_COOKIES, TREE_SHAKE_STORAGE_HEADERS, TREE_SHAKE_STORAGE_LOCAL_STORAGE, TREE_SHAKE_STORAGE_SESSION_STORAGE } from "@intlayer/config/envVars";
2
+ import { internationalization, routing } from "@intlayer/config/built";
4
3
 
5
4
  //#region src/utils/localeStorage.ts
5
+ /**
6
+ * True when cookie storage is explicitly disabled at build time.
7
+ */
8
+ const TREE_SHAKE_STORAGE_COOKIES = process.env["INTLAYER_ROUTING_STORAGE_COOKIES"] === "false";
9
+ /**
10
+ * True when localStorage is explicitly disabled at build time.
11
+ */
12
+ const TREE_SHAKE_STORAGE_LOCAL_STORAGE = process.env["INTLAYER_ROUTING_STORAGE_LOCALSTORAGE"] === "false";
13
+ /**
14
+ * True when sessionStorage is explicitly disabled at build time.
15
+ */
16
+ const TREE_SHAKE_STORAGE_SESSION_STORAGE = process.env["INTLAYER_ROUTING_STORAGE_SESSIONSTORAGE"] === "false";
17
+ /**
18
+ * True when header storage is explicitly disabled at build time.
19
+ */
20
+ const TREE_SHAKE_STORAGE_HEADERS = process.env["INTLAYER_ROUTING_STORAGE_HEADERS"] === "false";
6
21
  const buildCookieString = (name, value, attributes) => {
7
22
  const parts = [`${name}=${encodeURIComponent(value)}`];
8
23
  if (attributes.path) parts.push(`Path=${attributes.path}`);
@@ -18,20 +33,19 @@ const buildCookieString = (name, value, attributes) => {
18
33
  * Does not read from headers — use `getLocaleFromStorageServer` for that.
19
34
  */
20
35
  const getLocaleFromStorageClient = (options) => {
21
- const { routing, internationalization } = configuration;
22
36
  const { locales } = internationalization;
23
37
  const storageAttributes = routing.storage;
24
38
  if (options?.isCookieEnabled === false) return void 0;
25
39
  const isValidLocale = (value) => !!value && locales.includes(value);
26
- if (!TREE_SHAKE_STORAGE_COOKIES) for (let i = 0; i < storageAttributes.cookies.length; i++) try {
40
+ if (!TREE_SHAKE_STORAGE_COOKIES) for (let i = 0; i < (storageAttributes.cookies ?? []).length; i++) try {
27
41
  const value = options?.getCookie?.(storageAttributes.cookies[i].name);
28
42
  if (isValidLocale(value)) return value;
29
43
  } catch {}
30
- if (!TREE_SHAKE_STORAGE_LOCAL_STORAGE) for (let i = 0; i < storageAttributes.localStorage.length; i++) try {
44
+ if (!TREE_SHAKE_STORAGE_LOCAL_STORAGE) for (let i = 0; i < (storageAttributes.localStorage ?? []).length; i++) try {
31
45
  const value = options?.getLocaleStorage?.(storageAttributes.localStorage[i].name);
32
46
  if (isValidLocale(value)) return value;
33
47
  } catch {}
34
- if (!TREE_SHAKE_STORAGE_SESSION_STORAGE) for (let i = 0; i < storageAttributes.sessionStorage.length; i++) try {
48
+ if (!TREE_SHAKE_STORAGE_SESSION_STORAGE && storageAttributes.sessionStorage) for (let i = 0; i < storageAttributes.sessionStorage.length; i++) try {
35
49
  const value = options?.getSessionStorage?.(storageAttributes.sessionStorage[i].name);
36
50
  if (isValidLocale(value)) return value;
37
51
  } catch {}
@@ -42,10 +56,9 @@ const getLocaleFromStorageClient = (options) => {
42
56
  * Does not write to headers — use `setLocaleInStorageServer` for that.
43
57
  */
44
58
  const setLocaleInStorageClient = (locale, options) => {
45
- const { routing } = configuration;
46
59
  const storageAttributes = routing.storage;
47
60
  if (options?.isCookieEnabled === false) return;
48
- if (!TREE_SHAKE_STORAGE_COOKIES) for (let i = 0; i < storageAttributes.cookies.length; i++) {
61
+ if (!TREE_SHAKE_STORAGE_COOKIES && storageAttributes.cookies) for (let i = 0; i < storageAttributes.cookies.length; i++) {
49
62
  const { name, attributes } = storageAttributes.cookies[i];
50
63
  try {
51
64
  if (options?.setCookieStore) options.setCookieStore(name, locale, {
@@ -58,7 +71,7 @@ const setLocaleInStorageClient = (locale, options) => {
58
71
  } catch {}
59
72
  }
60
73
  }
61
- if (!TREE_SHAKE_STORAGE_LOCAL_STORAGE && options?.setLocaleStorage) for (let i = 0; i < storageAttributes.localStorage.length; i++) {
74
+ if (!TREE_SHAKE_STORAGE_LOCAL_STORAGE && storageAttributes.localStorage && options?.setLocaleStorage) for (let i = 0; i < storageAttributes.localStorage.length; i++) {
62
75
  const { name } = storageAttributes.localStorage[i];
63
76
  try {
64
77
  if (!(options?.overwrite ?? true) && options?.getLocaleStorage) {
@@ -67,7 +80,7 @@ const setLocaleInStorageClient = (locale, options) => {
67
80
  options.setLocaleStorage(name, locale);
68
81
  } catch {}
69
82
  }
70
- if (!TREE_SHAKE_STORAGE_SESSION_STORAGE && options?.setSessionStorage) for (let i = 0; i < storageAttributes.sessionStorage.length; i++) {
83
+ if (!TREE_SHAKE_STORAGE_SESSION_STORAGE && storageAttributes.sessionStorage && options?.setSessionStorage) for (let i = 0; i < storageAttributes.sessionStorage.length; i++) {
71
84
  const { name } = storageAttributes.sessionStorage[i];
72
85
  try {
73
86
  if (!(options?.overwrite ?? true) && options?.getSessionStorage) {
@@ -99,16 +112,15 @@ const LocaleStorageClient = (options) => ({
99
112
  * No browser cookie fallback — the caller must provide `getCookie`.
100
113
  */
101
114
  const getLocaleFromStorageServer = (options) => {
102
- const { routing, internationalization } = configuration;
103
115
  const { locales } = internationalization;
104
116
  const storageAttributes = routing.storage;
105
117
  if (options?.isCookieEnabled === false) return void 0;
106
118
  const isValidLocale = (value) => !!value && locales.includes(value);
107
- if (!TREE_SHAKE_STORAGE_COOKIES) for (let i = 0; i < storageAttributes.cookies.length; i++) try {
119
+ if (!TREE_SHAKE_STORAGE_COOKIES && storageAttributes.cookies) for (let i = 0; i < storageAttributes.cookies.length; i++) try {
108
120
  const value = options?.getCookie?.(storageAttributes.cookies[i].name);
109
121
  if (isValidLocale(value)) return value;
110
122
  } catch {}
111
- if (!TREE_SHAKE_STORAGE_HEADERS) for (let i = 0; i < storageAttributes.headers.length; i++) try {
123
+ if (!TREE_SHAKE_STORAGE_HEADERS && storageAttributes.headers) for (let i = 0; i < storageAttributes.headers.length; i++) try {
112
124
  const value = options?.getHeader?.(storageAttributes.headers[i].name);
113
125
  if (isValidLocale(value)) return value;
114
126
  } catch {}
@@ -118,10 +130,9 @@ const getLocaleFromStorageServer = (options) => {
118
130
  * Does not write to localStorage or sessionStorage.
119
131
  */
120
132
  const setLocaleInStorageServer = (locale, options) => {
121
- const { routing } = configuration;
122
133
  const storageAttributes = routing.storage;
123
134
  if (options?.isCookieEnabled === false) return;
124
- if (!TREE_SHAKE_STORAGE_COOKIES) for (let i = 0; i < storageAttributes.cookies.length; i++) {
135
+ if (!TREE_SHAKE_STORAGE_COOKIES && storageAttributes.cookies) for (let i = 0; i < storageAttributes.cookies.length; i++) {
125
136
  const { name, attributes } = storageAttributes.cookies[i];
126
137
  try {
127
138
  if (options?.setCookieStore) options.setCookieStore(name, locale, {
@@ -134,7 +145,7 @@ const setLocaleInStorageServer = (locale, options) => {
134
145
  } catch {}
135
146
  }
136
147
  }
137
- if (!TREE_SHAKE_STORAGE_HEADERS && options?.setHeader) for (let i = 0; i < storageAttributes.headers.length; i++) try {
148
+ if (!TREE_SHAKE_STORAGE_HEADERS && storageAttributes.headers && options?.setHeader) for (let i = 0; i < storageAttributes.headers.length; i++) try {
138
149
  options.setHeader(storageAttributes.headers[i].name, locale);
139
150
  } catch {}
140
151
  };
@@ -167,7 +178,6 @@ const LocaleStorageServer = (options) => ({
167
178
  * {@link getLocaleFromStorageServer} (server) instead.
168
179
  */
169
180
  const getLocaleFromStorage = (options) => {
170
- const { routing, internationalization } = configuration;
171
181
  const { locales } = internationalization;
172
182
  const storageAttributes = routing.storage;
173
183
  if (options?.isCookieEnabled === false) return void 0;
@@ -179,19 +189,19 @@ const getLocaleFromStorage = (options) => {
179
189
  } catch {}
180
190
  return getCookie(name);
181
191
  };
182
- if (!TREE_SHAKE_STORAGE_COOKIES) for (let i = 0; i < storageAttributes.cookies.length; i++) {
192
+ if (!TREE_SHAKE_STORAGE_COOKIES && storageAttributes.cookies) for (let i = 0; i < storageAttributes.cookies.length; i++) {
183
193
  const value = readCookie(storageAttributes.cookies[i].name);
184
194
  if (isValidLocale(value)) return value;
185
195
  }
186
- if (!TREE_SHAKE_STORAGE_LOCAL_STORAGE) for (let i = 0; i < storageAttributes.localStorage.length; i++) try {
196
+ if (!TREE_SHAKE_STORAGE_LOCAL_STORAGE && storageAttributes.localStorage) for (let i = 0; i < storageAttributes.localStorage.length; i++) try {
187
197
  const value = options?.getLocaleStorage?.(storageAttributes.localStorage[i].name);
188
198
  if (isValidLocale(value)) return value;
189
199
  } catch {}
190
- if (!TREE_SHAKE_STORAGE_SESSION_STORAGE) for (let i = 0; i < storageAttributes.sessionStorage.length; i++) try {
200
+ if (!TREE_SHAKE_STORAGE_SESSION_STORAGE && storageAttributes.sessionStorage) for (let i = 0; i < storageAttributes.sessionStorage.length; i++) try {
191
201
  const value = options?.getSessionStorage?.(storageAttributes.sessionStorage[i].name);
192
202
  if (isValidLocale(value)) return value;
193
203
  } catch {}
194
- if (!TREE_SHAKE_STORAGE_HEADERS) for (let i = 0; i < storageAttributes.headers.length; i++) try {
204
+ if (!TREE_SHAKE_STORAGE_HEADERS && storageAttributes.headers) for (let i = 0; i < storageAttributes.headers.length; i++) try {
195
205
  const value = options?.getHeader?.(storageAttributes.headers[i].name);
196
206
  if (isValidLocale(value)) return value;
197
207
  } catch {}
@@ -204,10 +214,9 @@ const getLocaleFromStorage = (options) => {
204
214
  * {@link setLocaleInStorageServer} (server) instead.
205
215
  */
206
216
  const setLocaleInStorage = (locale, options) => {
207
- const { routing } = configuration;
208
217
  const storageAttributes = routing.storage;
209
218
  if (options?.isCookieEnabled === false) return;
210
- if (!TREE_SHAKE_STORAGE_COOKIES) for (let i = 0; i < storageAttributes.cookies.length; i++) {
219
+ if (!TREE_SHAKE_STORAGE_COOKIES && storageAttributes.cookies) for (let i = 0; i < storageAttributes.cookies.length; i++) {
211
220
  const { name, attributes } = storageAttributes.cookies[i];
212
221
  try {
213
222
  if (options?.setCookieStore) options.setCookieStore(name, locale, {
@@ -220,7 +229,7 @@ const setLocaleInStorage = (locale, options) => {
220
229
  } catch {}
221
230
  }
222
231
  }
223
- if (!TREE_SHAKE_STORAGE_LOCAL_STORAGE && options?.setLocaleStorage) for (let i = 0; i < storageAttributes.localStorage.length; i++) {
232
+ if (!TREE_SHAKE_STORAGE_LOCAL_STORAGE && storageAttributes.localStorage && options?.setLocaleStorage) for (let i = 0; i < storageAttributes.localStorage.length; i++) {
224
233
  const { name } = storageAttributes.localStorage[i];
225
234
  try {
226
235
  if (!(options?.overwrite ?? true) && options?.getLocaleStorage) {
@@ -229,7 +238,7 @@ const setLocaleInStorage = (locale, options) => {
229
238
  options.setLocaleStorage(name, locale);
230
239
  } catch {}
231
240
  }
232
- if (!TREE_SHAKE_STORAGE_SESSION_STORAGE && options?.setSessionStorage) for (let i = 0; i < storageAttributes.sessionStorage.length; i++) {
241
+ if (!TREE_SHAKE_STORAGE_SESSION_STORAGE && storageAttributes.sessionStorage && options?.setSessionStorage) for (let i = 0; i < storageAttributes.sessionStorage.length; i++) {
233
242
  const { name } = storageAttributes.sessionStorage[i];
234
243
  try {
235
244
  if (!(options?.overwrite ?? true) && options?.getSessionStorage) {
@@ -238,7 +247,7 @@ const setLocaleInStorage = (locale, options) => {
238
247
  options.setSessionStorage(name, locale);
239
248
  } catch {}
240
249
  }
241
- if (!TREE_SHAKE_STORAGE_HEADERS && options?.setHeader) for (let i = 0; i < storageAttributes.headers.length; i++) try {
250
+ if (!TREE_SHAKE_STORAGE_HEADERS && storageAttributes.headers && options?.setHeader) for (let i = 0; i < storageAttributes.headers.length; i++) try {
242
251
  options.setHeader(storageAttributes.headers[i].name, locale);
243
252
  } catch {}
244
253
  };