@intlayer/core 8.12.5-canary.0 → 9.0.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 (177) hide show
  1. package/dist/cjs/deepTransformPlugins/getEditedContent.cjs +96 -0
  2. package/dist/cjs/deepTransformPlugins/getEditedContent.cjs.map +1 -0
  3. package/dist/cjs/deepTransformPlugins/index.cjs +3 -0
  4. package/dist/cjs/dictionaryManipulator/index.cjs +18 -0
  5. package/dist/cjs/dictionaryManipulator/mergeDictionaries.cjs +4 -1
  6. package/dist/cjs/dictionaryManipulator/mergeDictionaries.cjs.map +1 -1
  7. package/dist/cjs/dictionaryManipulator/mergeQualifiedDictionaries.cjs +70 -0
  8. package/dist/cjs/dictionaryManipulator/mergeQualifiedDictionaries.cjs.map +1 -0
  9. package/dist/cjs/dictionaryManipulator/qualifiedDictionary.cjs +302 -0
  10. package/dist/cjs/dictionaryManipulator/qualifiedDictionary.cjs.map +1 -0
  11. package/dist/cjs/index.cjs +26 -0
  12. package/dist/cjs/interpreter/getCollection.cjs +25 -0
  13. package/dist/cjs/interpreter/getCollection.cjs.map +1 -0
  14. package/dist/cjs/interpreter/getDictionary.cjs +26 -11
  15. package/dist/cjs/interpreter/getDictionary.cjs.map +1 -1
  16. package/dist/cjs/interpreter/getIntlayer.cjs +16 -3
  17. package/dist/cjs/interpreter/getIntlayer.cjs.map +1 -1
  18. package/dist/cjs/interpreter/getVariant.cjs +30 -0
  19. package/dist/cjs/interpreter/getVariant.cjs.map +1 -0
  20. package/dist/cjs/localization/getBrowserLocale.cjs +1 -1
  21. package/dist/cjs/localization/getBrowserLocale.cjs.map +1 -1
  22. package/dist/cjs/messageFormat/ICU.cjs +32 -2
  23. package/dist/cjs/messageFormat/ICU.cjs.map +1 -1
  24. package/dist/cjs/messageFormat/i18next.cjs +1 -1
  25. package/dist/cjs/messageFormat/i18next.cjs.map +1 -1
  26. package/dist/cjs/messageFormat/index.cjs +5 -0
  27. package/dist/cjs/messageFormat/resolveMessage.cjs +183 -0
  28. package/dist/cjs/messageFormat/resolveMessage.cjs.map +1 -0
  29. package/dist/cjs/messageFormat/vue-i18n.cjs +8 -9
  30. package/dist/cjs/messageFormat/vue-i18n.cjs.map +1 -1
  31. package/dist/cjs/transpiler/collection/collection.cjs +32 -0
  32. package/dist/cjs/transpiler/collection/collection.cjs.map +1 -0
  33. package/dist/cjs/transpiler/collection/index.cjs +4 -0
  34. package/dist/cjs/transpiler/markdown/getMarkdownMetadata.cjs +2 -3
  35. package/dist/cjs/transpiler/markdown/getMarkdownMetadata.cjs.map +1 -1
  36. package/dist/cjs/transpiler/variant/index.cjs +4 -0
  37. package/dist/cjs/transpiler/variant/variant.cjs +35 -0
  38. package/dist/cjs/transpiler/variant/variant.cjs.map +1 -0
  39. package/dist/cjs/utils/parseYaml.cjs +37 -17
  40. package/dist/cjs/utils/parseYaml.cjs.map +1 -1
  41. package/dist/esm/deepTransformPlugins/getEditedContent.mjs +92 -0
  42. package/dist/esm/deepTransformPlugins/getEditedContent.mjs.map +1 -0
  43. package/dist/esm/deepTransformPlugins/index.mjs +2 -1
  44. package/dist/esm/dictionaryManipulator/index.mjs +3 -1
  45. package/dist/esm/dictionaryManipulator/mergeDictionaries.mjs +4 -1
  46. package/dist/esm/dictionaryManipulator/mergeDictionaries.mjs.map +1 -1
  47. package/dist/esm/dictionaryManipulator/mergeQualifiedDictionaries.mjs +68 -0
  48. package/dist/esm/dictionaryManipulator/mergeQualifiedDictionaries.mjs.map +1 -0
  49. package/dist/esm/dictionaryManipulator/qualifiedDictionary.mjs +286 -0
  50. package/dist/esm/dictionaryManipulator/qualifiedDictionary.mjs.map +1 -0
  51. package/dist/esm/index.mjs +5 -1
  52. package/dist/esm/interpreter/getCollection.mjs +23 -0
  53. package/dist/esm/interpreter/getCollection.mjs.map +1 -0
  54. package/dist/esm/interpreter/getDictionary.mjs +26 -11
  55. package/dist/esm/interpreter/getDictionary.mjs.map +1 -1
  56. package/dist/esm/interpreter/getIntlayer.mjs +16 -3
  57. package/dist/esm/interpreter/getIntlayer.mjs.map +1 -1
  58. package/dist/esm/interpreter/getVariant.mjs +28 -0
  59. package/dist/esm/interpreter/getVariant.mjs.map +1 -0
  60. package/dist/esm/localization/getBrowserLocale.mjs +1 -1
  61. package/dist/esm/localization/getBrowserLocale.mjs.map +1 -1
  62. package/dist/esm/messageFormat/ICU.mjs +32 -2
  63. package/dist/esm/messageFormat/ICU.mjs.map +1 -1
  64. package/dist/esm/messageFormat/i18next.mjs +1 -1
  65. package/dist/esm/messageFormat/i18next.mjs.map +1 -1
  66. package/dist/esm/messageFormat/index.mjs +2 -1
  67. package/dist/esm/messageFormat/resolveMessage.mjs +177 -0
  68. package/dist/esm/messageFormat/resolveMessage.mjs.map +1 -0
  69. package/dist/esm/messageFormat/vue-i18n.mjs +8 -9
  70. package/dist/esm/messageFormat/vue-i18n.mjs.map +1 -1
  71. package/dist/esm/transpiler/collection/collection.mjs +30 -0
  72. package/dist/esm/transpiler/collection/collection.mjs.map +1 -0
  73. package/dist/esm/transpiler/collection/index.mjs +3 -0
  74. package/dist/esm/transpiler/markdown/getMarkdownMetadata.mjs +2 -3
  75. package/dist/esm/transpiler/markdown/getMarkdownMetadata.mjs.map +1 -1
  76. package/dist/esm/transpiler/variant/index.mjs +3 -0
  77. package/dist/esm/transpiler/variant/variant.mjs +33 -0
  78. package/dist/esm/transpiler/variant/variant.mjs.map +1 -0
  79. package/dist/esm/utils/parseYaml.mjs +37 -17
  80. package/dist/esm/utils/parseYaml.mjs.map +1 -1
  81. package/dist/types/@intlayer/core/dist/types/formatters/compact.d.ts +1 -0
  82. package/dist/types/@intlayer/core/dist/types/formatters/currency.d.ts +1 -0
  83. package/dist/types/@intlayer/core/dist/types/formatters/date.d.ts +1 -0
  84. package/dist/types/@intlayer/core/dist/types/formatters/index.d.ts +1 -0
  85. package/dist/types/@intlayer/core/dist/types/formatters/list.d.ts +1 -0
  86. package/dist/types/@intlayer/core/dist/types/formatters/number.d.ts +1 -0
  87. package/dist/types/@intlayer/core/dist/types/formatters/percentage.d.ts +1 -0
  88. package/dist/types/@intlayer/core/dist/types/formatters/relativeTime.d.ts +1 -0
  89. package/dist/types/@intlayer/core/dist/types/formatters/units.d.ts +1 -0
  90. package/dist/types/@intlayer/core/dist/types/interpreter/getCondition.d.ts +1 -0
  91. package/dist/types/@intlayer/core/dist/types/interpreter/getContent/deepTransform.d.ts +1 -0
  92. package/dist/types/@intlayer/core/dist/types/interpreter/getContent/getContent.d.ts +2 -0
  93. package/dist/types/@intlayer/core/dist/types/interpreter/getContent/plugins.d.ts +4 -0
  94. package/dist/types/@intlayer/core/dist/types/interpreter/getDictionary.d.ts +2 -0
  95. package/dist/types/@intlayer/core/dist/types/interpreter/getEnumeration.d.ts +1 -0
  96. package/dist/types/@intlayer/core/dist/types/interpreter/getIntlayer.d.ts +2 -0
  97. package/dist/types/@intlayer/core/dist/types/interpreter/getNesting.d.ts +2 -0
  98. package/dist/types/@intlayer/core/dist/types/interpreter/getPlural.d.ts +1 -0
  99. package/dist/types/@intlayer/core/dist/types/interpreter/getTranslation.d.ts +1 -0
  100. package/dist/types/@intlayer/core/dist/types/interpreter/getVariant.d.ts +1 -0
  101. package/dist/types/@intlayer/core/dist/types/interpreter/index.d.ts +1 -0
  102. package/dist/types/@intlayer/core/dist/types/intlayer/dist/types/index.d.ts +1 -0
  103. package/dist/types/@intlayer/core/dist/types/localization/generateSitemap.d.ts +2 -0
  104. package/dist/types/@intlayer/core/dist/types/localization/getBrowserLocale.d.ts +1 -0
  105. package/dist/types/@intlayer/core/dist/types/localization/getHTMLTextDir.d.ts +1 -0
  106. package/dist/types/@intlayer/core/dist/types/localization/getLocale.d.ts +1 -0
  107. package/dist/types/@intlayer/core/dist/types/localization/getLocaleFromPath.d.ts +1 -0
  108. package/dist/types/@intlayer/core/dist/types/localization/getLocaleLang.d.ts +1 -0
  109. package/dist/types/@intlayer/core/dist/types/localization/getLocaleName.d.ts +1 -0
  110. package/dist/types/@intlayer/core/dist/types/localization/getLocalizedUrl.d.ts +1 -0
  111. package/dist/types/@intlayer/core/dist/types/localization/getMultilingualUrls.d.ts +1 -0
  112. package/dist/types/@intlayer/core/dist/types/localization/getPathWithoutLocale.d.ts +1 -0
  113. package/dist/types/@intlayer/core/dist/types/localization/getPrefix.d.ts +3 -0
  114. package/dist/types/@intlayer/core/dist/types/localization/index.d.ts +1 -0
  115. package/dist/types/@intlayer/core/dist/types/localization/localeDetector.d.ts +1 -0
  116. package/dist/types/@intlayer/core/dist/types/localization/localeMapper.d.ts +2 -0
  117. package/dist/types/@intlayer/core/dist/types/localization/localeResolver.d.ts +2 -0
  118. package/dist/types/@intlayer/core/dist/types/localization/rewriteUtils.d.ts +3 -0
  119. package/dist/types/@intlayer/core/dist/types/localization/validatePrefix.d.ts +1 -0
  120. package/dist/types/@intlayer/core/dist/types/markdown/index.d.ts +1 -0
  121. package/dist/types/@intlayer/core/dist/types/transpiler/collection/collection.d.ts +1 -0
  122. package/dist/types/@intlayer/core/dist/types/transpiler/condition/condition.d.ts +1 -0
  123. package/dist/types/@intlayer/core/dist/types/transpiler/enumeration/enumeration.d.ts +1 -0
  124. package/dist/types/@intlayer/core/dist/types/transpiler/file/file.d.ts +1 -0
  125. package/dist/types/@intlayer/core/dist/types/transpiler/gender/gender.d.ts +1 -0
  126. package/dist/types/@intlayer/core/dist/types/transpiler/html/html.d.ts +1 -0
  127. package/dist/types/@intlayer/core/dist/types/transpiler/index.d.ts +1 -0
  128. package/dist/types/@intlayer/core/dist/types/transpiler/insertion/insertion.d.ts +1 -0
  129. package/dist/types/@intlayer/core/dist/types/transpiler/markdown/markdown.d.ts +1 -0
  130. package/dist/types/@intlayer/core/dist/types/transpiler/nesting/nesting.d.ts +2 -0
  131. package/dist/types/@intlayer/core/dist/types/transpiler/plural/plural.d.ts +1 -0
  132. package/dist/types/@intlayer/core/dist/types/transpiler/translation/translation.d.ts +2 -0
  133. package/dist/types/@intlayer/core/dist/types/transpiler/variant/variant.d.ts +1 -0
  134. package/dist/types/@intlayer/core/dist/types/utils/index.d.ts +1 -0
  135. package/dist/types/@intlayer/core/dist/types/utils/intl.d.ts +1 -0
  136. package/dist/types/@intlayer/core/dist/types/utils/isSameKeyPath.d.ts +1 -0
  137. package/dist/types/@intlayer/core/dist/types/utils/localeStorage.d.ts +2 -0
  138. package/dist/types/deepTransformPlugins/getEditedContent.d.ts +33 -0
  139. package/dist/types/deepTransformPlugins/getEditedContent.d.ts.map +1 -0
  140. package/dist/types/deepTransformPlugins/getFilterMissingTranslationsContent.d.ts +6 -3
  141. package/dist/types/deepTransformPlugins/getFilterMissingTranslationsContent.d.ts.map +1 -1
  142. package/dist/types/deepTransformPlugins/getFilterTranslationsOnlyContent.d.ts +6 -3
  143. package/dist/types/deepTransformPlugins/getFilterTranslationsOnlyContent.d.ts.map +1 -1
  144. package/dist/types/deepTransformPlugins/getFilteredLocalesContent.d.ts +4 -1
  145. package/dist/types/deepTransformPlugins/index.d.ts +2 -1
  146. package/dist/types/dictionaryManipulator/index.d.ts +3 -1
  147. package/dist/types/dictionaryManipulator/mergeQualifiedDictionaries.d.ts +22 -0
  148. package/dist/types/dictionaryManipulator/mergeQualifiedDictionaries.d.ts.map +1 -0
  149. package/dist/types/dictionaryManipulator/qualifiedDictionary.d.ts +176 -0
  150. package/dist/types/dictionaryManipulator/qualifiedDictionary.d.ts.map +1 -0
  151. package/dist/types/index.d.ts +6 -2
  152. package/dist/types/interpreter/getCollection.d.ts +19 -0
  153. package/dist/types/interpreter/getCollection.d.ts.map +1 -0
  154. package/dist/types/interpreter/getDictionary.d.ts +13 -7
  155. package/dist/types/interpreter/getDictionary.d.ts.map +1 -1
  156. package/dist/types/interpreter/getIntlayer.d.ts +13 -2
  157. package/dist/types/interpreter/getIntlayer.d.ts.map +1 -1
  158. package/dist/types/interpreter/getPlural.d.ts +1 -1
  159. package/dist/types/interpreter/getVariant.d.ts +26 -0
  160. package/dist/types/interpreter/getVariant.d.ts.map +1 -0
  161. package/dist/types/intlayer/dist/types/index.d.ts +4 -0
  162. package/dist/types/messageFormat/ICU.d.ts.map +1 -1
  163. package/dist/types/messageFormat/i18next.d.ts.map +1 -1
  164. package/dist/types/messageFormat/index.d.ts +2 -1
  165. package/dist/types/messageFormat/resolveMessage.d.ts +72 -0
  166. package/dist/types/messageFormat/resolveMessage.d.ts.map +1 -0
  167. package/dist/types/messageFormat/vue-i18n.d.ts.map +1 -1
  168. package/dist/types/transpiler/collection/collection.d.ts +34 -0
  169. package/dist/types/transpiler/collection/collection.d.ts.map +1 -0
  170. package/dist/types/transpiler/collection/index.d.ts +2 -0
  171. package/dist/types/transpiler/variant/index.d.ts +2 -0
  172. package/dist/types/transpiler/variant/variant.d.ts +43 -0
  173. package/dist/types/transpiler/variant/variant.d.ts.map +1 -0
  174. package/dist/types/utils/index.d.ts +2 -2
  175. package/dist/types/utils/parseYaml.d.ts +12 -2
  176. package/dist/types/utils/parseYaml.d.ts.map +1 -1
  177. package/package.json +7 -7
