feat(compiler): add target locale to the translation bundles (#14290)
PR Close #14290
This commit is contained in:
parent
4676df5833
commit
bb4db2d8f3
|
@ -14,7 +14,8 @@ export abstract class Serializer {
|
||||||
// - Placeholder names are already map to public names using the provided mapper
|
// - Placeholder names are already map to public names using the provided mapper
|
||||||
abstract write(messages: i18n.Message[]): string;
|
abstract write(messages: i18n.Message[]): string;
|
||||||
|
|
||||||
abstract load(content: string, url: string): {[msgId: string]: i18n.Node[]};
|
abstract load(content: string, url: string):
|
||||||
|
{locale: string | null, i18nNodesByMsgId: {[msgId: string]: i18n.Node[]}};
|
||||||
|
|
||||||
abstract digest(message: i18n.Message): string;
|
abstract digest(message: i18n.Message): string;
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ const _XMLNS = 'urn:oasis:names:tc:xliff:document:1.2';
|
||||||
const _SOURCE_LANG = 'en';
|
const _SOURCE_LANG = 'en';
|
||||||
const _PLACEHOLDER_TAG = 'x';
|
const _PLACEHOLDER_TAG = 'x';
|
||||||
|
|
||||||
|
const _FILE_TAG = 'file';
|
||||||
const _SOURCE_TAG = 'source';
|
const _SOURCE_TAG = 'source';
|
||||||
const _TARGET_TAG = 'target';
|
const _TARGET_TAG = 'target';
|
||||||
const _UNIT_TAG = 'trans-unit';
|
const _UNIT_TAG = 'trans-unit';
|
||||||
|
@ -68,10 +69,11 @@ export class Xliff extends Serializer {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
load(content: string, url: string): {[msgId: string]: i18n.Node[]} {
|
load(content: string, url: string):
|
||||||
|
{locale: string, i18nNodesByMsgId: {[msgId: string]: i18n.Node[]}} {
|
||||||
// xliff to xml nodes
|
// xliff to xml nodes
|
||||||
const xliffParser = new XliffParser();
|
const xliffParser = new XliffParser();
|
||||||
const {mlNodesByMsgId, errors} = xliffParser.parse(content, url);
|
const {locale, mlNodesByMsgId, errors} = xliffParser.parse(content, url);
|
||||||
|
|
||||||
// xml nodes to i18n nodes
|
// xml nodes to i18n nodes
|
||||||
const i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {};
|
const i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {};
|
||||||
|
@ -86,7 +88,7 @@ export class Xliff extends Serializer {
|
||||||
throw new Error(`xliff parse errors:\n${errors.join('\n')}`);
|
throw new Error(`xliff parse errors:\n${errors.join('\n')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return i18nNodesByMsgId;
|
return {locale, i18nNodesByMsgId};
|
||||||
}
|
}
|
||||||
|
|
||||||
digest(message: i18n.Message): string { return digest(message); }
|
digest(message: i18n.Message): string { return digest(message); }
|
||||||
|
@ -154,6 +156,7 @@ class XliffParser implements ml.Visitor {
|
||||||
private _unitMlNodes: ml.Node[];
|
private _unitMlNodes: ml.Node[];
|
||||||
private _errors: I18nError[];
|
private _errors: I18nError[];
|
||||||
private _mlNodesByMsgId: {[msgId: string]: ml.Node[]};
|
private _mlNodesByMsgId: {[msgId: string]: ml.Node[]};
|
||||||
|
private _locale: string|null = null;
|
||||||
|
|
||||||
parse(xliff: string, url: string) {
|
parse(xliff: string, url: string) {
|
||||||
this._unitMlNodes = [];
|
this._unitMlNodes = [];
|
||||||
|
@ -167,6 +170,7 @@ class XliffParser implements ml.Visitor {
|
||||||
return {
|
return {
|
||||||
mlNodesByMsgId: this._mlNodesByMsgId,
|
mlNodesByMsgId: this._mlNodesByMsgId,
|
||||||
errors: this._errors,
|
errors: this._errors,
|
||||||
|
locale: this._locale,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,6 +204,14 @@ class XliffParser implements ml.Visitor {
|
||||||
this._unitMlNodes = element.children;
|
this._unitMlNodes = element.children;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case _FILE_TAG:
|
||||||
|
const localeAttr = element.attrs.find((attr) => attr.name === 'target-language');
|
||||||
|
if (localeAttr) {
|
||||||
|
this._locale = localeAttr.value;
|
||||||
|
}
|
||||||
|
ml.visitAll(this, element.children, null);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// TODO(vicb): assert file structure, xliff version
|
// TODO(vicb): assert file structure, xliff version
|
||||||
// For now only recurse on unhandled nodes
|
// For now only recurse on unhandled nodes
|
||||||
|
|
|
@ -70,7 +70,8 @@ export class Xmb extends Serializer {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
load(content: string, url: string): {[msgId: string]: i18n.Node[]} {
|
load(content: string, url: string):
|
||||||
|
{locale: string, i18nNodesByMsgId: {[msgId: string]: i18n.Node[]}} {
|
||||||
throw new Error('Unsupported');
|
throw new Error('Unsupported');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,11 @@ const _PLACEHOLDER_TAG = 'ph';
|
||||||
export class Xtb extends Serializer {
|
export class Xtb extends Serializer {
|
||||||
write(messages: i18n.Message[]): string { throw new Error('Unsupported'); }
|
write(messages: i18n.Message[]): string { throw new Error('Unsupported'); }
|
||||||
|
|
||||||
load(content: string, url: string): {[msgId: string]: i18n.Node[]} {
|
load(content: string, url: string):
|
||||||
|
{locale: string, i18nNodesByMsgId: {[msgId: string]: i18n.Node[]}} {
|
||||||
// xtb to xml nodes
|
// xtb to xml nodes
|
||||||
const xtbParser = new XtbParser();
|
const xtbParser = new XtbParser();
|
||||||
const {msgIdToHtml, errors} = xtbParser.parse(content, url);
|
const {locale, msgIdToHtml, errors} = xtbParser.parse(content, url);
|
||||||
|
|
||||||
// xml nodes to i18n nodes
|
// xml nodes to i18n nodes
|
||||||
const i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {};
|
const i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {};
|
||||||
|
@ -48,7 +49,7 @@ export class Xtb extends Serializer {
|
||||||
throw new Error(`xtb parse errors:\n${errors.join('\n')}`);
|
throw new Error(`xtb parse errors:\n${errors.join('\n')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return i18nNodesByMsgId;
|
return {locale, i18nNodesByMsgId};
|
||||||
}
|
}
|
||||||
|
|
||||||
digest(message: i18n.Message): string { return digest(message); }
|
digest(message: i18n.Message): string { return digest(message); }
|
||||||
|
@ -76,6 +77,7 @@ class XtbParser implements ml.Visitor {
|
||||||
private _bundleDepth: number;
|
private _bundleDepth: number;
|
||||||
private _errors: I18nError[];
|
private _errors: I18nError[];
|
||||||
private _msgIdToHtml: {[msgId: string]: string};
|
private _msgIdToHtml: {[msgId: string]: string};
|
||||||
|
private _locale: string|null = null;
|
||||||
|
|
||||||
parse(xtb: string, url: string) {
|
parse(xtb: string, url: string) {
|
||||||
this._bundleDepth = 0;
|
this._bundleDepth = 0;
|
||||||
|
@ -91,6 +93,7 @@ class XtbParser implements ml.Visitor {
|
||||||
return {
|
return {
|
||||||
msgIdToHtml: this._msgIdToHtml,
|
msgIdToHtml: this._msgIdToHtml,
|
||||||
errors: this._errors,
|
errors: this._errors,
|
||||||
|
locale: this._locale,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +104,10 @@ class XtbParser implements ml.Visitor {
|
||||||
if (this._bundleDepth > 1) {
|
if (this._bundleDepth > 1) {
|
||||||
this._addError(element, `<${_TRANSLATIONS_TAG}> elements can not be nested`);
|
this._addError(element, `<${_TRANSLATIONS_TAG}> elements can not be nested`);
|
||||||
}
|
}
|
||||||
|
const langAttr = element.attrs.find((attr) => attr.name === 'lang');
|
||||||
|
if (langAttr) {
|
||||||
|
this._locale = langAttr.value;
|
||||||
|
}
|
||||||
ml.visitAll(this, element.children, null);
|
ml.visitAll(this, element.children, null);
|
||||||
this._bundleDepth--;
|
this._bundleDepth--;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -23,13 +23,13 @@ export class TranslationBundle {
|
||||||
private _i18nToHtml: I18nToHtmlVisitor;
|
private _i18nToHtml: I18nToHtmlVisitor;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {},
|
private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {}, locale: string|null,
|
||||||
public digest: (m: i18n.Message) => string,
|
public digest: (m: i18n.Message) => string,
|
||||||
public mapperFactory?: (m: i18n.Message) => PlaceholderMapper,
|
public mapperFactory?: (m: i18n.Message) => PlaceholderMapper,
|
||||||
missingTranslationStrategy: MissingTranslationStrategy = MissingTranslationStrategy.Warning,
|
missingTranslationStrategy: MissingTranslationStrategy = MissingTranslationStrategy.Warning,
|
||||||
console?: Console) {
|
console?: Console) {
|
||||||
this._i18nToHtml = new I18nToHtmlVisitor(
|
this._i18nToHtml = new I18nToHtmlVisitor(
|
||||||
_i18nNodesByMsgId, digest, mapperFactory, missingTranslationStrategy, console);
|
_i18nNodesByMsgId, locale, digest, mapperFactory, missingTranslationStrategy, console);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a `TranslationBundle` by parsing the given `content` with the `serializer`.
|
// Creates a `TranslationBundle` by parsing the given `content` with the `serializer`.
|
||||||
|
@ -37,11 +37,11 @@ export class TranslationBundle {
|
||||||
content: string, url: string, serializer: Serializer,
|
content: string, url: string, serializer: Serializer,
|
||||||
missingTranslationStrategy: MissingTranslationStrategy,
|
missingTranslationStrategy: MissingTranslationStrategy,
|
||||||
console?: Console): TranslationBundle {
|
console?: Console): TranslationBundle {
|
||||||
const i18nNodesByMsgId = serializer.load(content, url);
|
const {locale, i18nNodesByMsgId} = serializer.load(content, url);
|
||||||
const digestFn = (m: i18n.Message) => serializer.digest(m);
|
const digestFn = (m: i18n.Message) => serializer.digest(m);
|
||||||
const mapperFactory = (m: i18n.Message) => serializer.createNameMapper(m);
|
const mapperFactory = (m: i18n.Message) => serializer.createNameMapper(m);
|
||||||
return new TranslationBundle(
|
return new TranslationBundle(
|
||||||
i18nNodesByMsgId, digestFn, mapperFactory, missingTranslationStrategy, console);
|
i18nNodesByMsgId, locale, digestFn, mapperFactory, missingTranslationStrategy, console);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the translation as HTML nodes from the given source message.
|
// Returns the translation as HTML nodes from the given source message.
|
||||||
|
@ -65,7 +65,7 @@ class I18nToHtmlVisitor implements i18n.Visitor {
|
||||||
private _mapper: (name: string) => string;
|
private _mapper: (name: string) => string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {},
|
private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {}, private _locale: string|null,
|
||||||
private _digest: (m: i18n.Message) => string,
|
private _digest: (m: i18n.Message) => string,
|
||||||
private _mapperFactory: (m: i18n.Message) => PlaceholderMapper,
|
private _mapperFactory: (m: i18n.Message) => PlaceholderMapper,
|
||||||
private _missingTranslationStrategy: MissingTranslationStrategy, private _console?: Console) {
|
private _missingTranslationStrategy: MissingTranslationStrategy, private _console?: Console) {
|
||||||
|
@ -166,11 +166,13 @@ class I18nToHtmlVisitor implements i18n.Visitor {
|
||||||
// - use the nodes from the original message
|
// - use the nodes from the original message
|
||||||
// - placeholders are already internal and need no mapper
|
// - placeholders are already internal and need no mapper
|
||||||
if (this._missingTranslationStrategy === MissingTranslationStrategy.Error) {
|
if (this._missingTranslationStrategy === MissingTranslationStrategy.Error) {
|
||||||
this._addError(srcMsg.nodes[0], `Missing translation for message "${id}"`);
|
const ctx = this._locale ? ` for locale "${this._locale}"` : '';
|
||||||
|
this._addError(srcMsg.nodes[0], `Missing translation for message "${id}"${ctx}`);
|
||||||
} else if (
|
} else if (
|
||||||
this._console &&
|
this._console &&
|
||||||
this._missingTranslationStrategy === MissingTranslationStrategy.Warning) {
|
this._missingTranslationStrategy === MissingTranslationStrategy.Warning) {
|
||||||
this._console.warn(`Missing translation for message "${id}"`);
|
const ctx = this._locale ? ` for locale "${this._locale}"` : '';
|
||||||
|
this._console.warn(`Missing translation for message "${id}"${ctx}`);
|
||||||
}
|
}
|
||||||
nodes = srcMsg.nodes;
|
nodes = srcMsg.nodes;
|
||||||
this._mapper = (name: string) => name;
|
this._mapper = (name: string) => name;
|
||||||
|
|
|
@ -393,7 +393,7 @@ export function main() {
|
||||||
expect(messages.length).toEqual(1);
|
expect(messages.length).toEqual(1);
|
||||||
const i18nMsgMap: {[id: string]: i18n.Node[]} = {};
|
const i18nMsgMap: {[id: string]: i18n.Node[]} = {};
|
||||||
i18nMsgMap[digest(messages[0])] = [];
|
i18nMsgMap[digest(messages[0])] = [];
|
||||||
const translations = new TranslationBundle(i18nMsgMap, digest);
|
const translations = new TranslationBundle(i18nMsgMap, null, digest);
|
||||||
|
|
||||||
const output =
|
const output =
|
||||||
mergeTranslations(htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, [], {});
|
mergeTranslations(htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, [], {});
|
||||||
|
@ -450,7 +450,7 @@ export function main() {
|
||||||
expect(messages.length).toEqual(1);
|
expect(messages.length).toEqual(1);
|
||||||
const i18nMsgMap: {[id: string]: i18n.Node[]} = {};
|
const i18nMsgMap: {[id: string]: i18n.Node[]} = {};
|
||||||
i18nMsgMap[digest(messages[0])] = [];
|
i18nMsgMap[digest(messages[0])] = [];
|
||||||
const translations = new TranslationBundle(i18nMsgMap, digest);
|
const translations = new TranslationBundle(i18nMsgMap, null, digest);
|
||||||
|
|
||||||
const output =
|
const output =
|
||||||
mergeTranslations(htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, [], {});
|
mergeTranslations(htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, [], {});
|
||||||
|
@ -488,7 +488,7 @@ function fakeTranslate(
|
||||||
i18nMsgMap[id] = [new i18n.Text(`**${text}**`, null)];
|
i18nMsgMap[id] = [new i18n.Text(`**${text}**`, null)];
|
||||||
});
|
});
|
||||||
|
|
||||||
const translations = new TranslationBundle(i18nMsgMap, digest);
|
const translations = new TranslationBundle(i18nMsgMap, null, digest);
|
||||||
|
|
||||||
const output = mergeTranslations(
|
const output = mergeTranslations(
|
||||||
htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, implicitTags, implicitAttrs);
|
htmlNodes, translations, DEFAULT_INTERPOLATION_CONFIG, implicitTags, implicitAttrs);
|
||||||
|
|
|
@ -31,7 +31,7 @@ export function main() {
|
||||||
|
|
||||||
// https://github.com/angular/angular/issues/14322
|
// https://github.com/angular/angular/issues/14322
|
||||||
it('should parse the translations only once', () => {
|
it('should parse the translations only once', () => {
|
||||||
const transBundle = new TranslationBundle({}, () => 'id');
|
const transBundle = new TranslationBundle({}, null, () => 'id');
|
||||||
spyOn(TranslationBundle, 'load').and.returnValue(transBundle);
|
spyOn(TranslationBundle, 'load').and.returnValue(transBundle);
|
||||||
const htmlParser = new HtmlParser();
|
const htmlParser = new HtmlParser();
|
||||||
const i18nHtmlParser = new I18NHtmlParser(htmlParser, 'translations');
|
const i18nHtmlParser = new I18NHtmlParser(htmlParser, 'translations');
|
||||||
|
|
|
@ -47,7 +47,10 @@ class _TestSerializer extends Serializer {
|
||||||
.join('//');
|
.join('//');
|
||||||
}
|
}
|
||||||
|
|
||||||
load(content: string, url: string): {} { return null; }
|
load(content: string, url: string):
|
||||||
|
{locale: string | null, i18nNodesByMsgId: {[id: string]: i18n.Node[]}} {
|
||||||
|
return {locale: null, i18nNodesByMsgId: {}};
|
||||||
|
}
|
||||||
|
|
||||||
digest(msg: i18n.Message): string { return msg.id || `default`; }
|
digest(msg: i18n.Message): string { return msg.id || `default`; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
const LOAD_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
const LOAD_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
<file source-language="en" datatype="plaintext" original="ng2.template">
|
<file source-language="en" target-language="fr" datatype="plaintext" original="ng2.template">
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="983775b9a51ce14b036be72d4cfd65d68d64e231" datatype="html">
|
<trans-unit id="983775b9a51ce14b036be72d4cfd65d68d64e231" datatype="html">
|
||||||
<source>translatable attribute</source>
|
<source>translatable attribute</source>
|
||||||
|
@ -114,7 +114,7 @@ export function main(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadAsMap(xliff: string): {[id: string]: string} {
|
function loadAsMap(xliff: string): {[id: string]: string} {
|
||||||
const i18nNodesByMsgId = serializer.load(xliff, 'url');
|
const {i18nNodesByMsgId} = serializer.load(xliff, 'url');
|
||||||
|
|
||||||
const msgMap: {[id: string]: string} = {};
|
const msgMap: {[id: string]: string} = {};
|
||||||
Object.keys(i18nNodesByMsgId)
|
Object.keys(i18nNodesByMsgId)
|
||||||
|
@ -143,6 +143,10 @@ export function main(): void {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return the target locale',
|
||||||
|
() => { expect(serializer.load(LOAD_XLIFF, 'url').locale).toEqual('fr'); });
|
||||||
|
|
||||||
|
|
||||||
describe('structure errors', () => {
|
describe('structure errors', () => {
|
||||||
it('should throw when a trans-unit has no translation', () => {
|
it('should throw when a trans-unit has no translation', () => {
|
||||||
const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
const XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
|
@ -18,7 +18,7 @@ export function main(): void {
|
||||||
const serializer = new Xtb();
|
const serializer = new Xtb();
|
||||||
|
|
||||||
function loadAsMap(xtb: string): {[id: string]: string} {
|
function loadAsMap(xtb: string): {[id: string]: string} {
|
||||||
const i18nNodesByMsgId = serializer.load(xtb, 'url');
|
const {i18nNodesByMsgId} = serializer.load(xtb, 'url');
|
||||||
const msgMap: {[id: string]: string} = {};
|
const msgMap: {[id: string]: string} = {};
|
||||||
Object.keys(i18nNodesByMsgId).forEach(id => {
|
Object.keys(i18nNodesByMsgId).forEach(id => {
|
||||||
msgMap[id] = serializeNodes(i18nNodesByMsgId[id]).join('');
|
msgMap[id] = serializeNodes(i18nNodesByMsgId[id]).join('');
|
||||||
|
@ -54,6 +54,14 @@ export function main(): void {
|
||||||
expect(loadAsMap(XTB)).toEqual({'8841459487341224498': 'rab'});
|
expect(loadAsMap(XTB)).toEqual({'8841459487341224498': 'rab'});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return the target locale', () => {
|
||||||
|
const XTB = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<translationbundle lang='fr'>
|
||||||
|
<translation id="8841459487341224498">rab</translation>
|
||||||
|
</translationbundle>`;
|
||||||
|
|
||||||
|
expect(serializer.load(XTB, 'url').locale).toEqual('fr');
|
||||||
|
});
|
||||||
|
|
||||||
it('should load XTB files with placeholders', () => {
|
it('should load XTB files with placeholders', () => {
|
||||||
const XTB = `<?xml version="1.0" encoding="UTF-8"?>
|
const XTB = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
@ -111,7 +119,9 @@ export function main(): void {
|
||||||
|
|
||||||
// Invalid messages should not cause the parser to throw
|
// Invalid messages should not cause the parser to throw
|
||||||
let i18nNodesByMsgId: {[id: string]: i18n.Node[]};
|
let i18nNodesByMsgId: {[id: string]: i18n.Node[]};
|
||||||
expect(() => { i18nNodesByMsgId = serializer.load(XTB, 'url'); }).not.toThrow();
|
expect(() => {
|
||||||
|
i18nNodesByMsgId = serializer.load(XTB, 'url').i18nNodesByMsgId;
|
||||||
|
}).not.toThrow();
|
||||||
|
|
||||||
expect(Object.keys(i18nNodesByMsgId).length).toEqual(2);
|
expect(Object.keys(i18nNodesByMsgId).length).toEqual(2);
|
||||||
expect(serializeNodes(i18nNodesByMsgId['angular']).join('')).toEqual('is great');
|
expect(serializeNodes(i18nNodesByMsgId['angular']).join('')).toEqual('is great');
|
||||||
|
|
|
@ -23,7 +23,7 @@ export function main(): void {
|
||||||
|
|
||||||
it('should translate a plain message', () => {
|
it('should translate a plain message', () => {
|
||||||
const msgMap = {foo: [new i18n.Text('bar', null)]};
|
const msgMap = {foo: [new i18n.Text('bar', null)]};
|
||||||
const tb = new TranslationBundle(msgMap, (_) => 'foo');
|
const tb = new TranslationBundle(msgMap, null, (_) => 'foo');
|
||||||
const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i');
|
const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i');
|
||||||
expect(serializeNodes(tb.get(msg))).toEqual(['bar']);
|
expect(serializeNodes(tb.get(msg))).toEqual(['bar']);
|
||||||
});
|
});
|
||||||
|
@ -38,7 +38,7 @@ export function main(): void {
|
||||||
const phMap = {
|
const phMap = {
|
||||||
ph1: '*phContent*',
|
ph1: '*phContent*',
|
||||||
};
|
};
|
||||||
const tb = new TranslationBundle(msgMap, (_) => 'foo');
|
const tb = new TranslationBundle(msgMap, null, (_) => 'foo');
|
||||||
const msg = new i18n.Message([srcNode], phMap, {}, 'm', 'd', 'i');
|
const msg = new i18n.Message([srcNode], phMap, {}, 'm', 'd', 'i');
|
||||||
expect(serializeNodes(tb.get(msg))).toEqual(['bar*phContent*']);
|
expect(serializeNodes(tb.get(msg))).toEqual(['bar*phContent*']);
|
||||||
});
|
});
|
||||||
|
@ -58,7 +58,7 @@ export function main(): void {
|
||||||
const msg = new i18n.Message([srcNode], {}, {ph1: refMsg}, 'm', 'd', 'i');
|
const msg = new i18n.Message([srcNode], {}, {ph1: refMsg}, 'm', 'd', 'i');
|
||||||
let count = 0;
|
let count = 0;
|
||||||
const digest = (_: any) => count++ ? 'ref' : 'foo';
|
const digest = (_: any) => count++ ? 'ref' : 'foo';
|
||||||
const tb = new TranslationBundle(msgMap, digest);
|
const tb = new TranslationBundle(msgMap, null, digest);
|
||||||
|
|
||||||
expect(serializeNodes(tb.get(msg))).toEqual(['--*refMsg*++']);
|
expect(serializeNodes(tb.get(msg))).toEqual(['--*refMsg*++']);
|
||||||
});
|
});
|
||||||
|
@ -70,13 +70,13 @@ export function main(): void {
|
||||||
|
|
||||||
const digest = (_: any) => `no matching id`;
|
const digest = (_: any) => `no matching id`;
|
||||||
// Empty message map -> use source messages in Ignore mode
|
// Empty message map -> use source messages in Ignore mode
|
||||||
let tb = new TranslationBundle({}, digest, null, MissingTranslationStrategy.Ignore);
|
let tb = new TranslationBundle({}, null, digest, null, MissingTranslationStrategy.Ignore);
|
||||||
expect(serializeNodes(tb.get(messages[0])).join('')).toEqual(src);
|
expect(serializeNodes(tb.get(messages[0])).join('')).toEqual(src);
|
||||||
// Empty message map -> use source messages in Warning mode
|
// Empty message map -> use source messages in Warning mode
|
||||||
tb = new TranslationBundle({}, digest, null, MissingTranslationStrategy.Warning);
|
tb = new TranslationBundle({}, null, digest, null, MissingTranslationStrategy.Warning);
|
||||||
expect(serializeNodes(tb.get(messages[0])).join('')).toEqual(src);
|
expect(serializeNodes(tb.get(messages[0])).join('')).toEqual(src);
|
||||||
// Empty message map -> throw in Error mode
|
// Empty message map -> throw in Error mode
|
||||||
tb = new TranslationBundle({}, digest, null, MissingTranslationStrategy.Error);
|
tb = new TranslationBundle({}, null, digest, null, MissingTranslationStrategy.Error);
|
||||||
expect(() => serializeNodes(tb.get(messages[0])).join('')).toThrow();
|
expect(() => serializeNodes(tb.get(messages[0])).join('')).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -88,13 +88,14 @@ export function main(): void {
|
||||||
new i18n.Placeholder('', 'ph1', span),
|
new i18n.Placeholder('', 'ph1', span),
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
const tb = new TranslationBundle(msgMap, (_) => 'foo');
|
const tb = new TranslationBundle(msgMap, null, (_) => 'foo');
|
||||||
const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i');
|
const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i');
|
||||||
expect(() => tb.get(msg)).toThrowError(/Unknown placeholder/);
|
expect(() => tb.get(msg)).toThrowError(/Unknown placeholder/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should report missing translation', () => {
|
it('should report missing translation', () => {
|
||||||
const tb = new TranslationBundle({}, (_) => 'foo', null, MissingTranslationStrategy.Error);
|
const tb =
|
||||||
|
new TranslationBundle({}, null, (_) => 'foo', null, MissingTranslationStrategy.Error);
|
||||||
const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i');
|
const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i');
|
||||||
expect(() => tb.get(msg)).toThrowError(/Missing translation for message "foo"/);
|
expect(() => tb.get(msg)).toThrowError(/Missing translation for message "foo"/);
|
||||||
});
|
});
|
||||||
|
@ -107,16 +108,17 @@ export function main(): void {
|
||||||
};
|
};
|
||||||
|
|
||||||
const tb = new TranslationBundle(
|
const tb = new TranslationBundle(
|
||||||
{}, (_) => 'foo', null, MissingTranslationStrategy.Warning, console);
|
{}, 'en', (_) => 'foo', null, MissingTranslationStrategy.Warning, console);
|
||||||
const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i');
|
const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i');
|
||||||
|
|
||||||
expect(() => tb.get(msg)).not.toThrowError();
|
expect(() => tb.get(msg)).not.toThrowError();
|
||||||
expect(log.length).toEqual(1);
|
expect(log.length).toEqual(1);
|
||||||
expect(log[0]).toMatch(/Missing translation for message "foo"/);
|
expect(log[0]).toMatch(/Missing translation for message "foo" for locale "en"/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not report missing translation with MissingTranslationStrategy.Ignore', () => {
|
it('should not report missing translation with MissingTranslationStrategy.Ignore', () => {
|
||||||
const tb = new TranslationBundle({}, (_) => 'foo', null, MissingTranslationStrategy.Ignore);
|
const tb =
|
||||||
|
new TranslationBundle({}, null, (_) => 'foo', null, MissingTranslationStrategy.Ignore);
|
||||||
const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i');
|
const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i');
|
||||||
expect(() => tb.get(msg)).not.toThrowError();
|
expect(() => tb.get(msg)).not.toThrowError();
|
||||||
});
|
});
|
||||||
|
@ -129,7 +131,8 @@ export function main(): void {
|
||||||
const msg = new i18n.Message([srcNode], {}, {ph1: refMsg}, 'm', 'd', 'i');
|
const msg = new i18n.Message([srcNode], {}, {ph1: refMsg}, 'm', 'd', 'i');
|
||||||
let count = 0;
|
let count = 0;
|
||||||
const digest = (_: any) => count++ ? 'ref' : 'foo';
|
const digest = (_: any) => count++ ? 'ref' : 'foo';
|
||||||
const tb = new TranslationBundle(msgMap, digest, null, MissingTranslationStrategy.Error);
|
const tb =
|
||||||
|
new TranslationBundle(msgMap, null, digest, null, MissingTranslationStrategy.Error);
|
||||||
expect(() => tb.get(msg)).toThrowError(/Missing translation for message "ref"/);
|
expect(() => tb.get(msg)).toThrowError(/Missing translation for message "ref"/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -143,7 +146,7 @@ export function main(): void {
|
||||||
const phMap = {
|
const phMap = {
|
||||||
ph1: '</b>',
|
ph1: '</b>',
|
||||||
};
|
};
|
||||||
const tb = new TranslationBundle(msgMap, (_) => 'foo');
|
const tb = new TranslationBundle(msgMap, null, (_) => 'foo');
|
||||||
const msg = new i18n.Message([srcNode], phMap, {}, 'm', 'd', 'i');
|
const msg = new i18n.Message([srcNode], phMap, {}, 'm', 'd', 'i');
|
||||||
expect(() => tb.get(msg)).toThrowError(/Unexpected closing tag "b"/);
|
expect(() => tb.get(msg)).toThrowError(/Unexpected closing tag "b"/);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue