From 5921c872b6ef2f2de31f90576660a1bf56afb979 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Tue, 31 Jan 2017 09:47:10 -0800 Subject: [PATCH] fix(compiler): only lex messages that are needed by angular (#14208) To avoid the lexer erroring on syntax not supported by Angular. PR Close #14208 --- .../compiler/src/i18n/serializers/xtb.ts | 36 ++++++++++++------- .../test/i18n/serializers/xtb_spec.ts | 2 +- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/modules/@angular/compiler/src/i18n/serializers/xtb.ts b/modules/@angular/compiler/src/i18n/serializers/xtb.ts index 56234aba56..46fb151860 100644 --- a/modules/@angular/compiler/src/i18n/serializers/xtb.ts +++ b/modules/@angular/compiler/src/i18n/serializers/xtb.ts @@ -24,7 +24,7 @@ export class Xtb extends Serializer { load(content: string, url: string): {[msgId: string]: i18n.Node[]} { // xtb to xml nodes const xtbParser = new XtbParser(); - const {mlNodesByMsgId, errors} = xtbParser.parse(content, url); + const {msgIdToHtml, errors} = xtbParser.parse(content, url); // xml nodes to i18n nodes const i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {}; @@ -33,9 +33,9 @@ export class Xtb extends Serializer { // Because we should be able to load xtb files that rely on features not supported by angular, // we need to delay the conversion of html to i18n nodes so that non angular messages are not // converted - Object.keys(mlNodesByMsgId).forEach(msgId => { + Object.keys(msgIdToHtml).forEach(msgId => { const valueFn = function() { - const {i18nNodes, errors} = converter.convert(mlNodesByMsgId[msgId]); + const {i18nNodes, errors} = converter.convert(msgIdToHtml[msgId], url); if (errors.length) { throw new Error(`xtb parse errors:\n${errors.join('\n')}`); } @@ -75,19 +75,21 @@ function createLazyProperty(messages: any, id: string, valueFn: () => any) { class XtbParser implements ml.Visitor { private _bundleDepth: number; private _errors: I18nError[]; - private _mlNodesByMsgId: {[msgId: string]: ml.Node[]}; + private _msgIdToHtml: {[msgId: string]: string}; parse(xtb: string, url: string) { this._bundleDepth = 0; - this._mlNodesByMsgId = {}; + this._msgIdToHtml = {}; - const xml = new XmlParser().parse(xtb, url, true); + // We can not parse the ICU messages at this point as some messages might not originate + // from Angular that could not be lex'd. + const xml = new XmlParser().parse(xtb, url, false); this._errors = xml.errors; ml.visitAll(this, xml.rootNodes); return { - mlNodesByMsgId: this._mlNodesByMsgId, + msgIdToHtml: this._msgIdToHtml, errors: this._errors, }; } @@ -109,10 +111,14 @@ class XtbParser implements ml.Visitor { this._addError(element, `<${_TRANSLATION_TAG}> misses the "id" attribute`); } else { const id = idAttr.value; - if (this._mlNodesByMsgId.hasOwnProperty(id)) { + if (this._msgIdToHtml.hasOwnProperty(id)) { this._addError(element, `Duplicated translations for msg ${id}`); } else { - this._mlNodesByMsgId[id] = element.children; + const innerTextStart = element.startSourceSpan.end.offset; + const innerTextEnd = element.endSourceSpan.start.offset; + const content = element.startSourceSpan.start.file.content; + const innerText = content.slice(innerTextStart, innerTextEnd); + this._msgIdToHtml[id] = innerText; } } break; @@ -141,10 +147,16 @@ class XtbParser implements ml.Visitor { class XmlToI18n implements ml.Visitor { private _errors: I18nError[]; - convert(nodes: ml.Node[]) { - this._errors = []; + convert(message: string, url: string) { + const xmlIcu = new XmlParser().parse(message, url, true); + this._errors = xmlIcu.errors; + + const i18nNodes = this._errors.length > 0 || xmlIcu.rootNodes.length == 0 ? + [] : + ml.visitAll(this, xmlIcu.rootNodes); + return { - i18nNodes: ml.visitAll(this, nodes), + i18nNodes, errors: this._errors, }; } diff --git a/modules/@angular/compiler/test/i18n/serializers/xtb_spec.ts b/modules/@angular/compiler/test/i18n/serializers/xtb_spec.ts index b8eb6a8d07..08446a6e73 100644 --- a/modules/@angular/compiler/test/i18n/serializers/xtb_spec.ts +++ b/modules/@angular/compiler/test/i18n/serializers/xtb_spec.ts @@ -106,7 +106,7 @@ export function main(): void { const XTB = ` is great - is less great + is less {count, plural, =0 {{GREAT}}} `; // Invalid messages should not cause the parser to throw