@intlayer/core 8.9.4-canary.0 → 8.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/cjs/dictionaryManipulator/orderDictionaries.cjs +2 -2
  2. package/dist/cjs/dictionaryManipulator/orderDictionaries.cjs.map +1 -1
  3. package/dist/cjs/index.cjs +3 -0
  4. package/dist/cjs/messageFormat/ICU.cjs +47 -12
  5. package/dist/cjs/messageFormat/ICU.cjs.map +1 -1
  6. package/dist/cjs/messageFormat/i18next.cjs +49 -12
  7. package/dist/cjs/messageFormat/i18next.cjs.map +1 -1
  8. package/dist/cjs/messageFormat/index.cjs +3 -0
  9. package/dist/cjs/messageFormat/po.cjs +171 -0
  10. package/dist/cjs/messageFormat/po.cjs.map +1 -0
  11. package/dist/cjs/messageFormat/vue-i18n.cjs +23 -9
  12. package/dist/cjs/messageFormat/vue-i18n.cjs.map +1 -1
  13. package/dist/esm/dictionaryManipulator/orderDictionaries.mjs +2 -2
  14. package/dist/esm/dictionaryManipulator/orderDictionaries.mjs.map +1 -1
  15. package/dist/esm/index.mjs +2 -1
  16. package/dist/esm/messageFormat/ICU.mjs +47 -12
  17. package/dist/esm/messageFormat/ICU.mjs.map +1 -1
  18. package/dist/esm/messageFormat/i18next.mjs +49 -12
  19. package/dist/esm/messageFormat/i18next.mjs.map +1 -1
  20. package/dist/esm/messageFormat/index.mjs +2 -1
  21. package/dist/esm/messageFormat/po.mjs +167 -0
  22. package/dist/esm/messageFormat/po.mjs.map +1 -0
  23. package/dist/esm/messageFormat/vue-i18n.mjs +23 -9
  24. package/dist/esm/messageFormat/vue-i18n.mjs.map +1 -1
  25. package/dist/types/dictionaryManipulator/orderDictionaries.d.ts +5 -1
  26. package/dist/types/dictionaryManipulator/orderDictionaries.d.ts.map +1 -1
  27. package/dist/types/index.d.ts +2 -1
  28. package/dist/types/messageFormat/ICU.d.ts.map +1 -1
  29. package/dist/types/messageFormat/i18next.d.ts.map +1 -1
  30. package/dist/types/messageFormat/index.d.ts +2 -1
  31. package/dist/types/messageFormat/po.d.ts +15 -0
  32. package/dist/types/messageFormat/po.d.ts.map +1 -0
  33. package/dist/types/messageFormat/vue-i18n.d.ts.map +1 -1
  34. package/package.json +6 -6
@@ -0,0 +1,171 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
3
+ const require_interpreter_getContent_deepTransform = require('../interpreter/getContent/deepTransform.cjs');
4
+ const require_transpiler_enumeration_enumeration = require('../transpiler/enumeration/enumeration.cjs');
5
+ const require_transpiler_gender_gender = require('../transpiler/gender/gender.cjs');
6
+ const require_transpiler_html_html = require('../transpiler/html/html.cjs');
7
+ const require_transpiler_insertion_insertion = require('../transpiler/insertion/insertion.cjs');
8
+ const require_transpiler_plural_plural = require('../transpiler/plural/plural.cjs');
9
+ let _intlayer_types_nodeType = require("@intlayer/types/nodeType");
10
+ _intlayer_types_nodeType = require_runtime.__toESM(_intlayer_types_nodeType);
11
+
12
+ //#region src/messageFormat/po.ts
13
+ /**
14
+ * Extracts the string value from a transformed AST node or generic object.
15
+ */
16
+ const extractStringValue = (val) => {
17
+ if (typeof val === "string") return val;
18
+ if (val && typeof val === "object" && "msgstr" in val) return val.msgstr[0] || "";
19
+ return JSON.stringify(val);
20
+ };
21
+ const intlayerToPoPlugin = {
22
+ canHandle: (node) => {
23
+ if (typeof node === "string") return true;
24
+ if (node && typeof node === "object" && (node.nodeType === _intlayer_types_nodeType.INSERTION || node.nodeType === _intlayer_types_nodeType.HTML || node.nodeType === _intlayer_types_nodeType.ENUMERATION || node.nodeType === _intlayer_types_nodeType.PLURAL || node.nodeType === _intlayer_types_nodeType.GENDER || node.nodeType === "composite")) return true;
25
+ if (Array.isArray(node)) {
26
+ if (node.length === 0) return false;
27
+ let hasNode = false;
28
+ let hasPlainObjectOrArray = false;
29
+ for (const item of node) if (typeof item === "string") {} else if (item && typeof item === "object" && (item.nodeType === _intlayer_types_nodeType.INSERTION || item.nodeType === _intlayer_types_nodeType.HTML || item.nodeType === _intlayer_types_nodeType.ENUMERATION || item.nodeType === _intlayer_types_nodeType.PLURAL || item.nodeType === _intlayer_types_nodeType.GENDER || item.nodeType === "composite")) hasNode = true;
30
+ else hasPlainObjectOrArray = true;
31
+ if (hasPlainObjectOrArray) return false;
32
+ if (!hasNode) return false;
33
+ return true;
34
+ }
35
+ return false;
36
+ },
37
+ transform: (node, props, next) => {
38
+ if (typeof node === "string") {
39
+ const poVal = node.replace(/\{\{([^}]+)\}\}/g, "%($1)s");
40
+ return {
41
+ msgid: poVal,
42
+ msgstr: [poVal]
43
+ };
44
+ }
45
+ if (node.nodeType === _intlayer_types_nodeType.INSERTION) return next(node[_intlayer_types_nodeType.INSERTION], props);
46
+ if (node.nodeType === _intlayer_types_nodeType.HTML) {
47
+ const val = node[_intlayer_types_nodeType.HTML];
48
+ return {
49
+ msgid: val,
50
+ msgstr: [val]
51
+ };
52
+ }
53
+ if (node.nodeType === _intlayer_types_nodeType.PLURAL || node.nodeType === _intlayer_types_nodeType.ENUMERATION) {
54
+ const isPlural = node.nodeType === _intlayer_types_nodeType.PLURAL;
55
+ const options = isPlural ? node[_intlayer_types_nodeType.PLURAL] : node[_intlayer_types_nodeType.ENUMERATION];
56
+ const rawMsgid = isPlural ? options.one || options.other || options.fallback : options.fallback || options["0"];
57
+ const msgid = extractStringValue(next(rawMsgid, props));
58
+ const msgid_plural = isPlural ? extractStringValue(next(options.other || options.fallback || rawMsgid, props)) : extractStringValue(next(options.fallback || rawMsgid, props));
59
+ const msgstr = [];
60
+ if (isPlural) {
61
+ if ("zero" in options) msgstr.push(extractStringValue(next(options.zero, props)));
62
+ msgstr.push(extractStringValue(next(options.one || options.fallback, props)));
63
+ if ("two" in options) msgstr.push(extractStringValue(next(options.two, props)));
64
+ if ("few" in options) msgstr.push(extractStringValue(next(options.few, props)));
65
+ if ("many" in options) msgstr.push(extractStringValue(next(options.many, props)));
66
+ const otherVal = extractStringValue(next(options.other || options.fallback, props));
67
+ if (!msgstr.includes(otherVal)) msgstr.push(otherVal);
68
+ } else {
69
+ msgstr[0] = extractStringValue(next(options.fallback || options["0"], props));
70
+ msgstr[1] = msgstr[0];
71
+ }
72
+ return {
73
+ msgctxt: isPlural ? void 0 : "enumeration",
74
+ msgid,
75
+ msgid_plural,
76
+ msgstr
77
+ };
78
+ }
79
+ if (node.nodeType === _intlayer_types_nodeType.GENDER) {
80
+ const options = node[_intlayer_types_nodeType.GENDER];
81
+ const fallback = extractStringValue(next(options.fallback, props));
82
+ return {
83
+ msgctxt: "gender",
84
+ msgid: fallback,
85
+ msgstr: [
86
+ extractStringValue(next(options.male || options.fallback, props)),
87
+ extractStringValue(next(options.female || options.fallback, props)),
88
+ fallback
89
+ ]
90
+ };
91
+ }
92
+ if (Array.isArray(node) || node.nodeType === "composite" && Array.isArray(node.composite)) {
93
+ const combined = (Array.isArray(node) ? node : node.composite).map((item) => extractStringValue(next(item, props))).join("");
94
+ return {
95
+ msgid: combined,
96
+ msgstr: [combined]
97
+ };
98
+ }
99
+ return node;
100
+ }
101
+ };
102
+ const poToIntlayerPlugin = {
103
+ canHandle: (node) => node && typeof node === "object" && "msgid" in node && "msgstr" in node,
104
+ transform: (node) => {
105
+ const msgstr = node.msgstr || [];
106
+ const isPlural = Boolean(node.msgid_plural) || msgstr.length > 1;
107
+ const processString = (str) => {
108
+ if (!str) return "";
109
+ const parsed = str.replace(/%\(([a-zA-Z0-9_-]+)\)[sdf]/g, "{{$1}}");
110
+ if (/<[a-zA-Z0-9-]+[^>]*>/.test(parsed)) return require_transpiler_html_html.html(parsed);
111
+ if (parsed.includes("{{")) return require_transpiler_insertion_insertion.insert(parsed);
112
+ return parsed;
113
+ };
114
+ if (!isPlural) return processString(msgstr[0] || node.msgid || "");
115
+ const options = {};
116
+ if (node.msgctxt === "gender") return require_transpiler_gender_gender.gender({
117
+ male: processString(msgstr[0] || node.msgid),
118
+ female: processString(msgstr[1] || msgstr[0]),
119
+ fallback: processString(msgstr[2] || msgstr[msgstr.length - 1])
120
+ });
121
+ if (node.msgctxt === "enumeration") return require_transpiler_enumeration_enumeration.enu({
122
+ "0": processString(msgstr[0]),
123
+ fallback: processString(msgstr[msgstr.length - 1])
124
+ });
125
+ if (msgstr.length === 2) {
126
+ options.one = processString(msgstr[0]);
127
+ options.other = processString(msgstr[1]);
128
+ } else if (msgstr.length === 3) {
129
+ options.one = processString(msgstr[0]);
130
+ options.few = processString(msgstr[1]);
131
+ options.other = processString(msgstr[2]);
132
+ } else if (msgstr.length === 6) {
133
+ options.zero = processString(msgstr[0]);
134
+ options.one = processString(msgstr[1]);
135
+ options.two = processString(msgstr[2]);
136
+ options.few = processString(msgstr[3]);
137
+ options.many = processString(msgstr[4]);
138
+ options.other = processString(msgstr[5]);
139
+ } else {
140
+ options.one = processString(msgstr[0]);
141
+ options.other = processString(msgstr[msgstr.length - 1]);
142
+ for (let i = 1; i < msgstr.length - 1; i++) options[`${i + 1}`] = processString(msgstr[i]);
143
+ }
144
+ return require_transpiler_plural_plural.plural(options);
145
+ }
146
+ };
147
+ const intlayerToPortableObjectFormatter = (dictionary) => {
148
+ return require_interpreter_getContent_deepTransform.deepTransformNode(dictionary, {
149
+ dictionaryKey: "po",
150
+ keyPath: [],
151
+ plugins: [{
152
+ id: "po",
153
+ ...intlayerToPoPlugin
154
+ }]
155
+ });
156
+ };
157
+ const portableObjectToIntlayerFormatter = (message) => {
158
+ return require_interpreter_getContent_deepTransform.deepTransformNode(message, {
159
+ dictionaryKey: "po",
160
+ keyPath: [],
161
+ plugins: [{
162
+ id: "po",
163
+ ...poToIntlayerPlugin
164
+ }]
165
+ });
166
+ };
167
+
168
+ //#endregion
169
+ exports.intlayerToPortableObjectFormatter = intlayerToPortableObjectFormatter;
170
+ exports.portableObjectToIntlayerFormatter = portableObjectToIntlayerFormatter;
171
+ //# sourceMappingURL=po.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"po.cjs","names":["NodeTypes","html","insert","gender","enu","plural","deepTransformNode"],"sources":["../../../src/messageFormat/po.ts"],"sourcesContent":["import type { Dictionary } from '@intlayer/types/dictionary';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { deepTransformNode } from '../interpreter';\nimport { enu, gender, html, insert, plural } from '../transpiler';\nimport type { JsonValue } from './ICU';\n\nexport type PortableObject = {\n msgid: string;\n msgctxt?: string;\n msgid_plural?: string;\n msgstr: string[];\n};\n\n/**\n * Extracts the string value from a transformed AST node or generic object.\n */\nconst extractStringValue = (val: any): string => {\n if (typeof val === 'string') return val;\n if (val && typeof val === 'object' && 'msgstr' in val) {\n return val.msgstr[0] || '';\n }\n return JSON.stringify(val);\n};\n\nconst intlayerToPoPlugin = {\n canHandle: (node: any) => {\n if (typeof node === 'string') return true;\n\n if (\n node &&\n typeof node === 'object' &&\n (node.nodeType === NodeTypes.INSERTION ||\n node.nodeType === NodeTypes.HTML ||\n node.nodeType === NodeTypes.ENUMERATION ||\n node.nodeType === NodeTypes.PLURAL ||\n node.nodeType === NodeTypes.GENDER ||\n node.nodeType === 'composite')\n ) {\n return true;\n }\n\n // Handle structural string arrays (composite phrases)\n if (Array.isArray(node)) {\n if (node.length === 0) return false;\n let hasNode = false;\n let hasPlainObjectOrArray = false;\n\n for (const item of node) {\n if (typeof item === 'string') {\n } else if (\n item &&\n typeof item === 'object' &&\n (item.nodeType === NodeTypes.INSERTION ||\n item.nodeType === NodeTypes.HTML ||\n item.nodeType === NodeTypes.ENUMERATION ||\n item.nodeType === NodeTypes.PLURAL ||\n item.nodeType === NodeTypes.GENDER ||\n item.nodeType === 'composite')\n ) {\n hasNode = true;\n } else {\n hasPlainObjectOrArray = true;\n }\n }\n\n if (hasPlainObjectOrArray) return false;\n if (!hasNode) return false;\n return true;\n }\n\n return false;\n },\n transform: (node: any, props: any, next: any): PortableObject => {\n // 1. Strings\n if (typeof node === 'string') {\n const poVal = node.replace(/\\{\\{([^}]+)\\}\\}/g, '%($1)s');\n return { msgid: poVal, msgstr: [poVal] };\n }\n\n // 2. Insertions & Variables\n if (node.nodeType === NodeTypes.INSERTION) {\n return next(node[NodeTypes.INSERTION], props);\n }\n\n // 3. HTML\n if (node.nodeType === NodeTypes.HTML) {\n const val = node[NodeTypes.HTML];\n return { msgid: val, msgstr: [val] };\n }\n\n // 4. Plurals & Enumerations\n if (\n node.nodeType === NodeTypes.PLURAL ||\n node.nodeType === NodeTypes.ENUMERATION\n ) {\n const isPlural = node.nodeType === NodeTypes.PLURAL;\n const options = isPlural\n ? node[NodeTypes.PLURAL]\n : node[NodeTypes.ENUMERATION];\n\n const rawMsgid = isPlural\n ? options.one || options.other || options.fallback\n : options.fallback || options['0'];\n\n const msgid = extractStringValue(next(rawMsgid, props));\n const msgid_plural = isPlural\n ? extractStringValue(\n next(options.other || options.fallback || rawMsgid, props)\n )\n : extractStringValue(next(options.fallback || rawMsgid, props));\n\n const msgstr: string[] = [];\n\n if (isPlural) {\n // Standard CLDR/Gettext mapping fallback\n if ('zero' in options)\n msgstr.push(extractStringValue(next(options.zero, props)));\n msgstr.push(\n extractStringValue(next(options.one || options.fallback, props))\n );\n if ('two' in options)\n msgstr.push(extractStringValue(next(options.two, props)));\n if ('few' in options)\n msgstr.push(extractStringValue(next(options.few, props)));\n if ('many' in options)\n msgstr.push(extractStringValue(next(options.many, props)));\n\n // Ensure 'other' is always the last fallback if others are missing\n const otherVal = extractStringValue(\n next(options.other || options.fallback, props)\n );\n if (!msgstr.includes(otherVal)) msgstr.push(otherVal);\n } else {\n // Enums don't have standard PO mapping, pack linearly\n msgstr[0] = extractStringValue(\n next(options.fallback || options['0'], props)\n );\n msgstr[1] = msgstr[0];\n }\n\n return {\n msgctxt: isPlural ? undefined : 'enumeration',\n msgid,\n msgid_plural,\n msgstr,\n };\n }\n\n // 5. Gender (mapped to PO via msgctxt)\n if (node.nodeType === NodeTypes.GENDER) {\n const options = node[NodeTypes.GENDER];\n const fallback = extractStringValue(next(options.fallback, props));\n return {\n msgctxt: 'gender',\n msgid: fallback,\n msgstr: [\n extractStringValue(next(options.male || options.fallback, props)),\n extractStringValue(next(options.female || options.fallback, props)),\n fallback,\n ],\n };\n }\n\n // 6. Arrays / Composites\n if (\n Array.isArray(node) ||\n (node.nodeType === 'composite' && Array.isArray(node.composite))\n ) {\n const arr = Array.isArray(node) ? node : node.composite;\n const combined = arr\n .map((item: any) => extractStringValue(next(item, props)))\n .join('');\n return { msgid: combined, msgstr: [combined] };\n }\n\n return node;\n },\n};\n\nconst poToIntlayerPlugin = {\n canHandle: (node: any) =>\n node && typeof node === 'object' && 'msgid' in node && 'msgstr' in node,\n\n transform: (node: PortableObject) => {\n const msgstr = node.msgstr || [];\n const isPlural = Boolean(node.msgid_plural) || msgstr.length > 1;\n\n const processString = (str: string) => {\n if (!str) return '';\n // Convert Python/C-style gettext variables %(name)s or %(name)d -> {{name}}\n const parsed = str.replace(/%\\(([a-zA-Z0-9_-]+)\\)[sdf]/g, '{{$1}}');\n\n if (/<[a-zA-Z0-9-]+[^>]*>/.test(parsed)) return html(parsed);\n if (parsed.includes('{{')) return insert(parsed);\n return parsed;\n };\n\n // Fast-path: Single string translation\n if (!isPlural) {\n return processString(msgstr[0] || node.msgid || '');\n }\n\n // Handle Plural/Enum/Gender matrix\n const options: Record<string, any> = {};\n\n if (node.msgctxt === 'gender') {\n return gender({\n male: processString(msgstr[0] || node.msgid),\n female: processString(msgstr[1] || msgstr[0]),\n fallback: processString(msgstr[2] || msgstr[msgstr.length - 1]),\n });\n }\n\n if (node.msgctxt === 'enumeration') {\n return enu({\n '0': processString(msgstr[0]),\n fallback: processString(msgstr[msgstr.length - 1]),\n });\n }\n\n // Plural Form Mapping based on array length (Gettext Plural-Forms approximation)\n if (msgstr.length === 2) {\n options.one = processString(msgstr[0]);\n options.other = processString(msgstr[1]);\n } else if (msgstr.length === 3) {\n // E.g., Polish/Russian: one, few, many/other\n options.one = processString(msgstr[0]);\n options.few = processString(msgstr[1]);\n options.other = processString(msgstr[2]);\n } else if (msgstr.length === 6) {\n // E.g., Arabic: zero, one, two, few, many, other\n options.zero = processString(msgstr[0]);\n options.one = processString(msgstr[1]);\n options.two = processString(msgstr[2]);\n options.few = processString(msgstr[3]);\n options.many = processString(msgstr[4]);\n options.other = processString(msgstr[5]);\n } else {\n // Generic arbitrary length mapping\n options.one = processString(msgstr[0]);\n options.other = processString(msgstr[msgstr.length - 1]);\n for (let i = 1; i < msgstr.length - 1; i++) {\n options[`${i + 1}`] = processString(msgstr[i]);\n }\n }\n\n return plural(options as any);\n },\n};\n\nexport const intlayerToPortableObjectFormatter = (\n dictionary: Dictionary['content']\n): JsonValue => {\n return deepTransformNode(dictionary, {\n dictionaryKey: 'po',\n keyPath: [],\n plugins: [{ id: 'po', ...intlayerToPoPlugin }],\n });\n};\n\nexport const portableObjectToIntlayerFormatter = (\n message: JsonValue\n): Dictionary['content'] => {\n return deepTransformNode(message, {\n dictionaryKey: 'po',\n keyPath: [],\n plugins: [{ id: 'po', ...poToIntlayerPlugin }],\n });\n};\n"],"mappings":"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"}
@@ -3,6 +3,7 @@ const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
3
3
  const require_interpreter_getContent_deepTransform = require('../interpreter/getContent/deepTransform.cjs');
