diff --git a/modules/@angular/compiler-cli/integrationtest/src/entry_components.ts b/modules/@angular/compiler-cli/integrationtest/src/entry_components.ts index f969dd25d2941f..463ba825545460 100644 --- a/modules/@angular/compiler-cli/integrationtest/src/entry_components.ts +++ b/modules/@angular/compiler-cli/integrationtest/src/entry_components.ts @@ -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) {} } diff --git a/modules/@angular/compiler-cli/integrationtest/test/i18n_spec.ts b/modules/@angular/compiler-cli/integrationtest/test/i18n_spec.ts index 019b354ce9c03a..92fdc3199e6281 100644 --- a/modules/@angular/compiler-cli/integrationtest/test/i18n_spec.ts +++ b/modules/@angular/compiler-cli/integrationtest/test/i18n_spec.ts @@ -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> diff --git a/modules/@angular/compiler-cli/integrationtest/test/ng_module_spec.ts b/modules/@angular/compiler-cli/integrationtest/test/ng_module_spec.ts index 812fc6b9ad49ca..c66b9657ca5073 100644 --- a/modules/@angular/compiler-cli/integrationtest/test/ng_module_spec.ts +++ b/modules/@angular/compiler-cli/integrationtest/test/ng_module_spec.ts @@ -49,7 +49,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 diff --git a/modules/@angular/compiler-cli/integrationtest/third_party_src/other_comp.ts b/modules/@angular/compiler-cli/integrationtest/third_party_src/other_comp.ts index 553e08ba45a3fd..130baabd8f564e 100644 --- a/modules/@angular/compiler-cli/integrationtest/third_party_src/other_comp.ts +++ b/modules/@angular/compiler-cli/integrationtest/third_party_src/other_comp.ts @@ -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 { -} \ No newline at end of file +} diff --git a/modules/@angular/compiler-cli/src/extractor.ts b/modules/@angular/compiler-cli/src/extractor.ts index 1ff09b6416c435..aa3d333c02177f 100644 --- a/modules/@angular/compiler-cli/src/extractor.ts +++ b/modules/@angular/compiler-cli/src/extractor.ts @@ -59,8 +59,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 { diff --git a/modules/@angular/compiler/src/i18n/i18n_ast.ts b/modules/@angular/compiler/src/i18n/i18n_ast.ts index 1e94ea416f7d55..c2d18649341f68 100644 --- a/modules/@angular/compiler/src/i18n/i18n_ast.ts +++ b/modules/@angular/compiler/src/i18n/i18n_ast.ts @@ -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{}; -} \ No newline at end of file +} diff --git a/modules/@angular/compiler/src/i18n/message_bundle.ts b/modules/@angular/compiler/src/i18n/message_bundle.ts index 7b42b4fa213a4f..ea632e91385139 100644 --- a/modules/@angular/compiler/src/i18n/message_bundle.ts +++ b/modules/@angular/compiler/src/i18n/message_bundle.ts @@ -47,7 +47,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(); @@ -56,6 +56,8 @@ export class MessageBundle { const id = serializer.digest(message); if (!messages.hasOwnProperty(id)) { messages[id] = message; + } else { + messages[id].sources.push(...message.sources); } }); @@ -64,7 +66,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); diff --git a/modules/@angular/compiler/src/i18n/serializers/xliff.ts b/modules/@angular/compiler/src/i18n/serializers/xliff.ts index dd9549409c069b..ce4932602734ba 100644 --- a/modules/@angular/compiler/src/i18n/serializers/xliff.ts +++ b/modules/@angular/compiler/src/i18n/serializers/xliff.ts @@ -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( diff --git a/modules/@angular/compiler/src/i18n/serializers/xmb.ts b/modules/@angular/compiler/src/i18n/serializers/xmb.ts index 9110b27221dc94..93d08757ad9df8 100644 --- a/modules/@angular/compiler/src/i18n/serializers/xmb.ts +++ b/modules/@angular/compiler/src/i18n/serializers/xmb.ts @@ -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,16 @@ 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()); diff --git a/modules/@angular/compiler/test/i18n/digest_spec.ts b/modules/@angular/compiler/test/i18n/digest_spec.ts index 7297f6cc76130d..532430d64f9846 100644 --- a/modules/@angular/compiler/test/i18n/digest_spec.ts +++ b/modules/@angular/compiler/test/i18n/digest_spec.ts @@ -19,6 +19,7 @@ export function main(): void { placeholderToMessage: {}, meaning: '', description: '', + sources: [], })).toEqual('i'); }); }); diff --git a/modules/@angular/compiler/test/i18n/integration_spec.ts b/modules/@angular/compiler/test/i18n/integration_spec.ts index b7a36dd3714831..43e7a32de4474e 100644 --- a/modules/@angular/compiler/test/i18n/integration_spec.ts +++ b/modules/@angular/compiler/test/i18n/integration_spec.ts @@ -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); }); @@ -168,32 +168,32 @@ 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>`; const HTML = ` <div> diff --git a/modules/@angular/compiler/test/i18n/serializers/xliff_spec.ts b/modules/@angular/compiler/test/i18n/serializers/xliff_spec.ts index e8a81b66117ac3..334ebaf6368703 100644 --- a/modules/@angular/compiler/test/i18n/serializers/xliff_spec.ts +++ b/modules/@angular/compiler/test/i18n/serializers/xliff_spec.ts @@ -18,6 +18,7 @@ const HTML = ` <p i18n-title title="translatable attribute">not translatable</p> <p i18n>translatable element <b>with placeholders</b> {{ interpolation}}</p> <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> @@ -30,30 +31,58 @@ 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="db3e0a6a5a96481f60aec61d98c3eecddef5ac23" datatype="html"> <source>foo</source> <target/> + <context-group purpose="location"> + <context context-type="sourcefile">file.ts</context> + <context context-type="linenumber">4</context> + </context-group> + <context-group purpose="location"> + <context context-type="sourcefile">file.ts</context> + <context context-type="linenumber">5</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">6</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">7</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">8</context> + </context-group> <note priority="1" from="description">ph names</note> </trans-unit> </body> @@ -68,35 +97,63 @@ 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="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> </body> @@ -109,7 +166,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); } diff --git a/modules/@angular/compiler/test/i18n/serializers/xmb_spec.ts b/modules/@angular/compiler/test/i18n/serializers/xmb_spec.ts index cb8c535fd52c6d..58084111b4adb9 100644 --- a/modules/@angular/compiler/test/i18n/serializers/xmb_spec.ts +++ b/modules/@angular/compiler/test/i18n/serializers/xmb_spec.ts @@ -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); } diff --git a/modules/@angular/compiler/test/i18n/translation_bundle_spec.ts b/modules/@angular/compiler/test/i18n/translation_bundle_spec.ts index 2d6dccd973c2ce..81a1648d0a08e7 100644 --- a/modules/@angular/compiler/test/i18n/translation_bundle_spec.ts +++ b/modules/@angular/compiler/test/i18n/translation_bundle_spec.ts @@ -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', () => {