feat(compiler): add source files to xmb/xliff translations (#14705)
Fixes #14190
This commit is contained in:
parent
09c4cb2540
commit
4054055d0d
|
@ -10,7 +10,11 @@ import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ComponentFactoryResolver, Injec
|
|||
|
||||
import {BasicComp} from './basic';
|
||||
|
||||
@Component({selector: 'cmp-entryComponents', template: '', entryComponents: [BasicComp]})
|
||||
@Component({
|
||||
selector: 'cmp-entryComponents',
|
||||
template: '<p i18n>Welcome</p>',
|
||||
entryComponents: [BasicComp]
|
||||
})
|
||||
export class CompWithEntryComponents {
|
||||
constructor(public cfr: ComponentFactoryResolver) {}
|
||||
}
|
||||
|
|
|
@ -34,9 +34,10 @@ const EXPECTED_XMB = `<?xml version="1.0" encoding="UTF-8" ?>
|
|||
<!ELEMENT ex (#PCDATA)>
|
||||
]>
|
||||
<messagebundle>
|
||||
<msg id="8136548302122759730" desc="desc" meaning="meaning">translate me</msg>
|
||||
<msg id="3492007542396725315">Welcome</msg>
|
||||
<msg id="3772663375917578720">other-3rdP-component</msg>
|
||||
<msg id="8136548302122759730" desc="desc" meaning="meaning"><source>src/basic.ts:1</source>translate me</msg>
|
||||
<msg id="3492007542396725315"><source>src/basic.ts:5</source><source>src/entry_components.ts:1</source>Welcome</msg>
|
||||
<msg id="126808141597411718"><source>node_modules/third_party/other_comp.d.ts:1,2</source>other-3rdP-component
|
||||
multi-lines</msg>
|
||||
</messagebundle>
|
||||
`;
|
||||
|
||||
|
@ -47,16 +48,33 @@ const EXPECTED_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
|||
<trans-unit id="76e1eccb1b772fa9f294ef9c146ea6d0efa8a2d4" datatype="html">
|
||||
<source>translate me</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/basic.ts</context>
|
||||
<context context-type="linenumber">1</context>
|
||||
</context-group>
|
||||
<note priority="1" from="description">desc</note>
|
||||
<note priority="1" from="meaning">meaning</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="65cc4ab3b4c438e07c89be2b677d08369fb62da2" datatype="html">
|
||||
<source>Welcome</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/basic.ts</context>
|
||||
<context context-type="linenumber">5</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/entry_components.ts</context>
|
||||
<context context-type="linenumber">1</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="63a85808f03b8181e36a952e0fa38202c2304862" datatype="html">
|
||||
<source>other-3rdP-component</source>
|
||||
<trans-unit id="b0a17f08a4bd742b2acf39780c257c2f519d33ed" datatype="html">
|
||||
<source>other-3rdP-component
|
||||
multi-lines</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">node_modules/third_party/other_comp.d.ts</context>
|
||||
<context context-type="linenumber">1</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
|
|
@ -59,7 +59,8 @@ describe('NgModule', () => {
|
|||
const fixture = createComponent(ComponentUsingThirdParty);
|
||||
const thirdPComps = fixture.nativeElement.children;
|
||||
expect(thirdPComps[0].children[0].children[0].data).toEqual('3rdP-component');
|
||||
expect(thirdPComps[1].children[0].children[0].data).toEqual('other-3rdP-component');
|
||||
expect(thirdPComps[1].children[0].children[0].data).toEqual(`other-3rdP-component
|
||||
multi-lines`);
|
||||
});
|
||||
|
||||
// https://github.com/angular/angular/issues/12428
|
||||
|
|
|
@ -10,7 +10,8 @@ import {Component} from '@angular/core';
|
|||
|
||||
@Component({
|
||||
selector: 'another-third-party-comp',
|
||||
template: '<div i18n>other-3rdP-component</div>',
|
||||
template: `<div i18n>other-3rdP-component
|
||||
multi-lines</div>`,
|
||||
})
|
||||
export class AnotherThirdpartyComponent {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,8 +65,9 @@ export class Extractor {
|
|||
default:
|
||||
serializer = new compiler.Xliff();
|
||||
}
|
||||
|
||||
return bundle.write(serializer);
|
||||
return bundle.write(
|
||||
serializer,
|
||||
(sourcePath: string) => sourcePath.replace(path.join(this.options.basePath, '/'), ''));
|
||||
}
|
||||
|
||||
getExtension(formatName: string): string {
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
import {ParseSourceSpan} from '../parse_util';
|
||||
|
||||
export class Message {
|
||||
sources: MessageSpan[];
|
||||
|
||||
/**
|
||||
* @param nodes message AST
|
||||
* @param placeholders maps placeholder names to static content
|
||||
|
@ -20,7 +22,28 @@ export class Message {
|
|||
constructor(
|
||||
public nodes: Node[], public placeholders: {[phName: string]: string},
|
||||
public placeholderToMessage: {[phName: string]: Message}, public meaning: string,
|
||||
public description: string, public id: string) {}
|
||||
public description: string, public id: string) {
|
||||
if (nodes.length) {
|
||||
this.sources = [{
|
||||
filePath: nodes[0].sourceSpan.start.file.url,
|
||||
startLine: nodes[0].sourceSpan.start.line + 1,
|
||||
startCol: nodes[0].sourceSpan.start.col + 1,
|
||||
endLine: nodes[nodes.length - 1].sourceSpan.end.line + 1,
|
||||
endCol: nodes[0].sourceSpan.start.col + 1
|
||||
}];
|
||||
} else {
|
||||
this.sources = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// line and columns indexes are 1 based
|
||||
export interface MessageSpan {
|
||||
filePath: string;
|
||||
startLine: number;
|
||||
startCol: number;
|
||||
endLine: number;
|
||||
endCol: number;
|
||||
}
|
||||
|
||||
export interface Node {
|
||||
|
@ -131,4 +154,4 @@ export class RecurseVisitor implements Visitor {
|
|||
visitPlaceholder(ph: Placeholder, context?: any): any{};
|
||||
|
||||
visitIcuPlaceholder(ph: IcuPlaceholder, context?: any): any{};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ export class MessageBundle {
|
|||
// The public (serialized) format might be different, see the `write` method.
|
||||
getMessages(): i18n.Message[] { return this._messages; }
|
||||
|
||||
write(serializer: Serializer): string {
|
||||
write(serializer: Serializer, filterSources?: (path: string) => string): string {
|
||||
const messages: {[id: string]: i18n.Message} = {};
|
||||
const mapperVisitor = new MapPlaceholderNames();
|
||||
|
||||
|
@ -57,6 +57,8 @@ export class MessageBundle {
|
|||
const id = serializer.digest(message);
|
||||
if (!messages.hasOwnProperty(id)) {
|
||||
messages[id] = message;
|
||||
} else {
|
||||
messages[id].sources.push(...message.sources);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -65,7 +67,13 @@ export class MessageBundle {
|
|||
const mapper = serializer.createNameMapper(messages[id]);
|
||||
const src = messages[id];
|
||||
const nodes = mapper ? mapperVisitor.convert(src.nodes, mapper) : src.nodes;
|
||||
return new i18n.Message(nodes, {}, {}, src.meaning, src.description, id);
|
||||
let transformedMessage = new i18n.Message(nodes, {}, {}, src.meaning, src.description, id);
|
||||
transformedMessage.sources = src.sources;
|
||||
if (filterSources) {
|
||||
transformedMessage.sources.forEach(
|
||||
(source: i18n.MessageSpan) => source.filePath = filterSources(source.filePath));
|
||||
}
|
||||
return transformedMessage;
|
||||
});
|
||||
|
||||
return serializer.write(msgList, this._locale);
|
||||
|
|
|
@ -25,6 +25,8 @@ const _FILE_TAG = 'file';
|
|||
const _SOURCE_TAG = 'source';
|
||||
const _TARGET_TAG = 'target';
|
||||
const _UNIT_TAG = 'trans-unit';
|
||||
const _CONTEXT_GROUP_TAG = 'context-group';
|
||||
const _CONTEXT_TAG = 'context';
|
||||
|
||||
// http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html
|
||||
// http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html
|
||||
|
@ -34,10 +36,24 @@ export class Xliff extends Serializer {
|
|||
const transUnits: xml.Node[] = [];
|
||||
|
||||
messages.forEach(message => {
|
||||
let contextTags: xml.Node[] = [];
|
||||
message.sources.forEach((source: i18n.MessageSpan) => {
|
||||
let contextGroupTag = new xml.Tag(_CONTEXT_GROUP_TAG, {purpose: 'location'});
|
||||
contextGroupTag.children.push(
|
||||
new xml.CR(10),
|
||||
new xml.Tag(
|
||||
_CONTEXT_TAG, {'context-type': 'sourcefile'}, [new xml.Text(source.filePath)]),
|
||||
new xml.CR(10), new xml.Tag(
|
||||
_CONTEXT_TAG, {'context-type': 'linenumber'},
|
||||
[new xml.Text(`${source.startLine}`)]),
|
||||
new xml.CR(8));
|
||||
contextTags.push(new xml.CR(8), contextGroupTag);
|
||||
});
|
||||
|
||||
const transUnit = new xml.Tag(_UNIT_TAG, {id: message.id, datatype: 'html'});
|
||||
transUnit.children.push(
|
||||
new xml.CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)),
|
||||
new xml.CR(8), new xml.Tag(_TARGET_TAG));
|
||||
new xml.CR(8), new xml.Tag(_TARGET_TAG), ...contextTags);
|
||||
|
||||
if (message.description) {
|
||||
transUnit.children.push(
|
||||
|
|
|
@ -16,6 +16,7 @@ const _MESSAGES_TAG = 'messagebundle';
|
|||
const _MESSAGE_TAG = 'msg';
|
||||
const _PLACEHOLDER_TAG = 'ph';
|
||||
const _EXEMPLE_TAG = 'ex';
|
||||
const _SOURCE_TAG = 'source';
|
||||
|
||||
const _DOCTYPE = `<!ELEMENT messagebundle (msg)*>
|
||||
<!ATTLIST messagebundle class CDATA #IMPLIED>
|
||||
|
@ -54,8 +55,17 @@ export class Xmb extends Serializer {
|
|||
attrs['meaning'] = message.meaning;
|
||||
}
|
||||
|
||||
let sourceTags: xml.Tag[] = [];
|
||||
message.sources.forEach((source: i18n.MessageSpan) => {
|
||||
sourceTags.push(new xml.Tag(_SOURCE_TAG, {}, [
|
||||
new xml.Text(
|
||||
`${source.filePath}:${source.startLine}${source.endLine !== source.startLine ? ',' + source.endLine : ''}`)
|
||||
]));
|
||||
});
|
||||
|
||||
rootNode.children.push(
|
||||
new xml.CR(2), new xml.Tag(_MESSAGE_TAG, attrs, visitor.serialize(message.nodes)));
|
||||
new xml.CR(2),
|
||||
new xml.Tag(_MESSAGE_TAG, attrs, [...sourceTags, ...visitor.serialize(message.nodes)]));
|
||||
});
|
||||
|
||||
rootNode.children.push(new xml.CR());
|
||||
|
|
|
@ -19,6 +19,7 @@ export function main(): void {
|
|||
placeholderToMessage: {},
|
||||
meaning: '',
|
||||
description: '',
|
||||
sources: [],
|
||||
})).toEqual('i');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -39,7 +39,7 @@ export function main() {
|
|||
it('should extract from templates', () => {
|
||||
const catalog = new MessageBundle(new HtmlParser, [], {});
|
||||
const serializer = new Xliff();
|
||||
catalog.updateFromTemplate(HTML, '', DEFAULT_INTERPOLATION_CONFIG);
|
||||
catalog.updateFromTemplate(HTML, 'file.ts', DEFAULT_INTERPOLATION_CONFIG);
|
||||
|
||||
expect(catalog.write(serializer)).toContain(XLIFF_EXTRACTED);
|
||||
});
|
||||
|
@ -163,67 +163,139 @@ const XLIFF_EXTRACTED = `
|
|||
<trans-unit id="3cb04208df1c2f62553ed48e75939cf7107f9dad" datatype="html">
|
||||
<source>i18n attribute on tags</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">3</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="52895b1221effb3f3585b689f049d2784d714952" datatype="html">
|
||||
<source>nested</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">5</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="88d5f22050a9df477ee5646153558b3a4862d47e" datatype="html">
|
||||
<source>nested</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">7</context>
|
||||
</context-group>
|
||||
<note priority="1" from="meaning">different meaning</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="34fec9cc62e28e8aa6ffb306fa8569ef0a8087fe" datatype="html">
|
||||
<source><x id="START_ITALIC_TEXT" ctype="x-i"/>with placeholders<x id="CLOSE_ITALIC_TEXT" ctype="x-i"/></source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">9</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">10</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="1fe4616cce80a57c7707bac1c97054aa8e244a67" datatype="html">
|
||||
<source>on not translatable node</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">13</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="67162b5af5f15fd0eb6480c88688dafdf952b93a" datatype="html">
|
||||
<source>on translatable node</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">14</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="dc5536bb9e0e07291c185a0d306601a2ecd4813f" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">19</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">36</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="85ef51de59fe5a8d13fba977b6689f164420c8ca" datatype="html">
|
||||
<source>
|
||||
<x id="ICU"/>
|
||||
</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">21</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="c0ca5e58fe954d528bbfa516007a5a11690a7e99" datatype="html">
|
||||
<source>{VAR_SELECT, select, m {male} f {female} }</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">22</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="078b7089573c5f66a2f78dce0adaa55e6715beb1" datatype="html">
|
||||
<source>
|
||||
<x id="ICU"/>
|
||||
</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">24</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="a25cf2e21a299f30be1392e731163825233edc61" datatype="html">
|
||||
<source>{VAR_SELECT, select, m {male} f {female} }</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">25</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="d9879678f727b244bc7c7e20f22b63d98cb14890" datatype="html">
|
||||
<source><x id="INTERPOLATION"/></source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">28</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="50dac33dc6fc0578884baac79d875785ed77c928" datatype="html">
|
||||
<source>sex = <x id="INTERPOLATION"/></source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">29</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="a46f833b1fe6ca49e8b97c18f4b7ea0b930c9383" datatype="html">
|
||||
<source><x id="CUSTOM_NAME"/></source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">30</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2ec983b4893bcd5b24af33bebe3ecba63868453c" datatype="html">
|
||||
<source>in a translatable section</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">35</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">53</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="eee74a5be8a75881a4785905bd8302a71f7d9f75" datatype="html">
|
||||
<source>
|
||||
|
@ -232,29 +304,57 @@ const XLIFF_EXTRACTED = `
|
|||
<x id="START_TAG_DIV_1" ctype="x-div"/><x id="ICU"/><x id="CLOSE_TAG_DIV" ctype="x-div"/>
|
||||
</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">33</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="93a30c67d4e6c9b37aecfe2ac0f2b5d366d7b520" datatype="html">
|
||||
<source>it <x id="START_BOLD_TEXT" ctype="x-b"/>should<x id="CLOSE_BOLD_TEXT" ctype="x-b"/> work</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">39</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="i18n16" datatype="html">
|
||||
<source>with an explicit ID</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">41</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="i18n17" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<x id="START_BOLD_TEXT" ctype="x-b"/>many<x id="CLOSE_BOLD_TEXT" ctype="x-b"/>} }</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">42</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2370d995bdcc1e7496baa32df20654aff65c2d10" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <x id="INTERPOLATION"/> results} }</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">45</context>
|
||||
</context-group>
|
||||
<note priority="1" from="description">desc</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="296ab5eab8d370822488c152586db3a5875ee1a2" datatype="html">
|
||||
<source>foo<x id="START_LINK" ctype="x-a"/>bar<x id="CLOSE_LINK" ctype="x-a"/></source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">53</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2e013b311caa0916478941a985887e091d8288b6" datatype="html">
|
||||
<source><x id="MAP NAME"/></source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">55</context>
|
||||
</context-group>
|
||||
</trans-unit>`;
|
||||
|
|
|
@ -39,7 +39,7 @@ export function main() {
|
|||
it('should extract from templates', () => {
|
||||
const catalog = new MessageBundle(new HtmlParser, [], {});
|
||||
const serializer = new Xmb();
|
||||
catalog.updateFromTemplate(HTML, '', DEFAULT_INTERPOLATION_CONFIG);
|
||||
catalog.updateFromTemplate(HTML, 'file.ts', DEFAULT_INTERPOLATION_CONFIG);
|
||||
|
||||
expect(catalog.write(serializer)).toContain(XMB);
|
||||
});
|
||||
|
@ -84,29 +84,29 @@ const XTB = `
|
|||
<translation id="5339604010413301604"><ph name="MAP_NAME"><ex>MAP_NAME</ex></ph></translation>
|
||||
</translationbundle>`;
|
||||
|
||||
const XMB = ` <msg id="615790887472569365">i18n attribute on tags</msg>
|
||||
<msg id="3707494640264351337">nested</msg>
|
||||
<msg id="5539162898278769904" meaning="different meaning">nested</msg>
|
||||
<msg id="3780349238193953556"><ph name="START_ITALIC_TEXT"><ex><i></ex></ph>with placeholders<ph name="CLOSE_ITALIC_TEXT"><ex></i></ex></ph></msg>
|
||||
<msg id="5525133077318024839">on not translatable node</msg>
|
||||
<msg id="8670732454866344690">on translatable node</msg>
|
||||
<msg id="4593805537723189714">{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex><b></ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>} }</msg>
|
||||
<msg id="1746565782635215">
|
||||
const XMB = ` <msg id="615790887472569365"><source>file.ts:3</source>i18n attribute on tags</msg>
|
||||
<msg id="3707494640264351337"><source>file.ts:5</source>nested</msg>
|
||||
<msg id="5539162898278769904" meaning="different meaning"><source>file.ts:7</source>nested</msg>
|
||||
<msg id="3780349238193953556"><source>file.ts:9</source><source>file.ts:10</source><ph name="START_ITALIC_TEXT"><ex><i></ex></ph>with placeholders<ph name="CLOSE_ITALIC_TEXT"><ex></i></ex></ph></msg>
|
||||
<msg id="5525133077318024839"><source>file.ts:13</source>on not translatable node</msg>
|
||||
<msg id="8670732454866344690"><source>file.ts:14</source>on translatable node</msg>
|
||||
<msg id="4593805537723189714"><source>file.ts:19</source><source>file.ts:36</source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex><b></ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>} }</msg>
|
||||
<msg id="1746565782635215"><source>file.ts:21,23</source><source>file.ts:24,26</source>
|
||||
<ph name="ICU"><ex>ICU</ex></ph>
|
||||
</msg>
|
||||
<msg id="5868084092545682515">{VAR_SELECT, select, m {male} f {female} }</msg>
|
||||
<msg id="4851788426695310455"><ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph></msg>
|
||||
<msg id="9013357158046221374">sex = <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph></msg>
|
||||
<msg id="8324617391167353662"><ph name="CUSTOM_NAME"><ex>CUSTOM_NAME</ex></ph></msg>
|
||||
<msg id="7685649297917455806">in a translatable section</msg>
|
||||
<msg id="2387287228265107305">
|
||||
<msg id="5868084092545682515"><source>file.ts:22</source><source>file.ts:25</source>{VAR_SELECT, select, m {male} f {female} }</msg>
|
||||
<msg id="4851788426695310455"><source>file.ts:28</source><ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph></msg>
|
||||
<msg id="9013357158046221374"><source>file.ts:29</source>sex = <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph></msg>
|
||||
<msg id="8324617391167353662"><source>file.ts:30</source><ph name="CUSTOM_NAME"><ex>CUSTOM_NAME</ex></ph></msg>
|
||||
<msg id="7685649297917455806"><source>file.ts:35</source><source>file.ts:53</source>in a translatable section</msg>
|
||||
<msg id="2387287228265107305"><source>file.ts:33,37</source>
|
||||
<ph name="START_HEADING_LEVEL1"><ex><h1></ex></ph>Markers in html comments<ph name="CLOSE_HEADING_LEVEL1"><ex></h1></ex></ph>
|
||||
<ph name="START_TAG_DIV"><ex><div></ex></ph><ph name="CLOSE_TAG_DIV"><ex></div></ex></ph>
|
||||
<ph name="START_TAG_DIV_1"><ex><div></ex></ph><ph name="ICU"><ex>ICU</ex></ph><ph name="CLOSE_TAG_DIV"><ex></div></ex></ph>
|
||||
</msg>
|
||||
<msg id="1491627405349178954">it <ph name="START_BOLD_TEXT"><ex><b></ex></ph>should<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> work</msg>
|
||||
<msg id="i18n16">with an explicit ID</msg>
|
||||
<msg id="i18n17">{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex><b></ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>} }</msg>
|
||||
<msg id="4085484936881858615" desc="desc">{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> results} }</msg>
|
||||
<msg id="4035252431381981115">foo<ph name="START_LINK"><ex><a></ex></ph>bar<ph name="CLOSE_LINK"><ex></a></ex></ph></msg>
|
||||
<msg id="5339604010413301604"><ph name="MAP_NAME"><ex>MAP_NAME</ex></ph></msg>`;
|
||||
<msg id="1491627405349178954"><source>file.ts:39</source>it <ph name="START_BOLD_TEXT"><ex><b></ex></ph>should<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> work</msg>
|
||||
<msg id="i18n16"><source>file.ts:41</source>with an explicit ID</msg>
|
||||
<msg id="i18n17"><source>file.ts:42</source>{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex><b></ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>} }</msg>
|
||||
<msg id="4085484936881858615" desc="desc"><source>file.ts:45,51</source>{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> results} }</msg>
|
||||
<msg id="4035252431381981115"><source>file.ts:53</source>foo<ph name="START_LINK"><ex><a></ex></ph>bar<ph name="CLOSE_LINK"><ex></a></ex></ph></msg>
|
||||
<msg id="5339604010413301604"><source>file.ts:55</source><ph name="MAP_NAME"><ex>MAP_NAME</ex></ph></msg>`;
|
||||
|
|
|
@ -19,6 +19,7 @@ const HTML = `
|
|||
<p i18n>translatable element <b>with placeholders</b> {{ interpolation}}</p>
|
||||
<!-- i18n -->{ count, plural, =0 {<p>test</p>}}<!-- /i18n -->
|
||||
<p i18n="m|d">foo</p>
|
||||
<p i18n="m|d">foo</p>
|
||||
<p i18n="m|d@@i">foo</p>
|
||||
<p i18n="@@bar">foo</p>
|
||||
<p i18n="ph names"><br><img><div></div></p>
|
||||
|
@ -33,43 +34,83 @@ const WRITE_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
|||
<trans-unit id="983775b9a51ce14b036be72d4cfd65d68d64e231" datatype="html">
|
||||
<source>translatable attribute</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">2</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ec1d033f2436133c14ab038286c4f5df4697484a" datatype="html">
|
||||
<source>translatable element <x id="START_BOLD_TEXT" ctype="x-b"/>with placeholders<x id="CLOSE_BOLD_TEXT" ctype="x-b"/> <x id="INTERPOLATION"/></source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">3</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="e2ccf3d131b15f54aa1fcf1314b1ca77c14bfcc2" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {<x id="START_PARAGRAPH" ctype="x-p"/>test<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} }</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">4</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="db3e0a6a5a96481f60aec61d98c3eecddef5ac23" datatype="html">
|
||||
<source>foo</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">5</context>
|
||||
</context-group>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">6</context>
|
||||
</context-group>
|
||||
<note priority="1" from="description">d</note>
|
||||
<note priority="1" from="meaning">m</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="i" datatype="html">
|
||||
<source>foo</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">7</context>
|
||||
</context-group>
|
||||
<note priority="1" from="description">d</note>
|
||||
<note priority="1" from="meaning">m</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="bar" datatype="html">
|
||||
<source>foo</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">8</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="d7fa2d59aaedcaa5309f13028c59af8c85b8c49d" datatype="html">
|
||||
<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>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">9</context>
|
||||
</context-group>
|
||||
<note priority="1" from="description">ph names</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="baz" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">10</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="0e16a673a5a7a135c9f7b957ec2c5c6f6ee6e2c4" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<x id="START_PARAGRAPH" ctype="x-p"/>deeply nested<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} } } }</source>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">11</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
@ -83,10 +124,18 @@ const LOAD_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
|||
<trans-unit id="983775b9a51ce14b036be72d4cfd65d68d64e231" datatype="html">
|
||||
<source>translatable attribute</source>
|
||||
<target>etubirtta elbatalsnart</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">1</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="ec1d033f2436133c14ab038286c4f5df4697484a" datatype="html">
|
||||
<source>translatable element <x id="START_BOLD_TEXT" ctype="b"/>with placeholders<x id="CLOSE_BOLD_TEXT" ctype="b"/> <x id="INTERPOLATION"/></source>
|
||||
<target><x id="INTERPOLATION"/> footnemele elbatalsnart <x id="START_BOLD_TEXT" ctype="x-b"/>sredlohecalp htiw<x id="CLOSE_BOLD_TEXT" ctype="x-b"/></target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">2</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="e2ccf3d131b15f54aa1fcf1314b1ca77c14bfcc2" datatype="html">
|
||||
<source>{VAR_PLURAL, plural, =0 {<x id="START_PARAGRAPH" ctype="x-p"/>test<x id="CLOSE_PARAGRAPH" ctype="x-p"/>} }</source>
|
||||
|
@ -95,27 +144,47 @@ const LOAD_XLIFF = `<?xml version="1.0" encoding="UTF-8" ?>
|
|||
<trans-unit id="db3e0a6a5a96481f60aec61d98c3eecddef5ac23" datatype="html">
|
||||
<source>foo</source>
|
||||
<target>oof</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">3</context>
|
||||
</context-group>
|
||||
<note priority="1" from="description">d</note>
|
||||
<note priority="1" from="meaning">m</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="i" datatype="html">
|
||||
<source>foo</source>
|
||||
<target>toto</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">4</context>
|
||||
</context-group>
|
||||
<note priority="1" from="description">d</note>
|
||||
<note priority="1" from="meaning">m</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="bar" datatype="html">
|
||||
<source>foo</source>
|
||||
<target>tata</target>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">5</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="d7fa2d59aaedcaa5309f13028c59af8c85b8c49d" datatype="html">
|
||||
<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>
|
||||
<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>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">6</context>
|
||||
</context-group>
|
||||
<note priority="1" from="description">ph names</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="empty target" datatype="html">
|
||||
<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>
|
||||
<target/>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">file.ts</context>
|
||||
<context context-type="linenumber">6</context>
|
||||
</context-group>
|
||||
<note priority="1" from="description">ph names</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="baz" datatype="html">
|
||||
|
@ -136,7 +205,7 @@ export function main(): void {
|
|||
|
||||
function toXliff(html: string, locale: string | null = null): string {
|
||||
const catalog = new MessageBundle(new HtmlParser, [], {}, locale);
|
||||
catalog.updateFromTemplate(html, '', DEFAULT_INTERPOLATION_CONFIG);
|
||||
catalog.updateFromTemplate(html, 'file.ts', DEFAULT_INTERPOLATION_CONFIG);
|
||||
return catalog.write(serializer);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,9 @@ export function main(): void {
|
|||
<p i18n="m|d@@i">foo</p>
|
||||
<p i18n="@@bar">foo</p>
|
||||
<p i18n="@@baz">{ count, plural, =0 { { sex, select, other {<p>deeply nested</p>}} }}</p>
|
||||
<p i18n>{ count, plural, =0 { { sex, select, other {<p>deeply nested</p>}} }}</p>`;
|
||||
<p i18n>{ count, plural, =0 { { sex, select, other {<p>deeply nested</p>}} }}</p>
|
||||
<p i18n>multi
|
||||
lines</p>`;
|
||||
|
||||
const XMB = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE messagebundle [
|
||||
|
@ -46,20 +48,22 @@ export function main(): void {
|
|||
<!ELEMENT ex (#PCDATA)>
|
||||
]>
|
||||
<messagebundle>
|
||||
<msg id="7056919470098446707">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"><ex>INTERPOLATION</ex></ph></msg>
|
||||
<msg id="2981514368455622387">{VAR_PLURAL, plural, =0 {<ph name="START_PARAGRAPH"><ex><p></ex></ph>test<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>} }</msg>
|
||||
<msg id="7999024498831672133" desc="d" meaning="m">foo</msg>
|
||||
<msg id="i" desc="d" meaning="m">foo</msg>
|
||||
<msg id="bar">foo</msg>
|
||||
<msg id="baz">{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<ph name="START_PARAGRAPH"><ex><p></ex></ph>deeply nested<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>} } } }</msg>
|
||||
<msg id="2015957479576096115">{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<ph name="START_PARAGRAPH"><ex><p></ex></ph>deeply nested<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>} } } }</msg>
|
||||
<msg id="7056919470098446707"><source>file.ts:3</source>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"><ex>INTERPOLATION</ex></ph></msg>
|
||||
<msg id="2981514368455622387"><source>file.ts:4</source>{VAR_PLURAL, plural, =0 {<ph name="START_PARAGRAPH"><ex><p></ex></ph>test<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>} }</msg>
|
||||
<msg id="7999024498831672133" desc="d" meaning="m"><source>file.ts:5</source>foo</msg>
|
||||
<msg id="i" desc="d" meaning="m"><source>file.ts:6</source>foo</msg>
|
||||
<msg id="bar"><source>file.ts:7</source>foo</msg>
|
||||
<msg id="baz"><source>file.ts:8</source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<ph name="START_PARAGRAPH"><ex><p></ex></ph>deeply nested<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>} } } }</msg>
|
||||
<msg id="2015957479576096115"><source>file.ts:9</source>{VAR_PLURAL, plural, =0 {{VAR_SELECT, select, other {<ph name="START_PARAGRAPH"><ex><p></ex></ph>deeply nested<ph name="CLOSE_PARAGRAPH"><ex></p></ex></ph>} } } }</msg>
|
||||
<msg id="2340165783990709777"><source>file.ts:10,11</source>multi
|
||||
lines</msg>
|
||||
</messagebundle>
|
||||
`;
|
||||
|
||||
it('should write a valid xmb file', () => {
|
||||
expect(toXmb(HTML)).toEqual(XMB);
|
||||
expect(toXmb(HTML, 'file.ts')).toEqual(XMB);
|
||||
// the locale is not specified in the xmb file
|
||||
expect(toXmb(HTML, 'fr')).toEqual(XMB);
|
||||
expect(toXmb(HTML, 'file.ts', 'fr')).toEqual(XMB);
|
||||
});
|
||||
|
||||
it('should throw when trying to load an xmb file', () => {
|
||||
|
@ -71,11 +75,11 @@ export function main(): void {
|
|||
});
|
||||
}
|
||||
|
||||
function toXmb(html: string, locale: string | null = null): string {
|
||||
function toXmb(html: string, url: string, locale: string | null = null): string {
|
||||
const catalog = new MessageBundle(new HtmlParser, [], {}, locale);
|
||||
const serializer = new Xmb();
|
||||
|
||||
catalog.updateFromTemplate(html, '', DEFAULT_INTERPOLATION_CONFIG);
|
||||
catalog.updateFromTemplate(html, url, DEFAULT_INTERPOLATION_CONFIG);
|
||||
|
||||
return catalog.write(serializer);
|
||||
}
|
||||
|
|
|
@ -17,8 +17,9 @@ import {_extractMessages} from './i18n_parser_spec';
|
|||
export function main(): void {
|
||||
describe('TranslationBundle', () => {
|
||||
const file = new ParseSourceFile('content', 'url');
|
||||
const location = new ParseLocation(file, 0, 0, 0);
|
||||
const span = new ParseSourceSpan(location, null !);
|
||||
const startLocation = new ParseLocation(file, 0, 0, 0);
|
||||
const endLocation = new ParseLocation(file, 0, 0, 7);
|
||||
const span = new ParseSourceSpan(startLocation, endLocation);
|
||||
const srcNode = new i18n.Text('src', span);
|
||||
|
||||
it('should translate a plain message', () => {
|
||||
|
|
Loading…
Reference in New Issue