From ea6a2e9f252f052664342d1613ae9fd59f1013c4 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Fri, 6 Sep 2019 12:26:48 +0100 Subject: [PATCH] fix(ivy): template compiler should render correct $localize placeholder names (#32509) The `goog.getMsg()` function requires placeholder names to be camelCased. This is not the case for `$localize`. Here placeholder names need match what is serialized to translation files. Specifically such placeholder names keep their casing but have all characters that are not in `a-z`, `A-Z`, `0-9` and `_` converted to `_`. PR Close #32509 --- .../compliance/r3_view_compiler_i18n_spec.ts | 248 +- .../src/render3/view/i18n/localize_utils.ts | 3 +- .../compiler/src/render3/view/template.ts | 8 +- .../compiler/test/render3/view/i18n_spec.ts | 23 +- packages/core/test/acceptance/i18n_spec.ts | 2325 +++++++++-------- .../acceptance/view_container_ref_spec.ts | 8 +- .../test/bundling/todo_i18n/translations.ts | 8 +- 7 files changed, 1344 insertions(+), 1279 deletions(-) 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 2261a57782..474f09e69f 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 @@ -417,7 +417,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_2$ = $localize \`intro $` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } var $I18N_3$; if (ngI18nClosureMode) { @@ -432,7 +432,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_3$ = $localize \`$` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c1$ = [ "aria-roledescription", $I18N_1$, @@ -453,9 +453,9 @@ describe('i18n support in the template compiler', () => { } else { $I18N_6$ = $localize \`$` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation: and $` + - String.raw `{"\uFFFD1\uFFFD"}:interpolation_1: and again $` + - String.raw `{"\uFFFD2\uFFFD"}:interpolation_2:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: and $` + + String.raw `{"\uFFFD1\uFFFD"}:INTERPOLATION_1: and again $` + + String.raw `{"\uFFFD2\uFFFD"}:INTERPOLATION_2:\`; } var $I18N_7$; if (ngI18nClosureMode) { @@ -466,7 +466,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_7$ = $localize \`$` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c3$ = [ "title", $I18N_6$, @@ -518,7 +518,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_1$ = $localize \`intro $` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c3$ = ["title", $I18N_1$]; … @@ -561,7 +561,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_1$ = $localize \`different scope $` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c2$ = ["title", $I18N_1$]; function MyComponent_div_0_Template(rf, ctx) { @@ -612,7 +612,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_1$ = $localize \`$` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation: title\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: title\`; } const $_c3$ = ["title", $I18N_1$]; … @@ -675,7 +675,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_2$ = $localize \`intro $` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } var $I18N_3$; if (ngI18nClosureMode) { @@ -690,7 +690,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_3$ = $localize \`$` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c1$ = [ "aria-roledescription", $I18N_1$, @@ -711,9 +711,9 @@ describe('i18n support in the template compiler', () => { } else { $I18N_6$ = $localize \`$` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation: and $` + - String.raw `{"\uFFFD1\uFFFD"}:interpolation_1: and again $` + - String.raw `{"\uFFFD2\uFFFD"}:interpolation_2:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: and $` + + String.raw `{"\uFFFD1\uFFFD"}:INTERPOLATION_1: and again $` + + String.raw `{"\uFFFD2\uFFFD"}:INTERPOLATION_2:\`; } var $I18N_7$; if (ngI18nClosureMode) { @@ -724,7 +724,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_7$ = $localize \`$` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c3$ = [ "title", $I18N_6$, @@ -779,7 +779,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_2$ = $localize \`different scope $` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c4$ = ["title", $I18N_2$]; function MyComponent_div_0_Template(rf, ctx) { @@ -1033,8 +1033,8 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` Named interpolation: $` + - String.raw `{"\uFFFD0\uFFFD"}:phA: Named interpolation with spaces: $` + - String.raw `{"\uFFFD1\uFFFD"}:phB: \`; + String.raw `{"\uFFFD0\uFFFD"}:PH_A: Named interpolation with spaces: $` + + String.raw `{"\uFFFD1\uFFFD"}:PH_B: \`; } … consts: 2, @@ -1071,7 +1071,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \`$` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } … template: function MyComponent_Template(rf, ctx) { @@ -1111,9 +1111,9 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` $` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation: $` + - String.raw `{"\uFFFD1\uFFFD"}:interpolation_1: $` + - String.raw `{"\uFFFD2\uFFFD"}:interpolation_2: \`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + + String.raw `{"\uFFFD1\uFFFD"}:INTERPOLATION_1: $` + + String.raw `{"\uFFFD2\uFFFD"}:INTERPOLATION_2: \`; } … template: function MyComponent_Template(rf, ctx) { @@ -1152,7 +1152,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \`My i18n block #$` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } var $I18N_1$; if (ngI18nClosureMode) { @@ -1163,7 +1163,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_1$ = $localize \`My i18n block #$` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } var $I18N_2$; if (ngI18nClosureMode) { @@ -1174,7 +1174,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_2$ = $localize \`My i18n block #$` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } … consts: 7, @@ -1239,9 +1239,9 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` My i18n block #$` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation: $` + - String.raw `{"\uFFFD#2\uFFFD"}:startTagSpan:Plain text in nested element$` + - String.raw `{"\uFFFD/#2\uFFFD"}:closeTagSpan:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + + String.raw `{"\uFFFD#2\uFFFD"}:START_TAG_SPAN:Plain text in nested element$` + + String.raw `{"\uFFFD/#2\uFFFD"}:CLOSE_TAG_SPAN:\`; } var $I18N_1$; if (ngI18nClosureMode) { @@ -1257,14 +1257,14 @@ describe('i18n support in the template compiler', () => { } else { $I18N_1$ = $localize \` My i18n block #$` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation: $` + - String.raw `{"[\uFFFD#6\uFFFD|\uFFFD#7\uFFFD]"}:startTagDiv:$` + - String.raw `{"[\uFFFD#6\uFFFD|\uFFFD#7\uFFFD]"}:startTagDiv:$` + String.raw - `{"\uFFFD#8\uFFFD"}:startTagSpan: More bindings in more nested element: $` + - String.raw `{"\uFFFD1\uFFFD"}:interpolation_1: $` + - String.raw `{"\uFFFD/#8\uFFFD"}:closeTagSpan:$` + - String.raw `{"[\uFFFD/#7\uFFFD|\uFFFD/#6\uFFFD]"}:closeTagDiv:$` + - String.raw `{"[\uFFFD/#7\uFFFD|\uFFFD/#6\uFFFD]"}:closeTagDiv:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + + String.raw `{"[\uFFFD#6\uFFFD|\uFFFD#7\uFFFD]"}:START_TAG_DIV:$` + + String.raw `{"[\uFFFD#6\uFFFD|\uFFFD#7\uFFFD]"}:START_TAG_DIV:$` + String.raw + `{"\uFFFD#8\uFFFD"}:START_TAG_SPAN: More bindings in more nested element: $` + + String.raw `{"\uFFFD1\uFFFD"}:INTERPOLATION_1: $` + + String.raw `{"\uFFFD/#8\uFFFD"}:CLOSE_TAG_SPAN:$` + + String.raw `{"[\uFFFD/#7\uFFFD|\uFFFD/#6\uFFFD]"}:CLOSE_TAG_DIV:$` + + String.raw `{"[\uFFFD/#7\uFFFD|\uFFFD/#6\uFFFD]"}:CLOSE_TAG_DIV:\`; } $I18N_1$ = $r3$.ɵɵi18nPostprocess($I18N_1$); … @@ -1330,8 +1330,8 @@ describe('i18n support in the template compiler', () => { } else { $I18N_2$ = $localize \`Span title $` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation: and $` + - String.raw `{"\uFFFD1\uFFFD"}:interpolation_1:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: and $` + + String.raw `{"\uFFFD1\uFFFD"}:INTERPOLATION_1:\`; } const $_c4$ = ["title", $I18N_2$]; var $I18N_0$; @@ -1345,9 +1345,9 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` My i18n block #1 with value: $` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation: $` + String.raw - `{"\uFFFD#2\uFFFD"}:startTagSpan: Plain text in nested element (block #1) $` + - String.raw `{"\uFFFD/#2\uFFFD"}:closeTagSpan:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + String.raw + `{"\uFFFD#2\uFFFD"}:START_TAG_SPAN: Plain text in nested element (block #1) $` + + String.raw `{"\uFFFD/#2\uFFFD"}:CLOSE_TAG_SPAN:\`; } var $I18N_7$; if (ngI18nClosureMode) { @@ -1358,7 +1358,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_7$ = $localize \`Span title $` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c9$ = ["title", $I18N_7$]; var $I18N_6$; @@ -1372,9 +1372,9 @@ describe('i18n support in the template compiler', () => { } else { $I18N_6$ = $localize \` My i18n block #2 with value $` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation: $` + String.raw - `{"\uFFFD#7\uFFFD"}:startTagSpan: Plain text in nested element (block #2) $` + - String.raw `{"\uFFFD/#7\uFFFD"}:closeTagSpan:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + String.raw + `{"\uFFFD#7\uFFFD"}:START_TAG_SPAN: Plain text in nested element (block #2) $` + + String.raw `{"\uFFFD/#7\uFFFD"}:CLOSE_TAG_SPAN:\`; } … consts: 9, @@ -1444,10 +1444,10 @@ describe('i18n support in the template compiler', () => { } else { $I18N_1$ = $localize \` Some other content $` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation: $` + - String.raw `{"\uFFFD#3\uFFFD"}:startTagDiv: More nested levels with bindings $` + - String.raw `{"\uFFFD1\uFFFD"}:interpolation_1: $` + - String.raw `{"\uFFFD/#3\uFFFD"}:closeTagDiv:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + + String.raw `{"\uFFFD#3\uFFFD"}:START_TAG_DIV: More nested levels with bindings $` + + String.raw `{"\uFFFD1\uFFFD"}:INTERPOLATION_1: $` + + String.raw `{"\uFFFD/#3\uFFFD"}:CLOSE_TAG_DIV:\`; } … function MyComponent_div_2_Template(rf, ctx) { @@ -1514,7 +1514,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_2$ = $localize \`App logo #$` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c4$ = ["title", $I18N_2$]; function MyComponent_img_2_Template(rf, ctx) { @@ -1630,29 +1630,29 @@ describe('i18n support in the template compiler', () => { else { $I18N_0$ = $localize \` Some content $` + String.raw - `{"\uFFFD*2:1\uFFFD\uFFFD#1:1\uFFFD"}:startTagDiv_2: Some other content $` + - String.raw `{"\uFFFD0:1\uFFFD"}:interpolation: $` + String.raw - `{"[\uFFFD#2:1\uFFFD|\uFFFD#2:2\uFFFD|\uFFFD#2:3\uFFFD]"}:startTagDiv: More nested levels with bindings $` + - String.raw `{"\uFFFD1:1\uFFFD"}:interpolation_1: $` + String.raw - `{"\uFFFD*4:2\uFFFD\uFFFD#1:2\uFFFD"}:startTagDiv_1: Content inside sub-template $` + - String.raw `{"\uFFFD0:2\uFFFD"}:interpolation_2: $` + String.raw - `{"[\uFFFD#2:1\uFFFD|\uFFFD#2:2\uFFFD|\uFFFD#2:3\uFFFD]"}:startTagDiv: Bottom level element $` + - String.raw `{"\uFFFD1:2\uFFFD"}:interpolation_3: $` + String.raw - `{"[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]"}:closeTagDiv:$` + + `{"\uFFFD*2:1\uFFFD\uFFFD#1:1\uFFFD"}:START_TAG_DIV_2: Some other content $` + + String.raw `{"\uFFFD0:1\uFFFD"}:INTERPOLATION: $` + String.raw + `{"[\uFFFD#2:1\uFFFD|\uFFFD#2:2\uFFFD|\uFFFD#2:3\uFFFD]"}:START_TAG_DIV: More nested levels with bindings $` + + String.raw `{"\uFFFD1:1\uFFFD"}:INTERPOLATION_1: $` + String.raw + `{"\uFFFD*4:2\uFFFD\uFFFD#1:2\uFFFD"}:START_TAG_DIV_1: Content inside sub-template $` + + String.raw `{"\uFFFD0:2\uFFFD"}:INTERPOLATION_2: $` + String.raw + `{"[\uFFFD#2:1\uFFFD|\uFFFD#2:2\uFFFD|\uFFFD#2:3\uFFFD]"}:START_TAG_DIV: Bottom level element $` + + String.raw `{"\uFFFD1:2\uFFFD"}:INTERPOLATION_3: $` + String.raw + `{"[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]"}:CLOSE_TAG_DIV:$` + String.raw - `{"[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]"}:closeTagDiv:$` + + `{"[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]"}:CLOSE_TAG_DIV:$` + String.raw - `{"[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]"}:closeTagDiv:$` + + `{"[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]"}:CLOSE_TAG_DIV:$` + String.raw - `{"[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]"}:closeTagDiv:$` + + `{"[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]"}:CLOSE_TAG_DIV:$` + String.raw - `{"\uFFFD*3:3\uFFFD\uFFFD#1:3\uFFFD"}:startTagDiv_3: Some other content $` + - String.raw `{"\uFFFD0:3\uFFFD"}:interpolation_4: $` + String.raw - `{"[\uFFFD#2:1\uFFFD|\uFFFD#2:2\uFFFD|\uFFFD#2:3\uFFFD]"}:startTagDiv: More nested levels with bindings $` + - String.raw `{"\uFFFD1:3\uFFFD"}:interpolation_5: $` + String.raw - `{"[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]"}:closeTagDiv:$` + + `{"\uFFFD*3:3\uFFFD\uFFFD#1:3\uFFFD"}:START_TAG_DIV_3: Some other content $` + + String.raw `{"\uFFFD0:3\uFFFD"}:INTERPOLATION_4: $` + String.raw + `{"[\uFFFD#2:1\uFFFD|\uFFFD#2:2\uFFFD|\uFFFD#2:3\uFFFD]"}:START_TAG_DIV: More nested levels with bindings $` + + String.raw `{"\uFFFD1:3\uFFFD"}:INTERPOLATION_5: $` + String.raw + `{"[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]"}:CLOSE_TAG_DIV:$` + String.raw - `{"[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]"}:closeTagDiv:\`; + `{"[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]"}:CLOSE_TAG_DIV:\`; } $I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$); function MyComponent_div_3_Template(rf, ctx) { @@ -1712,9 +1712,9 @@ describe('i18n support in the template compiler', () => { } else { $I18N_1$ = $localize \`Some other content $` + - String.raw `{"\uFFFD#2\uFFFD"}:startTagSpan:$` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:$` + - String.raw `{"\uFFFD/#2\uFFFD"}:closeTagSpan:\`; + String.raw `{"\uFFFD#2\uFFFD"}:START_TAG_SPAN:$` + + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:$` + + String.raw `{"\uFFFD/#2\uFFFD"}:CLOSE_TAG_SPAN:\`; } … function MyComponent_div_0_Template(rf, ctx) { @@ -1945,7 +1945,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \`Some content: $` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } … consts: 3, @@ -1983,7 +1983,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \`Some content: $` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation:\`; + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } function MyComponent_ng_template_0_Template(rf, ctx) { if (rf & 1) { @@ -2031,12 +2031,12 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \`$` + - String.raw `{"\uFFFD*2:1\uFFFD"}:startTagNgTemplate:Template content: $` + - String.raw `{"\uFFFD0:1\uFFFD"}:interpolation:$` + - String.raw `{"\uFFFD/*2:1\uFFFD"}:closeTagNgTemplate:$` + - String.raw `{"\uFFFD#3\uFFFD"}:startTagNgContainer:Container content: $` + - String.raw `{"\uFFFD0\uFFFD"}:interpolation_1:$` + - String.raw `{"\uFFFD/#3\uFFFD"}:closeTagNgContainer:\`; + String.raw `{"\uFFFD*2:1\uFFFD"}:START_TAG_NG_TEMPLATE:Template content: $` + + String.raw `{"\uFFFD0:1\uFFFD"}:INTERPOLATION:$` + + String.raw `{"\uFFFD/*2:1\uFFFD"}:CLOSE_TAG_NG_TEMPLATE:$` + + String.raw `{"\uFFFD#3\uFFFD"}:START_TAG_NG_CONTAINER:Container content: $` + + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION_1:$` + + String.raw `{"\uFFFD/#3\uFFFD"}:CLOSE_TAG_NG_CONTAINER:\`; } function MyComponent_ng_template_2_Template(rf, ctx) { if (rf & 1) { @@ -2185,17 +2185,17 @@ describe('i18n support in the template compiler', () => { else { $I18N_0$ = $localize \`$` + String.raw - `{"[\uFFFD*2:1\uFFFD|\uFFFD*2:2\uFFFD|\uFFFD*1:3\uFFFD]"}:startTagNgTemplate: Template A: $` + - String.raw `{"\uFFFD0:1\uFFFD"}:interpolation: $` + String.raw - `{"[\uFFFD*2:1\uFFFD|\uFFFD*2:2\uFFFD|\uFFFD*1:3\uFFFD]"}:startTagNgTemplate: Template B: $` + - String.raw `{"\uFFFD0:2\uFFFD"}:interpolation_1: $` + String.raw - `{"[\uFFFD*2:1\uFFFD|\uFFFD*2:2\uFFFD|\uFFFD*1:3\uFFFD]"}:startTagNgTemplate: Template C: $` + - String.raw `{"\uFFFD0:3\uFFFD"}:interpolation_2: $` + String.raw - `{"[\uFFFD/*1:3\uFFFD|\uFFFD/*2:2\uFFFD|\uFFFD/*2:1\uFFFD]"}:closeTagNgTemplate:$` + + `{"[\uFFFD*2:1\uFFFD|\uFFFD*2:2\uFFFD|\uFFFD*1:3\uFFFD]"}:START_TAG_NG_TEMPLATE: Template A: $` + + String.raw `{"\uFFFD0:1\uFFFD"}:INTERPOLATION: $` + String.raw + `{"[\uFFFD*2:1\uFFFD|\uFFFD*2:2\uFFFD|\uFFFD*1:3\uFFFD]"}:START_TAG_NG_TEMPLATE: Template B: $` + + String.raw `{"\uFFFD0:2\uFFFD"}:INTERPOLATION_1: $` + String.raw + `{"[\uFFFD*2:1\uFFFD|\uFFFD*2:2\uFFFD|\uFFFD*1:3\uFFFD]"}:START_TAG_NG_TEMPLATE: Template C: $` + + String.raw `{"\uFFFD0:3\uFFFD"}:INTERPOLATION_2: $` + String.raw + `{"[\uFFFD/*1:3\uFFFD|\uFFFD/*2:2\uFFFD|\uFFFD/*2:1\uFFFD]"}:CLOSE_TAG_NG_TEMPLATE:$` + String.raw - `{"[\uFFFD/*1:3\uFFFD|\uFFFD/*2:2\uFFFD|\uFFFD/*2:1\uFFFD]"}:closeTagNgTemplate:$` + + `{"[\uFFFD/*1:3\uFFFD|\uFFFD/*2:2\uFFFD|\uFFFD/*2:1\uFFFD]"}:CLOSE_TAG_NG_TEMPLATE:$` + String.raw - `{"[\uFFFD/*1:3\uFFFD|\uFFFD/*2:2\uFFFD|\uFFFD/*2:1\uFFFD]"}:closeTagNgTemplate:\`; + `{"[\uFFFD/*1:3\uFFFD|\uFFFD/*2:2\uFFFD|\uFFFD/*2:1\uFFFD]"}:CLOSE_TAG_NG_TEMPLATE:\`; } $I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$); function MyComponent_ng_template_2_Template(rf, ctx) { @@ -2309,7 +2309,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \`$` + - String.raw `{"\uFFFD#2\uFFFD\uFFFD/#2\uFFFD"}:tagImg: is my logo #1 \`; + String.raw `{"\uFFFD#2\uFFFD\uFFFD/#2\uFFFD"}:TAG_IMG: is my logo #1 \`; } var $I18N_2$; if (ngI18nClosureMode) { @@ -2320,7 +2320,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_2$ = $localize \`$` + - String.raw `{"\uFFFD#1\uFFFD\uFFFD/#1\uFFFD"}:tagImg: is my logo #2 \`; + String.raw `{"\uFFFD#1\uFFFD\uFFFD/#1\uFFFD"}:TAG_IMG: is my logo #2 \`; } function MyComponent_ng_template_3_Template(rf, ctx) { if (rf & 1) { @@ -2367,8 +2367,8 @@ describe('i18n support in the template compiler', () => { else { $I18N_0$ = $localize \` Root content $` + String.raw - `{"\uFFFD*1:1\uFFFD\uFFFD#1:1\uFFFD"}:startTagNgContainer: Nested content $` + - String.raw `{"\uFFFD/#1:1\uFFFD\uFFFD/*1:1\uFFFD"}:closeTagNgContainer:\`; + `{"\uFFFD*1:1\uFFFD\uFFFD#1:1\uFFFD"}:START_TAG_NG_CONTAINER: Nested content $` + + String.raw `{"\uFFFD/#1:1\uFFFD\uFFFD/*1:1\uFFFD"}:CLOSE_TAG_NG_CONTAINER:\`; } … `; @@ -2423,8 +2423,8 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` Hello $` + - String.raw `{"\uFFFD#2\uFFFD"}:startTagNgContainer:there$` + - String.raw `{"\uFFFD/#2\uFFFD"}:closeTagNgContainer:\`; + String.raw `{"\uFFFD#2\uFFFD"}:START_TAG_NG_CONTAINER:there$` + + String.raw `{"\uFFFD/#2\uFFFD"}:CLOSE_TAG_NG_CONTAINER:\`; } … consts: 3, @@ -2459,10 +2459,10 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` Hello $` + - String.raw `{"\uFFFD#2\uFFFD"}:startTagNgContainer:there $` + - String.raw `{"\uFFFD#3\uFFFD"}:startTagStrong:!$` + - String.raw `{"\uFFFD/#3\uFFFD"}:closeTagStrong:$` + - String.raw `{"\uFFFD/#2\uFFFD"}:closeTagNgContainer:\`; + String.raw `{"\uFFFD#2\uFFFD"}:START_TAG_NG_CONTAINER:there $` + + String.raw `{"\uFFFD#3\uFFFD"}:START_TAG_STRONG:!$` + + String.raw `{"\uFFFD/#3\uFFFD"}:CLOSE_TAG_STRONG:$` + + String.raw `{"\uFFFD/#2\uFFFD"}:CLOSE_TAG_NG_CONTAINER:\`; } … consts: 4, @@ -2505,8 +2505,8 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \`\n Some text\n $` + - String.raw `{"\uFFFD#3\uFFFD"}:startTagSpan:Text inside span$` + - String.raw `{"\uFFFD/#3\uFFFD"}:closeTagSpan:\n \`; + String.raw `{"\uFFFD#3\uFFFD"}:START_TAG_SPAN:Text inside span$` + + String.raw `{"\uFFFD/#3\uFFFD"}:CLOSE_TAG_SPAN:\n \`; } … template: function MyComponent_Template(rf, ctx) { @@ -2805,13 +2805,13 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` $` + - String.raw `{$I18N_1$}:icu: $` + - String.raw `{"\uFFFD#2\uFFFD"}:startBoldText:Other content$` + - String.raw `{"\uFFFD/#2\uFFFD"}:closeBoldText:$` + - String.raw `{"\uFFFD#3\uFFFD"}:startTagDiv:$` + - String.raw `{"\uFFFD#4\uFFFD"}:startItalicText:Another content$` + - String.raw `{"\uFFFD/#4\uFFFD"}:closeItalicText:$` + - String.raw `{"\uFFFD/#3\uFFFD"}:closeTagDiv:\`; + String.raw `{$I18N_1$}:ICU: $` + + String.raw `{"\uFFFD#2\uFFFD"}:START_BOLD_TEXT:Other content$` + + String.raw `{"\uFFFD/#2\uFFFD"}:CLOSE_BOLD_TEXT:$` + + String.raw `{"\uFFFD#3\uFFFD"}:START_TAG_DIV:$` + + String.raw `{"\uFFFD#4\uFFFD"}:START_ITALIC_TEXT:Another content$` + + String.raw `{"\uFFFD/#4\uFFFD"}:CLOSE_ITALIC_TEXT:$` + + String.raw `{"\uFFFD/#3\uFFFD"}:CLOSE_TAG_DIV:\`; } … consts: 5, @@ -2917,7 +2917,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` $` + - String.raw `{$I18N_1$}:icu: $` + String.raw `{$I18N_2$}:icu_1: \`; + String.raw `{$I18N_1$}:ICU: $` + String.raw `{$I18N_2$}:ICU_1: \`; } … consts: 2, @@ -2999,13 +2999,13 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` $` + - String.raw `{"\uFFFDI18N_EXP_ICU\uFFFD"}:icu: $` + - String.raw `{"\uFFFD#2\uFFFD"}:startTagDiv: $` + - String.raw `{"\uFFFDI18N_EXP_ICU\uFFFD"}:icu: $` + String.raw - `{"[\uFFFD/#2\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*3:1\uFFFD]"}:closeTagDiv:$` + - String.raw `{"\uFFFD*3:1\uFFFD\uFFFD#1:1\uFFFD"}:startTagDiv_1: $` + - String.raw `{"\uFFFDI18N_EXP_ICU\uFFFD"}:icu: $` + String.raw - `{"[\uFFFD/#2\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*3:1\uFFFD]"}:closeTagDiv:\`; + String.raw `{"\uFFFDI18N_EXP_ICU\uFFFD"}:ICU: $` + + String.raw `{"\uFFFD#2\uFFFD"}:START_TAG_DIV: $` + + String.raw `{"\uFFFDI18N_EXP_ICU\uFFFD"}:ICU: $` + String.raw + `{"[\uFFFD/#2\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*3:1\uFFFD]"}:CLOSE_TAG_DIV:$` + + String.raw `{"\uFFFD*3:1\uFFFD\uFFFD#1:1\uFFFD"}:START_TAG_DIV_1: $` + + String.raw `{"\uFFFDI18N_EXP_ICU\uFFFD"}:ICU: $` + String.raw + `{"[\uFFFD/#2\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*3:1\uFFFD]"}:CLOSE_TAG_DIV:\`; } $I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$, { "ICU": [$I18N_1$, $I18N_2$, $I18N_4$] @@ -3080,7 +3080,7 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` $` + - String.raw `{$I18N_1$}:icu: \`; + String.raw `{$I18N_1$}:ICU: \`; } … consts: 2, vars: 2, @@ -3196,10 +3196,10 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` $` + - String.raw `{$I18N_1$}:icu: $` + - String.raw `{"\uFFFD*2:1\uFFFD\uFFFD#1:1\uFFFD"}:startTagSpan: $` + - String.raw `{$I18N_3$}:icu_1: $` + - String.raw `{"\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD"}:closeTagSpan:\`; + String.raw `{$I18N_1$}:ICU: $` + + String.raw `{"\uFFFD*2:1\uFFFD\uFFFD#1:1\uFFFD"}:START_TAG_SPAN: $` + + String.raw `{$I18N_3$}:ICU_1: $` + + String.raw `{"\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD"}:CLOSE_TAG_SPAN:\`; } function MyComponent_span_2_Template(rf, ctx) { if (rf & 1) { @@ -3285,10 +3285,10 @@ describe('i18n support in the template compiler', () => { } else { $I18N_0$ = $localize \` $` + - String.raw `{I18N_1}:icu: $` + - String.raw `{"\uFFFD*2:1\uFFFD\uFFFD#1:1\uFFFD"}:startTagSpan: $` + - String.raw `{I18N_4}:icu_1: $` + - String.raw `{"\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD"}:closeTagSpan:\`; + String.raw `{I18N_1}:ICU: $` + + String.raw `{"\uFFFD*2:1\uFFFD\uFFFD#1:1\uFFFD"}:START_TAG_SPAN: $` + + String.raw `{I18N_4}:ICU_1: $` + + String.raw `{"\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD"}:CLOSE_TAG_SPAN:\`; } function MyComponent_span_2_Template(rf, ctx) { if (rf & 1) { diff --git a/packages/compiler/src/render3/view/i18n/localize_utils.ts b/packages/compiler/src/render3/view/i18n/localize_utils.ts index ff1f15bd5c..b89323acf5 100644 --- a/packages/compiler/src/render3/view/i18n/localize_utils.ts +++ b/packages/compiler/src/render3/view/i18n/localize_utils.ts @@ -9,7 +9,6 @@ import * as i18n from '../../../i18n/i18n_ast'; import * as o from '../../../output/output_ast'; import {serializeIcuNode} from './icu_serializer'; -import {i18nMetaToDocStmt, metaFromI18nMessage} from './meta'; import {formatI18nPlaceholderName} from './util'; export function createLocalizeStatements( @@ -37,7 +36,7 @@ class MessagePiece { } class LiteralPiece extends MessagePiece {} class PlaceholderPiece extends MessagePiece { - constructor(name: string) { super(formatI18nPlaceholderName(name)); } + constructor(name: string) { super(formatI18nPlaceholderName(name, /* useCamelCase */ false)); } } /** diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts index af1c151d6a..cfc9e7ada5 100644 --- a/packages/compiler/src/render3/view/template.ts +++ b/packages/compiler/src/render3/view/template.ts @@ -2021,13 +2021,15 @@ export function getTranslationDeclStmts( message: i18n.Message, variable: o.ReadVarExpr, closureVar: o.ReadVarExpr, params: {[name: string]: o.Expression} = {}, transformFn?: (raw: o.ReadVarExpr) => o.Expression): o.Statement[] { - const formattedParams = i18nFormatPlaceholderNames(params, /* useCamelCase */ true); const statements: o.Statement[] = [ declareI18nVariable(variable), o.ifStmt( o.variable(NG_I18N_CLOSURE_MODE), - createGoogleGetMsgStatements(variable, message, closureVar, formattedParams), - createLocalizeStatements(variable, message, formattedParams)), + createGoogleGetMsgStatements( + variable, message, closureVar, + i18nFormatPlaceholderNames(params, /* useCamelCase */ true)), + createLocalizeStatements( + variable, message, i18nFormatPlaceholderNames(params, /* useCamelCase */ false))), ]; if (transformFn) { diff --git a/packages/compiler/test/render3/view/i18n_spec.ts b/packages/compiler/test/render3/view/i18n_spec.ts index 2eb1a3dd19..a92d92e09e 100644 --- a/packages/compiler/test/render3/view/i18n_spec.ts +++ b/packages/compiler/test/render3/view/i18n_spec.ts @@ -232,6 +232,11 @@ describe('serializeI18nMessageForGetMsg', () => { .toEqual('Some text {$interpolation} and {$interpolation_1}'); }); + it('should serialize interpolation with named placeholder for `GetMsg()`', () => { + expect(serialize('{{ valueB + valueC // i18n(ph="PLACEHOLDER NAME") }}')) + .toEqual('{$placeholderName}'); + }); + it('should serialize content with HTML tags for `GetMsg()`', () => { expect(serialize('A B
C
D')) .toEqual('A {$startTagSpan}B{$startTagDiv}C{$closeTagDiv}{$closeTagSpan} D'); @@ -277,14 +282,14 @@ describe('serializeI18nMessageForLocalize', () => { it('should serialize text with interpolation for `$localize()`', () => { expect(serialize('Some text {{ valueA }} and {{ valueB + valueC }} done')).toEqual({ messageParts: ['Some text ', ' and ', ' done'], - placeHolders: ['interpolation', 'interpolation_1'] + placeHolders: ['INTERPOLATION', 'INTERPOLATION_1'] }); }); it('should serialize text with interpolation at start for `$localize()`', () => { expect(serialize('{{ valueA }} and {{ valueB + valueC }} done')).toEqual({ messageParts: ['', ' and ', ' done'], - placeHolders: ['interpolation', 'interpolation_1'] + placeHolders: ['INTERPOLATION', 'INTERPOLATION_1'] }); }); @@ -292,21 +297,27 @@ describe('serializeI18nMessageForLocalize', () => { it('should serialize text with interpolation at end for `$localize()`', () => { expect(serialize('Some text {{ valueA }} and {{ valueB + valueC }}')).toEqual({ messageParts: ['Some text ', ' and ', ''], - placeHolders: ['interpolation', 'interpolation_1'] + placeHolders: ['INTERPOLATION', 'INTERPOLATION_1'] }); }); it('should serialize only interpolation for `$localize()`', () => { expect(serialize('{{ valueB + valueC }}')) - .toEqual({messageParts: ['', ''], placeHolders: ['interpolation']}); + .toEqual({messageParts: ['', ''], placeHolders: ['INTERPOLATION']}); + }); + + + it('should serialize interpolation with named placeholder for `$localize()`', () => { + expect(serialize('{{ valueB + valueC // i18n(ph="PLACEHOLDER NAME") }}')) + .toEqual({messageParts: ['', ''], placeHolders: ['PLACEHOLDER_NAME']}); }); it('should serialize content with HTML tags for `$localize()`', () => { expect(serialize('A B
C
D')).toEqual({ messageParts: ['A ', 'B', 'C', '', ' D'], - placeHolders: ['startTagSpan', 'startTagDiv', 'closeTagDiv', 'closeTagSpan'] + placeHolders: ['START_TAG_SPAN', 'START_TAG_DIV', 'CLOSE_TAG_DIV', 'CLOSE_TAG_SPAN'] }); }); @@ -346,7 +357,7 @@ describe('serializeI18nMessageForLocalize', () => { '{gender, select, male {male} female {female} other {other}}
{gender, select, male {male} female {female} other {other}}
')) .toEqual({ messageParts: ['', '', '', '', ''], - placeHolders: ['icu', 'startTagDiv', 'icu', 'closeTagDiv'] + placeHolders: ['ICU', 'START_TAG_DIV', 'ICU', 'CLOSE_TAG_DIV'] }); }); }); diff --git a/packages/core/test/acceptance/i18n_spec.ts b/packages/core/test/acceptance/i18n_spec.ts index fccbdf8519..41dca47aea 100644 --- a/packages/core/test/acceptance/i18n_spec.ts +++ b/packages/core/test/acceptance/i18n_spec.ts @@ -17,149 +17,155 @@ import {expect} from '@angular/platform-browser/testing/src/matchers'; import {onlyInIvy} from '@angular/private/testing'; -onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { - beforeEach(() => { - TestBed.configureTestingModule({declarations: [AppComp, DirectiveWithTplRef, UppercasePipe]}); - }); +onlyInIvy('Ivy i18n logic') + .describe( + 'runtime i18n', () => { + beforeEach(() => { + TestBed.configureTestingModule( + {declarations: [AppComp, DirectiveWithTplRef, UppercasePipe]}); + }); - afterEach(() => { setDelayProjection(false); }); + afterEach(() => { setDelayProjection(false); }); - it('should translate text', () => { - loadTranslations({'text': 'texte'}); - const fixture = initWithTemplate(AppComp, `
text
`); - expect(fixture.nativeElement.innerHTML).toEqual(`
texte
`); - }); + it('should translate text', () => { + loadTranslations({'text': 'texte'}); + const fixture = initWithTemplate(AppComp, `
text
`); + expect(fixture.nativeElement.innerHTML).toEqual(`
texte
`); + }); - it('should support interpolations', () => { - loadTranslations({'Hello {$interpolation}!': 'Bonjour {$interpolation}!'}); - const fixture = initWithTemplate(AppComp, `
Hello {{name}}!
`); - expect(fixture.nativeElement.innerHTML).toEqual(`
Bonjour Angular!
`); - fixture.componentRef.instance.name = `John`; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual(`
Bonjour John!
`); - }); + it('should support interpolations', () => { + loadTranslations({'Hello {$INTERPOLATION}!': 'Bonjour {$INTERPOLATION}!'}); + const fixture = initWithTemplate(AppComp, `
Hello {{name}}!
`); + expect(fixture.nativeElement.innerHTML).toEqual(`
Bonjour Angular!
`); + fixture.componentRef.instance.name = `John`; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual(`
Bonjour John!
`); + }); - it('should support named interpolations', () => { - loadTranslations({ - ' Hello {$userName}! Emails: {$amountOfEmailsReceived} ': - ' Bonjour {$userName}! Emails: {$amountOfEmailsReceived} ' - }); - const fixture = initWithTemplate(AppComp, ` + it('should support named interpolations', () => { + loadTranslations({ + ' Hello {$USER_NAME}! Emails: {$AMOUNT_OF_EMAILS_RECEIVED} ': + ' Bonjour {$USER_NAME}! Emails: {$AMOUNT_OF_EMAILS_RECEIVED} ' + }); + const fixture = initWithTemplate(AppComp, `
Hello {{ name // i18n(ph="user_name") }}! Emails: {{ count // i18n(ph="amount of emails received") }}
`); - expect(fixture.nativeElement.innerHTML).toEqual(`
Bonjour Angular! Emails: 0
`); - fixture.componentRef.instance.name = `John`; - fixture.componentRef.instance.count = 5; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual(`
Bonjour John! Emails: 5
`); - }); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
Bonjour Angular! Emails: 0
`); + fixture.componentRef.instance.name = `John`; + fixture.componentRef.instance.count = 5; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual(`
Bonjour John! Emails: 5
`); + }); - it('should support interpolations with custom interpolation config', () => { - loadTranslations({'Hello {$interpolation}': 'Bonjour {$interpolation}'}); - const interpolation = ['{%', '%}'] as[string, string]; - TestBed.overrideComponent(AppComp, {set: {interpolation}}); - const fixture = initWithTemplate(AppComp, `
Hello {% name %}
`); + it('should support interpolations with custom interpolation config', () => { + loadTranslations({'Hello {$INTERPOLATION}': 'Bonjour {$INTERPOLATION}'}); + const interpolation = ['{%', '%}'] as[string, string]; + TestBed.overrideComponent(AppComp, {set: {interpolation}}); + const fixture = initWithTemplate(AppComp, `
Hello {% name %}
`); - expect(fixture.nativeElement.innerHTML).toBe('
Bonjour Angular
'); - }); + expect(fixture.nativeElement.innerHTML).toBe('
Bonjour Angular
'); + }); - it('should support &ngsp; in translatable sections', () => { - // note: the `` unicode symbol represents the `&ngsp;` in translations - loadTranslations({'text ||': 'texte ||'}); - const fixture = initWithTemplate(AppCompWithWhitespaces, `
text |&ngsp;|
`); + it('should support &ngsp; in translatable sections', () => { + // note: the `` unicode symbol represents the `&ngsp;` in translations + loadTranslations({'text ||': 'texte ||'}); + const fixture = + initWithTemplate(AppCompWithWhitespaces, `
text |&ngsp;|
`); - expect(fixture.nativeElement.innerHTML).toEqual(`
texte | |
`); - }); + expect(fixture.nativeElement.innerHTML).toEqual(`
texte | |
`); + }); - it('should support interpolations with complex expressions', () => { - loadTranslations({ - ' {$interpolation} - {$interpolation_1} - {$interpolation_2} ': - ' {$interpolation} - {$interpolation_1} - {$interpolation_2} (fr) ' - }); - const fixture = initWithTemplate(AppComp, ` + it('should support interpolations with complex expressions', () => { + loadTranslations({ + ' {$INTERPOLATION} - {$INTERPOLATION_1} - {$INTERPOLATION_2} ': + ' {$INTERPOLATION} - {$INTERPOLATION_1} - {$INTERPOLATION_2} (fr) ' + }); + const fixture = initWithTemplate(AppComp, `
{{ name | uppercase }} - {{ obj?.a?.b }} - {{ obj?.getA()?.b }}
`); - // the `obj` field is not yet defined, so 2nd and 3rd interpolations return empty strings - expect(fixture.nativeElement.innerHTML).toEqual(`
ANGULAR - - (fr)
`); + // the `obj` field is not yet defined, so 2nd and 3rd interpolations return empty + // strings + expect(fixture.nativeElement.innerHTML).toEqual(`
ANGULAR - - (fr)
`); - fixture.componentRef.instance.obj = { - a: {b: 'value 1'}, - getA: () => ({b: 'value 2'}), - }; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual(`
ANGULAR - value 1 - value 2 (fr)
`); - }); + fixture.componentRef.instance.obj = { + a: {b: 'value 1'}, + getA: () => ({b: 'value 2'}), + }; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
ANGULAR - value 1 - value 2 (fr)
`); + }); - it('should support elements', () => { - loadTranslations({ - 'Hello {$startTagSpan}world{$closeTagSpan} and {$startTagDiv}universe{$closeTagDiv}!': - 'Bonjour {$startTagSpan}monde{$closeTagSpan} et {$startTagDiv}univers{$closeTagDiv}!' - }); - const fixture = initWithTemplate( - AppComp, `
Hello world and
universe
!
`); - expect(fixture.nativeElement.innerHTML) - .toEqual(`
Bonjour monde et
univers
!
`); - }); + it('should support elements', () => { + loadTranslations({ + 'Hello {$START_TAG_SPAN}world{$CLOSE_TAG_SPAN} and {$START_TAG_DIV}universe{$CLOSE_TAG_DIV}!': + 'Bonjour {$START_TAG_SPAN}monde{$CLOSE_TAG_SPAN} et {$START_TAG_DIV}univers{$CLOSE_TAG_DIV}!' + }); + const fixture = initWithTemplate( + AppComp, `
Hello world and
universe
!
`); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
Bonjour monde et
univers
!
`); + }); - it('should support removing elements', () => { - loadTranslations({ - 'Hello {$startBoldText}my{$closeBoldText}{$startTagSpan}world{$closeTagSpan}': - 'Bonjour {$startTagSpan}monde{$closeTagSpan}' - }); - const fixture = - initWithTemplate(AppComp, `
Hello myworld
!
`); - expect(fixture.nativeElement.innerHTML) - .toEqual(`
Bonjour monde
!
`); - }); + it('should support removing elements', () => { + loadTranslations({ + 'Hello {$START_BOLD_TEXT}my{$CLOSE_BOLD_TEXT}{$START_TAG_SPAN}world{$CLOSE_TAG_SPAN}': + 'Bonjour {$START_TAG_SPAN}monde{$CLOSE_TAG_SPAN}' + }); + const fixture = initWithTemplate( + AppComp, `
Hello myworld
!
`); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
Bonjour monde
!
`); + }); - it('should support moving elements', () => { - loadTranslations({ - 'Hello {$startTagSpan}world{$closeTagSpan} and {$startTagDiv}universe{$closeTagDiv}!': - 'Bonjour {$startTagDiv}univers{$closeTagDiv} et {$startTagSpan}monde{$closeTagSpan}!' - }); - const fixture = initWithTemplate( - AppComp, `
Hello world and
universe
!
`); - expect(fixture.nativeElement.innerHTML) - .toEqual(`
Bonjour
univers
et monde!
`); - }); + it('should support moving elements', () => { + loadTranslations({ + 'Hello {$START_TAG_SPAN}world{$CLOSE_TAG_SPAN} and {$START_TAG_DIV}universe{$CLOSE_TAG_DIV}!': + 'Bonjour {$START_TAG_DIV}univers{$CLOSE_TAG_DIV} et {$START_TAG_SPAN}monde{$CLOSE_TAG_SPAN}!' + }); + const fixture = initWithTemplate( + AppComp, `
Hello world and
universe
!
`); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
Bonjour
univers
et monde!
`); + }); - it('should support template directives', () => { - loadTranslations({ - 'Content: {$startTagDiv}before{$startTagSpan}middle{$closeTagSpan}after{$closeTagDiv}!': - 'Contenu: {$startTagDiv}avant{$startTagSpan}milieu{$closeTagSpan}après{$closeTagDiv}!' - }); - const fixture = initWithTemplate( - AppComp, - `
Content:
beforemiddleafter
!
`); - expect(fixture.nativeElement.innerHTML) - .toEqual(`
Contenu:
avantmilieuaprès
!
`); - fixture.componentRef.instance.visible = false; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual(`
Contenu: !
`); - }); + }); - it('should support multiple i18n blocks', () => { - loadTranslations({ - 'trad {$interpolation}': 'traduction {$interpolation}', - 'start {$interpolation} middle {$interpolation_1} end': - 'start {$interpolation_1} middle {$interpolation} end', - '{$startTagC}trad{$closeTagC}{$startTagD}{$closeTagD}{$startTagE}{$closeTagE}': - '{$startTagE}{$closeTagE}{$startTagC}traduction{$closeTagC}' - }); - const fixture = initWithTemplate(AppComp, ` + it('should support multiple i18n blocks', () => { + loadTranslations({ + 'trad {$INTERPOLATION}': 'traduction {$INTERPOLATION}', + 'start {$INTERPOLATION} middle {$INTERPOLATION_1} end': + 'start {$INTERPOLATION_1} middle {$INTERPOLATION} end', + '{$START_TAG_C}trad{$CLOSE_TAG_C}{$START_TAG_D}{$CLOSE_TAG_D}{$START_TAG_E}{$CLOSE_TAG_E}': + '{$START_TAG_E}{$CLOSE_TAG_E}{$START_TAG_C}traduction{$CLOSE_TAG_C}' + }); + const fixture = initWithTemplate(AppComp, `
trad {{name}} hello @@ -169,132 +175,137 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
`); - expect(fixture.nativeElement.innerHTML) - .toEqual( - `
traduction Angular hello traduction
`); - }); + expect(fixture.nativeElement.innerHTML) + .toEqual( + `
traduction Angular hello traduction
`); + }); - it('should support multiple sibling i18n blocks', () => { - loadTranslations({ - 'Section 1': 'Section un', - 'Section 2': 'Section deux', - 'Section 3': 'Section trois', - }); - const fixture = initWithTemplate(AppComp, ` + it('should support multiple sibling i18n blocks', () => { + loadTranslations({ + 'Section 1': 'Section un', + 'Section 2': 'Section deux', + 'Section 3': 'Section trois', + }); + const fixture = initWithTemplate(AppComp, `
Section 1
Section 2
Section 3
`); - expect(fixture.nativeElement.innerHTML) - .toEqual(`
Section un
Section deux
Section trois
`); - }); + expect(fixture.nativeElement.innerHTML) + .toEqual( + `
Section un
Section deux
Section trois
`); + }); - it('should support multiple sibling i18n blocks inside of a template directive', () => { - loadTranslations({ - 'Section 1': 'Section un', - 'Section 2': 'Section deux', - 'Section 3': 'Section trois', - }); - const fixture = initWithTemplate(AppComp, ` + it('should support multiple sibling i18n blocks inside of a template directive', () => { + loadTranslations({ + 'Section 1': 'Section un', + 'Section 2': 'Section deux', + 'Section 3': 'Section trois', + }); + const fixture = initWithTemplate(AppComp, ` `); - expect(fixture.nativeElement.innerHTML) - .toEqual( - ``); - }); + }); - it('should properly escape quotes in content', () => { - loadTranslations({ - '\'Single quotes\' and "Double quotes"': '\'Guillemets simples\' et "Guillemets doubles"' - }); - const fixture = - initWithTemplate(AppComp, `
'Single quotes' and "Double quotes"
`); + it('should properly escape quotes in content', () => { + loadTranslations({ + '\'Single quotes\' and "Double quotes"': + '\'Guillemets simples\' et "Guillemets doubles"' + }); + const fixture = + initWithTemplate(AppComp, `
'Single quotes' and "Double quotes"
`); - expect(fixture.nativeElement.innerHTML) - .toEqual('
\'Guillemets simples\' et "Guillemets doubles"
'); - }); + expect(fixture.nativeElement.innerHTML) + .toEqual('
\'Guillemets simples\' et "Guillemets doubles"
'); + }); - it('should correctly bind to context in nested template', () => { - loadTranslations({'Item {$interpolation}': 'Article {$interpolation}'}); - const fixture = initWithTemplate(AppComp, ` + it('should correctly bind to context in nested template', () => { + loadTranslations({'Item {$INTERPOLATION}': 'Article {$INTERPOLATION}'}); + const fixture = initWithTemplate(AppComp, `
Item {{ id }}
`); - const element = fixture.nativeElement; - for (let i = 0; i < element.children.length; i++) { - const child = element.children[i]; - expect(child).toHaveText(`Article ${i + 1}`); - } - }); + const element = fixture.nativeElement; + for (let i = 0; i < element.children.length; i++) { + const child = element.children[i]; + expect(child).toHaveText(`Article ${i + 1}`); + } + }); - it('should ignore i18n attributes on self-closing tags', () => { - const fixture = initWithTemplate(AppComp, ''); - expect(fixture.nativeElement.innerHTML).toBe(``); - }); + it('should ignore i18n attributes on self-closing tags', () => { + const fixture = initWithTemplate(AppComp, ''); + expect(fixture.nativeElement.innerHTML).toBe(``); + }); - it('should handle i18n attribute with directives', () => { - loadTranslations({'Hello {$interpolation}': 'Bonjour {$interpolation}'}); - const fixture = initWithTemplate(AppComp, `
Hello {{ name }}
`); - expect(fixture.nativeElement.firstChild).toHaveText('Bonjour Angular'); - }); + it('should handle i18n attribute with directives', () => { + loadTranslations({'Hello {$INTERPOLATION}': 'Bonjour {$INTERPOLATION}'}); + const fixture = + initWithTemplate(AppComp, `
Hello {{ name }}
`); + expect(fixture.nativeElement.firstChild).toHaveText('Bonjour Angular'); + }); - it('should work correctly with event listeners', () => { - loadTranslations({'Hello {$interpolation}': 'Bonjour {$interpolation}'}); + it('should work correctly with event listeners', () => { + loadTranslations({'Hello {$INTERPOLATION}': 'Bonjour {$INTERPOLATION}'}); - @Component( - {selector: 'app-comp', template: `
Hello {{ name }}
`}) - class ListenerComp { - name = `Angular`; - clicks = 0; + @Component({ + selector: 'app-comp', + template: `
Hello {{ name }}
` + }) + class ListenerComp { + name = `Angular`; + clicks = 0; - onClick() { this.clicks++; } - } + onClick() { this.clicks++; } + } - TestBed.configureTestingModule({declarations: [ListenerComp]}); - const fixture = TestBed.createComponent(ListenerComp); - fixture.detectChanges(); + TestBed.configureTestingModule({declarations: [ListenerComp]}); + const fixture = TestBed.createComponent(ListenerComp); + fixture.detectChanges(); - const element = fixture.nativeElement.firstChild; - const instance = fixture.componentInstance; + const element = fixture.nativeElement.firstChild; + const instance = fixture.componentInstance; - expect(element).toHaveText('Bonjour Angular'); - expect(instance.clicks).toBe(0); + expect(element).toHaveText('Bonjour Angular'); + expect(instance.clicks).toBe(0); - element.click(); - expect(instance.clicks).toBe(1); - }); + element.click(); + expect(instance.clicks).toBe(1); + }); - describe('ng-container and ng-template support', () => { - it('should support ng-container', () => { - loadTranslations({'text': 'texte'}); - const fixture = initWithTemplate(AppComp, `text`); - expect(fixture.nativeElement.innerHTML).toEqual(`texte`); - }); + describe('ng-container and ng-template support', () => { + it('should support ng-container', () => { + loadTranslations({'text': 'texte'}); + const fixture = initWithTemplate(AppComp, `text`); + expect(fixture.nativeElement.innerHTML).toEqual(`texte`); + }); - it('should handle single translation message within ng-template', () => { - loadTranslations({'Hello {$interpolation}': 'Bonjour {$interpolation}'}); - const fixture = - initWithTemplate(AppComp, `Hello {{ name }}`); + it('should handle single translation message within ng-template', () => { + loadTranslations({'Hello {$INTERPOLATION}': 'Bonjour {$INTERPOLATION}'}); + const fixture = initWithTemplate( + AppComp, `Hello {{ name }}`); - const element = fixture.nativeElement; - expect(element).toHaveText('Bonjour Angular'); - }); + const element = fixture.nativeElement; + expect(element).toHaveText('Bonjour Angular'); + }); - it('should be able to act as child elements inside i18n block (plain text content)', () => { - loadTranslations({ + it('should be able to act as child elements inside i18n block (plain text content)', () => { + loadTranslations({ - '{$startTagNgTemplate} Hello {$closeTagNgTemplate}{$startTagNgContainer} Bye {$closeTagNgContainer}': - '{$startTagNgTemplate} Bonjour {$closeTagNgTemplate}{$startTagNgContainer} Au revoir {$closeTagNgContainer}' - }); - const fixture = initWithTemplate(AppComp, ` + '{$START_TAG_NG_TEMPLATE} Hello {$CLOSE_TAG_NG_TEMPLATE}{$START_TAG_NG_CONTAINER} Bye {$CLOSE_TAG_NG_CONTAINER}': + '{$START_TAG_NG_TEMPLATE} Bonjour {$CLOSE_TAG_NG_TEMPLATE}{$START_TAG_NG_CONTAINER} Au revoir {$CLOSE_TAG_NG_CONTAINER}' + }); + const fixture = initWithTemplate(AppComp, `
Hello @@ -305,17 +316,17 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
`); - const element = fixture.nativeElement.firstChild; - expect(element.textContent.replace(/\s+/g, ' ').trim()).toBe('Bonjour Au revoir'); - }); + const element = fixture.nativeElement.firstChild; + expect(element.textContent.replace(/\s+/g, ' ').trim()).toBe('Bonjour Au revoir'); + }); - it('should be able to act as child elements inside i18n block (text + tags)', () => { - loadTranslations({ + it('should be able to act as child elements inside i18n block (text + tags)', () => { + loadTranslations({ - '{$startTagNgTemplate}{$startTagSpan}Hello{$closeTagSpan}{$closeTagNgTemplate}{$startTagNgContainer}{$startTagSpan}Hello{$closeTagSpan}{$closeTagNgContainer}': - '{$startTagNgTemplate}{$startTagSpan}Bonjour{$closeTagSpan}{$closeTagNgTemplate}{$startTagNgContainer}{$startTagSpan}Bonjour{$closeTagSpan}{$closeTagNgContainer}' - }); - const fixture = initWithTemplate(AppComp, ` + '{$START_TAG_NG_TEMPLATE}{$START_TAG_SPAN}Hello{$CLOSE_TAG_SPAN}{$CLOSE_TAG_NG_TEMPLATE}{$START_TAG_NG_CONTAINER}{$START_TAG_SPAN}Hello{$CLOSE_TAG_SPAN}{$CLOSE_TAG_NG_CONTAINER}': + '{$START_TAG_NG_TEMPLATE}{$START_TAG_SPAN}Bonjour{$CLOSE_TAG_SPAN}{$CLOSE_TAG_NG_TEMPLATE}{$START_TAG_NG_CONTAINER}{$START_TAG_SPAN}Bonjour{$CLOSE_TAG_SPAN}{$CLOSE_TAG_NG_CONTAINER}' + }); + const fixture = initWithTemplate(AppComp, `
Hello @@ -326,42 +337,44 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
`); - const element = fixture.nativeElement; - const spans = element.getElementsByTagName('span'); - for (let i = 0; i < spans.length; i++) { - expect(spans[i]).toHaveText('Bonjour'); - } - }); + const element = fixture.nativeElement; + const spans = element.getElementsByTagName('span'); + for (let i = 0; i < spans.length; i++) { + expect(spans[i]).toHaveText('Bonjour'); + } + }); - it('should be able to act as child elements inside i18n block (text + pipes)', () => { - // Note: for some reason keeping this key inline causes clang to reformat the entire file - // in a very weird way. Keeping it separated like this seems to make it happy. - const key = '{$startTagNgTemplate}Hello {$interpolation}{$closeTagNgTemplate}' + - '{$startTagNgContainer}Bye {$interpolation}{$closeTagNgContainer}'; + it('should be able to act as child elements inside i18n block (text + pipes)', () => { + // Note: for some reason keeping this key inline causes clang to reformat the entire + // file + // in a very weird way. Keeping it separated like this seems to make it happy. + const key = '{$START_TAG_NG_TEMPLATE}Hello {$INTERPOLATION}{$CLOSE_TAG_NG_TEMPLATE}' + + '{$START_TAG_NG_CONTAINER}Bye {$INTERPOLATION}{$CLOSE_TAG_NG_CONTAINER}'; - loadTranslations({ + loadTranslations({ - [key]: - '{$startTagNgTemplate}Hej {$interpolation}{$closeTagNgTemplate}{$startTagNgContainer}Vi ses {$interpolation}{$closeTagNgContainer}' - }); - const fixture = initWithTemplate(AppComp, ` + [key]: + '{$START_TAG_NG_TEMPLATE}Hej {$INTERPOLATION}{$CLOSE_TAG_NG_TEMPLATE}{$START_TAG_NG_CONTAINER}Vi ses {$INTERPOLATION}{$CLOSE_TAG_NG_CONTAINER}' + }); + const fixture = initWithTemplate(AppComp, `
Hello {{name | uppercase}} Bye {{name | uppercase}}
`); - const element = fixture.nativeElement.firstChild; - expect(element.textContent.replace(/\s+/g, ' ').trim()).toBe('Hej ANGULARVi ses ANGULAR'); - }); + const element = fixture.nativeElement.firstChild; + expect(element.textContent.replace(/\s+/g, ' ').trim()) + .toBe('Hej ANGULARVi ses ANGULAR'); + }); - it('should be able to handle deep nested levels with templates', () => { - loadTranslations({ + it('should be able to handle deep nested levels with templates', () => { + loadTranslations({ - '{$startTagSpan} Hello - 1 {$closeTagSpan}{$startTagSpan_1} Hello - 2 {$startTagSpan_1} Hello - 3 {$startTagSpan_1} Hello - 4 {$closeTagSpan}{$closeTagSpan}{$closeTagSpan}{$startTagSpan} Hello - 5 {$closeTagSpan}': - '{$startTagSpan} Bonjour - 1 {$closeTagSpan}{$startTagSpan_1} Bonjour - 2 {$startTagSpan_1} Bonjour - 3 {$startTagSpan_1} Bonjour - 4 {$closeTagSpan}{$closeTagSpan}{$closeTagSpan}{$startTagSpan} Bonjour - 5 {$closeTagSpan}' - }); - const fixture = initWithTemplate(AppComp, ` + '{$START_TAG_SPAN} Hello - 1 {$CLOSE_TAG_SPAN}{$START_TAG_SPAN_1} Hello - 2 {$START_TAG_SPAN_1} Hello - 3 {$START_TAG_SPAN_1} Hello - 4 {$CLOSE_TAG_SPAN}{$CLOSE_TAG_SPAN}{$CLOSE_TAG_SPAN}{$START_TAG_SPAN} Hello - 5 {$CLOSE_TAG_SPAN}': + '{$START_TAG_SPAN} Bonjour - 1 {$CLOSE_TAG_SPAN}{$START_TAG_SPAN_1} Bonjour - 2 {$START_TAG_SPAN_1} Bonjour - 3 {$START_TAG_SPAN_1} Bonjour - 4 {$CLOSE_TAG_SPAN}{$CLOSE_TAG_SPAN}{$CLOSE_TAG_SPAN}{$START_TAG_SPAN} Bonjour - 5 {$CLOSE_TAG_SPAN}' + }); + const fixture = initWithTemplate(AppComp, `
Hello - 1 @@ -381,21 +394,21 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
`); - const element = fixture.nativeElement; - const spans = element.getElementsByTagName('span'); - for (let i = 0; i < spans.length; i++) { - expect(spans[i].innerHTML).toContain(`Bonjour - ${i + 1}`); - } - }); + const element = fixture.nativeElement; + const spans = element.getElementsByTagName('span'); + for (let i = 0; i < spans.length; i++) { + expect(spans[i].innerHTML).toContain(`Bonjour - ${i + 1}`); + } + }); - it('should handle self-closing tags as content', () => { - loadTranslations({ + it('should handle self-closing tags as content', () => { + loadTranslations({ - '{$startTagSpan}My logo{$tagImg}{$closeTagSpan}': - '{$startTagSpan}Mon logo{$tagImg}{$closeTagSpan}' - }); - const content = `My logo`; - const fixture = initWithTemplate(AppComp, ` + '{$START_TAG_SPAN}My logo{$TAG_IMG}{$CLOSE_TAG_SPAN}': + '{$START_TAG_SPAN}Mon logo{$TAG_IMG}{$CLOSE_TAG_SPAN}' + }); + const content = `My logo`; + const fixture = initWithTemplate(AppComp, ` ${content} @@ -404,23 +417,24 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { `); - const element = fixture.nativeElement; - const spans = element.getElementsByTagName('span'); - for (let i = 0; i < spans.length; i++) { - const child = spans[i]; - expect(child).toHaveText('Mon logo'); - } - }); + const element = fixture.nativeElement; + const spans = element.getElementsByTagName('span'); + for (let i = 0; i < spans.length; i++) { + const child = spans[i]; + expect(child).toHaveText('Mon logo'); + } + }); - it('should correctly find context for an element inside i18n section in ', () => { - @Directive({selector: '[myDir]'}) - class Dir { - condition = true; - } + it('should correctly find context for an element inside i18n section in ', + () => { + @Directive({selector: '[myDir]'}) + class Dir { + condition = true; + } - @Component({ - selector: 'my-cmp', - template: ` + @Component({ + selector: 'my-cmp', + template: `
Logged in
@@ -428,181 +442,187 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { Not logged in
`, - }) - class Cmp { - isLogged = false; - } + }) + class Cmp { + isLogged = false; + } - TestBed.configureTestingModule({ - declarations: [Cmp, Dir], - }); - const fixture = TestBed.createComponent(Cmp); - fixture.detectChanges(); + TestBed.configureTestingModule({ + declarations: [Cmp, Dir], + }); + const fixture = TestBed.createComponent(Cmp); + fixture.detectChanges(); - const a = fixture.debugElement.query(By.css('a')); - const dir = a.injector.get(Dir); - expect(dir.condition).toEqual(true); - }); - }); + const a = fixture.debugElement.query(By.css('a')); + const dir = a.injector.get(Dir); + expect(dir.condition).toEqual(true); + }); + }); - describe('should support ICU expressions', () => { - it('with no root node', () => { - loadTranslations({ + describe('should support ICU expressions', () => { + it('with no root node', () => { + loadTranslations({ - '{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}': - '{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}' - }); - const fixture = - initWithTemplate(AppComp, `{count, select, 10 {ten} 20 {twenty} other {other}}`); + '{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}': + '{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}' + }); + const fixture = + initWithTemplate(AppComp, `{count, select, 10 {ten} 20 {twenty} other {other}}`); - const element = fixture.nativeElement; - expect(element).toHaveText('autre'); - }); + const element = fixture.nativeElement; + expect(element).toHaveText('autre'); + }); - it('with no i18n tag', () => { - loadTranslations({ + it('with no i18n tag', () => { + loadTranslations({ - '{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}': - '{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}' - }); - const fixture = initWithTemplate( - AppComp, `
{count, select, 10 {ten} 20 {twenty} other {other}}
`); + '{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}': + '{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}' + }); + const fixture = initWithTemplate( + AppComp, `
{count, select, 10 {ten} 20 {twenty} other {other}}
`); - const element = fixture.nativeElement; - expect(element).toHaveText('autre'); - }); + const element = fixture.nativeElement; + expect(element).toHaveText('autre'); + }); - it('multiple', () => { - loadTranslations({ + it('multiple', () => { + loadTranslations({ - '{VAR_PLURAL, plural, =0 {no {START_BOLD_TEXT}emails{CLOSE_BOLD_TEXT}!} =1 {one {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} {START_TAG_SPAN}emails{CLOSE_TAG_SPAN}}}': - '{VAR_PLURAL, plural, =0 {aucun {START_BOLD_TEXT}email{CLOSE_BOLD_TEXT}!} =1 {un {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} {START_TAG_SPAN}emails{CLOSE_TAG_SPAN}}}', - '{VAR_SELECT, select, other {(name)}}': '{VAR_SELECT, select, other {({$interpolation})}}' - }); - const fixture = initWithTemplate(AppComp, `
{count, plural, + '{VAR_PLURAL, plural, =0 {no {START_BOLD_TEXT}emails{CLOSE_BOLD_TEXT}!} =1 {one {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} {START_TAG_SPAN}emails{CLOSE_TAG_SPAN}}}': + '{VAR_PLURAL, plural, =0 {aucun {START_BOLD_TEXT}email{CLOSE_BOLD_TEXT}!} =1 {un {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} {START_TAG_SPAN}emails{CLOSE_TAG_SPAN}}}', + '{VAR_SELECT, select, other {(name)}}': + '{VAR_SELECT, select, other {({$INTERPOLATION})}}' + }); + const fixture = initWithTemplate(AppComp, `
{count, plural, =0 {no emails!} =1 {one email} other {{{count}} emails} } - {name, select, other {({{name}})} }
`); - expect(fixture.nativeElement.innerHTML) - .toEqual(`
aucun email! - (Angular)
`); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
aucun email! - (Angular)
`); - fixture.componentRef.instance.count = 4; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual( - `
4 emails - (Angular)
`); + fixture.componentRef.instance.count = 4; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual( + `
4 emails - (Angular)
`); - fixture.componentRef.instance.count = 0; - fixture.componentRef.instance.name = 'John'; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual(`
aucun email! - (John)
`); - }); + fixture.componentRef.instance.count = 0; + fixture.componentRef.instance.name = 'John'; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
aucun email! - (John)
`); + }); - it('with custom interpolation config', () => { - loadTranslations({ + it('with custom interpolation config', () => { + loadTranslations({ - '{VAR_SELECT, select, 10 {ten} other {{$interpolation}}}': - '{VAR_SELECT, select, 10 {dix} other {{$interpolation}}}' - }); - const interpolation = ['{%', '%}'] as[string, string]; - TestBed.overrideComponent(AppComp, {set: {interpolation}}); - const fixture = - initWithTemplate(AppComp, `
{count, select, 10 {ten} other {{% name %}}}
`); + '{VAR_SELECT, select, 10 {ten} other {{$INTERPOLATION}}}': + '{VAR_SELECT, select, 10 {dix} other {{$INTERPOLATION}}}' + }); + const interpolation = ['{%', '%}'] as[string, string]; + TestBed.overrideComponent(AppComp, {set: {interpolation}}); + const fixture = initWithTemplate( + AppComp, `
{count, select, 10 {ten} other {{% name %}}}
`); - expect(fixture.nativeElement).toHaveText(`Angular`); - }); + expect(fixture.nativeElement).toHaveText(`Angular`); + }); - it('inside HTML elements', () => { - loadTranslations({ + it('inside HTML elements', () => { + loadTranslations({ - '{VAR_PLURAL, plural, =0 {no {START_BOLD_TEXT}emails{CLOSE_BOLD_TEXT}!} =1 {one {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} {START_TAG_SPAN}emails{CLOSE_TAG_SPAN}}}': - '{VAR_PLURAL, plural, =0 {aucun {START_BOLD_TEXT}email{CLOSE_BOLD_TEXT}!} =1 {un {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} {START_TAG_SPAN}emails{CLOSE_TAG_SPAN}}}', - '{VAR_SELECT, select, other {(name)}}': '{VAR_SELECT, select, other {({$interpolation})}}' - }); - const fixture = initWithTemplate(AppComp, `
{count, plural, + '{VAR_PLURAL, plural, =0 {no {START_BOLD_TEXT}emails{CLOSE_BOLD_TEXT}!} =1 {one {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} {START_TAG_SPAN}emails{CLOSE_TAG_SPAN}}}': + '{VAR_PLURAL, plural, =0 {aucun {START_BOLD_TEXT}email{CLOSE_BOLD_TEXT}!} =1 {un {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} {START_TAG_SPAN}emails{CLOSE_TAG_SPAN}}}', + '{VAR_SELECT, select, other {(name)}}': + '{VAR_SELECT, select, other {({$INTERPOLATION})}}' + }); + const fixture = initWithTemplate(AppComp, `
{count, plural, =0 {no emails!} =1 {one email} other {{{count}} emails} } - {name, select, other {({{name}})} }
`); - expect(fixture.nativeElement.innerHTML) - .toEqual( - `
aucun email! - (Angular)
`); + expect(fixture.nativeElement.innerHTML) + .toEqual( + `
aucun email! - (Angular)
`); - fixture.componentRef.instance.count = 4; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual( - `
4 emails - (Angular)
`); + fixture.componentRef.instance.count = 4; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual( + `
4 emails - (Angular)
`); - fixture.componentRef.instance.count = 0; - fixture.componentRef.instance.name = 'John'; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual( - `
aucun email! - (John)
`); - }); + fixture.componentRef.instance.count = 0; + fixture.componentRef.instance.name = 'John'; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual( + `
aucun email! - (John)
`); + }); - it('inside template directives', () => { - loadTranslations({ + it('inside template directives', () => { + loadTranslations({ - '{VAR_SELECT, select, other {(name)}}': '{VAR_SELECT, select, other {({$interpolation})}}' - }); - const fixture = initWithTemplate(AppComp, `
{name, select, + '{VAR_SELECT, select, other {(name)}}': + '{VAR_SELECT, select, other {({$INTERPOLATION})}}' + }); + const fixture = + initWithTemplate(AppComp, `
{name, select, other {({{name}})} }
`); - expect(fixture.nativeElement.innerHTML) - .toEqual(`
(Angular)
`); - fixture.componentRef.instance.visible = false; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual(`
`); - }); + }); - it('inside ng-container', () => { - loadTranslations({ + it('inside ng-container', () => { + loadTranslations({ - '{VAR_SELECT, select, other {(name)}}': '{VAR_SELECT, select, other {({$interpolation})}}' - }); - const fixture = initWithTemplate(AppComp, `{name, select, + '{VAR_SELECT, select, other {(name)}}': + '{VAR_SELECT, select, other {({$INTERPOLATION})}}' + }); + const fixture = initWithTemplate(AppComp, `{name, select, other {({{name}})} }`); - expect(fixture.nativeElement.innerHTML).toEqual(`(Angular)`); - }); + expect(fixture.nativeElement.innerHTML) + .toEqual(`(Angular)`); + }); - it('inside ', () => { - loadTranslations({ + it('inside ', () => { + loadTranslations({ - '{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}': - '{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}' - }); - const fixture = initWithTemplate( - AppComp, ` + '{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}': + '{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}' + }); + const fixture = initWithTemplate( + AppComp, ` ` + - `{count, select, 10 {ten} 20 {twenty} other {other}}` + - ` + `{count, select, 10 {ten} 20 {twenty} other {other}}` + + ` `); - const element = fixture.nativeElement; - expect(element).toHaveText('autre'); - }); + const element = fixture.nativeElement; + expect(element).toHaveText('autre'); + }); - it('nested', () => { - loadTranslations({ + it('nested', () => { + loadTranslations({ - '{VAR_PLURAL, plural, =0 {zero} other {{INTERPOLATION} {VAR_SELECT, select, cat {cats} dog {dogs} other {animals}}!}}': - '{VAR_PLURAL, plural, =0 {zero} other {{INTERPOLATION} {VAR_SELECT, select, cat {chats} dog {chients} other {animaux}}!}}' - }); - const fixture = initWithTemplate(AppComp, `
{count, plural, + '{VAR_PLURAL, plural, =0 {zero} other {{INTERPOLATION} {VAR_SELECT, select, cat {cats} dog {dogs} other {animals}}!}}': + '{VAR_PLURAL, plural, =0 {zero} other {{INTERPOLATION} {VAR_SELECT, select, cat {chats} dog {chients} other {animaux}}!}}' + }); + const fixture = initWithTemplate(AppComp, `
{count, plural, =0 {zero} other {{{count}} {name, select, cat {cats} @@ -610,24 +630,25 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { other {animals} }!} }
`); - expect(fixture.nativeElement.innerHTML).toEqual(`
zero
`); + expect(fixture.nativeElement.innerHTML).toEqual(`
zero
`); - fixture.componentRef.instance.count = 4; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual(`
4 animaux!
`); - }); + fixture.componentRef.instance.count = 4; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
4 animaux!
`); + }); - it('nested with interpolations in "other" blocks', () => { - // Note: for some reason long string causing clang to reformat the entire file. - const key = '{VAR_PLURAL, plural, =0 {zero} =2 {{INTERPOLATION} {VAR_SELECT, select, ' + - 'cat {cats} dog {dogs} other {animals}}!} other {other - {INTERPOLATION}}}'; - const translation = - '{VAR_PLURAL, plural, =0 {zero} =2 {{INTERPOLATION} {VAR_SELECT, select, ' + - 'cat {chats} dog {chients} other {animaux}}!} other {other - {INTERPOLATION}}}'; - loadTranslations({[key]: translation}); + it('nested with interpolations in "other" blocks', () => { + // Note: for some reason long string causing clang to reformat the entire file. + const key = + '{VAR_PLURAL, plural, =0 {zero} =2 {{INTERPOLATION} {VAR_SELECT, select, ' + + 'cat {cats} dog {dogs} other {animals}}!} other {other - {INTERPOLATION}}}'; + const translation = + '{VAR_PLURAL, plural, =0 {zero} =2 {{INTERPOLATION} {VAR_SELECT, select, ' + + 'cat {chats} dog {chients} other {animaux}}!} other {other - {INTERPOLATION}}}'; + loadTranslations({[key]: translation}); - const fixture = initWithTemplate(AppComp, `
{count, plural, + const fixture = initWithTemplate(AppComp, `
{count, plural, =0 {zero} =2 {{{count}} {name, select, cat {cats} @@ -636,24 +657,25 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }!} other {other - {{count}}} }
`); - expect(fixture.nativeElement.innerHTML).toEqual(`
zero
`); + expect(fixture.nativeElement.innerHTML).toEqual(`
zero
`); - fixture.componentRef.instance.count = 2; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual(`
2 animaux!
`); + fixture.componentRef.instance.count = 2; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
2 animaux!
`); - fixture.componentRef.instance.count = 4; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual(`
other - 4
`); - }); + fixture.componentRef.instance.count = 4; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual(`
other - 4
`); + }); - it('should return the correct plural form for ICU expressions when using a specific locale', - () => { - registerLocaleData(localeRo); - TestBed.configureTestingModule({providers: [{provide: LOCALE_ID, useValue: 'ro'}]}); - // We could also use `TestBed.overrideProvider(LOCALE_ID, {useValue: 'ro'});` - const fixture = initWithTemplate(AppComp, ` + it('should return the correct plural form for ICU expressions when using a specific locale', + () => { + registerLocaleData(localeRo); + TestBed.configureTestingModule( + {providers: [{provide: LOCALE_ID, useValue: 'ro'}]}); + // We could also use `TestBed.overrideProvider(LOCALE_ID, {useValue: 'ro'});` + const fixture = initWithTemplate(AppComp, ` {count, plural, =0 {no email} =one {one email} @@ -661,93 +683,94 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { =other {lots of emails} }`); - expect(fixture.nativeElement.innerHTML).toEqual('no email'); + expect(fixture.nativeElement.innerHTML).toEqual('no email'); - // Change detection cycle, no model changes - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual('no email'); + // Change detection cycle, no model changes + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual('no email'); - fixture.componentInstance.count = 3; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual('a few emails'); + fixture.componentInstance.count = 3; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual('a few emails'); - fixture.componentInstance.count = 1; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual('one email'); + fixture.componentInstance.count = 1; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual('one email'); - fixture.componentInstance.count = 10; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual('a few emails'); + fixture.componentInstance.count = 10; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual('a few emails'); - fixture.componentInstance.count = 20; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual('lots of emails'); + fixture.componentInstance.count = 20; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual('lots of emails'); - fixture.componentInstance.count = 0; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual('no email'); - }); + fixture.componentInstance.count = 0; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual('no email'); + }); - it('projection', () => { - @Component({selector: 'child', template: '
'}) - class Child { - } + it('projection', () => { + @Component({selector: 'child', template: '
'}) + class Child { + } - @Component({ - selector: 'parent', - template: ` + @Component({ + selector: 'parent', + template: ` { value // i18n(ph = "blah"), plural, =1 {one} other {at least {{value}} .} }` - }) - class Parent { - value = 3; - } - TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({}); + }) + class Parent { + value = 3; + } + TestBed.configureTestingModule({declarations: [Parent, Child]}); + loadTranslations({}); - const fixture = TestBed.createComponent(Parent); - fixture.detectChanges(); + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toContain('at least'); - }); + expect(fixture.nativeElement.innerHTML).toContain('at least'); + }); - it('with empty values', () => { - const fixture = initWithTemplate(AppComp, `{count, select, 10 {} 20 {twenty} other {other}}`); + it('with empty values', () => { + const fixture = + initWithTemplate(AppComp, `{count, select, 10 {} 20 {twenty} other {other}}`); - const element = fixture.nativeElement; - expect(element).toHaveText('other'); - }); + const element = fixture.nativeElement; + expect(element).toHaveText('other'); + }); - it('inside a container when creating a view via vcr.createEmbeddedView', () => { - @Directive({ - selector: '[someDir]', - }) - class Dir { - constructor( - private readonly viewContainerRef: ViewContainerRef, - private readonly templateRef: TemplateRef) {} + it('inside a container when creating a view via vcr.createEmbeddedView', () => { + @Directive({ + selector: '[someDir]', + }) + class Dir { + constructor( + private readonly viewContainerRef: ViewContainerRef, + private readonly templateRef: TemplateRef) {} - ngOnInit() { this.viewContainerRef.createEmbeddedView(this.templateRef); } - } + ngOnInit() { this.viewContainerRef.createEmbeddedView(this.templateRef); } + } - @Component({ - selector: 'my-cmp', - template: ` + @Component({ + selector: 'my-cmp', + template: `
`, - }) - class Cmp { - } + }) + class Cmp { + } - @Component({ - selector: 'my-app', - template: ` + @Component({ + selector: 'my-app', + template: ` { count, plural, @@ -755,68 +778,70 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { other {OTHER} } `, - }) - class App { - count = 1; - condition = true; - } + }) + class App { + count = 1; + condition = true; + } - TestBed.configureTestingModule({ - declarations: [App, Cmp, Dir], - }); - const fixture = TestBed.createComponent(App); - fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.innerHTML) - .toContain('
ONE
'); + TestBed.configureTestingModule({ + declarations: [App, Cmp, Dir], + }); + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.innerHTML) + .toContain('
ONE
'); - fixture.componentRef.instance.count = 2; - fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.innerHTML) - .toContain('
OTHER
'); + fixture.componentRef.instance.count = 2; + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.innerHTML) + .toContain('
OTHER
'); - // destroy component - fixture.componentInstance.condition = false; - fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.textContent).toBe(''); + // destroy component + fixture.componentInstance.condition = false; + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.textContent).toBe(''); - // render it again and also change ICU case - fixture.componentInstance.condition = true; - fixture.componentInstance.count = 1; - fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.innerHTML) - .toContain('
ONE
'); - }); + // render it again and also change ICU case + fixture.componentInstance.condition = true; + fixture.componentInstance.count = 1; + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.innerHTML) + .toContain('
ONE
'); + }); - it('with nested ICU expression and inside a container when creating a view via vcr.createEmbeddedView', - () => { - let dir: Dir|null = null; - @Directive({ - selector: '[someDir]', - }) - class Dir { - constructor( - private readonly viewContainerRef: ViewContainerRef, - private readonly templateRef: TemplateRef) { - dir = this; - } + it('with nested ICU expression and inside a container when creating a view via vcr.createEmbeddedView', + () => { + let dir: Dir|null = null; + @Directive({ + selector: '[someDir]', + }) + class Dir { + constructor( + private readonly viewContainerRef: ViewContainerRef, + private readonly templateRef: TemplateRef) { + dir = this; + } - attachEmbeddedView() { this.viewContainerRef.createEmbeddedView(this.templateRef); } - } + attachEmbeddedView() { + this.viewContainerRef.createEmbeddedView(this.templateRef); + } + } - @Component({ - selector: 'my-cmp', - template: ` + @Component({ + selector: 'my-cmp', + template: `
`, - }) - class Cmp { - } + }) + class Cmp { + } - @Component({ - selector: 'my-app', - template: ` + @Component({ + selector: 'my-app', + template: ` { count, plural, @@ -828,36 +853,36 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }!} } `, - }) - class App { - count = 1; - } + }) + class App { + count = 1; + } - TestBed.configureTestingModule({ - declarations: [App, Cmp, Dir], - }); - const fixture = TestBed.createComponent(App); - fixture.componentRef.instance.count = 2; - fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.innerHTML) - .toBe(''); + TestBed.configureTestingModule({ + declarations: [App, Cmp, Dir], + }); + const fixture = TestBed.createComponent(App); + fixture.componentRef.instance.count = 2; + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.innerHTML) + .toBe(''); - dir !.attachEmbeddedView(); - fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.innerHTML) - .toBe( - '
2 animals!
'); + dir !.attachEmbeddedView(); + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.innerHTML) + .toBe( + '
2 animals!
'); - fixture.componentRef.instance.count = 1; - fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.innerHTML) - .toBe('
ONE
'); - }); + fixture.componentRef.instance.count = 1; + fixture.detectChanges(); + expect(fixture.debugElement.nativeElement.innerHTML) + .toBe('
ONE
'); + }); - it('with nested containers', () => { - @Component({ - selector: 'comp', - template: ` + it('with nested containers', () => { + @Component({ + selector: 'comp', + template: ` {type, select, A { A } B { B } other { C }} @@ -867,31 +892,31 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { `, - }) - class Comp { - type = 'A'; - visible = true; - isVisible() { return true; } - } + }) + class Comp { + type = 'A'; + visible = true; + isVisible() { return true; } + } - TestBed.configureTestingModule({declarations: [Comp]}); + TestBed.configureTestingModule({declarations: [Comp]}); - const fixture = TestBed.createComponent(Comp); - fixture.detectChanges(); + const fixture = TestBed.createComponent(Comp); + fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.innerHTML).toContain('A'); + expect(fixture.debugElement.nativeElement.innerHTML).toContain('A'); - fixture.componentInstance.visible = false; - fixture.detectChanges(); + fixture.componentInstance.visible = false; + fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.innerHTML).not.toContain('A'); - expect(fixture.debugElement.nativeElement.innerHTML).toContain('C1'); - }); + expect(fixture.debugElement.nativeElement.innerHTML).not.toContain('A'); + expect(fixture.debugElement.nativeElement.innerHTML).toContain('C1'); + }); - it('with named interpolations', () => { - @Component({ - selector: 'comp', - template: ` + it('with named interpolations', () => { + @Component({ + selector: 'comp', + template: ` { type, select, @@ -900,32 +925,32 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { other {other - {{ typeC // i18n(ph="PH WITH SPACES") }}} } `, - }) - class Comp { - type = 'A'; - typeA = 'Type A'; - typeB = 'Type B'; - typeC = 'Type C'; - } + }) + class Comp { + type = 'A'; + typeA = 'Type A'; + typeB = 'Type B'; + typeC = 'Type C'; + } - TestBed.configureTestingModule({declarations: [Comp]}); + TestBed.configureTestingModule({declarations: [Comp]}); - const fixture = TestBed.createComponent(Comp); - fixture.detectChanges(); + const fixture = TestBed.createComponent(Comp); + fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.innerHTML).toContain('A - Type A'); + expect(fixture.debugElement.nativeElement.innerHTML).toContain('A - Type A'); - fixture.componentInstance.type = 'C'; // trigger "other" case - fixture.detectChanges(); + fixture.componentInstance.type = 'C'; // trigger "other" case + fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.innerHTML).not.toContain('A - Type A'); - expect(fixture.debugElement.nativeElement.innerHTML).toContain('other - Type C'); - }); + expect(fixture.debugElement.nativeElement.innerHTML).not.toContain('A - Type A'); + expect(fixture.debugElement.nativeElement.innerHTML).toContain('other - Type C'); + }); - it('should work inside an ngTemplateOutlet inside an ngFor', () => { - @Component({ - selector: 'app', - template: ` + it('should work inside an ngTemplateOutlet inside an ngFor', () => { + @Component({ + selector: 'app', + template: ` { type, select, @@ -940,171 +965,176 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
` - }) - class AppComponent { - types = ['A', 'B', 'C']; - } + }) + class AppComponent { + types = ['A', 'B', 'C']; + } - TestBed.configureTestingModule({declarations: [AppComponent]}); + TestBed.configureTestingModule({declarations: [AppComponent]}); - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.innerHTML).toContain('A'); - expect(fixture.debugElement.nativeElement.innerHTML).toContain('B'); - }); - }); + expect(fixture.debugElement.nativeElement.innerHTML).toContain('A'); + expect(fixture.debugElement.nativeElement.innerHTML).toContain('B'); + }); + }); - describe('should support attributes', () => { - it('text', () => { - loadTranslations({'text': 'texte'}); - const fixture = initWithTemplate(AppComp, `
`); - expect(fixture.nativeElement.innerHTML).toEqual(`
`); - }); + describe('should support attributes', () => { + it('text', () => { + loadTranslations({'text': 'texte'}); + const fixture = initWithTemplate(AppComp, `
`); + expect(fixture.nativeElement.innerHTML).toEqual(`
`); + }); - it('interpolations', () => { - loadTranslations({'hello {$interpolation}': 'bonjour {$interpolation}'}); - const fixture = - initWithTemplate(AppComp, `
`); - expect(fixture.nativeElement.innerHTML).toEqual(`
`); + it('interpolations', () => { + loadTranslations({'hello {$INTERPOLATION}': 'bonjour {$INTERPOLATION}'}); + const fixture = + initWithTemplate(AppComp, `
`); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
`); - fixture.componentRef.instance.name = 'John'; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual(`
`); - }); + fixture.componentRef.instance.name = 'John'; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual(`
`); + }); - it('with pipes', () => { - loadTranslations({'hello {$interpolation}': 'bonjour {$interpolation}'}); - const fixture = initWithTemplate( - AppComp, `
`); - expect(fixture.nativeElement.innerHTML).toEqual(`
`); - }); + it('with pipes', () => { + loadTranslations({'hello {$INTERPOLATION}': 'bonjour {$INTERPOLATION}'}); + const fixture = initWithTemplate( + AppComp, `
`); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
`); + }); - it('multiple attributes', () => { - loadTranslations({'hello {$interpolation}': 'bonjour {$interpolation}'}); - const fixture = initWithTemplate( - AppComp, - ``); - expect(fixture.nativeElement.innerHTML) - .toEqual(``); + it('multiple attributes', () => { + loadTranslations({'hello {$INTERPOLATION}': 'bonjour {$INTERPOLATION}'}); + const fixture = initWithTemplate( + AppComp, + ``); + expect(fixture.nativeElement.innerHTML) + .toEqual(``); - fixture.componentRef.instance.name = 'John'; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual(``); - }); + fixture.componentRef.instance.name = 'John'; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual(``); + }); - it('on removed elements', () => { - loadTranslations({'text': 'texte', '{$startTagSpan}content{$closeTagSpan}': 'contenu'}); - const fixture = - initWithTemplate(AppComp, `
content
`); - expect(fixture.nativeElement.innerHTML).toEqual(`
contenu
`); - }); + it('on removed elements', () => { + loadTranslations( + {'text': 'texte', '{$START_TAG_SPAN}content{$CLOSE_TAG_SPAN}': 'contenu'}); + const fixture = initWithTemplate( + AppComp, `
content
`); + expect(fixture.nativeElement.innerHTML).toEqual(`
contenu
`); + }); - it('with custom interpolation config', () => { - loadTranslations({'Hello {$interpolation}': 'Bonjour {$interpolation}'}); - const interpolation = ['{%', '%}'] as[string, string]; - TestBed.overrideComponent(AppComp, {set: {interpolation}}); - const fixture = - initWithTemplate(AppComp, `
`); + it('with custom interpolation config', () => { + loadTranslations({'Hello {$INTERPOLATION}': 'Bonjour {$INTERPOLATION}'}); + const interpolation = ['{%', '%}'] as[string, string]; + TestBed.overrideComponent(AppComp, {set: {interpolation}}); + const fixture = initWithTemplate( + AppComp, `
`); - const element = fixture.nativeElement.firstChild; - expect(element.title).toBe('Bonjour Angular'); - }); + const element = fixture.nativeElement.firstChild; + expect(element.title).toBe('Bonjour Angular'); + }); - it('in nested template', () => { - loadTranslations({'Item {$interpolation}': 'Article {$interpolation}'}); - const fixture = initWithTemplate(AppComp, ` + it('in nested template', () => { + loadTranslations({'Item {$INTERPOLATION}': 'Article {$INTERPOLATION}'}); + const fixture = initWithTemplate(AppComp, `
`); - const element = fixture.nativeElement; - for (let i = 0; i < element.children.length; i++) { - const child = element.children[i]; - expect((child as any).innerHTML).toBe(`
`); - } - }); + const element = fixture.nativeElement; + for (let i = 0; i < element.children.length; i++) { + const child = element.children[i]; + expect((child as any).innerHTML).toBe(`
`); + } + }); - it('should add i18n attributes on self-closing tags', () => { - loadTranslations({'Hello {$interpolation}': 'Bonjour {$interpolation}'}); - const fixture = - initWithTemplate(AppComp, ``); + it('should add i18n attributes on self-closing tags', () => { + loadTranslations({'Hello {$INTERPOLATION}': 'Bonjour {$INTERPOLATION}'}); + const fixture = initWithTemplate( + AppComp, ``); - const element = fixture.nativeElement.firstChild; - expect(element.title).toBe('Bonjour Angular'); - }); + const element = fixture.nativeElement.firstChild; + expect(element.title).toBe('Bonjour Angular'); + }); - it('should apply i18n attributes during second template pass', () => { - @Directive({ - selector: '[test]', - inputs: ['test'], - exportAs: 'dir', - }) - class Dir { - } + it('should apply i18n attributes during second template pass', () => { + @Directive({ + selector: '[test]', + inputs: ['test'], + exportAs: 'dir', + }) + class Dir { + } - @Component({ - selector: 'other', - template: `
` - }) - class Other { - } + @Component({ + selector: 'other', + template: `
` + }) + class Other { + } - @Component({ - selector: 'blah', - template: ` + @Component({ + selector: 'blah', + template: ` ` - }) - class Cmp { - } + }) + class Cmp { + } - TestBed.configureTestingModule({ - declarations: [Dir, Cmp, Other], - }); + TestBed.configureTestingModule({ + declarations: [Dir, Cmp, Other], + }); - const fixture = TestBed.createComponent(Cmp); - fixture.detectChanges(); - expect(fixture.debugElement.children[0].children[0].references.ref.test).toBe('Set'); - expect(fixture.debugElement.children[1].children[0].references.ref.test).toBe('Set'); - }); + const fixture = TestBed.createComponent(Cmp); + fixture.detectChanges(); + expect(fixture.debugElement.children[0].children[0].references.ref.test).toBe('Set'); + expect(fixture.debugElement.children[1].children[0].references.ref.test).toBe('Set'); + }); - it('with complex expressions', () => { - loadTranslations({ - '{$interpolation} - {$interpolation_1} - {$interpolation_2}': - '{$interpolation} - {$interpolation_1} - {$interpolation_2} (fr)' - }); - const fixture = initWithTemplate(AppComp, ` + it('with complex expressions', () => { + loadTranslations({ + '{$INTERPOLATION} - {$INTERPOLATION_1} - {$INTERPOLATION_2}': + '{$INTERPOLATION} - {$INTERPOLATION_1} - {$INTERPOLATION_2} (fr)' + }); + const fixture = initWithTemplate(AppComp, `
`); - // the `obj` field is not yet defined, so 2nd and 3rd interpolations return empty strings - expect(fixture.nativeElement.firstChild.title).toEqual(`ANGULAR - - (fr)`); + // the `obj` field is not yet defined, so 2nd and 3rd interpolations return empty + // strings + expect(fixture.nativeElement.firstChild.title).toEqual(`ANGULAR - - (fr)`); - fixture.componentRef.instance.obj = { - a: {b: 'value 1'}, - getA: () => ({b: 'value 2'}), - }; - fixture.detectChanges(); - expect(fixture.nativeElement.firstChild.title).toEqual(`ANGULAR - value 1 - value 2 (fr)`); - }); - }); + fixture.componentRef.instance.obj = { + a: {b: 'value 1'}, + getA: () => ({b: 'value 2'}), + }; + fixture.detectChanges(); + expect(fixture.nativeElement.firstChild.title) + .toEqual(`ANGULAR - value 1 - value 2 (fr)`); + }); + }); - it('should work with directives and host bindings', () => { - let directiveInstances: ClsDir[] = []; + it('should work with directives and host bindings', () => { + let directiveInstances: ClsDir[] = []; - @Directive({selector: '[test]'}) - class ClsDir { - @HostBinding('className') - klass = 'foo'; + @Directive({selector: '[test]'}) + class ClsDir { + @HostBinding('className') + klass = 'foo'; - constructor() { directiveInstances.push(this); } - } + constructor() { directiveInstances.push(this); } + } - @Component({ - selector: `my-app`, - template: ` + @Component({ + selector: `my-app`, + template: `
trad: {exp1, plural, =0 {no emails!} @@ -1112,80 +1142,81 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { other {{{exp1}} emails} }
` - }) - class MyApp { - exp1 = 1; - exp2 = 2; - } + }) + class MyApp { + exp1 = 1; + exp2 = 2; + } - TestBed.configureTestingModule({declarations: [ClsDir, MyApp]}); - loadTranslations({ - // Not that this translation switches the order of the expressions! - 'start {$interpolation} middle {$interpolation_1} end': - 'début {$interpolation_1} milieu {$interpolation} fin', - '{VAR_PLURAL, plural, =0 {no {START_BOLD_TEXT}emails{CLOSE_BOLD_TEXT}!} =1 {one {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} emails}}': - '{VAR_PLURAL, plural, =0 {aucun {START_BOLD_TEXT}email{CLOSE_BOLD_TEXT}!} =1 {un {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} emails}}', - ' trad: {$icu} ': ' traduction: {$icu} ' - }); - const fixture = TestBed.createComponent(MyApp); - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual( - `
traduction: un email ` + - `
`); + TestBed.configureTestingModule({declarations: [ClsDir, MyApp]}); + loadTranslations({ + // Not that this translation switches the order of the expressions! + 'start {$INTERPOLATION} middle {$INTERPOLATION_1} end': + 'début {$INTERPOLATION_1} milieu {$INTERPOLATION} fin', + '{VAR_PLURAL, plural, =0 {no {START_BOLD_TEXT}emails{CLOSE_BOLD_TEXT}!} =1 {one {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} emails}}': + '{VAR_PLURAL, plural, =0 {aucun {START_BOLD_TEXT}email{CLOSE_BOLD_TEXT}!} =1 {un {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} emails}}', + ' trad: {$ICU} ': ' traduction: {$ICU} ' + }); + const fixture = TestBed.createComponent(MyApp); + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual( + `
traduction: un email ` + + `
`); - directiveInstances.forEach(instance => instance.klass = 'bar'); - fixture.componentRef.instance.exp1 = 2; - fixture.componentRef.instance.exp2 = 3; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual( - `
traduction: 2 emails ` + - `
`); - }); + directiveInstances.forEach(instance => instance.klass = 'bar'); + fixture.componentRef.instance.exp1 = 2; + fixture.componentRef.instance.exp2 = 3; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual( + `
traduction: 2 emails ` + + `
`); + }); - it('should handle i18n attribute with directive inputs', () => { - let calledTitle = false; - let calledValue = false; - @Component({selector: 'my-comp', template: ''}) - class MyComp { - t !: string; - @Input() - get title() { return this.t; } - set title(title) { - calledTitle = true; - this.t = title; - } + it('should handle i18n attribute with directive inputs', () => { + let calledTitle = false; + let calledValue = false; + @Component({selector: 'my-comp', template: ''}) + class MyComp { + t !: string; + @Input() + get title() { return this.t; } + set title(title) { + calledTitle = true; + this.t = title; + } - @Input() - get value() { return this.val; } - set value(value: string) { - calledValue = true; - this.val = value; - } - val !: string; - } + @Input() + get value() { return this.val; } + set value(value: string) { + calledValue = true; + this.val = value; + } + val !: string; + } - TestBed.configureTestingModule({declarations: [AppComp, MyComp]}); - loadTranslations({'Hello {$interpolation}': 'Bonjour {$interpolation}', 'works': 'fonctionne'}); - const fixture = initWithTemplate( - AppComp, - ``); - fixture.detectChanges(); + TestBed.configureTestingModule({declarations: [AppComp, MyComp]}); + loadTranslations( + {'Hello {$INTERPOLATION}': 'Bonjour {$INTERPOLATION}', 'works': 'fonctionne'}); + const fixture = initWithTemplate( + AppComp, + ``); + fixture.detectChanges(); - const directive = fixture.debugElement.children[0].injector.get(MyComp); - expect(calledValue).toEqual(true); - expect(calledTitle).toEqual(true); - expect(directive.value).toEqual(`Bonjour Angular`); - expect(directive.title).toEqual(`fonctionne`); - }); + const directive = fixture.debugElement.children[0].injector.get(MyComp); + expect(calledValue).toEqual(true); + expect(calledTitle).toEqual(true); + expect(directive.value).toEqual(`Bonjour Angular`); + expect(directive.title).toEqual(`fonctionne`); + }); - it('should support adding/moving/removing nodes', () => { - loadTranslations({ - '{$startTagDiv2}{$closeTagDiv2}{$startTagDiv3}{$closeTagDiv3}{$startTagDiv4}{$closeTagDiv4}{$startTagDiv5}{$closeTagDiv5}{$startTagDiv6}{$closeTagDiv6}{$startTagDiv7}{$closeTagDiv7}{$startTagDiv8}{$closeTagDiv8}': - '{$startTagDiv2}{$closeTagDiv2}{$startTagDiv8}{$closeTagDiv8}{$startTagDiv4}{$closeTagDiv4}{$startTagDiv5}{$closeTagDiv5}Bonjour monde{$startTagDiv3}{$closeTagDiv3}{$startTagDiv7}{$closeTagDiv7}' - }); - const fixture = initWithTemplate(AppComp, ` + it('should support adding/moving/removing nodes', () => { + loadTranslations({ + '{$START_TAG_DIV2}{$CLOSE_TAG_DIV2}{$START_TAG_DIV3}{$CLOSE_TAG_DIV3}{$START_TAG_DIV4}{$CLOSE_TAG_DIV4}{$START_TAG_DIV5}{$CLOSE_TAG_DIV5}{$START_TAG_DIV6}{$CLOSE_TAG_DIV6}{$START_TAG_DIV7}{$CLOSE_TAG_DIV7}{$START_TAG_DIV8}{$CLOSE_TAG_DIV8}': + '{$START_TAG_DIV2}{$CLOSE_TAG_DIV2}{$START_TAG_DIV8}{$CLOSE_TAG_DIV8}{$START_TAG_DIV4}{$CLOSE_TAG_DIV4}{$START_TAG_DIV5}{$CLOSE_TAG_DIV5}Bonjour monde{$START_TAG_DIV3}{$CLOSE_TAG_DIV3}{$START_TAG_DIV7}{$CLOSE_TAG_DIV7}' + }); + const fixture = initWithTemplate(AppComp, `
@@ -1195,20 +1226,20 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
`); - expect(fixture.nativeElement.innerHTML) - .toEqual( - `
Bonjour monde
`); - }); + expect(fixture.nativeElement.innerHTML) + .toEqual( + `
Bonjour monde
`); + }); - describe('projection', () => { - it('should project the translations', () => { - @Component({selector: 'child', template: '

'}) - class Child { - } + describe('projection', () => { + it('should project the translations', () => { + @Component({selector: 'child', template: '

'}) + class Child { + } - @Component({ - selector: 'parent', - template: ` + @Component({ + selector: 'parent', + template: `
I am projected from {{name}} @@ -1216,32 +1247,32 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
` - }) - class Parent { - name: string = 'Parent'; - } - TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({ + }) + class Parent { + name: string = 'Parent'; + } + TestBed.configureTestingModule({declarations: [Parent, Child]}); + loadTranslations({ - 'Child of {$interpolation}': 'Enfant de {$interpolation}', - '{$startTagChild}I am projected from {$startBoldText}{$interpolation}{$startTagRemoveMe_1}{$closeTagRemoveMe_1}{$closeBoldText}{$startTagRemoveMe_2}{$closeTagRemoveMe_2}{$closeTagChild}{$startTagRemoveMe_3}{$closeTagRemoveMe_3}': - '{$startTagChild}Je suis projeté depuis {$startBoldText}{$interpolation}{$closeBoldText}{$closeTagChild}' - }); - const fixture = TestBed.createComponent(Parent); - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual( - `

Je suis projeté depuis Parent

`); - }); + 'Child of {$INTERPOLATION}': 'Enfant de {$INTERPOLATION}', + '{$START_TAG_CHILD}I am projected from {$START_BOLD_TEXT}{$INTERPOLATION}{$START_TAG_REMOVE_ME_1}{$CLOSE_TAG_REMOVE_ME_1}{$CLOSE_BOLD_TEXT}{$START_TAG_REMOVE_ME_2}{$CLOSE_TAG_REMOVE_ME_2}{$CLOSE_TAG_CHILD}{$START_TAG_REMOVE_ME_3}{$CLOSE_TAG_REMOVE_ME_3}': + '{$START_TAG_CHILD}Je suis projeté depuis {$START_BOLD_TEXT}{$INTERPOLATION}{$CLOSE_BOLD_TEXT}{$CLOSE_TAG_CHILD}' + }); + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual( + `

Je suis projeté depuis Parent

`); + }); - it('should project a translated i18n block', () => { - @Component({selector: 'child', template: '

'}) - class Child { - } + it('should project a translated i18n block', () => { + @Component({selector: 'child', template: '

'}) + class Child { + } - @Component({ - selector: 'parent', - template: ` + @Component({ + selector: 'parent', + template: `
@@ -1249,360 +1280,381 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
` - }) - class Parent { - name: string = 'Parent'; - } - TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({ + }) + class Parent { + name: string = 'Parent'; + } + TestBed.configureTestingModule({declarations: [Parent, Child]}); + loadTranslations({ - 'Child of {$interpolation}': 'Enfant de {$interpolation}', - 'I am projected from {$interpolation}': 'Je suis projeté depuis {$interpolation}' - }); - const fixture = TestBed.createComponent(Parent); - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual( - `

Je suis projeté depuis Parent

`); + 'Child of {$INTERPOLATION}': 'Enfant de {$INTERPOLATION}', + 'I am projected from {$INTERPOLATION}': 'Je suis projeté depuis {$INTERPOLATION}' + }); + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual( + `

Je suis projeté depuis Parent

`); - // it should be able to render a new component with the same template code - const fixture2 = TestBed.createComponent(Parent); - fixture2.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual(fixture2.nativeElement.innerHTML); + // it should be able to render a new component with the same template code + const fixture2 = TestBed.createComponent(Parent); + fixture2.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual(fixture2.nativeElement.innerHTML); - fixture2.componentRef.instance.name = 'Parent 2'; - fixture2.detectChanges(); - expect(fixture2.nativeElement.innerHTML) - .toEqual( - `

Je suis projeté depuis Parent 2

`); + fixture2.componentRef.instance.name = 'Parent 2'; + fixture2.detectChanges(); + expect(fixture2.nativeElement.innerHTML) + .toEqual( + `

Je suis projeté depuis Parent 2

`); - // The first fixture should not have changed - expect(fixture.nativeElement.innerHTML).not.toEqual(fixture2.nativeElement.innerHTML); - }); + // The first fixture should not have changed + expect(fixture.nativeElement.innerHTML).not.toEqual(fixture2.nativeElement.innerHTML); + }); - it('should re-project translations when multiple projections', () => { - @Component({selector: 'grand-child', template: '
'}) - class GrandChild { - } + it('should re-project translations when multiple projections', () => { + @Component( + {selector: 'grand-child', template: '
'}) + class GrandChild { + } - @Component( - {selector: 'child', template: ''}) - class Child { - } + @Component({ + selector: 'child', + template: '' + }) + class Child { + } - @Component({selector: 'parent', template: `Hello World!`}) - class Parent { - name: string = 'Parent'; - } + @Component({selector: 'parent', template: `Hello World!`}) + class Parent { + name: string = 'Parent'; + } - TestBed.configureTestingModule({declarations: [Parent, Child, GrandChild]}); - loadTranslations({ + TestBed.configureTestingModule({declarations: [Parent, Child, GrandChild]}); + loadTranslations({ - '{$startBoldText}Hello{$closeBoldText} World!': - '{$startBoldText}Bonjour{$closeBoldText} monde!' - }); - const fixture = TestBed.createComponent(Parent); - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual('
Bonjour monde!
'); - }); + '{$START_BOLD_TEXT}Hello{$CLOSE_BOLD_TEXT} World!': + '{$START_BOLD_TEXT}Bonjour{$CLOSE_BOLD_TEXT} monde!' + }); + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual( + '
Bonjour monde!
'); + }); - it('should be able to remove projected placeholders', () => { - @Component({selector: 'grand-child', template: '
'}) - class GrandChild { - } + it('should be able to remove projected placeholders', () => { + @Component( + {selector: 'grand-child', template: '
'}) + class GrandChild { + } - @Component( - {selector: 'child', template: ''}) - class Child { - } + @Component({ + selector: 'child', + template: '' + }) + class Child { + } - @Component({selector: 'parent', template: `Hello World!`}) - class Parent { - name: string = 'Parent'; - } + @Component({selector: 'parent', template: `Hello World!`}) + class Parent { + name: string = 'Parent'; + } - TestBed.configureTestingModule({declarations: [Parent, Child, GrandChild]}); - loadTranslations({'{$startBoldText}Hello{$closeBoldText} World!': 'Bonjour monde!'}); - const fixture = TestBed.createComponent(Parent); - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual('
Bonjour monde!
'); - }); + TestBed.configureTestingModule({declarations: [Parent, Child, GrandChild]}); + loadTranslations( + {'{$START_BOLD_TEXT}Hello{$CLOSE_BOLD_TEXT} World!': 'Bonjour monde!'}); + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual('
Bonjour monde!
'); + }); - it('should project translations with selectors', () => { - @Component({selector: 'child', template: ``}) - class Child { - } + it('should project translations with selectors', () => { + @Component({selector: 'child', template: ``}) + class Child { + } - @Component({ - selector: 'parent', - template: ` + @Component({ + selector: 'parent', + template: ` ` - }) - class Parent { - } + }) + class Parent { + } - TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({ + TestBed.configureTestingModule({declarations: [Parent, Child]}); + loadTranslations({ - '{$startTagSpan}{$closeTagSpan}{$startTagSpan_1}{$closeTagSpan}': - '{$startTagSpan}Contenu{$closeTagSpan}' - }); - const fixture = TestBed.createComponent(Parent); - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual('Contenu'); - }); + '{$START_TAG_SPAN}{$CLOSE_TAG_SPAN}{$START_TAG_SPAN_1}{$CLOSE_TAG_SPAN}': + '{$START_TAG_SPAN}Contenu{$CLOSE_TAG_SPAN}' + }); + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual('Contenu'); + }); - it('should project content in i18n blocks', () => { - @Component({ - selector: 'child', - template: `
Content projected from
` - }) - class Child { - } + it('should project content in i18n blocks', () => { + @Component({ + selector: 'child', + template: `
Content projected from
` + }) + class Child { + } - @Component({selector: 'parent', template: `{{name}}`}) - class Parent { - name: string = 'Parent'; - } - TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({ + @Component({selector: 'parent', template: `{{name}}`}) + class Parent { + name: string = 'Parent'; + } + TestBed.configureTestingModule({declarations: [Parent, Child]}); + loadTranslations({ - 'Content projected from {$startTagNgContent}{$closeTagNgContent}': - 'Contenu projeté depuis {$startTagNgContent}{$closeTagNgContent}' - }); + 'Content projected from {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}': + 'Contenu projeté depuis {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}' + }); - const fixture = TestBed.createComponent(Parent); - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual(`
Contenu projeté depuis Parent
`); + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
Contenu projeté depuis Parent
`); - fixture.componentRef.instance.name = 'Parent component'; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual(`
Contenu projeté depuis Parent component
`); - }); + fixture.componentRef.instance.name = 'Parent component'; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
Contenu projeté depuis Parent component
`); + }); - it('should project content in i18n blocks with placeholders', () => { - @Component({ - selector: 'child', - template: `
Content projected from
` - }) - class Child { - } + it('should project content in i18n blocks with placeholders', () => { + @Component({ + selector: 'child', + template: `
Content projected from
` + }) + class Child { + } - @Component({selector: 'parent', template: `{{name}}`}) - class Parent { - name: string = 'Parent'; - } - TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({ + @Component({selector: 'parent', template: `{{name}}`}) + class Parent { + name: string = 'Parent'; + } + TestBed.configureTestingModule({declarations: [Parent, Child]}); + loadTranslations({ - 'Content projected from {$startTagNgContent}{$closeTagNgContent}': - '{$startTagNgContent}{$closeTagNgContent} a projeté le contenu' - }); - const fixture = TestBed.createComponent(Parent); - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual(`
Parent a projeté le contenu
`); - }); + 'Content projected from {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}': + '{$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT} a projeté le contenu' + }); + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
Parent a projeté le contenu
`); + }); - it('should project translated content in i18n blocks', () => { - @Component( - {selector: 'child', template: `
Child content
`}) - class Child { - } + it('should project translated content in i18n blocks', () => { + @Component({ + selector: 'child', + template: `
Child content
` + }) + class Child { + } - @Component({selector: 'parent', template: `and projection from {{name}}`}) - class Parent { - name: string = 'Parent'; - } - TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({ + @Component({ + selector: 'parent', + template: `and projection from {{name}}` + }) + class Parent { + name: string = 'Parent'; + } + TestBed.configureTestingModule({declarations: [Parent, Child]}); + loadTranslations({ - 'Child content {$startTagNgContent}{$closeTagNgContent}': - 'Contenu enfant {$startTagNgContent}{$closeTagNgContent}', - 'and projection from {$interpolation}': 'et projection depuis {$interpolation}' - }); - const fixture = TestBed.createComponent(Parent); - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual(`
Contenu enfant et projection depuis Parent
`); - }); + 'Child content {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}': + 'Contenu enfant {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}', + 'and projection from {$INTERPOLATION}': 'et projection depuis {$INTERPOLATION}' + }); + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
Contenu enfant et projection depuis Parent
`); + }); - it('should project bare ICU expressions', () => { - @Component({selector: 'child', template: '
'}) - class Child { - } + it('should project bare ICU expressions', () => { + @Component({selector: 'child', template: '
'}) + class Child { + } - @Component({ - selector: 'parent', - template: ` + @Component({ + selector: 'parent', + template: ` { value // i18n(ph = "blah"), plural, =1 {one} other {at least {{value}} .} }` - }) - class Parent { - value = 3; - } - TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({}); + }) + class Parent { + value = 3; + } + TestBed.configureTestingModule({declarations: [Parent, Child]}); + loadTranslations({}); - const fixture = TestBed.createComponent(Parent); - fixture.detectChanges(); + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toContain('at least'); - }); + expect(fixture.nativeElement.innerHTML).toContain('at least'); + }); - it('should project ICUs in i18n blocks', () => { - @Component( - {selector: 'child', template: `
Child content
`}) - class Child { - } + it('should project ICUs in i18n blocks', () => { + @Component({ + selector: 'child', + template: `
Child content
` + }) + class Child { + } - @Component({ - selector: 'parent', - template: - `and projection from {name, select, angular {Angular} other {{{name}}}}` - }) - class Parent { - name: string = 'Parent'; - } - TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({ + @Component({ + selector: 'parent', + template: + `and projection from {name, select, angular {Angular} other {{{name}}}}` + }) + class Parent { + name: string = 'Parent'; + } + TestBed.configureTestingModule({declarations: [Parent, Child]}); + loadTranslations({ - 'Child content {$startTagNgContent}{$closeTagNgContent}': - 'Contenu enfant {$startTagNgContent}{$closeTagNgContent}', - 'and projection from {$icu}': 'et projection depuis {$icu}' - }); - const fixture = TestBed.createComponent(Parent); - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual( - `
Contenu enfant et projection depuis Parent
`); + 'Child content {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}': + 'Contenu enfant {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}', + 'and projection from {$ICU}': 'et projection depuis {$ICU}' + }); + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual( + `
Contenu enfant et projection depuis Parent
`); - fixture.componentRef.instance.name = 'angular'; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual( - `
Contenu enfant et projection depuis Angular
`); - }); + fixture.componentRef.instance.name = 'angular'; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual( + `
Contenu enfant et projection depuis Angular
`); + }); - it(`shouldn't project deleted projections in i18n blocks`, () => { - @Component( - {selector: 'child', template: `
Child content
`}) - class Child { - } + it(`shouldn't project deleted projections in i18n blocks`, () => { + @Component({ + selector: 'child', + template: `
Child content
` + }) + class Child { + } - @Component({selector: 'parent', template: `and projection from {{name}}`}) - class Parent { - name: string = 'Parent'; - } - TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({ + @Component({ + selector: 'parent', + template: `and projection from {{name}}` + }) + class Parent { + name: string = 'Parent'; + } + TestBed.configureTestingModule({declarations: [Parent, Child]}); + loadTranslations({ - 'Child content {$startTagNgContent}{$closeTagNgContent}': 'Contenu enfant', - 'and projection from {$interpolation}': 'et projection depuis {$interpolation}' - }); - const fixture = TestBed.createComponent(Parent); - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual(`
Contenu enfant
`); - }); + 'Child content {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}': 'Contenu enfant', + 'and projection from {$INTERPOLATION}': 'et projection depuis {$INTERPOLATION}' + }); + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
Contenu enfant
`); + }); - it('should display/destroy projected i18n content', () => { + it('should display/destroy projected i18n content', () => { - @Component({ - selector: 'app', - template: ` + @Component({ + selector: 'app', + template: ` () ` - }) - class MyContentApp { - } + }) + class MyContentApp { + } - @Component({ - selector: 'my-app', - template: ` + @Component({ + selector: 'my-app', + template: ` {type, select, A {A} B {B} other {other}} ` - }) - class MyApp { - type = 'A'; - condition = true; - } + }) + class MyApp { + type = 'A'; + condition = true; + } - TestBed.configureTestingModule({declarations: [MyApp, MyContentApp]}); + TestBed.configureTestingModule({declarations: [MyApp, MyContentApp]}); - const fixture = TestBed.createComponent(MyApp); - fixture.detectChanges(); + const fixture = TestBed.createComponent(MyApp); + fixture.detectChanges(); - expect(fixture.nativeElement.textContent).toContain('(A)'); + expect(fixture.nativeElement.textContent).toContain('(A)'); - // change `condition` to remove - fixture.componentInstance.condition = false; - fixture.detectChanges(); + // change `condition` to remove + fixture.componentInstance.condition = false; + fixture.detectChanges(); - // should not contain 'A' - expect(fixture.nativeElement.textContent).toBe(''); + // should not contain 'A' + expect(fixture.nativeElement.textContent).toBe(''); - // display again - fixture.componentInstance.type = 'B'; - fixture.componentInstance.condition = true; - fixture.detectChanges(); + // display again + fixture.componentInstance.type = 'B'; + fixture.componentInstance.condition = true; + fixture.detectChanges(); - // expect that 'B' is now displayed - expect(fixture.nativeElement.textContent).toContain('(B)'); - }); - }); + // expect that 'B' is now displayed + expect(fixture.nativeElement.textContent).toContain('(B)'); + }); + }); - describe('queries', () => { - function toHtml(element: Element): string { - return element.innerHTML.replace(/\sng-reflect-\S*="[^"]*"/g, '') - .replace(//g, ''); - } + describe('queries', () => { + function toHtml(element: Element): string { + return element.innerHTML.replace(/\sng-reflect-\S*="[^"]*"/g, '') + .replace(//g, ''); + } - it('detached nodes should still be part of query', () => { - @Directive({selector: '[text]', inputs: ['text'], exportAs: 'textDir'}) - class TextDirective { - // TODO(issue/24571): remove '!'. - text !: string; - constructor() {} - } + it('detached nodes should still be part of query', () => { + @Directive({selector: '[text]', inputs: ['text'], exportAs: 'textDir'}) + class TextDirective { + // TODO(issue/24571): remove '!'. + text !: string; + constructor() {} + } - @Component({selector: 'div-query', template: ''}) - class DivQuery { - // TODO(issue/24571): remove '!'. - @ContentChild(TemplateRef, {static: true}) template !: TemplateRef; + @Component({selector: 'div-query', template: ''}) + class DivQuery { + // TODO(issue/24571): remove '!'. + @ContentChild(TemplateRef, {static: true}) template !: TemplateRef; - // TODO(issue/24571): remove '!'. - @ViewChild('vc', {read: ViewContainerRef, static: true}) - vc !: ViewContainerRef; + // TODO(issue/24571): remove '!'. + @ViewChild('vc', {read: ViewContainerRef, static: true}) + vc !: ViewContainerRef; - // TODO(issue/24571): remove '!'. - @ContentChildren(TextDirective, {descendants: true}) - query !: QueryList; + // TODO(issue/24571): remove '!'. + @ContentChildren(TextDirective, {descendants: true}) + query !: QueryList; - create() { this.vc.createEmbeddedView(this.template); } + create() { this.vc.createEmbeddedView(this.template); } - destroy() { this.vc.clear(); } - } + destroy() { this.vc.clear(); } + } - TestBed.configureTestingModule({declarations: [TextDirective, DivQuery]}); - loadTranslations({ + TestBed.configureTestingModule({declarations: [TextDirective, DivQuery]}); + loadTranslations({ - '{$startTagNgTemplate}{$startTagDiv_1}{$startTagDiv}{$startTagSpan}Content{$closeTagSpan}{$closeTagDiv}{$closeTagDiv}{$closeTagNgTemplate}': - '{$startTagNgTemplate}Contenu{$closeTagNgTemplate}' - }); - const fixture = initWithTemplate(AppComp, ` + '{$START_TAG_NG_TEMPLATE}{$START_TAG_DIV_1}{$START_TAG_DIV}{$START_TAG_SPAN}Content{$CLOSE_TAG_SPAN}{$CLOSE_TAG_DIV}{$CLOSE_TAG_DIV}{$CLOSE_TAG_NG_TEMPLATE}': + '{$START_TAG_NG_TEMPLATE}Contenu{$CLOSE_TAG_NG_TEMPLATE}' + }); + const fixture = initWithTemplate(AppComp, `
@@ -1613,28 +1665,28 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { `); - const q = fixture.debugElement.children[0].references.q; - expect(q.query.length).toEqual(0); + const q = fixture.debugElement.children[0].references.q; + expect(q.query.length).toEqual(0); - // Create embedded view - q.create(); - fixture.detectChanges(); - expect(q.query.length).toEqual(1); - expect(toHtml(fixture.nativeElement)) - .toEqual(`Contenu`); + // Create embedded view + q.create(); + fixture.detectChanges(); + expect(q.query.length).toEqual(1); + expect(toHtml(fixture.nativeElement)) + .toEqual(`Contenu`); - // Disable ng-if - fixture.componentInstance.visible = false; - fixture.detectChanges(); - expect(q.query.length).toEqual(0); - expect(toHtml(fixture.nativeElement)) - .toEqual(`Contenu`); - }); - }); + // Disable ng-if + fixture.componentInstance.visible = false; + fixture.detectChanges(); + expect(q.query.length).toEqual(0); + expect(toHtml(fixture.nativeElement)) + .toEqual(`Contenu`); + }); + }); - it('should not alloc expando slots when there is no new variable to create', () => { - @Component({ - template: ` + it('should not alloc expando slots when there is no new variable to create', () => { + @Component({ + template: `
Some content @@ -1642,20 +1694,21 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
` - }) - class ContentElementDialog { - data = false; - } + }) + class ContentElementDialog { + data = false; + } - TestBed.configureTestingModule({declarations: [DialogDir, CloseBtn, ContentElementDialog]}); + TestBed.configureTestingModule( + {declarations: [DialogDir, CloseBtn, ContentElementDialog]}); - const fixture = TestBed.createComponent(ContentElementDialog); - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual(`
`); - }); -}); + }); + }); function initWithTemplate(compType: Type, template: string) { TestBed.overrideComponent(compType, {set: {template}}); diff --git a/packages/core/test/acceptance/view_container_ref_spec.ts b/packages/core/test/acceptance/view_container_ref_spec.ts index 2442cf5ed9..efa28c70b0 100644 --- a/packages/core/test/acceptance/view_container_ref_spec.ts +++ b/packages/core/test/acceptance/view_container_ref_spec.ts @@ -20,10 +20,10 @@ describe('ViewContainerRef', () => { const TRANSLATIONS: any = { 'Bar': 'o', - '{$startTagBefore}{$closeTagBefore}{$startTagDiv}{$startTagInside}{$closeTagInside}{$closeTagDiv}{$startTagAfter}{$closeTagAfter}': - 'F{$startTagDiv}{$closeTagDiv}o', - '{$startTagBefore}{$closeTagBefore}{$startTagDiv}{$startTagIn}{$closeTagIn}{$closeTagDiv}{$startTagAfter}{$closeTagAfter}': - '{$startTagDiv}{$closeTagDiv}{$startTagBefore}{$closeTagBefore}' + '{$START_TAG_BEFORE}{$CLOSE_TAG_BEFORE}{$START_TAG_DIV}{$START_TAG_INSIDE}{$CLOSE_TAG_INSIDE}{$CLOSE_TAG_DIV}{$START_TAG_AFTER}{$CLOSE_TAG_AFTER}': + 'F{$START_TAG_DIV}{$CLOSE_TAG_DIV}o', + '{$START_TAG_BEFORE}{$CLOSE_TAG_BEFORE}{$START_TAG_DIV}{$START_TAG_IN}{$CLOSE_TAG_IN}{$CLOSE_TAG_DIV}{$START_TAG_AFTER}{$CLOSE_TAG_AFTER}': + '{$START_TAG_DIV}{$CLOSE_TAG_DIV}{$START_TAG_BEFORE}{$CLOSE_TAG_BEFORE}' }; /** diff --git a/packages/core/test/bundling/todo_i18n/translations.ts b/packages/core/test/bundling/todo_i18n/translations.ts index fa79645d3c..f0871aa370 100644 --- a/packages/core/test/bundling/todo_i18n/translations.ts +++ b/packages/core/test/bundling/todo_i18n/translations.ts @@ -10,12 +10,12 @@ import {loadTranslations} from '@angular/localize/run_time'; export const translations = { 'What needs to be done?': `Qu'y a-t-il à faire ?`, - '{$startHeadingLevel1}todos{$closeHeadingLevel1}{$tagInput}': - '{$startHeadingLevel1}liste de tâches{$closeHeadingLevel1}{$tagInput}', + '{$START_HEADING_LEVEL1}todos{$CLOSE_HEADING_LEVEL1}{$TAG_INPUT}': + '{$START_HEADING_LEVEL1}liste de tâches{$CLOSE_HEADING_LEVEL1}{$TAG_INPUT}', '{VAR_PLURAL, plural, =1 {item left} other {items left}}': '{VAR_PLURAL, plural, =1 {tâche restante} other {tâches restantes}}', - '{$startTagStrong}{$interpolation}{$closeTagStrong}{$icu}': - '{$startTagStrong}{$interpolation}{$closeTagStrong} {$icu}', + '{$START_TAG_STRONG}{$INTERPOLATION}{$CLOSE_TAG_STRONG}{$ICU}': + '{$START_TAG_STRONG}{$INTERPOLATION}{$CLOSE_TAG_STRONG} {$ICU}', ' Clear completed ': ' Effacer terminés ', 'Demonstrate Components': 'Démontrer les components', 'Demonstrate Structural Directives': 'Démontrer les directives structurelles',