@intlayer/core 9.3.2 → 9.4.0-canary.0

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 (130) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/dictionaryManipulator/index.cjs +5 -1
  3. package/dist/cjs/dictionaryManipulator/plainDynamicLoader.cjs +71 -0
  4. package/dist/cjs/dictionaryManipulator/plainDynamicLoader.cjs.map +1 -0
  5. package/dist/cjs/dictionaryManipulator/preloadedDynamicLoader.cjs +29 -0
  6. package/dist/cjs/dictionaryManipulator/preloadedDynamicLoader.cjs.map +1 -0
  7. package/dist/cjs/dictionaryManipulator/qualifiedDynamicLoader.cjs +4 -10
  8. package/dist/cjs/dictionaryManipulator/qualifiedDynamicLoader.cjs.map +1 -1
  9. package/dist/cjs/index.cjs +13 -2
  10. package/dist/cjs/localization/comparePaths.cjs +1 -1
  11. package/dist/cjs/localization/comparePaths.cjs.map +1 -1
  12. package/dist/cjs/localization/domainUtils.cjs.map +1 -1
  13. package/dist/cjs/localization/getHTMLTextDir.cjs +11 -10
  14. package/dist/cjs/localization/getHTMLTextDir.cjs.map +1 -1
  15. package/dist/cjs/localization/getLocale.cjs.map +1 -1
  16. package/dist/cjs/localization/getLocaleFromPath.cjs +3 -3
  17. package/dist/cjs/localization/getLocaleFromPath.cjs.map +1 -1
  18. package/dist/cjs/localization/getLocalizedPath.cjs +74 -0
  19. package/dist/cjs/localization/getLocalizedPath.cjs.map +1 -0
  20. package/dist/cjs/localization/getLocalizedPath.test-d.cjs +83 -0
  21. package/dist/cjs/localization/getLocalizedPath.test-d.cjs.map +1 -0
  22. package/dist/cjs/localization/getLocalizedUrl.cjs +9 -25
  23. package/dist/cjs/localization/getLocalizedUrl.cjs.map +1 -1
  24. package/dist/cjs/localization/getLocalizedUrl.test-d.cjs +42 -0
  25. package/dist/cjs/localization/getLocalizedUrl.test-d.cjs.map +1 -0
  26. package/dist/cjs/localization/getPathWithoutLocale.cjs +2 -2
  27. package/dist/cjs/localization/getPathWithoutLocale.cjs.map +1 -1
  28. package/dist/cjs/localization/getPrefix.cjs +15 -1
  29. package/dist/cjs/localization/getPrefix.cjs.map +1 -1
  30. package/dist/cjs/localization/index.cjs +7 -1
  31. package/dist/cjs/localization/localeDetector.cjs +5 -3
  32. package/dist/cjs/localization/localeDetector.cjs.map +1 -1
  33. package/dist/cjs/localization/localeMapper.cjs.map +1 -1
  34. package/dist/cjs/localization/localeResolver.cjs.map +1 -1
  35. package/dist/cjs/localization/resolveBrowserLocale.cjs +63 -0
  36. package/dist/cjs/localization/resolveBrowserLocale.cjs.map +1 -0
  37. package/dist/cjs/localization/rewriteUtils.cjs +26 -14
  38. package/dist/cjs/localization/rewriteUtils.cjs.map +1 -1
  39. package/dist/cjs/localization/rewriteUtils.test-d.cjs +48 -0
  40. package/dist/cjs/localization/rewriteUtils.test-d.cjs.map +1 -0
  41. package/dist/cjs/markdown/constants.cjs +3 -0
  42. package/dist/cjs/markdown/constants.cjs.map +1 -1
  43. package/dist/cjs/markdown/index.cjs +1 -0
  44. package/dist/cjs/markdown/utils.cjs +23 -1
  45. package/dist/cjs/markdown/utils.cjs.map +1 -1
  46. package/dist/esm/dictionaryManipulator/index.mjs +2 -1
  47. package/dist/esm/dictionaryManipulator/plainDynamicLoader.mjs +70 -0
  48. package/dist/esm/dictionaryManipulator/plainDynamicLoader.mjs.map +1 -0
  49. package/dist/esm/dictionaryManipulator/preloadedDynamicLoader.mjs +27 -0
  50. package/dist/esm/dictionaryManipulator/preloadedDynamicLoader.mjs.map +1 -0
  51. package/dist/esm/dictionaryManipulator/qualifiedDynamicLoader.mjs +2 -8
  52. package/dist/esm/dictionaryManipulator/qualifiedDynamicLoader.mjs.map +1 -1
  53. package/dist/esm/index.mjs +7 -4
  54. package/dist/esm/localization/comparePaths.mjs +1 -1
  55. package/dist/esm/localization/comparePaths.mjs.map +1 -1
  56. package/dist/esm/localization/domainUtils.mjs.map +1 -1
  57. package/dist/esm/localization/getHTMLTextDir.mjs +11 -10
  58. package/dist/esm/localization/getHTMLTextDir.mjs.map +1 -1
  59. package/dist/esm/localization/getLocale.mjs.map +1 -1
  60. package/dist/esm/localization/getLocaleFromPath.mjs +4 -4
  61. package/dist/esm/localization/getLocaleFromPath.mjs.map +1 -1
  62. package/dist/esm/localization/getLocalizedPath.mjs +73 -0
  63. package/dist/esm/localization/getLocalizedPath.mjs.map +1 -0
  64. package/dist/esm/localization/getLocalizedPath.test-d.mjs +83 -0
  65. package/dist/esm/localization/getLocalizedPath.test-d.mjs.map +1 -0
  66. package/dist/esm/localization/getLocalizedUrl.mjs +10 -26
  67. package/dist/esm/localization/getLocalizedUrl.mjs.map +1 -1
  68. package/dist/esm/localization/getLocalizedUrl.test-d.mjs +42 -0
  69. package/dist/esm/localization/getLocalizedUrl.test-d.mjs.map +1 -0
  70. package/dist/esm/localization/getPathWithoutLocale.mjs +2 -2
  71. package/dist/esm/localization/getPathWithoutLocale.mjs.map +1 -1
  72. package/dist/esm/localization/getPrefix.mjs +15 -2
  73. package/dist/esm/localization/getPrefix.mjs.map +1 -1
  74. package/dist/esm/localization/index.mjs +5 -3
  75. package/dist/esm/localization/localeDetector.mjs +5 -3
  76. package/dist/esm/localization/localeDetector.mjs.map +1 -1
  77. package/dist/esm/localization/localeMapper.mjs.map +1 -1
  78. package/dist/esm/localization/localeResolver.mjs.map +1 -1
  79. package/dist/esm/localization/resolveBrowserLocale.mjs +61 -0
  80. package/dist/esm/localization/resolveBrowserLocale.mjs.map +1 -0
  81. package/dist/esm/localization/rewriteUtils.mjs +26 -14
  82. package/dist/esm/localization/rewriteUtils.mjs.map +1 -1
  83. package/dist/esm/localization/rewriteUtils.test-d.mjs +48 -0
  84. package/dist/esm/localization/rewriteUtils.test-d.mjs.map +1 -0
  85. package/dist/esm/markdown/constants.mjs +3 -1
  86. package/dist/esm/markdown/constants.mjs.map +1 -1
  87. package/dist/esm/markdown/index.mjs +2 -2
  88. package/dist/esm/markdown/utils.mjs +24 -2
  89. package/dist/esm/markdown/utils.mjs.map +1 -1
  90. package/dist/types/dictionaryManipulator/index.d.ts +2 -1
  91. package/dist/types/dictionaryManipulator/plainDynamicLoader.d.ts +50 -0
  92. package/dist/types/dictionaryManipulator/plainDynamicLoader.d.ts.map +1 -0
  93. package/dist/types/dictionaryManipulator/preloadedDynamicLoader.d.ts +38 -0
  94. package/dist/types/dictionaryManipulator/preloadedDynamicLoader.d.ts.map +1 -0
  95. package/dist/types/dictionaryManipulator/qualifiedDynamicLoader.d.ts +1 -7
  96. package/dist/types/dictionaryManipulator/qualifiedDynamicLoader.d.ts.map +1 -1
  97. package/dist/types/index.d.ts +7 -4
  98. package/dist/types/localization/domainUtils.d.ts +2 -3
  99. package/dist/types/localization/domainUtils.d.ts.map +1 -1
  100. package/dist/types/localization/getHTMLTextDir.d.ts.map +1 -1
  101. package/dist/types/localization/getLocale.d.ts +2 -2
  102. package/dist/types/localization/getLocale.d.ts.map +1 -1
  103. package/dist/types/localization/getLocaleFromPath.d.ts +2 -2
  104. package/dist/types/localization/getLocaleFromPath.d.ts.map +1 -1
  105. package/dist/types/localization/getLocalizedPath.d.ts +47 -0
  106. package/dist/types/localization/getLocalizedPath.d.ts.map +1 -0
  107. package/dist/types/localization/getLocalizedPath.test-d.d.ts +1 -0
  108. package/dist/types/localization/getLocalizedUrl.d.ts +5 -1
  109. package/dist/types/localization/getLocalizedUrl.d.ts.map +1 -1
  110. package/dist/types/localization/getLocalizedUrl.test-d.d.ts +1 -0
  111. package/dist/types/localization/getPrefix.d.ts +20 -8
  112. package/dist/types/localization/getPrefix.d.ts.map +1 -1
  113. package/dist/types/localization/index.d.ts +5 -3
  114. package/dist/types/localization/localeDetector.d.ts +2 -1
  115. package/dist/types/localization/localeDetector.d.ts.map +1 -1
  116. package/dist/types/localization/localeMapper.d.ts +4 -5
  117. package/dist/types/localization/localeMapper.d.ts.map +1 -1
  118. package/dist/types/localization/localeResolver.d.ts +2 -3
  119. package/dist/types/localization/localeResolver.d.ts.map +1 -1
  120. package/dist/types/localization/resolveBrowserLocale.d.ts +35 -0
  121. package/dist/types/localization/resolveBrowserLocale.d.ts.map +1 -0
  122. package/dist/types/localization/rewriteUtils.d.ts +51 -11
  123. package/dist/types/localization/rewriteUtils.d.ts.map +1 -1
  124. package/dist/types/localization/rewriteUtils.test-d.d.ts +1 -0
  125. package/dist/types/markdown/constants.d.ts +3 -1
  126. package/dist/types/markdown/constants.d.ts.map +1 -1
  127. package/dist/types/markdown/index.d.ts +2 -2
  128. package/dist/types/markdown/utils.d.ts +6 -0
  129. package/dist/types/markdown/utils.d.ts.map +1 -1
  130. package/package.json +7 -7
