feat(i18n): xtb serializer
This commit is contained in:
@ -6,14 +6,12 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {PlaceholderRegistry} from '../../../src/i18n/serializers/util';
|
||||
import {PlaceholderRegistry} from '../../../src/i18n/serializers/placeholder';
|
||||
|
||||
export function main(): void {
|
||||
ddescribe('PlaceholderRegistry', () => {
|
||||
describe('PlaceholderRegistry', () => {
|
||||
let reg: PlaceholderRegistry;
|
||||
|
||||
beforeEach(() => { reg = new PlaceholderRegistry(); });
|
||||
@ -34,11 +32,11 @@ export function main(): void {
|
||||
expect(reg.getStartTagPlaceholderName('p', {}, false)).toEqual('START_PARAGRAPH');
|
||||
});
|
||||
|
||||
it('should be case insensitive for tag name', () => {
|
||||
it('should be case sensitive for tag name', () => {
|
||||
expect(reg.getStartTagPlaceholderName('p', {}, false)).toEqual('START_PARAGRAPH');
|
||||
expect(reg.getStartTagPlaceholderName('P', {}, false)).toEqual('START_PARAGRAPH');
|
||||
expect(reg.getStartTagPlaceholderName('P', {}, false)).toEqual('START_PARAGRAPH_1');
|
||||
expect(reg.getCloseTagPlaceholderName('p')).toEqual('CLOSE_PARAGRAPH');
|
||||
expect(reg.getCloseTagPlaceholderName('P')).toEqual('CLOSE_PARAGRAPH');
|
||||
expect(reg.getCloseTagPlaceholderName('P')).toEqual('CLOSE_PARAGRAPH_1');
|
||||
});
|
||||
|
||||
it('should generate the same name for the same tag with the same attributes', () => {
|
@ -6,16 +6,15 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Catalog} from '@angular/compiler/src/i18n/catalog';
|
||||
import {XmbSerializer} from '@angular/compiler/src/i18n/serializers/xmb';
|
||||
import {Xmb} from '@angular/compiler/src/i18n/serializers/xmb';
|
||||
import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {HtmlParser} from '../../../src/html_parser/html_parser';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG} from '../../../src/html_parser/interpolation_config';
|
||||
|
||||
import {MessageBundle} from '../../../src/i18n/message_bundle';
|
||||
|
||||
export function main(): void {
|
||||
ddescribe('XMB serializer', () => {
|
||||
describe('XMB serializer', () => {
|
||||
const HTML = `
|
||||
<p>not translatable</p>
|
||||
<p i18n>translatable element <b>with placeholders</b> {{ interpolation}}</p>
|
||||
@ -35,35 +34,18 @@ export function main(): void {
|
||||
|
||||
it('should throw when trying to load an xmb file', () => {
|
||||
expect(() => {
|
||||
const serializer = new XmbSerializer();
|
||||
serializer.load(XMB);
|
||||
const serializer = new Xmb();
|
||||
serializer.load(XMB, 'url', {});
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function toXmb(html: string): string {
|
||||
let catalog = new Catalog(new HtmlParser, [], {});
|
||||
const serializer = new XmbSerializer();
|
||||
let catalog = new MessageBundle(new HtmlParser, [], {});
|
||||
const serializer = new Xmb();
|
||||
|
||||
catalog.updateFromTemplate(html, '', DEFAULT_INTERPOLATION_CONFIG);
|
||||
|
||||
return catalog.write(serializer);
|
||||
}
|
||||
|
||||
// <? xml version="1.0" encoding="UTF-8" ?><messagebundle><message id="834fa53b">translatable
|
||||
// element <ph name="START_BOLD_TEXT"><ex><b></ex></ph>with placeholders<ph
|
||||
// name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> <ph name="INTERPOLATION"/></message><message
|
||||
// id="7a2843db">{ count, plural, =0 {<ph name="START_PARAGRAPH"><ex><p></ex></ph>test<ph
|
||||
// name="CLOSE_PARAGRAPH"><ex></p></ex></ph>}}</message><message id="b45e58a5" description="d"
|
||||
// meaning="m">foo</message><message id="18ea85bc">{ count, plural, =0 {{ sex, gender, other {<ph
|
||||
// name="START_PARAGRAPH"><ex><p></ex></ph>deeply nested<ph
|
||||
// name="CLOSE_PARAGRAPH"><ex></p></ex></ph>}} }}</message></messagebundle>
|
||||
// <? xml version="1.0" encoding="UTF-8" ?><messagebundle><message id="834fa53b">translatable
|
||||
// element <ph name="START_BOLD_TEXT"><ex><b></ex></ph>with placeholders<ph
|
||||
// name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> <ph name="INTERPOLATION"/></message><message
|
||||
// id="7a2843db">{ count, plural, =0 {<ph name="START_PARAGRAPH"><ex><p></ex></ph>test<ph
|
||||
// name="CLOSE_PARAGRAPH"><ex></p></ex></ph>}}</message><message id="18ea85bc">{ count,
|
||||
// plural, =0 {{ sex, gender, other {<ph name="START_PARAGRAPH"><ex><p></ex></ph>deeply
|
||||
// nested<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>}} }}</message><message id="b45e58a5"
|
||||
// description="d" meaning="m">foo</message></messagebundle>
|
||||
}
|
@ -11,7 +11,7 @@ import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit
|
||||
import * as xml from '../../../src/i18n/serializers/xml_helper';
|
||||
|
||||
export function main(): void {
|
||||
ddescribe('XML helper', () => {
|
||||
describe('XML helper', () => {
|
||||
it('should serialize XML declaration', () => {
|
||||
expect(xml.serialize([new xml.Declaration({version: '1.0'})]))
|
||||
.toEqual('<? xml version="1.0" ?>');
|
||||
|
171
modules/@angular/compiler/test/i18n/serializers/xtb_spec.ts
Normal file
171
modules/@angular/compiler/test/i18n/serializers/xtb_spec.ts
Normal file
@ -0,0 +1,171 @@
|
||||
/**
|
||||
* @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 {Xtb} from '@angular/compiler/src/i18n/serializers/xtb';
|
||||
import {escapeRegExp} from '@angular/core/src/facade/lang';
|
||||
import {beforeEach, ddescribe, describe, expect, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal';
|
||||
|
||||
import {HtmlParser} from '../../../src/html_parser/html_parser';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG} from '../../../src/html_parser/interpolation_config';
|
||||
import {serializeAst} from '../../html_parser/ast_serializer_spec';
|
||||
|
||||
export function main(): void {
|
||||
describe('XTB serializer', () => {
|
||||
let serializer: Xtb;
|
||||
|
||||
function loadAsText(content: string, placeholders: {[id: string]: {[name: string]: string}}):
|
||||
{[id: string]: string} {
|
||||
const asAst = serializer.load(content, 'url', placeholders);
|
||||
let asText: {[id: string]: string} = {};
|
||||
Object.getOwnPropertyNames(asAst).forEach(
|
||||
id => { asText[id] = serializeAst(asAst[id]).join(''); });
|
||||
|
||||
return asText;
|
||||
}
|
||||
|
||||
beforeEach(() => { serializer = new Xtb(new HtmlParser(), DEFAULT_INTERPOLATION_CONFIG); });
|
||||
|
||||
|
||||
describe('load', () => {
|
||||
it('should load XTB files without placeholders', () => {
|
||||
const XTB = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<translationbundle>
|
||||
<translation id="foo">bar</translation>
|
||||
</translationbundle>`;
|
||||
|
||||
expect(loadAsText(XTB, {})).toEqual({foo: 'bar'});
|
||||
});
|
||||
|
||||
it('should load XTB files with placeholders', () => {
|
||||
const XTB = `
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<translationbundle>
|
||||
<translation id="foo">bar<ph name="PLACEHOLDER"/><ph name="PLACEHOLDER"/></translation>
|
||||
</translationbundle>`;
|
||||
|
||||
expect(loadAsText(XTB, {foo: {PLACEHOLDER: '!'}})).toEqual({foo: 'bar!!'});
|
||||
});
|
||||
|
||||
it('should load complex XTB files', () => {
|
||||
const XTB = `
|
||||
<? xml version="1.0" encoding="UTF-8" ?>
|
||||
<translationbundle>
|
||||
<translation id="a">translatable element <ph name="START_BOLD_TEXT"><ex><b></ex></ph>with placeholders<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> <ph name="INTERPOLATION"/></translation>
|
||||
<translation id="b">{ count, plural, =0 {<ph name="START_PARAGRAPH"><ex><p></ex></ph>test<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>}}</translation>
|
||||
<translation id="c" desc="d" meaning="m">foo</translation>
|
||||
<translation id="d">{ count, plural, =0 {{ sex, gender, other {<ph name="START_PARAGRAPH"><ex><p></ex></ph>deeply nested<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>}} }}</translation>
|
||||
</translationbundle>`;
|
||||
|
||||
const PLACEHOLDERS = {
|
||||
a: {
|
||||
START_BOLD_TEXT: '<b>',
|
||||
CLOSE_BOLD_TEXT: '</b>',
|
||||
INTERPOLATION: '{{ a + b }}',
|
||||
},
|
||||
b: {
|
||||
START_PARAGRAPH: '<p translated=true>',
|
||||
CLOSE_PARAGRAPH: '</p>',
|
||||
},
|
||||
d: {
|
||||
START_PARAGRAPH: '<p>',
|
||||
CLOSE_PARAGRAPH: '</p>',
|
||||
},
|
||||
};
|
||||
|
||||
expect(loadAsText(XTB, PLACEHOLDERS)).toEqual({
|
||||
a: 'translatable element <b>with placeholders</b> {{ a + b }}',
|
||||
b: '{ count, plural, =0 {<p translated="true">test</p>}}',
|
||||
c: 'foo',
|
||||
d: '{ count, plural, =0 {{ sex, gender, other {<p>deeply nested</p>}} }}',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
it('should throw on nested <translationbundle>', () => {
|
||||
const XTB =
|
||||
'<translationbundle><translationbundle></translationbundle></translationbundle>';
|
||||
|
||||
expect(() => {
|
||||
serializer.load(XTB, 'url', {});
|
||||
}).toThrowError(/<translationbundle> elements can not be nested/);
|
||||
});
|
||||
|
||||
it('should throw on nested <translation>', () => {
|
||||
const XTB = `
|
||||
<translationbundle>
|
||||
<translation id="outer">
|
||||
<translation id="inner">
|
||||
</translation>
|
||||
</translation>
|
||||
</translationbundle>`;
|
||||
|
||||
expect(() => {
|
||||
serializer.load(XTB, 'url', {});
|
||||
}).toThrowError(/<translation> elements can not be nested/);
|
||||
});
|
||||
|
||||
it('should throw when a <translation> has no id attribute', () => {
|
||||
const XTB = `
|
||||
<translationbundle>
|
||||
<translation></translation>
|
||||
</translationbundle>`;
|
||||
|
||||
expect(() => {
|
||||
serializer.load(XTB, 'url', {});
|
||||
}).toThrowError(/<translation> misses the "id" attribute/);
|
||||
});
|
||||
|
||||
it('should throw when a placeholder has no name attribute', () => {
|
||||
const XTB = `
|
||||
<translationbundle>
|
||||
<translation id="fail"><ph /></translation>
|
||||
</translationbundle>`;
|
||||
|
||||
expect(() => {
|
||||
serializer.load(XTB, 'url', {});
|
||||
}).toThrowError(/<ph> misses the "name" attribute/);
|
||||
});
|
||||
|
||||
it('should throw when a placeholder is not present in the source message', () => {
|
||||
const XTB = `
|
||||
<translationbundle>
|
||||
<translation id="fail"><ph name="UNKNOWN"/></translation>
|
||||
</translationbundle>`;
|
||||
|
||||
expect(() => {
|
||||
serializer.load(XTB, 'url', {});
|
||||
}).toThrowError(/The placeholder "UNKNOWN" does not exists in the source message/);
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw when the translation results in invalid html', () => {
|
||||
const XTB = `
|
||||
<translationbundle>
|
||||
<translation id="fail">foo<ph name="CLOSE_P"/>bar</translation>
|
||||
</translationbundle>`;
|
||||
|
||||
expect(() => {
|
||||
serializer.load(XTB, 'url', {fail: {CLOSE_P: '</p>'}});
|
||||
}).toThrowError(/xtb parse errors:\nUnexpected closing tag "p"/);
|
||||
|
||||
});
|
||||
|
||||
it('should throw on unknown tags', () => {
|
||||
const XTB = `<what></what>`;
|
||||
|
||||
expect(() => {
|
||||
serializer.load(XTB, 'url', {});
|
||||
}).toThrowError(new RegExp(escapeRegExp(`Unexpected tag ("[ERROR ->]<what></what>")`)));
|
||||
});
|
||||
|
||||
it('should throw when trying to save an xmb file',
|
||||
() => { expect(() => { serializer.write({}); }).toThrow(); });
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user