@intlayer/core 8.5.2 → 8.6.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 (113) hide show
  1. package/dist/cjs/formatters/compact.cjs +1 -1
  2. package/dist/cjs/formatters/compact.cjs.map +1 -1
  3. package/dist/cjs/formatters/currency.cjs +1 -1
  4. package/dist/cjs/formatters/currency.cjs.map +1 -1
  5. package/dist/cjs/formatters/date.cjs +1 -1
  6. package/dist/cjs/formatters/date.cjs.map +1 -1
  7. package/dist/cjs/formatters/index.cjs +1 -1
  8. package/dist/cjs/formatters/list.cjs +1 -1
  9. package/dist/cjs/formatters/list.cjs.map +1 -1
  10. package/dist/cjs/formatters/number.cjs +1 -1
  11. package/dist/cjs/formatters/number.cjs.map +1 -1
  12. package/dist/cjs/formatters/percentage.cjs +2 -2
  13. package/dist/cjs/formatters/percentage.cjs.map +1 -1
  14. package/dist/cjs/formatters/relativeTime.cjs +1 -1
  15. package/dist/cjs/formatters/relativeTime.cjs.map +1 -1
  16. package/dist/cjs/formatters/units.cjs +1 -1
  17. package/dist/cjs/formatters/units.cjs.map +1 -1
  18. package/dist/cjs/index.cjs +16 -9
  19. package/dist/cjs/interpreter/getContent/getContent.cjs +8 -8
  20. package/dist/cjs/interpreter/getContent/getContent.cjs.map +1 -1
  21. package/dist/cjs/interpreter/splitAndJoinInsertion.cjs +10 -35
  22. package/dist/cjs/interpreter/splitAndJoinInsertion.cjs.map +1 -1
  23. package/dist/cjs/localization/generateSitemap.cjs +111 -0
  24. package/dist/cjs/localization/generateSitemap.cjs.map +1 -0
  25. package/dist/cjs/localization/getBrowserLocale.cjs +1 -1
  26. package/dist/cjs/localization/getBrowserLocale.cjs.map +1 -1
  27. package/dist/cjs/localization/getLocale.cjs +2 -2
  28. package/dist/cjs/localization/getLocale.cjs.map +1 -1
  29. package/dist/cjs/localization/getLocalizedUrl.cjs +1 -1
  30. package/dist/cjs/localization/getPrefix.cjs +1 -1
  31. package/dist/cjs/localization/index.cjs +8 -7
  32. package/dist/cjs/localization/localeMapper.cjs +1 -1
  33. package/dist/cjs/transpiler/html/getHTMLCustomComponents.cjs +2 -3
  34. package/dist/cjs/transpiler/html/getHTMLCustomComponents.cjs.map +1 -1
  35. package/dist/cjs/utils/index.cjs +9 -3
  36. package/dist/cjs/utils/intl.cjs +88 -72
  37. package/dist/cjs/utils/intl.cjs.map +1 -1
  38. package/dist/cjs/utils/localeStorage.cjs +191 -64
  39. package/dist/cjs/utils/localeStorage.cjs.map +1 -1
  40. package/dist/esm/formatters/compact.mjs +2 -2
  41. package/dist/esm/formatters/compact.mjs.map +1 -1
  42. package/dist/esm/formatters/currency.mjs +2 -2
  43. package/dist/esm/formatters/currency.mjs.map +1 -1
  44. package/dist/esm/formatters/date.mjs +2 -2
  45. package/dist/esm/formatters/date.mjs.map +1 -1
  46. package/dist/esm/formatters/index.mjs +2 -2
  47. package/dist/esm/formatters/list.mjs +2 -2
  48. package/dist/esm/formatters/list.mjs.map +1 -1
  49. package/dist/esm/formatters/number.mjs +2 -2
  50. package/dist/esm/formatters/number.mjs.map +1 -1
  51. package/dist/esm/formatters/percentage.mjs +3 -3
  52. package/dist/esm/formatters/percentage.mjs.map +1 -1
  53. package/dist/esm/formatters/relativeTime.mjs +2 -2
  54. package/dist/esm/formatters/relativeTime.mjs.map +1 -1
  55. package/dist/esm/formatters/units.mjs +2 -2
  56. package/dist/esm/formatters/units.mjs.map +1 -1
  57. package/dist/esm/index.mjs +10 -10
  58. package/dist/esm/interpreter/getContent/getContent.mjs +8 -8
  59. package/dist/esm/interpreter/getContent/getContent.mjs.map +1 -1
  60. package/dist/esm/interpreter/splitAndJoinInsertion.mjs +10 -35
  61. package/dist/esm/interpreter/splitAndJoinInsertion.mjs.map +1 -1
  62. package/dist/esm/localization/generateSitemap.mjs +109 -0
  63. package/dist/esm/localization/generateSitemap.mjs.map +1 -0
  64. package/dist/esm/localization/getBrowserLocale.mjs +2 -2
  65. package/dist/esm/localization/getBrowserLocale.mjs.map +1 -1
  66. package/dist/esm/localization/getLocale.mjs +3 -3
  67. package/dist/esm/localization/getLocale.mjs.map +1 -1
  68. package/dist/esm/localization/getLocalizedUrl.mjs +1 -1
  69. package/dist/esm/localization/getPrefix.mjs +1 -1
  70. package/dist/esm/localization/index.mjs +7 -7
  71. package/dist/esm/localization/localeMapper.mjs +1 -1
  72. package/dist/esm/transpiler/html/getHTMLCustomComponents.mjs +2 -4
  73. package/dist/esm/transpiler/html/getHTMLCustomComponents.mjs.map +1 -1
  74. package/dist/esm/utils/index.mjs +4 -4
  75. package/dist/esm/utils/intl.mjs +87 -72
  76. package/dist/esm/utils/intl.mjs.map +1 -1
  77. package/dist/esm/utils/localeStorage.mjs +186 -65
  78. package/dist/esm/utils/localeStorage.mjs.map +1 -1
  79. package/dist/types/deepTransformPlugins/getFilterMissingTranslationsContent.d.ts +10 -10
  80. package/dist/types/deepTransformPlugins/getFilterMissingTranslationsContent.d.ts.map +1 -1
  81. package/dist/types/deepTransformPlugins/getFilterTranslationsOnlyContent.d.ts +10 -10
  82. package/dist/types/deepTransformPlugins/getFilterTranslationsOnlyContent.d.ts.map +1 -1
  83. package/dist/types/deepTransformPlugins/getFilteredLocalesContent.d.ts +10 -10
  84. package/dist/types/deepTransformPlugins/getFilteredLocalesContent.d.ts.map +1 -1
  85. package/dist/types/dictionaryManipulator/orderDictionaries.d.ts +2 -2
  86. package/dist/types/dictionaryManipulator/orderDictionaries.d.ts.map +1 -1
  87. package/dist/types/formatters/index.d.ts +2 -2
  88. package/dist/types/formatters/number.d.ts +4 -1
  89. package/dist/types/formatters/number.d.ts.map +1 -1
  90. package/dist/types/formatters/percentage.d.ts +4 -1
  91. package/dist/types/formatters/percentage.d.ts.map +1 -1
  92. package/dist/types/index.d.ts +4 -4
  93. package/dist/types/interpreter/getContent/getContent.d.ts.map +1 -1
  94. package/dist/types/interpreter/splitAndJoinInsertion.d.ts +0 -17
  95. package/dist/types/interpreter/splitAndJoinInsertion.d.ts.map +1 -1
  96. package/dist/types/localization/generateSitemap.d.ts +93 -0
  97. package/dist/types/localization/generateSitemap.d.ts.map +1 -0
  98. package/dist/types/localization/getBrowserLocale.d.ts +2 -2
  99. package/dist/types/localization/getBrowserLocale.d.ts.map +1 -1
  100. package/dist/types/localization/index.d.ts +2 -2
  101. package/dist/types/transpiler/html/getHTMLCustomComponents.d.ts.map +1 -1
  102. package/dist/types/utils/index.d.ts +3 -3
  103. package/dist/types/utils/intl.d.ts +15 -11
  104. package/dist/types/utils/intl.d.ts.map +1 -1
  105. package/dist/types/utils/localeStorage.d.ts +85 -68
  106. package/dist/types/utils/localeStorage.d.ts.map +1 -1
  107. package/package.json +8 -8
  108. package/dist/cjs/getStorageAttributes.cjs +0 -135
  109. package/dist/cjs/getStorageAttributes.cjs.map +0 -1
  110. package/dist/esm/getStorageAttributes.mjs +0 -133
  111. package/dist/esm/getStorageAttributes.mjs.map +0 -1
  112. package/dist/types/getStorageAttributes.d.ts +0 -29
  113. package/dist/types/getStorageAttributes.d.ts.map +0 -1
@@ -1,87 +1,102 @@
1
- import { ENGLISH } from "@intlayer/types/locales";
1
+ import configuration from "@intlayer/config/built";
2
2
 
3
3
  //#region src/utils/intl.ts
4
4
  /**
5
- * Optimized Cache Key Generator
6
- * 1. Fast path: If no options, just use the locale string.
7
- * 2. Normal path: JSON.stringify for deterministic object comparison.
5
+ * Cached Intl helper – drop‑in replacement for the global `Intl` object.
6
+ * ‑‑‑
7
+ * Uses a `Proxy` to lazily wrap every *constructor* hanging off `Intl` (NumberFormat, DateTimeFormat, …).
8
+ * • Each wrapped constructor keeps an in‑memory cache keyed by `[locales, options]` so that identical requests
9
+ * reuse the same heavy instance instead of reparsing CLDR data every time.
10
+ * • A polyfill warning for `Intl.DisplayNames` is emitted only once and only in dev.
11
+ * • The public API is fully type‑safe and mirrors the native `Intl` surface exactly –
12
+ * you can treat `CachedIntl` just like the built‑in `Intl`.
13
+ *
14
+ * Usage @example:
15
+ * ---------------
16
+ * ```ts
17
+ * import { CachedIntl } from "./cached-intl";
18
+ *
19
+ * const nf = CachedIntl.NumberFormat("en-US", { style: "currency", currency: "USD" });
20
+ * console.log(nf.format(1234));
21
+ *
22
+ * const dn = CachedIntl.DisplayNames(["fr"], { type: "language" });
23
+ * console.log(dn.of("en")); * → "anglais"
24
+ *
25
+ * You can also spin up an isolated instance with its own caches (handy in test suites):
26
+ * const TestIntl = createCachedIntl();
27
+ * ```
8
28
  */