@@ -0,0 +1,70 @@
1
+ import { PRELOADED_DYNAMIC_KEY } from "./preloadedDynamicLoader.mjs";
2
+ import { QUALIFIER_DYNAMIC_TYPES_KEY } from "./qualifiedDynamicLoader.mjs";
3
+ import { internationalization } from "@intlayer/config/built";
4
+
5
+ //#region src/dictionaryManipulator/plainDynamicLoader.ts
6
+ /** Marker properties a loader map carries alongside its per-locale loaders. */
7
+ const MARKER_KEYS = [PRELOADED_DYNAMIC_KEY, QUALIFIER_DYNAMIC_TYPES_KEY];
8
+ /**
9
+ * Ordered locale candidates for a chunk lookup, mirroring the static fallback
10
+ * chain `getTranslation` applies: the requested locale, its base language, then
11
+ * the default locale and its base language.
12
+ */
13
+ const getLocaleCandidates = (locale) => {
14
+ const candidates = [];
15
+ const addCandidate = (candidate) => {
16
+ if (candidate && !candidates.includes(candidate)) candidates.push(candidate);
17
+ };
18
+ addCandidate(locale);
19
+ addCandidate(locale.split("-")[0]);
20
+ addCandidate(internationalization.defaultLocale);
21
+ addCandidate(internationalization.defaultLocale?.split("-")[0]);
22
+ return candidates;
23
+ };
24
+ /**
25
+ * Picks the chunk loader serving a locale out of a plain dynamic loader map.
26
+ *
27
+ * A dictionary is only emitted for the locales it declares, so a read for any
28
+ * other locale — an undeclared one, or a partially translated dictionary under
29
+ * `requiredLocales` — has no chunk of its own. Static mode answers such a read
30
+ * with the default locale's content rather than nothing, and this walks the
31
+ * same chain so dynamic mode stays interchangeable with it.
32
+ *
33
+ * @param loaderMap - Default export of a generated dynamic entry point.
34
+ * @param locale - Locale the caller is about to render.
35
+ * @returns The loader to invoke and the locale its chunk holds, or `undefined`
36
+ * when the map holds no chunk for the locale nor for any fallback.
37
+ */
38
+ const findPlainDynamicLoader = (loaderMap, locale) => {
39
+ for (const candidate of getLocaleCandidates(locale)) {
40
+ const loader = loaderMap[candidate];
41
+ if (typeof loader === "function") return {
42
+ locale: candidate,
43
+ loader
44
+ };
45
+ }
46
+ };
47
+ /** Lists the locales a loader map actually emitted a chunk for. */
48
+ const getEmittedLocales = (loaderMap) => Object.keys(loaderMap).filter((loaderKey) => !MARKER_KEYS.includes(loaderKey) && typeof loaderMap[loaderKey] === "function");
49
+ /**
50
+ * {@link findPlainDynamicLoader}, for callers that cannot render without a
51
+ * chunk and would otherwise fail on an `undefined` loader with an error naming
52
+ * neither the dictionary nor the locale.
53
+ *
54
+ * @param loaderMap - Default export of a generated dynamic entry point.
55
+ * @param key - Dictionary key, used to describe an unresolvable lookup.
56
+ * @param locale - Locale the caller is about to render.
57
+ * @returns The loader to invoke, and the locale its chunk holds.
58
+ * @throws When neither the locale nor any of its fallbacks has a chunk, which
59
+ * only happens on a malformed or empty entry point.
60
+ */
61
+ const resolvePlainDynamicLoader = (loaderMap, key, locale) => {
62
+ const resolved = findPlainDynamicLoader(loaderMap, locale);
63
+ if (resolved) return resolved;
64
+ const emittedLocales = getEmittedLocales(loaderMap);
65
+ throw new Error(`[intlayer] No dynamic chunk found for dictionary "${key}" and locale "${locale}". Emitted locales: ${emittedLocales.length > 0 ? emittedLocales.join(", ") : "(none)"}.`);
66
+ };
67
+
68
+ //#endregion
69
+ export { findPlainDynamicLoader, resolvePlainDynamicLoader };
70
+ //# sourceMappingURL=plainDynamicLoader.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plainDynamicLoader.mjs","names":[],"sources":["../../../src/dictionaryManipulator/plainDynamicLoader.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { PRELOADED_DYNAMIC_KEY } from './preloadedDynamicLoader';\nimport { QUALIFIER_DYNAMIC_TYPES_KEY } from './qualifiedDynamicLoader';\n\n/**\n * Default export shape of a generated dynamic entry point for a plain (non\n * qualified) key: one lazy chunk loader per emitted locale.\n */\nexport type PlainDynamicLoaderMap<T extends Dictionary = Dictionary> = {\n [locale: string]: (() => Promise<T>) | undefined;\n};\n\n/** A chunk loader together with the locale it was emitted for. */\nexport type ResolvedDynamicLoader<T extends Dictionary = Dictionary> = {\n /**\n * Locale the returned loader actually serves, which differs from the\n * requested one whenever the fallback chain was walked.\n */\n locale: LocalesValues;\n loader: () => Promise<T>;\n};\n\n/** Marker properties a loader map carries alongside its per-locale loaders. */\nconst MARKER_KEYS: string[] = [\n PRELOADED_DYNAMIC_KEY,\n QUALIFIER_DYNAMIC_TYPES_KEY,\n];\n\n/**\n * Ordered locale candidates for a chunk lookup, mirroring the static fallback\n * chain `getTranslation` applies: the requested locale, its base language, then\n * the default locale and its base language.\n */\nconst getLocaleCandidates = (locale: LocalesValues): string[] => {\n const candidates: string[] = [];\n\n const addCandidate = (candidate?: string) => {\n if (candidate && !candidates.includes(candidate))\n candidates.push(candidate);\n };\n\n addCandidate(locale);\n addCandidate(locale.split('-')[0]);\n addCandidate(internationalization.defaultLocale);\n addCandidate(internationalization.defaultLocale?.split('-')[0]);\n\n return candidates;\n};\n\n/**\n * Picks the chunk loader serving a locale out of a plain dynamic loader map.\n *\n * A dictionary is only emitted for the locales it declares, so a read for any\n * other locale — an undeclared one, or a partially translated dictionary under\n * `requiredLocales` — has no chunk of its own. Static mode answers such a read\n * with the default locale's content rather than nothing, and this walks the\n * same chain so dynamic mode stays interchangeable with it.\n *\n * @param loaderMap - Default export of a generated dynamic entry point.\n * @param locale - Locale the caller is about to render.\n * @returns The loader to invoke and the locale its chunk holds, or `undefined`\n * when the map holds no chunk for the locale nor for any fallback.\n */\nexport const findPlainDynamicLoader = <T extends Dictionary>(\n loaderMap: PlainDynamicLoaderMap<T>,\n locale: LocalesValues\n): ResolvedDynamicLoader<T> | undefined => {\n for (const candidate of getLocaleCandidates(locale)) {\n const loader = loaderMap[candidate];\n\n if (typeof loader === 'function') {\n return { locale: candidate as LocalesValues, loader };\n }\n }\n\n return undefined;\n};\n\n/** Lists the locales a loader map actually emitted a chunk for. */\nconst getEmittedLocales = (loaderMap: PlainDynamicLoaderMap): string[] =>\n Object.keys(loaderMap).filter(\n (loaderKey) =>\n !MARKER_KEYS.includes(loaderKey) &&\n typeof loaderMap[loaderKey] === 'function'\n );\n\n/**\n * {@link findPlainDynamicLoader}, for callers that cannot render without a\n * chunk and would otherwise fail on an `undefined` loader with an error naming\n * neither the dictionary nor the locale.\n *\n * @param loaderMap - Default export of a generated dynamic entry point.\n * @param key - Dictionary key, used to describe an unresolvable lookup.\n * @param locale - Locale the caller is about to render.\n * @returns The loader to invoke, and the locale its chunk holds.\n * @throws When neither the locale nor any of its fallbacks has a chunk, which\n * only happens on a malformed or empty entry point.\n */\nexport const resolvePlainDynamicLoader = <T extends Dictionary>(\n loaderMap: PlainDynamicLoaderMap<T>,\n key: string,\n locale: LocalesValues\n): ResolvedDynamicLoader<T> => {\n const resolved = findPlainDynamicLoader<T>(loaderMap, locale);\n\n if (resolved) return resolved;\n\n const emittedLocales = getEmittedLocales(loaderMap);\n\n throw new Error(\n `[intlayer] No dynamic chunk found for dictionary \"${key}\" and locale \"${locale}\". Emitted locales: ${\n emittedLocales.length > 0 ? emittedLocales.join(', ') : '(none)'\n }.`\n );\n};\n"],"mappings":";;;;;;AAyBA,MAAM,cAAwB,CAC5B,uBACA,2BACF;;;;;;AAOA,MAAM,uBAAuB,WAAoC;CAC/D,MAAM,aAAuB,CAAC;CAE9B,MAAM,gBAAgB,cAAuB;EAC3C,IAAI,aAAa,CAAC,WAAW,SAAS,SAAS,GAC7C,WAAW,KAAK,SAAS;CAC7B;CAEA,aAAa,MAAM;CACnB,aAAa,OAAO,MAAM,GAAG,CAAC,CAAC,EAAE;CACjC,aAAa,qBAAqB,aAAa;CAC/C,aAAa,qBAAqB,eAAe,MAAM,GAAG,CAAC,CAAC,EAAE;CAE9D,OAAO;AACT;;;;;;;;;;;;;;;AAgBA,MAAa,0BACX,WACA,WACyC;CACzC,KAAK,MAAM,aAAa,oBAAoB,MAAM,GAAG;EACnD,MAAM,SAAS,UAAU;EAEzB,IAAI,OAAO,WAAW,YACpB,OAAO;GAAE,QAAQ;GAA4B;EAAO;CAExD;AAGF;;AAGA,MAAM,qBAAqB,cACzB,OAAO,KAAK,SAAS,CAAC,CAAC,QACpB,cACC,CAAC,YAAY,SAAS,SAAS,KAC/B,OAAO,UAAU,eAAe,UACpC;;;;;;;;;;;;;AAcF,MAAa,6BACX,WACA,KACA,WAC6B;CAC7B,MAAM,WAAW,uBAA0B,WAAW,MAAM;CAE5D,IAAI,UAAU,OAAO;CAErB,MAAM,iBAAiB,kBAAkB,SAAS;CAElD,MAAM,IAAI,MACR,qDAAqD,IAAI,gBAAgB,OAAO,sBAC9E,eAAe,SAAS,IAAI,eAAe,KAAK,IAAI,IAAI,SACzD,EACH;AACF"}
@@ -0,0 +1,27 @@
1
+ import { PRELOADED_DYNAMIC_KEY } from "@intlayer/config/defaultValues";
2
+
3
+ //#region src/dictionaryManipulator/preloadedDynamicLoader.ts
4
+ /**
5
+ * Reads the preloaded dictionary of a dynamic loader map, when one was resolved
6
+ * for the requested locale.
7
+ *
8
+ * Deliberately synchronous, and deliberately not routed through the suspender
9
+ * cache: a suspender only leaves `pending` inside a `.then` callback, so feeding
10
+ * it an already-resolved promise would still suspend for one microtask — long
11
+ * enough for a pending fallback to paint, which is the flash this whole
12
+ * mechanism exists to remove.
13
+ *
14
+ * @param loaderMap - Default export of a generated dynamic entry point.
15
+ * @param locale - Locale the caller is about to render.
16
+ * @returns The dictionary when it was preloaded for `locale`, else `undefined`.
17
+ */
18
+ const getPreloadedDictionary = (loaderMap, locale) => {
19
+ if (typeof loaderMap !== "object" || loaderMap === null) return void 0;
20
+ const preloaded = loaderMap[PRELOADED_DYNAMIC_KEY];
21
+ if (!preloaded || preloaded.locale !== locale) return void 0;
22
+ return preloaded.dictionary;
23
+ };
24
+
25
+ //#endregion
26
+ export { PRELOADED_DYNAMIC_KEY, getPreloadedDictionary };
27
+ //# sourceMappingURL=preloadedDynamicLoader.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preloadedDynamicLoader.mjs","names":[],"sources":["../../../src/dictionaryManipulator/preloadedDynamicLoader.ts"],"sourcesContent":["import { PRELOADED_DYNAMIC_KEY } from '@intlayer/config/defaultValues';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\n/**\n * Marker under which a generated dynamic entry point carries the dictionary it\n * preloaded while evaluating. Defined in `@intlayer/config` so the code\n * generator and the build plugins can read it without pulling the runtime in,\n * and re-exported here where the consumers of the loaded content live.\n */\nexport { PRELOADED_DYNAMIC_KEY };\n\n/**\n * The value attached under {@link PRELOADED_DYNAMIC_KEY}.\n *\n * `locale` is the locale the preamble resolved at module-evaluation time. It is\n * compared against the locale the read actually asks for, so a disagreement\n * (a client-side locale switch, or a resolution the provider overrode) falls\n * back to loading rather than serving content in the wrong language.\n */\nexport type PreloadedDynamicEntry = {\n locale: LocalesValues;\n dictionary: Dictionary;\n};\n\n/** A plain dynamic loader map that may carry a preloaded entry. */\nexport type PreloadableDynamicLoaderMap = {\n [PRELOADED_DYNAMIC_KEY]?: PreloadedDynamicEntry;\n};\n\n/**\n * Reads the preloaded dictionary of a dynamic loader map, when one was resolved\n * for the requested locale.\n *\n * Deliberately synchronous, and deliberately not routed through the suspender\n * cache: a suspender only leaves `pending` inside a `.then` callback, so feeding\n * it an already-resolved promise would still suspend for one microtask — long\n * enough for a pending fallback to paint, which is the flash this whole\n * mechanism exists to remove.\n *\n * @param loaderMap - Default export of a generated dynamic entry point.\n * @param locale - Locale the caller is about to render.\n * @returns The dictionary when it was preloaded for `locale`, else `undefined`.\n */\nexport const getPreloadedDictionary = (\n loaderMap: unknown,\n locale: LocalesValues\n): Dictionary | undefined => {\n if (typeof loaderMap !== 'object' || loaderMap === null) return undefined;\n\n const preloaded = (loaderMap as PreloadableDynamicLoaderMap)[\n PRELOADED_DYNAMIC_KEY\n ];\n\n if (!preloaded || preloaded.locale !== locale) return undefined;\n\n return preloaded.dictionary;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AA4CA,MAAa,0BACX,WACA,WAC2B;CAC3B,IAAI,OAAO,cAAc,YAAY,cAAc,MAAM,OAAO;CAEhE,MAAM,YAAa,UACjB;CAGF,IAAI,CAAC,aAAa,UAAU,WAAW,QAAQ,OAAO;CAEtD,OAAO,UAAU;AACnB"}
@@ -1,18 +1,12 @@
1
1
  import { COMPOSITE_ID_SEPARATOR, resolveEffectiveVariantId, serializeVariantChain } from "./qualifiedDictionary.mjs";
2
+ import { QUALIFIER_DYNAMIC_TYPES_KEY } from "@intlayer/config/defaultValues";
2
3
 
3
4
  //#region src/dictionaryManipulator/qualifiedDynamicLoader.ts
4
5
  /**
5
- * Marker property carrying the ordered qualifier dimensions on a dynamic loader
6
- * map. Its presence distinguishes a qualified group loader map (a nested tree
7
- * of chunks) from a plain dynamic loader map (one chunk per `locale`). Prefixed
8
- * and unlikely to collide with a real locale code.
9
- */
10
- const QUALIFIER_DYNAMIC_TYPES_KEY = "__intlayerQualifierTypes";
11
- /**
12
6
  * Type guard discriminating a qualified dynamic loader map (collections /
13
7
  * variants, possibly combined) from a plain dynamic loader map.
14
8
  */
