UNPKG

@ngx-i18nsupport/ngx-i18nsupport-lib

Version:

A Typescript library to work with Angular generated i18n files (xliff, xmb)

218 lines 9.55 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const parsed_message_1 = require("./parsed-message"); const parsed_message_tokenizer_1 = require("./parsed-message-tokenizer"); const xmldom_1 = require("xmldom"); const util_1 = require("util"); const dom_utilities_1 = require("./dom-utilities"); const parsed_message_part_icu_message_1 = require("./parsed-message-part-icu-message"); /** * Created by roobm on 10.05.2017. * A message parser can parse the xml content of a translatable message. * It generates a ParsedMessage from it. */ class AbstractMessageParser { /** * Parse XML to ParsedMessage. * @param xmlElement the xml representation * @param sourceMessage optional original message that will be translated by normalized new one * Throws an error if normalized xml is not well formed. */ createNormalizedMessageFromXML(xmlElement, sourceMessage) { const message = new parsed_message_1.ParsedMessage(this, sourceMessage); if (xmlElement) { message.setXmlRepresentation(xmlElement); this.addPartsOfNodeToMessage(xmlElement, message, false); } return message; } /** * Parse XML string to ParsedMessage. * @param xmlString the xml representation without root element, e.g. this is <ph x></ph> an example. * @param sourceMessage optional original message that will be translated by normalized new one * Throws an error if normalized xml is not well formed. */ createNormalizedMessageFromXMLString(xmlString, sourceMessage) { const doc = new xmldom_1.DOMParser().parseFromString('<dummy>' + xmlString + '</dummy>', 'text/xml'); const xmlElement = doc.childNodes.item(0); return this.createNormalizedMessageFromXML(xmlElement, sourceMessage); } /** * recursively run through a node and add all identified parts to the message. * @param node node * @param message message to be generated. * @param includeSelf if true, add node by itself, otherwise only children. */ addPartsOfNodeToMessage(node, message, includeSelf) { let processChildren = true; if (includeSelf) { if (node.nodeType === node.TEXT_NODE) { message.addText(node.textContent); return; } if (node.nodeType === node.ELEMENT_NODE) { processChildren = this.processStartElement(node, message); } } if (processChildren) { const icuMessageText = this.getICUMessageText(node); let isICU = !util_1.isNullOrUndefined(icuMessageText); if (isICU) { try { message.addICUMessage(icuMessageText); } catch (error) { // if it is not parsable, handle it as non ICU console.log('non ICU message: ', icuMessageText, error); isICU = false; } } if (!isICU) { const children = node.childNodes; for (let i = 0; i < children.length; i++) { this.addPartsOfNodeToMessage(children.item(i), message, true); } } } if (node.nodeType === node.ELEMENT_NODE) { this.processEndElement(node, message); } } /** * Return the ICU message content of the node, if it is an ICU Message. * @param node node * @return message or null, if it is no ICU Message. */ getICUMessageText(node) { const children = node.childNodes; if (children.length === 0) { return null; } const firstChild = children.item(0); if (firstChild.nodeType === firstChild.TEXT_NODE) { if (this.isICUMessageStart(firstChild.textContent)) { return dom_utilities_1.DOMUtilities.getXMLContent(node); } else { return null; } } else { return null; } } /** * Test, wether text is beginning of ICU Message. * @param text text */ isICUMessageStart(text) { return parsed_message_part_icu_message_1.ParsedMessagePartICUMessage.looksLikeICUMessage(text); // return text.startsWith('{VAR_PLURAL') || text.startsWith('{VAR_SELECT'); } /** * Parse normalized string to ParsedMessage. * @param normalizedString normalized string * @param sourceMessage optional original message that will be translated by normalized new one * @return a new parsed message. * Throws an error if normalized string is not well formed. */ parseNormalizedString(normalizedString, sourceMessage) { const message = new parsed_message_1.ParsedMessage(this, sourceMessage); const openTags = []; let tokens; try { tokens = new parsed_message_tokenizer_1.ParsedMesageTokenizer().tokenize(normalizedString); } catch (error) { throw new Error(util_1.format('unexpected error while parsing message: "%s" (parsed "%")', error.message, normalizedString)); } tokens.forEach((token) => { let disp = null; switch (token.type) { case parsed_message_tokenizer_1.TEXT: message.addText(token.value.text); break; case parsed_message_tokenizer_1.START_TAG: message.addStartTag(token.value.name, token.value.idcounter); openTags.push(token.value.name); break; case parsed_message_tokenizer_1.END_TAG: message.addEndTag(token.value.name); if (openTags.length === 0 || openTags[openTags.length - 1] !== token.value.name) { // oops, not well formed throw new Error(util_1.format('unexpected close tag "%s" (parsed "%s")', token.value.name, normalizedString)); } openTags.pop(); break; case parsed_message_tokenizer_1.EMPTY_TAG: message.addEmptyTag(token.value.name, token.value.idcounter); break; case parsed_message_tokenizer_1.PLACEHOLDER: disp = (sourceMessage) ? sourceMessage.getPlaceholderDisp(token.value.idcounter) : null; message.addPlaceholder(token.value.idcounter, disp); break; case parsed_message_tokenizer_1.ICU_MESSAGE_REF: disp = (sourceMessage) ? sourceMessage.getICUMessageRefDisp(token.value.idcounter) : null; message.addICUMessageRef(token.value.idcounter, disp); break; case parsed_message_tokenizer_1.ICU_MESSAGE: throw new Error(util_1.format('<ICUMessage/> not allowed here, use parseICUMessage instead (parsed "%")', normalizedString)); default: break; } }); if (openTags.length > 0) { // oops, not well closed tags throw new Error(util_1.format('missing close tag "%s" (parsed "%s")', openTags[openTags.length - 1], normalizedString)); } message.setXmlRepresentation(this.createXmlRepresentation(message)); return message; } /** * Parse a string, that is an ICU message, to ParsedMessage. * @param icuMessageString the message, like '{x, plural, =0 {nothing} =1 {one} other {many}}'. * @param sourceMessage optional original message that will be translated by normalized new one * @return a new parsed message. * Throws an error if icuMessageString has not the correct syntax. */ parseICUMessage(icuMessageString, sourceMessage) { const message = new parsed_message_1.ParsedMessage(this, sourceMessage); message.addICUMessage(icuMessageString); return message; } /** * Helper function: Parse ID from a name. * name optionally ends with _<number>. This is the idcount. * E.g. name="TAG_IMG" returns 0 * name = "TAG_IMG_1" returns 1 * @param name name * @return id count */ parseIdCountFromName(name) { const regex = /.*_([0-9]*)/; const match = regex.exec(name); if (util_1.isNullOrUndefined(match) || match[1] === '') { return 0; } else { const num = match[1]; return parseInt(num, 10); } } /** * Create the native xml for a message. * Parts are already set here. * @param message message */ createXmlRepresentation(message) { const root = new xmldom_1.DOMParser().parseFromString('<dummy/>', 'text/xml'); const rootElem = root.getElementsByTagName('dummy').item(0); this.addXmlRepresentationToRoot(message, rootElem); return rootElem; } createXmlRepresentationOfTextPart(part, rootElem) { return rootElem.ownerDocument.createTextNode(part.asDisplayString()); } } exports.AbstractMessageParser = AbstractMessageParser; //# sourceMappingURL=abstract-message-parser.js.map