From 38ec05f53375dc4cc4bf89cecf288eec8b5f4bd5 Mon Sep 17 00:00:00 2001 From: Olivier Combe Date: Fri, 21 Jul 2017 19:49:45 +0200 Subject: [PATCH] fix(compiler): add equiv & disp attributes to Xliff2 ICU placeholders (#18283) Fixes #17344 PR Close #18283 --- .../compiler/src/i18n/serializers/xliff2.ts | 9 +- .../test/i18n/integration_xliff2_spec.ts | 416 ++++++++++++++++++ .../test/i18n/serializers/xliff2_spec.ts | 34 +- 3 files changed, 449 insertions(+), 10 deletions(-) create mode 100644 packages/compiler/test/i18n/integration_xliff2_spec.ts diff --git a/packages/compiler/src/i18n/serializers/xliff2.ts b/packages/compiler/src/i18n/serializers/xliff2.ts index 6db2de5d9c..ea754b34fc 100644 --- a/packages/compiler/src/i18n/serializers/xliff2.ts +++ b/packages/compiler/src/i18n/serializers/xliff2.ts @@ -165,15 +165,20 @@ class _WriteVisitor implements i18n.Visitor { } visitPlaceholder(ph: i18n.Placeholder, context?: any): xml.Node[] { + const idStr = (this._nextPlaceholderId++).toString(); return [new xml.Tag(_PLACEHOLDER_TAG, { - id: (this._nextPlaceholderId++).toString(), + id: idStr, equiv: ph.name, disp: `{{${ph.value}}}`, })]; } visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): xml.Node[] { - return [new xml.Tag(_PLACEHOLDER_TAG, {id: (this._nextPlaceholderId++).toString()})]; + const cases = Object.keys(ph.value.cases).map((value: string) => value + ' {...}').join(' '); + const idStr = (this._nextPlaceholderId++).toString(); + return [new xml.Tag( + _PLACEHOLDER_TAG, + {id: idStr, equiv: ph.name, disp: `{${ph.value.expression}, ${ph.value.type}, ${cases}}`})]; } serialize(nodes: i18n.Node[]): xml.Node[] { diff --git a/packages/compiler/test/i18n/integration_xliff2_spec.ts b/packages/compiler/test/i18n/integration_xliff2_spec.ts new file mode 100644 index 0000000000..3594db225a --- /dev/null +++ b/packages/compiler/test/i18n/integration_xliff2_spec.ts @@ -0,0 +1,416 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {NgLocalization} from '@angular/common'; +import {ResourceLoader} from '@angular/compiler'; +import {MessageBundle} from '@angular/compiler/src/i18n/message_bundle'; +import {Xliff2} from '@angular/compiler/src/i18n/serializers/xliff2'; +import {HtmlParser} from '@angular/compiler/src/ml_parser/html_parser'; +import {DEFAULT_INTERPOLATION_CONFIG} from '@angular/compiler/src/ml_parser/interpolation_config'; +import {DebugElement, TRANSLATIONS, TRANSLATIONS_FORMAT} from '@angular/core'; +import {ComponentFixture, TestBed, async} from '@angular/core/testing'; +import {expect} from '@angular/platform-browser/testing/src/matchers'; + +import {SpyResourceLoader} from '../spies'; + +import {FrLocalization, HTML, I18nComponent, validateHtml} from './integration_common'; + +export function main() { + describe('i18n XLIFF 2.0 integration spec', () => { + + beforeEach(async(() => { + TestBed.configureCompiler({ + providers: [ + {provide: ResourceLoader, useClass: SpyResourceLoader}, + {provide: NgLocalization, useClass: FrLocalization}, + {provide: TRANSLATIONS, useValue: XLIFF2_TOMERGE}, + {provide: TRANSLATIONS_FORMAT, useValue: 'xlf2'}, + ] + }); + + TestBed.configureTestingModule({declarations: [I18nComponent]}); + })); + + it('should extract from templates', () => { + const catalog = new MessageBundle(new HtmlParser, [], {}); + const serializer = new Xliff2(); + catalog.updateFromTemplate(HTML, 'file.ts', DEFAULT_INTERPOLATION_CONFIG); + + expect(catalog.write(serializer)).toContain(XLIFF2_EXTRACTED); + }); + + it('should translate templates', () => { + const tb: ComponentFixture = + TestBed.overrideTemplate(I18nComponent, HTML).createComponent(I18nComponent); + const cmp: I18nComponent = tb.componentInstance; + const el: DebugElement = tb.debugElement; + + validateHtml(tb, cmp, el); + }); + }); +} + +const XLIFF2_TOMERGE = ` + + + i18n attribute on tags + attributs i18n sur les balises + + + + + nested + imbriqué + + + + + nested + imbriqué + + + + + with placeholders + avec des espaces réservés + + + + + file.ts:11 + + + with nested placeholders + avec espaces réservés imbriqués + + + + + on not translatable node + sur des balises non traductibles + + + + + on translatable node + sur des balises traductibles + + + + + {VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {many} } + {VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {beaucoup} } + + + + + + + + + + + + + file.ts:23 + + + {VAR_SELECT, select, 0 {other} m {male} f {female} } + {VAR_SELECT, select, 0 {autre} m {homme} f {femme} } + + + + + file.ts:25,27 + + + + + + + + + + + {VAR_SELECT, select, m {male} f {female} } + {VAR_SELECT, select, m {homme} f {femme} } + + + + + + + + + + + sex = + sexe = + + + + + + + + + + + in a translatable section + dans une section traductible + + + + + + Markers in html comments + + + + + Balises dans les commentaires html + + + + + + + + it should work + ca devrait marcher + + + + + with an explicit ID + avec un ID explicite + + + + + {VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {many} } + {VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {beaucoup} } + + + + + {VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found results} } + {VAR_PLURAL, plural, =0 {Pas de réponse} =1 {une réponse} other {Trouvé réponses} } + + + + + foobar + FOOBAR + + + + + + + + `; + +const XLIFF2_EXTRACTED = ` + + + file.ts:3 + + + i18n attribute on tags + + + + + file.ts:5 + + + nested + + + + + different meaning + file.ts:7 + + + nested + + + + + file.ts:9 + file.ts:10 + + + with placeholders + + + + + file.ts:11 + + + with nested placeholders + + + + + file.ts:14 + + + on not translatable node + + + + + file.ts:15 + + + on translatable node + + + + + file.ts:20 + file.ts:37 + + + {VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {many} } + + + + + file.ts:22,24 + + + + + + + + + + file.ts:23 + + + {VAR_SELECT, select, 0 {other} m {male} f {female} } + + + + + file.ts:25,27 + + + + + + + + + + file.ts:26 + + + {VAR_SELECT, select, m {male} f {female} } + + + + + file.ts:29 + + + + + + + + file.ts:30 + + + sex = + + + + + file.ts:31 + + + + + + + + file.ts:36 + file.ts:54 + + + in a translatable section + + + + + file.ts:34,38 + + + + Markers in html comments + + + + + + + + file.ts:40 + + + it should work + + + + + file.ts:42 + + + with an explicit ID + + + + + file.ts:43 + + + {VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {many} } + + + + + desc + file.ts:46,52 + + + {VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found results} } + + + + + file.ts:54 + + + foobar + + + + + file.ts:56 + + + + + `; diff --git a/packages/compiler/test/i18n/serializers/xliff2_spec.ts b/packages/compiler/test/i18n/serializers/xliff2_spec.ts index 5c10135c92..2ed9c998ab 100644 --- a/packages/compiler/test/i18n/serializers/xliff2_spec.ts +++ b/packages/compiler/test/i18n/serializers/xliff2_spec.ts @@ -23,7 +23,7 @@ const HTML = `


hello

{ count, plural, =0 { { sex, select, other {

deeply nested

}} }}

-

{ count, plural, =0 { { sex, select, other {

deeply nested

}} }}

+

Test: { count, plural, =0 { { sex, select, other {

deeply nested

}} } =other {a lot}}

multi lines

`; @@ -100,12 +100,20 @@ const WRITE_XLIFF = ` {VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {deeply nested} } } } - + file.ts:10 - {VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {deeply nested} } } } + Test: + + + + + file.ts:10 + + + {VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {deeply nested} } } =other {a lot} } @@ -201,13 +209,22 @@ const LOAD_XLIFF = ` {VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {profondément imbriqué} } } } - + file.ts:10 - {VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {deeply nested} } } } - {VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {profondément imbriqué} } } } + Test: + Test: + + + + + file.ts:10 + + + {VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {deeply nested} } } =other {a lot} } + {VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {profondément imbriqué} } } =other {beaucoup} } @@ -268,8 +285,9 @@ export function main(): void { '6536355551500405293': ' olleh', 'baz': '{VAR_PLURAL, plural, =0 {[{VAR_SELECT, select, other {[, profondément imbriqué, ]}}, ]}}', - '2015957479576096115': '{VAR_PLURAL, plural, =0 {[{VAR_SELECT, select, other {[, profondément imbriqué, ]}}, ]}}', + '6997386649824869937': 'Test: ', + '5229984852258993423': '{VAR_PLURAL, plural, =0 {[{VAR_SELECT, select, other {[, profondément imbriqué, ]}}, ]}, =other {[beaucoup]}}', '2340165783990709777': `multi lignes` });