@@ -0,0 +1,68 @@
1
+ import { QUALIFIER_ORDER, getDictionaryCompositeId, getDictionaryQualifierTypes } from "./qualifiedDictionary.mjs";
2
+ import { mergeDictionaries } from "./mergeDictionaries.mjs";
3
+ import { log } from "@intlayer/config/built";
4
+ import { colorizeKey, getAppLogger } from "@intlayer/config/logger";
5
+
6
+ //#region src/dictionaryManipulator/mergeQualifiedDictionaries.ts
7
+ /**
8
+ * Merges sibling dictionaries sharing the same key, honouring qualifiers.
9
+ *
10
+ * - No dictionary declares a qualifier → behaves exactly like
11
+ * `mergeDictionaries` (single merged dictionary).
12
+ * - At least one dictionary declares a qualifier → the group's dimension set is
13
+ * the union of every declared dimension (in canonical order
14
+ * `variant → meta → item`). Dictionaries are grouped by their composite id
15
+ * (one segment per dimension), merged within each group (locale completion /
16
+ * priority overrides preserved), and a `QualifiedDictionaryGroup` is returned.
17
+ * Unqualified siblings act as shared base content merged into every entry.
18
+ *
19
+ * Every qualified entry must declare ALL dimensions of the group; an entry that
20
+ * declares only a subset is ambiguous and is rejected with an error log.
21
+ */
22
+ const mergeQualifiedDictionaries = (dictionaries) => {
23
+ const perDictionaryTypes = dictionaries.map(getDictionaryQualifierTypes);
24
+ const declaredDimensions = /* @__PURE__ */ new Set();
25
+ for (const types of perDictionaryTypes) for (const type of types) declaredDimensions.add(type);
26
+ const groupQualifierTypes = QUALIFIER_ORDER.filter((qualifierType) => declaredDimensions.has(qualifierType));
27
+ if (groupQualifierTypes.length === 0) return mergeDictionaries(dictionaries);
28
+ const appLogger = getAppLogger({ log });
29
+ const baseDictionaries = [];
30
+ const entriesDictionaries = /* @__PURE__ */ new Map();
31
+ dictionaries.forEach((dictionary, index) => {
32
+ if (perDictionaryTypes[index].length === 0) {
33
+ baseDictionaries.push(dictionary);
34
+ return;
35
+ }
36
+ const compositeId = getDictionaryCompositeId(dictionary, groupQualifierTypes);
37
+ if (compositeId === void 0) {
38
+ appLogger(`Dictionary ${colorizeKey(dictionary.key)} declares (${perDictionaryTypes[index].join(", ")}) but the key's dimensions are (${groupQualifierTypes.join(", ")}); every entry must declare all of them. Entry ignored${dictionary.filePath ? ` - ${dictionary.filePath}` : ""}.`, { level: "error" });
39
+ return;
40
+ }
41
+ const existingEntries = entriesDictionaries.get(compositeId) ?? [];
42
+ existingEntries.push(dictionary);
43
+ entriesDictionaries.set(compositeId, existingEntries);
44
+ });
45
+ const content = {};
46
+ const metaByCompositeId = {};
47
+ const declaresMeta = groupQualifierTypes.includes("meta");
48
+ let importMode;
49
+ for (const [compositeId, qualifiedDictionaries] of entriesDictionaries) {
50
+ content[compositeId] = mergeDictionaries([...qualifiedDictionaries, ...baseDictionaries]).content;
51
+ const [firstQualified] = qualifiedDictionaries;
52
+ if (declaresMeta && firstQualified?.meta !== void 0) metaByCompositeId[compositeId] = firstQualified.meta;
53
+ importMode ??= firstQualified?.importMode;
54
+ }
55
+ const localIds = Array.from(new Set(dictionaries.filter((dictionary) => dictionary.localId).map((dictionary) => dictionary.localId)));
56
+ return {
57
+ key: dictionaries[0].key,
58
+ qualifierTypes: groupQualifierTypes,
59
+ content,
60
+ ...declaresMeta && { meta: metaByCompositeId },
61
+ ...importMode !== void 0 && { importMode },
62
+ localIds
63
+ };
64
+ };
65
+
66
+ //#endregion
67
+ export { mergeQualifiedDictionaries };
68
+ //# sourceMappingURL=mergeQualifiedDictionaries.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mergeQualifiedDictionaries.mjs","names":[],"sources":["../../../src/dictionaryManipulator/mergeQualifiedDictionaries.ts"],"sourcesContent":["import { log } from '@intlayer/config/built';\nimport { colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport type {\n Dictionary,\n DictionaryMeta,\n DictionaryQualifierType,\n QualifiedDictionaryGroup,\n} from '@intlayer/types/dictionary';\nimport { mergeDictionaries } from './mergeDictionaries';\nimport {\n getDictionaryCompositeId,\n getDictionaryQualifierTypes,\n QUALIFIER_ORDER,\n} from './qualifiedDictionary';\n\n/**\n * Merges sibling dictionaries sharing the same key, honouring qualifiers.\n *\n * - No dictionary declares a qualifier → behaves exactly like\n * `mergeDictionaries` (single merged dictionary).\n * - At least one dictionary declares a qualifier → the group's dimension set is\n * the union of every declared dimension (in canonical order\n * `variant → meta → item`). Dictionaries are grouped by their composite id\n * (one segment per dimension), merged within each group (locale completion /\n * priority overrides preserved), and a `QualifiedDictionaryGroup` is returned.\n * Unqualified siblings act as shared base content merged into every entry.\n *\n * Every qualified entry must declare ALL dimensions of the group; an entry that\n * declares only a subset is ambiguous and is rejected with an error log.\n */\nexport const mergeQualifiedDictionaries = (\n dictionaries: Dictionary[]\n): Dictionary | QualifiedDictionaryGroup => {\n const perDictionaryTypes = dictionaries.map(getDictionaryQualifierTypes);\n\n const declaredDimensions = new Set<DictionaryQualifierType>();\n for (const types of perDictionaryTypes) {\n for (const type of types) declaredDimensions.add(type);\n }\n\n // Canonical order, restricted to the dimensions actually declared.\n const groupQualifierTypes = QUALIFIER_ORDER.filter((qualifierType) =>\n declaredDimensions.has(qualifierType)\n );\n\n if (groupQualifierTypes.length === 0) {\n return mergeDictionaries(dictionaries);\n }\n\n const appLogger = getAppLogger({ log });\n\n const baseDictionaries: Dictionary[] = [];\n const entriesDictionaries = new Map<string, Dictionary[]>();\n\n dictionaries.forEach((dictionary, index) => {\n if (perDictionaryTypes[index].length === 0) {\n baseDictionaries.push(dictionary);\n return;\n }\n\n const compositeId = getDictionaryCompositeId(\n dictionary,\n groupQualifierTypes\n );\n\n if (compositeId === undefined) {\n appLogger(\n `Dictionary ${colorizeKey(dictionary.key)} declares (${perDictionaryTypes[index].join(', ')}) but the key's dimensions are (${groupQualifierTypes.join(', ')}); every entry must declare all of them. Entry ignored${dictionary.filePath ? ` - ${dictionary.filePath}` : ''}.`,\n { level: 'error' }\n );\n return;\n }\n\n const existingEntries = entriesDictionaries.get(compositeId) ?? [];\n existingEntries.push(dictionary);\n entriesDictionaries.set(compositeId, existingEntries);\n });\n\n // `content` maps each composite id to its resolved content node directly; the\n // qualifier coordinates live in the key, not in a per-entry wrapper. `meta`\n // preserves the declared meta fields (composite id only encodes `meta.id`).\n const content: Record<string, unknown> = {};\n const metaByCompositeId: Record<string, DictionaryMeta> = {};\n const declaresMeta = groupQualifierTypes.includes('meta');\n\n let importMode: Dictionary['importMode'];\n\n for (const [compositeId, qualifiedDictionaries] of entriesDictionaries) {\n // Unqualified siblings act as shared base content: appended last so the\n // qualified entries take precedence (mergeDictionaries prefers first).\n const mergedEntry = mergeDictionaries([\n ...qualifiedDictionaries,\n ...baseDictionaries,\n ]);\n\n content[compositeId] = mergedEntry.content;\n\n const [firstQualified] = qualifiedDictionaries;\n\n if (declaresMeta && firstQualified?.meta !== undefined) {\n metaByCompositeId[compositeId] = firstQualified.meta;\n }\n\n importMode ??= firstQualified?.importMode;\n }\n\n const localIds = Array.from(\n new Set(\n dictionaries\n .filter((dictionary) => dictionary.localId)\n .map((dictionary) => dictionary.localId!)\n )\n );\n\n return {\n key: dictionaries[0]!.key,\n qualifierTypes: groupQualifierTypes,\n content,\n ...(declaresMeta && { meta: metaByCompositeId }),\n ...(importMode !== undefined && { importMode }),\n localIds,\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,8BACX,iBAC0C;CAC1C,MAAM,qBAAqB,aAAa,IAAI,4BAA4B;CAExE,MAAM,qCAAqB,IAAI,KAA8B;AAC7D,MAAK,MAAM,SAAS,mBAClB,MAAK,MAAM,QAAQ,MAAO,oBAAmB,IAAI,KAAK;CAIxD,MAAM,sBAAsB,gBAAgB,QAAQ,kBAClD,mBAAmB,IAAI,cAAc,CACtC;AAED,KAAI,oBAAoB,WAAW,EACjC,QAAO,kBAAkB,aAAa;CAGxC,MAAM,YAAY,aAAa,EAAE,KAAK,CAAC;CAEvC,MAAM,mBAAiC,EAAE;CACzC,MAAM,sCAAsB,IAAI,KAA2B;AAE3D,cAAa,SAAS,YAAY,UAAU;AAC1C,MAAI,mBAAmB,OAAO,WAAW,GAAG;AAC1C,oBAAiB,KAAK,WAAW;AACjC;;EAGF,MAAM,cAAc,yBAClB,YACA,oBACD;AAED,MAAI,gBAAgB,QAAW;AAC7B,aACE,cAAc,YAAY,WAAW,IAAI,CAAC,aAAa,mBAAmB,OAAO,KAAK,KAAK,CAAC,kCAAkC,oBAAoB,KAAK,KAAK,CAAC,wDAAwD,WAAW,WAAW,MAAM,WAAW,aAAa,GAAG,IAC5Q,EAAE,OAAO,SAAS,CACnB;AACD;;EAGF,MAAM,kBAAkB,oBAAoB,IAAI,YAAY,IAAI,EAAE;AAClE,kBAAgB,KAAK,WAAW;AAChC,sBAAoB,IAAI,aAAa,gBAAgB;GACrD;CAKF,MAAM,UAAmC,EAAE;CAC3C,MAAM,oBAAoD,EAAE;CAC5D,MAAM,eAAe,oBAAoB,SAAS,OAAO;CAEzD,IAAI;AAEJ,MAAK,MAAM,CAAC,aAAa,0BAA0B,qBAAqB;AAQtE,UAAQ,eALY,kBAAkB,CACpC,GAAG,uBACH,GAAG,iBACJ,CAEiC,CAAC;EAEnC,MAAM,CAAC,kBAAkB;AAEzB,MAAI,gBAAgB,gBAAgB,SAAS,OAC3C,mBAAkB,eAAe,eAAe;AAGlD,iBAAe,gBAAgB;;CAGjC,MAAM,WAAW,MAAM,KACrB,IAAI,IACF,aACG,QAAQ,eAAe,WAAW,QAAQ,CAC1C,KAAK,eAAe,WAAW,QAAS,CAC5C,CACF;AAED,QAAO;EACL,KAAK,aAAa,GAAI;EACtB,gBAAgB;EAChB;EACA,GAAI,gBAAgB,EAAE,MAAM,mBAAmB;EAC/C,GAAI,eAAe,UAAa,EAAE,YAAY;EAC9C;EACD"}
@@ -0,0 +1,286 @@
1
+ //#region src/dictionaryManipulator/qualifiedDictionary.ts
2
+ /**
3
+ * Selector keys that are reserved for dictionary resolution and therefore
4
+ * excluded from meta field matching.
5
+ */
6
+ const RESERVED_SELECTOR_KEYS = [
7
+ "locale",
8
+ "item",
9
+ "variant"
10
+ ];
11
+ /**
12
+ * Canonical order of qualifier dimensions. A key that declares several
13
+ * dimensions always nests them in this order, with `item` innermost so it can
14
+ * act as the collection (array) axis.
15
+ */
16
+ const QUALIFIER_ORDER = [
17
+ "variant",
18
+ "meta",
19
+ "item"
20
+ ];
21
+ /**
22
+ * Separator joining per-dimension ids into a composite entry id. Also used as
23
+ * the chunk path separator in dynamic mode.
24
+ */
25
+ const COMPOSITE_ID_SEPARATOR = "/";
26
+ /**
27
+ * Returns the qualifier dimensions declared on a dictionary, in canonical
28
+ * order (`variant → meta → item`). Empty when the dictionary is unqualified
29
+ * (plain dictionary or shared base content of a qualified group).
30
+ */
31
+ const getDictionaryQualifierTypes = (dictionary) => {
32
+ const declaredQualifiers = [];
33
+ if (typeof dictionary.variant === "string") declaredQualifiers.push("variant");
34
+ if (dictionary.meta !== void 0) declaredQualifiers.push("meta");
35
+ if (typeof dictionary.item === "number") declaredQualifiers.push("item");
36
+ return declaredQualifiers;
37
+ };
38
+ /**
39
+ * Returns the qualifier identifier of a dictionary for the given qualifier
40
+ * dimension — one segment of the composite entry id.
41
+ *
42
+ * - 'variant' → the variant name
43
+ * - 'meta' → the `meta.id` discriminator
44
+ * - 'item' → the item index as string
45
+ */
46
+ const getDictionaryQualifierId = (dictionary, qualifierType) => {
47
+ if (qualifierType === "variant") return dictionary.variant;
48
+ if (qualifierType === "meta") {
49
+ const metaId = dictionary.meta?.id;
50
+ return metaId === void 0 ? void 0 : String(metaId);
51
+ }
52
+ return dictionary.item === void 0 ? void 0 : String(dictionary.item);
53
+ };
54
+ /**
55
+ * Returns the per-dimension id segments of a dictionary for the given ordered
56
+ * dimension set, or `undefined` when the dictionary does not declare every
57
+ * dimension of the set.
58
+ */
59
+ const getDictionaryQualifierSegments = (dictionary, qualifierTypes) => {
60
+ const segments = [];
61
+ for (const qualifierType of qualifierTypes) {
62
+ const id = getDictionaryQualifierId(dictionary, qualifierType);
63
+ if (id === void 0) return void 0;
64
+ segments.push(id);
65
+ }
66
+ return segments;
67
+ };
68
+ /**
69
+ * Builds the composite entry id of a dictionary — its per-dimension id segments
70
+ * joined in canonical order. `undefined` when a dimension is missing.
71
+ */
72
+ const getDictionaryCompositeId = (dictionary, qualifierTypes) => getDictionaryQualifierSegments(dictionary, qualifierTypes)?.join("/");
73
+ /**
74
+ * Checks that every declared meta field is provided and equal in the selector.
75
+ * Reserved keys (`locale`, `item`, `variant`) are skipped; `meta.id` is part of
76
+ * the equality check.
77
+ */
78
+ const metaFieldsMatch = (meta, selector) => {
79
+ if (!meta) return false;
80
+ return Object.entries(meta).every(([metaField, declaredValue]) => {
81
+ if (RESERVED_SELECTOR_KEYS.includes(metaField)) return true;
82
+ const providedValue = selector?.[metaField];
83
+ return providedValue !== void 0 && String(providedValue) === String(declaredValue);
84
+ });
85
+ };
86
+ /**
87
+ * Tests whether a group entry matches a selector across every declared
88
+ * dimension. The `item` dimension matches any value when the selector does not
89
+ * provide one (open collection axis).
90
+ */
91
+ const entryMatchesSelector = (entry, qualifierTypes, selector) => qualifierTypes.every((qualifierType) => {
92
+ if (qualifierType === "variant") return entry.variant === (selector?.variant ?? "default");
93
+ if (qualifierType === "item") return selector?.item === void 0 || String(entry.item) === String(selector.item);
94
+ return metaFieldsMatch(entry.meta, selector);
95
+ });
96
+ /**
97
+ * Type guard discriminating a `QualifiedDictionaryGroup` (merge output of a
98
+ * qualified key) from a plain `Dictionary`. Both carry a `content` field; only
99
+ * the group declares `qualifierTypes`, which is therefore the discriminator.
100
+ */
101
+ const isQualifiedDictionaryGroup = (value) => typeof value === "object" && value !== null && "qualifierTypes" in value && Array.isArray(value.qualifierTypes) && "content" in value;
102
+ /**
103
+ * Reconstructs a resolvable {@link Dictionary} from a single entry of a
104
+ * qualified group: the content node stored under its composite id, plus the
105
+ * qualifier coordinates decoded from that id (`variant`, `item`) and the
106
+ * preserved `meta` object for the meta dimension.
107
+ *
108
+ * This keeps the resolver's matching/transform code unchanged: it still sees a
109
+ * `{ key, content, variant?, item?, meta? }` shape, even though the stored
110
+ * format no longer duplicates those fields per entry.
111
+ */
112
+ const reconstructQualifiedEntry = (group, compositeId) => {
113
+ const segments = compositeId.split("/");
114
+ const entry = {
115
+ key: group.key,
116
+ content: group.content[compositeId]
117
+ };
118
+ group.qualifierTypes.forEach((qualifierType, index) => {
119
+ if (qualifierType === "variant") entry.variant = segments[index];
120
+ else if (qualifierType === "item") entry.item = Number(segments[index]);
121
+ });
122
+ if (group.qualifierTypes.includes("meta")) {
123
+ const metaIndex = group.qualifierTypes.indexOf("meta");
124
+ entry.meta = group.meta?.[compositeId] ?? { id: segments[metaIndex] };
125
+ }
126
+ return entry;
127
+ };
128
+ /**
129
+ * Resolves a dictionary (or qualified dictionary group) against a selector,
130
+ * across every declared dimension.
131
+ *
132
+ * - Plain dictionary → returned as-is (selector ignored)
133
+ * - `item` declared but not selected → every matching entry ordered by index
134
+ * - `item` selected → the matching entry or null
135
+ * - `variant` defaults to the `default` entry when not selected
136
+ * - `meta` requires `{ id }` and every declared meta field to match
137
+ *
138
+ * Dimensions compose: e.g. a variant × item key with `{ variant: 'promo' }`
139
+ * returns every promo item as an array; adding `{ item: 2 }` narrows to one.
140
+ */
141
+ const resolveQualifiedDictionary = (dictionaryOrGroup, selector) => {
142
+ if (!isQualifiedDictionaryGroup(dictionaryOrGroup)) return dictionaryOrGroup;
143
+ const { qualifierTypes, content } = dictionaryOrGroup;
144
+ if (qualifierTypes.includes("meta") && selector?.id === void 0) return null;
145
+ const itemAxisOpen = qualifierTypes.includes("item") && selector?.item === void 0;
146
+ const matchedEntries = Object.keys(content).map((compositeId) => reconstructQualifiedEntry(dictionaryOrGroup, compositeId)).filter((entry) => entryMatchesSelector(entry, qualifierTypes, selector));
147
+ if (itemAxisOpen) return matchedEntries.sort((left, right) => (left.item ?? 0) - (right.item ?? 0));
148
+ return matchedEntries[0] ?? null;
149
+ };
150
+ /**
151
+ * Splits the second argument of `getIntlayer` / `getDictionary` into the
152
+ * effective locale and the selector object (if any).
153
+ */
154
+ const parseDictionarySelector = (localeOrSelector) => {
155
+ if (typeof localeOrSelector === "object" && localeOrSelector !== null) return {
156
+ locale: localeOrSelector.locale,
157
+ selector: localeOrSelector
158
+ };
159
+ return { locale: localeOrSelector };
160
+ };
161
+ /**
162
+ * Builds a stable string identity of a selector (excluding `locale`), suitable
163
+ * for cache keys and memoization dependencies.
164
+ */
165
+ const getDictionarySelectorCacheKey = (selector) => {
166
+ if (!selector) return "";
167
+ return Object.keys(selector).filter((selectorKey) => selectorKey !== "locale").sort().map((selectorKey) => `${selectorKey}:${String(selector[selectorKey])}`).join("|");
168
+ };
169
+ /**
170
+ * Marker property carrying the ordered qualifier dimensions on a dynamic loader
171
+ * map. Its presence distinguishes a qualified group loader map (a nested tree
172
+ * of chunks) from a plain dynamic loader map (one chunk per `locale`). Prefixed
173
+ * and unlikely to collide with a real locale code.
174
+ */
175
+ const QUALIFIER_DYNAMIC_TYPES_KEY = "__intlayerQualifierTypes";
176
+ /**
177
+ * Type guard discriminating a qualified dynamic loader map (collections /
178
+ * variants / meta records, possibly combined) from a plain dynamic loader map.
179
+ */
180
+ const isQualifiedDynamicLoaderMap = (value) => typeof value === "object" && value !== null && "__intlayerQualifierTypes" in value;
181
+ /**
182
+ * Walks the loader tree following the selector and collects the chunk loaders
183
+ * it targets — shared by the sync ({@link resolveQualifiedDynamicContent}) and
184
+ * async ({@link resolveQualifiedDynamicContentAsync}) resolvers.
185
+ */
186
+ const collectQualifiedChunks = (loaderMap, key, locale, selector) => {
187
+ const qualifierTypes = loaderMap[QUALIFIER_DYNAMIC_TYPES_KEY];
188
+ const localeTree = loaderMap[locale];
189
+ const itemAxisOpen = qualifierTypes.includes("item") && selector?.item === void 0;
190
+ if (!localeTree) return {
191
+ itemAxisOpen,
192
+ missed: true,
193
+ chunks: []
194
+ };
195
+ if (qualifierTypes.includes("meta") && selector?.id === void 0) return {
196
+ itemAxisOpen,
197
+ missed: true,
198
+ chunks: []
199
+ };
200
+ const chunks = [];
201
+ const walk = (node, dimensions, segments) => {
202
+ if (dimensions.length === 0) {
203
+ chunks.push({
204
+ cacheKey: `${key}.${locale}.${segments.join("/")}`,
205
+ loader: node
206
+ });
207
+ return true;
208
+ }
209
+ const [dimension, ...rest] = dimensions;
210
+ const tree = node;
211
+ if (dimension === "item" && selector?.item === void 0) {
212
+ for (const segment of Object.keys(tree).sort((left, right) => Number(left) - Number(right))) walk(tree[segment], rest, [...segments, segment]);
213
+ return true;
214
+ }
215
+ const segment = dimension === "variant" ? selector?.variant ?? "default" : dimension === "meta" ? String(selector?.id) : String(selector?.item);
216
+ const child = tree[segment];
217
+ if (!child) return false;
218
+ return walk(child, rest, [...segments, segment]);
219
+ };
220
+ return {
221
+ itemAxisOpen,
222
+ missed: !walk(localeTree, qualifierTypes, []),
223
+ chunks
224
+ };
225
+ };
226
+ /**
227
+ * Whether a loaded chunk satisfies the selector's meta fields (no-op unless the
228
+ * key declares a `meta` dimension).
229
+ */
230
+ const chunkMatchesMeta = (loaderMap, dictionary, selector) => !loaderMap["__intlayerQualifierTypes"].includes("meta") || metaFieldsMatch(dictionary.meta, selector);
231
+ /**
232
+ * Resolves the content of a qualified dynamic loader map against a selector,
233
+ * loading only the chunk(s) the selector actually targets.
234
+ *
235
+ * Walks the nested loader tree one dimension at a time (canonical order
236
+ * `variant → meta → item`): `variant` defaults to `default`, `meta` descends by
237
+ * `id`, and `item` either narrows to the selected index or — when no item is
238
+ * given — expands into every sibling chunk (the collection axis). Meta-equality
239
+ * is verified on the loaded chunk. Semantics mirror
240
+ * {@link resolveQualifiedDictionary} so dynamic and static modes behave alike.
241
+ *
242
+ * The Suspense mechanism is injected through `loadChunk` so the same logic
243
+ * serves both the client (suspender cache) and the server (`react.use`). Every
244
+ * targeted loader is started before the first chunk is read, so sibling chunks
245
+ * load in parallel rather than waterfalling.
246
+ *
247
+ * @param loaderMap - The qualified dynamic loader map (entry point default export).
248
+ * @param key - The dictionary key (used to build stable chunk cache keys).
249
+ * @param locale - The resolved locale to load chunks for.
250
+ * @param selector - The selector splitting the qualifier dimensions.
251
+ * @param loadChunk - Reads a started chunk promise, suspending until it resolves.
252
+ * @param transform - Turns a resolved chunk dictionary into final content.
253
+ */
254
+ const resolveQualifiedDynamicContent = (params) => {
255
+ const { loaderMap, key, locale, selector, loadChunk, transform } = params;
256
+ const { itemAxisOpen, missed, chunks } = collectQualifiedChunks(loaderMap, key, locale, selector);
257
+ if (missed) return itemAxisOpen ? [] : null;
258
+ const dictionaries = chunks.map(({ cacheKey, loader }) => loadChunk(cacheKey, loader())).filter((dictionary) => chunkMatchesMeta(loaderMap, dictionary, selector));
259
+ if (itemAxisOpen) return dictionaries.map(transform);
260
+ const [dictionary] = dictionaries;
261
+ return dictionary ? transform(dictionary) : null;
262
+ };
263
+ /**
264
+ * Async counterpart of {@link resolveQualifiedDynamicContent} for frameworks
265
+ * that load dictionaries with `await` instead of Suspense (Vue, Svelte, Lit,
266
+ * vanilla). Awaits every targeted chunk in parallel, then resolves identically.
267
+ *
268
+ * @param loaderMap - The qualified dynamic loader map.
269
+ * @param key - The dictionary key (used to build stable chunk cache keys).
270
+ * @param locale - The resolved locale to load chunks for.
271
+ * @param selector - The selector splitting the qualifier dimensions.
272
+ * @param transform - Turns a resolved chunk dictionary into final content.
273
+ */
274
+ const resolveQualifiedDynamicContentAsync = async (params) => {
275
+ const { loaderMap, key, locale, selector, transform } = params;
276
+ const { itemAxisOpen, missed, chunks } = collectQualifiedChunks(loaderMap, key, locale, selector);
277
+ if (missed) return itemAxisOpen ? [] : null;
278
+ const dictionaries = (await Promise.all(chunks.map(({ loader }) => loader()))).filter((dictionary) => chunkMatchesMeta(loaderMap, dictionary, selector));
279
+ if (itemAxisOpen) return dictionaries.map(transform);
280
+ const [dictionary] = dictionaries;
281
+ return dictionary ? transform(dictionary) : null;
282
+ };
283
+
284
+ //#endregion
285
+ export { COMPOSITE_ID_SEPARATOR, QUALIFIER_DYNAMIC_TYPES_KEY, QUALIFIER_ORDER, getDictionaryCompositeId, getDictionaryQualifierId, getDictionaryQualifierSegments, getDictionaryQualifierTypes, getDictionarySelectorCacheKey, isQualifiedDictionaryGroup, isQualifiedDynamicLoaderMap, parseDictionarySelector, reconstructQualifiedEntry, resolveQualifiedDictionary, resolveQualifiedDynamicContent, resolveQualifiedDynamicContentAsync };
286
+ //# sourceMappingURL=qualifiedDictionary.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qualifiedDictionary.mjs","names":[],"sources":["../../../src/dictionaryManipulator/qualifiedDictionary.ts"],"sourcesContent":["import type {\n Dictionary,\n DictionaryQualifierType,\n DictionarySelector,\n QualifiedDictionaryGroup,\n} from '@intlayer/types/dictionary';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\n\n/**\n * Selector keys that are reserved for dictionary resolution and therefore\n * excluded from meta field matching.\n */\nconst RESERVED_SELECTOR_KEYS = ['locale', 'item', 'variant'] as const;\n\n/**\n * Canonical order of qualifier dimensions. A key that declares several\n * dimensions always nests them in this order, with `item` innermost so it can\n * act as the collection (array) axis.\n */\nexport const QUALIFIER_ORDER = [\n 'variant',\n 'meta',\n 'item',\n] as const satisfies readonly DictionaryQualifierType[];\n\n/**\n * Separator joining per-dimension ids into a composite entry id. Also used as\n * the chunk path separator in dynamic mode.\n */\nexport const COMPOSITE_ID_SEPARATOR = '/';\n\n/**\n * Returns the qualifier dimensions declared on a dictionary, in canonical\n * order (`variant → meta → item`). Empty when the dictionary is unqualified\n * (plain dictionary or shared base content of a qualified group).\n */\nexport const getDictionaryQualifierTypes = (\n dictionary: Dictionary\n): DictionaryQualifierType[] => {\n const declaredQualifiers: DictionaryQualifierType[] = [];\n\n if (typeof dictionary.variant === 'string')\n declaredQualifiers.push('variant');\n if (dictionary.meta !== undefined) declaredQualifiers.push('meta');\n if (typeof dictionary.item === 'number') declaredQualifiers.push('item');\n\n return declaredQualifiers;\n};\n\n/**\n * Returns the qualifier identifier of a dictionary for the given qualifier\n * dimension — one segment of the composite entry id.\n *\n * - 'variant' → the variant name\n * - 'meta' → the `meta.id` discriminator\n * - 'item' → the item index as string\n */\nexport const getDictionaryQualifierId = (\n dictionary: Dictionary,\n qualifierType: DictionaryQualifierType\n): string | undefined => {\n if (qualifierType === 'variant') return dictionary.variant;\n if (qualifierType === 'meta') {\n const metaId = dictionary.meta?.id;\n return metaId === undefined ? undefined : String(metaId);\n }\n return dictionary.item === undefined ? undefined : String(dictionary.item);\n};\n\n/**\n * Returns the per-dimension id segments of a dictionary for the given ordered\n * dimension set, or `undefined` when the dictionary does not declare every\n * dimension of the set.\n */\nexport const getDictionaryQualifierSegments = (\n dictionary: Dictionary,\n qualifierTypes: DictionaryQualifierType[]\n): string[] | undefined => {\n const segments: string[] = [];\n\n for (const qualifierType of qualifierTypes) {\n const id = getDictionaryQualifierId(dictionary, qualifierType);\n if (id === undefined) return undefined;\n segments.push(id);\n }\n\n return segments;\n};\n\n/**\n * Builds the composite entry id of a dictionary — its per-dimension id segments\n * joined in canonical order. `undefined` when a dimension is missing.\n */\nexport const getDictionaryCompositeId = (\n dictionary: Dictionary,\n qualifierTypes: DictionaryQualifierType[]\n): string | undefined =>\n getDictionaryQualifierSegments(dictionary, qualifierTypes)?.join(\n COMPOSITE_ID_SEPARATOR\n );\n\n/**\n * Checks that every declared meta field is provided and equal in the selector.\n * Reserved keys (`locale`, `item`, `variant`) are skipped; `meta.id` is part of\n * the equality check.\n */\nconst metaFieldsMatch = (\n meta: Dictionary['meta'] | undefined,\n selector: DictionarySelector | undefined\n): boolean => {\n if (!meta) return false;\n\n return Object.entries(meta).every(([metaField, declaredValue]) => {\n if ((RESERVED_SELECTOR_KEYS as readonly string[]).includes(metaField)) {\n return true;\n }\n\n const providedValue = selector?.[metaField];\n\n return (\n providedValue !== undefined &&\n String(providedValue) === String(declaredValue)\n );\n });\n};\n\n/**\n * Tests whether a group entry matches a selector across every declared\n * dimension. The `item` dimension matches any value when the selector does not\n * provide one (open collection axis).\n */\nconst entryMatchesSelector = (\n entry: Dictionary,\n qualifierTypes: DictionaryQualifierType[],\n selector: DictionarySelector | undefined\n): boolean =>\n qualifierTypes.every((qualifierType) => {\n if (qualifierType === 'variant') {\n return entry.variant === (selector?.variant ?? 'default');\n }\n\n if (qualifierType === 'item') {\n return (\n selector?.item === undefined ||\n String(entry.item) === String(selector.item)\n );\n }\n\n // qualifierType === 'meta'\n return metaFieldsMatch(entry.meta, selector);\n });\n\n/**\n * Type guard discriminating a `QualifiedDictionaryGroup` (merge output of a\n * qualified key) from a plain `Dictionary`. Both carry a `content` field; only\n * the group declares `qualifierTypes`, which is therefore the discriminator.\n */\nexport const isQualifiedDictionaryGroup = (\n value: unknown\n): value is QualifiedDictionaryGroup =>\n typeof value === 'object' &&\n value !== null &&\n 'qualifierTypes' in value &&\n Array.isArray((value as { qualifierTypes: unknown }).qualifierTypes) &&\n 'content' in value;\n\n/**\n * Reconstructs a resolvable {@link Dictionary} from a single entry of a\n * qualified group: the content node stored under its composite id, plus the\n * qualifier coordinates decoded from that id (`variant`, `item`) and the\n * preserved `meta` object for the meta dimension.\n *\n * This keeps the resolver's matching/transform code unchanged: it still sees a\n * `{ key, content, variant?, item?, meta? }` shape, even though the stored\n * format no longer duplicates those fields per entry.\n */\nexport const reconstructQualifiedEntry = (\n group: QualifiedDictionaryGroup,\n compositeId: string\n): Dictionary => {\n const segments = compositeId.split(COMPOSITE_ID_SEPARATOR);\n\n const entry = {\n key: group.key,\n content: group.content[compositeId],\n } as Dictionary;\n\n group.qualifierTypes.forEach((qualifierType, index) => {\n if (qualifierType === 'variant') {\n entry.variant = segments[index];\n } else if (qualifierType === 'item') {\n entry.item = Number(segments[index]);\n }\n });\n\n if (group.qualifierTypes.includes('meta')) {\n const metaIndex = group.qualifierTypes.indexOf('meta');\n entry.meta = group.meta?.[compositeId] ?? { id: segments[metaIndex] };\n }\n\n return entry;\n};\n\n/**\n * Resolves a dictionary (or qualified dictionary group) against a selector,\n * across every declared dimension.\n *\n * - Plain dictionary → returned as-is (selector ignored)\n * - `item` declared but not selected → every matching entry ordered by index\n * - `item` selected → the matching entry or null\n * - `variant` defaults to the `default` entry when not selected\n * - `meta` requires `{ id }` and every declared meta field to match\n *\n * Dimensions compose: e.g. a variant × item key with `{ variant: 'promo' }`\n * returns every promo item as an array; adding `{ item: 2 }` narrows to one.\n */\nexport const resolveQualifiedDictionary = (\n dictionaryOrGroup: Dictionary | QualifiedDictionaryGroup,\n selector?: DictionarySelector\n): Dictionary | Dictionary[] | null => {\n if (!isQualifiedDictionaryGroup(dictionaryOrGroup)) {\n return dictionaryOrGroup;\n }\n\n const { qualifierTypes, content } = dictionaryOrGroup;\n\n // The meta dimension cannot resolve without an id discriminator.\n if (qualifierTypes.includes('meta') && selector?.id === undefined) {\n return null;\n }\n\n const itemAxisOpen =\n qualifierTypes.includes('item') && selector?.item === undefined;\n\n const matchedEntries = Object.keys(content)\n .map((compositeId) =>\n reconstructQualifiedEntry(dictionaryOrGroup, compositeId)\n )\n .filter((entry) => entryMatchesSelector(entry, qualifierTypes, selector));\n\n if (itemAxisOpen) {\n return matchedEntries.sort(\n (left, right) => (left.item ?? 0) - (right.item ?? 0)\n );\n }\n\n return matchedEntries[0] ?? null;\n};\n\n/**\n * Splits the second argument of `getIntlayer` / `getDictionary` into the\n * effective locale and the selector object (if any).\n */\nexport const parseDictionarySelector = <L extends LocalesValues>(\n localeOrSelector?: L | DictionarySelector\n): { locale?: L; selector?: DictionarySelector } => {\n if (typeof localeOrSelector === 'object' && localeOrSelector !== null) {\n return {\n locale: localeOrSelector.locale as L | undefined,\n selector: localeOrSelector,\n };\n }\n\n return { locale: localeOrSelector };\n};\n\n/**\n * Builds a stable string identity of a selector (excluding `locale`), suitable\n * for cache keys and memoization dependencies.\n */\nexport const getDictionarySelectorCacheKey = (\n selector?: DictionarySelector\n): string => {\n if (!selector) return '';\n\n return Object.keys(selector)\n .filter((selectorKey) => selectorKey !== 'locale')\n .sort()\n .map((selectorKey) => `${selectorKey}:${String(selector[selectorKey])}`)\n .join('|');\n};\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 / meta records, 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/**\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 → meta → item`): `variant` defaults to `default`, `meta` descends by\n * `id`, and `item` either narrows to the selected index or — when no item is\n * given — expands into every sibling chunk (the collection axis). Meta-equality\n * is verified on the loaded chunk. Semantics mirror\n * {@link 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 */\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 // The meta dimension cannot resolve without an id discriminator.\n if (qualifierTypes.includes('meta') && selector?.id === undefined) {\n return { itemAxisOpen, missed: true, chunks: [] };\n }\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 const segment =\n dimension === 'variant'\n ? (selector?.variant ?? 'default')\n : dimension === 'meta'\n ? String(selector?.id)\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 * Whether a loaded chunk satisfies the selector's meta fields (no-op unless the\n * key declares a `meta` dimension).\n */\nconst chunkMatchesMeta = (\n loaderMap: QualifiedDynamicLoaderMap,\n dictionary: Dictionary,\n selector: DictionarySelector | undefined\n): boolean =>\n !loaderMap[QUALIFIER_DYNAMIC_TYPES_KEY].includes('meta') ||\n metaFieldsMatch(dictionary.meta, selector);\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 → meta → item`): `variant` defaults to `default`, `meta` descends by\n * `id`, and `item` either narrows to the selected index or — when no item is\n * given — expands into every sibling chunk (the collection axis). Meta-equality\n * is verified on the loaded chunk. Semantics mirror\n * {@link 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\n .map(({ cacheKey, loader }) => loadChunk(cacheKey, loader()))\n .filter((dictionary) => chunkMatchesMeta(loaderMap, dictionary, selector));\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 = (\n await Promise.all(chunks.map(({ loader }) => loader()))\n ).filter((dictionary) => chunkMatchesMeta(loaderMap, dictionary, selector));\n\n if (itemAxisOpen) return dictionaries.map(transform);\n\n const [dictionary] = dictionaries;\n return dictionary ? transform(dictionary) : null;\n};\n"],"mappings":"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"}
@@ -1,6 +1,8 @@
1
+ import { getEditedContent, getEditedDictionary } from "./deepTransformPlugins/getEditedContent.mjs";
1
2
  import { getCondition } from "./interpreter/getCondition.mjs";
