/** * @license * Copyright Google LLC 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 {escapeRegExp} from '@angular/compiler/src/util'; import {serializeNodes} from '../../../src/i18n/digest'; import {MessageBundle} from '../../../src/i18n/message_bundle'; import {Xliff2} from '../../../src/i18n/serializers/xliff2'; import {HtmlParser} from '../../../src/ml_parser/html_parser'; import {DEFAULT_INTERPOLATION_CONFIG} from '../../../src/ml_parser/interpolation_config'; const HTML = `

not translatable

translatable element with placeholders {{ interpolation}}

{ count, plural, =0 {

test

}}

foo

{{interpolation}} Text


hello

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

deeply nested

}} }}

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

deeply nested

}} } =other {a lot}}

multi lines

`; const WRITE_XLIFF = ` file.ts:2 translatable attribute file.ts:3 translatable element with placeholders file.ts:4 {VAR_PLURAL, plural, =0 {test} } d m file.ts:5 foo nested file.ts:6 Text ph names file.ts:7 empty element file.ts:8 hello file.ts:9 {VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {deeply nested} } } } file.ts:10 Test: file.ts:10 {VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {deeply nested} } } =other {a lot} } file.ts:11,12 multi lines `; const LOAD_XLIFF = ` file.ts:2 translatable attribute etubirtta elbatalsnart file.ts:3 translatable element with placeholders sredlohecalp htiw tnemele elbatalsnart file.ts:4 {VAR_PLURAL, plural, =0 {test} } {VAR_PLURAL, plural, =0 {TEST} } d m file.ts:5 foo oof nested file.ts:6 Text txeT ph names file.ts:7 empty element file.ts:8 hello olleh file.ts:9 {VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {deeply nested} } } } {VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {profondément imbriqué} } } } file.ts:10 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} } file.ts:11,12 multi lines multi lignes Please check the translation for 'namespace'. On also can use 'espace de nom',but I think most technical manuals use the English term. You use your own namespace. Vous pouvez utiliser votre propre namespace. Please check the translation for 'namespace'. On also can use 'espace de nom',but I think most technical manuals use the English term. You use your own namespace. Vous pouvez utiliser votre propre namespace. `; (function() { const serializer = new Xliff2(); function toXliff(html: string, locale: string|null = null): string { const catalog = new MessageBundle(new HtmlParser, [], {}, locale); catalog.updateFromTemplate(html, 'file.ts', DEFAULT_INTERPOLATION_CONFIG); return catalog.write(serializer); } 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('')); return msgMap; } describe('XLIFF 2.0 serializer', () => { describe('write', () => { it('should write a valid xliff 2.0 file', () => { expect(toXliff(HTML)).toEqual(WRITE_XLIFF); }); it('should write a valid xliff 2.0 file with a source language', () => { expect(toXliff(HTML, 'fr')).toContain('srcLang="fr"'); }); }); describe('load', () => { it('should load XLIFF files', () => { expect(loadAsMap(LOAD_XLIFF)).toEqual({ '1933478729560469763': 'etubirtta elbatalsnart', '7056919470098446707': ' sredlohecalp htiw tnemele elbatalsnart', '2981514368455622387': '{VAR_PLURAL, plural, =0 {[, TEST, ]}}', 'i': 'oof', '6440235004920703622': 'txeT ', '8779402634269838862': '', '6536355551500405293': ' olleh', 'baz': '{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`, 'mrk-test': 'Vous pouvez utiliser votre propre namespace.', 'mrk-test2': 'Vous pouvez utiliser votre propre namespace.' }); }); it('should return the target locale', () => { expect(serializer.load(LOAD_XLIFF, 'url').locale).toEqual('fr'); }); }); describe('structure errors', () => { it('should throw when a wrong xliff version is used', () => { const XLIFF = ` `; expect(() => { loadAsMap(XLIFF); }).toThrowError(/The XLIFF file version 1.2 is not compatible with XLIFF 2.0 serializer/); }); it('should throw when an unit has no translation', () => { const XLIFF = ` `; expect(() => { loadAsMap(XLIFF); }).toThrowError(/Message missingtarget misses a translation/); }); it('should throw when an unit has no id attribute', () => { const XLIFF = ` `; expect(() => { loadAsMap(XLIFF); }).toThrowError(/ misses the "id" attribute/); }); it('should throw on duplicate unit id', () => { const XLIFF = ` `; expect(() => { loadAsMap(XLIFF); }).toThrowError(/Duplicated translations for msg deadbeef/); }); }); describe('message errors', () => { it('should throw on unknown message tags', () => { const XLIFF = ` msg should contain only ph and pc tags `; expect(() => { loadAsMap(XLIFF); }) .toThrowError( new RegExp(escapeRegExp(`[ERROR ->]msg should contain only ph and pc tags`))); }); it('should throw when a placeholder misses an id attribute', () => { const XLIFF = ` `; expect(() => { loadAsMap(XLIFF); }).toThrowError(new RegExp(escapeRegExp(` misses the "equiv" attribute`))); }); }); }); })();