9
- const getCacheKey = (locales, options) => {
10
- const localeKey = locales ? String(locales) : ENGLISH;
11
- if (!options) return localeKey;
12
- return `${localeKey}|${JSON.stringify(options)}`;
13
- };
29
+ const MAX_CACHE_SIZE = 50;
30
+ const cache = /* @__PURE__ */ new Map();
14
31
  /**
15
- * Generic wrapper for any `new Intl.*()` constructor.
32
+ * Generic caching instantiator for Intl constructors.
16
33
  */
17
- const createCachedConstructor = (Ctor) => {
18
- const cache = /* @__PURE__ */ new Map();
19
- const MAX_CACHE_SIZE = 50;
20
- function Wrapped(locales, options) {
21
- let resolvedLocales = locales;
22
- let resolvedOptions = options;
23
- if (typeof locales === "object" && !Array.isArray(locales) && locales !== null) {
24
- resolvedOptions = locales;
25
- resolvedLocales = locales.locale;
26
- }
27
- if (Ctor.name === "DisplayNames" && typeof Intl?.DisplayNames !== "function") {}
28
- const key = getCacheKey(resolvedLocales, resolvedOptions);
29
- let instance = cache.get(key);
30
- if (instance) return instance;
31
- instance = new Ctor(resolvedLocales, resolvedOptions);
32
- if (cache.size >= MAX_CACHE_SIZE) {
33
- const oldestKey = cache.keys().next().value;
34
- if (oldestKey) cache.delete(oldestKey);
35
- }
36
- cache.set(key, instance);
37
- return instance;
34
+ const getCachedIntl = (Ctor, locale, options) => {
35
+ const resLoc = locale ?? configuration?.internationalization?.defaultLocale;
36
+ const key = `${resLoc}|${options ? JSON.stringify(options) : ""}`;
37
+ let ctorCache = cache.get(Ctor);
38
+ if (!ctorCache) {
39
+ ctorCache = /* @__PURE__ */ new Map();
40
+ cache.set(Ctor, ctorCache);
38
41
  }
39
- Wrapped.prototype = Ctor.prototype;
40
- return Wrapped;
42
+ let instance = ctorCache.get(key);
43
+ if (!instance) {
44
+ if (ctorCache.size > MAX_CACHE_SIZE) ctorCache.clear();
45
+ instance = new Ctor(resLoc, options);
46
+ ctorCache.set(key, instance);
47
+ }
48
+ return instance;
41
49
  };
42
50
  /**
43
- * Factory that turns the global `Intl` into a cached clone.
51
+ * Optional: Keep bindIntl if your library exports it publicly.
52
+ * It now uses the much smaller getCachedIntl under the hood.
44
53
  */
45
- const createCachedIntl = () => {
46
- const constructorCache = /* @__PURE__ */ new Map();
47
- return new Proxy(Intl, { get: (target, prop, receiver) => {
48
- if (constructorCache.has(prop)) return constructorCache.get(prop);
49
- const value = Reflect.get(target, prop, receiver);
50
- if (typeof value === "function" && typeof prop === "string" && /^[A-Z]/.test(prop)) {
51
- const wrapped = createCachedConstructor(value);
52
- constructorCache.set(prop, wrapped);
53
- return wrapped;
54
- }
55
- return value;
56
- } });
54
+ const bindIntl = (boundLocale) => {
55
+ const bindWrap = (Ctor) => function intlConstructor(locales, options) {
56
+ const isOptsFirst = locales !== null && typeof locales === "object" && !Array.isArray(locales);
57
+ const resOpts = isOptsFirst ? locales : options;
58
+ return getCachedIntl(Ctor, isOptsFirst ? resOpts.locale || boundLocale : locales || boundLocale, resOpts);
59
+ };
60
+ return {
61
+ ...Intl,
62
+ Collator: bindWrap(Intl.Collator),
63
+ DateTimeFormat: bindWrap(Intl.DateTimeFormat),
64
+ DisplayNames: bindWrap(Intl.DisplayNames),
65
+ ListFormat: bindWrap(Intl.ListFormat),
66
+ NumberFormat: bindWrap(Intl.NumberFormat),
67
+ PluralRules: bindWrap(Intl.PluralRules),
68
+ RelativeTimeFormat: bindWrap(Intl.RelativeTimeFormat),
69
+ Locale: bindWrap(Intl.Locale),
70
+ Segmenter: bindWrap(Intl.Segmenter)
71
+ };
57
72
  };
58
- const CachedIntl = createCachedIntl();
59
- /**
60
- * Creates a proxied Intl object with a preset locale.
61
- *
62
- * @example
63
- * const intl = bindIntl(Locales.FRENCH);
64
- * new intl.NumberFormat(undefined, { style: 'currency', currency: 'EUR' }).format(10);
65
- * // Uses 'fr' automatically
66
- */
67
- const bindIntl = (locale) => {
68
- return new Proxy(CachedIntl, { get: (target, prop) => {
69
- const value = Reflect.get(target, prop);
70
- if (typeof value === "function" && typeof prop === "string" && /^[A-Z]/.test(prop)) return new Proxy(value, {
71
- construct: (Ctor, args) => {
72
- let [locales, options] = args;
73
- if (typeof locales === "object" && !Array.isArray(locales) && locales !== null) {
74
- options = locales;
75
- locales = options.locale ?? locale;
76
- } else if (locales === void 0) locales = locale;
77
- return new Ctor(locales, options);
78
- },
79
- get: (Ctor, key) => Reflect.get(Ctor, key)
80
- });
81
- return value;
82
- } });
73
+ const CachedIntl = {
74
+ Collator: function Collator(locales, options) {
75
+ return getCachedIntl(Intl.Collator, locales, options);
76
+ },
77
+ DateTimeFormat: function DateTimeFormat(locales, options) {
78
+ return getCachedIntl(Intl.DateTimeFormat, locales, options);
79
+ },
80
+ DisplayNames: function DisplayNames(locales, options) {
81
+ return getCachedIntl(Intl.DisplayNames, locales, options);
82
+ },
83
+ ListFormat: function ListFormat(locales, options) {
84
+ return getCachedIntl(Intl.ListFormat, locales, options);
85
+ },
86
+ NumberFormat: function NumberFormat(locales, options) {
87
+ return getCachedIntl(Intl.NumberFormat, locales, options);
88
+ },
89
+ PluralRules: function PluralRules(locales, options) {
90
+ return getCachedIntl(Intl.PluralRules, locales, options);
91
+ },
92
+ RelativeTimeFormat: function RelativeTimeFormat(locales, options) {
93
+ return getCachedIntl(Intl.RelativeTimeFormat, locales, options);
94
+ },
95
+ Segmenter: function Segmenter(locales, options) {
96
+ return getCachedIntl(Intl.Segmenter, locales, options);
97
+ }
83
98
  };
84
99
 
85
100
  //#endregion
86
- export { CachedIntl, CachedIntl as Intl, bindIntl, createCachedIntl };
101
+ export { CachedIntl, CachedIntl as Intl, bindIntl, getCachedIntl };
87
102
  //# sourceMappingURL=intl.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"intl.mjs","names":[],"sources":["../../../src/utils/intl.ts"],"sourcesContent":["// Cached Intl helper – drop‑in replacement for the global `Intl` object.\n// ‑‑‑\n// • Uses a `Proxy` to lazily wrap every *constructor* hanging off `Intl` (NumberFormat, DateTimeFormat, …).\n// • Each wrapped constructor keeps an in‑memory cache keyed by `[locales, options]` so that identical requests\n// reuse the same heavy instance instead of reparsing CLDR data every time.\n// • A polyfill warning for `Intl.DisplayNames` is emitted only once and only in dev.\n// • The public API is fully type‑safe and mirrors the native `Intl` surface exactly –\n// you can treat `CachedIntl` just like the built‑in `Intl`.\n//\n// Usage examples:\n// ---------------\n// import { CachedIntl } from \"./cached-intl\";\n//\n// const nf = CachedIntl.NumberFormat(\"en-US\", { style: \"currency\", currency: \"USD\" });\n// console.log(nf.format(1234));\n//\n// const dn = CachedIntl.DisplayNames([\"fr\"], { type: \"language\" });\n// console.log(dn.of(\"en\")); // → \"anglais\"\n//\n// You can also spin up an isolated instance with its own caches (handy in test suites):\n// const TestIntl = createCachedIntl();\n//\n// ---------------------------------------------------------------------\n\nimport { ENGLISH } from '@intlayer/types/locales';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\n// Helper type that picks just the constructor members off `typeof Intl`.\n// The \"capital‑letter\" heuristic is 100 % accurate today and keeps the\n// mapping short‑lived, so we don't have to manually list every constructor.\ntype IntlConstructors = {\n [K in keyof typeof Intl as (typeof Intl)[K] extends new (\n ...args: any\n ) => any\n ? K\n : never]: (typeof Intl)[K];\n};\n\n// Type wrapper to replace locale arguments with LocalesValues\ntype ReplaceLocaleWithLocalesValues<T> = T extends new (\n locales: any,\n options?: infer Options\n) => infer Instance\n ? {\n new (locales?: LocalesValues, options?: Options): Instance;\n new (options?: Options & { locale?: LocalesValues }): Instance;\n }\n : T extends new (\n locales: any\n ) => infer Instance\n ? {\n new (locales?: LocalesValues): Instance;\n new (options?: { locale?: LocalesValues }): Instance;\n }\n : T;\n\n// Wrapped Intl type with LocalesValues\nexport type WrappedIntl = {\n [K in keyof typeof Intl]: K extends keyof IntlConstructors\n ? ReplaceLocaleWithLocalesValues<(typeof Intl)[K]>\n : (typeof Intl)[K];\n};\n\n// ... (Keep your Type Helper definitions here: IntlConstructors, ReplaceLocaleWithLocalesValues, WrappedIntl) ...\n\n/**\n * Optimized Cache Key Generator\n * 1. Fast path: If no options, just use the locale string.\n * 2. Normal path: JSON.stringify for deterministic object comparison.\n */\nconst getCacheKey = (\n locales: LocalesValues | undefined,\n options: unknown\n): string => {\n const localeKey = locales ? String(locales) : ENGLISH;\n\n if (!options) return localeKey;\n\n // JSON.stringify is the most robust way to handle nested options objects\n // without a heavy custom hashing function.\n return `${localeKey}|${JSON.stringify(options)}`;\n};\n\n/**\n * Generic wrapper for any `new Intl.*()` constructor.\n */\nconst createCachedConstructor = <T extends new (...args: any[]) => any>(\n Ctor: T\n) => {\n // The cache lives here, inside the closure of the wrapped constructor.\n const cache = new Map<string, InstanceType<T>>();\n const MAX_CACHE_SIZE = 50;\n\n function Wrapped(locales?: LocalesValues | any, options?: any) {\n let resolvedLocales = locales;\n let resolvedOptions = options;\n\n // Handle case where first argument is an options object instead of locales\n if (\n typeof locales === 'object' &&\n !Array.isArray(locales) &&\n locales !== null\n ) {\n resolvedOptions = locales;\n resolvedLocales = locales.locale;\n }\n\n // Handle DisplayNames Polyfill warning\n if (\n Ctor.name === 'DisplayNames' &&\n typeof (Intl as any)?.DisplayNames !== 'function'\n ) {\n // ... (Existing polyfill logic would go here if needed, but let's keep it simple for now as it was empty in the read output)\n // Actually the read output had \"// ... (Your existing polyfill warning logic) ...\"\n // I should check what was there before or just preserve it.\n }\n\n // Generate Key\n const key = getCacheKey(resolvedLocales, resolvedOptions);\n\n // Check Cache\n let instance = cache.get(key);\n if (instance) return instance;\n\n // Create New Instance\n instance = new Ctor(resolvedLocales as never, resolvedOptions as never);\n\n // Smart Eviction (LRU-ish)\n // Map iterates in insertion order. Deleting the first key removes the \"oldest\".\n if (cache.size >= MAX_CACHE_SIZE) {\n const oldestKey = cache.keys().next().value;\n if (oldestKey) cache.delete(oldestKey);\n }\n\n cache.set(key, instance as InstanceType<T>);\n return instance as InstanceType<T>;\n }\n\n // Preserve prototype for `instanceof` checks\n (Wrapped as any).prototype = (Ctor as any).prototype;\n\n return Wrapped as unknown as ReplaceLocaleWithLocalesValues<T>;\n};\n\n/**\n * Factory that turns the global `Intl` into a cached clone.\n */\nexport const createCachedIntl = (): WrappedIntl => {\n // We must cache the *wrapped constructors* themselves.\n // Otherwise, the Proxy creates a new `Wrapped` function (and a new empty Map)\n // on every single property access.\n const constructorCache = new Map<string | symbol, any>();\n\n return new Proxy(Intl as IntlConstructors, {\n get: (target, prop, receiver) => {\n // Fast return if we already wrapped this constructor\n if (constructorCache.has(prop)) {\n return constructorCache.get(prop);\n }\n\n const value = Reflect.get(target, prop, receiver);\n\n // Wrap only Constructors (Heuristic: Function + starts with Uppercase)\n // This prevents wrapping static methods like `Intl.getCanonicalLocales`\n if (\n typeof value === 'function' &&\n typeof prop === 'string' &&\n /^[A-Z]/.test(prop)\n ) {\n const wrapped = createCachedConstructor(value);\n constructorCache.set(prop, wrapped);\n return wrapped;\n }\n\n // Pass through everything else (static methods, constants)\n return value;\n },\n }) as unknown as WrappedIntl;\n};\n\nexport const CachedIntl = createCachedIntl();\n\n/**\n * Creates a proxied Intl object with a preset locale.\n *\n * @example\n * const intl = bindIntl(Locales.FRENCH);\n * new intl.NumberFormat(undefined, { style: 'currency', currency: 'EUR' }).format(10);\n * // Uses 'fr' automatically\n */\nexport const bindIntl = (locale: LocalesValues): WrappedIntl => {\n return new Proxy(CachedIntl, {\n get: (target, prop) => {\n const value = Reflect.get(target, prop);\n\n // We only want to intercept Constructors (e.g., NumberFormat, DateTimeFormat)\n // to inject the locale into their arguments.\n if (\n typeof value === 'function' &&\n typeof prop === 'string' &&\n /^[A-Z]/.test(prop)\n ) {\n return new Proxy(value, {\n construct: (Ctor, args) => {\n let [locales, options] = args;\n\n // If the user provided a locale (args[0]), respect it.\n // If args[0] is undefined, inject the bound locale.\n // If args[0] is an object (not array), it's the options object.\n if (\n typeof locales === 'object' &&\n !Array.isArray(locales) &&\n locales !== null\n ) {\n options = locales;\n locales = options.locale ?? locale;\n } else if (locales === undefined) {\n locales = locale;\n }\n\n // We pass it to `CachedIntl` which handles caching logic.\n return new Ctor(locales, options);\n },\n // Ensure static methods (like supportedLocalesOf) still work\n get: (Ctor, key) => Reflect.get(Ctor, key),\n });\n }\n\n // Return constants or static methods as-is\n return value;\n },\n }) as unknown as WrappedIntl;\n};\n\n// new CachedIntl.DisplayNames(Locales.FRENCH, { type: 'language' });\n// new CachedIntl.DisplayNames('fr', { type: 'language' });\n// new CachedIntl.DateTimeFormat('fr', {\n// year: 'numeric',\n// month: 'long',\n// day: 'numeric',\n// });\n// new CachedIntl.NumberFormat('fr', {\n// style: 'currency',\n// currency: 'EUR',\n// });\n// new CachedIntl.Collator('fr', { sensitivity: 'base' });\n// new CachedIntl.PluralRules('fr');\n// new CachedIntl.RelativeTimeFormat('fr', { numeric: 'auto' });\n// new CachedIntl.ListFormat('fr', { type: 'conjunction' });\nexport { CachedIntl as Intl };\n"],"mappings":";;;;;;;;AAsEA,MAAM,eACJ,SACA,YACW;CACX,MAAM,YAAY,UAAU,OAAO,QAAQ,GAAG;AAE9C,KAAI,CAAC,QAAS,QAAO;AAIrB,QAAO,GAAG,UAAU,GAAG,KAAK,UAAU,QAAQ;;;;;AAMhD,MAAM,2BACJ,SACG;CAEH,MAAM,wBAAQ,IAAI,KAA8B;CAChD,MAAM,iBAAiB;CAEvB,SAAS,QAAQ,SAA+B,SAAe;EAC7D,IAAI,kBAAkB;EACtB,IAAI,kBAAkB;AAGtB,MACE,OAAO,YAAY,YACnB,CAAC,MAAM,QAAQ,QAAQ,IACvB,YAAY,MACZ;AACA,qBAAkB;AAClB,qBAAkB,QAAQ;;AAI5B,MACE,KAAK,SAAS,kBACd,OAAQ,MAAc,iBAAiB,YACvC;EAOF,MAAM,MAAM,YAAY,iBAAiB,gBAAgB;EAGzD,IAAI,WAAW,MAAM,IAAI,IAAI;AAC7B,MAAI,SAAU,QAAO;AAGrB,aAAW,IAAI,KAAK,iBAA0B,gBAAyB;AAIvE,MAAI,MAAM,QAAQ,gBAAgB;GAChC,MAAM,YAAY,MAAM,MAAM,CAAC,MAAM,CAAC;AACtC,OAAI,UAAW,OAAM,OAAO,UAAU;;AAGxC,QAAM,IAAI,KAAK,SAA4B;AAC3C,SAAO;;AAIT,CAAC,QAAgB,YAAa,KAAa;AAE3C,QAAO;;;;;AAMT,MAAa,yBAAsC;CAIjD,MAAM,mCAAmB,IAAI,KAA2B;AAExD,QAAO,IAAI,MAAM,MAA0B,EACzC,MAAM,QAAQ,MAAM,aAAa;AAE/B,MAAI,iBAAiB,IAAI,KAAK,CAC5B,QAAO,iBAAiB,IAAI,KAAK;EAGnC,MAAM,QAAQ,QAAQ,IAAI,QAAQ,MAAM,SAAS;AAIjD,MACE,OAAO,UAAU,cACjB,OAAO,SAAS,YAChB,SAAS,KAAK,KAAK,EACnB;GACA,MAAM,UAAU,wBAAwB,MAAM;AAC9C,oBAAiB,IAAI,MAAM,QAAQ;AACnC,UAAO;;AAIT,SAAO;IAEV,CAAC;;AAGJ,MAAa,aAAa,kBAAkB;;;;;;;;;AAU5C,MAAa,YAAY,WAAuC;AAC9D,QAAO,IAAI,MAAM,YAAY,EAC3B,MAAM,QAAQ,SAAS;EACrB,MAAM,QAAQ,QAAQ,IAAI,QAAQ,KAAK;AAIvC,MACE,OAAO,UAAU,cACjB,OAAO,SAAS,YAChB,SAAS,KAAK,KAAK,CAEnB,QAAO,IAAI,MAAM,OAAO;GACtB,YAAY,MAAM,SAAS;IACzB,IAAI,CAAC,SAAS,WAAW;AAKzB,QACE,OAAO,YAAY,YACnB,CAAC,MAAM,QAAQ,QAAQ,IACvB,YAAY,MACZ;AACA,eAAU;AACV,eAAU,QAAQ,UAAU;eACnB,YAAY,OACrB,WAAU;AAIZ,WAAO,IAAI,KAAK,SAAS,QAAQ;;GAGnC,MAAM,MAAM,QAAQ,QAAQ,IAAI,MAAM,IAAI;GAC3C,CAAC;AAIJ,SAAO;IAEV,CAAC"}
1
+ {"version":3,"file":"intl.mjs","names":[],"sources":["../../../src/utils/intl.ts"],"sourcesContent":["/**\n * Cached Intl helper – drop‑in replacement for the global `Intl` object.\n * ‑‑‑\n * • Uses a `Proxy` to lazily wrap every *constructor* hanging off `Intl` (NumberFormat, DateTimeFormat, …).\n * • Each wrapped constructor keeps an in‑memory cache keyed by `[locales, options]` so that identical requests\n * reuse the same heavy instance instead of reparsing CLDR data every time.\n * • A polyfill warning for `Intl.DisplayNames` is emitted only once and only in dev.\n * • The public API is fully type‑safe and mirrors the native `Intl` surface exactly –\n * you can treat `CachedIntl` just like the built‑in `Intl`.\n *\n * Usage @example:\n * ---------------\n * ```ts\n * import { CachedIntl } from \"./cached-intl\";\n *\n * const nf = CachedIntl.NumberFormat(\"en-US\", { style: \"currency\", currency: \"USD\" });\n * console.log(nf.format(1234));\n *\n * const dn = CachedIntl.DisplayNames([\"fr\"], { type: \"language\" });\n * console.log(dn.of(\"en\")); * → \"anglais\"\n *\n * You can also spin up an isolated instance with its own caches (handy in test suites):\n * const TestIntl = createCachedIntl();\n * ```\n */\n\nimport configuration from '@intlayer/config/built';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\nconst MAX_CACHE_SIZE = 50;\nconst cache = new Map<any, Map<string, any>>();\n\ntype IntlConstructors = {\n [K in keyof typeof Intl as (typeof Intl)[K] extends new (\n ...args: any\n ) => any\n ? K\n : never]: (typeof Intl)[K];\n};\n\ntype ReplaceLocaleWithLocalesValues<T> = T extends new (\n locales: any,\n options?: infer Options\n) => infer Instance\n ? {\n new (locales?: LocalesValues, options?: Options): Instance;\n new (options?: Options & { locale?: LocalesValues }): Instance;\n (locales?: LocalesValues, options?: Options): Instance;\n (options?: Options & { locale?: LocalesValues }): Instance;\n }\n : T extends new (\n locales: any\n ) => infer Instance\n ? {\n new (locales?: LocalesValues): Instance;\n new (options?: { locale?: LocalesValues }): Instance;\n (locales?: LocalesValues): Instance;\n (options?: { locale?: LocalesValues }): Instance;\n }\n : T;\n\nexport type WrappedIntl = {\n [K in keyof typeof Intl]: K extends keyof IntlConstructors\n ? ReplaceLocaleWithLocalesValues<(typeof Intl)[K]>\n : (typeof Intl)[K];\n};\n\n/**\n * Generic caching instantiator for Intl constructors.\n */\nexport const getCachedIntl = <T extends new (...args: any[]) => any>(\n Ctor: T,\n locale?: LocalesValues | string,\n options?: any\n): InstanceType<T> => {\n const resLoc = locale ?? configuration?.internationalization?.defaultLocale;\n const optKey = options ? JSON.stringify(options) : '';\n const key = `${resLoc}|${optKey}`;\n\n let ctorCache = cache.get(Ctor);\n\n if (!ctorCache) {\n ctorCache = new Map();\n cache.set(Ctor, ctorCache);\n }\n\n let instance = ctorCache.get(key);\n\n if (!instance) {\n if (ctorCache.size > MAX_CACHE_SIZE) ctorCache.clear();\n instance = new Ctor(resLoc, options);\n ctorCache.set(key, instance);\n }\n return instance;\n};\n\n/**\n * Optional: Keep bindIntl if your library exports it publicly.\n * It now uses the much smaller getCachedIntl under the hood.\n */\nexport const bindIntl = (boundLocale: LocalesValues): WrappedIntl => {\n const bindWrap = (Ctor: any) =>\n // function is used as a constructor, do not change in arrow function\n function intlConstructor(locales?: any, options?: any) {\n const isOptsFirst =\n locales !== null &&\n typeof locales === 'object' &&\n !Array.isArray(locales);\n const resOpts = isOptsFirst ? locales : options;\n const resLoc = isOptsFirst\n ? (resOpts as any).locale || boundLocale\n : locales || boundLocale;\n\n return getCachedIntl(Ctor, resLoc, resOpts);\n };\n\n return {\n ...Intl,\n Collator: bindWrap(Intl.Collator),\n DateTimeFormat: bindWrap(Intl.DateTimeFormat),\n DisplayNames: bindWrap(Intl.DisplayNames),\n ListFormat: bindWrap(Intl.ListFormat),\n NumberFormat: bindWrap(Intl.NumberFormat),\n PluralRules: bindWrap(Intl.PluralRules),\n RelativeTimeFormat: bindWrap(Intl.RelativeTimeFormat),\n Locale: bindWrap(Intl.Locale),\n Segmenter: bindWrap((Intl as any).Segmenter),\n } as unknown as WrappedIntl;\n};\n\n// Add this to the bottom of utils/intl.ts ONLY if required for public API compatibility.\nexport const CachedIntl = {\n // function is used as a constructor, do not change in arrow function\n Collator: function Collator(locales?: any, options?: any) {\n return getCachedIntl(Intl.Collator, locales, options);\n },\n DateTimeFormat: function DateTimeFormat(locales?: any, options?: any) {\n return getCachedIntl(Intl.DateTimeFormat, locales, options);\n },\n DisplayNames: function DisplayNames(locales?: any, options?: any) {\n return getCachedIntl(Intl.DisplayNames, locales, options);\n },\n ListFormat: function ListFormat(locales?: any, options?: any) {\n return getCachedIntl(Intl.ListFormat as any, locales, options);\n },\n NumberFormat: function NumberFormat(locales?: any, options?: any) {\n return getCachedIntl(Intl.NumberFormat, locales, options);\n },\n PluralRules: function PluralRules(locales?: any, options?: any) {\n return getCachedIntl(Intl.PluralRules, locales, options);\n },\n RelativeTimeFormat: function RelativeTimeFormat(\n locales?: any,\n options?: any\n ) {\n return getCachedIntl(Intl.RelativeTimeFormat, locales, options);\n },\n Segmenter: function Segmenter(locales?: any, options?: any) {\n return getCachedIntl((Intl as any).Segmenter, locales, options);\n },\n} as any; // Cast to 'any' internally to avoid TS readonly errors\n\nexport { CachedIntl as Intl };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAM,iBAAiB;AACvB,MAAM,wBAAQ,IAAI,KAA4B;;;;AAwC9C,MAAa,iBACX,MACA,QACA,YACoB;CACpB,MAAM,SAAS,UAAU,eAAe,sBAAsB;CAE9D,MAAM,MAAM,GAAG,OAAO,GADP,UAAU,KAAK,UAAU,QAAQ,GAAG;CAGnD,IAAI,YAAY,MAAM,IAAI,KAAK;AAE/B,KAAI,CAAC,WAAW;AACd,8BAAY,IAAI,KAAK;AACrB,QAAM,IAAI,MAAM,UAAU;;CAG5B,IAAI,WAAW,UAAU,IAAI,IAAI;AAEjC,KAAI,CAAC,UAAU;AACb,MAAI,UAAU,OAAO,eAAgB,WAAU,OAAO;AACtD,aAAW,IAAI,KAAK,QAAQ,QAAQ;AACpC,YAAU,IAAI,KAAK,SAAS;;AAE9B,QAAO;;;;;;AAOT,MAAa,YAAY,gBAA4C;CACnE,MAAM,YAAY,SAEhB,SAAS,gBAAgB,SAAe,SAAe;EACrD,MAAM,cACJ,YAAY,QACZ,OAAO,YAAY,YACnB,CAAC,MAAM,QAAQ,QAAQ;EACzB,MAAM,UAAU,cAAc,UAAU;AAKxC,SAAO,cAAc,MAJN,cACV,QAAgB,UAAU,cAC3B,WAAW,aAEoB,QAAQ;;AAG/C,QAAO;EACL,GAAG;EACH,UAAU,SAAS,KAAK,SAAS;EACjC,gBAAgB,SAAS,KAAK,eAAe;EAC7C,cAAc,SAAS,KAAK,aAAa;EACzC,YAAY,SAAS,KAAK,WAAW;EACrC,cAAc,SAAS,KAAK,aAAa;EACzC,aAAa,SAAS,KAAK,YAAY;EACvC,oBAAoB,SAAS,KAAK,mBAAmB;EACrD,QAAQ,SAAS,KAAK,OAAO;EAC7B,WAAW,SAAU,KAAa,UAAU;EAC7C;;AAIH,MAAa,aAAa;CAExB,UAAU,SAAS,SAAS,SAAe,SAAe;AACxD,SAAO,cAAc,KAAK,UAAU,SAAS,QAAQ;;CAEvD,gBAAgB,SAAS,eAAe,SAAe,SAAe;AACpE,SAAO,cAAc,KAAK,gBAAgB,SAAS,QAAQ;;CAE7D,cAAc,SAAS,aAAa,SAAe,SAAe;AAChE,SAAO,cAAc,KAAK,cAAc,SAAS,QAAQ;;CAE3D,YAAY,SAAS,WAAW,SAAe,SAAe;AAC5D,SAAO,cAAc,KAAK,YAAmB,SAAS,QAAQ;;CAEhE,cAAc,SAAS,aAAa,SAAe,SAAe;AAChE,SAAO,cAAc,KAAK,cAAc,SAAS,QAAQ;;CAE3D,aAAa,SAAS,YAAY,SAAe,SAAe;AAC9D,SAAO,cAAc,KAAK,aAAa,SAAS,QAAQ;;CAE1D,oBAAoB,SAAS,mBAC3B,SACA,SACA;AACA,SAAO,cAAc,KAAK,oBAAoB,SAAS,QAAQ;;CAEjE,WAAW,SAAS,UAAU,SAAe,SAAe;AAC1D,SAAO,cAAe,KAAa,WAAW,SAAS,QAAQ;;CAElE"}
@@ -1,4 +1,3 @@
1
- import { getStorageAttributes } from "../getStorageAttributes.mjs";
2
1
  import { getCookie } from "./getCookie.mjs";
3
2
  import configuration from "@intlayer/config/built";
4
3
 
@@ -13,78 +12,210 @@ const buildCookieString = (name, value, attributes) => {
13
12
  return parts.join("; ");
14
13
  };
15
14
  /**
16
- * Retrieves the locale from various storage mechanisms (cookies, localStorage, sessionStorage, headers).
17
- * The function checks storage locations in order of priority as defined in the configuration.
18
- *
19
- * @returns The locale if found in any storage, or undefined if not found
15
+ * Retrieves the locale from browser storage mechanisms
16
+ * (cookies, localStorage, sessionStorage).
17
+ * Does not read from headers — use `getLocaleFromStorageServer` for that.
20
18
  */
21
- const getLocaleFromStorage = (options) => {
19
+ const getLocaleFromStorageClient = (options) => {
22
20
  const { routing, internationalization } = configuration;
23
21
  const { locales } = internationalization;
24
- const { storage } = routing;
25
- if (storage === false || options?.isCookieEnabled === false) return void 0;
26
- const storageAttributes = getStorageAttributes(storage);
27
- const isValidLocale = (value) => {
28
- if (!value) return false;
29
- return locales.includes(value);
30
- };
31
- const readCookie = (name) => {
32
- try {
33
- const fromOption = options?.getCookie?.(name);
34
- if (fromOption !== null && fromOption !== void 0) return fromOption;
35
- } catch {}
36
- return getCookie(name);
37
- };
38
- for (let i = 0; i < storageAttributes.cookies.length; i++) {
39
- const { name } = storageAttributes.cookies[i];
40
- const value = readCookie(name);
22
+ const storageAttributes = routing.storage;
23
+ if (options?.isCookieEnabled === false) return void 0;
24
+ const isValidLocale = (value) => !!value && locales.includes(value);
25
+ for (let i = 0; i < storageAttributes.cookies.length; i++) try {
26
+ const value = options?.getCookie?.(storageAttributes.cookies[i].name);
41
27
  if (isValidLocale(value)) return value;
28
+ } catch {}
29
+ for (let i = 0; i < storageAttributes.localStorage.length; i++) try {
30
+ const value = options?.getLocaleStorage?.(storageAttributes.localStorage[i].name);
31
+ if (isValidLocale(value)) return value;
32
+ } catch {}
33
+ for (let i = 0; i < storageAttributes.sessionStorage.length; i++) try {
34
+ const value = options?.getSessionStorage?.(storageAttributes.sessionStorage[i].name);
35
+ if (isValidLocale(value)) return value;
36
+ } catch {}
37
+ };
38
+ /**
39
+ * Stores the locale in browser storage mechanisms
40
+ * (cookies, localStorage, sessionStorage).
41
+ * Does not write to headers — use `setLocaleInStorageServer` for that.
42
+ */
43
+ const setLocaleInStorageClient = (locale, options) => {
44
+ const { routing } = configuration;
45
+ const storageAttributes = routing.storage;
46
+ if (options?.isCookieEnabled === false) return;
47
+ for (let i = 0; i < storageAttributes.cookies.length; i++) {
48
+ const { name, attributes } = storageAttributes.cookies[i];
49
+ try {
50
+ if (options?.setCookieStore) options.setCookieStore(name, locale, {
51
+ ...attributes,
52
+ expires: attributes.expires instanceof Date ? attributes.expires.getTime() : attributes.expires
53
+ });
54
+ } catch {
55
+ try {
56
+ if (options?.setCookieString) options.setCookieString(name, buildCookieString(name, locale, attributes));
57
+ } catch {}
58
+ }
42
59
  }
43
- for (let i = 0; i < storageAttributes.localStorage.length; i++) {
60
+ if (options?.setLocaleStorage) for (let i = 0; i < storageAttributes.localStorage.length; i++) {
44
61
  const { name } = storageAttributes.localStorage[i];
45
62
  try {
46
- const value = options?.getLocaleStorage?.(name);
47
- if (isValidLocale(value)) return value;
63
+ if (!(options?.overwrite ?? true) && options?.getLocaleStorage) {
64
+ if (options.getLocaleStorage(name)) continue;
65
+ }
66
+ options.setLocaleStorage(name, locale);
48
67
  } catch {}
49
68
  }
50
- for (let i = 0; i < storageAttributes.sessionStorage.length; i++) {
69
+ if (options?.setSessionStorage) for (let i = 0; i < storageAttributes.sessionStorage.length; i++) {
51
70
  const { name } = storageAttributes.sessionStorage[i];
52
71
  try {
53
- const value = options?.getSessionStorage?.(name);
54
- if (isValidLocale(value)) return value;
72
+ if (!(options?.overwrite ?? true) && options?.getSessionStorage) {
73
+ if (options.getSessionStorage(name)) continue;
74
+ }
75
+ options.setSessionStorage(name, locale);
55
76
  } catch {}
56
77
  }
57
- for (let i = 0; i < storageAttributes.headers.length; i++) {
58
- const { name } = storageAttributes.headers[i];
78
+ };
79
+ /**
80
+ * Client-side locale storage utility.
81
+ * Handles cookies (browser), localStorage and sessionStorage.
82
+ * Does not access headers.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * const storage = LocaleStorageClient(localeStorageOptions);
87
+ * const locale = storage.getLocale();
88
+ * storage.setLocale('fr');
89
+ * ```
90
+ */
91
+ const LocaleStorageClient = (options) => ({
92
+ getLocale: () => getLocaleFromStorageClient(options),
93
+ setLocale: (locale) => setLocaleInStorageClient(locale, options)
94
+ });
95
+ /**
96
+ * Retrieves the locale from server-side storage mechanisms (cookies, headers).
97
+ * Does not access localStorage or sessionStorage.
98
+ * No browser cookie fallback — the caller must provide `getCookie`.
99
+ */
100
+ const getLocaleFromStorageServer = (options) => {
101
+ const { routing, internationalization } = configuration;
102
+ const { locales } = internationalization;
103
+ const storageAttributes = routing.storage;
104
+ if (options?.isCookieEnabled === false) return void 0;
105
+ const isValidLocale = (value) => !!value && locales.includes(value);
106
+ for (let i = 0; i < storageAttributes.cookies.length; i++) try {
107
+ const value = options?.getCookie?.(storageAttributes.cookies[i].name);
108
+ if (isValidLocale(value)) return value;
109
+ } catch {}
110
+ for (let i = 0; i < storageAttributes.headers.length; i++) try {
111
+ const value = options?.getHeader?.(storageAttributes.headers[i].name);
112
+ if (isValidLocale(value)) return value;
113
+ } catch {}
114
+ };
115
+ /**
116
+ * Stores the locale in server-side storage mechanisms (cookies, headers).
117
+ * Does not write to localStorage or sessionStorage.
118
+ */
119
+ const setLocaleInStorageServer = (locale, options) => {
120
+ const { routing } = configuration;
121
+ const storageAttributes = routing.storage;
122
+ if (options?.isCookieEnabled === false) return;
123
+ for (let i = 0; i < storageAttributes.cookies.length; i++) {
124
+ const { name, attributes } = storageAttributes.cookies[i];
59
125
  try {
60
- const value = options?.getHeader?.(name);
61
- if (isValidLocale(value)) return value;
126
+ if (options?.setCookieStore) options.setCookieStore(name, locale, {
127
+ ...attributes,
128
+ expires: attributes.expires instanceof Date ? attributes.expires.getTime() : attributes.expires
129
+ });
130
+ } catch {
131
+ try {
132
+ if (options?.setCookieString) options.setCookieString(name, buildCookieString(name, locale, attributes));
133
+ } catch {}
134
+ }
135
+ }
136
+ if (options?.setHeader) for (let i = 0; i < storageAttributes.headers.length; i++) try {
137
+ options.setHeader(storageAttributes.headers[i].name, locale);
138
+ } catch {}
139
+ };
140
+ /**
141
+ * Server-side locale storage utility.
142
+ * Handles cookies (via injected getter/setter) and headers.
143
+ * Does not access localStorage or sessionStorage.
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * const storage = LocaleStorageServer({
148
+ * getCookie: (name) => req.cookies[name],
149
+ * setCookieStore: (name, value, attrs) => res.cookie(name, value, attrs),
150
+ * getHeader: (name) => req.headers[name],
151
+ * setHeader: (name, value) => res.setHeader(name, value),
152
+ * });
153
+ * const locale = storage.getLocale();
154
+ * storage.setLocale('fr');
155
+ * ```
156
+ */
157
+ const LocaleStorageServer = (options) => ({
158
+ getLocale: () => getLocaleFromStorageServer(options),
159
+ setLocale: (locale) => setLocaleInStorageServer(locale, options)
160
+ });
161
+ /**
162
+ * Retrieves the locale from all storage mechanisms
163
+ * (cookies, localStorage, sessionStorage, headers).
164
+ *
165
+ * @deprecated Use {@link getLocaleFromStorageClient} (browser) or
166
+ * {@link getLocaleFromStorageServer} (server) instead.
167
+ */
168
+ const getLocaleFromStorage = (options) => {
169
+ const { routing, internationalization } = configuration;
170
+ const { locales } = internationalization;
171
+ const storageAttributes = routing.storage;
172
+ if (options?.isCookieEnabled === false) return void 0;
173
+ const isValidLocale = (value) => !!value && locales.includes(value);
174
+ const readCookie = (name) => {
175
+ try {
176
+ const fromOption = options?.getCookie?.(name);
177
+ if (fromOption !== null && fromOption !== void 0) return fromOption;
62
178
  } catch {}
179
+ return getCookie(name);
180
+ };
181
+ for (let i = 0; i < storageAttributes.cookies.length; i++) {
182
+ const value = readCookie(storageAttributes.cookies[i].name);
183
+ if (isValidLocale(value)) return value;
63
184
  }
185
+ for (let i = 0; i < storageAttributes.localStorage.length; i++) try {
186
+ const value = options?.getLocaleStorage?.(storageAttributes.localStorage[i].name);
187
+ if (isValidLocale(value)) return value;
188
+ } catch {}
189
+ for (let i = 0; i < storageAttributes.sessionStorage.length; i++) try {
190
+ const value = options?.getSessionStorage?.(storageAttributes.sessionStorage[i].name);
191
+ if (isValidLocale(value)) return value;
192
+ } catch {}
193
+ for (let i = 0; i < storageAttributes.headers.length; i++) try {
194
+ const value = options?.getHeader?.(storageAttributes.headers[i].name);
195
+ if (isValidLocale(value)) return value;
196
+ } catch {}
64
197
  };
65
198
  /**
66
- * Stores the locale in various storage mechanisms (cookies, localStorage, sessionStorage, headers).
67
- * The function writes to all configured storage locations according to their attributes.
68
- * Respects overwrite flags for localStorage and sessionStorage.
199
+ * Stores the locale in all configured storage mechanisms
200
+ * (cookies, localStorage, sessionStorage, headers).
69
201
  *
70
- * @param locale - The locale to store
202
+ * @deprecated Use {@link setLocaleInStorageClient} (browser) or
203
+ * {@link setLocaleInStorageServer} (server) instead.
71
204
  */
72
205
  const setLocaleInStorage = (locale, options) => {
73
- if (configuration.routing.storage === false || options?.isCookieEnabled === false) return;
74
- const storageAttributes = getStorageAttributes(configuration.routing.storage);
206
+ const { routing } = configuration;
207
+ const storageAttributes = routing.storage;
208
+ if (options?.isCookieEnabled === false) return;
75
209
  for (let i = 0; i < storageAttributes.cookies.length; i++) {
76
210
  const { name, attributes } = storageAttributes.cookies[i];
77
211
  try {
78
- if (options?.setCookieStore) options?.setCookieStore?.(name, locale, {
212
+ if (options?.setCookieStore) options.setCookieStore(name, locale, {
79
213
  ...attributes,
80
214
  expires: attributes.expires instanceof Date ? attributes.expires.getTime() : attributes.expires
81
215
  });
82
216
  } catch {
83
217
  try {
84
- if (options?.setCookieString) {
85
- const cookieString = buildCookieString(name, locale, attributes);
86
- options?.setCookieString?.(name, cookieString);
87
- }
218
+ if (options?.setCookieString) options.setCookieString(name, buildCookieString(name, locale, attributes));
88
219
  } catch {}
89
220
  }
90
221
  }
@@ -92,39 +223,29 @@ const setLocaleInStorage = (locale, options) => {
92
223
  const { name } = storageAttributes.localStorage[i];
93
224
  try {
94
225
  if (!(options?.overwrite ?? true) && options?.getLocaleStorage) {
95
- if (options?.getLocaleStorage?.(name)) continue;
226
+ if (options.getLocaleStorage(name)) continue;
96
227
  }
97
- options?.setLocaleStorage?.(name, locale);
228
+ options.setLocaleStorage(name, locale);
98
229
  } catch {}
99
230
  }
100
231
  if (options?.setSessionStorage) for (let i = 0; i < storageAttributes.sessionStorage.length; i++) {
101
232
  const { name } = storageAttributes.sessionStorage[i];
102
233
  try {
103
234
  if (!(options?.overwrite ?? true) && options?.getSessionStorage) {
104
- if (options?.getSessionStorage?.(name)) continue;
235
+ if (options.getSessionStorage(name)) continue;
105
236
  }
106
- options?.setSessionStorage?.(name, locale);
107
- } catch {}
108
- }
109
- if (options?.setHeader) for (let i = 0; i < storageAttributes.headers.length; i++) {
110
- const { name } = storageAttributes.headers[i];
111
- try {
112
- options?.setHeader?.(name, locale);
237
+ options.setSessionStorage(name, locale);
113
238
  } catch {}
114
239
  }
240
+ if (options?.setHeader) for (let i = 0; i < storageAttributes.headers.length; i++) try {
241
+ options.setHeader(storageAttributes.headers[i].name, locale);
242
+ } catch {}
115
243
  };
116
244
  /**
117
- * Utility object to get and set the locale in the storage by considering the configuration
118
- *
119
- * @property getLocale - Retrieves the locale from various storage mechanisms (cookies, localStorage, sessionStorage, headers).
120
- * Retrieves the locale from various storage mechanisms (cookies, localStorage, sessionStorage, headers).
121
- * The function checks storage locations in order of priority as defined in the configuration.
122
- *
123
- * @property setLocale - Stores the locale in various storage mechanisms (cookies, localStorage, sessionStorage, headers).
124
- * The function writes to all configured storage locations according to their attributes.
125
- * Respects overwrite flags for localStorage and sessionStorage.
245
+ * Utility object to get and set the locale in storage based on configuration.
126
246
  *
127
- * @returns The locale if found in any storage, or undefined if not found
247
+ * @deprecated Use {@link LocaleStorageClient} (browser) or
248
+ * {@link LocaleStorageServer} (server) instead.
128
249
  */
129
250
  const LocaleStorage = (options) => ({
130
251
  getLocale: () => getLocaleFromStorage(options),
@@ -132,5 +253,5 @@ const LocaleStorage = (options) => ({
132
253
  });
133
254
 
134
255
  //#endregion
135
- export { LocaleStorage, getLocaleFromStorage, setLocaleInStorage };
256
+ export { LocaleStorage, LocaleStorageClient, LocaleStorageServer, getLocaleFromStorage, getLocaleFromStorageClient, getLocaleFromStorageServer, setLocaleInStorage, setLocaleInStorageClient, setLocaleInStorageServer };
136
257
  //# sourceMappingURL=localeStorage.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"localeStorage.mjs","names":[],"sources":["../../../src/utils/localeStorage.ts"],"sourcesContent":["import configuration from '@intlayer/config/built';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { CookiesAttributes } from '@intlayer/types/config';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { getStorageAttributes } from '../getStorageAttributes';\nimport { getCookie } from './getCookie';\n\ntype CookieBuildAttributes = {\n /**\n * Cookie domain to store the locale information\n *\n * Default: undefined\n *\n * Define the domain where the cookie is available. Defaults to\n * the domain of the page where the cookie was created.\n */\n domain?: string;\n /**\n * Cookie path to store the locale information\n *\n * Default: undefined\n *\n * Define the path where the cookie is available. Defaults to '/'\n */\n path?: string;\n /**\n * Cookie secure to store the locale information\n *\n * Default: undefined\n *\n * A Boolean indicating if the cookie transmission requires a\n * secure protocol (https). Defaults to false.\n */\n secure?: boolean;\n /**\n * Cookie httpOnly to store the locale information\n *\n * Default: undefined\n *\n * The cookie httpOnly where the locale information is stored.\n */\n httpOnly?: boolean;\n /**\n * Cookie sameSite to store the locale information\n *\n * Default: undefined\n *\n * Asserts that a cookie must not be sent with cross-origin requests,\n * providing some protection against cross-site request forgery\n * attacks (CSRF)\n */\n sameSite?: 'strict' | 'lax' | 'none';\n\n /**\n * Cookie expires to store the locale information\n *\n * Default: undefined\n *\n * Define when the cookie will be removed. Value can be a Number\n * which will be interpreted as days from time of creation or a\n * Date instance. If omitted, the cookie becomes a session cookie.\n */\n expires?: number | undefined;\n};\n\nconst buildCookieString = (\n name: string,\n value: string,\n attributes: Omit<CookiesAttributes, 'name' | 'type'>\n): string => {\n const encodedValue = encodeURIComponent(value);\n const parts: string[] = [`${name}=${encodedValue}`];\n\n if (attributes.path) parts.push(`Path=${attributes.path}`);\n if (attributes.domain) parts.push(`Domain=${attributes.domain}`);\n if (attributes.expires instanceof Date)\n parts.push(`Expires=${attributes.expires.toUTCString()}`);\n\n if (attributes.secure) parts.push('Secure');\n if (attributes.sameSite) parts.push(`SameSite=${attributes.sameSite}`);\n return parts.join('; ');\n};\n\nexport type LocaleStorageOptions = {\n overwrite?: boolean;\n isCookieEnabled?: boolean;\n setCookieStore?: (\n name: string,\n value: string,\n cookie: CookieBuildAttributes\n ) => void;\n setCookieString?: (name: string, cookie: string) => void;\n getCookie?: (name: string) => string | undefined | null;\n setSessionStorage?: (name: string, value: string) => void;\n getSessionStorage?: (name: string) => string | undefined | null;\n setLocaleStorage?: (name: string, value: string) => void;\n getLocaleStorage?: (name: string) => string | undefined | null;\n getHeader?: (name: string) => string | undefined | null;\n setHeader?: (name: string, value: string) => void;\n};\n\n/**\n * Retrieves the locale from various storage mechanisms (cookies, localStorage, sessionStorage, headers).\n * The function checks storage locations in order of priority as defined in the configuration.\n *\n * @returns The locale if found in any storage, or undefined if not found\n */\nexport const getLocaleFromStorage = (\n options: Pick<\n LocaleStorageOptions,\n | 'getCookie'\n | 'getSessionStorage'\n | 'getLocaleStorage'\n | 'getHeader'\n | 'isCookieEnabled'\n >\n): Locale | undefined => {\n const { routing, internationalization } = configuration;\n const { locales } = internationalization;\n const { storage } = routing;\n\n // If storage is disabled, return undefined\n if (storage === false || options?.isCookieEnabled === false) return undefined;\n\n const storageAttributes = getStorageAttributes(storage);\n\n const isValidLocale = (value: string | null | undefined): value is Locale => {\n if (!value) return false;\n\n return locales.includes(value as Locale);\n };\n\n const readCookie = (name: string): string | undefined => {\n // Prefer provided getter (server or custom environment)\n try {\n const fromOption = options?.getCookie?.(name);\n\n if (fromOption !== null && fromOption !== undefined) return fromOption;\n } catch {}\n\n // Fallback to browser cookie parsing\n return getCookie(name);\n };\n\n // Check cookies first\n for (let i = 0; i < storageAttributes.cookies.length; i++) {\n const { name } = storageAttributes.cookies[i];\n\n const value = readCookie(name);\n\n if (isValidLocale(value)) return value;\n }\n\n // Then check localStorage candidates (browser only)\n for (let i = 0; i < storageAttributes.localStorage.length; i++) {\n const { name } = storageAttributes.localStorage[i];\n\n try {\n const value = options?.getLocaleStorage?.(name);\n\n if (isValidLocale(value)) return value;\n } catch {}\n }\n\n // Check sessionStorage candidates (browser only)\n for (let i = 0; i < storageAttributes.sessionStorage.length; i++) {\n const { name } = storageAttributes.sessionStorage[i];\n\n try {\n const value = options?.getSessionStorage?.(name);\n\n if (isValidLocale(value)) return value;\n } catch {}\n }\n\n // Finally check header candidates (server only)\n for (let i = 0; i < storageAttributes.headers.length; i++) {\n const { name } = storageAttributes.headers[i];\n\n try {\n const value = options?.getHeader?.(name);\n\n if (isValidLocale(value)) return value;\n } catch {}\n }\n};\n\n/**\n * Stores the locale in various storage mechanisms (cookies, localStorage, sessionStorage, headers).\n * The function writes to all configured storage locations according to their attributes.\n * Respects overwrite flags for localStorage and sessionStorage.\n *\n * @param locale - The locale to store\n */\nexport const setLocaleInStorage = (\n locale: LocalesValues,\n options?: LocaleStorageOptions\n): void => {\n // If storage is disabled, do nothing\n if (\n configuration.routing.storage === false ||\n options?.isCookieEnabled === false\n )\n return;\n\n const storageAttributes = getStorageAttributes(configuration.routing.storage);\n\n // Write to cookies (server via setCookie, client via cookieStore/document)\n for (let i = 0; i < storageAttributes.cookies.length; i++) {\n const { name, attributes } = storageAttributes.cookies[i];\n\n try {\n if (options?.setCookieStore) {\n options?.setCookieStore?.(name, locale, {\n ...attributes,\n expires:\n attributes.expires instanceof Date\n ? attributes.expires.getTime()\n : attributes.expires,\n });\n }\n } catch {\n try {\n if (options?.setCookieString) {\n const cookieString = buildCookieString(name, locale, attributes);\n\n options?.setCookieString?.(name, cookieString);\n }\n } catch {}\n }\n }\n\n // Write to localStorage (browser only)\n if (options?.setLocaleStorage) {\n for (let i = 0; i < storageAttributes.localStorage.length; i++) {\n const { name } = storageAttributes.localStorage[i];\n\n try {\n const shouldOverwrite = options?.overwrite ?? true;\n\n if (!shouldOverwrite && options?.getLocaleStorage) {\n const existing = options?.getLocaleStorage?.(name);\n\n if (existing) continue;\n }\n options?.setLocaleStorage?.(name, locale);\n } catch {}\n }\n }\n\n // Write to sessionStorage (browser only)\n if (options?.setSessionStorage) {\n for (let i = 0; i < storageAttributes.sessionStorage.length; i++) {\n const { name } = storageAttributes.sessionStorage[i];\n\n try {\n const shouldOverwrite = options?.overwrite ?? true;\n\n if (!shouldOverwrite && options?.getSessionStorage) {\n const existing = options?.getSessionStorage?.(name);\n if (existing) continue;\n }\n\n options?.setSessionStorage?.(name, locale);\n } catch {}\n }\n }\n\n // Write to headers (server only)\n if (options?.setHeader) {\n for (let i = 0; i < storageAttributes.headers.length; i++) {\n const { name } = storageAttributes.headers[i];\n\n try {\n options?.setHeader?.(name, locale);\n } catch {}\n }\n }\n};\n\n/**\n * Utility object to get and set the locale in the storage by considering the configuration\n *\n * @property getLocale - Retrieves the locale from various storage mechanisms (cookies, localStorage, sessionStorage, headers).\n * Retrieves the locale from various storage mechanisms (cookies, localStorage, sessionStorage, headers).\n * The function checks storage locations in order of priority as defined in the configuration.\n *\n * @property setLocale - Stores the locale in various storage mechanisms (cookies, localStorage, sessionStorage, headers).\n * The function writes to all configured storage locations according to their attributes.\n * Respects overwrite flags for localStorage and sessionStorage.\n *\n * @returns The locale if found in any storage, or undefined if not found\n */\nexport const LocaleStorage = (options: LocaleStorageOptions) => ({\n getLocale: () => getLocaleFromStorage(options),\n setLocale: (locale: LocalesValues) => setLocaleInStorage(locale, options),\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"localeStorage.mjs","names":[],"sources":["../../../src/utils/localeStorage.ts"],"sourcesContent":["import configuration from '@intlayer/config/built';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { CookiesAttributes } from '@intlayer/types/config';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { getCookie } from './getCookie';\n\n// ============================================================================\n// Shared types\n// ============================================================================\n\nexport type CookieBuildAttributes = {\n domain?: string;\n path?: string;\n secure?: boolean;\n httpOnly?: boolean;\n sameSite?: 'strict' | 'lax' | 'none';\n /** Expiry as milliseconds since epoch (Date.getTime()) or number of days */\n expires?: number | undefined;\n};\n\n// ============================================================================\n// Shared helpers\n// ============================================================================\n\nconst buildCookieString = (\n name: string,\n value: string,\n attributes: Omit<CookiesAttributes, 'name' | 'type'>\n): string => {\n const encodedValue = encodeURIComponent(value);\n const parts: string[] = [`${name}=${encodedValue}`];\n\n if (attributes.path) parts.push(`Path=${attributes.path}`);\n if (attributes.domain) parts.push(`Domain=${attributes.domain}`);\n if (attributes.expires instanceof Date)\n parts.push(`Expires=${attributes.expires.toUTCString()}`);\n if (attributes.secure) parts.push('Secure');\n if (attributes.sameSite) parts.push(`SameSite=${attributes.sameSite}`);\n return parts.join('; ');\n};\n\n// ============================================================================\n// Client-specific types and functions\n// (cookies via browser APIs, localStorage, sessionStorage — no headers)\n// ============================================================================\n\nexport type LocaleStorageClientOptions = {\n overwrite?: boolean;\n isCookieEnabled?: boolean;\n setCookieStore?: (\n name: string,\n value: string,\n cookie: CookieBuildAttributes\n ) => void;\n setCookieString?: (name: string, cookie: string) => void;\n getCookie?: (name: string) => string | undefined | null;\n setSessionStorage?: (name: string, value: string) => void;\n getSessionStorage?: (name: string) => string | undefined | null;\n setLocaleStorage?: (name: string, value: string) => void;\n getLocaleStorage?: (name: string) => string | undefined | null;\n};\n\n/**\n * Retrieves the locale from browser storage mechanisms\n * (cookies, localStorage, sessionStorage).\n * Does not read from headers — use `getLocaleFromStorageServer` for that.\n */\nexport const getLocaleFromStorageClient = (\n options: LocaleStorageClientOptions\n): Locale | undefined => {\n const { routing, internationalization } = configuration;\n const { locales } = internationalization;\n const storageAttributes = routing.storage;\n\n if (options?.isCookieEnabled === false) return undefined;\n\n const isValidLocale = (value: string | null | undefined): value is Locale =>\n !!value && locales.includes(value as Locale);\n\n for (let i = 0; i < storageAttributes.cookies.length; i++) {\n try {\n const value = options?.getCookie?.(storageAttributes.cookies[i].name);\n if (isValidLocale(value)) return value;\n } catch {}\n }\n\n for (let i = 0; i < storageAttributes.localStorage.length; i++) {\n try {\n const value = options?.getLocaleStorage?.(\n storageAttributes.localStorage[i].name\n );\n if (isValidLocale(value)) return value;\n } catch {}\n }\n\n for (let i = 0; i < storageAttributes.sessionStorage.length; i++) {\n try {\n const value = options?.getSessionStorage?.(\n storageAttributes.sessionStorage[i].name\n );\n if (isValidLocale(value)) return value;\n } catch {}\n }\n};\n\n/**\n * Stores the locale in browser storage mechanisms\n * (cookies, localStorage, sessionStorage).\n * Does not write to headers — use `setLocaleInStorageServer` for that.\n */\nexport const setLocaleInStorageClient = (\n locale: LocalesValues,\n options?: LocaleStorageClientOptions\n): void => {\n const { routing } = configuration;\n const storageAttributes = routing.storage;\n\n if (options?.isCookieEnabled === false) return;\n\n for (let i = 0; i < storageAttributes.cookies.length; i++) {\n const { name, attributes } = storageAttributes.cookies[i];\n try {\n if (options?.setCookieStore) {\n options.setCookieStore(name, locale, {\n ...attributes,\n expires:\n attributes.expires instanceof Date\n ? attributes.expires.getTime()\n : attributes.expires,\n });\n }\n } catch {\n try {\n if (options?.setCookieString) {\n options.setCookieString(\n name,\n buildCookieString(name, locale, attributes)\n );\n }\n } catch {}\n }\n }\n\n if (options?.setLocaleStorage) {\n for (let i = 0; i < storageAttributes.localStorage.length; i++) {\n const { name } = storageAttributes.localStorage[i];\n try {\n if (!(options?.overwrite ?? true) && options?.getLocaleStorage) {\n if (options.getLocaleStorage(name)) continue;\n }\n options.setLocaleStorage(name, locale);\n } catch {}\n }\n }\n\n if (options?.setSessionStorage) {\n for (let i = 0; i < storageAttributes.sessionStorage.length; i++) {\n const { name } = storageAttributes.sessionStorage[i];\n try {\n if (!(options?.overwrite ?? true) && options?.getSessionStorage) {\n if (options.getSessionStorage(name)) continue;\n }\n options.setSessionStorage(name, locale);\n } catch {}\n }\n }\n};\n\n/**\n * Client-side locale storage utility.\n * Handles cookies (browser), localStorage and sessionStorage.\n * Does not access headers.\n *\n * @example\n * ```ts\n * const storage = LocaleStorageClient(localeStorageOptions);\n * const locale = storage.getLocale();\n * storage.setLocale('fr');\n * ```\n */\nexport const LocaleStorageClient = (options: LocaleStorageClientOptions) => ({\n getLocale: () => getLocaleFromStorageClient(options),\n setLocale: (locale: LocalesValues) =>\n setLocaleInStorageClient(locale, options),\n});\n\n// ============================================================================\n// Server-specific types and functions\n// (cookies via injected getter/setter, headers — no localStorage/sessionStorage)\n// ============================================================================\n\nexport type LocaleStorageServerOptions = {\n overwrite?: boolean;\n isCookieEnabled?: boolean;\n setCookieStore?: (\n name: string,\n value: string,\n cookie: CookieBuildAttributes\n ) => void;\n setCookieString?: (name: string, cookie: string) => void;\n getCookie?: (name: string) => string | undefined | null;\n getHeader?: (name: string) => string | undefined | null;\n setHeader?: (name: string, value: string) => void;\n};\n\n/**\n * Retrieves the locale from server-side storage mechanisms (cookies, headers).\n * Does not access localStorage or sessionStorage.\n * No browser cookie fallback — the caller must provide `getCookie`.\n */\nexport const getLocaleFromStorageServer = (\n options: LocaleStorageServerOptions\n): Locale | undefined => {\n const { routing, internationalization } = configuration;\n const { locales } = internationalization;\n const storageAttributes = routing.storage;\n\n if (options?.isCookieEnabled === false) return undefined;\n\n const isValidLocale = (value: string | null | undefined): value is Locale =>\n !!value && locales.includes(value as Locale);\n\n for (let i = 0; i < storageAttributes.cookies.length; i++) {\n try {\n const value = options?.getCookie?.(storageAttributes.cookies[i].name);\n if (isValidLocale(value)) return value;\n } catch {}\n }\n\n for (let i = 0; i < storageAttributes.headers.length; i++) {\n try {\n const value = options?.getHeader?.(storageAttributes.headers[i].name);\n if (isValidLocale(value)) return value;\n } catch {}\n }\n};\n\n/**\n * Stores the locale in server-side storage mechanisms (cookies, headers).\n * Does not write to localStorage or sessionStorage.\n */\nexport const setLocaleInStorageServer = (\n locale: LocalesValues,\n options?: LocaleStorageServerOptions\n): void => {\n const { routing } = configuration;\n const storageAttributes = routing.storage;\n\n if (options?.isCookieEnabled === false) return;\n\n for (let i = 0; i < storageAttributes.cookies.length; i++) {\n const { name, attributes } = storageAttributes.cookies[i];\n try {\n if (options?.setCookieStore) {\n options.setCookieStore(name, locale, {\n ...attributes,\n expires:\n attributes.expires instanceof Date\n ? attributes.expires.getTime()\n : attributes.expires,\n });\n }\n } catch {\n try {\n if (options?.setCookieString) {\n options.setCookieString(\n name,\n buildCookieString(name, locale, attributes)\n );\n }\n } catch {}\n }\n }\n\n if (options?.setHeader) {\n for (let i = 0; i < storageAttributes.headers.length; i++) {\n try {\n options.setHeader(storageAttributes.headers[i].name, locale);\n } catch {}\n }\n }\n};\n\n/**\n * Server-side locale storage utility.\n * Handles cookies (via injected getter/setter) and headers.\n * Does not access localStorage or sessionStorage.\n *\n * @example\n * ```ts\n * const storage = LocaleStorageServer({\n * getCookie: (name) => req.cookies[name],\n * setCookieStore: (name, value, attrs) => res.cookie(name, value, attrs),\n * getHeader: (name) => req.headers[name],\n * setHeader: (name, value) => res.setHeader(name, value),\n * });\n * const locale = storage.getLocale();\n * storage.setLocale('fr');\n * ```\n */\nexport const LocaleStorageServer = (options: LocaleStorageServerOptions) => ({\n getLocale: () => getLocaleFromStorageServer(options),\n setLocale: (locale: LocalesValues) =>\n setLocaleInStorageServer(locale, options),\n});\n\n// ============================================================================\n// Deprecated: combined LocaleStorage\n// Use LocaleStorageClient or LocaleStorageServer instead\n// ============================================================================\n\n/**\n * @deprecated Use {@link LocaleStorageClientOptions} or {@link LocaleStorageServerOptions} instead.\n */\nexport type LocaleStorageOptions = LocaleStorageClientOptions &\n LocaleStorageServerOptions;\n\n/**\n * Retrieves the locale from all storage mechanisms\n * (cookies, localStorage, sessionStorage, headers).\n *\n * @deprecated Use {@link getLocaleFromStorageClient} (browser) or\n * {@link getLocaleFromStorageServer} (server) instead.\n */\nexport const getLocaleFromStorage = (\n options: Pick<\n LocaleStorageOptions,\n | 'getCookie'\n | 'getSessionStorage'\n | 'getLocaleStorage'\n | 'getHeader'\n | 'isCookieEnabled'\n >\n): Locale | undefined => {\n const { routing, internationalization } = configuration;\n const { locales } = internationalization;\n const storageAttributes = routing.storage;\n\n if (options?.isCookieEnabled === false) return undefined;\n\n const isValidLocale = (value: string | null | undefined): value is Locale =>\n !!value && locales.includes(value as Locale);\n\n const readCookie = (name: string): string | undefined => {\n try {\n const fromOption = options?.getCookie?.(name);\n if (fromOption !== null && fromOption !== undefined) return fromOption;\n } catch {}\n // Browser fallback kept for backward compatibility\n return getCookie(name);\n };\n\n for (let i = 0; i < storageAttributes.cookies.length; i++) {\n const value = readCookie(storageAttributes.cookies[i].name);\n if (isValidLocale(value)) return value;\n }\n\n for (let i = 0; i < storageAttributes.localStorage.length; i++) {\n try {\n const value = options?.getLocaleStorage?.(\n storageAttributes.localStorage[i].name\n );\n if (isValidLocale(value)) return value;\n } catch {}\n }\n\n for (let i = 0; i < storageAttributes.sessionStorage.length; i++) {\n try {\n const value = options?.getSessionStorage?.(\n storageAttributes.sessionStorage[i].name\n );\n if (isValidLocale(value)) return value;\n } catch {}\n }\n\n for (let i = 0; i < storageAttributes.headers.length; i++) {\n try {\n const value = options?.getHeader?.(storageAttributes.headers[i].name);\n if (isValidLocale(value)) return value;\n } catch {}\n }\n};\n\n/**\n * Stores the locale in all configured storage mechanisms\n * (cookies, localStorage, sessionStorage, headers).\n *\n * @deprecated Use {@link setLocaleInStorageClient} (browser) or\n * {@link setLocaleInStorageServer} (server) instead.\n */\nexport const setLocaleInStorage = (\n locale: LocalesValues,\n options?: LocaleStorageOptions\n): void => {\n const { routing } = configuration;\n const storageAttributes = routing.storage;\n\n if (options?.isCookieEnabled === false) return;\n\n for (let i = 0; i < storageAttributes.cookies.length; i++) {\n const { name, attributes } = storageAttributes.cookies[i];\n try {\n if (options?.setCookieStore) {\n options.setCookieStore(name, locale, {\n ...attributes,\n expires:\n attributes.expires instanceof Date\n ? attributes.expires.getTime()\n : attributes.expires,\n });\n }\n } catch {\n try {\n if (options?.setCookieString) {\n options.setCookieString(\n name,\n buildCookieString(name, locale, attributes)\n );\n }\n } catch {}\n }\n }\n\n if (options?.setLocaleStorage) {\n for (let i = 0; i < storageAttributes.localStorage.length; i++) {\n const { name } = storageAttributes.localStorage[i];\n try {\n if (!(options?.overwrite ?? true) && options?.getLocaleStorage) {\n if (options.getLocaleStorage(name)) continue;\n }\n options.setLocaleStorage(name, locale);\n } catch {}\n }\n }\n\n if (options?.setSessionStorage) {\n for (let i = 0; i < storageAttributes.sessionStorage.length; i++) {\n const { name } = storageAttributes.sessionStorage[i];\n try {\n if (!(options?.overwrite ?? true) && options?.getSessionStorage) {\n if (options.getSessionStorage(name)) continue;\n }\n options.setSessionStorage(name, locale);\n } catch {}\n }\n }\n\n if (options?.setHeader) {\n for (let i = 0; i < storageAttributes.headers.length; i++) {\n try {\n options.setHeader(storageAttributes.headers[i].name, locale);\n } catch {}\n }\n }\n};\n\n/**\n * Utility object to get and set the locale in storage based on configuration.\n *\n * @deprecated Use {@link LocaleStorageClient} (browser) or\n * {@link LocaleStorageServer} (server) instead.\n */\nexport const LocaleStorage = (options: LocaleStorageOptions) => ({\n getLocale: () => getLocaleFromStorage(options),\n setLocale: (locale: LocalesValues) => setLocaleInStorage(locale, options),\n});\n"],"mappings":"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"}