15
- const isQualifiedDynamicLoaderMap = (value) => typeof value === "object" && value !== null && "__intlayerQualifierTypes" in value;
9
+ const isQualifiedDynamicLoaderMap = (value) => typeof value === "object" && value !== null && QUALIFIER_DYNAMIC_TYPES_KEY in value;
16
10
  /**
17
11
  * Walks the loader tree following the selector and collects the chunk loaders
18
12
  * it targets — shared by the sync ({@link resolveQualifiedDynamicContent}) and
@@ -1 +1 @@
1
- {"version":3,"file":"qualifiedDynamicLoader.mjs","names":[],"sources":["../../../src/dictionaryManipulator/qualifiedDynamicLoader.ts"],"sourcesContent":["import type {\n Dictionary,\n DictionaryQualifierType,\n DictionarySelector,\n} from '@intlayer/types/dictionary';\nimport {\n COMPOSITE_ID_SEPARATOR,\n resolveEffectiveVariantId,\n serializeVariantChain,\n} from './qualifiedDictionary';\n\n/**\n * Marker property carrying the ordered qualifier dimensions on a dynamic loader\n * map. Its presence distinguishes a qualified group loader map (a nested tree\n * of chunks) from a plain dynamic loader map (one chunk per `locale`). Prefixed\n * and unlikely to collide with a real locale code.\n */\nexport const QUALIFIER_DYNAMIC_TYPES_KEY = '__intlayerQualifierTypes';\n\n/**\n * A lazily-imported per-locale dictionary chunk loader.\n */\nexport type DynamicDictionaryLoader = () => Promise<Dictionary>;\n\n/**\n * Nested tree of chunk loaders: one nesting level per declared dimension (in\n * canonical order), leaves are loaders.\n */\nexport type QualifiedDynamicLoaderTree = {\n [segment: string]: QualifiedDynamicLoaderTree | DynamicDictionaryLoader;\n};\n\n/**\n * Default export shape of a generated dynamic entry point for a qualified key.\n * One nesting level per dimension under each locale, plus the dimension marker.\n *\n * ```ts\n * {\n * __intlayerQualifierTypes: ['variant', 'item'],\n * en: { promo: { '1': () => import('./json/x/promo/1/en.json'), … }, … },\n * fr: { … },\n * }\n * ```\n */\nexport type QualifiedDynamicLoaderMap = {\n [QUALIFIER_DYNAMIC_TYPES_KEY]: DictionaryQualifierType[];\n [locale: string]: QualifiedDynamicLoaderTree | DictionaryQualifierType[];\n};\n\n/**\n * Type guard discriminating a qualified dynamic loader map (collections /\n * variants, possibly combined) from a plain dynamic loader map.\n */\nexport const isQualifiedDynamicLoaderMap = (\n value: unknown\n): value is QualifiedDynamicLoaderMap =>\n typeof value === 'object' &&\n value !== null &&\n QUALIFIER_DYNAMIC_TYPES_KEY in value;\n\n/** One targeted chunk: its stable cache key and lazy loader. */\ntype CollectedChunk = {\n cacheKey: string;\n loader: DynamicDictionaryLoader;\n};\n\ntype CollectedChunks = {\n /** True when the `item` axis is open (collection result → array). */\n itemAxisOpen: boolean;\n /** True when a required coordinate is absent (result → [] or null). */\n missed: boolean;\n /** The chunks the selector targets (in collection order for the item axis). */\n chunks: CollectedChunk[];\n};\n\n/**\n * Walks the loader tree following the selector and collects the chunk loaders\n * it targets — shared by the sync ({@link resolveQualifiedDynamicContent}) and\n * async ({@link resolveQualifiedDynamicContentAsync}) resolvers.\n */\nconst collectQualifiedChunks = (\n loaderMap: QualifiedDynamicLoaderMap,\n key: string,\n locale: string,\n selector: DictionarySelector | undefined\n): CollectedChunks => {\n const qualifierTypes = loaderMap[QUALIFIER_DYNAMIC_TYPES_KEY];\n const localeTree = loaderMap[locale] as\n | QualifiedDynamicLoaderTree\n | undefined;\n\n const itemAxisOpen =\n qualifierTypes.includes('item') && selector?.item === undefined;\n\n if (!localeTree) return { itemAxisOpen, missed: true, chunks: [] };\n\n const chunks: CollectedChunk[] = [];\n\n const walk = (\n node: QualifiedDynamicLoaderTree | DynamicDictionaryLoader,\n dimensions: DictionaryQualifierType[],\n segments: string[]\n ): boolean => {\n if (dimensions.length === 0) {\n chunks.push({\n cacheKey: `${key}.${locale}.${segments.join(COMPOSITE_ID_SEPARATOR)}`,\n loader: node as DynamicDictionaryLoader,\n });\n return true;\n }\n\n const [dimension, ...rest] = dimensions;\n const tree = node as QualifiedDynamicLoaderTree;\n\n if (dimension === 'item' && selector?.item === undefined) {\n // Open collection axis: fan out into every sibling chunk, ordered.\n for (const segment of Object.keys(tree).sort(\n (left, right) => Number(left) - Number(right)\n )) {\n walk(tree[segment]!, rest, [...segments, segment]);\n }\n return true;\n }\n\n // A variant with no chunk of its own falls back to the `default` chunk, so\n // only the entries that actually differ need to be emitted.\n const segment =\n dimension === 'variant'\n ? resolveEffectiveVariantId(\n serializeVariantChain(selector?.variant),\n (variantId) => tree[variantId] !== undefined\n )\n : String(selector?.item);\n\n const child = tree[segment];\n if (!child) return false;\n\n return walk(child, rest, [...segments, segment]);\n };\n\n const found = walk(localeTree, qualifierTypes, []);\n\n return { itemAxisOpen, missed: !found, chunks };\n};\n\n/**\n * Resolves the content of a qualified dynamic loader map against a selector,\n * loading only the chunk(s) the selector actually targets.\n *\n * Walks the nested loader tree one dimension at a time (canonical order\n * `variant → item`): `variant` descends by the serialized id, falling back to\n * `default` when the selected variant has no chunk of its own, and `item`\n * either narrows to the selected index or — when no item is given — expands\n * into every sibling chunk (the collection axis). Semantics mirror\n * `resolveQualifiedDictionary` so dynamic and static modes behave alike.\n *\n * The Suspense mechanism is injected through `loadChunk` so the same logic\n * serves both the client (suspender cache) and the server (`react.use`). Every\n * targeted loader is started before the first chunk is read, so sibling chunks\n * load in parallel rather than waterfalling.\n *\n * @param loaderMap - The qualified dynamic loader map (entry point default export).\n * @param key - The dictionary key (used to build stable chunk cache keys).\n * @param locale - The resolved locale to load chunks for.\n * @param selector - The selector splitting the qualifier dimensions.\n * @param loadChunk - Reads a started chunk promise, suspending until it resolves.\n * @param transform - Turns a resolved chunk dictionary into final content.\n */\nexport const resolveQualifiedDynamicContent = <Content>(params: {\n loaderMap: QualifiedDynamicLoaderMap;\n key: string;\n locale: string;\n selector: DictionarySelector | undefined;\n loadChunk: (cacheKey: string, promise: Promise<Dictionary>) => Dictionary;\n transform: (dictionary: Dictionary) => Content;\n}): Content | Content[] | null => {\n const { loaderMap, key, locale, selector, loadChunk, transform } = params;\n\n const { itemAxisOpen, missed, chunks } = collectQualifiedChunks(\n loaderMap,\n key,\n locale,\n selector\n );\n\n if (missed) return itemAxisOpen ? [] : null;\n\n // Start every loader before reading, so siblings load in parallel.\n const dictionaries = chunks.map(({ cacheKey, loader }) =>\n loadChunk(cacheKey, loader())\n );\n\n if (itemAxisOpen) return dictionaries.map(transform);\n\n const [dictionary] = dictionaries;\n return dictionary ? transform(dictionary) : null;\n};\n\n/**\n * Async counterpart of {@link resolveQualifiedDynamicContent} for frameworks\n * that load dictionaries with `await` instead of Suspense (Vue, Svelte, Lit,\n * vanilla). Awaits every targeted chunk in parallel, then resolves identically.\n *\n * @param loaderMap - The qualified dynamic loader map.\n * @param key - The dictionary key (used to build stable chunk cache keys).\n * @param locale - The resolved locale to load chunks for.\n * @param selector - The selector splitting the qualifier dimensions.\n * @param transform - Turns a resolved chunk dictionary into final content.\n */\nexport const resolveQualifiedDynamicContentAsync = async <Content>(params: {\n loaderMap: QualifiedDynamicLoaderMap;\n key: string;\n locale: string;\n selector: DictionarySelector | undefined;\n transform: (dictionary: Dictionary) => Content;\n}): Promise<Content | Content[] | null> => {\n const { loaderMap, key, locale, selector, transform } = params;\n\n const { itemAxisOpen, missed, chunks } = collectQualifiedChunks(\n loaderMap,\n key,\n locale,\n selector\n );\n\n if (missed) return itemAxisOpen ? [] : null;\n\n const dictionaries = await Promise.all(chunks.map(({ loader }) => loader()));\n\n if (itemAxisOpen) return dictionaries.map(transform);\n\n const [dictionary] = dictionaries;\n return dictionary ? transform(dictionary) : null;\n};\n"],"mappings":";;;;;;;;;AAiBA,MAAa,8BAA8B;;;;;AAoC3C,MAAa,+BACX,UAEA,OAAO,UAAU,YACjB,UAAU,sCACqB;;;;;;AAsBjC,MAAM,0BACJ,WACA,KACA,QACA,aACoB;CACpB,MAAM,iBAAiB,UAAU;CACjC,MAAM,aAAa,UAAU;CAI7B,MAAM,eACJ,eAAe,SAAS,MAAM,KAAK,UAAU,SAAS;CAExD,IAAI,CAAC,YAAY,OAAO;EAAE;EAAc,QAAQ;EAAM,QAAQ,CAAC;CAAE;CAEjE,MAAM,SAA2B,CAAC;CAElC,MAAM,QACJ,MACA,YACA,aACY;EACZ,IAAI,WAAW,WAAW,GAAG;GAC3B,OAAO,KAAK;IACV,UAAU,GAAG,IAAI,GAAG,OAAO,GAAG,SAAS,QAA2B;IAClE,QAAQ;GACV,CAAC;GACD,OAAO;EACT;EAEA,MAAM,CAAC,WAAW,GAAG,QAAQ;EAC7B,MAAM,OAAO;EAEb,IAAI,cAAc,UAAU,UAAU,SAAS,QAAW;GAExD,KAAK,MAAM,WAAW,OAAO,KAAK,IAAI,CAAC,CAAC,MACrC,MAAM,UAAU,OAAO,IAAI,IAAI,OAAO,KAAK,CAC9C,GACE,KAAK,KAAK,UAAW,MAAM,CAAC,GAAG,UAAU,OAAO,CAAC;GAEnD,OAAO;EACT;EAIA,MAAM,UACJ,cAAc,YACV,0BACE,sBAAsB,UAAU,OAAO,IACtC,cAAc,KAAK,eAAe,MACrC,IACA,OAAO,UAAU,IAAI;EAE3B,MAAM,QAAQ,KAAK;EACnB,IAAI,CAAC,OAAO,OAAO;EAEnB,OAAO,KAAK,OAAO,MAAM,CAAC,GAAG,UAAU,OAAO,CAAC;CACjD;CAIA,OAAO;EAAE;EAAc,QAAQ,CAFjB,KAAK,YAAY,gBAAgB,CAAC,CAEZ;EAAG;CAAO;AAChD;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,MAAa,kCAA2C,WAOtB;CAChC,MAAM,EAAE,WAAW,KAAK,QAAQ,UAAU,WAAW,cAAc;CAEnE,MAAM,EAAE,cAAc,QAAQ,WAAW,uBACvC,WACA,KACA,QACA,QACF;CAEA,IAAI,QAAQ,OAAO,eAAe,CAAC,IAAI;CAGvC,MAAM,eAAe,OAAO,KAAK,EAAE,UAAU,aAC3C,UAAU,UAAU,OAAO,CAAC,CAC9B;CAEA,IAAI,cAAc,OAAO,aAAa,IAAI,SAAS;CAEnD,MAAM,CAAC,cAAc;CACrB,OAAO,aAAa,UAAU,UAAU,IAAI;AAC9C;;;;;;;;;;;;AAaA,MAAa,sCAAsC,OAAgB,WAMxB;CACzC,MAAM,EAAE,WAAW,KAAK,QAAQ,UAAU,cAAc;CAExD,MAAM,EAAE,cAAc,QAAQ,WAAW,uBACvC,WACA,KACA,QACA,QACF;CAEA,IAAI,QAAQ,OAAO,eAAe,CAAC,IAAI;CAEvC,MAAM,eAAe,MAAM,QAAQ,IAAI,OAAO,KAAK,EAAE,aAAa,OAAO,CAAC,CAAC;CAE3E,IAAI,cAAc,OAAO,aAAa,IAAI,SAAS;CAEnD,MAAM,CAAC,cAAc;CACrB,OAAO,aAAa,UAAU,UAAU,IAAI;AAC9C"}
1
+ {"version":3,"file":"qualifiedDynamicLoader.mjs","names":[],"sources":["../../../src/dictionaryManipulator/qualifiedDynamicLoader.ts"],"sourcesContent":["import { QUALIFIER_DYNAMIC_TYPES_KEY } from '@intlayer/config/defaultValues';\nimport type {\n Dictionary,\n DictionaryQualifierType,\n DictionarySelector,\n} from '@intlayer/types/dictionary';\nimport {\n COMPOSITE_ID_SEPARATOR,\n resolveEffectiveVariantId,\n serializeVariantChain,\n} from './qualifiedDictionary';\n\n/**\n * Marker property carrying the ordered qualifier dimensions on a dynamic loader\n * map. Defined in `@intlayer/config` so the code generator and the build\n * plugins can read it without pulling the runtime in, and re-exported here\n * where its consumers live.\n */\nexport { QUALIFIER_DYNAMIC_TYPES_KEY };\n\n/**\n * A lazily-imported per-locale dictionary chunk loader.\n */\nexport type DynamicDictionaryLoader = () => Promise<Dictionary>;\n\n/**\n * Nested tree of chunk loaders: one nesting level per declared dimension (in\n * canonical order), leaves are loaders.\n */\nexport type QualifiedDynamicLoaderTree = {\n [segment: string]: QualifiedDynamicLoaderTree | DynamicDictionaryLoader;\n};\n\n/**\n * Default export shape of a generated dynamic entry point for a qualified key.\n * One nesting level per dimension under each locale, plus the dimension marker.\n *\n * ```ts\n * {\n * __intlayerQualifierTypes: ['variant', 'item'],\n * en: { promo: { '1': () => import('./json/x/promo/1/en.json'), … }, … },\n * fr: { … },\n * }\n * ```\n */\nexport type QualifiedDynamicLoaderMap = {\n [QUALIFIER_DYNAMIC_TYPES_KEY]: DictionaryQualifierType[];\n [locale: string]: QualifiedDynamicLoaderTree | DictionaryQualifierType[];\n};\n\n/**\n * Type guard discriminating a qualified dynamic loader map (collections /\n * variants, possibly combined) from a plain dynamic loader map.\n */\nexport const isQualifiedDynamicLoaderMap = (\n value: unknown\n): value is QualifiedDynamicLoaderMap =>\n typeof value === 'object' &&\n value !== null &&\n QUALIFIER_DYNAMIC_TYPES_KEY in value;\n\n/** One targeted chunk: its stable cache key and lazy loader. */\ntype CollectedChunk = {\n cacheKey: string;\n loader: DynamicDictionaryLoader;\n};\n\ntype CollectedChunks = {\n /** True when the `item` axis is open (collection result → array). */\n itemAxisOpen: boolean;\n /** True when a required coordinate is absent (result → [] or null). */\n missed: boolean;\n /** The chunks the selector targets (in collection order for the item axis). */\n chunks: CollectedChunk[];\n};\n\n/**\n * Walks the loader tree following the selector and collects the chunk loaders\n * it targets — shared by the sync ({@link resolveQualifiedDynamicContent}) and\n * async ({@link resolveQualifiedDynamicContentAsync}) resolvers.\n */\nconst collectQualifiedChunks = (\n loaderMap: QualifiedDynamicLoaderMap,\n key: string,\n locale: string,\n selector: DictionarySelector | undefined\n): CollectedChunks => {\n const qualifierTypes = loaderMap[QUALIFIER_DYNAMIC_TYPES_KEY];\n const localeTree = loaderMap[locale] as\n | QualifiedDynamicLoaderTree\n | undefined;\n\n const itemAxisOpen =\n qualifierTypes.includes('item') && selector?.item === undefined;\n\n if (!localeTree) return { itemAxisOpen, missed: true, chunks: [] };\n\n const chunks: CollectedChunk[] = [];\n\n const walk = (\n node: QualifiedDynamicLoaderTree | DynamicDictionaryLoader,\n dimensions: DictionaryQualifierType[],\n segments: string[]\n ): boolean => {\n if (dimensions.length === 0) {\n chunks.push({\n cacheKey: `${key}.${locale}.${segments.join(COMPOSITE_ID_SEPARATOR)}`,\n loader: node as DynamicDictionaryLoader,\n });\n return true;\n }\n\n const [dimension, ...rest] = dimensions;\n const tree = node as QualifiedDynamicLoaderTree;\n\n if (dimension === 'item' && selector?.item === undefined) {\n // Open collection axis: fan out into every sibling chunk, ordered.\n for (const segment of Object.keys(tree).sort(\n (left, right) => Number(left) - Number(right)\n )) {\n walk(tree[segment]!, rest, [...segments, segment]);\n }\n return true;\n }\n\n // A variant with no chunk of its own falls back to the `default` chunk, so\n // only the entries that actually differ need to be emitted.\n const segment =\n dimension === 'variant'\n ? resolveEffectiveVariantId(\n serializeVariantChain(selector?.variant),\n (variantId) => tree[variantId] !== undefined\n )\n : String(selector?.item);\n\n const child = tree[segment];\n if (!child) return false;\n\n return walk(child, rest, [...segments, segment]);\n };\n\n const found = walk(localeTree, qualifierTypes, []);\n\n return { itemAxisOpen, missed: !found, chunks };\n};\n\n/**\n * Resolves the content of a qualified dynamic loader map against a selector,\n * loading only the chunk(s) the selector actually targets.\n *\n * Walks the nested loader tree one dimension at a time (canonical order\n * `variant → item`): `variant` descends by the serialized id, falling back to\n * `default` when the selected variant has no chunk of its own, and `item`\n * either narrows to the selected index or — when no item is given — expands\n * into every sibling chunk (the collection axis). Semantics mirror\n * `resolveQualifiedDictionary` so dynamic and static modes behave alike.\n *\n * The Suspense mechanism is injected through `loadChunk` so the same logic\n * serves both the client (suspender cache) and the server (`react.use`). Every\n * targeted loader is started before the first chunk is read, so sibling chunks\n * load in parallel rather than waterfalling.\n *\n * @param loaderMap - The qualified dynamic loader map (entry point default export).\n * @param key - The dictionary key (used to build stable chunk cache keys).\n * @param locale - The resolved locale to load chunks for.\n * @param selector - The selector splitting the qualifier dimensions.\n * @param loadChunk - Reads a started chunk promise, suspending until it resolves.\n * @param transform - Turns a resolved chunk dictionary into final content.\n */\nexport const resolveQualifiedDynamicContent = <Content>(params: {\n loaderMap: QualifiedDynamicLoaderMap;\n key: string;\n locale: string;\n selector: DictionarySelector | undefined;\n loadChunk: (cacheKey: string, promise: Promise<Dictionary>) => Dictionary;\n transform: (dictionary: Dictionary) => Content;\n}): Content | Content[] | null => {\n const { loaderMap, key, locale, selector, loadChunk, transform } = params;\n\n const { itemAxisOpen, missed, chunks } = collectQualifiedChunks(\n loaderMap,\n key,\n locale,\n selector\n );\n\n if (missed) return itemAxisOpen ? [] : null;\n\n // Start every loader before reading, so siblings load in parallel.\n const dictionaries = chunks.map(({ cacheKey, loader }) =>\n loadChunk(cacheKey, loader())\n );\n\n if (itemAxisOpen) return dictionaries.map(transform);\n\n const [dictionary] = dictionaries;\n return dictionary ? transform(dictionary) : null;\n};\n\n/**\n * Async counterpart of {@link resolveQualifiedDynamicContent} for frameworks\n * that load dictionaries with `await` instead of Suspense (Vue, Svelte, Lit,\n * vanilla). Awaits every targeted chunk in parallel, then resolves identically.\n *\n * @param loaderMap - The qualified dynamic loader map.\n * @param key - The dictionary key (used to build stable chunk cache keys).\n * @param locale - The resolved locale to load chunks for.\n * @param selector - The selector splitting the qualifier dimensions.\n * @param transform - Turns a resolved chunk dictionary into final content.\n */\nexport const resolveQualifiedDynamicContentAsync = async <Content>(params: {\n loaderMap: QualifiedDynamicLoaderMap;\n key: string;\n locale: string;\n selector: DictionarySelector | undefined;\n transform: (dictionary: Dictionary) => Content;\n}): Promise<Content | Content[] | null> => {\n const { loaderMap, key, locale, selector, transform } = params;\n\n const { itemAxisOpen, missed, chunks } = collectQualifiedChunks(\n loaderMap,\n key,\n locale,\n selector\n );\n\n if (missed) return itemAxisOpen ? [] : null;\n\n const dictionaries = await Promise.all(chunks.map(({ loader }) => loader()));\n\n if (itemAxisOpen) return dictionaries.map(transform);\n\n const [dictionary] = dictionaries;\n return dictionary ? transform(dictionary) : null;\n};\n"],"mappings":";;;;;;;;AAsDA,MAAa,+BACX,UAEA,OAAO,UAAU,YACjB,UAAU,QACV,+BAA+B;;;;;;AAsBjC,MAAM,0BACJ,WACA,KACA,QACA,aACoB;CACpB,MAAM,iBAAiB,UAAU;CACjC,MAAM,aAAa,UAAU;CAI7B,MAAM,eACJ,eAAe,SAAS,MAAM,KAAK,UAAU,SAAS;CAExD,IAAI,CAAC,YAAY,OAAO;EAAE;EAAc,QAAQ;EAAM,QAAQ,CAAC;CAAE;CAEjE,MAAM,SAA2B,CAAC;CAElC,MAAM,QACJ,MACA,YACA,aACY;EACZ,IAAI,WAAW,WAAW,GAAG;GAC3B,OAAO,KAAK;IACV,UAAU,GAAG,IAAI,GAAG,OAAO,GAAG,SAAS,QAA2B;IAClE,QAAQ;GACV,CAAC;GACD,OAAO;EACT;EAEA,MAAM,CAAC,WAAW,GAAG,QAAQ;EAC7B,MAAM,OAAO;EAEb,IAAI,cAAc,UAAU,UAAU,SAAS,QAAW;GAExD,KAAK,MAAM,WAAW,OAAO,KAAK,IAAI,CAAC,CAAC,MACrC,MAAM,UAAU,OAAO,IAAI,IAAI,OAAO,KAAK,CAC9C,GACE,KAAK,KAAK,UAAW,MAAM,CAAC,GAAG,UAAU,OAAO,CAAC;GAEnD,OAAO;EACT;EAIA,MAAM,UACJ,cAAc,YACV,0BACE,sBAAsB,UAAU,OAAO,IACtC,cAAc,KAAK,eAAe,MACrC,IACA,OAAO,UAAU,IAAI;EAE3B,MAAM,QAAQ,KAAK;EACnB,IAAI,CAAC,OAAO,OAAO;EAEnB,OAAO,KAAK,OAAO,MAAM,CAAC,GAAG,UAAU,OAAO,CAAC;CACjD;CAIA,OAAO;EAAE;EAAc,QAAQ,CAFjB,KAAK,YAAY,gBAAgB,CAAC,CAEZ;EAAG;CAAO;AAChD;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,MAAa,kCAA2C,WAOtB;CAChC,MAAM,EAAE,WAAW,KAAK,QAAQ,UAAU,WAAW,cAAc;CAEnE,MAAM,EAAE,cAAc,QAAQ,WAAW,uBACvC,WACA,KACA,QACA,QACF;CAEA,IAAI,QAAQ,OAAO,eAAe,CAAC,IAAI;CAGvC,MAAM,eAAe,OAAO,KAAK,EAAE,UAAU,aAC3C,UAAU,UAAU,OAAO,CAAC,CAC9B;CAEA,IAAI,cAAc,OAAO,aAAa,IAAI,SAAS;CAEnD,MAAM,CAAC,cAAc;CACrB,OAAO,aAAa,UAAU,UAAU,IAAI;AAC9C;;;;;;;;;;;;AAaA,MAAa,sCAAsC,OAAgB,WAMxB;CACzC,MAAM,EAAE,WAAW,KAAK,QAAQ,UAAU,cAAc;CAExD,MAAM,EAAE,cAAc,QAAQ,WAAW,uBACvC,WACA,KACA,QACA,QACF;CAEA,IAAI,QAAQ,OAAO,eAAe,CAAC,IAAI;CAEvC,MAAM,eAAe,MAAM,QAAQ,IAAI,OAAO,KAAK,EAAE,aAAa,OAAO,CAAC,CAAC;CAE3E,IAAI,cAAc,OAAO,aAAa,IAAI,SAAS;CAEnD,MAAM,CAAC,cAAc;CACrB,OAAO,aAAa,UAAU,UAAU,IAAI;AAC9C"}
@@ -64,6 +64,7 @@ import { QUALIFIER_ORDER, getDictionaryCompositeIds, getDictionaryQualifierIds,
64
64
  import { mergeQualifiedDictionaries } from "./dictionaryManipulator/mergeQualifiedDictionaries.mjs";
65
65
  import { orderDictionaries } from "./dictionaryManipulator/orderDictionaries.mjs";
66
66
  import { normalizeDictionaries, normalizeDictionary } from "./dictionaryManipulator/normalizeDictionary.mjs";
67
+ import { PRELOADED_DYNAMIC_KEY, getPreloadedDictionary } from "./dictionaryManipulator/preloadedDynamicLoader.mjs";
67
68
  import { QUALIFIER_DYNAMIC_TYPES_KEY, isQualifiedDynamicLoaderMap, resolveQualifiedDynamicContent, resolveQualifiedDynamicContentAsync } from "./dictionaryManipulator/qualifiedDynamicLoader.mjs";
68
69
  import { removeContentNodeByKeyPath } from "./dictionaryManipulator/removeContentNodeByKeyPath.mjs";
69
70
  import { renameContentNodeByKeyPath } from "./dictionaryManipulator/renameContentNodeByKeyPath.mjs";
@@ -72,8 +73,9 @@ import { checkIsURLAbsolute } from "./utils/checkIsURLAbsolute.mjs";
72
73
  import { getPathWithoutLocale } from "./localization/getPathWithoutLocale.mjs";
73
74
  import { comparePaths, normalizePath } from "./localization/comparePaths.mjs";
74
75
  import { getDomainHostname, getDomainOrigin, getLocaleFromDomain, isLocaleExclusiveOnDomain } from "./localization/domainUtils.mjs";
75
- import { getPrefix } from "./localization/getPrefix.mjs";
76
- import { getCanonicalPath, getInternalPath, getLocalizedPath, getRewritePath, getRewriteRules } from "./localization/rewriteUtils.mjs";
76
+ import { getPrefix, isDeclaredLocale } from "./localization/getPrefix.mjs";
77
+ import { getCanonicalPath, getInternalPath, getRewritePath, getRewriteRules, resolveLocalizedPath } from "./localization/rewriteUtils.mjs";
78
+ import { getLocalizedPath } from "./localization/getLocalizedPath.mjs";
77
79
  import { getLocalizedUrl } from "./localization/getLocalizedUrl.mjs";
78
80
  import { getMultilingualUrls } from "./localization/getMultilingualUrls.mjs";
79
81
  import { generateSitemap, generateSitemapUrl } from "./localization/generateSitemap.mjs";
@@ -89,8 +91,9 @@ import { getLocaleLang } from "./localization/getLocaleLang.mjs";
89
91
  import { getLocaleName } from "./localization/getLocaleName.mjs";
90
92
  import { localeFlatMap, localeMap, localeRecord } from "./localization/localeMapper.mjs";
91
93
  import { formatProxyEnabledMessage, isProxyStorageLocaleEnabled, resolveProxyMode } from "./localization/proxyMode.mjs";
94
+ import { getPreloadLocale, resolveBrowserLocale } from "./localization/resolveBrowserLocale.mjs";
92
95
  import { validatePrefix } from "./localization/validatePrefix.mjs";
93
- import { ATTRIBUTES_TO_SANITIZE, ATTRIBUTE_TO_NODE_PROP_MAP, ATTR_EXTRACTOR_R, BLOCKQUOTE_ALERT_R, BLOCKQUOTE_R, BLOCKQUOTE_TRIM_LEFT_MULTILINE_R, BLOCK_END_R, BREAK_LINE_R, BREAK_THEMATIC_R, CAPTURE_LETTER_AFTER_HYPHEN, CODE_BLOCK_FENCED_R, CODE_BLOCK_R, CODE_INLINE_R, CONSECUTIVE_NEWLINE_R, CR_NEWLINE_R, CUSTOM_COMPONENT_R, DO_NOT_PROCESS_HTML_ELEMENTS, DURATION_DELAY_TRIGGER, FOOTNOTE_R, FOOTNOTE_REFERENCE_R, FORMFEED_R, FRONT_MATTER_R, GFM_TASK_R, HEADING_ATX_COMPLIANT_R, HEADING_R, HEADING_SETEXT_R, HTML_BLOCK_ELEMENT_R, HTML_CHAR_CODE_R, HTML_COMMENT_R, HTML_CUSTOM_ATTR_R, HTML_LEFT_TRIM_AMOUNT_R, HTML_SELF_CLOSING_ELEMENT_R, INLINE_SKIP_R, INTERPOLATION_R, LINK_AUTOLINK_BARE_URL_R, LINK_AUTOLINK_R, LIST_LOOKBEHIND_R, LOOKAHEAD, NAMED_CODES_TO_UNICODE, NP_TABLE_R, ORDERED, ORDERED_LIST_BULLET, ORDERED_LIST_ITEM_PREFIX, ORDERED_LIST_ITEM_PREFIX_R, ORDERED_LIST_ITEM_R, ORDERED_LIST_R, PARAGRAPH_R, Priority, REFERENCE_IMAGE_OR_LINK, REFERENCE_IMAGE_R, REFERENCE_LINK_R, RuleType, SHORTCODE_R, SHOULD_RENDER_AS_BLOCK_R, TABLE_CENTER_ALIGN, TABLE_LEFT_ALIGN, TABLE_RIGHT_ALIGN, TABLE_TRIM_PIPES, TAB_R, TEXT_BOLD_R, TEXT_EMPHASIZED_R, TEXT_ESCAPED_R, TEXT_MARKED_R, TEXT_PLAIN_R, TEXT_STRIKETHROUGHED_R, TRIM_STARTING_NEWLINES, UNESCAPE_R, UNORDERED, UNORDERED_LIST_BULLET, UNORDERED_LIST_ITEM_PREFIX, UNORDERED_LIST_ITEM_PREFIX_R, UNORDERED_LIST_ITEM_R, UNORDERED_LIST_R, generateListItemPrefix, generateListItemPrefixRegex, generateListItemRegex, generateListRegex } from "./markdown/constants.mjs";
96
+ import { ATTRIBUTES_TO_SANITIZE, ATTRIBUTE_TO_NODE_PROP_MAP, ATTR_EXTRACTOR_R, BLOCKQUOTE_ALERT_R, BLOCKQUOTE_R, BLOCKQUOTE_TRIM_LEFT_MULTILINE_R, BLOCK_END_R, BREAK_LINE_R, BREAK_THEMATIC_R, CAPTURE_LETTER_AFTER_HYPHEN, CODE_BLOCK_FENCED_R, CODE_BLOCK_R, CODE_INLINE_R, CONSECUTIVE_NEWLINE_R, CR_NEWLINE_R, CUSTOM_COMPONENT_R, DO_NOT_PROCESS_HTML_ELEMENTS, DURATION_DELAY_TRIGGER, FENCE_DELIMITER_R, FOOTNOTE_R, FOOTNOTE_REFERENCE_R, FORMFEED_R, FRONT_MATTER_R, GFM_TASK_R, HEADING_ATX_COMPLIANT_R, HEADING_R, HEADING_SETEXT_R, HTML_BLOCK_ELEMENT_R, HTML_CHAR_CODE_R, HTML_COMMENT_R, HTML_CUSTOM_ATTR_R, HTML_LEFT_TRIM_AMOUNT_R, HTML_SELF_CLOSING_ELEMENT_R, INLINE_SKIP_R, INTERPOLATION_R, LINK_AUTOLINK_BARE_URL_R, LINK_AUTOLINK_R, LIST_LOOKBEHIND_R, LOOKAHEAD, NAMED_CODES_TO_UNICODE, NP_TABLE_R, ORDERED, ORDERED_LIST_BULLET, ORDERED_LIST_ITEM_PREFIX, ORDERED_LIST_ITEM_PREFIX_R, ORDERED_LIST_ITEM_R, ORDERED_LIST_R, PARAGRAPH_R, Priority, REFERENCE_IMAGE_OR_LINK, REFERENCE_IMAGE_R, REFERENCE_LINK_R, RuleType, SHORTCODE_R, SHOULD_RENDER_AS_BLOCK_R, TABLE_CENTER_ALIGN, TABLE_LEFT_ALIGN, TABLE_RIGHT_ALIGN, TABLE_TRIM_PIPES, TAB_R, TEXT_BOLD_R, TEXT_EMPHASIZED_R, TEXT_ESCAPED_R, TEXT_MARKED_R, TEXT_PLAIN_R, TEXT_STRIKETHROUGHED_R, TRIM_STARTING_NEWLINES, UNESCAPE_R, UNORDERED, UNORDERED_LIST_BULLET, UNORDERED_LIST_ITEM_PREFIX, UNORDERED_LIST_ITEM_PREFIX_R, UNORDERED_LIST_ITEM_R, UNORDERED_LIST_R, generateListItemPrefix, generateListItemPrefixRegex, generateListItemRegex, generateListRegex } from "./markdown/constants.mjs";
94
97
  import { allowInline, anyScopeRegex, attributeValueToNodePropValue, blockRegex, captureNothing, cx, get, inlineRegex, normalizeAttributeKey, normalizeWhitespace, parseBlock, parseCaptureInline, parseInline, parseSimpleInline, parseStyleAttribute, parseTableAlign, parseTableAlignCapture, parseTableCells, parseTableRow, qualifies, renderNothing, sanitizer, simpleInlineRegex, slugify, some, startsWith, trimEnd, trimLeadingWhitespaceOutsideFences, unescapeString, unquote } from "./markdown/utils.mjs";
95
98
  import { parserFor } from "./markdown/parser.mjs";
96
99
  import { createRenderer, renderFor } from "./markdown/renderer.mjs";
@@ -104,4 +107,4 @@ import { interpolateMessage, parseTaggedMessage, resolveMessage, resolveMessageN
104
107
  import { isSameKeyPath } from "./utils/isSameKeyPath.mjs";
105
108
  import { stringifyYaml } from "./utils/stringifyYaml.mjs";
106
109
 
107
- export { ATTRIBUTES_TO_SANITIZE, ATTRIBUTE_TO_NODE_PROP_MAP, ATTR_EXTRACTOR_R, BLOCKQUOTE_ALERT_R, BLOCKQUOTE_R, BLOCKQUOTE_TRIM_LEFT_MULTILINE_R, BLOCK_END_R, BREAK_LINE_R, BREAK_THEMATIC_R, CAPTURE_LETTER_AFTER_HYPHEN, CODE_BLOCK_FENCED_R, CODE_BLOCK_R, CODE_INLINE_R, COMPOSITE_ID_SEPARATOR, CONSECUTIVE_NEWLINE_R, CR_NEWLINE_R, CUSTOM_COMPONENT_R, CachedIntl, CachedIntl as Intl, DEFAULT_VARIANT_ID, DO_NOT_PROCESS_HTML_ELEMENTS, DURATION_DELAY_TRIGGER, FOOTNOTE_R, FOOTNOTE_REFERENCE_R, FORMFEED_R, FRONT_MATTER_R, GFM_TASK_R, HEADING_ATX_COMPLIANT_R, HEADING_R, HEADING_SETEXT_R, HTML_BLOCK_ELEMENT_R, HTML_CHAR_CODE_R, HTML_COMMENT_R, HTML_CUSTOM_ATTR_R, HTML_LEFT_TRIM_AMOUNT_R, HTML_SELF_CLOSING_ELEMENT_R, HTML_TAGS, INLINE_SKIP_R, INTERPOLATION_R, LINK_AUTOLINK_BARE_URL_R, LINK_AUTOLINK_R, LIST_LOOKBEHIND_R, LOOKAHEAD, LocaleStorage, LocaleStorageClient, LocaleStorageServer, NAMED_CODES_TO_UNICODE, NP_TABLE_R, ORDERED, ORDERED_LIST_BULLET, ORDERED_LIST_ITEM_PREFIX, ORDERED_LIST_ITEM_PREFIX_R, ORDERED_LIST_ITEM_R, ORDERED_LIST_R, PARAGRAPH_R, Priority, QUALIFIER_DYNAMIC_TYPES_KEY, QUALIFIER_ORDER, REFERENCE_IMAGE_OR_LINK, REFERENCE_IMAGE_R, REFERENCE_LINK_R, RuleType, SHORTCODE_R, SHOULD_RENDER_AS_BLOCK_R, TABLE_CENTER_ALIGN, TABLE_LEFT_ALIGN, TABLE_RIGHT_ALIGN, TABLE_TRIM_PIPES, TAB_R, TEXT_BOLD_R, TEXT_EMPHASIZED_R, TEXT_ESCAPED_R, TEXT_MARKED_R, TEXT_PLAIN_R, TEXT_STRIKETHROUGHED_R, TRIM_STARTING_NEWLINES, UNESCAPE_R, UNORDERED, UNORDERED_LIST_BULLET, UNORDERED_LIST_ITEM_PREFIX, UNORDERED_LIST_ITEM_PREFIX_R, UNORDERED_LIST_ITEM_R, UNORDERED_LIST_R, VOID_HTML_ELEMENTS, allowInline, anyScopeRegex, attributeValueToNodePropValue, bindIntl, blockRegex, buildMaskPlugin, captureNothing, checkIsURLAbsolute, checkMissingLocalesPlugin, compact, comparePaths, compile, compileWithOptions, condition as cond, conditionPlugin, createCompiler, createRenderer, currency, cx, date, deepTransformNode, editDictionaryByKeyPath, enumeration as enu, enumerationPlugin, fallbackPlugin, filePlugin, filterMissingTranslationsOnlyPlugin, filterTranslationsOnlyPlugin, findMatchingCondition, formatProxyEnabledMessage, gender, genderPlugin, generateListItemPrefix, generateListItemPrefixRegex, generateListItemRegex, generateListRegex, generateSitemap, generateSitemapUrl, get, getBasePlugins, getBrowserLocale, getCachedIntl, getCanonicalPath, getCondition, getContent, getContentNodeByKeyPath, getCookie, getDefaultNode, getDictionary, getDictionaryCompositeIds, getDictionaryQualifierIds, getDictionaryQualifierTypes, getDictionarySelectorCacheKey, getDomainHostname, getDomainOrigin, getEditedContent, getEditedDictionary, getEmptyNode, getEnumeration, getFilterMissingTranslationsContent, getFilterMissingTranslationsDictionary, getFilterTranslationsOnlyContent, getFilterTranslationsOnlyDictionary, getFilteredLocalesContent, getFilteredLocalesDictionary, getGender, getHTML, getHTMLTextDir, getInsertion, getInsertionValues, getInternalPath, getInterpolableContent, getIntlayer, getLocale, getLocaleFromDomain, getLocaleFromPath, getLocaleFromStorage, getLocaleFromStorageClient, getLocaleFromStorageServer, getLocaleLang, getLocaleName, getLocalizedContent, getLocalizedPath, getLocalizedUrl, getMarkdownMetadata, getMaskContent, getMissingLocalesContent, getMissingLocalesContentFromDictionary, getMultilingualDictionary, getMultilingualUrls, getNestedDictionaryGraph, getNestedDictionaryKeys, getNestedDictionaryReferences, getNesting, getNodeChildren, getNodeType, getPathWithoutLocale, getPerLocaleDictionary, getPlural, getPrefix, getReplacedValuesContent, getRewritePath, getRewriteRules, getSelect, getSplittedContent, getSplittedDictionaryContent, getTranslation, getVariantIds, html, i18nextToIntlayerFormatter, icuToIntlayerFormatter, inlineRegex, insertion as insert, insertContentInDictionary, insertionPlugin, interpolateMessage, intlayerToI18nextFormatter, intlayerToICUFormatter, intlayerToPortableObjectFormatter, intlayerToVueI18nFormatter, isInterpolableWrapperNode, isLocaleExclusiveOnDomain, isProxyStorageLocaleEnabled, isQualifiedDictionaryGroup, isQualifiedDynamicLoaderMap, isSameKeyPath, isValidElement, list, localeDetector, localeFlatMap, localeMap, localeRecord, localeResolver, localeStorageOptions, markdown as md, mergeDictionaries, mergeQualifiedDictionaries, navigatePath, nesting as nest, nestedPlugin, normalizeAttributeKey, normalizeDictionaries, normalizeDictionary, normalizePath, normalizeWhitespace, number, orderDictionaries, parseBlock, parseCaptureInline, parseDictionarySelector, parseInline, parseMarkdown, parseSimpleInline, parseStyleAttribute, parseTableAlign, parseTableAlignCapture, parseTableCells, parseTableRow, parseTaggedMessage, parseYaml, parserFor, percentage, plural, pluralPlugin, portableObjectToIntlayerFormatter, presets, qualifies, rebuildInterpolableContent, reconstructQualifiedEntry, relativeTime, removeContentNodeByKeyPath, renameContentNodeByKeyPath, renderFor, renderMarkdownAst, renderNothing, resolveDictionaryArgument, resolveEffectiveVariantId, resolveMessage, resolveMessageNode, resolveProviderVariant, resolveProxyMode, resolveQualifiedDictionary, resolveQualifiedDynamicContent, resolveQualifiedDynamicContentAsync, sanitizer, select, selectPlugin, serializeVariant, serializeVariantChain, setLocaleInStorage, setLocaleInStorageClient, setLocaleInStorageServer, simpleInlineRegex, slugify, some, splitInsertionTemplate, startsWith, stringifyYaml, translation as t, transformInterpolableNode, translationPlugin, trimEnd, trimLeadingWhitespaceOutsideFences, unescapeString, units, unquote, updateNodeChildren, validateHTML, validateMarkdown, validatePrefix, vueI18nToIntlayerFormatter };
110
+ export { ATTRIBUTES_TO_SANITIZE, ATTRIBUTE_TO_NODE_PROP_MAP, ATTR_EXTRACTOR_R, BLOCKQUOTE_ALERT_R, BLOCKQUOTE_R, BLOCKQUOTE_TRIM_LEFT_MULTILINE_R, BLOCK_END_R, BREAK_LINE_R, BREAK_THEMATIC_R, CAPTURE_LETTER_AFTER_HYPHEN, CODE_BLOCK_FENCED_R, CODE_BLOCK_R, CODE_INLINE_R, COMPOSITE_ID_SEPARATOR, CONSECUTIVE_NEWLINE_R, CR_NEWLINE_R, CUSTOM_COMPONENT_R, CachedIntl, CachedIntl as Intl, DEFAULT_VARIANT_ID, DO_NOT_PROCESS_HTML_ELEMENTS, DURATION_DELAY_TRIGGER, FENCE_DELIMITER_R, FOOTNOTE_R, FOOTNOTE_REFERENCE_R, FORMFEED_R, FRONT_MATTER_R, GFM_TASK_R, HEADING_ATX_COMPLIANT_R, HEADING_R, HEADING_SETEXT_R, HTML_BLOCK_ELEMENT_R, HTML_CHAR_CODE_R, HTML_COMMENT_R, HTML_CUSTOM_ATTR_R, HTML_LEFT_TRIM_AMOUNT_R, HTML_SELF_CLOSING_ELEMENT_R, HTML_TAGS, INLINE_SKIP_R, INTERPOLATION_R, LINK_AUTOLINK_BARE_URL_R, LINK_AUTOLINK_R, LIST_LOOKBEHIND_R, LOOKAHEAD, LocaleStorage, LocaleStorageClient, LocaleStorageServer, NAMED_CODES_TO_UNICODE, NP_TABLE_R, ORDERED, ORDERED_LIST_BULLET, ORDERED_LIST_ITEM_PREFIX, ORDERED_LIST_ITEM_PREFIX_R, ORDERED_LIST_ITEM_R, ORDERED_LIST_R, PARAGRAPH_R, PRELOADED_DYNAMIC_KEY, Priority, QUALIFIER_DYNAMIC_TYPES_KEY, QUALIFIER_ORDER, REFERENCE_IMAGE_OR_LINK, REFERENCE_IMAGE_R, REFERENCE_LINK_R, RuleType, SHORTCODE_R, SHOULD_RENDER_AS_BLOCK_R, TABLE_CENTER_ALIGN, TABLE_LEFT_ALIGN, TABLE_RIGHT_ALIGN, TABLE_TRIM_PIPES, TAB_R, TEXT_BOLD_R, TEXT_EMPHASIZED_R, TEXT_ESCAPED_R, TEXT_MARKED_R, TEXT_PLAIN_R, TEXT_STRIKETHROUGHED_R, TRIM_STARTING_NEWLINES, UNESCAPE_R, UNORDERED, UNORDERED_LIST_BULLET, UNORDERED_LIST_ITEM_PREFIX, UNORDERED_LIST_ITEM_PREFIX_R, UNORDERED_LIST_ITEM_R, UNORDERED_LIST_R, VOID_HTML_ELEMENTS, allowInline, anyScopeRegex, attributeValueToNodePropValue, bindIntl, blockRegex, buildMaskPlugin, captureNothing, checkIsURLAbsolute, checkMissingLocalesPlugin, compact, comparePaths, compile, compileWithOptions, condition as cond, conditionPlugin, createCompiler, createRenderer, currency, cx, date, deepTransformNode, editDictionaryByKeyPath, enumeration as enu, enumerationPlugin, fallbackPlugin, filePlugin, filterMissingTranslationsOnlyPlugin, filterTranslationsOnlyPlugin, findMatchingCondition, formatProxyEnabledMessage, gender, genderPlugin, generateListItemPrefix, generateListItemPrefixRegex, generateListItemRegex, generateListRegex, generateSitemap, generateSitemapUrl, get, getBasePlugins, getBrowserLocale, getCachedIntl, getCanonicalPath, getCondition, getContent, getContentNodeByKeyPath, getCookie, getDefaultNode, getDictionary, getDictionaryCompositeIds, getDictionaryQualifierIds, getDictionaryQualifierTypes, getDictionarySelectorCacheKey, getDomainHostname, getDomainOrigin, getEditedContent, getEditedDictionary, getEmptyNode, getEnumeration, getFilterMissingTranslationsContent, getFilterMissingTranslationsDictionary, getFilterTranslationsOnlyContent, getFilterTranslationsOnlyDictionary, getFilteredLocalesContent, getFilteredLocalesDictionary, getGender, getHTML, getHTMLTextDir, getInsertion, getInsertionValues, getInternalPath, getInterpolableContent, getIntlayer, getLocale, getLocaleFromDomain, getLocaleFromPath, getLocaleFromStorage, getLocaleFromStorageClient, getLocaleFromStorageServer, getLocaleLang, getLocaleName, getLocalizedContent, getLocalizedPath, getLocalizedUrl, getMarkdownMetadata, getMaskContent, getMissingLocalesContent, getMissingLocalesContentFromDictionary, getMultilingualDictionary, getMultilingualUrls, getNestedDictionaryGraph, getNestedDictionaryKeys, getNestedDictionaryReferences, getNesting, getNodeChildren, getNodeType, getPathWithoutLocale, getPerLocaleDictionary, getPlural, getPrefix, getPreloadLocale, getPreloadedDictionary, getReplacedValuesContent, getRewritePath, getRewriteRules, getSelect, getSplittedContent, getSplittedDictionaryContent, getTranslation, getVariantIds, html, i18nextToIntlayerFormatter, icuToIntlayerFormatter, inlineRegex, insertion as insert, insertContentInDictionary, insertionPlugin, interpolateMessage, intlayerToI18nextFormatter, intlayerToICUFormatter, intlayerToPortableObjectFormatter, intlayerToVueI18nFormatter, isDeclaredLocale, isInterpolableWrapperNode, isLocaleExclusiveOnDomain, isProxyStorageLocaleEnabled, isQualifiedDictionaryGroup, isQualifiedDynamicLoaderMap, isSameKeyPath, isValidElement, list, localeDetector, localeFlatMap, localeMap, localeRecord, localeResolver, localeStorageOptions, markdown as md, mergeDictionaries, mergeQualifiedDictionaries, navigatePath, nesting as nest, nestedPlugin, normalizeAttributeKey, normalizeDictionaries, normalizeDictionary, normalizePath, normalizeWhitespace, number, orderDictionaries, parseBlock, parseCaptureInline, parseDictionarySelector, parseInline, parseMarkdown, parseSimpleInline, parseStyleAttribute, parseTableAlign, parseTableAlignCapture, parseTableCells, parseTableRow, parseTaggedMessage, parseYaml, parserFor, percentage, plural, pluralPlugin, portableObjectToIntlayerFormatter, presets, qualifies, rebuildInterpolableContent, reconstructQualifiedEntry, relativeTime, removeContentNodeByKeyPath, renameContentNodeByKeyPath, renderFor, renderMarkdownAst, renderNothing, resolveBrowserLocale, resolveDictionaryArgument, resolveEffectiveVariantId, resolveLocalizedPath, resolveMessage, resolveMessageNode, resolveProviderVariant, resolveProxyMode, resolveQualifiedDictionary, resolveQualifiedDynamicContent, resolveQualifiedDynamicContentAsync, sanitizer, select, selectPlugin, serializeVariant, serializeVariantChain, setLocaleInStorage, setLocaleInStorageClient, setLocaleInStorageServer, simpleInlineRegex, slugify, some, splitInsertionTemplate, startsWith, stringifyYaml, translation as t, transformInterpolableNode, translationPlugin, trimEnd, trimLeadingWhitespaceOutsideFences, unescapeString, units, unquote, updateNodeChildren, validateHTML, validateMarkdown, validatePrefix, vueI18nToIntlayerFormatter };
@@ -34,7 +34,7 @@ import { getPathWithoutLocale } from "./getPathWithoutLocale.mjs";
34
34
  */
35
35
  const normalizePath = (inputUrl, locales) => {
36
36
  const withoutLocale = getPathWithoutLocale(inputUrl || "/", locales);
37
- const { pathname } = checkIsURLAbsolute(withoutLocale) ? new URL(withoutLocale) : new URL(withoutLocale, "http://example.com");
37
+ const { pathname } = checkIsURLAbsolute(withoutLocale) ? new URL(withoutLocale) : new URL(withoutLocale, "http://e.com");
38
38
  if (pathname.length > 1 && pathname.endsWith("/")) return pathname.slice(0, -1);
39
39
  return pathname || "/";
40
40
  };
@@ -1 +1 @@
1
- {"version":3,"file":"comparePaths.mjs","names":[],"sources":["../../../src/localization/comparePaths.ts"],"sourcesContent":["import type {\n DeclaredLocales,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { checkIsURLAbsolute } from '../utils/checkIsURLAbsolute';\nimport { getPathWithoutLocale } from './getPathWithoutLocale';\n\n/**\n * Normalizes a URL or pathname into a canonical, locale-agnostic pathname so\n * that two paths pointing to the same page compare equal — useful for\n * highlighting the active navigation link.\n *\n * The normalization:\n * - strips the locale segment (reusing {@link getPathWithoutLocale})\n * - strips the protocol/host so absolute and relative inputs compare equally\n * - drops the query string and hash\n * - ensures a single leading slash\n * - removes any trailing slash (except for the root path)\n * - falls back to `'/'` for empty values\n *\n * Example:\n *\n * ```ts\n * normalizePath('/ru/path') // '/path'\n * normalizePath('/ru/path/') // '/path'\n * normalizePath('ru/path') // '/path'\n * normalizePath('/ru/') // '/'\n * normalizePath('/ru') // '/'\n * normalizePath('') // '/'\n * normalizePath('https://example.com/ru/path') // '/path'\n * ```\n *\n * @param inputUrl - The URL string or pathname to normalize.\n * @param locales - Optional array of supported locales. Defaults to the\n * configured locales.\n * @returns The normalized, locale-agnostic pathname.\n */\nexport const normalizePath = <const L extends LocalesValues = DeclaredLocales>(\n inputUrl: string,\n locales?: L[]\n): string => {\n const withoutLocale = getPathWithoutLocale(inputUrl || '/', locales);\n\n // Reuse the URL API to isolate the pathname from any host/query/hash.\n const url = checkIsURLAbsolute(withoutLocale)\n ? new URL(withoutLocale)\n : new URL(withoutLocale, 'http://example.com');\n\n const { pathname } = url;\n\n // Remove a trailing slash, keeping the root path intact.\n if (pathname.length > 1 && pathname.endsWith('/')) {\n return pathname.slice(0, -1);\n }\n\n return pathname || '/';\n};\n\n/**\n * Compares two URLs or pathnames for equality, ignoring locale segment,\n * protocol/host, query string, hash and trailing slashes.\n *\n * Both inputs are normalized through {@link normalizePath} before comparison,\n * making this ideal for matching the current pathname against a navigation\n * link's `href`.\n *\n * Example:\n *\n * ```ts\n * comparePaths('/ru/path', '/path') // true\n * comparePaths('/ru/path/', '/path') // true\n * comparePaths('/ru/path', '/path/') // true\n * comparePaths('/ru/', '/') // true\n * comparePaths('/ru', '/') // true\n * comparePaths('ru/path', '/path') // true\n * comparePaths('', '/') // true\n * comparePaths('/ru', '') // true\n * comparePaths('/ru/path', '/other') // false\n * ```\n *\n * @param pathname - The first URL string or pathname to compare.\n * @param href - The second URL string or pathname to compare.\n * @param locales - Optional array of supported locales. Defaults to the\n * configured locales.\n * @returns `true` when both inputs resolve to the same locale-agnostic path.\n */\nexport const comparePaths = <const L extends LocalesValues = DeclaredLocales>(\n pathname: string,\n href: string,\n locales?: L[]\n): boolean => normalizePath(pathname, locales) === normalizePath(href, locales);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,MAAa,iBACX,UACA,YACW;CACX,MAAM,gBAAgB,qBAAqB,YAAY,KAAK,OAAO;CAOnE,MAAM,EAAE,aAJI,mBAAmB,aAAa,IACxC,IAAI,IAAI,aAAa,IACrB,IAAI,IAAI,eAAe,oBAAoB;CAK/C,IAAI,SAAS,SAAS,KAAK,SAAS,SAAS,GAAG,GAC9C,OAAO,SAAS,MAAM,GAAG,EAAE;CAG7B,OAAO,YAAY;AACrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,gBACX,UACA,MACA,YACY,cAAc,UAAU,OAAO,MAAM,cAAc,MAAM,OAAO"}
1
+ {"version":3,"file":"comparePaths.mjs","names":[],"sources":["../../../src/localization/comparePaths.ts"],"sourcesContent":["import type {\n DeclaredLocales,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { checkIsURLAbsolute } from '../utils/checkIsURLAbsolute';\nimport { getPathWithoutLocale } from './getPathWithoutLocale';\n\n/**\n * Normalizes a URL or pathname into a canonical, locale-agnostic pathname so\n * that two paths pointing to the same page compare equal — useful for\n * highlighting the active navigation link.\n *\n * The normalization:\n * - strips the locale segment (reusing {@link getPathWithoutLocale})\n * - strips the protocol/host so absolute and relative inputs compare equally\n * - drops the query string and hash\n * - ensures a single leading slash\n * - removes any trailing slash (except for the root path)\n * - falls back to `'/'` for empty values\n *\n * Example:\n *\n * ```ts\n * normalizePath('/ru/path') // '/path'\n * normalizePath('/ru/path/') // '/path'\n * normalizePath('ru/path') // '/path'\n * normalizePath('/ru/') // '/'\n * normalizePath('/ru') // '/'\n * normalizePath('') // '/'\n * normalizePath('https://example.com/ru/path') // '/path'\n * ```\n *\n * @param inputUrl - The URL string or pathname to normalize.\n * @param locales - Optional array of supported locales. Defaults to the\n * configured locales.\n * @returns The normalized, locale-agnostic pathname.\n */\nexport const normalizePath = <const L extends LocalesValues = DeclaredLocales>(\n inputUrl: string,\n locales?: L[]\n): string => {\n const withoutLocale = getPathWithoutLocale(inputUrl || '/', locales);\n\n // Reuse the URL API to isolate the pathname from any host/query/hash.\n const url = checkIsURLAbsolute(withoutLocale)\n ? new URL(withoutLocale)\n : new URL(withoutLocale, 'http://e.com');\n\n const { pathname } = url;\n\n // Remove a trailing slash, keeping the root path intact.\n if (pathname.length > 1 && pathname.endsWith('/')) {\n return pathname.slice(0, -1);\n }\n\n return pathname || '/';\n};\n\n/**\n * Compares two URLs or pathnames for equality, ignoring locale segment,\n * protocol/host, query string, hash and trailing slashes.\n *\n * Both inputs are normalized through {@link normalizePath} before comparison,\n * making this ideal for matching the current pathname against a navigation\n * link's `href`.\n *\n * Example:\n *\n * ```ts\n * comparePaths('/ru/path', '/path') // true\n * comparePaths('/ru/path/', '/path') // true\n * comparePaths('/ru/path', '/path/') // true\n * comparePaths('/ru/', '/') // true\n * comparePaths('/ru', '/') // true\n * comparePaths('ru/path', '/path') // true\n * comparePaths('', '/') // true\n * comparePaths('/ru', '') // true\n * comparePaths('/ru/path', '/other') // false\n * ```\n *\n * @param pathname - The first URL string or pathname to compare.\n * @param href - The second URL string or pathname to compare.\n * @param locales - Optional array of supported locales. Defaults to the\n * configured locales.\n * @returns `true` when both inputs resolve to the same locale-agnostic path.\n */\nexport const comparePaths = <const L extends LocalesValues = DeclaredLocales>(\n pathname: string,\n href: string,\n locales?: L[]\n): boolean => normalizePath(pathname, locales) === normalizePath(href, locales);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,MAAa,iBACX,UACA,YACW;CACX,MAAM,gBAAgB,qBAAqB,YAAY,KAAK,OAAO;CAOnE,MAAM,EAAE,aAJI,mBAAmB,aAAa,IACxC,IAAI,IAAI,aAAa,IACrB,IAAI,IAAI,eAAe,cAAc;CAKzC,IAAI,SAAS,SAAS,KAAK,SAAS,SAAS,GAAG,GAC9C,OAAO,SAAS,MAAM,GAAG,EAAE;CAG7B,OAAO,YAAY;AACrB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,gBACX,UACA,MACA,YACY,cAAc,UAAU,OAAO,MAAM,cAAc,MAAM,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"domainUtils.mjs","names":[],"sources":["../../../src/localization/domainUtils.ts"],"sourcesContent":["import type { Locale } from '@intlayer/types/allLocales';\nimport type { RoutingConfig } from '@intlayer/types/config';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\n/**\n * The locale → domain map configured under `routing.domains`\n * (e.g. `{ en: 'intlayer.org', zh: 'intlayer.cn' }`).\n */\nexport type LocaleDomainMap = RoutingConfig['domains'];\n\n/**\n * Strips the protocol from a domain string and returns only the hostname.\n *\n * Example:\n *\n * ```ts\n * getDomainHostname('https://intlayer.cn') // 'intlayer.cn'\n * getDomainHostname('intlayer.cn') // 'intlayer.cn'\n * ```\n *\n * @param domain - A domain value from `routing.domains`, with or without protocol.\n * @returns The bare hostname.\n */\nexport const getDomainHostname = (domain: string): string => {\n try {\n return /^https?:\\/\\//.test(domain) ? new URL(domain).hostname : domain;\n } catch {\n return domain;\n }\n};\n\n/**\n * Returns the absolute origin for a domain value, prepending `https://`\n * when no protocol is present.\n *\n * Example:\n *\n * ```ts\n * getDomainOrigin('intlayer.cn') // 'https://intlayer.cn'\n * getDomainOrigin('http://intlayer.cn') // 'http://intlayer.cn'\n * ```\n *\n * @param domain - A domain value from `routing.domains`, with or without protocol.\n * @returns The origin usable as a URL base.\n */\nexport const getDomainOrigin = (domain: string): string =>\n /^https?:\\/\\//.test(domain) ? domain : `https://${domain}`;\n\n/**\n * Returns the locale exclusively mapped to a given hostname via `routing.domains`,\n * or `undefined` when zero or more than one locale share that hostname.\n *\n * Example: with `domains = { zh: 'intlayer.cn', en: 'intlayer.org', fr: 'intlayer.org' }`\n *\n * ```ts\n * getLocaleFromDomain('intlayer.cn', domains) // 'zh'\n * getLocaleFromDomain('intlayer.org', domains) // undefined (en and fr share it)\n * getLocaleFromDomain('example.com', domains) // undefined (not mapped)\n * ```\n *\n * @param hostname - The bare hostname of the incoming request.\n * @param domains - The configured locale → domain map.\n * @returns The exclusively mapped locale, or `undefined`.\n */\nexport const getLocaleFromDomain = (\n hostname: string,\n domains: LocaleDomainMap\n): Locale | undefined => {\n if (!domains) return undefined;\n\n const matchingLocales = Object.entries(domains).filter(\n ([, domain]) =>\n typeof domain === 'string' && getDomainHostname(domain) === hostname\n );\n\n return matchingLocales.length === 1\n ? (matchingLocales[0]?.[0] as Locale)\n : undefined;\n};\n\n/**\n * Checks whether a locale is the only locale mapped to its configured domain.\n * When true, the domain alone identifies the locale and no URL prefix is needed.\n * Hostnames are compared after protocol stripping, so\n * `{ en: 'https://intlayer.org', fr: 'intlayer.org' }` counts as a shared domain.\n *\n * @param locale - The locale to check.\n * @param domains - The configured locale → domain map.\n * @returns `true` when the locale has a domain shared with no other locale.\n */\nexport const isLocaleExclusiveOnDomain = (\n locale: LocalesValues,\n domains: LocaleDomainMap\n): boolean => {\n const localeDomain = domains?.[locale];\n\n if (!localeDomain) return false;\n\n const localeHostname = getDomainHostname(localeDomain);\n\n const localesOnSameDomain = Object.values(domains).filter(\n (domain) =>\n typeof domain === 'string' && getDomainHostname(domain) === localeHostname\n ).length;\n\n return localesOnSameDomain === 1;\n};\n"],"mappings":";;;;;;;;;;;;;;AAuBA,MAAa,qBAAqB,WAA2B;CAC3D,IAAI;EACF,OAAO,eAAe,KAAK,MAAM,IAAI,IAAI,IAAI,MAAM,CAAC,CAAC,WAAW;CAClE,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;;;;;;;;AAgBA,MAAa,mBAAmB,WAC9B,eAAe,KAAK,MAAM,IAAI,SAAS,WAAW;;;;;;;;;;;;;;;;;AAkBpD,MAAa,uBACX,UACA,YACuB;CACvB,IAAI,CAAC,SAAS,OAAO;CAErB,MAAM,kBAAkB,OAAO,QAAQ,OAAO,CAAC,CAAC,QAC7C,GAAG,YACF,OAAO,WAAW,YAAY,kBAAkB,MAAM,MAAM,QAChE;CAEA,OAAO,gBAAgB,WAAW,IAC7B,gBAAgB,EAAE,GAAG,KACtB;AACN;;;;;;;;;;;AAYA,MAAa,6BACX,QACA,YACY;CACZ,MAAM,eAAe,UAAU;CAE/B,IAAI,CAAC,cAAc,OAAO;CAE1B,MAAM,iBAAiB,kBAAkB,YAAY;CAOrD,OAL4B,OAAO,OAAO,OAAO,CAAC,CAAC,QAChD,WACC,OAAO,WAAW,YAAY,kBAAkB,MAAM,MAAM,cAChE,CAAC,CAAC,WAE6B;AACjC"}
1
+ {"version":3,"file":"domainUtils.mjs","names":[],"sources":["../../../src/localization/domainUtils.ts"],"sourcesContent":["import type { RoutingConfig } from '@intlayer/types/config';\nimport type {\n DeclaredLocales,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\n\n/**\n * The locale → domain map configured under `routing.domains`\n * (e.g. `{ en: 'intlayer.org', zh: 'intlayer.cn' }`).\n */\nexport type LocaleDomainMap = RoutingConfig['domains'];\n\n/**\n * Strips the protocol from a domain string and returns only the hostname.\n *\n * Example:\n *\n * ```ts\n * getDomainHostname('https://intlayer.cn') // 'intlayer.cn'\n * getDomainHostname('intlayer.cn') // 'intlayer.cn'\n * ```\n *\n * @param domain - A domain value from `routing.domains`, with or without protocol.\n * @returns The bare hostname.\n */\nexport const getDomainHostname = (domain: string): string => {\n try {\n return /^https?:\\/\\//.test(domain) ? new URL(domain).hostname : domain;\n } catch {\n return domain;\n }\n};\n\n/**\n * Returns the absolute origin for a domain value, prepending `https://`\n * when no protocol is present.\n *\n * Example:\n *\n * ```ts\n * getDomainOrigin('intlayer.cn') // 'https://intlayer.cn'\n * getDomainOrigin('http://intlayer.cn') // 'http://intlayer.cn'\n * ```\n *\n * @param domain - A domain value from `routing.domains`, with or without protocol.\n * @returns The origin usable as a URL base.\n */\nexport const getDomainOrigin = (domain: string): string =>\n /^https?:\\/\\//.test(domain) ? domain : `https://${domain}`;\n\n/**\n * Returns the locale exclusively mapped to a given hostname via `routing.domains`,\n * or `undefined` when zero or more than one locale share that hostname.\n *\n * Example: with `domains = { zh: 'intlayer.cn', en: 'intlayer.org', fr: 'intlayer.org' }`\n *\n * ```ts\n * getLocaleFromDomain('intlayer.cn', domains) // 'zh'\n * getLocaleFromDomain('intlayer.org', domains) // undefined (en and fr share it)\n * getLocaleFromDomain('example.com', domains) // undefined (not mapped)\n * ```\n *\n * @param hostname - The bare hostname of the incoming request.\n * @param domains - The configured locale → domain map.\n * @returns The exclusively mapped locale, or `undefined`.\n */\nexport const getLocaleFromDomain = (\n hostname: string,\n domains: LocaleDomainMap\n): DeclaredLocales | undefined => {\n if (!domains) return undefined;\n\n const matchingLocales = Object.entries(domains).filter(\n ([, domain]) =>\n typeof domain === 'string' && getDomainHostname(domain) === hostname\n );\n\n return matchingLocales.length === 1\n ? (matchingLocales[0]?.[0] as DeclaredLocales)\n : undefined;\n};\n\n/**\n * Checks whether a locale is the only locale mapped to its configured domain.\n * When true, the domain alone identifies the locale and no URL prefix is needed.\n * Hostnames are compared after protocol stripping, so\n * `{ en: 'https://intlayer.org', fr: 'intlayer.org' }` counts as a shared domain.\n *\n * @param locale - The locale to check.\n * @param domains - The configured locale → domain map.\n * @returns `true` when the locale has a domain shared with no other locale.\n */\nexport const isLocaleExclusiveOnDomain = (\n locale: LocalesValues,\n domains: LocaleDomainMap\n): boolean => {\n const localeDomain = domains?.[locale];\n\n if (!localeDomain) return false;\n\n const localeHostname = getDomainHostname(localeDomain);\n\n const localesOnSameDomain = Object.values(domains).filter(\n (domain) =>\n typeof domain === 'string' && getDomainHostname(domain) === localeHostname\n ).length;\n\n return localesOnSameDomain === 1;\n};\n"],"mappings":";;;;;;;;;;;;;;AAyBA,MAAa,qBAAqB,WAA2B;CAC3D,IAAI;EACF,OAAO,eAAe,KAAK,MAAM,IAAI,IAAI,IAAI,MAAM,CAAC,CAAC,WAAW;CAClE,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;;;;;;;;AAgBA,MAAa,mBAAmB,WAC9B,eAAe,KAAK,MAAM,IAAI,SAAS,WAAW;;;;;;;;;;;;;;;;;AAkBpD,MAAa,uBACX,UACA,YACgC;CAChC,IAAI,CAAC,SAAS,OAAO;CAErB,MAAM,kBAAkB,OAAO,QAAQ,OAAO,CAAC,CAAC,QAC7C,GAAG,YACF,OAAO,WAAW,YAAY,kBAAkB,MAAM,MAAM,QAChE;CAEA,OAAO,gBAAgB,WAAW,IAC7B,gBAAgB,EAAE,GAAG,KACtB;AACN;;;;;;;;;;;AAYA,MAAa,6BACX,QACA,YACY;CACZ,MAAM,eAAe,UAAU;CAE/B,IAAI,CAAC,cAAc,OAAO;CAE1B,MAAM,iBAAiB,kBAAkB,YAAY;CAOrD,OAL4B,OAAO,OAAO,OAAO,CAAC,CAAC,QAChD,WACC,OAAO,WAAW,YAAY,kBAAkB,MAAM,MAAM,cAChE,CAAC,CAAC,WAE6B;AACjC"}
@@ -1,4 +1,14 @@
1
1
  //#region src/localization/getHTMLTextDir.ts
2
+ const rtlScripts = [
3
+ "Arab",
4
+ "Hebr",
5
+ "Thaa",
6
+ "Syrc",
7
+ "Mand",
8
+ "Adlm",
9
+ "Rohg",
10
+ "Nkoo"
11
+ ];
2
12
  /**
3
13
  * Returns the text direction of the given locale.
4
14
  *
@@ -19,16 +29,7 @@ const getHTMLTextDir = (locale) => {
19
29
  if ("getTextInfo" in localeInfo) return localeInfo.getTextInfo().direction;
20
30
  if ("textInfo" in localeInfo) return localeInfo.textInfo.direction;
21
31
  const maximized = localeInfo.maximize();
22
- return [
23
- "Arab",
24
- "Hebr",
25
- "Thaa",
26
- "Syrc",
27
- "Mand",
28
- "Adlm",
29
- "Rohg",
30
- "Nkoo"
31
- ].includes(maximized.script ?? "") ? "rtl" : "ltr";
32
+ return rtlScripts.includes(maximized.script ?? "") ? "rtl" : "ltr";
32
33
  } catch {
33
34
  return "ltr";
34
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"getHTMLTextDir.mjs","names":[],"sources":["../../../src/localization/getHTMLTextDir.ts"],"sourcesContent":["import type { LocalesValues } from '@intlayer/types/module_augmentation';\n\ntype Dir = 'ltr' | 'rtl' | 'auto';\n\n/**\n * Returns the text direction of the given locale.\n *\n * Example:\n *\n * getHTMLTextDir('en-US') // 'ltr'\n * getHTMLTextDir('en') // 'ltr'\n * getHTMLTextDir('fr-CA') // 'ltr'\n * getHTMLTextDir('fr') // 'ltr'\n *\n * @param locale The locale to get the text direction for.\n * @returns The text direction of the given locale.\n */\nexport const getHTMLTextDir = (locale?: LocalesValues): Dir => {\n if (!locale) return 'ltr';\n\n try {\n const localeInfo = new Intl.Locale(locale);\n\n // Check for the 'textInfo' property (part of the Intl Enumeration API)\n // Most modern browsers support 'direction' via 'getTextInfo()' or 'textInfo'\n if ('getTextInfo' in localeInfo) {\n return (localeInfo as any).getTextInfo().direction as Dir;\n }\n\n // Fallback for environments supporting 'textInfo' property\n if ('textInfo' in localeInfo) {\n return (localeInfo as any).textInfo.direction as Dir;\n }\n\n // Manual fallback for older environments using script detection\n const maximized = localeInfo.maximize();\n const rtlScripts = [\n 'Arab',\n 'Hebr',\n 'Thaa',\n 'Syrc',\n 'Mand',\n 'Adlm',\n 'Rohg',\n 'Nkoo',\n ];\n\n return rtlScripts.includes(maximized.script ?? '') ? 'rtl' : 'ltr';\n } catch {\n return 'ltr';\n }\n};\n"],"mappings":";;;;;;;;;;;;;;AAiBA,MAAa,kBAAkB,WAAgC;CAC7D,IAAI,CAAC,QAAQ,OAAO;CAEpB,IAAI;EACF,MAAM,aAAa,IAAI,KAAK,OAAO,MAAM;EAIzC,IAAI,iBAAiB,YACnB,OAAQ,WAAmB,YAAY,CAAC,CAAC;EAI3C,IAAI,cAAc,YAChB,OAAQ,WAAmB,SAAS;EAItC,MAAM,YAAY,WAAW,SAAS;EAYtC,OAAO;GAVL;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EAGc,CAAC,CAAC,SAAS,UAAU,UAAU,EAAE,IAAI,QAAQ;CAC/D,QAAQ;EACN,OAAO;CACT;AACF"}
1
+ {"version":3,"file":"getHTMLTextDir.mjs","names":[],"sources":["../../../src/localization/getHTMLTextDir.ts"],"sourcesContent":["import type { LocalesValues } from '@intlayer/types/module_augmentation';\n\ntype Dir = 'ltr' | 'rtl' | 'auto';\n\nconst rtlScripts = [\n 'Arab',\n 'Hebr',\n 'Thaa',\n 'Syrc',\n 'Mand',\n 'Adlm',\n 'Rohg',\n 'Nkoo',\n];\n\n/**\n * Returns the text direction of the given locale.\n *\n * Example:\n *\n * getHTMLTextDir('en-US') // 'ltr'\n * getHTMLTextDir('en') // 'ltr'\n * getHTMLTextDir('fr-CA') // 'ltr'\n * getHTMLTextDir('fr') // 'ltr'\n *\n * @param locale The locale to get the text direction for.\n * @returns The text direction of the given locale.\n */\nexport const getHTMLTextDir = (locale?: LocalesValues): Dir => {\n if (!locale) return 'ltr';\n\n try {\n const localeInfo = new Intl.Locale(locale);\n\n // Check for the 'textInfo' property (part of the Intl Enumeration API)\n // Most modern browsers support 'direction' via 'getTextInfo()' or 'textInfo'\n if ('getTextInfo' in localeInfo) {\n return (localeInfo as any).getTextInfo().direction as Dir;\n }\n\n // Fallback for environments supporting 'textInfo' property\n if ('textInfo' in localeInfo) {\n return (localeInfo as any).textInfo.direction as Dir;\n }\n\n // Manual fallback for older environments using script detection\n const maximized = (localeInfo as Intl.Locale).maximize();\n\n return rtlScripts.includes(maximized.script ?? '') ? 'rtl' : 'ltr';\n } catch {\n return 'ltr';\n }\n};\n"],"mappings":";AAIA,MAAM,aAAa;CACjB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;;;;;;;;AAeA,MAAa,kBAAkB,WAAgC;CAC7D,IAAI,CAAC,QAAQ,OAAO;CAEpB,IAAI;EACF,MAAM,aAAa,IAAI,KAAK,OAAO,MAAM;EAIzC,IAAI,iBAAiB,YACnB,OAAQ,WAAmB,YAAY,CAAC,CAAC;EAI3C,IAAI,cAAc,YAChB,OAAQ,WAAmB,SAAS;EAItC,MAAM,YAAa,WAA2B,SAAS;EAEvD,OAAO,WAAW,SAAS,UAAU,UAAU,EAAE,IAAI,QAAQ;CAC/D,QAAQ;EACN,OAAO;CACT;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"getLocale.mjs","names":[],"sources":["../../../src/localization/getLocale.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\nimport { DEFAULT_LOCALE, LOCALES } from '@intlayer/config/defaultValues';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport { getLocaleFromStorageServer } from '../utils/localeStorage';\nimport { getPreferredLanguages } from './localeDetector';\nimport { localeResolver } from './localeResolver';\n\nexport type RequestContext = {\n getHeader?: (name: string) => string | null | undefined;\n getCookie?: (name: string) => string | null | undefined;\n};\n\nexport const getLocale = async (ctx: RequestContext = {}): Promise<Locale> => {\n const defaultLocale = internationalization?.defaultLocale ?? DEFAULT_LOCALE;\n const availableLocales = internationalization?.locales ?? LOCALES;\n\n // Try locale from storage (cookie or header)\n const storedLocale = getLocaleFromStorageServer({\n getCookie: ctx.getCookie,\n getHeader: ctx.getHeader,\n });\n\n if (storedLocale) {\n return storedLocale;\n }\n\n // Fallback to Accept-Language negotiation\n const acceptLanguageHeader = ctx.getHeader?.('accept-language');\n\n if (!acceptLanguageHeader) {\n return defaultLocale;\n }\n\n const preferredLocaleStrings = getPreferredLanguages(\n acceptLanguageHeader,\n availableLocales\n );\n\n const userFallbackLocale = localeResolver(\n preferredLocaleStrings,\n availableLocales,\n defaultLocale\n );\n\n if (userFallbackLocale) {\n return userFallbackLocale;\n }\n\n // Default locale\n return defaultLocale;\n};\n"],"mappings":";;;;;;;AAYA,MAAa,YAAY,OAAO,MAAsB,CAAC,MAAuB;CAC5E,MAAM,gBAAgB,sBAAsB,iBAAiB;CAC7D,MAAM,mBAAmB,sBAAsB,WAAW;CAG1D,MAAM,eAAe,2BAA2B;EAC9C,WAAW,IAAI;EACf,WAAW,IAAI;CACjB,CAAC;CAED,IAAI,cACF,OAAO;CAIT,MAAM,uBAAuB,IAAI,YAAY,iBAAiB;CAE9D,IAAI,CAAC,sBACH,OAAO;CAGT,MAAM,yBAAyB,sBAC7B,sBACA,gBACF;CAEA,MAAM,qBAAqB,eACzB,wBACA,kBACA,aACF;CAEA,IAAI,oBACF,OAAO;CAIT,OAAO;AACT"}
1
+ {"version":3,"file":"getLocale.mjs","names":[],"sources":["../../../src/localization/getLocale.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\nimport { DEFAULT_LOCALE, LOCALES } from '@intlayer/config/defaultValues';\nimport type { DeclaredLocales } from '@intlayer/types/module_augmentation';\nimport { getLocaleFromStorageServer } from '../utils/localeStorage';\nimport { getPreferredLanguages } from './localeDetector';\nimport { localeResolver } from './localeResolver';\n\nexport type RequestContext = {\n getHeader?: (name: string) => string | null | undefined;\n getCookie?: (name: string) => string | null | undefined;\n};\n\nexport const getLocale = async (\n ctx: RequestContext = {}\n): Promise<DeclaredLocales> => {\n const defaultLocale = internationalization?.defaultLocale ?? DEFAULT_LOCALE;\n const availableLocales = internationalization?.locales ?? LOCALES;\n\n // Try locale from storage (cookie or header)\n const storedLocale = getLocaleFromStorageServer({\n getCookie: ctx.getCookie,\n getHeader: ctx.getHeader,\n });\n\n if (storedLocale) {\n return storedLocale;\n }\n\n // Fallback to Accept-Language negotiation\n const acceptLanguageHeader = ctx.getHeader?.('accept-language');\n\n if (!acceptLanguageHeader) {\n return defaultLocale;\n }\n\n const preferredLocaleStrings = getPreferredLanguages(\n acceptLanguageHeader,\n availableLocales\n );\n\n const userFallbackLocale = localeResolver(\n preferredLocaleStrings,\n availableLocales,\n defaultLocale\n );\n\n if (userFallbackLocale) {\n return userFallbackLocale;\n }\n\n // Default locale\n return defaultLocale;\n};\n"],"mappings":";;;;;;;AAYA,MAAa,YAAY,OACvB,MAAsB,CAAC,MACM;CAC7B,MAAM,gBAAgB,sBAAsB,iBAAiB;CAC7D,MAAM,mBAAmB,sBAAsB,WAAW;CAG1D,MAAM,eAAe,2BAA2B;EAC9C,WAAW,IAAI;EACf,WAAW,IAAI;CACjB,CAAC;CAED,IAAI,cACF,OAAO;CAIT,MAAM,uBAAuB,IAAI,YAAY,iBAAiB;CAE9D,IAAI,CAAC,sBACH,OAAO;CAGT,MAAM,yBAAyB,sBAC7B,sBACA,gBACF;CAEA,MAAM,qBAAqB,eACzB,wBACA,kBACA,aACF;CAEA,IAAI,oBACF,OAAO;CAIT,OAAO;AACT"}
@@ -1,5 +1,5 @@
1
1
  import { checkIsURLAbsolute } from "../utils/checkIsURLAbsolute.mjs";
2
- import { resolveRoutingConfig } from "./getPrefix.mjs";
2
+ import { isDeclaredLocale, resolveRoutingConfig } from "./getPrefix.mjs";
3
3
 
4
4
  //#region src/localization/getLocaleFromPath.ts
5
5
  /**
@@ -19,15 +19,15 @@ const getLocaleFromPath = (inputUrl = "/", options) => {
19
19
  if (!defaultLocale || !locales) return defaultLocale;
20
20
  const isAbsoluteUrl = checkIsURLAbsolute(inputUrl);
21
21
  const fixedInputUrl = inputUrl?.endsWith("/") && inputUrl.length > 1 ? inputUrl.slice(0, -1) : inputUrl;
22
- const url = isAbsoluteUrl ? new URL(fixedInputUrl) : new URL(fixedInputUrl, "http://example.com");
22
+ const url = isAbsoluteUrl ? new URL(fixedInputUrl) : new URL(fixedInputUrl, "http://e.com");
23
23
  if (mode === "search-params") {
24
24
  const localeParam = url.searchParams.get("locale");
25
- if (localeParam && locales.includes(localeParam)) return localeParam;
25
+ if (isDeclaredLocale(localeParam, locales)) return localeParam;
26
26
  return defaultLocale;
27
27
  }
28
28
  if (mode === "no-prefix") return defaultLocale;
29
29
  const firstSegment = url.pathname.split("/")[1];
30
- if (firstSegment && locales.includes(firstSegment)) return firstSegment;
30
+ if (isDeclaredLocale(firstSegment, locales)) return firstSegment;
31
31
  if (mode === "prefix-no-default") return defaultLocale;
32
32
  };
33
33
 
@@ -1 +1 @@
1
- {"version":3,"file":"getLocaleFromPath.mjs","names":[],"sources":["../../../src/localization/getLocaleFromPath.ts"],"sourcesContent":["import type { Locale } from '@intlayer/types/allLocales';\nimport { checkIsURLAbsolute } from '../utils/checkIsURLAbsolute';\nimport { type RoutingOptions, resolveRoutingConfig } from './getPrefix';\n\n/**\n * Extracts the locale segment from the given URL or pathname based on the routing mode.\n *\n * Mode Behaviors:\n * - 'prefix-no-default': Checks path prefix. If no prefix found, assumes default locale.\n * - 'prefix-all': Checks path prefix.\n * - 'search-params': Checks for 'locale' query parameter.\n * - 'no-prefix': Returns the default locale.\n *\n * @param inputUrl - The complete URL string or pathname to process.\n * @returns The detected locale, default locale (if fallback/implicit), or undefined.\n */\nexport const getLocaleFromPath = (\n inputUrl: string = '/',\n options?: RoutingOptions\n): Locale | undefined => {\n const { defaultLocale, locales, mode } = resolveRoutingConfig(options);\n\n if (!defaultLocale || !locales) {\n return defaultLocale as Locale | undefined;\n }\n\n const isAbsoluteUrl = checkIsURLAbsolute(inputUrl);\n const fixedInputUrl =\n inputUrl?.endsWith('/') && inputUrl.length > 1\n ? inputUrl.slice(0, -1)\n : inputUrl;\n\n const url = isAbsoluteUrl\n ? new URL(fixedInputUrl)\n : new URL(fixedInputUrl, 'http://example.com');\n\n // Handle 'search-params' mode — locale is in query string\n if (mode === 'search-params') {\n const localeParam = url.searchParams.get('locale');\n if (localeParam && locales.includes(localeParam)) {\n return localeParam as Locale;\n }\n return defaultLocale as Locale;\n }\n\n // Handle 'no-prefix' mode — locale is not in the URL\n if (mode === 'no-prefix') {\n return defaultLocale as Locale;\n }\n\n // Handle prefix modes ('prefix-all' | 'prefix-no-default')\n const firstSegment = url.pathname.split('/')[1];\n if (firstSegment && locales.includes(firstSegment)) {\n return firstSegment as Locale;\n }\n\n // In 'prefix-no-default', no prefix implies the default locale\n if (mode === 'prefix-no-default') {\n return defaultLocale as Locale;\n }\n\n return undefined;\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,MAAa,qBACX,WAAmB,KACnB,YACuB;CACvB,MAAM,EAAE,eAAe,SAAS,SAAS,qBAAqB,OAAO;CAErE,IAAI,CAAC,iBAAiB,CAAC,SACrB,OAAO;CAGT,MAAM,gBAAgB,mBAAmB,QAAQ;CACjD,MAAM,gBACJ,UAAU,SAAS,GAAG,KAAK,SAAS,SAAS,IACzC,SAAS,MAAM,GAAG,EAAE,IACpB;CAEN,MAAM,MAAM,gBACR,IAAI,IAAI,aAAa,IACrB,IAAI,IAAI,eAAe,oBAAoB;CAG/C,IAAI,SAAS,iBAAiB;EAC5B,MAAM,cAAc,IAAI,aAAa,IAAI,QAAQ;EACjD,IAAI,eAAe,QAAQ,SAAS,WAAW,GAC7C,OAAO;EAET,OAAO;CACT;CAGA,IAAI,SAAS,aACX,OAAO;CAIT,MAAM,eAAe,IAAI,SAAS,MAAM,GAAG,CAAC,CAAC;CAC7C,IAAI,gBAAgB,QAAQ,SAAS,YAAY,GAC/C,OAAO;CAIT,IAAI,SAAS,qBACX,OAAO;AAIX"}
1
+ {"version":3,"file":"getLocaleFromPath.mjs","names":[],"sources":["../../../src/localization/getLocaleFromPath.ts"],"sourcesContent":["import type { DeclaredLocales } from '@intlayer/types/module_augmentation';\nimport { checkIsURLAbsolute } from '../utils/checkIsURLAbsolute';\nimport {\n isDeclaredLocale,\n type RoutingOptions,\n resolveRoutingConfig,\n} from './getPrefix';\n\n/**\n * Extracts the locale segment from the given URL or pathname based on the routing mode.\n *\n * Mode Behaviors:\n * - 'prefix-no-default': Checks path prefix. If no prefix found, assumes default locale.\n * - 'prefix-all': Checks path prefix.\n * - 'search-params': Checks for 'locale' query parameter.\n * - 'no-prefix': Returns the default locale.\n *\n * @param inputUrl - The complete URL string or pathname to process.\n * @returns The detected locale, default locale (if fallback/implicit), or undefined.\n */\nexport const getLocaleFromPath = (\n inputUrl: string = '/',\n options?: RoutingOptions\n): DeclaredLocales | undefined => {\n const { defaultLocale, locales, mode } = resolveRoutingConfig(options);\n\n if (!defaultLocale || !locales) {\n return defaultLocale as DeclaredLocales | undefined;\n }\n\n const isAbsoluteUrl = checkIsURLAbsolute(inputUrl);\n const fixedInputUrl =\n inputUrl?.endsWith('/') && inputUrl.length > 1\n ? inputUrl.slice(0, -1)\n : inputUrl;\n\n const url = isAbsoluteUrl\n ? new URL(fixedInputUrl)\n : new URL(fixedInputUrl, 'http://e.com');\n\n // Handle 'search-params' mode — locale is in query string\n if (mode === 'search-params') {\n const localeParam = url.searchParams.get('locale');\n if (isDeclaredLocale(localeParam, locales)) {\n return localeParam;\n }\n return defaultLocale as DeclaredLocales;\n }\n\n // Handle 'no-prefix' mode — locale is not in the URL\n if (mode === 'no-prefix') {\n return defaultLocale as DeclaredLocales;\n }\n\n // Handle prefix modes ('prefix-all' | 'prefix-no-default')\n const firstSegment = url.pathname.split('/')[1];\n if (isDeclaredLocale(firstSegment, locales)) {\n return firstSegment;\n }\n\n // In 'prefix-no-default', no prefix implies the default locale\n if (mode === 'prefix-no-default') {\n return defaultLocale as DeclaredLocales;\n }\n\n return undefined;\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAoBA,MAAa,qBACX,WAAmB,KACnB,YACgC;CAChC,MAAM,EAAE,eAAe,SAAS,SAAS,qBAAqB,OAAO;CAErE,IAAI,CAAC,iBAAiB,CAAC,SACrB,OAAO;CAGT,MAAM,gBAAgB,mBAAmB,QAAQ;CACjD,MAAM,gBACJ,UAAU,SAAS,GAAG,KAAK,SAAS,SAAS,IACzC,SAAS,MAAM,GAAG,EAAE,IACpB;CAEN,MAAM,MAAM,gBACR,IAAI,IAAI,aAAa,IACrB,IAAI,IAAI,eAAe,cAAc;CAGzC,IAAI,SAAS,iBAAiB;EAC5B,MAAM,cAAc,IAAI,aAAa,IAAI,QAAQ;EACjD,IAAI,iBAAiB,aAAa,OAAO,GACvC,OAAO;EAET,OAAO;CACT;CAGA,IAAI,SAAS,aACX,OAAO;CAIT,MAAM,eAAe,IAAI,SAAS,MAAM,GAAG,CAAC,CAAC;CAC7C,IAAI,iBAAiB,cAAc,OAAO,GACxC,OAAO;CAIT,IAAI,SAAS,qBACX,OAAO;AAIX"}
@@ -0,0 +1,73 @@
1
+ import { getPathWithoutLocale } from "./getPathWithoutLocale.mjs";
2
+ import { getPrefix, resolveRoutingConfig } from "./getPrefix.mjs";
3
+ import { getCanonicalPath, getRewriteRules, resolveLocalizedPath } from "./rewriteUtils.mjs";
4
+ import { internationalization } from "@intlayer/config/built";
5
+
6
+ //#region src/localization/getLocalizedPath.ts
7
+ /**
8
+ * Localizes an application path for a locale: resolves the `routing.rewrite`
9
+ * rules, then applies the locale representation of the routing mode — a path
10
+ * prefix, a `locale` search parameter, or nothing at all.
11
+ *
12
+ * Any query string and hash of the input are preserved.
13
+ *
14
+ * This is the relative half of {@link getLocalizedUrl}, which builds on it to
15
+ * add an origin and cross-domain routing. For a relative input both return the
16
+ * same value; this one never returns an absolute URL — the origin of an
17
+ * absolute input is dropped and only its path is localized.
18
+ *
19
+ * Use `resolveLocalizedPath` instead to apply the rewrite rules alone, without
20
+ * any locale representation.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * // routing: { mode: 'prefix-no-default', rewrite: { '/about': { fr: '/a-propos' } } }
25
+ * getLocalizedPath('/about', 'fr'); // '/fr/a-propos'
26
+ * getLocalizedPath('/about', 'en'); // '/about' (default locale, no prefix)
27
+ * getLocalizedPath('/', 'fr'); // '/fr'
28
+ * getLocalizedPath('/contact', 'fr'); // '/fr/contact' (no rewrite rule matches)
29
+ * getLocalizedPath('https://intlayer.org/about', 'fr'); // '/fr/a-propos' (origin dropped)
30
+ *
31
+ * // Manual rules, bypassing the configuration
32
+ * getLocalizedPath('/contact', 'fr', {
33
+ * rewrite: { '/contact': { fr: '/contactez-nous' } },
34
+ * });
35
+ * // '/fr/contactez-nous'
36
+ * ```
37
+ *
38
+ * @param canonicalPath - The internal application path (e.g. `/about`, `/product/123`).
39
+ * An absolute URL is accepted; only its path, query and hash are kept.
40
+ * @param locale - The target locale. Defaults to the configured default locale.
41
+ * @param options - Routing overrides (`locales`, `defaultLocale`, `mode`, `rewrite`,
42
+ * `domains`). Each one defaults to the project configuration. `domains` only
43
+ * suppresses the prefix of a locale served from its own domain — the origin
44
+ * itself is never emitted.
45
+ * @returns The localized path for the target locale.
46
+ */
47
+ const getLocalizedPath = (canonicalPath, locale = internationalization?.defaultLocale, options = {}) => {
48
+ const { defaultLocale, mode, locales, rewrite, domains } = resolveRoutingConfig(options);
49
+ const pathWithoutLocale = getPathWithoutLocale(canonicalPath, locales);
50
+ const rewriteRules = getRewriteRules(rewrite, "url");
51
+ const parsedPath = new URL(pathWithoutLocale, "http://e.com");
52
+ const translatedPathname = resolveLocalizedPath(getCanonicalPath(parsedPath.pathname, void 0, rewriteRules), locale, rewriteRules).path;
53
+ if (!(process.env.INTLAYER_ROUTING_MODE && process.env.INTLAYER_ROUTING_MODE !== "no-prefix") && mode === "no-prefix") return `${translatedPathname}${parsedPath.search}${parsedPath.hash}`;
54
+ if (!(process.env.INTLAYER_ROUTING_MODE && process.env.INTLAYER_ROUTING_MODE !== "search-params") && mode === "search-params") {
55
+ const searchParams = new URLSearchParams(parsedPath.search);
56
+ searchParams.set("locale", locale.toString());
57
+ const queryParams = searchParams.toString();
58
+ return `${translatedPathname}${queryParams ? `?${queryParams}` : ""}${parsedPath.hash}`;
59
+ }
60
+ const { prefix } = getPrefix(locale, {
61
+ defaultLocale,
62
+ mode,
63
+ locales,
64
+ domains
65
+ });
66
+ let localizedPath = `/${prefix}${translatedPathname}`.replace(/\/+/g, "/");
67
+ if (localizedPath.length > 1 && localizedPath.endsWith("/")) localizedPath = localizedPath.slice(0, -1);
68
+ return `${localizedPath}${parsedPath.search}${parsedPath.hash}`;
69
+ };
70
+
71
+ //#endregion
72
+ export { getLocalizedPath };
73
+ //# sourceMappingURL=getLocalizedPath.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getLocalizedPath.mjs","names":[],"sources":["../../../src/localization/getLocalizedPath.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type {\n LocalesValues,\n LocalizedPathname,\n ResolvedDefaultLocale,\n} from '@intlayer/types/module_augmentation';\nimport { getPathWithoutLocale } from './getPathWithoutLocale';\nimport {\n getPrefix,\n type RoutingOptions,\n resolveRoutingConfig,\n} from './getPrefix';\nimport {\n getCanonicalPath,\n getRewriteRules,\n resolveLocalizedPath,\n} from './rewriteUtils';\n\n/**\n * Localizes an application path for a locale: resolves the `routing.rewrite`\n * rules, then applies the locale representation of the routing mode — a path\n * prefix, a `locale` search parameter, or nothing at all.\n *\n * Any query string and hash of the input are preserved.\n *\n * This is the relative half of {@link getLocalizedUrl}, which builds on it to\n * add an origin and cross-domain routing. For a relative input both return the\n * same value; this one never returns an absolute URL — the origin of an\n * absolute input is dropped and only its path is localized.\n *\n * Use `resolveLocalizedPath` instead to apply the rewrite rules alone, without\n * any locale representation.\n *\n * @example\n * ```ts\n * // routing: { mode: 'prefix-no-default', rewrite: { '/about': { fr: '/a-propos' } } }\n * getLocalizedPath('/about', 'fr'); // '/fr/a-propos'\n * getLocalizedPath('/about', 'en'); // '/about' (default locale, no prefix)\n * getLocalizedPath('/', 'fr'); // '/fr'\n * getLocalizedPath('/contact', 'fr'); // '/fr/contact' (no rewrite rule matches)\n * getLocalizedPath('https://intlayer.org/about', 'fr'); // '/fr/a-propos' (origin dropped)\n *\n * // Manual rules, bypassing the configuration\n * getLocalizedPath('/contact', 'fr', {\n * rewrite: { '/contact': { fr: '/contactez-nous' } },\n * });\n * // '/fr/contactez-nous'\n * ```\n *\n * @param canonicalPath - The internal application path (e.g. `/about`, `/product/123`).\n * An absolute URL is accepted; only its path, query and hash are kept.\n * @param locale - The target locale. Defaults to the configured default locale.\n * @param options - Routing overrides (`locales`, `defaultLocale`, `mode`, `rewrite`,\n * `domains`). Each one defaults to the project configuration. `domains` only\n * suppresses the prefix of a locale served from its own domain — the origin\n * itself is never emitted.\n * @returns The localized path for the target locale.\n */\nexport const getLocalizedPath = <\n const CanonicalPath extends string,\n const CurrentLocale extends LocalesValues = ResolvedDefaultLocale,\n>(\n canonicalPath: CanonicalPath,\n locale: CurrentLocale = internationalization?.defaultLocale as CurrentLocale,\n options: RoutingOptions = {}\n): LocalizedPathname<CanonicalPath, CurrentLocale> => {\n const { defaultLocale, mode, locales, rewrite, domains } =\n resolveRoutingConfig(options);\n\n const pathWithoutLocale = getPathWithoutLocale(canonicalPath, locales);\n const rewriteRules = getRewriteRules(rewrite, 'url');\n\n // Parsed against a dummy origin to split the path from its query and hash.\n // An absolute input parses on its own, and only its path is kept.\n const parsedPath = new URL(pathWithoutLocale, 'http://e.com');\n\n const translatedPathname = resolveLocalizedPath(\n getCanonicalPath(parsedPath.pathname, undefined, rewriteRules),\n locale as Locale,\n rewriteRules\n ).path;\n\n if (\n !(\n process.env.INTLAYER_ROUTING_MODE &&\n process.env.INTLAYER_ROUTING_MODE !== 'no-prefix'\n ) &&\n mode === 'no-prefix'\n ) {\n return `${translatedPathname}${parsedPath.search}${parsedPath.hash}` as LocalizedPathname<\n CanonicalPath,\n CurrentLocale\n >;\n }\n\n if (\n !(\n process.env.INTLAYER_ROUTING_MODE &&\n process.env.INTLAYER_ROUTING_MODE !== 'search-params'\n ) &&\n mode === 'search-params'\n ) {\n const searchParams = new URLSearchParams(parsedPath.search);\n\n searchParams.set('locale', locale.toString());\n\n const queryParams = searchParams.toString();\n\n return `${translatedPathname}${queryParams ? `?${queryParams}` : ''}${parsedPath.hash}` as LocalizedPathname<\n CanonicalPath,\n CurrentLocale\n >;\n }\n\n const { prefix } = getPrefix(locale, {\n defaultLocale,\n mode,\n locales,\n domains,\n });\n\n let localizedPath = `/${prefix}${translatedPathname}`.replace(/\\/+/g, '/');\n\n if (localizedPath.length > 1 && localizedPath.endsWith('/')) {\n localizedPath = localizedPath.slice(0, -1);\n }\n\n return `${localizedPath}${parsedPath.search}${parsedPath.hash}` as LocalizedPathname<\n CanonicalPath,\n CurrentLocale\n >;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2DA,MAAa,oBAIX,eACA,SAAwB,sBAAsB,eAC9C,UAA0B,CAAC,MACyB;CACpD,MAAM,EAAE,eAAe,MAAM,SAAS,SAAS,YAC7C,qBAAqB,OAAO;CAE9B,MAAM,oBAAoB,qBAAqB,eAAe,OAAO;CACrE,MAAM,eAAe,gBAAgB,SAAS,KAAK;CAInD,MAAM,aAAa,IAAI,IAAI,mBAAmB,cAAc;CAE5D,MAAM,qBAAqB,qBACzB,iBAAiB,WAAW,UAAU,QAAW,YAAY,GAC7D,QACA,YACF,CAAC,CAAC;CAEF,IACE,EACE,QAAQ,IAAI,yBACZ,QAAQ,IAAI,0BAA0B,gBAExC,SAAS,aAET,OAAO,GAAG,qBAAqB,WAAW,SAAS,WAAW;CAMhE,IACE,EACE,QAAQ,IAAI,yBACZ,QAAQ,IAAI,0BAA0B,oBAExC,SAAS,iBACT;EACA,MAAM,eAAe,IAAI,gBAAgB,WAAW,MAAM;EAE1D,aAAa,IAAI,UAAU,OAAO,SAAS,CAAC;EAE5C,MAAM,cAAc,aAAa,SAAS;EAE1C,OAAO,GAAG,qBAAqB,cAAc,IAAI,gBAAgB,KAAK,WAAW;CAInF;CAEA,MAAM,EAAE,WAAW,UAAU,QAAQ;EACnC;EACA;EACA;EACA;CACF,CAAC;CAED,IAAI,gBAAgB,IAAI,SAAS,qBAAqB,QAAQ,QAAQ,GAAG;CAEzE,IAAI,cAAc,SAAS,KAAK,cAAc,SAAS,GAAG,GACxD,gBAAgB,cAAc,MAAM,GAAG,EAAE;CAG3C,OAAO,GAAG,gBAAgB,WAAW,SAAS,WAAW;AAI3D"}
@@ -0,0 +1,83 @@
1
+ import { getLocalizedPath } from "./getLocalizedPath.mjs";
2
+ import { getLocalizedUrl } from "./getLocalizedUrl.mjs";
3
+ import { describe, expectTypeOf, it } from "vitest";
4
+
5
+ //#region src/localization/getLocalizedPath.test-d.ts
6
+ /**
7
+ * `getLocalizedPath` prefixes the rewritten path, so it reports the very type
8
+ * its sibling reports for a relative URL. This package declares neither rewrite
9
+ * rules nor a default locale, so the registry leaves both steps as no-ops.
10
+ */
11
+ describe("getLocalizedPath", () => {
12
+ it("should report the same type as getLocalizedUrl", () => {
13
+ expectTypeOf(getLocalizedPath("/about", "fr")).toEqualTypeOf();
14
+ expectTypeOf(getLocalizedPath("/about", "fr")).toEqualTypeOf(getLocalizedUrl("/about", "fr"));
15
+ });
16
+ it("should keep the literal path when nothing rewrites or prefixes it", () => {
17
+ expectTypeOf(getLocalizedPath("/about", "fr")).toEqualTypeOf();
18
+ });
19
+ it("should drop the origin of an absolute URL", () => {
20
+ expectTypeOf(getLocalizedPath("https://intlayer.org/about", "fr")).toEqualTypeOf();
21
+ });
22
+ it("should widen to string when the path is not a literal", () => {
23
+ expectTypeOf(getLocalizedPath("/about", "fr")).toEqualTypeOf();
24
+ });
25
+ });
26
+ /**
27
+ * The resolution itself, exercised against explicit rules — the same shape the
28
+ * generated module augmentation feeds it as `__RoutingRegistry['rewrite']`.
29
+ */
30
+ describe("LocalizedPath", () => {
31
+ it("should rewrite a static path", () => {
32
+ expectTypeOf().toEqualTypeOf();
33
+ });
34
+ it("should keep a path no rule matches", () => {
35
+ expectTypeOf().toEqualTypeOf();
36
+ });
37
+ it("should substitute a route parameter", () => {
38
+ expectTypeOf().toEqualTypeOf();
39
+ });
40
+ it("should substitute several route parameters", () => {
41
+ expectTypeOf().toEqualTypeOf();
42
+ });
43
+ it("should not match a parameter across a segment boundary", () => {
44
+ expectTypeOf().toEqualTypeOf();
45
+ });
46
+ it("should keep the path when the locale declares no pattern", () => {
47
+ expectTypeOf().toEqualTypeOf();
48
+ });
49
+ it("should distribute over a union of locales", () => {
50
+ expectTypeOf().toEqualTypeOf();
51
+ });
52
+ it("should widen a variadic rule to string, but only for the paths it could match", () => {
53
+ expectTypeOf().toEqualTypeOf();
54
+ expectTypeOf().toEqualTypeOf();
55
+ });
56
+ it("should keep every path when the project declares no rule", () => {
57
+ expectTypeOf().toEqualTypeOf();
58
+ });
59
+ });
60
+ /**
61
+ * The return type of `getLocalizedPath`, exercised against an explicit routing
62
+ * mode, default locale and locale set — this package declares none of them.
63
+ */
64
+ describe("LocalizedPathname", () => {
65
+ it("should report the same type as LocalizedUrl for a relative path", () => {
66
+ expectTypeOf().toEqualTypeOf();
67
+ expectTypeOf().toEqualTypeOf();
68
+ });
69
+ it("should drop the origin of an absolute URL", () => {
70
+ expectTypeOf().toEqualTypeOf();
71
+ expectTypeOf().toEqualTypeOf();
72
+ });
73
+ it("should collapse an origin without a path to the root", () => {
74
+ expectTypeOf().toEqualTypeOf();
75
+ expectTypeOf().toEqualTypeOf();
76
+ });
77
+ it("should replace a locale segment carried by an absolute URL", () => {
78
+ expectTypeOf().toEqualTypeOf();
79
+ });
80
+ });
81
+
82
+ //#endregion
83
+ //# sourceMappingURL=getLocalizedPath.test-d.mjs.map