Skip to content

Commit 05b2b49

Browse files
authored
fix(compiler): allow empty translations for attributes (#14085)
fixes #13897
1 parent 3ef73c2 commit 05b2b49

File tree

3 files changed

+52
-6
lines changed

3 files changed

+52
-6
lines changed

modules/@angular/compiler/src/i18n/extractor_merger.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ class _Visitor implements html.Visitor {
327327
}
328328

329329
// Translates the given message given the `TranslationBundle`
330+
// This is used for translating elements / blocks - see `_translateAttributes` for attributes
330331
// no-op when called in extraction mode (returns [])
331332
private _translateMessage(el: html.Node, message: i18n.Message): html.Node[] {
332333
if (message && this._mode === _VisitorMode.Merge) {
@@ -368,7 +369,9 @@ class _Visitor implements html.Visitor {
368369
const message: i18n.Message = this._createI18nMessage([attr], meaning, '', '');
369370
const nodes = this._translations.get(message);
370371
if (nodes) {
371-
if (nodes[0] instanceof html.Text) {
372+
if (nodes.length == 0) {
373+
translatedAttributes.push(new html.Attribute(attr.name, '', attr.sourceSpan));
374+
} else if (nodes[0] instanceof html.Text) {
372375
const value = (nodes[0] as html.Text).value;
373376
translatedAttributes.push(new html.Attribute(attr.name, value, attr.sourceSpan));
374377
} else {

modules/@angular/compiler/test/i18n/extractor_merger_spec.ts

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,24 @@ export function main() {
383383
const HTML = `<div>before<p i18n="m|d">foo</p><!-- comment --></div>`;
384384
expect(fakeTranslate(HTML)).toEqual('<div>before<p>**foo**</p></div>');
385385
});
386+
387+
it('should merge empty messages', () => {
388+
const HTML = `<div i18n>some element</div>`;
389+
const htmlNodes: html.Node[] = parseHtml(HTML);
390+
const messages: i18n.Message[] =
391+
extractMessages(htmlNodes, DEFAULT_INTERPOLATION_CONFIG, [], {}).messages;
392+
393+
expect(messages.length).toEqual(1);
394+
const i18nMsgMap: {[id: string]: i18n.Node[]} = {};
395+
i18nMsgMap[digest(messages[0])] = [];
396+
const translations = new TranslationBundle(i18nMsgMap, digest);
397+
398+
const output =
399+
mergeTranslations(htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, [], {});
400+
expect(output.errors).toEqual([]);
401+
402+
expect(serializeHtmlNodes(output.rootNodes).join('')).toEqual(`<div></div>`);
403+
});
386404
});
387405

388406
describe('blocks', () => {
@@ -422,6 +440,25 @@ export function main() {
422440
const HTML = `<p i18n-title="m|d" title=""></p>`;
423441
expect(fakeTranslate(HTML)).toEqual('<p title=""></p>');
424442
});
443+
444+
it('should merge empty attributes', () => {
445+
const HTML = `<div i18n-title title="some attribute">some element</div>`;
446+
const htmlNodes: html.Node[] = parseHtml(HTML);
447+
const messages: i18n.Message[] =
448+
extractMessages(htmlNodes, DEFAULT_INTERPOLATION_CONFIG, [], {}).messages;
449+
450+
expect(messages.length).toEqual(1);
451+
const i18nMsgMap: {[id: string]: i18n.Node[]} = {};
452+
i18nMsgMap[digest(messages[0])] = [];
453+
const translations = new TranslationBundle(i18nMsgMap, digest);
454+
455+
const output =
456+
mergeTranslations(htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, [], {});
457+
expect(output.errors).toEqual([]);
458+
459+
expect(serializeHtmlNodes(output.rootNodes).join(''))
460+
.toEqual(`<div title="">some element</div>`);
461+
});
425462
});
426463
});
427464
}
@@ -453,12 +490,11 @@ function fakeTranslate(
453490

454491
const translations = new TranslationBundle(i18nMsgMap, digest);
455492

456-
const translatedNodes =
457-
mergeTranslations(
458-
htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, implicitTags, implicitAttrs)
459-
.rootNodes;
493+
const output = mergeTranslations(
494+
htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, implicitTags, implicitAttrs);
495+
expect(output.errors).toEqual([]);
460496

461-
return serializeHtmlNodes(translatedNodes).join('');
497+
return serializeHtmlNodes(output.rootNodes).join('');
462498
}
463499

464500
function extract(

modules/@angular/compiler/test/i18n/serializers/xliff_spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ const LOAD_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
9494
<target><x id="START_TAG_DIV" ctype="x-div"/><x id="CLOSE_TAG_DIV" ctype="x-div"/><x id="TAG_IMG" ctype="image"/><x id="LINE_BREAK" ctype="lb"/></target>
9595
<note priority="1" from="description">ph names</note>
9696
</trans-unit>
97+
<trans-unit id="empty target" datatype="html">
98+
<source><x id="LINE_BREAK" ctype="lb"/><x id="TAG_IMG" ctype="image"/><x id="START_TAG_DIV" ctype="x-div"/><x id="CLOSE_TAG_DIV" ctype="x-div"/></source>
99+
<target/>
100+
<note priority="1" from="description">ph names</note>
101+
</trans-unit>
97102
</body>
98103
</file>
99104
</xliff>
@@ -110,6 +115,7 @@ export function main(): void {
110115

111116
function loadAsMap(xliff: string): {[id: string]: string} {
112117
const i18nNodesByMsgId = serializer.load(xliff, 'url');
118+
113119
const msgMap: {[id: string]: string} = {};
114120
Object.keys(i18nNodesByMsgId)
115121
.forEach(id => msgMap[id] = serializeNodes(i18nNodesByMsgId[id]).join(''));
@@ -133,6 +139,7 @@ export function main(): void {
133139
'bar': 'tata',
134140
'd7fa2d59aaedcaa5309f13028c59af8c85b8c49d':
135141
'<ph name="START_TAG_DIV"/><ph name="CLOSE_TAG_DIV"/><ph name="TAG_IMG"/><ph name="LINE_BREAK"/>',
142+
'empty target': '',
136143
});
137144
});
138145

0 commit comments

Comments
 (0)