2
3
  import { deepTransformNode } from "./interpreter/getContent/deepTransform.mjs";
3
4
  import { findMatchingCondition, getEnumeration } from "./interpreter/getEnumeration.mjs";
5
+ import { COMPOSITE_ID_SEPARATOR, QUALIFIER_DYNAMIC_TYPES_KEY, QUALIFIER_ORDER, getDictionaryCompositeId, getDictionaryQualifierId, getDictionaryQualifierSegments, getDictionaryQualifierTypes, getDictionarySelectorCacheKey, isQualifiedDictionaryGroup, isQualifiedDynamicLoaderMap, parseDictionarySelector, reconstructQualifiedEntry, resolveQualifiedDictionary, resolveQualifiedDynamicContent, resolveQualifiedDynamicContentAsync } from "./dictionaryManipulator/qualifiedDictionary.mjs";
4
6
  import { getDictionary } from "./interpreter/getDictionary.mjs";
5
7
  import { getIntlayer } from "./interpreter/getIntlayer.mjs";
6
8
  import { getNesting } from "./interpreter/getNesting.mjs";
@@ -52,6 +54,7 @@ import { getNodeChildren } from "./dictionaryManipulator/getNodeChildren.mjs";
52
54
  import { isValidElement } from "./utils/isValidReactElement.mjs";
