diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts index 9ccb7a2cbe..a3ed0a3a28 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts @@ -182,15 +182,17 @@ const verify = (input: string, output: string, extra: any = {}): void => { } }; -// Describes a simple key-value object. -type KVList = { - [key: string]: string -}; +// Describes message metadata object. +interface Meta { + desc?: string; + meaning?: string; + id?: string; +} // Describes placeholder type used in tests. Note: the type is an array (not an object), since it's // important to preserve the order of placeholders (so that we can compare it with generated // output). -type Placeholder = string[]; +type Placeholder = [string, string]; // Unique message id index that is needed to avoid different i18n vars with the same name to appear // in the i18n block while generating an output string (used to verify compiler-generated code). @@ -202,7 +204,7 @@ let msgIndex = 0; const quotedValue = (value: string) => value.startsWith('$') ? value : `"${value}"`; // Generates a string that represents expected Closure metadata output. -const i18nMsgClosureMeta = (meta?: KVList): string => { +const i18nMsgClosureMeta = (meta?: Meta): string => { if (!meta || !(meta.desc || meta.meaning)) return ''; return ` /** @@ -220,7 +222,7 @@ const i18nPlaceholdersToString = (placeholders: Placeholder[]): string => { }; // Generates a string that represents expected $localize metadata output. -const i18nMsgLocalizeMeta = (meta?: KVList): string => { +const i18nMsgLocalizeMeta = (meta?: Meta): string => { if (!meta) return ''; let localizeMeta = ''; if (meta.meaning) localizeMeta += `${meta.meaning}|`; @@ -244,7 +246,7 @@ const i18nMsgInsertLocalizePlaceholders = }; // Generates a string that represents expected i18n block content for simple message. -const i18nMsg = (message: string, placeholders: Placeholder[] = [], meta?: KVList) => { +const i18nMsg = (message: string, placeholders: Placeholder[] = [], meta?: Meta) => { const varName = `$I18N_${msgIndex++}$`; const closurePlaceholders = i18nPlaceholdersToString(placeholders); const locMessageWithPlaceholders = i18nMsgInsertLocalizePlaceholders(message, placeholders); @@ -263,7 +265,7 @@ const i18nMsg = (message: string, placeholders: Placeholder[] = [], meta?: KVLis // Generates a string that represents expected i18n block content for a message that requires // post-processing (thus includes `ɵɵi18nPostprocess` in generated code). const i18nMsgWithPostprocess = - (message: string, placeholders: Placeholder[] = [], meta?: KVList, + (message: string, placeholders: Placeholder[] = [], meta?: Meta, postprocessPlaceholders?: Placeholder[]) => { const varName = `$I18N_${msgIndex}$`; const ppPaceholders = @@ -275,10 +277,9 @@ const i18nMsgWithPostprocess = }; // Generates a string that represents expected i18n block content for an ICU. -const i18nIcuMsg = - (message: string, placeholders: string[][] = []) => { - return i18nMsgWithPostprocess(message, [], undefined, placeholders); - } +const i18nIcuMsg = (message: string, placeholders: Placeholder[] = []) => { + return i18nMsgWithPostprocess(message, [], undefined, placeholders); +}; describe('i18n support in the template compiler', () => { describe('element attributes', () => { @@ -303,7 +304,7 @@ describe('i18n support in the template compiler', () => { // Keeping this block as a raw string, since it checks escaping of special chars. const i18n_6 = String.raw` - var $I18N_23$; + var $i18n_23$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { /** * @desc [BACKUP_$` + @@ -311,10 +312,10 @@ describe('i18n support in the template compiler', () => { '`' + String.raw`desc */ const $MSG_EXTERNAL_idG$$APP_SPEC_TS_24$ = goog.getMsg("Title G"); - $I18N_23$ = $MSG_EXTERNAL_idG$$APP_SPEC_TS_24$; + $i18n_23$ = $MSG_EXTERNAL_idG$$APP_SPEC_TS_24$; } else { - $I18N_23$ = $localize \`:[BACKUP_$\{MESSAGE}_ID\:idH]\\\`desc@@idG:Title G\`; + $i18n_23$ = $localize \`:[BACKUP_$\{MESSAGE}_ID\:idH]\\\`desc@@idG:Title G\`; } `; @@ -334,53 +335,58 @@ describe('i18n support in the template compiler', () => { `; const output = String.raw` - ${i18n_0} - ${i18n_1} - const $_c5$ = ["title", $i18n_1$]; - ${i18n_2} - const $_c9$ = ["title", $i18n_2$]; - ${i18n_3} - const $_c13$ = ["title", $i18n_3$]; - ${i18n_4} - const $_c17$ = ["title", $i18n_4$]; - ${i18n_5} - const $_c21$ = ["title", $i18n_5$]; - ${i18n_6} - const $_c25$ = ["title", $i18n_6$]; - ${i18n_7} - … - consts: [[${AttributeMarker.I18n}, "title"]], + consts: function () { + ${i18n_0} + ${i18n_1} + ${i18n_2} + ${i18n_3} + ${i18n_4} + ${i18n_5} + ${i18n_6} + ${i18n_7} + return [ + $i18n_0$, + [${AttributeMarker.I18n}, "title"], + ["title", $i18n_1$], + ["title", $i18n_2$], + ["title", $i18n_3$], + ["title", $i18n_4$], + ["title", $i18n_5$], + ["title", $i18n_6$], + $i18n_7$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18n(1, $i18n_0$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵelementEnd(); - $r3$.ɵɵelementStart(2, "div", 0); - $r3$.ɵɵi18nAttributes(3, $_c5$); + $r3$.ɵɵelementStart(2, "div", 1); + $r3$.ɵɵi18nAttributes(3, 2); $r3$.ɵɵtext(4, "Content B"); $r3$.ɵɵelementEnd(); - $r3$.ɵɵelementStart(5, "div", 0); - $r3$.ɵɵi18nAttributes(6, $_c9$); + $r3$.ɵɵelementStart(5, "div", 1); + $r3$.ɵɵi18nAttributes(6, 3); $r3$.ɵɵtext(7, "Content C"); $r3$.ɵɵelementEnd(); - $r3$.ɵɵelementStart(8, "div", 0); - $r3$.ɵɵi18nAttributes(9, $_c13$); + $r3$.ɵɵelementStart(8, "div", 1); + $r3$.ɵɵi18nAttributes(9, 4); $r3$.ɵɵtext(10, "Content D"); $r3$.ɵɵelementEnd(); - $r3$.ɵɵelementStart(11, "div", 0); - $r3$.ɵɵi18nAttributes(12, $_c17$); + $r3$.ɵɵelementStart(11, "div", 1); + $r3$.ɵɵi18nAttributes(12, 5); $r3$.ɵɵtext(13, "Content E"); $r3$.ɵɵelementEnd(); - $r3$.ɵɵelementStart(14, "div", 0); - $r3$.ɵɵi18nAttributes(15, $_c21$); + $r3$.ɵɵelementStart(14, "div", 1); + $r3$.ɵɵi18nAttributes(15, 6); $r3$.ɵɵtext(16, "Content F"); $r3$.ɵɵelementEnd(); - $r3$.ɵɵelementStart(17, "div", 0); - $r3$.ɵɵi18nAttributes(18, $_c25$); + $r3$.ɵɵelementStart(17, "div", 1); + $r3$.ɵɵi18nAttributes(18, 7); $r3$.ɵɵtext(19, "Content G"); $r3$.ɵɵelementEnd(); $r3$.ɵɵelementStart(20, "div"); - $r3$.ɵɵi18n(21, $i18n_7$); + $r3$.ɵɵi18n(21, 8); $r3$.ɵɵelementEnd(); } } @@ -396,14 +402,17 @@ describe('i18n support in the template compiler', () => { const i18n_0 = i18nMsg('Hello'); const output = String.raw` - ${i18n_0} - const $_c2$ = ["title", $i18n_0$]; - … - consts: [[${AttributeMarker.I18n}, "title"]], + consts: function () { + ${i18n_0} + return [ + [${AttributeMarker.I18n}, "title"], + ["title", $i18n_0$] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_ng_template_0_Template, 0, 0, "ng-template", 0); - $r3$.ɵɵi18nAttributes(1, $_c2$); + $r3$.ɵɵi18nAttributes(1, 1); } } `; @@ -417,9 +426,8 @@ describe('i18n support in the template compiler', () => { `; const i18n_0 = i18nMsg('Hello'); + const output = String.raw` - ${i18n_0} - const $_c2$ = ["title", $i18n_0$]; function MyComponent_0_ng_template_0_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtext(0, "Test"); @@ -428,11 +436,18 @@ describe('i18n support in the template compiler', () => { function MyComponent_0_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_0_ng_template_0_Template, 1, 0, "ng-template", 1); - $r3$.ɵɵi18nAttributes(1, $_c2$); + $r3$.ɵɵi18nAttributes(1, 2); } } … - consts: [[${AttributeMarker.Template}, "ngIf"], [${AttributeMarker.I18n}, "title"]], + consts: function() { + ${i18n_0} + return [ + [${AttributeMarker.Template}, "ngIf"], + [${AttributeMarker.I18n}, "title"], + ["title", $i18n_0$] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_0_Template, 2, 0, undefined, 0); @@ -454,14 +469,17 @@ describe('i18n support in the template compiler', () => { const i18n_0 = i18nMsg('Hello {$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]]); const output = String.raw` - ${i18n_0} - const $_c2$ = ["title", $i18n_0$]; - … - consts: [[${AttributeMarker.Bindings}, "title"]], + consts: function() { + ${i18n_0} + return [ + [${AttributeMarker.Bindings}, "title"], + ["title", $i18n_0$] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_ng_template_0_Template, 0, 0, "ng-template", 0); - $r3$.ɵɵi18nAttributes(1, $_c2$); + $r3$.ɵɵi18nAttributes(1, 1); } if (rf & 2) { $r3$.ɵɵi18nExp(ctx.name); @@ -481,13 +499,10 @@ describe('i18n support in the template compiler', () => { const i18n_0 = i18nMsg('Hello {$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]]); const output = String.raw` - ${i18n_0} - const $_c2$ = ["title", $i18n_0$]; - … function MyComponent_0_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_0_ng_template_0_Template, 0, 0, "ng-template", 1); - $r3$.ɵɵi18nAttributes(1, $_c2$); + $r3$.ɵɵi18nAttributes(1, 2); } if (rf & 2) { const $ctx_r2$ = $r3$.ɵɵnextContext(); @@ -496,7 +511,14 @@ describe('i18n support in the template compiler', () => { } } … - consts: [[${AttributeMarker.Template}, "ngIf"], [${AttributeMarker.Bindings}, "title"]], + consts: function() { + ${i18n_0} + return [ + [${AttributeMarker.Template}, "ngIf"], + [${AttributeMarker.Bindings}, "title"], + ["title", $i18n_0$] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_0_Template, 2, 1, undefined, 0); @@ -536,7 +558,6 @@ describe('i18n support in the template compiler', () => { `; const output = ` - … consts: [[3, "title"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) { @@ -558,15 +579,19 @@ describe('i18n support in the template compiler', () => { `; const i18n_0 = i18nMsg('introduction', [], {meaning: 'm', desc: 'd'}); + const output = String.raw` - ${i18n_0} - const $_c1$ = ["title", $i18n_0$]; - … - consts: [["id", "static", ${AttributeMarker.I18n}, "title"]], + consts: function() { + ${i18n_0} + return [ + ["id", "static", ${AttributeMarker.I18n}, "title"], + ["title", $i18n_0$] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div", 0); - $r3$.ɵɵi18nAttributes(1, $_c1$); + $r3$.ɵɵi18nAttributes(1, 1); $r3$.ɵɵelementEnd(); } } @@ -606,35 +631,30 @@ describe('i18n support in the template compiler', () => { const i18n_4 = i18nMsg('{$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]]); const output = String.raw` - ${i18n_0} - ${i18n_1} - ${i18n_2} - const $_c1$ = [ - "aria-roledescription", $i18n_0$, - "title", $i18n_1$, - "aria-label", $i18n_2$ - ]; - ${i18n_3} - ${i18n_4} - const $_c3$ = [ - "title", $i18n_3$, - "aria-roledescription", $i18n_4$ - ]; - … decls: 5, vars: 8, - consts: [["id", "dynamic-1", ${ - AttributeMarker - .I18n}, "aria-roledescription", "title", "aria-label"], ["id", "dynamic-2", ${ - AttributeMarker.I18n}, "title", "aria-roledescription"]], + consts: function() { + ${i18n_0} + ${i18n_1} + ${i18n_2} + ${i18n_3} + ${i18n_4} + return [ + ["id", "dynamic-1", ${AttributeMarker.I18n}, "aria-roledescription", + "title", "aria-label"], + ["aria-roledescription", $i18n_0$, "title", $i18n_1$, "aria-label", $i18n_2$], + ["id", "dynamic-2", ${AttributeMarker.I18n}, "title", "aria-roledescription"], + ["title", $i18n_3$, "aria-roledescription", $i18n_4$] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div", 0); $r3$.ɵɵpipe(1, "uppercase"); - $r3$.ɵɵi18nAttributes(2, $_c1$); + $r3$.ɵɵi18nAttributes(2, 1); $r3$.ɵɵelementEnd(); - $r3$.ɵɵelementStart(3, "div", 1); - $r3$.ɵɵi18nAttributes(4, $_c3$); + $r3$.ɵɵelementStart(3, "div", 2); + $r3$.ɵɵi18nAttributes(4, 3); $r3$.ɵɵelementEnd(); } if (rf & 2) { @@ -658,16 +678,20 @@ describe('i18n support in the template compiler', () => { const i18n_0 = i18nMsg( 'intro {$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]], {meaning: 'm', desc: 'd'}); + const output = String.raw` - ${i18n_0} - const $_c3$ = ["title", $i18n_0$]; - … - consts: [[${AttributeMarker.I18n}, "title"]], + consts: function() { + ${i18n_0} + return [ + [${AttributeMarker.I18n}, "title"], + ["title", $i18n_0$] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div", 0); $r3$.ɵɵpipe(1, "uppercase"); - $r3$.ɵɵi18nAttributes(2, $_c3$); + $r3$.ɵɵi18nAttributes(2, 1); $r3$.ɵɵelementEnd(); } if (rf & 2) { @@ -691,14 +715,12 @@ describe('i18n support in the template compiler', () => { {meaning: 'm', desc: 'd'}); const output = String.raw` - ${i18n_0} - const $_c2$ = ["title", $i18n_0$]; function MyComponent_div_0_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); $r3$.ɵɵelementStart(1, "div", 1); $r3$.ɵɵpipe(2, "uppercase"); - $r3$.ɵɵi18nAttributes(3, $_c2$); + $r3$.ɵɵi18nAttributes(3, 2); $r3$.ɵɵelementEnd(); $r3$.ɵɵelementEnd(); } @@ -712,8 +734,14 @@ describe('i18n support in the template compiler', () => { … decls: 1, vars: 1, - consts: [[${AttributeMarker.Template}, "ngFor", "ngForOf"], [${ - AttributeMarker.I18n}, "title"]], + consts: function() { + ${i18n_0} + return [ + [${AttributeMarker.Template}, "ngFor", "ngForOf"], + [${AttributeMarker.I18n}, "title"], + ["title", $i18n_0$] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_div_0_Template, 4, 3, "div", 0); @@ -736,16 +764,19 @@ describe('i18n support in the template compiler', () => { i18nMsg('{$interpolation} title', [['interpolation', String.raw`\uFFFD0\uFFFD`]]); const output = String.raw` - ${i18n_0} - const $_c3$ = ["title", $i18n_0$]; - … decls: 2, vars: 1, - consts: [[${AttributeMarker.I18n}, "title"]], + consts: function() { + ${i18n_0} + return [ + [${AttributeMarker.I18n}, "title"], + ["title", $i18n_0$] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div", 0); - $r3$.ɵɵi18nAttributes(1, $_c3$); + $r3$.ɵɵi18nAttributes(1, 1); $r3$.ɵɵelementEnd(); } if (rf & 2) { @@ -790,35 +821,30 @@ describe('i18n support in the template compiler', () => { const i18n_4 = i18nMsg('{$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]]); const output = String.raw` - ${i18n_0} - ${i18n_1} - ${i18n_2} - const $_c1$ = [ - "aria-roledescription", $i18n_0$, - "title", $i18n_1$, - "aria-label", $i18n_2$ - ]; - ${i18n_3} - ${i18n_4} - const $_c3$ = [ - "title", $i18n_3$, - "aria-roledescription", $i18n_4$ - ]; - … decls: 5, vars: 8, - consts: [[ - "id", "dynamic-1", - ${AttributeMarker.I18n}, "aria-roledescription", "title", "aria-label" - ], ["id", "dynamic-2", ${AttributeMarker.I18n}, "title", "aria-roledescription"]], + consts: function() { + ${i18n_0} + ${i18n_1} + ${i18n_2} + ${i18n_3} + ${i18n_4} + return [ + ["id", "dynamic-1", ${AttributeMarker.I18n}, "aria-roledescription", + "title", "aria-label"], + ["aria-roledescription", $i18n_0$, "title", $i18n_1$, "aria-label", $i18n_2$], + ["id", "dynamic-2", ${AttributeMarker.I18n}, "title", "aria-roledescription"], + ["title", $i18n_3$, "aria-roledescription", $i18n_4$] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div", 0); $r3$.ɵɵpipe(1, "uppercase"); - $r3$.ɵɵi18nAttributes(2, $_c1$); + $r3$.ɵɵi18nAttributes(2, 1); $r3$.ɵɵelementEnd(); - $r3$.ɵɵelementStart(3, "div", 1); - $r3$.ɵɵi18nAttributes(4, $_c3$); + $r3$.ɵɵelementStart(3, "div", 2); + $r3$.ɵɵi18nAttributes(4, 3); $r3$.ɵɵelementEnd(); } if (rf & 2) { @@ -846,14 +872,12 @@ describe('i18n support in the template compiler', () => { {meaning: 'm', desc: 'd'}); const output = String.raw` - ${i18n_0} - const $_c4$ = ["title", $i18n_0$]; function MyComponent_div_0_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); $r3$.ɵɵelementStart(1, "div", 1); $r3$.ɵɵpipe(2, "uppercase"); - $r3$.ɵɵi18nAttributes(3, $_c4$); + $r3$.ɵɵi18nAttributes(3, 2); $r3$.ɵɵelementEnd(); $r3$.ɵɵelementEnd(); } @@ -867,8 +891,14 @@ describe('i18n support in the template compiler', () => { … decls: 1, vars: 1, - consts: [[${AttributeMarker.Template}, "ngFor", "ngForOf"], [${ - AttributeMarker.I18n}, "title"]], + consts: function() { + ${i18n_0} + return [ + [${AttributeMarker.Template}, "ngFor", "ngForOf"], + [${AttributeMarker.I18n}, "title"], + ["title", $i18n_0$] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_div_0_Template, 4, 3, "div", 0); @@ -891,16 +921,20 @@ describe('i18n support in the template compiler', () => { const i18n_1 = i18nMsg('Some content'); const output = String.raw` - ${i18n_0} - const $_c1$ = ["title", $i18n_0$]; - ${i18n_1} - … - consts: [[${AttributeMarker.I18n}, "title"]], + consts: function() { + ${i18n_0} + ${i18n_1} + return [ + [${AttributeMarker.I18n}, "title"], + ["title", $i18n_0$], + $i18n_1$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div", 0); - $r3$.ɵɵi18nAttributes(1, $_c1$); - $r3$.ɵɵi18n(2, $i18n_1$); + $r3$.ɵɵi18nAttributes(1, 1); + $r3$.ɵɵi18n(2, 2); $r3$.ɵɵelementEnd(); } } @@ -925,7 +959,7 @@ describe('i18n support in the template compiler', () => { else { $I18N_0$ = $localize \`:@@ID.WITH.INVALID.CHARS:Element title\`; } - const $_c1$ = ["title", $I18N_0$]; + … var $I18N_2$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_ID_WITH_INVALID_CHARS_2$$APP_SPEC_TS_4$ = goog.getMsg(" Some content "); @@ -934,7 +968,6 @@ describe('i18n support in the template compiler', () => { else { $I18N_2$ = $localize \`:@@ID.WITH.INVALID.CHARS.2: Some content \`; } - … `; const exceptions = { @@ -1030,26 +1063,32 @@ describe('i18n support in the template compiler', () => { const i18n_2 = i18nMsg('My i18n block #3'); const output = String.raw` - ${i18n_0} - ${i18n_1} - ${i18n_2} - … + consts: function() { + ${i18n_0} + ${i18n_1} + ${i18n_2} + return [ + $i18n_0$, + $i18n_1$, + $i18n_2$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18n(1, $i18n_0$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵelementEnd(); $r3$.ɵɵelementStart(2, "div"); $r3$.ɵɵtext(3, "My non-i18n block #1"); $r3$.ɵɵelementEnd(); $r3$.ɵɵelementStart(4, "div"); - $r3$.ɵɵi18n(5, $i18n_1$); + $r3$.ɵɵi18n(5, 1); $r3$.ɵɵelementEnd(); $r3$.ɵɵelementStart(6, "div"); $r3$.ɵɵtext(7, "My non-i18n block #2"); $r3$.ɵɵelementEnd(); $r3$.ɵɵelementStart(8, "div"); - $r3$.ɵɵi18n(9, $i18n_2$); + $r3$.ɵɵi18n(9, 2); $r3$.ɵɵelementEnd(); } } @@ -1068,7 +1107,7 @@ describe('i18n support in the template compiler', () => { // Keeping raw content (avoiding `i18nMsg`) to illustrate how named interpolations are // generated. - const output = String.raw` + const i18n_0 = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_7597881511811528589$$APP_SPEC_TS_0$ = goog.getMsg(" Named interpolation: {$phA} Named interpolation with spaces: {$phB} ", { @@ -1082,13 +1121,21 @@ describe('i18n support in the template compiler', () => { String.raw`{"\uFFFD0\uFFFD"}:PH_A: Named interpolation with spaces: $` + String.raw`{"\uFFFD1\uFFFD"}:PH_B: \`; } - … + `; + + const output = String.raw` decls: 2, vars: 2, + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18n(1, $I18N_0$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵelementEnd(); } if (rf & 2) { @@ -1110,12 +1157,16 @@ describe('i18n support in the template compiler', () => { const i18n_0 = i18nMsg('{$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]]); const output = String.raw` - ${i18n_0} - … + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18n(1, $i18n_0$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵelementEnd(); } if (rf & 2) { @@ -1144,12 +1195,16 @@ describe('i18n support in the template compiler', () => { ]); const output = String.raw` - ${i18n_0} - … + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18n(1, $i18n_0$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵpipe(2, "async"); $r3$.ɵɵelementEnd(); } @@ -1181,23 +1236,29 @@ describe('i18n support in the template compiler', () => { 'My i18n block #{$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]]); const output = String.raw` - ${i18n_0} - ${i18n_1} - ${i18n_2} - … decls: 7, vars: 5, + consts: function() { + ${i18n_0} + ${i18n_1} + ${i18n_2} + return [ + $i18n_0$, + $i18n_1$, + $i18n_2$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18n(1, $i18n_0$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵelementEnd(); $r3$.ɵɵelementStart(2, "div"); - $r3$.ɵɵi18n(3, $i18n_1$); + $r3$.ɵɵi18n(3, 1); $r3$.ɵɵpipe(4, "uppercase"); $r3$.ɵɵelementEnd(); $r3$.ɵɵelementStart(5, "div"); - $r3$.ɵɵi18n(6, $i18n_2$); + $r3$.ɵɵi18n(6, 2); $r3$.ɵɵelementEnd(); } if (rf & 2) { @@ -1254,20 +1315,25 @@ describe('i18n support in the template compiler', () => { ]); const output = String.raw` - ${i18n_0} - ${i18n_1} - … decls: 9, vars: 5, + consts: function() { + ${i18n_0} + ${i18n_1} + return [ + $i18n_0$, + $i18n_1$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18nStart(1, $i18n_0$); + $r3$.ɵɵi18nStart(1, 0); $r3$.ɵɵelement(2, "span"); $r3$.ɵɵi18nEnd(); $r3$.ɵɵelementEnd(); $r3$.ɵɵelementStart(3, "div"); - $r3$.ɵɵi18nStart(4, $i18n_1$); + $r3$.ɵɵi18nStart(4, 1); $r3$.ɵɵpipe(5, "uppercase"); $r3$.ɵɵelementStart(6, "div"); $r3$.ɵɵelementStart(7, "div"); @@ -1328,30 +1394,35 @@ describe('i18n support in the template compiler', () => { ]); const output = String.raw` - ${i18n_0} - const $_c4$ = ["title", $i18n_0$]; - ${i18n_1} - ${i18n_2} - const $_c9$ = ["title", $i18n_2$]; - ${i18n_3} - … decls: 9, vars: 7, - consts: [[${AttributeMarker.I18n}, "title"]], + consts: function() { + ${i18n_0} + ${i18n_1} + ${i18n_2} + ${i18n_3} + return [ + $i18n_0$, + [${AttributeMarker.I18n}, "title"], + ["title", $i18n_1$], + $i18n_2$, + ["title", $i18n_3$] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18nStart(1, $i18n_1$); - $r3$.ɵɵelementStart(2, "span", 0); - $r3$.ɵɵi18nAttributes(3, $_c4$); + $r3$.ɵɵi18nStart(1, 0); + $r3$.ɵɵelementStart(2, "span", 1); + $r3$.ɵɵi18nAttributes(3, 2); $r3$.ɵɵelementEnd(); $r3$.ɵɵi18nEnd(); $r3$.ɵɵelementEnd(); $r3$.ɵɵelementStart(4, "div"); - $r3$.ɵɵi18nStart(5, $i18n_3$); + $r3$.ɵɵi18nStart(5, 3); $r3$.ɵɵpipe(6, "uppercase"); - $r3$.ɵɵelementStart(7, "span", 0); - $r3$.ɵɵi18nAttributes(8, $_c9$); + $r3$.ɵɵelementStart(7, "span", 1); + $r3$.ɵɵi18nAttributes(8, 4); $r3$.ɵɵelementEnd(); $r3$.ɵɵi18nEnd(); $r3$.ɵɵelementEnd(); @@ -1401,13 +1472,11 @@ describe('i18n support in the template compiler', () => { ]); const output = String.raw` - ${i18n_0} - … function MyComponent_div_2_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); $r3$.ɵɵelementStart(1, "div"); - $r3$.ɵɵi18nStart(2, $i18n_0$); + $r3$.ɵɵi18nStart(2, 1); $r3$.ɵɵelement(3, "div"); $r3$.ɵɵpipe(4, "uppercase"); $r3$.ɵɵi18nEnd(); @@ -1424,7 +1493,13 @@ describe('i18n support in the template compiler', () => { … decls: 3, vars: 1, - consts: [[${AttributeMarker.Template}, "ngIf"]], + consts: function() { + ${i18n_0} + return [ + [${AttributeMarker.Template}, "ngIf"], + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); @@ -1458,12 +1533,11 @@ describe('i18n support in the template compiler', () => { $r3$.ɵɵelement(0, "img", 0); } } - ${i18n_0} - const $_c4$ = ["title", $i18n_0$]; + … function MyComponent_img_2_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "img", 3); - $r3$.ɵɵi18nAttributes(1, $_c4$); + $r3$.ɵɵi18nAttributes(1, 4); $r3$.ɵɵelementEnd(); } if (rf & 2) { @@ -1475,11 +1549,17 @@ describe('i18n support in the template compiler', () => { … decls: 3, vars: 2, - consts: [["src", "logo.png"], ["src", "logo.png", ${ - AttributeMarker.Template}, "ngIf"], ["src", "logo.png", ${ - AttributeMarker.Bindings}, "title", ${ - AttributeMarker.Template}, "ngIf"], ["src", "logo.png", ${ - AttributeMarker.I18n}, "title"]], + consts: function() { + ${i18n_0} + return [ + ["src", "logo.png"], + ["src", "logo.png", ${AttributeMarker.Template}, "ngIf"], + ["src", "logo.png", ${AttributeMarker.Bindings}, "title", + ${AttributeMarker.Template}, "ngIf"], + ["src", "logo.png", ${AttributeMarker.I18n}, "title"], + ["title", $i18n_0$] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelement(0, "img", 0); @@ -1546,7 +1626,7 @@ describe('i18n support in the template compiler', () => { const output = String.raw` function MyComponent_div_2_div_4_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵi18nStart(0, $I18N_0$, 2); + $r3$.ɵɵi18nStart(0, 0, 2); $r3$.ɵɵelementStart(1, "div"); $r3$.ɵɵelement(2, "div"); $r3$.ɵɵelementEnd(); @@ -1561,11 +1641,11 @@ describe('i18n support in the template compiler', () => { } function MyComponent_div_2_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵi18nStart(0, $I18N_0$, 1); + $r3$.ɵɵi18nStart(0, 0, 1); $r3$.ɵɵelementStart(1, "div"); $r3$.ɵɵelementStart(2, "div"); $r3$.ɵɵpipe(3, "uppercase"); - $r3$.ɵɵtemplate(4, MyComponent_div_2_div_4_Template, 3, 2, "div", 0); + $r3$.ɵɵtemplate(4, MyComponent_div_2_div_4_Template, 3, 2, "div", 1); $r3$.ɵɵelementEnd(); $r3$.ɵɵelementEnd(); $r3$.ɵɵi18nEnd(); @@ -1578,10 +1658,10 @@ describe('i18n support in the template compiler', () => { $r3$.ɵɵi18nApply(0); } } - ${i18n_0} + … function MyComponent_div_3_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵi18nStart(0, $I18N_0$, 3); + $r3$.ɵɵi18nStart(0, 0, 3); $r3$.ɵɵelementStart(1, "div"); $r3$.ɵɵelement(2, "div"); $r3$.ɵɵpipe(3, "uppercase"); @@ -1598,13 +1678,19 @@ describe('i18n support in the template compiler', () => { … decls: 4, vars: 2, - consts: [[${AttributeMarker.Template}, "ngIf"]], + consts: function() { + ${i18n_0} + return [ + $i18n_0$, + [${AttributeMarker.Template}, "ngIf"] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18nStart(1, $I18N_0$); - $r3$.ɵɵtemplate(2, MyComponent_div_2_Template, 5, 5, "div", 0); - $r3$.ɵɵtemplate(3, MyComponent_div_3_Template, 4, 4, "div", 0); + $r3$.ɵɵi18nStart(1, 0); + $r3$.ɵɵtemplate(2, MyComponent_div_2_Template, 5, 5, "div", 1); + $r3$.ɵɵtemplate(3, MyComponent_div_3_Template, 4, 4, "div", 1); $r3$.ɵɵi18nEnd(); $r3$.ɵɵelementEnd(); } @@ -1631,12 +1717,10 @@ describe('i18n support in the template compiler', () => { ]); const output = String.raw` - ${i18n_0} - … function MyComponent_div_0_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18nStart(1, $i18n_0$); + $r3$.ɵɵi18nStart(1, 1); $r3$.ɵɵelement(2, "span"); $r3$.ɵɵi18nEnd(); $r3$.ɵɵelementEnd(); @@ -1651,7 +1735,13 @@ describe('i18n support in the template compiler', () => { … decls: 1, vars: 1, - consts: [[${AttributeMarker.Template}, "ngIf"]], + consts: function() { + ${i18n_0} + return [ + [${AttributeMarker.Template}, "ngIf"], + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_div_0_Template, 3, 1, "div", 0); @@ -1673,14 +1763,18 @@ describe('i18n support in the template compiler', () => { const i18n_0 = i18nMsg('Hello'); const output = String.raw` - ${i18n_0} - … - consts: [[${AttributeMarker.Bindings}, "click"]], + consts: function() { + ${i18n_0} + return [ + [${AttributeMarker.Bindings}, "click"], + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div", 0); $r3$.ɵɵlistener("click", function MyComponent_Template_div_click_0_listener() { return ctx.onClick(); }); - $r3$.ɵɵi18n(1, $i18n_0$); + $r3$.ɵɵi18n(1, 1); $r3$.ɵɵelementEnd(); } } @@ -1699,12 +1793,16 @@ describe('i18n support in the template compiler', () => { const i18n_0 = i18nMsg('My i18n block #1'); const output = String.raw` - ${i18n_0} - … + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18n(1, $i18n_0$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵelementEnd(); } } @@ -1723,14 +1821,18 @@ describe('i18n support in the template compiler', () => { [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]); const output = String.raw` - ${i18n_0} - … decls: 2, vars: 1, + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18n(1, $I18N_0$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵelementEnd(); } if (rf & 2) { @@ -1754,19 +1856,25 @@ describe('i18n support in the template compiler', () => { const i18n_1 = i18nMsg('My i18n block #1'); const output = String.raw` - ${i18n_0} - ${i18n_1} function MyComponent_ng_template_0_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵi18n(0, $i18n_1$); + $r3$.ɵɵi18n(0, 1); } } … + consts: function() { + ${i18n_0} + ${i18n_1} + return [ + $i18n_0$, + $i18n_1$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_ng_template_0_Template, 1, 0, "ng-template"); $r3$.ɵɵelementContainerStart(1); - $r3$.ɵɵi18n(2, $i18n_0$); + $r3$.ɵɵi18n(2, 0); $r3$.ɵɵelementContainerEnd(); } } @@ -1785,20 +1893,25 @@ describe('i18n support in the template compiler', () => { const i18n_1 = i18nMsg('Text #2'); const output = String.raw` - ${i18n_0} - ${i18n_1} - … decls: 4, vars: 0, - consts: [[${AttributeMarker.Classes}, "myClass"], [${ - AttributeMarker.Styles}, "padding", "10px"]], + consts: function() { + ${i18n_0} + ${i18n_1} + return [ + [${AttributeMarker.Classes}, "myClass"], + $i18n_0$, + [${AttributeMarker.Styles}, "padding", "10px"], + $i18n_1$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "span", 0); - $r3$.ɵɵi18n(1, $i18n_0$); + $r3$.ɵɵi18n(1, 1); $r3$.ɵɵelementEnd(); - $r3$.ɵɵelementStart(2, "span", 1); - $r3$.ɵɵi18n(3, $i18n_1$); + $r3$.ɵɵelementStart(2, "span", 2); + $r3$.ɵɵi18n(3, 3); $r3$.ɵɵelementEnd(); } } @@ -1818,14 +1931,18 @@ describe('i18n support in the template compiler', () => { i18nMsg('Some content: {$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]]); const output = String.raw` - ${i18n_0} - … decls: 3, vars: 3, + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementContainerStart(0); - $r3$.ɵɵi18n(1, $i18n_0$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵpipe(2, "uppercase"); $r3$.ɵɵelementContainerEnd(); } @@ -1849,10 +1966,9 @@ describe('i18n support in the template compiler', () => { i18nMsg('Some content: {$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]]); const output = String.raw` - ${i18n_0} function MyComponent_ng_template_0_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵi18n(0, $i18n_0$); + $r3$.ɵɵi18n(0, 0); $r3$.ɵɵpipe(1, "uppercase"); } if (rf & 2) { const $ctx_r0$ = $r3$.ɵɵnextContext(); @@ -1864,6 +1980,12 @@ describe('i18n support in the template compiler', () => { … decls: 1, vars: 0, + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_ng_template_0_Template, 2, 3, "ng-template"); @@ -1894,10 +2016,9 @@ describe('i18n support in the template compiler', () => { ]); const output = String.raw` - ${i18n_0} function MyComponent_ng_template_2_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵi18n(0, $I18N_0$, 1); + $r3$.ɵɵi18n(0, 0, 1); $r3$.ɵɵpipe(1, "uppercase"); } if (rf & 2) { @@ -1910,10 +2031,16 @@ describe('i18n support in the template compiler', () => { … decls: 5, vars: 3, + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18nStart(1, $i18n_0$); + $r3$.ɵɵi18nStart(1, 0); $r3$.ɵɵtemplate(2, MyComponent_ng_template_2_Template, 2, 3, "ng-template"); $r3$.ɵɵelementContainer(3); $r3$.ɵɵpipe(4, "uppercase"); @@ -1945,11 +2072,9 @@ describe('i18n support in the template compiler', () => { [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]); const output = String.raw` - ${i18n_0} - ${i18n_1} function MyComponent_ng_template_0_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵi18n(0, $i18n_1$); + $r3$.ɵɵi18n(0, 1); } if (rf & 2) { const $ctx_r0$ = $r3$.ɵɵnextContext(); @@ -1960,11 +2085,19 @@ describe('i18n support in the template compiler', () => { … decls: 3, vars: 1, + consts: function() { + ${i18n_0} + ${i18n_1} + return [ + $i18n_0$, + $i18n_1$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_ng_template_0_Template, 1, 1, "ng-template"); $r3$.ɵɵelementContainerStart(1); - $r3$.ɵɵi18n(2, $i18n_0$); + $r3$.ɵɵi18n(2, 0); $r3$.ɵɵelementContainerEnd(); } if (rf & 2) { @@ -2011,7 +2144,7 @@ describe('i18n support in the template compiler', () => { const output = String.raw` function MyComponent_ng_template_2_ng_template_2_ng_template_1_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵi18n(0, $i18n_0$, 3); + $r3$.ɵɵi18n(0, 0, 3); } if (rf & 2) { const $ctx_r2$ = $r3$.ɵɵnextContext(3); @@ -2021,7 +2154,7 @@ describe('i18n support in the template compiler', () => { } function MyComponent_ng_template_2_ng_template_2_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵi18nStart(0, $i18n_0$, 2); + $r3$.ɵɵi18nStart(0, 0, 2); $r3$.ɵɵtemplate(1, MyComponent_ng_template_2_ng_template_2_ng_template_1_Template, 1, 1, "ng-template"); $r3$.ɵɵi18nEnd(); } @@ -2032,10 +2165,10 @@ describe('i18n support in the template compiler', () => { $r3$.ɵɵi18nApply(0); } } - ${i18n_0} + … function MyComponent_ng_template_2_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵi18nStart(0, $i18n_0$, 1); + $r3$.ɵɵi18nStart(0, 0, 1); $r3$.ɵɵpipe(1, "uppercase"); $r3$.ɵɵtemplate(2, MyComponent_ng_template_2_ng_template_2_Template, 2, 1, "ng-template"); $r3$.ɵɵi18nEnd(); @@ -2050,10 +2183,16 @@ describe('i18n support in the template compiler', () => { … decls: 3, vars: 0, + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18nStart(1, $i18n_0$); + $r3$.ɵɵi18nStart(1, 0); $r3$.ɵɵtemplate(2, MyComponent_ng_template_2_Template, 3, 3, "ng-template"); $r3$.ɵɵi18nEnd(); $r3$.ɵɵelementEnd(); @@ -2078,11 +2217,9 @@ describe('i18n support in the template compiler', () => { [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]); const output = String.raw` - ${i18n_0} - ${i18n_1} function MyComponent_ng_template_2_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵi18n(0, $I18N_1$); + $r3$.ɵɵi18n(0, 1); } if (rf & 2) { const $ctx_r0$ = $r3$.ɵɵnextContext(); @@ -2093,10 +2230,18 @@ describe('i18n support in the template compiler', () => { … decls: 3, vars: 1, + consts: function() { + ${i18n_0} + ${i18n_1} + return [ + $i18n_0$, + $i18n_1$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementContainerStart(0); - $r3$.ɵɵi18n(1, $i18n_0$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵelementContainerEnd(); $r3$.ɵɵtemplate(2, MyComponent_ng_template_2_Template, 1, 1, "ng-template"); } @@ -2127,22 +2272,28 @@ describe('i18n support in the template compiler', () => { '{$tagImg} is my logo #2 ', [['tagImg', String.raw`\uFFFD#1\uFFFD\uFFFD/#1\uFFFD`]]); const output = String.raw` - ${i18n_0} - ${i18n_1} function MyComponent_ng_template_3_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵi18nStart(0, $i18n_1$); - $r3$.ɵɵelement(1, "img", 0); + $r3$.ɵɵi18nStart(0, 2); + $r3$.ɵɵelement(1, "img", 1); $r3$.ɵɵi18nEnd(); } } … - consts: [["src", "logo.png", "title", "Logo"]], + consts: function() { + ${i18n_0} + ${i18n_1} + return [ + $i18n_0$, + ["src", "logo.png", "title", "Logo"], + $i18n_1$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementContainerStart(0); - $r3$.ɵɵi18nStart(1, $i18n_0$); - $r3$.ɵɵelement(2, "img", 0); + $r3$.ɵɵi18nStart(1, 0); + $r3$.ɵɵelement(2, "img", 1); $r3$.ɵɵi18nEnd(); $r3$.ɵɵelementContainerEnd(); $r3$.ɵɵtemplate(3, MyComponent_ng_template_3_Template, 2, 0, "ng-template"); @@ -2202,14 +2353,18 @@ describe('i18n support in the template compiler', () => { ]); const output = String.raw` - ${i18n_0} - … decls: 3, vars: 0, + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18nStart(1, $i18n_0$); + $r3$.ɵɵi18nStart(1, 0); $r3$.ɵɵelementContainer(2); $r3$.ɵɵi18nEnd(); $r3$.ɵɵelementEnd(); @@ -2238,14 +2393,18 @@ describe('i18n support in the template compiler', () => { ]); const output = String.raw` - ${i18n_0} - … decls: 4, vars: 0, + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18nStart(1, I18N_0); + $r3$.ɵɵi18nStart(1, 0); $r3$.ɵɵelementContainerStart(2); $r3$.ɵɵelement(3, "strong"); $r3$.ɵɵelementContainerEnd(); @@ -2270,10 +2429,9 @@ describe('i18n support in the template compiler', () => { const i18n_1 = i18nMsg('Content B'); const output = String.raw` - ${i18n_0} function MyComponent_0_ng_template_0_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵi18n(0, $i18n_0$); + $r3$.ɵɵi18n(0, 1); } } function MyComponent_0_Template(rf, ctx) { @@ -2281,18 +2439,26 @@ describe('i18n support in the template compiler', () => { $r3$.ɵɵtemplate(0, MyComponent_0_ng_template_0_Template, 1, 0, "ng-template"); } } - ${i18n_1} + … function MyComponent_ng_container_1_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementContainerStart(0); - $r3$.ɵɵi18n(1, $i18n_1$); + $r3$.ɵɵi18n(1, 2); $r3$.ɵɵelementContainerEnd(); } } … decls: 2, vars: 2, - consts: [[4, "ngIf"]], + consts: function() { + ${i18n_0} + ${i18n_1} + return [ + [${AttributeMarker.Template}, "ngIf"], + $i18n_0$, + $i18n_1$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtemplate(0, MyComponent_0_Template, 1, 0, undefined, 0); @@ -2320,7 +2486,7 @@ describe('i18n support in the template compiler', () => { // Keeping raw content (avoiding `i18nMsg`) to illustrate message layout // in case of whitespace preserving mode. - const output = String.raw` + const i18n_0 = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_963542717423364282$$APP_SPEC_TS_0$ = goog.getMsg("\n Some text\n {$startTagSpan}Text inside span{$closeTagSpan}\n ", { @@ -2337,12 +2503,20 @@ describe('i18n support in the template compiler', () => { String.raw`{"\uFFFD/#3\uFFFD"}:CLOSE_TAG_SPAN: \`; } - … + `; + + const output = String.raw` + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵtext(0, "\n "); $r3$.ɵɵelementStart(1, "div"); - $r3$.ɵɵi18nStart(2, $I18N_0$); + $r3$.ɵɵi18nStart(2, 0); $r3$.ɵɵelement(3, "span"); $r3$.ɵɵi18nEnd(); $r3$.ɵɵelementEnd(); @@ -2366,14 +2540,18 @@ describe('i18n support in the template compiler', () => { [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]); const output = String.raw` - ${i18n_0} - … decls: 2, vars: 1, + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18n(1, $I18N_0$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵelementEnd(); } if (rf & 2) { @@ -2419,13 +2597,17 @@ describe('i18n support in the template compiler', () => { [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]); const output = String.raw` - ${i18n_0} - … decls: 1, vars: 1, + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵi18n(0, $i18n_0$); + $r3$.ɵɵi18n(0, 0); } if (rf & 2) { $r3$.ɵɵi18nExp(ctx.age); @@ -2460,13 +2642,11 @@ describe('i18n support in the template compiler', () => { ]); const output = String.raw` - ${i18n_0} - ${i18n_1} function MyComponent_div_2_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵelementStart(0, "div", 2); + $r3$.ɵɵelementStart(0, "div", 3); $r3$.ɵɵtext(1, " "); - $r3$.ɵɵi18n(2, $i18n_1$); + $r3$.ɵɵi18n(2, 4); $r3$.ɵɵtext(3, " "); $r3$.ɵɵelementEnd(); } @@ -2477,12 +2657,12 @@ describe('i18n support in the template compiler', () => { $r3$.ɵɵi18nApply(2); } } - ${i18n_2} + … function MyComponent_div_3_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵelementStart(0, "div", 3); + $r3$.ɵɵelementStart(0, "div", 5); $r3$.ɵɵtext(1, " You have "); - $r3$.ɵɵi18n(2, $i18n_2$); + $r3$.ɵɵi18n(2, 6); $r3$.ɵɵtext(3, ". "); $r3$.ɵɵelementEnd(); } @@ -2496,16 +2676,27 @@ describe('i18n support in the template compiler', () => { … decls: 4, vars: 3, - consts: [["title", "icu only", ${ - AttributeMarker.Template}, "ngIf"], ["title", "icu and text", ${ - AttributeMarker.Template}, "ngIf"], ["title", "icu only"], ["title", "icu and text"]], + consts: function() { + ${i18n_0} + ${i18n_1} + ${i18n_2} + return [ + $i18n_0$, + ["title", "icu only", ${AttributeMarker.Template}, "ngIf"], + ["title", "icu and text", ${AttributeMarker.Template}, "ngIf"], + ["title", "icu only"], + $i18n_1$, + ["title", "icu and text"], + $i18n_2$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18n(1, $i18n_0$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵelementEnd(); - $r3$.ɵɵtemplate(2, MyComponent_div_2_Template, 4, 1, "div", 0); - $r3$.ɵɵtemplate(3, MyComponent_div_3_Template, 4, 2, "div", 1); + $r3$.ɵɵtemplate(2, MyComponent_div_2_Template, 4, 1, "div", 1); + $r3$.ɵɵtemplate(3, MyComponent_div_3_Template, 4, 2, "div", 2); } if (rf & 2) { $r3$.ɵɵadvance(1); @@ -2533,12 +2724,16 @@ describe('i18n support in the template compiler', () => { ]); const output = String.raw` - ${i18n_0} - … + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18n(1, $i18n_0$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵelementEnd(); } if (rf & 2) { @@ -2586,18 +2781,22 @@ describe('i18n support in the template compiler', () => { ]); const output = String.raw` - ${i18n_0} - ${i18n_1} - … decls: 5, vars: 1, - consts: [[1, "other"]], + consts: function() { + ${i18n_0} + ${i18n_1} + return [ + $i18n_1$, + [${AttributeMarker.Classes}, "other"] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18nStart(1, $i18n_1$); + $r3$.ɵɵi18nStart(1, 0); $r3$.ɵɵelement(2, "b"); - $r3$.ɵɵelementStart(3, "div", 0); + $r3$.ɵɵelementStart(3, "div", 1); $r3$.ɵɵelement(4, "i"); $r3$.ɵɵelementEnd(); $r3$.ɵɵi18nEnd(); @@ -2627,14 +2826,18 @@ describe('i18n support in the template compiler', () => { ]); const output = String.raw` - ${i18n_0} - … decls: 2, vars: 2, + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18n(1, $i18n_0$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵelementEnd(); } if (rf & 2) { @@ -2668,16 +2871,20 @@ describe('i18n support in the template compiler', () => { ]); const output = String.raw` - ${i18n_0} - ${i18n_1} - ${i18n_2} - … decls: 2, vars: 2, + consts: function() { + ${i18n_0} + ${i18n_1} + ${i18n_2} + return [ + $i18n_2$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18n(1, $i18n_2$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵelementEnd(); } if (rf & 2) { @@ -2704,7 +2911,9 @@ describe('i18n support in the template compiler', () => { `; - const output = String.raw` + // Keeping raw content here to illustrate the difference in placeholders generated for + // goog.getMsg and $localize calls (see last i18n block). + const i18n_0 = String.raw` var $I18N_1$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_APP_SPEC_TS_1$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}"); @@ -2761,9 +2970,12 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$, { "ICU": [$I18N_1$, $I18N_2$, $I18N_4$] }); + `; + + const output = String.raw` function MyComponent_div_3_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵi18nStart(0, $I18N_0$, 1); + $r3$.ɵɵi18nStart(0, 0, 1); $r3$.ɵɵelement(1, "div"); $r3$.ɵɵi18nEnd(); } @@ -2777,13 +2989,19 @@ describe('i18n support in the template compiler', () => { … decls: 4, vars: 3, - consts: [[${AttributeMarker.Template}, "ngIf"]], + consts: function() { + ${i18n_0} + return [ + $i18n_0$, + [${AttributeMarker.Template}, "ngIf"] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18nStart(1, $I18N_0$); + $r3$.ɵɵi18nStart(1, 0); $r3$.ɵɵelement(2, "div"); - $r3$.ɵɵtemplate(3, MyComponent_div_3_Template, 2, 1, "div", 0); + $r3$.ɵɵtemplate(3, MyComponent_div_3_Template, 2, 1, "div", 1); $r3$.ɵɵi18nEnd(); $r3$.ɵɵelementEnd(); } @@ -2819,15 +3037,19 @@ describe('i18n support in the template compiler', () => { const i18n_1 = i18nMsg(' {$icu} ', [['icu', '$i18n_0$']]); const output = String.raw` - ${i18n_0} - ${i18n_1} - … decls: 2, vars: 2, + consts: function() { + ${i18n_0} + ${i18n_1} + return [ + $i18n_1$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18n(1, $i18n_1$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵelementEnd(); } if (rf & 2) { @@ -2865,14 +3087,18 @@ describe('i18n support in the template compiler', () => { ]); const output = String.raw` - ${i18n_0} - … decls: 2, vars: 3, + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18n(1, $i18n_0$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵelementEnd(); } if (rf & 2) { @@ -2910,13 +3136,9 @@ describe('i18n support in the template compiler', () => { ]); const output = String.raw` - ${i18n_0} - ${i18n_1} - ${i18n_2} - … function MyComponent_span_2_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵi18nStart(0, $i18n_2$, 1); + $r3$.ɵɵi18nStart(0, 0, 1); $r3$.ɵɵelement(1, "span"); $r3$.ɵɵi18nEnd(); } @@ -2930,12 +3152,20 @@ describe('i18n support in the template compiler', () => { … decls: 3, vars: 2, - consts: [[${AttributeMarker.Template}, "ngIf"]], + consts: function() { + ${i18n_0} + ${i18n_1} + ${i18n_2} + return [ + $i18n_2$, + [${AttributeMarker.Template}, "ngIf"] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18nStart(1, $i18n_2$); - $r3$.ɵɵtemplate(2, MyComponent_span_2_Template, 2, 1, "span", 0); + $r3$.ɵɵi18nStart(1, 0); + $r3$.ɵɵtemplate(2, MyComponent_span_2_Template, 2, 1, "span", 1); $r3$.ɵɵi18nEnd(); $r3$.ɵɵelementEnd(); } @@ -2981,12 +3211,9 @@ describe('i18n support in the template compiler', () => { ]); const output = String.raw` - ${i18n_0} - ${i18n_1} - ${i18n_2} function MyComponent_span_2_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵɵi18nStart(0, $i18n_2$, 1); + $r3$.ɵɵi18nStart(0, 0, 1); $r3$.ɵɵelement(1, "span"); $r3$.ɵɵi18nEnd(); } @@ -3000,12 +3227,20 @@ describe('i18n support in the template compiler', () => { … decls: 3, vars: 4, - consts: [[${AttributeMarker.Template}, "ngIf"]], + consts: function() { + ${i18n_0} + ${i18n_1} + ${i18n_2} + return [ + $i18n_2$, + [${AttributeMarker.Template}, "ngIf"] + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18nStart(1, $i18n_2$); - $r3$.ɵɵtemplate(2, MyComponent_span_2_Template, 2, 2, "span", 0); + $r3$.ɵɵi18nStart(1, 0); + $r3$.ɵɵtemplate(2, MyComponent_span_2_Template, 2, 2, "span", 1); $r3$.ɵɵi18nEnd(); $r3$.ɵɵelementEnd(); } @@ -3042,14 +3277,18 @@ describe('i18n support in the template compiler', () => { ]); const output = String.raw` - ${i18n_0} - … decls: 2, vars: 4, + consts: function() { + ${i18n_0} + return [ + $i18n_0$ + ]; + }, template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵi18n(1, $i18n_0$); + $r3$.ɵɵi18n(1, 0); $r3$.ɵɵelementEnd(); } if (rf & 2) { @@ -3283,7 +3522,7 @@ $` + String.raw`{$I18N_4$}:ICU:\`; `; - const output = String.raw` + const i18n_0 = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_7128002169381370313$$APP_SPEC_TS_1$ = goog.getMsg("{$startTagXhtmlDiv} Count: {$startTagXhtmlSpan}5{$closeTagXhtmlSpan}{$closeTagXhtmlDiv}", { @@ -3301,15 +3540,26 @@ $` + String.raw`{$I18N_4$}:ICU:\`; String.raw`{"\uFFFD/#4\uFFFD"}:CLOSE_TAG__XHTML_SPAN:$` + String.raw`{"\uFFFD/#3\uFFFD"}:CLOSE_TAG__XHTML_DIV:\`; } + `; + + const output = String.raw` … - function MyComponent_Template(rf, ctx) { + consts: function() { + ${i18n_0} + return [ + ["xmlns", "http://www.w3.org/2000/svg"], + $i18n_0$, + ["xmlns", "http://www.w3.org/1999/xhtml"] + ]; + }, + template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵnamespaceSVG(); $r3$.ɵɵelementStart(0, "svg", 0); $r3$.ɵɵelementStart(1, "foreignObject"); - $r3$.ɵɵi18nStart(2, $I18N_0$); + $r3$.ɵɵi18nStart(2, 1); $r3$.ɵɵnamespaceHTML(); - $r3$.ɵɵelementStart(3, "div", 1); + $r3$.ɵɵelementStart(3, "div", 2); $r3$.ɵɵelement(4, "span"); $r3$.ɵɵelementEnd(); $r3$.ɵɵi18nEnd(); @@ -3333,7 +3583,7 @@ $` + String.raw`{$I18N_4$}:ICU:\`; `; - const output = String.raw` + const i18n_0 = String.raw` var $I18N_0$; if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { const $MSG_EXTERNAL_7428861019045796010$$APP_SPEC_TS_1$ = goog.getMsg(" Count: {$startTagXhtmlSpan}5{$closeTagXhtmlSpan}", { @@ -3347,15 +3597,25 @@ $` + String.raw`{$I18N_4$}:ICU:\`; String.raw`{"\uFFFD#4\uFFFD"}:START_TAG__XHTML_SPAN:5$` + String.raw`{"\uFFFD/#4\uFFFD"}:CLOSE_TAG__XHTML_SPAN:\`; } - … - function MyComponent_Template(rf, ctx) { + `; + + const output = String.raw` + consts: function() { + ${i18n_0} + return [ + ["xmlns", "http://www.w3.org/2000/svg"], + ["xmlns", "http://www.w3.org/1999/xhtml"], + $i18n_0$ + ]; + }, + template: function MyComponent_Template(rf, ctx) { if (rf & 1) { $r3$.ɵɵnamespaceSVG(); $r3$.ɵɵelementStart(0, "svg", 0); $r3$.ɵɵelementStart(1, "foreignObject"); $r3$.ɵɵnamespaceHTML(); $r3$.ɵɵelementStart(2, "div", 1); - $r3$.ɵɵi18nStart(3, $I18N_0$); + $r3$.ɵɵi18nStart(3, 2); $r3$.ɵɵelement(4, "span"); $r3$.ɵɵi18nEnd(); $r3$.ɵɵelementEnd(); @@ -3365,7 +3625,7 @@ $` + String.raw`{$I18N_4$}:ICU:\`; } `; - verify(input, output, {verbose: true}); + verify(input, output); }); }); }); diff --git a/packages/compiler/src/render3/view/compiler.ts b/packages/compiler/src/render3/view/compiler.ts index 2aaeddc1fe..9844830d0a 100644 --- a/packages/compiler/src/render3/view/compiler.ts +++ b/packages/compiler/src/render3/view/compiler.ts @@ -196,10 +196,19 @@ export function compileComponentFromMetadata( // e.g. `vars: 2` definitionMap.set('vars', o.literal(templateBuilder.getVarCount())); - // e.g. `consts: [['one', 'two'], ['three', 'four']] - const consts = templateBuilder.getConsts(); - if (consts.length > 0) { - definitionMap.set('consts', o.literalArr(consts)); + // Generate `consts` section of ComponentDef: + // - either as an array: + // `consts: [['one', 'two'], ['three', 'four']]` + // - or as a factory function in case additional statements are present (to support i18n): + // `consts: function() { var i18n_0; if (ngI18nClosureMode) {...} else {...} return [i18n_0]; }` + const {constExpressions, prepareStatements} = templateBuilder.getConsts(); + if (constExpressions.length > 0) { + let constsExpr: o.LiteralArrayExpr|o.FunctionExpr = o.literalArr(constExpressions); + // Prepare statements are present - turn `consts` into a function. + if (prepareStatements.length > 0) { + constsExpr = o.fn([], [...prepareStatements, new o.ReturnStatement(constsExpr)]); + } + definitionMap.set('consts', constsExpr); } definitionMap.set('template', templateFunctionExpression); diff --git a/packages/compiler/src/render3/view/i18n/util.ts b/packages/compiler/src/render3/view/i18n/util.ts index f867dcc0c9..da71db4b67 100644 --- a/packages/compiler/src/render3/view/i18n/util.ts +++ b/packages/compiler/src/render3/view/i18n/util.ts @@ -12,10 +12,14 @@ import * as o from '../../../output/output_ast'; import * as t from '../../r3_ast'; /* Closure variables holding messages must be named `MSG_[A-Z0-9]+` */ -const CLOSURE_TRANSLATION_PREFIX = 'MSG_'; +const CLOSURE_TRANSLATION_VAR_PREFIX = 'MSG_'; -/* Prefix for non-`goog.getMsg` i18n-related vars */ -export const TRANSLATION_PREFIX = 'I18N_'; +/** + * Prefix for non-`goog.getMsg` i18n-related vars. + * Note: the prefix uses lowercase characters intentionally due to a Closure behavior that + * considers variables like `I18N_0` as constants and throws an error when their value changes. + */ +export const TRANSLATION_VAR_PREFIX = 'i18n_'; /** Name of the i18n attributes **/ export const I18N_ATTR = 'i18n'; @@ -166,7 +170,7 @@ export function formatI18nPlaceholderName(name: string, useCamelCase: boolean = * @returns Complete translation const prefix */ export function getTranslationConstPrefix(extra: string): string { - return `${CLOSURE_TRANSLATION_PREFIX}${extra}`.toUpperCase(); + return `${CLOSURE_TRANSLATION_VAR_PREFIX}${extra}`.toUpperCase(); } /** diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts index 4cb1601915..818d0c5119 100644 --- a/packages/compiler/src/render3/view/template.ts +++ b/packages/compiler/src/render3/view/template.ts @@ -36,7 +36,7 @@ import {I18nContext} from './i18n/context'; import {createGoogleGetMsgStatements} from './i18n/get_msg_utils'; import {createLocalizeStatements} from './i18n/localize_utils'; import {I18nMetaVisitor} from './i18n/meta'; -import {assembleBoundTextPlaceholders, assembleI18nBoundString, declareI18nVariable, getTranslationConstPrefix, hasI18nMeta, I18N_ICU_MAPPING_PREFIX, i18nFormatPlaceholderNames, icuFromI18nMessage, isI18nRootNode, isSingleI18nIcu, placeholdersToParams, TRANSLATION_PREFIX, wrapI18nPlaceholder} from './i18n/util'; +import {assembleBoundTextPlaceholders, assembleI18nBoundString, declareI18nVariable, getTranslationConstPrefix, hasI18nMeta, I18N_ICU_MAPPING_PREFIX, i18nFormatPlaceholderNames, icuFromI18nMessage, isI18nRootNode, isSingleI18nIcu, placeholdersToParams, TRANSLATION_VAR_PREFIX, wrapI18nPlaceholder} from './i18n/util'; import {StylingBuilder, StylingInstruction} from './styling_builder'; import {asLiteral, chainedInstruction, CONTEXT_NAME, getAttrsForDirectiveMatching, getInterpolationArgsLength, IMPLICIT_REFERENCE, invalid, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, trimTrailingNulls, unsupported} from './util'; @@ -103,6 +103,18 @@ export function prepareEventListenerParameters( return params; } +// Collects information needed to generate `consts` field of the ComponentDef. +// When a constant requires some pre-processing, the `prepareStatements` section +// contains corresponding statements. +export interface ComponentDefConsts { + prepareStatements: o.Statement[]; + constExpressions: o.Expression[]; +} + +function createComponentDefConsts(): ComponentDefConsts { + return {prepareStatements: [], constExpressions: []}; +} + export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver { private _dataIndex = 0; private _bindingContext = 0; @@ -171,7 +183,8 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver private directiveMatcher: SelectorMatcher|null, private directives: Set, private pipeTypeByName: Map, private pipes: Set, private _namespace: o.ExternalReference, relativeContextFilePath: string, - private i18nUseExternalIds: boolean, private _constants: o.Expression[] = []) { + private i18nUseExternalIds: boolean, + private _constants: ComponentDefConsts = createComponentDefConsts()) { this._bindingScope = parentBindingScope.nestedScope(level); // Turn the relative context file path into an identifier by replacing non-alphanumeric @@ -307,12 +320,12 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver private i18nTranslate( message: i18n.Message, params: {[name: string]: o.Expression} = {}, ref?: o.ReadVarExpr, transformFn?: (raw: o.ReadVarExpr) => o.Expression): o.ReadVarExpr { - const _ref = ref || o.variable(this.constantPool.uniqueName(TRANSLATION_PREFIX)); + const _ref = ref || this.i18nGenerateMainBlockVar(); // Closure Compiler requires const names to start with `MSG_` but disallows any other const to // start with `MSG_`. We define a variable starting with `MSG_` just for the `goog.getMsg` call const closureVar = this.i18nGenerateClosureVar(message.id); const statements = getTranslationDeclStmts(message, _ref, closureVar, params, transformFn); - this.constantPool.statements.push(...statements); + this._constants.prepareStatements.push(...statements); return _ref; } @@ -364,6 +377,12 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver return bound; } + // Generates top level vars for i18n blocks (i.e. `i18n_N`). + private i18nGenerateMainBlockVar(): o.ReadVarExpr { + return o.variable(this.constantPool.uniqueName(TRANSLATION_VAR_PREFIX)); + } + + // Generates vars with Closure-specific names for i18n blocks (i.e. `MSG_XXX`). private i18nGenerateClosureVar(messageId: string): o.ReadVarExpr { let name: string; const suffix = this.fileBasedI18nSuffix.toUpperCase(); @@ -426,16 +445,13 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver private i18nStart(span: ParseSourceSpan|null = null, meta: i18n.I18nMeta, selfClosing?: boolean): void { const index = this.allocateDataSlot(); - if (this.i18nContext) { - this.i18n = this.i18nContext.forkChildContext(index, this.templateIndex!, meta); - } else { - const ref = o.variable(this.constantPool.uniqueName(TRANSLATION_PREFIX)); - this.i18n = new I18nContext(index, ref, 0, this.templateIndex, meta); - } + this.i18n = this.i18nContext ? + this.i18nContext.forkChildContext(index, this.templateIndex!, meta) : + new I18nContext(index, this.i18nGenerateMainBlockVar(), 0, this.templateIndex, meta); // generate i18nStart instruction const {id, ref} = this.i18n; - const params: o.Expression[] = [o.literal(index), ref]; + const params: o.Expression[] = [o.literal(index), this.addToConsts(ref)]; if (id > 0) { // do not push 3rd argument (sub-block id) // into i18nStart call for top level i18n context @@ -507,8 +523,8 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver } if (i18nAttrArgs.length > 0) { const index: o.Expression = o.literal(this.allocateDataSlot()); - const args = this.constantPool.getConstLiteral(o.literalArr(i18nAttrArgs), true); - this.creationInstruction(sourceSpan, R3.i18nAttributes, [index, args]); + const constIndex = this.addToConsts(o.literalArr(i18nAttrArgs)); + this.creationInstruction(sourceSpan, R3.i18nAttributes, [index, constIndex]); if (hasBindings) { this.updateInstruction(sourceSpan, R3.i18nApply, [index]); } @@ -1028,7 +1044,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver return this._pureFunctionSlots; } - getConsts() { + getConsts(): ComponentDefConsts { return this._constants; } @@ -1352,14 +1368,16 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver return o.TYPED_NULL_EXPR; } + const consts = this._constants.constExpressions; + // Try to reuse a literal that's already in the array, if possible. - for (let i = 0; i < this._constants.length; i++) { - if (this._constants[i].isEquivalent(expression)) { + for (let i = 0; i < consts.length; i++) { + if (consts[i].isEquivalent(expression)) { return o.literal(i); } } - return o.literal(this._constants.push(expression) - 1); + return o.literal(consts.push(expression) - 1); } private addAttrsToConsts(attrs: o.Expression[]): o.LiteralExpr { diff --git a/packages/core/src/render3/definition.ts b/packages/core/src/render3/definition.ts index 74fa4237b6..5b528ded1c 100644 --- a/packages/core/src/render3/definition.ts +++ b/packages/core/src/render3/definition.ts @@ -18,7 +18,7 @@ import {stringify} from '../util/stringify'; import {EMPTY_ARRAY, EMPTY_OBJ} from './empty'; import {NG_COMP_DEF, NG_DIR_DEF, NG_FACTORY_DEF, NG_LOC_ID_DEF, NG_MOD_DEF, NG_PIPE_DEF} from './fields'; import {ComponentDef, ComponentDefFeature, ComponentTemplate, ComponentType, ContentQueriesFunction, DirectiveDef, DirectiveDefFeature, DirectiveTypesOrFactory, FactoryFn, HostBindingsFunction, PipeDef, PipeType, PipeTypesOrFactory, ViewQueriesFunction} from './interfaces/definition'; -import {AttributeMarker, TAttributes, TConstants} from './interfaces/node'; +import {AttributeMarker, TAttributes, TConstantsOrFactory} from './interfaces/node'; import {CssSelectorList, SelectorFlags} from './interfaces/projection'; import {NgModuleType} from './ng_module_ref'; @@ -220,7 +220,7 @@ export function ɵɵdefineComponent(componentDefinition: { * Constants for the nodes in the component's view. * Includes attribute arrays, local definition arrays etc. */ - consts?: TConstants; + consts?: TConstantsOrFactory; /** * An array of `ngContent[selector]` values that were found in the template. diff --git a/packages/core/src/render3/instructions/i18n.ts b/packages/core/src/render3/instructions/i18n.ts index bcc865e8d2..afaa3209c4 100644 --- a/packages/core/src/render3/instructions/i18n.ts +++ b/packages/core/src/render3/instructions/i18n.ts @@ -15,6 +15,7 @@ import {i18nAttributesFirstPass, i18nStartFirstPass} from '../i18n/i18n_parse'; import {i18nPostprocess} from '../i18n/i18n_postprocess'; import {HEADER_OFFSET} from '../interfaces/view'; import {getLView, getTView, nextBindingIndex} from '../state'; +import {getConstant} from '../util/view_utils'; import {setDelayProjection} from './all'; @@ -42,14 +43,15 @@ import {setDelayProjection} from './all'; * `template` instruction index. A `block` that matches the sub-template in which it was declared. * * @param index A unique index of the translation in the static block. - * @param message The translation message. + * @param messageIndex An index of the translation message from the `def.consts` array. * @param subTemplateIndex Optional sub-template index in the `message`. * * @codeGenApi */ -export function ɵɵi18nStart(index: number, message: string, subTemplateIndex?: number): void { +export function ɵɵi18nStart(index: number, messageIndex: number, subTemplateIndex?: number): void { const tView = getTView(); ngDevMode && assertDefined(tView, `tView should be defined`); + const message = getConstant(tView.consts, messageIndex)!; pushI18nIndex(index); // We need to delay projections until `i18nEnd` setDelayProjection(true); @@ -96,13 +98,13 @@ export function ɵɵi18nEnd(): void { * `template` instruction index. A `block` that matches the sub-template in which it was declared. * * @param index A unique index of the translation in the static block. - * @param message The translation message. + * @param messageIndex An index of the translation message from the `def.consts` array. * @param subTemplateIndex Optional sub-template index in the `message`. * * @codeGenApi */ -export function ɵɵi18n(index: number, message: string, subTemplateIndex?: number): void { - ɵɵi18nStart(index, message, subTemplateIndex); +export function ɵɵi18n(index: number, messageIndex: number, subTemplateIndex?: number): void { + ɵɵi18nStart(index, messageIndex, subTemplateIndex); ɵɵi18nEnd(); } @@ -114,11 +116,12 @@ export function ɵɵi18n(index: number, message: string, subTemplateIndex?: numb * * @codeGenApi */ -export function ɵɵi18nAttributes(index: number, values: string[]): void { +export function ɵɵi18nAttributes(index: number, attrsIndex: number): void { const lView = getLView(); const tView = getTView(); ngDevMode && assertDefined(tView, `tView should be defined`); - i18nAttributesFirstPass(lView, tView, index, values); + const attrs = getConstant(tView.consts, attrsIndex)!; + i18nAttributesFirstPass(lView, tView, index, attrs); } diff --git a/packages/core/src/render3/instructions/shared.ts b/packages/core/src/render3/instructions/shared.ts index c707856b0f..4addb1e83b 100644 --- a/packages/core/src/render3/instructions/shared.ts +++ b/packages/core/src/render3/instructions/shared.ts @@ -26,7 +26,7 @@ import {executeCheckHooks, executeInitAndCheckHooks, incrementInitPhaseFlags} fr import {CONTAINER_HEADER_OFFSET, HAS_TRANSPLANTED_VIEWS, LContainer, MOVED_VIEWS} from '../interfaces/container'; import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags, ViewQueriesFunction} from '../interfaces/definition'; import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from '../interfaces/injector'; -import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliases, PropertyAliasValue, TAttributes, TConstants, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode, TViewNode} from '../interfaces/node'; +import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliases, PropertyAliasValue, TAttributes, TConstantsOrFactory, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode, TViewNode} from '../interfaces/node'; import {isProceduralRenderer, RComment, RElement, Renderer3, RendererFactory3, RNode, RText} from '../interfaces/renderer'; import {SanitizerFn} from '../interfaces/sanitization'; import {isComponentDef, isComponentHost, isContentQueryHost, isLContainer, isRootView} from '../interfaces/type_checks'; @@ -650,7 +650,7 @@ export function createTView( type: TViewType, viewIndex: number, templateFn: ComponentTemplate|null, decls: number, vars: number, directives: DirectiveDefListOrFactory|null, pipes: PipeDefListOrFactory|null, viewQuery: ViewQueriesFunction|null, schemas: SchemaMetadata[]|null, - consts: TConstants|null): TView { + constsOrFactory: TConstantsOrFactory|null): TView { ngDevMode && ngDevMode.tView++; const bindingStartIndex = HEADER_OFFSET + decls; // This length does not yet contain host bindings from child directives because at this point, @@ -658,6 +658,7 @@ export function createTView( // that has a host binding, we will update the blueprint with that def's hostVars count. const initialViewLength = bindingStartIndex + vars; const blueprint = createViewBlueprint(bindingStartIndex, initialViewLength); + const consts = typeof constsOrFactory === 'function' ? constsOrFactory() : constsOrFactory; const tView = blueprint[TVIEW as any] = ngDevMode ? new TViewConstructor( type, diff --git a/packages/core/src/render3/interfaces/definition.ts b/packages/core/src/render3/interfaces/definition.ts index a48cb77732..c8de2c50e4 100644 --- a/packages/core/src/render3/interfaces/definition.ts +++ b/packages/core/src/render3/interfaces/definition.ts @@ -10,7 +10,7 @@ import {SchemaMetadata, ViewEncapsulation} from '../../core'; import {ProcessProvidersFunction} from '../../di/interface/provider'; import {Type} from '../../interface/type'; -import {TAttributes, TConstants} from './node'; +import {TAttributes, TConstantsOrFactory} from './node'; import {CssSelectorList} from './projection'; import {TView} from './view'; @@ -299,7 +299,7 @@ export interface ComponentDef extends DirectiveDef { readonly template: ComponentTemplate; /** Constants associated with the component's view. */ - readonly consts: TConstants|null; + readonly consts: TConstantsOrFactory|null; /** * An array of `ngContent[selector]` values that were found in the template. diff --git a/packages/core/src/render3/interfaces/node.ts b/packages/core/src/render3/interfaces/node.ts index 462105d2f4..22d9744786 100644 --- a/packages/core/src/render3/interfaces/node.ts +++ b/packages/core/src/render3/interfaces/node.ts @@ -255,9 +255,24 @@ export type TAttributes = (string|AttributeMarker|CssSelector)[]; * Constants that are associated with a view. Includes: * - Attribute arrays. * - Local definition arrays. + * - Translated messages (i18n). */ export type TConstants = (TAttributes|string)[]; +/** + * Factory function that returns an array of consts. Consts can be represented as a function in case + * any additional statements are required to define consts in the list. An example is i18n where + * additional i18n calls are generated, which should be executed when consts are requested for the + * first time. + */ +export type TConstantsFactory = () => TConstants; + +/** + * TConstants type that describes how the `consts` field is generated on ComponentDef: it can be + * either an array or a factory function that returns that array. + */ +export type TConstantsOrFactory = TConstants|TConstantsFactory; + /** * Binding data (flyweight) for a particular node that is shared between all templates * of a specific type. diff --git a/packages/core/test/render3/i18n_spec.ts b/packages/core/test/render3/i18n_spec.ts index 249a18ffc7..238a3a0d49 100644 --- a/packages/core/test/render3/i18n_spec.ts +++ b/packages/core/test/render3/i18n_spec.ts @@ -12,6 +12,7 @@ import {getTranslationForTemplate} from '@angular/core/src/render3/i18n/i18n_par import {noop} from '../../../compiler/src/render3/view/util'; import {setDelayProjection, ɵɵelementEnd, ɵɵelementStart} from '../../src/render3/instructions/all'; import {I18nUpdateOpCodes, TI18n, TIcu} from '../../src/render3/interfaces/i18n'; +import {TConstants} from '../../src/render3/interfaces/node'; import {HEADER_OFFSET, LView, TVIEW} from '../../src/render3/interfaces/view'; import {getNativeByIndex} from '../../src/render3/util/view_utils'; @@ -57,26 +58,29 @@ describe('Runtime i18n', () => { }); function prepareFixture( - createTemplate: () => void, updateTemplate: (() => void)|null, nbConsts = 0, - nbVars = 0): TemplateFixture { - return new TemplateFixture(createTemplate, updateTemplate || noop, nbConsts, nbVars); + createTemplate: () => void, updateTemplate: (() => void)|null, nbConsts = 0, nbVars = 0, + consts: TConstants = []): TemplateFixture { + return new TemplateFixture( + createTemplate, updateTemplate || noop, nbConsts, nbVars, null, null, null, undefined, + consts); } function getOpCodes( - createTemplate: () => void, updateTemplate: (() => void)|null, nbConsts: number, - index: number): TI18n|I18nUpdateOpCodes { - const fixture = prepareFixture(createTemplate, updateTemplate, nbConsts); + messageOrAtrs: string|string[], createTemplate: () => void, updateTemplate: (() => void)|null, + nbConsts: number, index: number): TI18n|I18nUpdateOpCodes { + const fixture = + prepareFixture(createTemplate, updateTemplate, nbConsts, undefined, [messageOrAtrs]); const tView = fixture.hostView[TVIEW]; return tView.data[index + HEADER_OFFSET] as TI18n; } describe('i18nStart', () => { it('for text', () => { - const MSG_DIV = `simple text`; + const message = 'simple text'; const nbConsts = 1; const index = 0; - const opCodes = getOpCodes(() => { - ɵɵi18nStart(index, MSG_DIV); + const opCodes = getOpCodes(message, () => { + ɵɵi18nStart(index, 0); }, null, nbConsts, index) as TI18n; expect(opCodes).toEqual({ @@ -91,13 +95,13 @@ describe('Runtime i18n', () => { }); it('for elements', () => { - const MSG_DIV = `Hello �#2�world�/#2� and �#3�universe�/#3�!`; + const message = `Hello �#2�world�/#2� and �#3�universe�/#3�!`; // Template: `
Hello
world
and universe!` // 3 consts for the 2 divs and 1 span + 1 const for `i18nStart` = 4 consts const nbConsts = 4; const index = 1; - const opCodes = getOpCodes(() => { - ɵɵi18nStart(index, MSG_DIV); + const opCodes = getOpCodes(message, () => { + ɵɵi18nStart(index, 0); }, null, nbConsts, index); expect(opCodes).toEqual({ @@ -124,11 +128,11 @@ describe('Runtime i18n', () => { }); it('for simple bindings', () => { - const MSG_DIV = `Hello �0�!`; + const message = `Hello �0�!`; const nbConsts = 2; const index = 1; - const opCodes = getOpCodes(() => { - ɵɵi18nStart(index, MSG_DIV); + const opCodes = getOpCodes(message, () => { + ɵɵi18nStart(index, 0); }, null, nbConsts, index); expect((opCodes as any).update.debug).toEqual([ @@ -148,11 +152,11 @@ describe('Runtime i18n', () => { }); it('for multiple bindings', () => { - const MSG_DIV = `Hello �0� and �1�, again �0�!`; + const message = `Hello �0� and �1�, again �0�!`; const nbConsts = 2; const index = 1; - const opCodes = getOpCodes(() => { - ɵɵi18nStart(index, MSG_DIV); + const opCodes = getOpCodes(message, () => { + ɵɵi18nStart(index, 0); }, null, nbConsts, index); expect(opCodes).toEqual({ @@ -176,17 +180,15 @@ describe('Runtime i18n', () => { // // ! //
- const MSG_DIV = + const message = `�0� is rendered as: �*2:1��#1:1�before�*2:2��#1:2�middle�/#1:2��/*2:2�after�/#1:1��/*2:1�!`; /**** Root template ****/ // �0� is rendered as: �*2:1��/*2:1�! let nbConsts = 3; let index = 1; - const firstTextNode = 3; - const rootTemplate = 2; - let opCodes = getOpCodes(() => { - ɵɵi18nStart(index, MSG_DIV); + let opCodes = getOpCodes(message, () => { + ɵɵi18nStart(index, 0); }, null, nbConsts, index); expect(opCodes).toEqual({ @@ -207,10 +209,8 @@ describe('Runtime i18n', () => { // �#1:1�before�*2:2�middle�/*2:2�after�/#1:1� nbConsts = 3; index = 0; - const spanElement = 1; - const bElementSubTemplate = 2; - opCodes = getOpCodes(() => { - ɵɵi18nStart(index, MSG_DIV, 1); + opCodes = getOpCodes(message, () => { + ɵɵi18nStart(index, 0, 1); }, null, nbConsts, index); expect(opCodes).toEqual({ @@ -233,9 +233,8 @@ describe('Runtime i18n', () => { // middle nbConsts = 2; index = 0; - const bElement = 1; - opCodes = getOpCodes(() => { - ɵɵi18nStart(index, MSG_DIV, 2); + opCodes = getOpCodes(message, () => { + ɵɵi18nStart(index, 0, 2); }, null, nbConsts, index); expect(opCodes).toEqual({ @@ -252,15 +251,15 @@ describe('Runtime i18n', () => { }); it('for ICU expressions', () => { - const MSG_DIV = `{�0�, plural, + const message = `{�0�, plural, =0 {no emails!} =1 {one email} other {�0� emails} }`; const nbConsts = 1; const index = 0; - const opCodes = getOpCodes(() => { - ɵɵi18nStart(index, MSG_DIV); + const opCodes = getOpCodes(message, () => { + ɵɵi18nStart(index, 0); }, null, nbConsts, index) as TI18n; expect(opCodes).toEqual({ @@ -337,7 +336,7 @@ describe('Runtime i18n', () => { }); it('for nested ICU expressions', () => { - const MSG_DIV = `{�0�, plural, + const message = `{�0�, plural, =0 {zero} other {�0� {�1�, select, cat {cats} @@ -347,16 +346,9 @@ describe('Runtime i18n', () => { }`; const nbConsts = 1; const index = 0; - const opCodes = getOpCodes(() => { - ɵɵi18nStart(index, MSG_DIV); + const opCodes = getOpCodes(message, () => { + ɵɵi18nStart(index, 0); }, null, nbConsts, index); - const icuCommentNodeIndex = index + 1; - const firstTextNodeIndex = index + 2; - const nestedIcuCommentNodeIndex = index + 3; - const lastTextNodeIndex = index + 4; - const nestedTextNodeIndex = index + 5; - const tIcuIndex = 1; - const nestedTIcuIndex = 0; expect(opCodes).toEqual({ vars: 9, @@ -443,31 +435,31 @@ describe('Runtime i18n', () => { describe(`i18nAttribute`, () => { it('for text', () => { - const MSG_title = `Hello world!`; - const MSG_div_attr = ['title', MSG_title]; + const message = `Hello world!`; + const attrs = ['title', message]; const nbConsts = 2; const index = 1; const fixture = prepareFixture(() => { ɵɵelementStart(0, 'div'); - ɵɵi18nAttributes(index, MSG_div_attr); + ɵɵi18nAttributes(index, 0); ɵɵelementEnd(); - }, null, nbConsts, index); + }, null, nbConsts, index, [attrs]); const tView = fixture.hostView[TVIEW]; const opCodes = tView.data[index + HEADER_OFFSET] as I18nUpdateOpCodes; expect(opCodes).toEqual([]); expect( (getNativeByIndex(0, fixture.hostView as LView) as any as Element).getAttribute('title')) - .toEqual(MSG_title); + .toEqual(message); }); it('for simple bindings', () => { - const MSG_title = `Hello �0�!`; - const MSG_div_attr = ['title', MSG_title]; + const message = `Hello �0�!`; + const attrs = ['title', message]; const nbConsts = 2; const index = 1; - const opCodes = getOpCodes(() => { - ɵɵi18nAttributes(index, MSG_div_attr); + const opCodes = getOpCodes(attrs, () => { + ɵɵi18nAttributes(index, 0); }, null, nbConsts, index); expect(opCodes).toEqual(debugMatch([ @@ -476,12 +468,12 @@ describe('Runtime i18n', () => { }); it('for multiple bindings', () => { - const MSG_title = `Hello �0� and �1�, again �0�!`; - const MSG_div_attr = ['title', MSG_title]; + const message = `Hello �0� and �1�, again �0�!`; + const attrs = ['title', message]; const nbConsts = 2; const index = 1; - const opCodes = getOpCodes(() => { - ɵɵi18nAttributes(index, MSG_div_attr); + const opCodes = getOpCodes(attrs, () => { + ɵɵi18nAttributes(index, 0); }, null, nbConsts, index); expect(opCodes).toEqual(debugMatch([ @@ -490,12 +482,12 @@ describe('Runtime i18n', () => { }); it('for multiple attributes', () => { - const MSG_title = `Hello �0�!`; - const MSG_div_attr = ['title', MSG_title, 'aria-label', MSG_title]; + const message = `Hello �0�!`; + const attrs = ['title', message, 'aria-label', message]; const nbConsts = 2; const index = 1; - const opCodes = getOpCodes(() => { - ɵɵi18nAttributes(index, MSG_div_attr); + const opCodes = getOpCodes(attrs, () => { + ɵɵi18nAttributes(index, 0); }, null, nbConsts, index); expect(opCodes).toEqual(debugMatch([ @@ -643,4 +635,4 @@ describe('Runtime i18n', () => { .toThrowError(); }); }); -}); +}); \ No newline at end of file