diff --git a/modules/@angular/compiler/src/i18n/serializers/xtb.ts b/modules/@angular/compiler/src/i18n/serializers/xtb.ts index 8578763525..56234aba56 100644 --- a/modules/@angular/compiler/src/i18n/serializers/xtb.ts +++ b/modules/@angular/compiler/src/i18n/serializers/xtb.ts @@ -29,10 +29,19 @@ export class Xtb extends Serializer { // xml nodes to i18n nodes const i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {}; const converter = new XmlToI18n(); + + // 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 => { - const {i18nNodes, errors: e} = converter.convert(mlNodesByMsgId[msgId]); - errors.push(...e); - i18nNodesByMsgId[msgId] = i18nNodes; + const valueFn = function() { + const {i18nNodes, errors} = converter.convert(mlNodesByMsgId[msgId]); + if (errors.length) { + throw new Error(`xtb parse errors:\n${errors.join('\n')}`); + } + return i18nNodes; + }; + createLazyProperty(i18nNodesByMsgId, msgId, valueFn); }); if (errors.length) { @@ -49,6 +58,19 @@ export class Xtb extends Serializer { } } +function createLazyProperty(messages: any, id: string, valueFn: () => any) { + Object.defineProperty(messages, id, { + configurable: true, + enumerable: true, + get: function() { + const value = valueFn(); + Object.defineProperty(messages, id, {enumerable: true, value}); + return value; + }, + set: _ => { throw new Error('Could not overwrite an XTB translation'); }, + }); +} + // Extract messages as xml nodes from the xtb file class XtbParser implements ml.Visitor { private _bundleDepth: number; diff --git a/modules/@angular/compiler/test/i18n/serializers/xtb_spec.ts b/modules/@angular/compiler/test/i18n/serializers/xtb_spec.ts index 57c9c52693..b8eb6a8d07 100644 --- a/modules/@angular/compiler/test/i18n/serializers/xtb_spec.ts +++ b/modules/@angular/compiler/test/i18n/serializers/xtb_spec.ts @@ -9,8 +9,10 @@ import {escapeRegExp} from '@angular/core/src/facade/lang'; import {serializeNodes} from '../../../src/i18n/digest'; +import * as i18n from '../../../src/i18n/i18n_ast'; import {Xtb} from '../../../src/i18n/serializers/xtb'; + export function main(): void { describe('XTB serializer', () => { const serializer = new Xtb(); @@ -100,6 +102,25 @@ export function main(): void { }); describe('errors', () => { + it('should be able to parse non-angular xtb files without error', () => { + const XTB = ` + + is great + is less great +`; + + // Invalid messages should not cause the parser to throw + let i18nNodesByMsgId: {[id: string]: i18n.Node[]}; + expect(() => { i18nNodesByMsgId = serializer.load(XTB, 'url'); }).not.toThrow(); + + expect(Object.keys(i18nNodesByMsgId).length).toEqual(2); + expect(serializeNodes(i18nNodesByMsgId['angular']).join('')).toEqual('is great'); + // Messages that contain unsupported feature should throw on access + expect(() => { + const read = i18nNodesByMsgId['non angular']; + }).toThrowError(/xtb parse errors/); + }); + it('should throw on nested ', () => { const XTB = '';