53
55
  import { getNodeType } from "./dictionaryManipulator/getNodeType.mjs";
54
56
  import { mergeDictionaries } from "./dictionaryManipulator/mergeDictionaries.mjs";
57
+ import { mergeQualifiedDictionaries } from "./dictionaryManipulator/mergeQualifiedDictionaries.mjs";
55
58
  import { orderDictionaries } from "./dictionaryManipulator/orderDictionaries.mjs";
56
59
  import { normalizeDictionaries, normalizeDictionary } from "./dictionaryManipulator/normalizeDictionary.mjs";
57
60
  import { removeContentNodeByKeyPath } from "./dictionaryManipulator/removeContentNodeByKeyPath.mjs";
@@ -85,7 +88,8 @@ import { icuToIntlayerFormatter, intlayerToICUFormatter } from "./messageFormat/
85
88
  import { i18nextToIntlayerFormatter, intlayerToI18nextFormatter } from "./messageFormat/i18next.mjs";
86
89
  import { intlayerToPortableObjectFormatter, portableObjectToIntlayerFormatter } from "./messageFormat/po.mjs";
87
90
  import { intlayerToVueI18nFormatter, vueI18nToIntlayerFormatter } from "./messageFormat/vue-i18n.mjs";
91
+ import { interpolateMessage, parseTaggedMessage, resolveMessage, resolveMessageNode } from "./messageFormat/resolveMessage.mjs";
88
92
  import { isSameKeyPath } from "./utils/isSameKeyPath.mjs";
