UNPKG

@intlayer/core

Version:

Includes core Intlayer functions like translation, dictionary, and utility functions shared across multiple packages.

1 lines 22.7 kB
{"version":3,"file":"ICU.cjs","names":["nodes: ICUNode[]","options: Record<string, ICUNode[]>","insert","options: Record<string, any>","enu","gender","NodeType","transformedOptions: Record<string, string>","deepTransformNode"],"sources":["../../../src/messageFormat/ICU.ts"],"sourcesContent":["import { type Dictionary, NodeType } from '@intlayer/types';\nimport { deepTransformNode } from '../interpreter';\nimport { enu, gender, 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') return nodes[0];\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 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') return node; // already handled\n if (node.type === 'argument') {\n if (node.format) {\n // Formatted variables keep ICU format: {var, type, style}\n return insert(\n `{${node.name}, ${node.format.type}${\n node.format.style ? `, ${node.format.style}` : ''\n }}`\n );\n }\n // Simple variables use Intlayer format: {{var}}\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' && (node.includes('{') || node.includes('}')),\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 (typeof node === 'string' && (node.includes('{') || node.includes('}'))) ||\n (node &&\n typeof node === 'object' &&\n (node.nodeType === NodeType.Insertion ||\n node.nodeType === NodeType.Enumeration ||\n node.nodeType === NodeType.Gender ||\n node.nodeType === 'composite')) ||\n Array.isArray(node),\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 === NodeType.Insertion) {\n // Convert Intlayer format to ICU format:\n // - {{name}} → {name} (simple variable)\n // - {amount, number, currency} → {amount, number, currency} (formatted variable, already in ICU format)\n return node.insertion.replace(/\\{\\{([^}]+)\\}\\}/g, '{$1}');\n }\n\n if (node.nodeType === NodeType.Enumeration) {\n const options = node.enumeration;\n\n // Transform all values first\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 // Infer variable name\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 // Match {variable} but avoid {variable, ...} (which are nested ICUs)\n // Actually nested ICU starts with {var, ...\n // Simple variable is {var}\n // We look for {var} that is NOT followed by ,\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 // Also check for numbers\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\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\n // Replace {varName} with #\n // Note: next() has already converted {{var}} -> {var}\n strVal = strVal.replace(new RegExp(`\\\\{${varName}\\\\}`, 'g'), '#');\n\n parts.push(`${icuKey} {${strVal}}`);\n }\n\n return `{${varName}, plural, ${parts.join(' ')}}`;\n } else {\n // Select\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 // Do NOT map other keys to 'other'. Keep 'active', 'inactive', etc.\n\n parts.push(`${icuKey} {${val}}`);\n }\n return `{${varName}, select, ${parts.join(' ')}}`;\n }\n }\n\n if (node.nodeType === NodeType.Gender) {\n const options = node.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\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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"}