4
4
  const require_transpiler_enumeration_enumeration = require('../transpiler/enumeration/enumeration.cjs');
5
5
  const require_transpiler_insertion_insertion = require('../transpiler/insertion/insertion.cjs');
6
+ const require_transpiler_plural_plural = require('../transpiler/plural/plural.cjs');
6
7
  let _intlayer_types_nodeType = require("@intlayer/types/nodeType");
7
8
  _intlayer_types_nodeType = require_runtime.__toESM(_intlayer_types_nodeType);
8
9
 
@@ -70,14 +71,16 @@ const vueI18nNodesToIntlayer = (parts) => {
70
71
  if (parts.length === 1) return vueI18nPartToIntlayer(parts[0]);
71
72
  const options = {};
72
73
  const varName = "count";
73
- if (parts.length === 2) {
74
- options["1"] = vueI18nPartToIntlayer(parts[0]);
75
- options.fallback = vueI18nPartToIntlayer(parts[1]);
76
- } else if (parts.length === 3) {
77
- options["0"] = vueI18nPartToIntlayer(parts[0]);
78
- options["1"] = vueI18nPartToIntlayer(parts[1]);
79
- options.fallback = vueI18nPartToIntlayer(parts[2]);
80
- } else parts.forEach((part, index) => {
74
+ if (parts.length === 2) return require_transpiler_plural_plural.plural({
75
+ one: vueI18nPartToIntlayer(parts[0]),
76
+ other: vueI18nPartToIntlayer(parts[1])
77
+ });
78
+ if (parts.length === 3) return require_transpiler_plural_plural.plural({
79
+ zero: vueI18nPartToIntlayer(parts[0]),
80
+ one: vueI18nPartToIntlayer(parts[1]),
81
+ other: vueI18nPartToIntlayer(parts[2])
82
+ });
83
+ parts.forEach((part, index) => {
81
84
  if (index === parts.length - 1) options.fallback = vueI18nPartToIntlayer(part);
82
85
  else options[index.toString()] = vueI18nPartToIntlayer(part);
83
86
  });
@@ -97,7 +100,7 @@ const vueI18nToIntlayerPlugin = {
97
100
  const intlayerToVueI18nPlugin = {
98
101
  canHandle: (node) => {
99
102
  if (typeof node === "string") return true;
100
- if (node && typeof node === "object" && (node.nodeType === _intlayer_types_nodeType.INSERTION || node.nodeType === _intlayer_types_nodeType.ENUMERATION || node.nodeType === _intlayer_types_nodeType.GENDER || node.nodeType === "composite")) return true;
103
+ if (node && typeof node === "object" && (node.nodeType === _intlayer_types_nodeType.INSERTION || node.nodeType === _intlayer_types_nodeType.ENUMERATION || node.nodeType === _intlayer_types_nodeType.PLURAL || node.nodeType === _intlayer_types_nodeType.GENDER || node.nodeType === "composite")) return true;
101
104
  if (Array.isArray(node)) {
102
105
  if (node.length === 0) return false;
103
106
  let hasNode = false;
@@ -113,6 +116,17 @@ const intlayerToVueI18nPlugin = {
113
116
  transform: (node, props, next) => {
114
117
  if (typeof node === "string") return node.replace(/\{\{([^}]+)\}\}/g, "{$1}");
115
118
  if (node.nodeType === _intlayer_types_nodeType.INSERTION) return node[_intlayer_types_nodeType.INSERTION].replace(/\{\{([^}]+)\}\}/g, "{$1}");
119
+ if (node.nodeType === _intlayer_types_nodeType.PLURAL) {
120
+ const options = node[_intlayer_types_nodeType.PLURAL];
121
+ const transformedOptions = {};
122
+ for (const [key, val] of Object.entries(options)) {
123
+ const childVal = next(val, props);
124
+ transformedOptions[key] = typeof childVal === "string" ? childVal : JSON.stringify(childVal);
125
+ }
126
+ if (transformedOptions.zero && transformedOptions.one && transformedOptions.other) return `${transformedOptions.zero} | ${transformedOptions.one} | ${transformedOptions.other}`;
127
+ if (transformedOptions.one && transformedOptions.other) return `${transformedOptions.one} | ${transformedOptions.other}`;
128
+ return transformedOptions.other || Object.values(transformedOptions)[0];
129
+ }
116
130
  if (node.nodeType === _intlayer_types_nodeType.ENUMERATION) {
117
131
  const options = node[_intlayer_types_nodeType.ENUMERATION];
118
132
  const transformedOptions = {};
@@ -1 +1 @@
1
- {"version":3,"file":"vue-i18n.cjs","names":["insert","enu","NodeTypes","deepTransformNode"],"sources":["../../../src/messageFormat/vue-i18n.ts"],"sourcesContent":["import type { Dictionary } from '@intlayer/types/dictionary';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { deepTransformNode } from '../interpreter';\nimport { enu, insert } from '../transpiler';\nimport type { JsonValue } from './ICU';\n\n// Types for our AST\ntype VueI18nNode =\n | string\n | {\n type: 'argument';\n name: string;\n };\n\nconst parseVueI18nPart = (text: string): VueI18nNode[] => {\n let index = 0;\n const nodes: VueI18nNode[] = [];\n let currentText = '';\n\n while (index < text.length) {\n const char = text[index];\n\n if (char === '{') {\n if (currentText) {\n nodes.push(currentText);\n currentText = '';\n }\n index++; // skip {\n let name = '';\n while (index < text.length && text[index] !== '}') {\n name += text[index];\n index++;\n }\n if (index < text.length) {\n index++; // skip }\n }\n nodes.push({ type: 'argument', name: name.trim() });\n } else {\n currentText += char;\n index++;\n }\n }\n\n if (currentText) {\n nodes.push(currentText);\n }\n\n return nodes;\n};\n\nconst parseVueI18n = (text: string): VueI18nNode[][] => {\n // Split by | but handle escaped \\|\n const parts: string[] = [];\n let currentPart = '';\n let index = 0;\n\n while (index < text.length) {\n const char = text[index];\n if (char === '\\\\' && index + 1 < text.length && text[index + 1] === '|') {\n currentPart += '|';\n index += 2;\n } else if (char === '|') {\n parts.push(currentPart.trim()); // Trim to remove surrounding spaces\n currentPart = '';\n index++;\n } else {\n currentPart += char;\n index++;\n }\n }\n parts.push(currentPart.trim()); // Trim last part too\n\n return parts.map(parseVueI18nPart);\n};\n\nconst vueI18nPartToIntlayer = (nodes: VueI18nNode[]): any => {\n if (nodes.length === 0) return '';\n if (nodes.length === 1 && typeof nodes[0] === 'string') return nodes[0];\n\n let str = '';\n for (const node of nodes) {\n if (typeof node === 'string') {\n str += node;\n } else {\n str += `{{${node.name}}}`;\n }\n }\n return insert(str);\n};\n\nconst vueI18nNodesToIntlayer = (parts: VueI18nNode[][]): any => {\n if (parts.length === 1) {\n return vueI18nPartToIntlayer(parts[0]);\n }\n\n // Handle pluralization (choice)\n const options: Record<string, any> = {};\n const varName = 'count'; // Default variable for vue-i18n choices\n\n if (parts.length === 2) {\n // 2 choices: 1 | other\n options['1'] = vueI18nPartToIntlayer(parts[0]);\n options.fallback = vueI18nPartToIntlayer(parts[1]);\n } else if (parts.length === 3) {\n // 3 choices: 0 | 1 | other\n options['0'] = vueI18nPartToIntlayer(parts[0]);\n options['1'] = vueI18nPartToIntlayer(parts[1]);\n options.fallback = vueI18nPartToIntlayer(parts[2]);\n } else {\n // > 3 choices: 0 | 1 | 2 | ... | other\n parts.forEach((part, index) => {\n if (index === parts.length - 1) {\n options.fallback = vueI18nPartToIntlayer(part);\n } else {\n options[index.toString()] = vueI18nPartToIntlayer(part);\n }\n });\n }\n\n // Preserve variable name\n options.__intlayer_vue_i18n_var = varName;\n\n return enu(options);\n};\n\nconst vueI18nToIntlayerPlugin = {\n canHandle: (node: any) =>\n typeof node === 'string' && (node.includes('{') || node.includes('|')),\n transform: (node: any) => {\n try {\n const ast = parseVueI18n(node);\n return vueI18nNodesToIntlayer(ast);\n } catch {\n return node;\n }\n },\n};\n\nconst intlayerToVueI18nPlugin = {\n canHandle: (node: any) => {\n if (typeof node === 'string') return true;\n\n if (\n node &&\n typeof node === 'object' &&\n (node.nodeType === NodeTypes.INSERTION ||\n node.nodeType === NodeTypes.ENUMERATION ||\n node.nodeType === NodeTypes.GENDER ||\n node.nodeType === 'composite')\n ) {\n return true;\n }\n\n if (Array.isArray(node)) {\n if (node.length === 0) return false;\n\n let hasNode = false;\n let hasPlainObjectOrArray = false;\n\n for (const item of node) {\n if (typeof item === 'string') {\n } else if (\n item &&\n typeof item === 'object' &&\n (item.nodeType === NodeTypes.INSERTION ||\n item.nodeType === NodeTypes.ENUMERATION ||\n item.nodeType === NodeTypes.GENDER ||\n item.nodeType === 'composite')\n ) {\n hasNode = true;\n } else {\n hasPlainObjectOrArray = true;\n }\n }\n\n // If it contains plain objects or nested arrays, it's a structural array\n if (hasPlainObjectOrArray) return false;\n // If it contains ONLY strings, it's a structural array, not a composite string\n if (!hasNode) return false;\n\n return true;\n }\n\n return false;\n },\n transform: (node: any, props: any, next: any) => {\n if (typeof node === 'string') {\n // replace {{...}} with {...} even in strings\n return node.replace(/\\{\\{([^}]+)\\}\\}/g, '{$1}');\n }\n\n if (node.nodeType === NodeTypes.INSERTION) {\n // {{name}} -> {name}\n return node[NodeTypes.INSERTION].replace(/\\{\\{([^}]+)\\}\\}/g, '{$1}');\n }\n\n if (node.nodeType === NodeTypes.ENUMERATION) {\n const options = node[NodeTypes.ENUMERATION];\n\n const transformedOptions: Record<string, string> = {};\n for (const [key, val] of Object.entries(options)) {\n if (key === '__intlayer_vue_i18n_var') continue;\n const childVal = next(val, props);\n transformedOptions[key] =\n typeof childVal === 'string' ? childVal : JSON.stringify(childVal);\n }\n\n const keys = Object.keys(transformedOptions);\n\n if (keys.includes('0')) {\n const indices = keys.filter((key) => /^\\d+$/.test(key)).map(Number);\n const maxIndex = Math.max(...indices);\n\n const fallback =\n transformedOptions.fallback || transformedOptions.other;\n const resultParts = [];\n\n if (maxIndex <= 1 && !keys.includes('2')) {\n const zero = transformedOptions['0'] || '';\n const one = transformedOptions['1'] || '';\n return `${zero} | ${one} | ${fallback}`;\n }\n\n const limit = Math.max(1, maxIndex);\n\n for (let i = 0; i <= limit; i++) {\n const key = i.toString();\n if (transformedOptions[key]) {\n resultParts.push(transformedOptions[key]);\n } else {\n resultParts.push('');\n }\n }\n resultParts.push(fallback);\n return resultParts.join(' | ').replace(/ \\| {2}\\| /g, ' | | ');\n }\n\n if (\n keys.includes('1') &&\n (keys.includes('fallback') || keys.includes('other'))\n ) {\n return `${transformedOptions['1']} | ${transformedOptions.fallback || transformedOptions.other}`;\n }\n\n if (\n keys.length === 1 &&\n (keys.includes('fallback') || keys.includes('other'))\n ) {\n return transformedOptions.fallback || transformedOptions.other;\n }\n\n return (\n transformedOptions.fallback || Object.values(transformedOptions)[0]\n );\n }\n\n if (node.nodeType === NodeTypes.GENDER) {\n const options = node[NodeTypes.GENDER];\n const transformedOptions: Record<string, any> = {};\n\n for (const [key, val] of Object.entries(options)) {\n let newKey = key;\n if (key === 'fallback') newKey = 'other';\n\n const childVal = next(val, props);\n transformedOptions[newKey] = childVal;\n }\n return transformedOptions;\n }\n\n if (\n Array.isArray(node) ||\n (node.nodeType === 'composite' && Array.isArray(node.composite))\n ) {\n const arr = Array.isArray(node) ? node : node.composite;\n const items = arr.map((item: any) => next(item, props));\n return items.join('');\n }\n\n return next(node, props);\n },\n};\n\nexport const intlayerToVueI18nFormatter = (\n message: Dictionary['content']\n): JsonValue => {\n return deepTransformNode(message, {\n dictionaryKey: 'vue-i18n',\n keyPath: [],\n plugins: [{ id: 'vue-i18n', ...intlayerToVueI18nPlugin }],\n });\n};\n\nexport const vueI18nToIntlayerFormatter = (\n message: JsonValue\n): Dictionary['content'] => {\n return deepTransformNode(message, {\n dictionaryKey: 'vue-i18n',\n keyPath: [],\n plugins: [{ id: 'vue-i18n', ...vueI18nToIntlayerPlugin }],\n });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"vue-i18n.cjs","names":["insert","plural","enu","NodeTypes","deepTransformNode"],"sources":["../../../src/messageFormat/vue-i18n.ts"],"sourcesContent":["import type { Dictionary } from '@intlayer/types/dictionary';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { deepTransformNode } from '../interpreter';\nimport { enu, insert, plural } from '../transpiler';\nimport type { JsonValue } from './ICU';\n\n// Types for our AST\ntype VueI18nNode =\n | string\n | {\n type: 'argument';\n name: string;\n };\n\nconst parseVueI18nPart = (text: string): VueI18nNode[] => {\n let index = 0;\n const nodes: VueI18nNode[] = [];\n let currentText = '';\n\n while (index < text.length) {\n const char = text[index];\n\n if (char === '{') {\n if (currentText) {\n nodes.push(currentText);\n currentText = '';\n }\n index++; // skip {\n let name = '';\n while (index < text.length && text[index] !== '}') {\n name += text[index];\n index++;\n }\n if (index < text.length) {\n index++; // skip }\n }\n nodes.push({ type: 'argument', name: name.trim() });\n } else {\n currentText += char;\n index++;\n }\n }\n\n if (currentText) {\n nodes.push(currentText);\n }\n\n return nodes;\n};\n\nconst parseVueI18n = (text: string): VueI18nNode[][] => {\n // Split by | but handle escaped \\|\n const parts: string[] = [];\n let currentPart = '';\n let index = 0;\n\n while (index < text.length) {\n const char = text[index];\n if (char === '\\\\' && index + 1 < text.length && text[index + 1] === '|') {\n currentPart += '|';\n index += 2;\n } else if (char === '|') {\n parts.push(currentPart.trim()); // Trim to remove surrounding spaces\n currentPart = '';\n index++;\n } else {\n currentPart += char;\n index++;\n }\n }\n parts.push(currentPart.trim()); // Trim last part too\n\n return parts.map(parseVueI18nPart);\n};\n\nconst vueI18nPartToIntlayer = (nodes: VueI18nNode[]): any => {\n if (nodes.length === 0) return '';\n if (nodes.length === 1 && typeof nodes[0] === 'string') return nodes[0];\n\n let str = '';\n for (const node of nodes) {\n if (typeof node === 'string') {\n str += node;\n } else {\n str += `{{${node.name}}}`;\n }\n }\n return insert(str);\n};\n\nconst vueI18nNodesToIntlayer = (parts: VueI18nNode[][]): any => {\n if (parts.length === 1) {\n return vueI18nPartToIntlayer(parts[0]);\n }\n\n // Handle pluralization (choice)\n const options: Record<string, any> = {};\n const varName = 'count'; // Default variable for vue-i18n choices\n\n if (parts.length === 2) {\n // 2 choices: 1 | other\n return plural({\n one: vueI18nPartToIntlayer(parts[0]),\n other: vueI18nPartToIntlayer(parts[1]),\n });\n }\n\n if (parts.length === 3) {\n // 3 choices: 0 | 1 | other\n return plural({\n zero: vueI18nPartToIntlayer(parts[0]),\n one: vueI18nPartToIntlayer(parts[1]),\n other: vueI18nPartToIntlayer(parts[2]),\n });\n }\n\n // > 3 choices: 0 | 1 | 2 | ... | other\n parts.forEach((part, index) => {\n if (index === parts.length - 1) {\n options.fallback = vueI18nPartToIntlayer(part);\n } else {\n options[index.toString()] = vueI18nPartToIntlayer(part);\n }\n });\n\n // Preserve variable name\n options.__intlayer_vue_i18n_var = varName;\n\n return enu(options);\n};\n\nconst vueI18nToIntlayerPlugin = {\n canHandle: (node: any) =>\n typeof node === 'string' && (node.includes('{') || node.includes('|')),\n transform: (node: any) => {\n try {\n const ast = parseVueI18n(node);\n return vueI18nNodesToIntlayer(ast);\n } catch {\n return node;\n }\n },\n};\n\nconst intlayerToVueI18nPlugin = {\n canHandle: (node: any) => {\n if (typeof node === 'string') return true;\n\n if (\n node &&\n typeof node === 'object' &&\n (node.nodeType === NodeTypes.INSERTION ||\n node.nodeType === NodeTypes.ENUMERATION ||\n node.nodeType === NodeTypes.PLURAL ||\n node.nodeType === NodeTypes.GENDER ||\n node.nodeType === 'composite')\n ) {\n return true;\n }\n\n if (Array.isArray(node)) {\n if (node.length === 0) return false;\n\n let hasNode = false;\n let hasPlainObjectOrArray = false;\n\n for (const item of node) {\n if (typeof item === 'string') {\n } else if (\n item &&\n typeof item === 'object' &&\n (item.nodeType === NodeTypes.INSERTION ||\n item.nodeType === NodeTypes.ENUMERATION ||\n item.nodeType === NodeTypes.GENDER ||\n item.nodeType === 'composite')\n ) {\n hasNode = true;\n } else {\n hasPlainObjectOrArray = true;\n }\n }\n\n // If it contains plain objects or nested arrays, it's a structural array\n if (hasPlainObjectOrArray) return false;\n // If it contains ONLY strings, it's a structural array, not a composite string\n if (!hasNode) return false;\n\n return true;\n }\n\n return false;\n },\n transform: (node: any, props: any, next: any) => {\n if (typeof node === 'string') {\n // replace {{...}} with {...} even in strings\n return node.replace(/\\{\\{([^}]+)\\}\\}/g, '{$1}');\n }\n\n if (node.nodeType === NodeTypes.INSERTION) {\n // {{name}} -> {name}\n return node[NodeTypes.INSERTION].replace(/\\{\\{([^}]+)\\}\\}/g, '{$1}');\n }\n\n if (node.nodeType === NodeTypes.PLURAL) {\n const options = node[NodeTypes.PLURAL];\n\n const transformedOptions: Record<string, string> = {};\n for (const [key, val] of Object.entries(options)) {\n const childVal = next(val, props);\n transformedOptions[key] =\n typeof childVal === 'string' ? childVal : JSON.stringify(childVal);\n }\n\n if (\n transformedOptions.zero &&\n transformedOptions.one &&\n transformedOptions.other\n ) {\n return `${transformedOptions.zero} | ${transformedOptions.one} | ${transformedOptions.other}`;\n }\n\n if (transformedOptions.one && transformedOptions.other) {\n return `${transformedOptions.one} | ${transformedOptions.other}`;\n }\n\n return transformedOptions.other || Object.values(transformedOptions)[0];\n }\n\n if (node.nodeType === NodeTypes.ENUMERATION) {\n const options = node[NodeTypes.ENUMERATION];\n\n const transformedOptions: Record<string, string> = {};\n for (const [key, val] of Object.entries(options)) {\n if (key === '__intlayer_vue_i18n_var') continue;\n const childVal = next(val, props);\n transformedOptions[key] =\n typeof childVal === 'string' ? childVal : JSON.stringify(childVal);\n }\n\n const keys = Object.keys(transformedOptions);\n\n if (keys.includes('0')) {\n const indices = keys.filter((key) => /^\\d+$/.test(key)).map(Number);\n const maxIndex = Math.max(...indices);\n\n const fallback =\n transformedOptions.fallback || transformedOptions.other;\n const resultParts = [];\n\n if (maxIndex <= 1 && !keys.includes('2')) {\n const zero = transformedOptions['0'] || '';\n const one = transformedOptions['1'] || '';\n return `${zero} | ${one} | ${fallback}`;\n }\n\n const limit = Math.max(1, maxIndex);\n\n for (let i = 0; i <= limit; i++) {\n const key = i.toString();\n if (transformedOptions[key]) {\n resultParts.push(transformedOptions[key]);\n } else {\n resultParts.push('');\n }\n }\n resultParts.push(fallback);\n return resultParts.join(' | ').replace(/ \\| {2}\\| /g, ' | | ');\n }\n\n if (\n keys.includes('1') &&\n (keys.includes('fallback') || keys.includes('other'))\n ) {\n return `${transformedOptions['1']} | ${transformedOptions.fallback || transformedOptions.other}`;\n }\n\n if (\n keys.length === 1 &&\n (keys.includes('fallback') || keys.includes('other'))\n ) {\n return transformedOptions.fallback || transformedOptions.other;\n }\n\n return (\n transformedOptions.fallback || Object.values(transformedOptions)[0]\n );\n }\n\n if (node.nodeType === NodeTypes.GENDER) {\n const options = node[NodeTypes.GENDER];\n const transformedOptions: Record<string, any> = {};\n\n for (const [key, val] of Object.entries(options)) {\n let newKey = key;\n if (key === 'fallback') newKey = 'other';\n\n const childVal = next(val, props);\n transformedOptions[newKey] = childVal;\n }\n return transformedOptions;\n }\n\n if (\n Array.isArray(node) ||\n (node.nodeType === 'composite' && Array.isArray(node.composite))\n ) {\n const arr = Array.isArray(node) ? node : node.composite;\n const items = arr.map((item: any) => next(item, props));\n return items.join('');\n }\n\n return next(node, props);\n },\n};\n\nexport const intlayerToVueI18nFormatter = (\n message: Dictionary['content']\n): JsonValue => {\n return deepTransformNode(message, {\n dictionaryKey: 'vue-i18n',\n keyPath: [],\n plugins: [{ id: 'vue-i18n', ...intlayerToVueI18nPlugin }],\n });\n};\n\nexport const vueI18nToIntlayerFormatter = (\n message: JsonValue\n): Dictionary['content'] => {\n return deepTransformNode(message, {\n dictionaryKey: 'vue-i18n',\n keyPath: [],\n plugins: [{ id: 'vue-i18n', ...vueI18nToIntlayerPlugin }],\n });\n};\n"],"mappings":";;;;;;;;;;AAcA,MAAM,oBAAoB,SAAgC;CACxD,IAAI,QAAQ;CACZ,MAAM,QAAuB,EAAE;CAC/B,IAAI,cAAc;CAElB,OAAO,QAAQ,KAAK,QAAQ;EAC1B,MAAM,OAAO,KAAK;EAElB,IAAI,SAAS,KAAK;GAChB,IAAI,aAAa;IACf,MAAM,KAAK,YAAY;IACvB,cAAc;;GAEhB;GACA,IAAI,OAAO;GACX,OAAO,QAAQ,KAAK,UAAU,KAAK,WAAW,KAAK;IACjD,QAAQ,KAAK;IACb;;GAEF,IAAI,QAAQ,KAAK,QACf;GAEF,MAAM,KAAK;IAAE,MAAM;IAAY,MAAM,KAAK,MAAM;IAAE,CAAC;SAC9C;GACL,eAAe;GACf;;;CAIJ,IAAI,aACF,MAAM,KAAK,YAAY;CAGzB,OAAO;;AAGT,MAAM,gBAAgB,SAAkC;CAEtD,MAAM,QAAkB,EAAE;CAC1B,IAAI,cAAc;CAClB,IAAI,QAAQ;CAEZ,OAAO,QAAQ,KAAK,QAAQ;EAC1B,MAAM,OAAO,KAAK;EAClB,IAAI,SAAS,QAAQ,QAAQ,IAAI,KAAK,UAAU,KAAK,QAAQ,OAAO,KAAK;GACvE,eAAe;GACf,SAAS;SACJ,IAAI,SAAS,KAAK;GACvB,MAAM,KAAK,YAAY,MAAM,CAAC;GAC9B,cAAc;GACd;SACK;GACL,eAAe;GACf;;;CAGJ,MAAM,KAAK,YAAY,MAAM,CAAC;CAE9B,OAAO,MAAM,IAAI,iBAAiB;;AAGpC,MAAM,yBAAyB,UAA8B;CAC3D,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,IAAI,MAAM,WAAW,KAAK,OAAO,MAAM,OAAO,UAAU,OAAO,MAAM;CAErE,IAAI,MAAM;CACV,KAAK,MAAM,QAAQ,OACjB,IAAI,OAAO,SAAS,UAClB,OAAO;MAEP,OAAO,KAAK,KAAK,KAAK;CAG1B,OAAOA,8CAAO,IAAI;;AAGpB,MAAM,0BAA0B,UAAgC;CAC9D,IAAI,MAAM,WAAW,GACnB,OAAO,sBAAsB,MAAM,GAAG;CAIxC,MAAM,UAA+B,EAAE;CACvC,MAAM,UAAU;CAEhB,IAAI,MAAM,WAAW,GAEnB,OAAOC,wCAAO;EACZ,KAAK,sBAAsB,MAAM,GAAG;EACpC,OAAO,sBAAsB,MAAM,GAAG;EACvC,CAAC;CAGJ,IAAI,MAAM,WAAW,GAEnB,OAAOA,wCAAO;EACZ,MAAM,sBAAsB,MAAM,GAAG;EACrC,KAAK,sBAAsB,MAAM,GAAG;EACpC,OAAO,sBAAsB,MAAM,GAAG;EACvC,CAAC;CAIJ,MAAM,SAAS,MAAM,UAAU;EAC7B,IAAI,UAAU,MAAM,SAAS,GAC3B,QAAQ,WAAW,sBAAsB,KAAK;OAE9C,QAAQ,MAAM,UAAU,IAAI,sBAAsB,KAAK;GAEzD;CAGF,QAAQ,0BAA0B;CAElC,OAAOC,+CAAI,QAAQ;;AAGrB,MAAM,0BAA0B;CAC9B,YAAY,SACV,OAAO,SAAS,aAAa,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,IAAI;CACvE,YAAY,SAAc;EACxB,IAAI;GAEF,OAAO,uBADK,aAAa,KACQ,CAAC;UAC5B;GACN,OAAO;;;CAGZ;AAED,MAAM,0BAA0B;CAC9B,YAAY,SAAc;EACxB,IAAI,OAAO,SAAS,UAAU,OAAO;EAErC,IACE,QACA,OAAO,SAAS,aACf,KAAK,aAAaC,yBAAU,aAC3B,KAAK,aAAaA,yBAAU,eAC5B,KAAK,aAAaA,yBAAU,UAC5B,KAAK,aAAaA,yBAAU,UAC5B,KAAK,aAAa,cAEpB,OAAO;EAGT,IAAI,MAAM,QAAQ,KAAK,EAAE;GACvB,IAAI,KAAK,WAAW,GAAG,OAAO;GAE9B,IAAI,UAAU;GACd,IAAI,wBAAwB;GAE5B,KAAK,MAAM,QAAQ,MACjB,IAAI,OAAO,SAAS,UAAU,QACvB,IACL,QACA,OAAO,SAAS,aACf,KAAK,aAAaA,yBAAU,aAC3B,KAAK,aAAaA,yBAAU,eAC5B,KAAK,aAAaA,yBAAU,UAC5B,KAAK,aAAa,cAEpB,UAAU;QAEV,wBAAwB;GAK5B,IAAI,uBAAuB,OAAO;GAElC,IAAI,CAAC,SAAS,OAAO;GAErB,OAAO;;EAGT,OAAO;;CAET,YAAY,MAAW,OAAY,SAAc;EAC/C,IAAI,OAAO,SAAS,UAElB,OAAO,KAAK,QAAQ,oBAAoB,OAAO;EAGjD,IAAI,KAAK,aAAaA,yBAAU,WAE9B,OAAO,KAAKA,yBAAU,WAAW,QAAQ,oBAAoB,OAAO;EAGtE,IAAI,KAAK,aAAaA,yBAAU,QAAQ;GACtC,MAAM,UAAU,KAAKA,yBAAU;GAE/B,MAAM,qBAA6C,EAAE;GACrD,KAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,QAAQ,EAAE;IAChD,MAAM,WAAW,KAAK,KAAK,MAAM;IACjC,mBAAmB,OACjB,OAAO,aAAa,WAAW,WAAW,KAAK,UAAU,SAAS;;GAGtE,IACE,mBAAmB,QACnB,mBAAmB,OACnB,mBAAmB,OAEnB,OAAO,GAAG,mBAAmB,KAAK,KAAK,mBAAmB,IAAI,KAAK,mBAAmB;GAGxF,IAAI,mBAAmB,OAAO,mBAAmB,OAC/C,OAAO,GAAG,mBAAmB,IAAI,KAAK,mBAAmB;GAG3D,OAAO,mBAAmB,SAAS,OAAO,OAAO,mBAAmB,CAAC;;EAGvE,IAAI,KAAK,aAAaA,yBAAU,aAAa;GAC3C,MAAM,UAAU,KAAKA,yBAAU;GAE/B,MAAM,qBAA6C,EAAE;GACrD,KAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,QAAQ,EAAE;IAChD,IAAI,QAAQ,2BAA2B;IACvC,MAAM,WAAW,KAAK,KAAK,MAAM;IACjC,mBAAmB,OACjB,OAAO,aAAa,WAAW,WAAW,KAAK,UAAU,SAAS;;GAGtE,MAAM,OAAO,OAAO,KAAK,mBAAmB;GAE5C,IAAI,KAAK,SAAS,IAAI,EAAE;IACtB,MAAM,UAAU,KAAK,QAAQ,QAAQ,QAAQ,KAAK,IAAI,CAAC,CAAC,IAAI,OAAO;IACnE,MAAM,WAAW,KAAK,IAAI,GAAG,QAAQ;IAErC,MAAM,WACJ,mBAAmB,YAAY,mBAAmB;IACpD,MAAM,cAAc,EAAE;IAEtB,IAAI,YAAY,KAAK,CAAC,KAAK,SAAS,IAAI,EAGtC,OAAO,GAFM,mBAAmB,QAAQ,GAEzB,KADH,mBAAmB,QAAQ,GACf,KAAK;IAG/B,MAAM,QAAQ,KAAK,IAAI,GAAG,SAAS;IAEnC,KAAK,IAAI,IAAI,GAAG,KAAK,OAAO,KAAK;KAC/B,MAAM,MAAM,EAAE,UAAU;KACxB,IAAI,mBAAmB,MACrB,YAAY,KAAK,mBAAmB,KAAK;UAEzC,YAAY,KAAK,GAAG;;IAGxB,YAAY,KAAK,SAAS;IAC1B,OAAO,YAAY,KAAK,MAAM,CAAC,QAAQ,eAAe,QAAQ;;GAGhE,IACE,KAAK,SAAS,IAAI,KACjB,KAAK,SAAS,WAAW,IAAI,KAAK,SAAS,QAAQ,GAEpD,OAAO,GAAG,mBAAmB,KAAK,KAAK,mBAAmB,YAAY,mBAAmB;GAG3F,IACE,KAAK,WAAW,MACf,KAAK,SAAS,WAAW,IAAI,KAAK,SAAS,QAAQ,GAEpD,OAAO,mBAAmB,YAAY,mBAAmB;GAG3D,OACE,mBAAmB,YAAY,OAAO,OAAO,mBAAmB,CAAC;;EAIrE,IAAI,KAAK,aAAaA,yBAAU,QAAQ;GACtC,MAAM,UAAU,KAAKA,yBAAU;GAC/B,MAAM,qBAA0C,EAAE;GAElD,KAAK,MAAM,CAAC,KAAK,QAAQ,OAAO,QAAQ,QAAQ,EAAE;IAChD,IAAI,SAAS;IACb,IAAI,QAAQ,YAAY,SAAS;IAGjC,mBAAmB,UADF,KAAK,KAAK,MACU;;GAEvC,OAAO;;EAGT,IACE,MAAM,QAAQ,KAAK,IAClB,KAAK,aAAa,eAAe,MAAM,QAAQ,KAAK,UAAU,EAI/D,QAFY,MAAM,QAAQ,KAAK,GAAG,OAAO,KAAK,WAC5B,KAAK,SAAc,KAAK,MAAM,MAAM,CAC1C,CAAC,KAAK,GAAG;EAGvB,OAAO,KAAK,MAAM,MAAM;;CAE3B;AAED,MAAa,8BACX,YACc;CACd,OAAOC,+DAAkB,SAAS;EAChC,eAAe;EACf,SAAS,EAAE;EACX,SAAS,CAAC;GAAE,IAAI;GAAY,GAAG;GAAyB,CAAC;EAC1D,CAAC;;AAGJ,MAAa,8BACX,YAC0B;CAC1B,OAAOA,+DAAkB,SAAS;EAChC,eAAe;EACf,SAAS,EAAE;EACX,SAAS,CAAC;GAAE,IAAI;GAAY,GAAG;GAAyB,CAAC;EAC1D,CAAC"}
@@ -8,8 +8,8 @@ import { editor } from "@intlayer/config/built";
8
8
  * @param priorityStrategy - The priority strategy ('local_first' or 'distant_first')
9
9
  * @returns Ordered array of dictionaries
10
10
  */
11
- const orderDictionaries = (dictionaries) => {
12
- const { dictionaryPriorityStrategy } = editor;
11
+ const orderDictionaries = (dictionaries, config) => {
12
+ const dictionaryPriorityStrategy = config?.editor?.dictionaryPriorityStrategy ?? editor.dictionaryPriorityStrategy;
13
13
  if (dictionaries.length <= 1) return dictionaries;
14
14
  const withIndex = dictionaries.map((dictionary, index) => ({
15
15
  dictionary,
@@ -1 +1 @@
1
- {"version":3,"file":"orderDictionaries.mjs","names":[],"sources":["../../../src/dictionaryManipulator/orderDictionaries.ts"],"sourcesContent":["import { editor } from '@intlayer/config/built';\nimport type { Dictionary } from '@intlayer/types/dictionary';\n\n/**\n * Orders dictionaries based on the dictionary priority strategy.\n *\n * @param dictionaries - Array of dictionaries to order\n * @param priorityStrategy - The priority strategy ('local_first' or 'distant_first')\n * @returns Ordered array of dictionaries\n */\nexport const orderDictionaries = (dictionaries: Dictionary[]): Dictionary[] => {\n const { dictionaryPriorityStrategy } = editor;\n\n if (dictionaries.length <= 1) {\n return dictionaries;\n }\n\n // Stabilize original indices to preserve relative order for complete ties\n const withIndex = dictionaries.map((dictionary, index) => ({\n dictionary,\n index,\n }));\n\n const getPriority = (dictionary: Dictionary): number => {\n const p = dictionary.priority ?? 0;\n\n return Number.isFinite(p) ? p : 0;\n };\n\n const getLocationWeight = (d: Dictionary): number => {\n const location = d.location;\n\n // undefined location should always be last\n if (location === undefined) {\n return 2;\n }\n\n if (dictionaryPriorityStrategy === 'distant_first') {\n // distant should come first\n return location === 'remote' ? 0 : 1;\n }\n // default: local_first\n return location === 'local' ? 0 : 1;\n };\n\n withIndex.sort((a, b) => {\n // 1) Non-autoFilled before autoFilled (autoFilled have lower precedence)\n const aAuto = a.dictionary.filled ? 1 : 0;\n const bAuto = b.dictionary.filled ? 1 : 0;\n if (aAuto !== bAuto) return aAuto - bAuto; // 0 before 1\n\n // 2) Higher priority first (larger number wins)\n const aP = getPriority(a.dictionary);\n const bP = getPriority(b.dictionary);\n if (aP !== bP) return bP - aP; // descending\n\n // 3) Location according to strategy\n const aLocation = getLocationWeight(a.dictionary);\n const bLocation = getLocationWeight(b.dictionary);\n if (aLocation !== bLocation) return aLocation - bLocation;\n\n // 4) Stable fallback by original index\n return a.index - b.index;\n });\n\n return withIndex.map(({ dictionary }) => dictionary);\n};\n"],"mappings":";;;;;;;;;;AAUA,MAAa,qBAAqB,iBAA6C;CAC7E,MAAM,EAAE,+BAA+B;CAEvC,IAAI,aAAa,UAAU,GACzB,OAAO;CAIT,MAAM,YAAY,aAAa,KAAK,YAAY,WAAW;EACzD;EACA;EACD,EAAE;CAEH,MAAM,eAAe,eAAmC;EACtD,MAAM,IAAI,WAAW,YAAY;EAEjC,OAAO,OAAO,SAAS,EAAE,GAAG,IAAI;;CAGlC,MAAM,qBAAqB,MAA0B;EACnD,MAAM,WAAW,EAAE;EAGnB,IAAI,aAAa,QACf,OAAO;EAGT,IAAI,+BAA+B,iBAEjC,OAAO,aAAa,WAAW,IAAI;EAGrC,OAAO,aAAa,UAAU,IAAI;;CAGpC,UAAU,MAAM,GAAG,MAAM;EAEvB,MAAM,QAAQ,EAAE,WAAW,SAAS,IAAI;EACxC,MAAM,QAAQ,EAAE,WAAW,SAAS,IAAI;EACxC,IAAI,UAAU,OAAO,OAAO,QAAQ;EAGpC,MAAM,KAAK,YAAY,EAAE,WAAW;EACpC,MAAM,KAAK,YAAY,EAAE,WAAW;EACpC,IAAI,OAAO,IAAI,OAAO,KAAK;EAG3B,MAAM,YAAY,kBAAkB,EAAE,WAAW;EACjD,MAAM,YAAY,kBAAkB,EAAE,WAAW;EACjD,IAAI,cAAc,WAAW,OAAO,YAAY;EAGhD,OAAO,EAAE,QAAQ,EAAE;GACnB;CAEF,OAAO,UAAU,KAAK,EAAE,iBAAiB,WAAW"}
1
+ {"version":3,"file":"orderDictionaries.mjs","names":[],"sources":["../../../src/dictionaryManipulator/orderDictionaries.ts"],"sourcesContent":["import { editor } from '@intlayer/config/built';\nimport type { Dictionary } from '@intlayer/types/dictionary';\n\n/**\n * Orders dictionaries based on the dictionary priority strategy.\n *\n * @param dictionaries - Array of dictionaries to order\n * @param priorityStrategy - The priority strategy ('local_first' or 'distant_first')\n * @returns Ordered array of dictionaries\n */\nexport const orderDictionaries = (\n dictionaries: Dictionary[],\n config?: {\n editor: {\n dictionaryPriorityStrategy: 'local_first' | 'distant_first';\n };\n }\n): Dictionary[] => {\n const dictionaryPriorityStrategy =\n config?.editor?.dictionaryPriorityStrategy ??\n editor.dictionaryPriorityStrategy;\n\n if (dictionaries.length <= 1) {\n return dictionaries;\n }\n\n // Stabilize original indices to preserve relative order for complete ties\n const withIndex = dictionaries.map((dictionary, index) => ({\n dictionary,\n index,\n }));\n\n const getPriority = (dictionary: Dictionary): number => {\n const p = dictionary.priority ?? 0;\n\n return Number.isFinite(p) ? p : 0;\n };\n\n const getLocationWeight = (d: Dictionary): number => {\n const location = d.location;\n\n // undefined location should always be last\n if (location === undefined) {\n return 2;\n }\n\n if (dictionaryPriorityStrategy === 'distant_first') {\n // distant should come first\n return location === 'remote' ? 0 : 1;\n }\n // default: local_first\n return location === 'local' ? 0 : 1;\n };\n\n withIndex.sort((a, b) => {\n // 1) Non-autoFilled before autoFilled (autoFilled have lower precedence)\n const aAuto = a.dictionary.filled ? 1 : 0;\n const bAuto = b.dictionary.filled ? 1 : 0;\n if (aAuto !== bAuto) return aAuto - bAuto; // 0 before 1\n\n // 2) Higher priority first (larger number wins)\n const aP = getPriority(a.dictionary);\n const bP = getPriority(b.dictionary);\n if (aP !== bP) return bP - aP; // descending\n\n // 3) Location according to strategy\n const aLocation = getLocationWeight(a.dictionary);\n const bLocation = getLocationWeight(b.dictionary);\n if (aLocation !== bLocation) return aLocation - bLocation;\n\n // 4) Stable fallback by original index\n return a.index - b.index;\n });\n\n return withIndex.map(({ dictionary }) => dictionary);\n};\n"],"mappings":";;;;;;;;;;AAUA,MAAa,qBACX,cACA,WAKiB;CACjB,MAAM,6BACJ,QAAQ,QAAQ,8BAChB,OAAO;CAET,IAAI,aAAa,UAAU,GACzB,OAAO;CAIT,MAAM,YAAY,aAAa,KAAK,YAAY,WAAW;EACzD;EACA;EACD,EAAE;CAEH,MAAM,eAAe,eAAmC;EACtD,MAAM,IAAI,WAAW,YAAY;EAEjC,OAAO,OAAO,SAAS,EAAE,GAAG,IAAI;;CAGlC,MAAM,qBAAqB,MAA0B;EACnD,MAAM,WAAW,EAAE;EAGnB,IAAI,aAAa,QACf,OAAO;EAGT,IAAI,+BAA+B,iBAEjC,OAAO,aAAa,WAAW,IAAI;EAGrC,OAAO,aAAa,UAAU,IAAI;;CAGpC,UAAU,MAAM,GAAG,MAAM;EAEvB,MAAM,QAAQ,EAAE,WAAW,SAAS,IAAI;EACxC,MAAM,QAAQ,EAAE,WAAW,SAAS,IAAI;EACxC,IAAI,UAAU,OAAO,OAAO,QAAQ;EAGpC,MAAM,KAAK,YAAY,EAAE,WAAW;EACpC,MAAM,KAAK,YAAY,EAAE,WAAW;EACpC,IAAI,OAAO,IAAI,OAAO,KAAK;EAG3B,MAAM,YAAY,kBAAkB,EAAE,WAAW;EACjD,MAAM,YAAY,kBAAkB,EAAE,WAAW;EACjD,IAAI,cAAc,WAAW,OAAO,YAAY;EAGhD,OAAO,EAAE,QAAQ,EAAE;GACnB;CAEF,OAAO,UAAU,KAAK,EAAE,iBAAiB,WAAW"}
@@ -83,7 +83,8 @@ import { createRenderer, renderFor } from "./markdown/renderer.mjs";
83
83
  import { compile, compileWithOptions, createCompiler } from "./markdown/compiler.mjs";
84
84
  import { icuToIntlayerFormatter, intlayerToICUFormatter } from "./messageFormat/ICU.mjs";
85
85
  import { i18nextToIntlayerFormatter, intlayerToI18nextFormatter } from "./messageFormat/i18next.mjs";
86
+ import { intlayerToPortableObjectFormatter, portableObjectToIntlayerFormatter } from "./messageFormat/po.mjs";
86
87
  import { intlayerToVueI18nFormatter, vueI18nToIntlayerFormatter } from "./messageFormat/vue-i18n.mjs";
87
88
  import { isSameKeyPath } from "./utils/isSameKeyPath.mjs";
88
89
 
89
- 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_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, 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, parseSimpleInline, parseStyleAttribute, parseTableAlign, parseTableAlignCapture, parseTableCells, parseTableRow, parseYaml, parserFor, percentage, plural, pluralPlugin, presets, qualifies, relativeTime, removeContentNodeByKeyPath, renameContentNodeByKeyPath, renderFor, renderNothing, sanitizer, setLocaleInStorage, setLocaleInStorageClient, setLocaleInStorageServer, simpleInlineRegex, slugify, some, splitInsertionTemplate, startsWith, translation as t, translationPlugin, trimEnd, trimLeadingWhitespaceOutsideFences, unescapeString, units, unquote, updateNodeChildren, validateHTML, validateMarkdown, validatePrefix, vueI18nToIntlayerFormatter };
90
+ 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_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, parseSimpleInline, parseStyleAttribute, parseTableAlign, parseTableAlignCapture, parseTableCells, parseTableRow, parseYaml, parserFor, percentage, plural, pluralPlugin, portableObjectToIntlayerFormatter, presets, qualifies, relativeTime, removeContentNodeByKeyPath, renameContentNodeByKeyPath, renderFor, renderNothing, sanitizer, setLocaleInStorage, setLocaleInStorageClient, setLocaleInStorageServer, simpleInlineRegex, slugify, some, splitInsertionTemplate, startsWith, translation as t, translationPlugin, trimEnd, trimLeadingWhitespaceOutsideFences, unescapeString, units, unquote, updateNodeChildren, validateHTML, validateMarkdown, validatePrefix, vueI18nToIntlayerFormatter };
@@ -3,6 +3,7 @@ import { enu as enumeration } from "../transpiler/enumeration/enumeration.mjs";
3
3
  import { gender } from "../transpiler/gender/gender.mjs";
4
4
  import { html } from "../transpiler/html/html.mjs";
5
5
  import { insert as insertion } from "../transpiler/insertion/insertion.mjs";
6
+ import { plural } from "../transpiler/plural/plural.mjs";
6
7
  import * as NodeTypes from "@intlayer/types/nodeType";
7
8
 
8
9
  //#region src/messageFormat/ICU.ts
@@ -156,21 +157,34 @@ const icuNodesToIntlayer = (nodes) => {
156
157
  }
157
158
  if (node.type === "plural") {
158
159
  const options = {};
159
- for (const [key, val] of Object.entries(node.options)) {
160
- let newKey = key;
161
- if (key.startsWith("=")) newKey = key.substring(1);
162
- else if (key === "one") newKey = "1";
163
- else if (key === "two") newKey = "2";
164
- else if (key === "few") newKey = "<=3";
165
- else if (key === "many") newKey = ">=4";
166
- else if (key === "other") newKey = "fallback";
167
- options[newKey] = icuNodesToIntlayer(val.map((v) => {
160
+ let hasExactMatch = false;
161
+ for (const key of Object.keys(node.options)) if (key.startsWith("=")) {
162
+ hasExactMatch = true;
163
+ break;
164
+ }
165
+ if (hasExactMatch) {
166
+ for (const [key, val] of Object.entries(node.options)) {
167
+ let newKey = key;
168
+ if (key.startsWith("=")) newKey = key.substring(1);
169
+ else if (key === "one") newKey = "1";
170
+ else if (key === "two") newKey = "2";
171
+ else if (key === "few") newKey = "<=3";
172
+ else if (key === "many") newKey = ">=4";
173
+ else if (key === "other") newKey = "fallback";
174
+ options[newKey] = icuNodesToIntlayer(val.map((v) => {
175
+ if (typeof v === "string") return v.replace(/#/g, `{{${node.name}}}`);
176
+ return v;
177
+ }));
178
+ }
179
+ options.__intlayer_icu_var = node.name;
180
+ return enumeration(options);
181
+ } else {
182
+ for (const [key, val] of Object.entries(node.options)) options[key] = icuNodesToIntlayer(val.map((v) => {
168
183
  if (typeof v === "string") return v.replace(/#/g, `{{${node.name}}}`);
169
184
  return v;
170
185
  }));
186
+ return plural(options);
171
187
  }
172
- options.__intlayer_icu_var = node.name;
173
- return enumeration(options);
174
188
  }
175
189
  if (node.type === "select") {
176
190
  const options = {};
@@ -205,7 +219,7 @@ const icuToIntlayerPlugin = {
205
219
  const intlayerToIcuPlugin = {
206
220
  canHandle: (node) => {
207
221
  if (typeof node === "string" && (node.includes("{") || node.includes("}"))) return true;
208
- if (node && typeof node === "object" && (node.nodeType === NodeTypes.INSERTION || node.nodeType === NodeTypes.HTML || node.nodeType === NodeTypes.ENUMERATION || node.nodeType === NodeTypes.GENDER || node.nodeType === "composite")) return true;
222
+ if (node && typeof node === "object" && (node.nodeType === NodeTypes.INSERTION || node.nodeType === NodeTypes.HTML || node.nodeType === NodeTypes.ENUMERATION || node.nodeType === NodeTypes.PLURAL || node.nodeType === NodeTypes.GENDER || node.nodeType === "composite")) return true;
209
223
  if (Array.isArray(node)) {
210
224
  if (node.length === 0) return false;
211
225
  let hasNode = false;
@@ -222,6 +236,27 @@ const intlayerToIcuPlugin = {
222
236
  if (typeof node === "string") return node.replace(/\{\{([^}]+)\}\}/g, "{$1}");
223
237
  if (node.nodeType === NodeTypes.INSERTION) return node[NodeTypes.INSERTION].replace(/\{\{([^}]+)\}\}/g, "{$1}");
224
238
  if (node.nodeType === NodeTypes.HTML) return node[NodeTypes.HTML];
239
+ if (node.nodeType === NodeTypes.PLURAL) {
240
+ const options = node[NodeTypes.PLURAL];
241
+ const transformedOptions = {};
242
+ for (const [key, val] of Object.entries(options)) {
243
+ const childVal = next(val, props);
244
+ transformedOptions[key] = typeof childVal === "string" ? childVal : JSON.stringify(childVal);
245
+ }
246
+ let varName = "count";
247
+ const fallbackVal = transformedOptions.other || Object.values(transformedOptions)[0];
248
+ if (fallbackVal) {
249
+ const match = fallbackVal.match(/\{([a-zA-Z0-9_]+)\}(?!,)/);
250
+ if (match) varName = match[1];
251
+ }
252
+ const parts = [];
253
+ for (const [key, val] of Object.entries(transformedOptions)) {
254
+ let strVal = val;
255
+ strVal = strVal.replace(new RegExp(`\\{${varName}\\}`, "g"), "#");
256
+ parts.push(`${key} {${strVal}}`);
257
+ }
258
+ return `{${varName}, plural, ${parts.join(" ")}}`;
259
+ }
225
260
  if (node.nodeType === NodeTypes.ENUMERATION) {
226
261
  const options = node[NodeTypes.ENUMERATION];
227
262
  const transformedOptions = {};
@@ -1 +1 @@
1
- {"version":3,"file":"ICU.mjs","names":["insert","enu"],"sources":["../../../src/messageFormat/ICU.ts"],"sourcesContent":["import type { Dictionary } from '@intlayer/types/dictionary';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { deepTransformNode } from '../interpreter';\nimport { enu, gender, html, insert } from '../transpiler';\n\n/**\n * ICU MessageFormat Converter\n *\n * This module converts between ICU MessageFormat and Intlayer's internal format.\n *\n * IMPORTANT: Two different formats are used:\n *\n * 1. ICU MessageFormat (external format):\n * - Simple variables: {name}\n * - Formatted variables: {amount, number, currency}\n * - Plural: {count, plural, =0 {none} other {# items}}\n * - Select: {gender, select, male {He} female {She} other {They}}\n *\n * 2. Intlayer Internal Format:\n * - Simple variables: {{name}} (double braces for clarity and to distinguish from literal text)\n * - Formatted variables: {amount, number, currency} (keeps ICU format)\n * - Plural: enu({ 0: 'none', fallback: '{{count}} items' })\n * - Select/Gender: gender({ male: 'He', female: 'She', fallback: 'They' })\n *\n * Conversion flow:\n * - ICU → Intlayer: {name} → {{name}}\n * - Intlayer → ICU: {{name}} → {name}\n *\n * The double braces in Intlayer format serve to:\n * - Distinguish variables from literal text containing braces\n * - Work with getInsertion() runtime function which expects {{var}} patterns\n * - Provide clear visual distinction in content dictionaries\n */\n\n// Types for our AST\ntype ICUNode =\n | string\n | {\n type: 'argument';\n name: string;\n format?: { type: string; style?: string };\n }\n | { type: 'plural'; name: string; options: Record<string, ICUNode[]> }\n | { type: 'select'; name: string; options: Record<string, ICUNode[]> };\n\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nconst parseICU = (text: string): ICUNode[] => {\n let index = 0;\n\n const parseNodes = (): ICUNode[] => {\n const nodes: ICUNode[] = [];\n let currentText = '';\n\n while (index < text.length) {\n const char = text[index];\n\n if (char === '{') {\n if (currentText) {\n nodes.push(currentText);\n currentText = '';\n }\n index++; // skip {\n nodes.push(parseArgument());\n } else if (char === '}') {\n // End of current block\n break;\n } else if (char === \"'\") {\n // Escaping\n if (index + 1 < text.length && text[index + 1] === \"'\") {\n currentText += \"'\";\n index += 2;\n } else {\n // Find next quote\n const nextQuote = text.indexOf(\"'\", index + 1);\n if (nextQuote !== -1) {\n // Determine if this is escaping syntax characters\n // For simplicity, we'll treat content between single quotes as literal\n // provided it contains syntax chars.\n // Standard ICU: ' quoted string '\n // If it is just an apostrophe, it should be doubled.\n // But simplified: take content between quotes literally.\n currentText += text.substring(index + 1, nextQuote);\n index = nextQuote + 1;\n } else {\n currentText += \"'\";\n index++;\n }\n }\n } else {\n currentText += char;\n index++;\n }\n }\n\n if (currentText) {\n nodes.push(currentText);\n }\n return nodes;\n };\n\n const parseArgument = (): ICUNode => {\n // We are past '{'\n // Parse name\n let name = '';\n while (index < text.length && /[^,}]/.test(text[index])) {\n name += text[index];\n index++;\n }\n name = name.trim();\n\n if (index >= text.length) throw new Error('Unclosed argument');\n\n if (text[index] === '}') {\n index++;\n return { type: 'argument', name };\n }\n\n // Must be comma\n if (text[index] === ',') {\n index++;\n // Parse type\n let type = '';\n while (index < text.length && /[^,}]/.test(text[index])) {\n type += text[index];\n index++;\n }\n type = type.trim();\n\n if (index >= text.length) throw new Error('Unclosed argument');\n\n if (text[index] === '}') {\n index++;\n return { type: 'argument', name, format: { type } };\n }\n\n if (text[index] === ',') {\n index++;\n\n // If plural or select, parse options\n if (type === 'plural' || type === 'select') {\n // Parse options\n const options: Record<string, ICUNode[]> = {};\n\n while (index < text.length && text[index] !== '}') {\n // skip whitespace\n while (index < text.length && /\\s/.test(text[index])) index++;\n\n // parse key\n let key = '';\n while (index < text.length && /[^{\\s]/.test(text[index])) {\n key += text[index];\n index++;\n }\n\n while (index < text.length && /\\s/.test(text[index])) index++;\n\n if (text[index] !== '{')\n throw new Error('Expected { after option key');\n index++; // skip {\n\n const value = parseNodes();\n\n if (text[index] !== '}')\n throw new Error('Expected } after option value');\n index++; // skip }\n\n options[key] = value;\n\n while (index < text.length && /\\s/.test(text[index])) index++;\n }\n\n index++; // skip closing argument }\n\n if (type === 'plural') {\n return { type: 'plural', name, options };\n } else if (type === 'select') {\n return { type: 'select', name, options };\n }\n } else {\n // Parse style for number/date/time\n let style = '';\n while (index < text.length && text[index] !== '}') {\n style += text[index];\n index++;\n }\n if (index >= text.length) throw new Error('Unclosed argument');\n\n style = style.trim();\n index++; // skip }\n\n return { type: 'argument', name, format: { type, style } };\n }\n }\n }\n\n throw new Error('Malformed argument');\n };\n\n return parseNodes();\n};\n\nconst icuNodesToIntlayer = (nodes: ICUNode[]): any => {\n if (nodes.length === 0) return '';\n if (nodes.length === 1 && typeof nodes[0] === 'string') {\n const node = nodes[0];\n if (/<[a-zA-Z0-9-]+[^>]*>/.test(node)) {\n return html(node);\n }\n return node;\n }\n\n // Check if we can flatten to a single string (insert)\n const canFlatten = nodes.every(\n (node) => typeof node === 'string' || node.type === 'argument'\n );\n if (canFlatten) {\n let str = '';\n for (const node of nodes) {\n if (typeof node === 'string') {\n str += node;\n } else if (typeof node !== 'string' && node.type === 'argument') {\n if (node.format) {\n // Formatted variables keep ICU format: {var, type, style}\n str += `{${node.name}, ${node.format.type}${\n node.format.style ? `, ${node.format.style}` : ''\n }}`;\n } else {\n // Simple variables use Intlayer format: {{var}}\n str += `{{${node.name}}}`;\n }\n }\n }\n if (/<[a-zA-Z0-9-]+[^>]*>/.test(str)) {\n return html(str);\n }\n return insert(str);\n }\n\n // Mix of string and complex types.\n // If we have just one complex type and it covers everything?\n if (nodes.length === 1) {\n const node = nodes[0];\n\n if (typeof node === 'string') {\n if (/<[a-zA-Z0-9-]+[^>]*>/.test(node)) {\n return html(node);\n }\n return node;\n }\n if (node.type === 'argument') {\n if (node.format) {\n return insert(\n `{${node.name}, ${node.format.type}${\n node.format.style ? `, ${node.format.style}` : ''\n }}`\n );\n }\n return insert(`{{${node.name}}}`);\n }\n if (node.type === 'plural') {\n const options: Record<string, any> = {};\n\n for (const [key, val] of Object.entries(node.options)) {\n // Map ICU keys to Intlayer keys\n let newKey = key;\n if (key.startsWith('=')) {\n newKey = key.substring(1); // =0 -> 0\n } else if (key === 'one') {\n newKey = '1';\n } else if (key === 'two') {\n newKey = '2';\n } else if (key === 'few') {\n newKey = '<=3';\n } else if (key === 'many') {\n newKey = '>=4';\n } else if (key === 'other') {\n newKey = 'fallback';\n }\n // Handle # in plural value\n // For plural, we need to pass the variable name down or replace #\n // Intlayer uses {{n}} (or whatever var name) for simple variables\n // We should replace # with {{n}} in the string parts of val\n const replacedVal = val.map((v) => {\n if (typeof v === 'string') {\n return v.replace(/#/g, `{{${node.name}}}`);\n }\n return v;\n });\n\n options[newKey] = icuNodesToIntlayer(replacedVal);\n }\n\n // Preserve variable name\n options.__intlayer_icu_var = node.name;\n\n return enu(options);\n }\n if (node.type === 'select') {\n const options: Record<string, any> = {};\n\n for (const [key, val] of Object.entries(node.options)) {\n options[key] = icuNodesToIntlayer(val);\n }\n\n // Check if it looks like gender\n const optionKeys = Object.keys(options);\n // It is gender if it has 'male' OR 'female' AND only contains gender keys (male, female, other)\n const isGender =\n (options.male || options.female) &&\n optionKeys.every((k) =>\n ['male', 'female', 'other', 'fallback'].includes(k)\n );\n\n if (isGender) {\n return gender({\n fallback: options.other,\n male: options.male,\n female: options.female,\n });\n }\n\n // Preserve variable name\n options.__intlayer_icu_var = node.name;\n\n return enu(options);\n }\n }\n\n // If multiple nodes, return array\n return nodes.map((node) => icuNodesToIntlayer([node]));\n};\n\nconst icuToIntlayerPlugin = {\n canHandle: (node: any) =>\n typeof node === 'string' &&\n (node.includes('{') ||\n node.includes('}') ||\n /<[a-zA-Z0-9-]+[^>]*>/.test(node)),\n transform: (node: any) => {\n try {\n const ast = parseICU(node);\n return icuNodesToIntlayer(ast);\n } catch {\n // If parsing fails, return original string\n return node;\n }\n },\n};\n\nconst intlayerToIcuPlugin = {\n canHandle: (node: any) => {\n if (\n typeof node === 'string' &&\n (node.includes('{') || node.includes('}'))\n ) {\n return true;\n }\n\n if (\n node &&\n typeof node === 'object' &&\n (node.nodeType === NodeTypes.INSERTION ||\n node.nodeType === NodeTypes.HTML ||\n node.nodeType === NodeTypes.ENUMERATION ||\n node.nodeType === NodeTypes.GENDER ||\n node.nodeType === 'composite')\n ) {\n return true;\n }\n\n if (Array.isArray(node)) {\n if (node.length === 0) return false;\n\n let hasNode = false;\n let hasPlainObjectOrArray = false;\n\n for (const item of node) {\n if (typeof item === 'string') {\n } else if (\n item &&\n typeof item === 'object' &&\n (item.nodeType === NodeTypes.INSERTION ||\n item.nodeType === NodeTypes.HTML ||\n item.nodeType === NodeTypes.ENUMERATION ||\n item.nodeType === NodeTypes.GENDER ||\n item.nodeType === 'composite')\n ) {\n hasNode = true;\n } else {\n hasPlainObjectOrArray = true;\n }\n }\n\n // If it contains plain objects or nested arrays, it's a structural array\n if (hasPlainObjectOrArray) return false;\n // If it contains ONLY strings, it's a structural array, not a composite string\n if (!hasNode) return false;\n\n return true;\n }\n\n return false;\n },\n transform: (node: any, props: any, next: any) => {\n // Convert Intlayer's double-brace format {{var}} to ICU's single-brace format {var}\n if (typeof node === 'string') {\n return node.replace(/\\{\\{([^}]+)\\}\\}/g, '{$1}');\n }\n\n if (node.nodeType === NodeTypes.INSERTION) {\n return node[NodeTypes.INSERTION].replace(/\\{\\{([^}]+)\\}\\}/g, '{$1}');\n }\n\n if (node.nodeType === NodeTypes.HTML) {\n return node[NodeTypes.HTML];\n }\n\n if (node.nodeType === NodeTypes.ENUMERATION) {\n const options = node[NodeTypes.ENUMERATION];\n\n const transformedOptions: Record<string, string> = {};\n for (const [key, val] of Object.entries(options)) {\n if (key === '__intlayer_icu_var') continue;\n const childVal = next(val, props);\n transformedOptions[key] =\n typeof childVal === 'string' ? childVal : JSON.stringify(childVal);\n }\n\n let varName = options.__intlayer_icu_var || 'n';\n\n if (!options.__intlayer_icu_var) {\n const fallbackVal =\n transformedOptions.fallback ||\n transformedOptions.other ||\n Object.values(transformedOptions)[0];\n const match = fallbackVal.match(/\\{([a-zA-Z0-9_]+)\\}(?!,)/);\n if (match) {\n varName = match[1];\n }\n }\n\n const keys = Object.keys(transformedOptions);\n const pluralKeys = [\n '1',\n '2',\n '<=3',\n '>=4',\n 'fallback',\n 'other',\n 'zero',\n 'one',\n 'two',\n 'few',\n 'many',\n ];\n\n const isPlural = keys.every(\n (k) => pluralKeys.includes(k) || /^[<>=]?\\d+(\\.\\d+)?$/.test(k)\n );\n\n const parts = [];\n\n if (isPlural) {\n for (const [key, val] of Object.entries(transformedOptions)) {\n let icuKey = key;\n if (key === 'fallback') icuKey = 'other';\n else if (key === '<=3') icuKey = 'few';\n else if (key === '>=4') icuKey = 'many';\n else if (/^\\d+$/.test(key)) icuKey = `=${key}`;\n else if (['zero', 'few', 'many'].includes(key)) icuKey = key;\n else icuKey = 'other';\n\n let strVal = val;\n strVal = strVal.replace(new RegExp(`\\\\{${varName}\\\\}`, 'g'), '#');\n parts.push(`${icuKey} {${strVal}}`);\n }\n return `{${varName}, plural, ${parts.join(' ')}}`;\n } else {\n const entries = Object.entries(transformedOptions).sort(\n ([keyA], [keyB]) => {\n if (keyA === 'fallback' || keyA === 'other') return 1;\n if (keyB === 'fallback' || keyB === 'other') return -1;\n return 0;\n }\n );\n\n for (const [key, val] of entries) {\n let icuKey = key;\n if (key === 'fallback') icuKey = 'other';\n parts.push(`${icuKey} {${val}}`);\n }\n return `{${varName}, select, ${parts.join(' ')}}`;\n }\n }\n\n if (node.nodeType === NodeTypes.GENDER) {\n const options = node[NodeTypes.GENDER];\n const varName = 'gender';\n const parts = [];\n\n const entries = Object.entries(options).sort(([keyA], [keyB]) => {\n if (keyA === 'fallback') return 1;\n if (keyB === 'fallback') return -1;\n return 0;\n });\n\n for (const [key, val] of entries) {\n let icuKey = key;\n if (key === 'fallback') icuKey = 'other';\n\n const childVal = next(val, props);\n const strVal =\n typeof childVal === 'string' ? childVal : JSON.stringify(childVal);\n\n parts.push(`${icuKey} {${strVal}}`);\n }\n return `{${varName}, select, ${parts.join(' ')}}`;\n }\n\n if (\n Array.isArray(node) ||\n (node.nodeType === 'composite' && Array.isArray(node.composite))\n ) {\n // handle array/composite strings that passed canHandle\n const arr = Array.isArray(node) ? node : node.composite;\n const items = arr.map((item: any) => next(item, props));\n return items.join('');\n }\n\n return next(node, props);\n },\n};\n\nexport const intlayerToICUFormatter = (\n message: Dictionary['content']\n): JsonValue => {\n return deepTransformNode(message, {\n dictionaryKey: 'icu',\n keyPath: [],\n plugins: [{ id: 'icu', ...intlayerToIcuPlugin }],\n });\n};\n\nexport const icuToIntlayerFormatter = (\n message: Dictionary['content']\n): JsonValue => {\n return deepTransformNode(message, {\n dictionaryKey: 'icu',\n keyPath: [],\n plugins: [{ id: 'icu', ...icuToIntlayerPlugin }],\n });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ICU.mjs","names":["insert","enu"],"sources":["../../../src/messageFormat/ICU.ts"],"sourcesContent":["import type { Dictionary } from '@intlayer/types/dictionary';\nimport * as NodeTypes from '@intlayer/types/nodeType';\nimport { deepTransformNode } from '../interpreter';\nimport { enu, gender, html, insert, plural } from '../transpiler';\n\n/**\n * ICU MessageFormat Converter\n *\n * This module converts between ICU MessageFormat and Intlayer's internal format.\n *\n * IMPORTANT: Two different formats are used:\n *\n * 1. ICU MessageFormat (external format):\n * - Simple variables: {name}\n * - Formatted variables: {amount, number, currency}\n * - Plural: {count, plural, =0 {none} other {# items}}\n * - Select: {gender, select, male {He} female {She} other {They}}\n *\n * 2. Intlayer Internal Format:\n * - Simple variables: {{name}} (double braces for clarity and to distinguish from literal text)\n * - Formatted variables: {amount, number, currency} (keeps ICU format)\n * - Plural: enu({ 0: 'none', fallback: '{{count}} items' })\n * - Select/Gender: gender({ male: 'He', female: 'She', fallback: 'They' })\n *\n * Conversion flow:\n * - ICU → Intlayer: {name} → {{name}}\n * - Intlayer → ICU: {{name}} → {name}\n *\n * The double braces in Intlayer format serve to:\n * - Distinguish variables from literal text containing braces\n * - Work with getInsertion() runtime function which expects {{var}} patterns\n * - Provide clear visual distinction in content dictionaries\n */\n\n// Types for our AST\ntype ICUNode =\n | string\n | {\n type: 'argument';\n name: string;\n format?: { type: string; style?: string };\n }\n | { type: 'plural'; name: string; options: Record<string, ICUNode[]> }\n | { type: 'select'; name: string; options: Record<string, ICUNode[]> };\n\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nconst parseICU = (text: string): ICUNode[] => {\n let index = 0;\n\n const parseNodes = (): ICUNode[] => {\n const nodes: ICUNode[] = [];\n let currentText = '';\n\n while (index < text.length) {\n const char = text[index];\n\n if (char === '{') {\n if (currentText) {\n nodes.push(currentText);\n currentText = '';\n }\n index++; // skip {\n nodes.push(parseArgument());\n } else if (char === '}') {\n // End of current block\n break;\n } else if (char === \"'\") {\n // Escaping\n if (index + 1 < text.length && text[index + 1] === \"'\") {\n currentText += \"'\";\n index += 2;\n } else {\n // Find next quote\n const nextQuote = text.indexOf(\"'\", index + 1);\n if (nextQuote !== -1) {\n // Determine if this is escaping syntax characters\n // For simplicity, we'll treat content between single quotes as literal\n // provided it contains syntax chars.\n // Standard ICU: ' quoted string '\n // If it is just an apostrophe, it should be doubled.\n // But simplified: take content between quotes literally.\n currentText += text.substring(index + 1, nextQuote);\n index = nextQuote + 1;\n } else {\n currentText += \"'\";\n index++;\n }\n }\n } else {\n currentText += char;\n index++;\n }\n }\n\n if (currentText) {\n nodes.push(currentText);\n }\n return nodes;\n };\n\n const parseArgument = (): ICUNode => {\n // We are past '{'\n // Parse name\n let name = '';\n while (index < text.length && /[^,}]/.test(text[index])) {\n name += text[index];\n index++;\n }\n name = name.trim();\n\n if (index >= text.length) throw new Error('Unclosed argument');\n\n if (text[index] === '}') {\n index++;\n return { type: 'argument', name };\n }\n\n // Must be comma\n if (text[index] === ',') {\n index++;\n // Parse type\n let type = '';\n while (index < text.length && /[^,}]/.test(text[index])) {\n type += text[index];\n index++;\n }\n type = type.trim();\n\n if (index >= text.length) throw new Error('Unclosed argument');\n\n if (text[index] === '}') {\n index++;\n return { type: 'argument', name, format: { type } };\n }\n\n if (text[index] === ',') {\n index++;\n\n // If plural or select, parse options\n if (type === 'plural' || type === 'select') {\n // Parse options\n const options: Record<string, ICUNode[]> = {};\n\n while (index < text.length && text[index] !== '}') {\n // skip whitespace\n while (index < text.length && /\\s/.test(text[index])) index++;\n\n // parse key\n let key = '';\n while (index < text.length && /[^{\\s]/.test(text[index])) {\n key += text[index];\n index++;\n }\n\n while (index < text.length && /\\s/.test(text[index])) index++;\n\n if (text[index] !== '{')\n throw new Error('Expected { after option key');\n index++; // skip {\n\n const value = parseNodes();\n\n if (text[index] !== '}')\n throw new Error('Expected } after option value');\n index++; // skip }\n\n options[key] = value;\n\n while (index < text.length && /\\s/.test(text[index])) index++;\n }\n\n index++; // skip closing argument }\n\n if (type === 'plural') {\n return { type: 'plural', name, options };\n } else if (type === 'select') {\n return { type: 'select', name, options };\n }\n } else {\n // Parse style for number/date/time\n let style = '';\n while (index < text.length && text[index] !== '}') {\n style += text[index];\n index++;\n }\n if (index >= text.length) throw new Error('Unclosed argument');\n\n style = style.trim();\n index++; // skip }\n\n return { type: 'argument', name, format: { type, style } };\n }\n }\n }\n\n throw new Error('Malformed argument');\n };\n\n return parseNodes();\n};\n\nconst icuNodesToIntlayer = (nodes: ICUNode[]): any => {\n if (nodes.length === 0) return '';\n if (nodes.length === 1 && typeof nodes[0] === 'string') {\n const node = nodes[0];\n if (/<[a-zA-Z0-9-]+[^>]*>/.test(node)) {\n return html(node);\n }\n return node;\n }\n\n // Check if we can flatten to a single string (insert)\n const canFlatten = nodes.every(\n (node) => typeof node === 'string' || node.type === 'argument'\n );\n if (canFlatten) {\n let str = '';\n for (const node of nodes) {\n if (typeof node === 'string') {\n str += node;\n } else if (typeof node !== 'string' && node.type === 'argument') {\n if (node.format) {\n // Formatted variables keep ICU format: {var, type, style}\n str += `{${node.name}, ${node.format.type}${\n node.format.style ? `, ${node.format.style}` : ''\n }}`;\n } else {\n // Simple variables use Intlayer format: {{var}}\n str += `{{${node.name}}}`;\n }\n }\n }\n if (/<[a-zA-Z0-9-]+[^>]*>/.test(str)) {\n return html(str);\n }\n return insert(str);\n }\n\n // Mix of string and complex types.\n // If we have just one complex type and it covers everything?\n if (nodes.length === 1) {\n const node = nodes[0];\n\n if (typeof node === 'string') {\n if (/<[a-zA-Z0-9-]+[^>]*>/.test(node)) {\n return html(node);\n }\n return node;\n }\n if (node.type === 'argument') {\n if (node.format) {\n return insert(\n `{${node.name}, ${node.format.type}${\n node.format.style ? `, ${node.format.style}` : ''\n }}`\n );\n }\n return insert(`{{${node.name}}}`);\n }\n if (node.type === 'plural') {\n const options: Record<string, any> = {};\n let hasExactMatch = false;\n\n for (const key of Object.keys(node.options)) {\n if (key.startsWith('=')) {\n hasExactMatch = true;\n break;\n }\n }\n\n if (hasExactMatch) {\n for (const [key, val] of Object.entries(node.options)) {\n // Map ICU keys to Intlayer keys\n let newKey = key;\n if (key.startsWith('=')) {\n newKey = key.substring(1); // =0 -> 0\n } else if (key === 'one') {\n newKey = '1';\n } else if (key === 'two') {\n newKey = '2';\n } else if (key === 'few') {\n newKey = '<=3';\n } else if (key === 'many') {\n newKey = '>=4';\n } else if (key === 'other') {\n newKey = 'fallback';\n }\n // Handle # in plural value\n // For plural, we need to pass the variable name down or replace #\n // Intlayer uses {{n}} (or whatever var name) for simple variables\n // We should replace # with {{n}} in the string parts of val\n const replacedVal = val.map((v) => {\n if (typeof v === 'string') {\n return v.replace(/#/g, `{{${node.name}}}`);\n }\n return v;\n });\n\n options[newKey] = icuNodesToIntlayer(replacedVal);\n }\n\n // Preserve variable name\n options.__intlayer_icu_var = node.name;\n\n return enu(options);\n } else {\n for (const [key, val] of Object.entries(node.options)) {\n // Handle # in plural value\n const replacedVal = val.map((v) => {\n if (typeof v === 'string') {\n return v.replace(/#/g, `{{${node.name}}}`);\n }\n return v;\n });\n\n options[key] = icuNodesToIntlayer(replacedVal);\n }\n\n return plural(options as any);\n }\n }\n if (node.type === 'select') {\n const options: Record<string, any> = {};\n\n for (const [key, val] of Object.entries(node.options)) {\n options[key] = icuNodesToIntlayer(val);\n }\n\n // Check if it looks like gender\n const optionKeys = Object.keys(options);\n // It is gender if it has 'male' OR 'female' AND only contains gender keys (male, female, other)\n const isGender =\n (options.male || options.female) &&\n optionKeys.every((k) =>\n ['male', 'female', 'other', 'fallback'].includes(k)\n );\n\n if (isGender) {\n return gender({\n fallback: options.other,\n male: options.male,\n female: options.female,\n });\n }\n\n // Preserve variable name\n options.__intlayer_icu_var = node.name;\n\n return enu(options);\n }\n }\n\n // If multiple nodes, return array\n return nodes.map((node) => icuNodesToIntlayer([node]));\n};\n\nconst icuToIntlayerPlugin = {\n canHandle: (node: any) =>\n typeof node === 'string' &&\n (node.includes('{') ||\n node.includes('}') ||\n /<[a-zA-Z0-9-]+[^>]*>/.test(node)),\n transform: (node: any) => {\n try {\n const ast = parseICU(node);\n return icuNodesToIntlayer(ast);\n } catch {\n // If parsing fails, return original string\n return node;\n }\n },\n};\n\nconst intlayerToIcuPlugin = {\n canHandle: (node: any) => {\n if (\n typeof node === 'string' &&\n (node.includes('{') || node.includes('}'))\n ) {\n return true;\n }\n\n if (\n node &&\n typeof node === 'object' &&\n (node.nodeType === NodeTypes.INSERTION ||\n node.nodeType === NodeTypes.HTML ||\n node.nodeType === NodeTypes.ENUMERATION ||\n node.nodeType === NodeTypes.PLURAL ||\n node.nodeType === NodeTypes.GENDER ||\n node.nodeType === 'composite')\n ) {\n return true;\n }\n\n if (Array.isArray(node)) {\n if (node.length === 0) return false;\n\n let hasNode = false;\n let hasPlainObjectOrArray = false;\n\n for (const item of node) {\n if (typeof item === 'string') {\n } else if (\n item &&\n typeof item === 'object' &&\n (item.nodeType === NodeTypes.INSERTION ||\n item.nodeType === NodeTypes.HTML ||\n item.nodeType === NodeTypes.ENUMERATION ||\n item.nodeType === NodeTypes.GENDER ||\n item.nodeType === 'composite')\n ) {\n hasNode = true;\n } else {\n hasPlainObjectOrArray = true;\n }\n }\n\n // If it contains plain objects or nested arrays, it's a structural array\n if (hasPlainObjectOrArray) return false;\n // If it contains ONLY strings, it's a structural array, not a composite string\n if (!hasNode) return false;\n\n return true;\n }\n\n return false;\n },\n transform: (node: any, props: any, next: any) => {\n // Convert Intlayer's double-brace format {{var}} to ICU's single-brace format {var}\n if (typeof node === 'string') {\n return node.replace(/\\{\\{([^}]+)\\}\\}/g, '{$1}');\n }\n\n if (node.nodeType === NodeTypes.INSERTION) {\n return node[NodeTypes.INSERTION].replace(/\\{\\{([^}]+)\\}\\}/g, '{$1}');\n }\n\n if (node.nodeType === NodeTypes.HTML) {\n return node[NodeTypes.HTML];\n }\n\n if (node.nodeType === NodeTypes.PLURAL) {\n const options = node[NodeTypes.PLURAL];\n\n const transformedOptions: Record<string, string> = {};\n for (const [key, val] of Object.entries(options)) {\n const childVal = next(val, props);\n transformedOptions[key] =\n typeof childVal === 'string' ? childVal : JSON.stringify(childVal);\n }\n\n let varName = 'count';\n\n const fallbackVal =\n transformedOptions.other || Object.values(transformedOptions)[0];\n\n if (fallbackVal) {\n const match = fallbackVal.match(/\\{([a-zA-Z0-9_]+)\\}(?!,)/);\n if (match) {\n varName = match[1];\n }\n }\n\n const parts = [];\n\n for (const [key, val] of Object.entries(transformedOptions)) {\n let strVal = val;\n strVal = strVal.replace(new RegExp(`\\\\{${varName}\\\\}`, 'g'), '#');\n parts.push(`${key} {${strVal}}`);\n }\n return `{${varName}, plural, ${parts.join(' ')}}`;\n }\n\n if (node.nodeType === NodeTypes.ENUMERATION) {\n const options = node[NodeTypes.ENUMERATION];\n\n const transformedOptions: Record<string, string> = {};\n for (const [key, val] of Object.entries(options)) {\n if (key === '__intlayer_icu_var') continue;\n const childVal = next(val, props);\n transformedOptions[key] =\n typeof childVal === 'string' ? childVal : JSON.stringify(childVal);\n }\n\n let varName = options.__intlayer_icu_var || 'n';\n\n if (!options.__intlayer_icu_var) {\n const fallbackVal =\n transformedOptions.fallback ||\n transformedOptions.other ||\n Object.values(transformedOptions)[0];\n const match = fallbackVal.match(/\\{([a-zA-Z0-9_]+)\\}(?!,)/);\n if (match) {\n varName = match[1];\n }\n }\n\n const keys = Object.keys(transformedOptions);\n const pluralKeys = [\n '1',\n '2',\n '<=3',\n '>=4',\n 'fallback',\n 'other',\n 'zero',\n 'one',\n 'two',\n 'few',\n 'many',\n ];\n\n const isPlural = keys.every(\n (k) => pluralKeys.includes(k) || /^[<>=]?\\d+(\\.\\d+)?$/.test(k)\n );\n\n const parts = [];\n\n if (isPlural) {\n for (const [key, val] of Object.entries(transformedOptions)) {\n let icuKey = key;\n if (key === 'fallback') icuKey = 'other';\n else if (key === '<=3') icuKey = 'few';\n else if (key === '>=4') icuKey = 'many';\n else if (/^\\d+$/.test(key)) icuKey = `=${key}`;\n else if (['zero', 'few', 'many'].includes(key)) icuKey = key;\n else icuKey = 'other';\n\n let strVal = val;\n strVal = strVal.replace(new RegExp(`\\\\{${varName}\\\\}`, 'g'), '#');\n parts.push(`${icuKey} {${strVal}}`);\n }\n return `{${varName}, plural, ${parts.join(' ')}}`;\n } else {\n const entries = Object.entries(transformedOptions).sort(\n ([keyA], [keyB]) => {\n if (keyA === 'fallback' || keyA === 'other') return 1;\n if (keyB === 'fallback' || keyB === 'other') return -1;\n return 0;\n }\n );\n\n for (const [key, val] of entries) {\n let icuKey = key;\n if (key === 'fallback') icuKey = 'other';\n parts.push(`${icuKey} {${val}}`);\n }\n return `{${varName}, select, ${parts.join(' ')}}`;\n }\n }\n\n if (node.nodeType === NodeTypes.GENDER) {\n const options = node[NodeTypes.GENDER];\n const varName = 'gender';\n const parts = [];\n\n const entries = Object.entries(options).sort(([keyA], [keyB]) => {\n if (keyA === 'fallback') return 1;\n if (keyB === 'fallback') return -1;\n return 0;\n });\n\n for (const [key, val] of entries) {\n let icuKey = key;\n if (key === 'fallback') icuKey = 'other';\n\n const childVal = next(val, props);\n const strVal =\n typeof childVal === 'string' ? childVal : JSON.stringify(childVal);\n\n parts.push(`${icuKey} {${strVal}}`);\n }\n return `{${varName}, select, ${parts.join(' ')}}`;\n }\n\n if (\n Array.isArray(node) ||\n (node.nodeType === 'composite' && Array.isArray(node.composite))\n ) {\n // handle array/composite strings that passed canHandle\n const arr = Array.isArray(node) ? node : node.composite;\n const items = arr.map((item: any) => next(item, props));\n return items.join('');\n }\n\n return next(node, props);\n },\n};\n\nexport const intlayerToICUFormatter = (\n message: Dictionary['content']\n): JsonValue => {\n return deepTransformNode(message, {\n dictionaryKey: 'icu',\n keyPath: [],\n plugins: [{ id: 'icu', ...intlayerToIcuPlugin }],\n });\n};\n\nexport const icuToIntlayerFormatter = (\n message: Dictionary['content']\n): JsonValue => {\n return deepTransformNode(message, {\n dictionaryKey: 'icu',\n keyPath: [],\n plugins: [{ id: 'icu', ...icuToIntlayerPlugin }],\n });\n};\n"],"mappings":"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"}