89
93
  import { stringifyYaml } from "./utils/stringifyYaml.mjs";
90
94
 
91
- 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, CONSECUTIVE_NEWLINE_R, CR_NEWLINE_R, CUSTOM_COMPONENT_R, CachedIntl, CachedIntl as Intl, 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, 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, compile, compileWithOptions, condition as cond, conditionPlugin, createCompiler, createRenderer, currency, cx, date, deepTransformNode, editDictionaryByKeyPath, enumeration as enu, enumerationPlugin, fallbackPlugin, filePlugin, filterMissingTranslationsOnlyPlugin, filterTranslationsOnlyPlugin, findMatchingCondition, gender, genderPlugin, generateListItemPrefix, generateListItemPrefixRegex, generateListItemRegex, generateListRegex, generateSitemap, generateSitemapUrl, get, getBasePlugins, getBrowserLocale, getCachedIntl, getCanonicalPath, getCondition, getContent, getContentNodeByKeyPath, getCookie, getDefaultNode, getDictionary, getEmptyNode, getEnumeration, getFilterMissingTranslationsContent, getFilterMissingTranslationsDictionary, getFilterTranslationsOnlyContent, getFilterTranslationsOnlyDictionary, getFilteredLocalesContent, getFilteredLocalesDictionary, getHTML, getHTMLTextDir, getInsertionValues, getInternalPath, getIntlayer, getLocale, getLocaleFromPath, getLocaleFromStorage, getLocaleFromStorageClient, getLocaleFromStorageServer, getLocaleLang, getLocaleName, getLocalizedContent, getLocalizedPath, getLocalizedUrl, getMarkdownMetadata, getMaskContent, getMissingLocalesContent, getMissingLocalesContentFromDictionary, getMultilingualDictionary, getMultilingualUrls, getNesting, getNodeChildren, getNodeType, getPathWithoutLocale, getPerLocaleDictionary, getPlural, getPrefix, getReplacedValuesContent, getRewritePath, getRewriteRules, getSplittedContent, getSplittedDictionaryContent, getTranslation, html, i18nextToIntlayerFormatter, icuToIntlayerFormatter, inlineRegex, insertion as insert, insertContentInDictionary, insertionPlugin, intlayerToI18nextFormatter, intlayerToICUFormatter, intlayerToPortableObjectFormatter, intlayerToVueI18nFormatter, isSameKeyPath, isValidElement, list, localeDetector, localeFlatMap, localeMap, localeRecord, localeResolver, localeStorageOptions, markdown as md, mergeDictionaries, nesting as nest, nestedPlugin, normalizeAttributeKey, normalizeDictionaries, normalizeDictionary, normalizeWhitespace, number, orderDictionaries, parseBlock, parseCaptureInline, parseInline, parseMarkdown, parseSimpleInline, parseStyleAttribute, parseTableAlign, parseTableAlignCapture, parseTableCells, parseTableRow, parseYaml, parserFor, percentage, plural, pluralPlugin, portableObjectToIntlayerFormatter, presets, qualifies, relativeTime, removeContentNodeByKeyPath, renameContentNodeByKeyPath, renderFor, renderMarkdownAst, renderNothing, sanitizer, setLocaleInStorage, setLocaleInStorageClient, setLocaleInStorageServer, simpleInlineRegex, slugify, some, splitInsertionTemplate, startsWith, stringifyYaml, translation as t, translationPlugin, trimEnd, trimLeadingWhitespaceOutsideFences, unescapeString, units, unquote, updateNodeChildren, validateHTML, validateMarkdown, validatePrefix, vueI18nToIntlayerFormatter };
95
+ 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, 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, compile, compileWithOptions, condition as cond, conditionPlugin, createCompiler, createRenderer, currency, cx, date, deepTransformNode, editDictionaryByKeyPath, enumeration as enu, enumerationPlugin, fallbackPlugin, filePlugin, filterMissingTranslationsOnlyPlugin, filterTranslationsOnlyPlugin, findMatchingCondition, gender, genderPlugin, generateListItemPrefix, generateListItemPrefixRegex, generateListItemRegex, generateListRegex, generateSitemap, generateSitemapUrl, get, getBasePlugins, getBrowserLocale, getCachedIntl, getCanonicalPath, getCondition, getContent, getContentNodeByKeyPath, getCookie, getDefaultNode, getDictionary, getDictionaryCompositeId, getDictionaryQualifierId, getDictionaryQualifierSegments, getDictionaryQualifierTypes, getDictionarySelectorCacheKey, getEditedContent, getEditedDictionary, getEmptyNode, getEnumeration, getFilterMissingTranslationsContent, getFilterMissingTranslationsDictionary, getFilterTranslationsOnlyContent, getFilterTranslationsOnlyDictionary, getFilteredLocalesContent, getFilteredLocalesDictionary, getHTML, getHTMLTextDir, getInsertionValues, getInternalPath, getIntlayer, getLocale, getLocaleFromPath, getLocaleFromStorage, getLocaleFromStorageClient, getLocaleFromStorageServer, getLocaleLang, getLocaleName, getLocalizedContent, getLocalizedPath, getLocalizedUrl, getMarkdownMetadata, getMaskContent, getMissingLocalesContent, getMissingLocalesContentFromDictionary, getMultilingualDictionary, getMultilingualUrls, getNesting, getNodeChildren, getNodeType, getPathWithoutLocale, getPerLocaleDictionary, getPlural, getPrefix, getReplacedValuesContent, getRewritePath, getRewriteRules, getSplittedContent, getSplittedDictionaryContent, getTranslation, html, i18nextToIntlayerFormatter, icuToIntlayerFormatter, inlineRegex, insertion as insert, insertContentInDictionary, insertionPlugin, interpolateMessage, intlayerToI18nextFormatter, intlayerToICUFormatter, intlayerToPortableObjectFormatter, intlayerToVueI18nFormatter, isQualifiedDictionaryGroup, isQualifiedDynamicLoaderMap, isSameKeyPath, isValidElement, list, localeDetector, localeFlatMap, localeMap, localeRecord, localeResolver, localeStorageOptions, markdown as md, mergeDictionaries, mergeQualifiedDictionaries, nesting as nest, nestedPlugin, normalizeAttributeKey, normalizeDictionaries, normalizeDictionary, normalizeWhitespace, number, orderDictionaries, parseBlock, parseCaptureInline, parseDictionarySelector, parseInline, parseMarkdown, parseSimpleInline, parseStyleAttribute, parseTableAlign, parseTableAlignCapture, parseTableCells, parseTableRow, parseTaggedMessage, parseYaml, parserFor, percentage, plural, pluralPlugin, portableObjectToIntlayerFormatter, presets, qualifies, reconstructQualifiedEntry, relativeTime, removeContentNodeByKeyPath, renameContentNodeByKeyPath, renderFor, renderMarkdownAst, renderNothing, resolveMessage, resolveMessageNode, resolveQualifiedDictionary, resolveQualifiedDynamicContent, resolveQualifiedDynamicContentAsync, sanitizer, setLocaleInStorage, setLocaleInStorageClient, setLocaleInStorageServer, simpleInlineRegex, slugify, some, splitInsertionTemplate, startsWith, stringifyYaml, translation as t, translationPlugin, trimEnd, trimLeadingWhitespaceOutsideFences, unescapeString, units, unquote, updateNodeChildren, validateHTML, validateMarkdown, validatePrefix, vueI18nToIntlayerFormatter };
@@ -0,0 +1,23 @@
1
+ //#region src/interpreter/getCollection.ts
2
+ /**
3
+ * Picks a single item from a resolved collection array, or returns the full
4
+ * array when no index is requested.
5
+ *
6
+ * @param items - The already-resolved collection items.
7
+ * @param itemIndex - Optional 0-based index of the item to retrieve.
8
+ * @returns The item at `itemIndex`, or the full array when no index is given.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * const all = getCollection(['a', 'b', 'c']); // ['a', 'b', 'c']
13
+ * const one = getCollection(['a', 'b', 'c'], 1); // 'b'
14
+ * ```
15
+ */
16
+ const getCollection = (items, itemIndex) => {
17
+ if (itemIndex === void 0 || itemIndex === null) return items;
18
+ return items[itemIndex];
19
+ };
20
+
21
+ //#endregion
22
+ export { getCollection };
23
+ //# sourceMappingURL=getCollection.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getCollection.mjs","names":[],"sources":["../../../src/interpreter/getCollection.ts"],"sourcesContent":["/**\n * Picks a single item from a resolved collection array, or returns the full\n * array when no index is requested.\n *\n * @param items - The already-resolved collection items.\n * @param itemIndex - Optional 0-based index of the item to retrieve.\n * @returns The item at `itemIndex`, or the full array when no index is given.\n *\n * @example\n * ```ts\n * const all = getCollection(['a', 'b', 'c']); // ['a', 'b', 'c']\n * const one = getCollection(['a', 'b', 'c'], 1); // 'b'\n * ```\n */\nexport const getCollection = <T>(items: T[], itemIndex?: number): T | T[] => {\n if (itemIndex === undefined || itemIndex === null) {\n return items;\n }\n\n return items[itemIndex] as T;\n};\n"],"mappings":";;;;;;;;;;;;;;;AAcA,MAAa,iBAAoB,OAAY,cAAgC;AAC3E,KAAI,cAAc,UAAa,cAAc,KAC3C,QAAO;AAGT,QAAO,MAAM"}
@@ -1,22 +1,37 @@
1
+ import { parseDictionarySelector, resolveQualifiedDictionary } from "../dictionaryManipulator/qualifiedDictionary.mjs";
1
2
  import { getBasePlugins, getContent } from "./getContent/getContent.mjs";
