UNPKG

@intlayer/core

Version:

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

167 lines (165 loc) 6.71 kB
import { deepTransformNode } from "../interpreter/getContent/deepTransform.mjs"; import { enu as enumeration } from "../transpiler/enumeration/enumeration.mjs"; import { gender } from "../transpiler/gender/gender.mjs"; import { html } from "../transpiler/html/html.mjs"; import { insert as insertion } from "../transpiler/insertion/insertion.mjs"; import { plural } from "../transpiler/plural/plural.mjs"; import * as NodeTypes from "@intlayer/types/nodeType"; //#region src/messageFormat/po.ts /** * Extracts the string value from a transformed AST node or generic object. */ const extractStringValue = (val) => { if (typeof val === "string") return val; if (val && typeof val === "object" && "msgstr" in val) return val.msgstr[0] || ""; return JSON.stringify(val); }; const intlayerToPoPlugin = { canHandle: (node) => { if (typeof node === "string") return true; 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; if (Array.isArray(node)) { if (node.length === 0) return false; let hasNode = false; let hasPlainObjectOrArray = false; for (const item of node) if (typeof item === "string") {} else if (item && typeof item === "object" && (item.nodeType === NodeTypes.INSERTION || item.nodeType === NodeTypes.HTML || item.nodeType === NodeTypes.ENUMERATION || item.nodeType === NodeTypes.PLURAL || item.nodeType === NodeTypes.GENDER || item.nodeType === "composite")) hasNode = true; else hasPlainObjectOrArray = true; if (hasPlainObjectOrArray) return false; if (!hasNode) return false; return true; } return false; }, transform: (node, props, next) => { if (typeof node === "string") { const poVal = node.replace(/\{\{([^}]+)\}\}/g, "%($1)s"); return { msgid: poVal, msgstr: [poVal] }; } if (node.nodeType === NodeTypes.INSERTION) return next(node[NodeTypes.INSERTION], props); if (node.nodeType === NodeTypes.HTML) { const val = node[NodeTypes.HTML]; return { msgid: val, msgstr: [val] }; } if (node.nodeType === NodeTypes.PLURAL || node.nodeType === NodeTypes.ENUMERATION) { const isPlural = node.nodeType === NodeTypes.PLURAL; const options = isPlural ? node[NodeTypes.PLURAL] : node[NodeTypes.ENUMERATION]; const rawMsgid = isPlural ? options.one || options.other || options.fallback : options.fallback || options["0"]; const msgid = extractStringValue(next(rawMsgid, props)); const msgid_plural = isPlural ? extractStringValue(next(options.other || options.fallback || rawMsgid, props)) : extractStringValue(next(options.fallback || rawMsgid, props)); const msgstr = []; if (isPlural) { if ("zero" in options) msgstr.push(extractStringValue(next(options.zero, props))); msgstr.push(extractStringValue(next(options.one || options.fallback, props))); if ("two" in options) msgstr.push(extractStringValue(next(options.two, props))); if ("few" in options) msgstr.push(extractStringValue(next(options.few, props))); if ("many" in options) msgstr.push(extractStringValue(next(options.many, props))); const otherVal = extractStringValue(next(options.other || options.fallback, props)); if (!msgstr.includes(otherVal)) msgstr.push(otherVal); } else { msgstr[0] = extractStringValue(next(options.fallback || options["0"], props)); msgstr[1] = msgstr[0]; } return { msgctxt: isPlural ? void 0 : "enumeration", msgid, msgid_plural, msgstr }; } if (node.nodeType === NodeTypes.GENDER) { const options = node[NodeTypes.GENDER]; const fallback = extractStringValue(next(options.fallback, props)); return { msgctxt: "gender", msgid: fallback, msgstr: [ extractStringValue(next(options.male || options.fallback, props)), extractStringValue(next(options.female || options.fallback, props)), fallback ] }; } if (Array.isArray(node) || node.nodeType === "composite" && Array.isArray(node.composite)) { const combined = (Array.isArray(node) ? node : node.composite).map((item) => extractStringValue(next(item, props))).join(""); return { msgid: combined, msgstr: [combined] }; } return node; } }; const poToIntlayerPlugin = { canHandle: (node) => node && typeof node === "object" && "msgid" in node && "msgstr" in node, transform: (node) => { const msgstr = node.msgstr || []; const isPlural = Boolean(node.msgid_plural) || msgstr.length > 1; const processString = (str) => { if (!str) return ""; const parsed = str.replace(/%\(([a-zA-Z0-9_-]+)\)[sdf]/g, "{{$1}}"); if (/<[a-zA-Z0-9-]+[^>]*>/.test(parsed)) return html(parsed); if (parsed.includes("{{")) return insertion(parsed); return parsed; }; if (!isPlural) return processString(msgstr[0] || node.msgid || ""); const options = {}; if (node.msgctxt === "gender") return gender({ male: processString(msgstr[0] || node.msgid), female: processString(msgstr[1] || msgstr[0]), fallback: processString(msgstr[2] || msgstr[msgstr.length - 1]) }); if (node.msgctxt === "enumeration") return enumeration({ "0": processString(msgstr[0]), fallback: processString(msgstr[msgstr.length - 1]) }); if (msgstr.length === 2) { options.one = processString(msgstr[0]); options.other = processString(msgstr[1]); } else if (msgstr.length === 3) { options.one = processString(msgstr[0]); options.few = processString(msgstr[1]); options.other = processString(msgstr[2]); } else if (msgstr.length === 6) { options.zero = processString(msgstr[0]); options.one = processString(msgstr[1]); options.two = processString(msgstr[2]); options.few = processString(msgstr[3]); options.many = processString(msgstr[4]); options.other = processString(msgstr[5]); } else { options.one = processString(msgstr[0]); options.other = processString(msgstr[msgstr.length - 1]); for (let i = 1; i < msgstr.length - 1; i++) options[`${i + 1}`] = processString(msgstr[i]); } return plural(options); } }; const intlayerToPortableObjectFormatter = (dictionary) => { return deepTransformNode(dictionary, { dictionaryKey: "po", keyPath: [], plugins: [{ id: "po", ...intlayerToPoPlugin }] }); }; const portableObjectToIntlayerFormatter = (message) => { return deepTransformNode(message, { dictionaryKey: "po", keyPath: [], plugins: [{ id: "po", ...poToIntlayerPlugin }] }); }; //#endregion export { intlayerToPortableObjectFormatter, portableObjectToIntlayerFormatter }; //# sourceMappingURL=po.mjs.map