From 05b2b49711f2d1624be362c42b60fff20c238f7d Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Wed, 25 Jan 2017 17:43:19 -0800 Subject: [PATCH] fix(compiler): allow empty translations for attributes (#14085) fixes #13897 --- .../compiler/src/i18n/extractor_merger.ts | 5 +- .../test/i18n/extractor_merger_spec.ts | 46 +++++++++++++++++-- .../test/i18n/serializers/xliff_spec.ts | 7 +++ 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/modules/@angular/compiler/src/i18n/extractor_merger.ts b/modules/@angular/compiler/src/i18n/extractor_merger.ts index 1d58fed826..99de2bd10a 100644 --- a/modules/@angular/compiler/src/i18n/extractor_merger.ts +++ b/modules/@angular/compiler/src/i18n/extractor_merger.ts @@ -327,6 +327,7 @@ class _Visitor implements html.Visitor { } // Translates the given message given the `TranslationBundle` + // This is used for translating elements / blocks - see `_translateAttributes` for attributes // no-op when called in extraction mode (returns []) private _translateMessage(el: html.Node, message: i18n.Message): html.Node[] { if (message && this._mode === _VisitorMode.Merge) { @@ -368,7 +369,9 @@ class _Visitor implements html.Visitor { const message: i18n.Message = this._createI18nMessage([attr], meaning, '', ''); const nodes = this._translations.get(message); if (nodes) { - if (nodes[0] instanceof html.Text) { + if (nodes.length == 0) { + translatedAttributes.push(new html.Attribute(attr.name, '', attr.sourceSpan)); + } else if (nodes[0] instanceof html.Text) { const value = (nodes[0] as html.Text).value; translatedAttributes.push(new html.Attribute(attr.name, value, attr.sourceSpan)); } else { diff --git a/modules/@angular/compiler/test/i18n/extractor_merger_spec.ts b/modules/@angular/compiler/test/i18n/extractor_merger_spec.ts index 0e88c7359d..e7f607c3d4 100644 --- a/modules/@angular/compiler/test/i18n/extractor_merger_spec.ts +++ b/modules/@angular/compiler/test/i18n/extractor_merger_spec.ts @@ -383,6 +383,24 @@ export function main() { const HTML = `
before

foo

`; expect(fakeTranslate(HTML)).toEqual('
before

**foo**

'); }); + + it('should merge empty messages', () => { + const HTML = `
some element
`; + const htmlNodes: html.Node[] = parseHtml(HTML); + const messages: i18n.Message[] = + extractMessages(htmlNodes, DEFAULT_INTERPOLATION_CONFIG, [], {}).messages; + + expect(messages.length).toEqual(1); + const i18nMsgMap: {[id: string]: i18n.Node[]} = {}; + i18nMsgMap[digest(messages[0])] = []; + const translations = new TranslationBundle(i18nMsgMap, digest); + + const output = + mergeTranslations(htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, [], {}); + expect(output.errors).toEqual([]); + + expect(serializeHtmlNodes(output.rootNodes).join('')).toEqual(`
`); + }); }); describe('blocks', () => { @@ -422,6 +440,25 @@ export function main() { const HTML = `

`; expect(fakeTranslate(HTML)).toEqual('

'); }); + + it('should merge empty attributes', () => { + const HTML = `
some element
`; + const htmlNodes: html.Node[] = parseHtml(HTML); + const messages: i18n.Message[] = + extractMessages(htmlNodes, DEFAULT_INTERPOLATION_CONFIG, [], {}).messages; + + expect(messages.length).toEqual(1); + const i18nMsgMap: {[id: string]: i18n.Node[]} = {}; + i18nMsgMap[digest(messages[0])] = []; + const translations = new TranslationBundle(i18nMsgMap, digest); + + const output = + mergeTranslations(htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, [], {}); + expect(output.errors).toEqual([]); + + expect(serializeHtmlNodes(output.rootNodes).join('')) + .toEqual(`
some element
`); + }); }); }); } @@ -453,12 +490,11 @@ function fakeTranslate( const translations = new TranslationBundle(i18nMsgMap, digest); - const translatedNodes = - mergeTranslations( - htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, implicitTags, implicitAttrs) - .rootNodes; + const output = mergeTranslations( + htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, implicitTags, implicitAttrs); + expect(output.errors).toEqual([]); - return serializeHtmlNodes(translatedNodes).join(''); + return serializeHtmlNodes(output.rootNodes).join(''); } function extract( diff --git a/modules/@angular/compiler/test/i18n/serializers/xliff_spec.ts b/modules/@angular/compiler/test/i18n/serializers/xliff_spec.ts index 6d7b3fb7cf..e2fb4fc207 100644 --- a/modules/@angular/compiler/test/i18n/serializers/xliff_spec.ts +++ b/modules/@angular/compiler/test/i18n/serializers/xliff_spec.ts @@ -94,6 +94,11 @@ const LOAD_XLIFF = ` ph names + + + + ph names + @@ -110,6 +115,7 @@ export function main(): void { function loadAsMap(xliff: string): {[id: string]: string} { const i18nNodesByMsgId = serializer.load(xliff, 'url'); + const msgMap: {[id: string]: string} = {}; Object.keys(i18nNodesByMsgId) .forEach(id => msgMap[id] = serializeNodes(i18nNodesByMsgId[id]).join('')); @@ -133,6 +139,7 @@ export function main(): void { 'bar': 'tata', 'd7fa2d59aaedcaa5309f13028c59af8c85b8c49d': '', + 'empty target': '', }); });