2
3
 
3
4
  //#region src/interpreter/getDictionary.ts
4
5
  /**
5
6
  * Transforms a dictionary in a single pass, applying each plugin as needed.
6
7
  *
7
- * @param dictionary The dictionary to transform.
8
- * @param locale The locale to use if your transformers need it (e.g. for translations).
9
- * @param additionalPlugins An array of NodeTransformer that define how to transform recognized nodes.
10
- * If omitted, we’ll use a default set of plugins.
8
+ * Also accepts a `QualifiedDictionaryGroup` (collections, variants, meta
9
+ * records) together with a selector as second argument the group is resolved
10
+ * to a single entry (or an ordered array of entries for collections without an
11
+ * `item` selector) before transformation.
12
+ *
13
+ * @param dictionary The dictionary (or qualified dictionary group) to transform.
14
+ * @param localeOrSelector The locale, or a selector object (`{ item }`,
15
+ * `{ variant }`, `{ id, ...meta }`, optionally with `locale`).
16
+ * @param plugins An array of NodeTransformer that define how to transform recognized nodes.
17
+ * If omitted, we’ll use a default set of plugins.
11
18
  */
12
- const getDictionary = (dictionary, locale, plugins = getBasePlugins(locale)) => {
13
- const props = {
14
- dictionaryKey: dictionary.key,
15
- dictionaryPath: dictionary.filePath,
16
- keyPath: [],
17
- plugins
19
+ const getDictionary = (dictionary, localeOrSelector, plugins) => {
20
+ const { locale, selector } = parseDictionarySelector(localeOrSelector);
21
+ const appliedPlugins = plugins ?? getBasePlugins(locale);
22
+ const resolved = resolveQualifiedDictionary(dictionary, selector);
23
+ const transformDictionary = (resolvedDictionary) => {
24
+ const props = {
25
+ dictionaryKey: resolvedDictionary.key,
26
+ dictionaryPath: resolvedDictionary.filePath,
27
+ keyPath: [],
28
+ plugins: appliedPlugins
29
+ };
30
+ return getContent(resolvedDictionary.content, props, appliedPlugins);
18
31
  };
19
- return getContent(dictionary.content, props, plugins);
32
+ if (resolved === null) return null;
33
+ if (Array.isArray(resolved)) return resolved.map(transformDictionary);
34
+ return transformDictionary(resolved);
20
35
  };
21
36
 
22
37
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"getDictionary.mjs","names":[],"sources":["../../../src/interpreter/getDictionary.ts"],"sourcesContent":["import type { Dictionary } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport type {\n DeepTransformContent,\n IInterpreterPluginState,\n NodeProps,\n Plugins,\n} from './getContent';\nimport { getBasePlugins, getContent } from './getContent/getContent';\n\n/**\n * Transforms a dictionary in a single pass, applying each plugin as needed.\n *\n * @param dictionary The dictionary to transform.\n * @param locale The locale to use if your transformers need it (e.g. for translations).\n * @param additionalPlugins An array of NodeTransformer that define how to transform recognized nodes.\n * If omitted, we’ll use a default set of plugins.\n */\nexport const getDictionary = <\n const T extends Dictionary,\n const L extends LocalesValues = DeclaredLocales,\n>(\n dictionary: T,\n locale?: L,\n plugins: Plugins[] = getBasePlugins(locale)\n): DeepTransformContent<T['content'], IInterpreterPluginState, L> => {\n const props: NodeProps = {\n dictionaryKey: dictionary.key,\n dictionaryPath: dictionary.filePath,\n keyPath: [],\n plugins,\n };\n\n return getContent(dictionary.content, props, plugins);\n};\n"],"mappings":";;;;;;;;;;;AAqBA,MAAa,iBAIX,YACA,QACA,UAAqB,eAAe,OAAO,KACwB;CACnE,MAAM,QAAmB;EACvB,eAAe,WAAW;EAC1B,gBAAgB,WAAW;EAC3B,SAAS,EAAE;EACX;EACD;AAED,QAAO,WAAW,WAAW,SAAS,OAAO,QAAQ"}
1
+ {"version":3,"file":"getDictionary.mjs","names":[],"sources":["../../../src/interpreter/getDictionary.ts"],"sourcesContent":["import type {\n Dictionary,\n DictionarySelector,\n QualifiedDictionaryGroup,\n ResolveQualifiedDictionaryContent,\n} from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport {\n parseDictionarySelector,\n resolveQualifiedDictionary,\n} from '../dictionaryManipulator/qualifiedDictionary';\nimport type {\n DeepTransformContent,\n IInterpreterPluginState,\n NodeProps,\n Plugins,\n} from './getContent';\nimport { getBasePlugins, getContent } from './getContent/getContent';\n\n/**\n * Transforms a dictionary in a single pass, applying each plugin as needed.\n *\n * Also accepts a `QualifiedDictionaryGroup` (collections, variants, meta\n * records) together with a selector as second argument — the group is resolved\n * to a single entry (or an ordered array of entries for collections without an\n * `item` selector) before transformation.\n *\n * @param dictionary The dictionary (or qualified dictionary group) to transform.\n * @param localeOrSelector The locale, or a selector object (`{ item }`,\n * `{ variant }`, `{ id, ...meta }`, optionally with `locale`).\n * @param plugins An array of NodeTransformer that define how to transform recognized nodes.\n * If omitted, we’ll use a default set of plugins.\n */\nexport const getDictionary = <\n const T extends Dictionary | QualifiedDictionaryGroup,\n const A extends LocalesValues | DictionarySelector = DeclaredLocales,\n>(\n dictionary: T,\n localeOrSelector?: A,\n plugins?: Plugins[]\n): DeepTransformContent<\n ResolveQualifiedDictionaryContent<T, A>,\n IInterpreterPluginState,\n ExtractSelectorLocale<A>\n> => {\n const { locale, selector } = parseDictionarySelector(localeOrSelector);\n const appliedPlugins = plugins ?? getBasePlugins(locale);\n\n const resolved = resolveQualifiedDictionary(dictionary, selector);\n\n const transformDictionary = (resolvedDictionary: Dictionary) => {\n const props: NodeProps = {\n dictionaryKey: resolvedDictionary.key,\n dictionaryPath: resolvedDictionary.filePath,\n keyPath: [],\n plugins: appliedPlugins,\n };\n\n return getContent(resolvedDictionary.content, props, appliedPlugins);\n };\n\n if (resolved === null) return null as any;\n\n if (Array.isArray(resolved)) {\n return resolved.map(transformDictionary) as any;\n }\n\n return transformDictionary(resolved) as any;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAqCA,MAAa,iBAIX,YACA,kBACA,YAKG;CACH,MAAM,EAAE,QAAQ,aAAa,wBAAwB,iBAAiB;CACtE,MAAM,iBAAiB,WAAW,eAAe,OAAO;CAExD,MAAM,WAAW,2BAA2B,YAAY,SAAS;CAEjE,MAAM,uBAAuB,uBAAmC;EAC9D,MAAM,QAAmB;GACvB,eAAe,mBAAmB;GAClC,gBAAgB,mBAAmB;GACnC,SAAS,EAAE;GACX,SAAS;GACV;AAED,SAAO,WAAW,mBAAmB,SAAS,OAAO,eAAe;;AAGtE,KAAI,aAAa,KAAM,QAAO;AAE9B,KAAI,MAAM,QAAQ,SAAS,CACzB,QAAO,SAAS,IAAI,oBAAoB;AAG1C,QAAO,oBAAoB,SAAS"}
@@ -1,3 +1,4 @@
1
+ import { getDictionarySelectorCacheKey, parseDictionarySelector } from "../dictionaryManipulator/qualifiedDictionary.mjs";
1
2
  import { getDictionary } from "./getDictionary.mjs";
2
3
  import { log } from "@intlayer/config/built";
3
4
  import { colorizeKey, getAppLogger } from "@intlayer/config/logger";
@@ -20,7 +21,17 @@ const createSafeFallback = (path = "") => {
20
21
  };
21
22
  const dictionaryCache = /* @__PURE__ */ new Map();
22
23
  const warnedMissingDictionaries = /* @__PURE__ */ new Set();
23
- const getIntlayer = (key, locale, plugins) => {
24
+ /**
25
+ * Picks one dictionary by its key and returns its content for the given
26
+ * locale or selector.
27
+ *
28
+ * The second argument is either a locale (`'fr'`) or a selector object:
29
+ * - `{ item: 2 }` — collection item (omit `item` to get every item as array)
30
+ * - `{ variant: 'black-friday' }` — named variant (omit for the `default` one)
31
+ * - `{ id: 'prod_abc', ...metaFields }` — meta record
32
+ * - `locale` can be combined with any selector: `{ item: 2, locale: 'fr' }`
33
+ */
34
+ const getIntlayer = (key, localeOrSelector, plugins) => {
24
35
  const dictionary = getDictionaries()[key];
25
36
  if (!dictionary && true) {
26
37
  if (!warnedMissingDictionaries.has(key)) {
@@ -29,9 +40,11 @@ const getIntlayer = (key, locale, plugins) => {
29
40
  }
30
41
  return createSafeFallback(key);
31
42
  }
32
- const cacheKey = `${key}_${locale ?? "default"}_${plugins ? "custom_plugins" : "default_plugins"}`;
43
+ const { locale, selector } = parseDictionarySelector(localeOrSelector);
44
+ const selectorCacheKey = getDictionarySelectorCacheKey(selector);
45
+ const cacheKey = `${key}_${locale ?? "default"}_${selectorCacheKey}_${plugins ? "custom_plugins" : "default_plugins"}`;
33
46
  if (dictionaryCache.has(cacheKey)) return dictionaryCache.get(cacheKey);
34
- const result = getDictionary(dictionary, locale, plugins);
47
+ const result = getDictionary(dictionary, localeOrSelector, plugins);
35
48
  dictionaryCache.set(cacheKey, result);
36
49
  return result;
37
50
  };
@@ -1 +1 @@
1
- {"version":3,"file":"getIntlayer.mjs","names":[],"sources":["../../../src/interpreter/getIntlayer.ts"],"sourcesContent":["import { log } from '@intlayer/config/built';\nimport { colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport type {\n DeclaredLocales,\n DictionaryKeys,\n DictionaryRegistryContent,\n DictionaryRegistryElement,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport type {\n DeepTransformContent,\n IInterpreterPluginState,\n Plugins,\n} from './getContent';\nimport { getDictionary } from './getDictionary';\n\n/**\n * Creates a Recursive Proxy that returns the path of the accessed key\n * stringified. This prevents the app from crashing on undefined access.\n */\nconst createSafeFallback = (path = ''): any => {\n return new Proxy({} as Record<string | symbol, unknown>, {\n get: (_target, prop) => {\n if (\n prop === 'toJSON' ||\n prop === Symbol.toPrimitive ||\n prop === 'toString' ||\n prop === 'valueOf'\n ) {\n return () => path;\n }\n if (prop === 'then') {\n return undefined; // Prevent it from being treated as a Promise\n }\n if (prop === Symbol.iterator) {\n return function* () {\n yield path;\n };\n }\n\n // Recursively build the path (e.g., \"myDictionary.home.title\")\n const nextPath = path ? `${path}.${String(prop)}` : String(prop);\n return createSafeFallback(nextPath);\n },\n });\n};\n\nconst dictionaryCache = new Map<string, any>();\nconst warnedMissingDictionaries = new Set<string>();\n\nexport const getIntlayer = <\n const T extends DictionaryKeys,\n const L extends LocalesValues = DeclaredLocales,\n>(\n key: T,\n locale?: L,\n plugins?: Plugins[]\n): DeepTransformContent<\n DictionaryRegistryContent<T>,\n IInterpreterPluginState,\n L\n> => {\n const dictionaries = getDictionaries();\n const dictionary = dictionaries[key as T] as DictionaryRegistryElement<T>;\n\n if (!dictionary && process.env.NODE_ENV === 'development') {\n if (!warnedMissingDictionaries.has(key as string)) {\n // Log a warning instead of throwing (so developers know it's missing)\n const logger = getAppLogger({ log });\n logger(\n typeof window === 'undefined'\n ? `Dictionary ${colorizeKey(key)} was not found. Using fallback proxy.`\n : `Dictionary ${key} was not found. Using fallback proxy.`,\n {\n level: 'warn',\n }\n );\n warnedMissingDictionaries.add(key as string);\n }\n\n return createSafeFallback(key as string);\n }\n\n const cacheKey = `${key}_${locale ?? 'default'}_${plugins ? 'custom_plugins' : 'default_plugins'}`;\n\n if (dictionaryCache.has(cacheKey)) {\n return dictionaryCache.get(cacheKey);\n }\n\n const result = getDictionary<DictionaryRegistryElement<T>, L>(\n dictionary,\n locale,\n plugins\n );\n\n dictionaryCache.set(cacheKey, result);\n\n return result;\n};\n"],"mappings":";;;;;;;;;;AAqBA,MAAM,sBAAsB,OAAO,OAAY;AAC7C,QAAO,IAAI,MAAM,EAAE,EAAsC,EACvD,MAAM,SAAS,SAAS;AACtB,MACE,SAAS,YACT,SAAS,OAAO,eAChB,SAAS,cACT,SAAS,UAET,cAAa;AAEf,MAAI,SAAS,OACX;AAEF,MAAI,SAAS,OAAO,SAClB,QAAO,aAAa;AAClB,SAAM;;AAMV,SAAO,mBADU,OAAO,GAAG,KAAK,GAAG,OAAO,KAAK,KAAK,OAAO,KAAK,CAC7B;IAEtC,CAAC;;AAGJ,MAAM,kCAAkB,IAAI,KAAkB;AAC9C,MAAM,4CAA4B,IAAI,KAAa;AAEnD,MAAa,eAIX,KACA,QACA,YAKG;CAEH,MAAM,aADe,iBACU,CAAC;AAEhC,KAAI,CAAC,cAAc,MAAwC;AACzD,MAAI,CAAC,0BAA0B,IAAI,IAAc,EAAE;AAGjD,GADe,aAAa,EAAE,KAAK,CAC7B,CACJ,OAAO,WAAW,cACd,cAAc,YAAY,IAAI,CAAC,yCAC/B,cAAc,IAAI,wCACtB,EACE,OAAO,QACR,CACF;AACD,6BAA0B,IAAI,IAAc;;AAG9C,SAAO,mBAAmB,IAAc;;CAG1C,MAAM,WAAW,GAAG,IAAI,GAAG,UAAU,UAAU,GAAG,UAAU,mBAAmB;AAE/E,KAAI,gBAAgB,IAAI,SAAS,CAC/B,QAAO,gBAAgB,IAAI,SAAS;CAGtC,MAAM,SAAS,cACb,YACA,QACA,QACD;AAED,iBAAgB,IAAI,UAAU,OAAO;AAErC,QAAO"}
1
+ {"version":3,"file":"getIntlayer.mjs","names":[],"sources":["../../../src/interpreter/getIntlayer.ts"],"sourcesContent":["import { log } from '@intlayer/config/built';\nimport { colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport type { DictionarySelector } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n DictionaryKeys,\n DictionaryRegistryResult,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport {\n getDictionarySelectorCacheKey,\n parseDictionarySelector,\n} from '../dictionaryManipulator/qualifiedDictionary';\nimport type {\n DeepTransformContent,\n IInterpreterPluginState,\n Plugins,\n} from './getContent';\nimport { getDictionary } from './getDictionary';\n\n/**\n * Creates a Recursive Proxy that returns the path of the accessed key\n * stringified. This prevents the app from crashing on undefined access.\n */\nconst createSafeFallback = (path = ''): any => {\n return new Proxy({} as Record<string | symbol, unknown>, {\n get: (_target, prop) => {\n if (\n prop === 'toJSON' ||\n prop === Symbol.toPrimitive ||\n prop === 'toString' ||\n prop === 'valueOf'\n ) {\n return () => path;\n }\n if (prop === 'then') {\n return undefined; // Prevent it from being treated as a Promise\n }\n if (prop === Symbol.iterator) {\n return function* () {\n yield path;\n };\n }\n\n // Recursively build the path (e.g., \"myDictionary.home.title\")\n const nextPath = path ? `${path}.${String(prop)}` : String(prop);\n return createSafeFallback(nextPath);\n },\n });\n};\n\nconst dictionaryCache = new Map<string, any>();\nconst warnedMissingDictionaries = new Set<string>();\n\n/**\n * Picks one dictionary by its key and returns its content for the given\n * locale or selector.\n *\n * The second argument is either a locale (`'fr'`) or a selector object:\n * - `{ item: 2 }` — collection item (omit `item` to get every item as array)\n * - `{ variant: 'black-friday' }` — named variant (omit for the `default` one)\n * - `{ id: 'prod_abc', ...metaFields }` — meta record\n * - `locale` can be combined with any selector: `{ item: 2, locale: 'fr' }`\n */\nexport const getIntlayer = <\n const T extends DictionaryKeys,\n const A extends LocalesValues | DictionarySelector = DeclaredLocales,\n>(\n key: T,\n localeOrSelector?: A,\n plugins?: Plugins[]\n): DeepTransformContent<\n DictionaryRegistryResult<T, A>,\n IInterpreterPluginState,\n ExtractSelectorLocale<A>\n> => {\n const dictionaries = getDictionaries();\n const dictionary = dictionaries[key as T];\n\n if (!dictionary && process.env.NODE_ENV === 'development') {\n if (!warnedMissingDictionaries.has(key as string)) {\n // Log a warning instead of throwing (so developers know it's missing)\n const logger = getAppLogger({ log });\n logger(\n typeof window === 'undefined'\n ? `Dictionary ${colorizeKey(key)} was not found. Using fallback proxy.`\n : `Dictionary ${key} was not found. Using fallback proxy.`,\n {\n level: 'warn',\n }\n );\n warnedMissingDictionaries.add(key as string);\n }\n\n return createSafeFallback(key as string);\n }\n\n const { locale, selector } = parseDictionarySelector(localeOrSelector);\n const selectorCacheKey = getDictionarySelectorCacheKey(selector);\n\n const cacheKey = `${key}_${locale ?? 'default'}_${selectorCacheKey}_${plugins ? 'custom_plugins' : 'default_plugins'}`;\n\n if (dictionaryCache.has(cacheKey)) {\n return dictionaryCache.get(cacheKey);\n }\n\n const result = getDictionary(dictionary, localeOrSelector, plugins);\n\n dictionaryCache.set(cacheKey, result);\n\n return result as any;\n};\n"],"mappings":";;;;;;;;;;;AA0BA,MAAM,sBAAsB,OAAO,OAAY;AAC7C,QAAO,IAAI,MAAM,EAAE,EAAsC,EACvD,MAAM,SAAS,SAAS;AACtB,MACE,SAAS,YACT,SAAS,OAAO,eAChB,SAAS,cACT,SAAS,UAET,cAAa;AAEf,MAAI,SAAS,OACX;AAEF,MAAI,SAAS,OAAO,SAClB,QAAO,aAAa;AAClB,SAAM;;AAMV,SAAO,mBADU,OAAO,GAAG,KAAK,GAAG,OAAO,KAAK,KAAK,OAAO,KAAK,CAC7B;IAEtC,CAAC;;AAGJ,MAAM,kCAAkB,IAAI,KAAkB;AAC9C,MAAM,4CAA4B,IAAI,KAAa;;;;;;;;;;;AAYnD,MAAa,eAIX,KACA,kBACA,YAKG;CAEH,MAAM,aADe,iBACU,CAAC;AAEhC,KAAI,CAAC,cAAc,MAAwC;AACzD,MAAI,CAAC,0BAA0B,IAAI,IAAc,EAAE;AAGjD,GADe,aAAa,EAAE,KAAK,CAC7B,CACJ,OAAO,WAAW,cACd,cAAc,YAAY,IAAI,CAAC,yCAC/B,cAAc,IAAI,wCACtB,EACE,OAAO,QACR,CACF;AACD,6BAA0B,IAAI,IAAc;;AAG9C,SAAO,mBAAmB,IAAc;;CAG1C,MAAM,EAAE,QAAQ,aAAa,wBAAwB,iBAAiB;CACtE,MAAM,mBAAmB,8BAA8B,SAAS;CAEhE,MAAM,WAAW,GAAG,IAAI,GAAG,UAAU,UAAU,GAAG,iBAAiB,GAAG,UAAU,mBAAmB;AAEnG,KAAI,gBAAgB,IAAI,SAAS,CAC/B,QAAO,gBAAgB,IAAI,SAAS;CAGtC,MAAM,SAAS,cAAc,YAAY,kBAAkB,QAAQ;AAEnE,iBAAgB,IAAI,UAAU,OAAO;AAErC,QAAO"}