From b741a1c3e72a407abf8959c87a8eb65b870ab689 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Fri, 13 Sep 2019 12:46:05 +0100 Subject: [PATCH] fix(ivy): i18n - update the compiler to output `MessageId`s (#32594) Now that the `$localize` translations are `MessageId` based the compiler must render `MessageId`s in its generated `$localize` code. This is because the `MessageId` used by the compiler is computed from information that does not get passed through to the `$localize` tagged string. For example, the generated code for the following template ```html
``` will contain these localization statements ```ts if (ngI18nClosureMode) { /** * @desc d * @meaning m */ const MSG_EXTERNAL_8809028065680254561$$APP_SPEC_TS_1 = goog.getMsg("introduction"); I18N_1 = MSG_EXTERNAL_8809028065680254561$$APP_SPEC_TS_1; } else { I18N_1 = $localize \`:m|d@@8809028065680254561:introduction\`; } ``` Since `$localize` is not able to accurately regenerate the source-message (and so the `MessageId`) from the generated code, it must rely upon the `MessageId` being provided explicitly in the generated code. The compiler now prepends all localized messages with a "metadata block" containing the id (and the meaning and description if defined). Note that this metadata block will also allow translation file extraction from the compiled code - rather than relying on the legacy ViewEngine extraction code. (This will be implemented post-v9). Although these metadata blocks add to the initial code size, compile-time inlining will completely remove these strings and so will not impact on production bundle size. PR Close #32594 --- .../compliance/r3_view_compiler_i18n_spec.ts | 214 +- .../src/render3/view/i18n/localize_utils.ts | 15 +- .../compiler/src/render3/view/i18n/meta.ts | 17 + .../compiler/test/render3/view/i18n_spec.ts | 24 +- packages/core/test/acceptance/i18n_spec.ts | 2333 ++++++++--------- .../acceptance/view_container_ref_spec.ts | 8 +- .../bundling/hello_world_i18n/translations.ts | 4 +- .../test/bundling/todo_i18n/translations.ts | 32 +- 8 files changed, 1282 insertions(+), 1365 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 4fdaab682f..228320d1c9 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 @@ -176,7 +176,7 @@ describe('i18n support in the template compiler', () => { const input = `
Content A
Content B
-
Content C
+
Content C
Content D
Content E
Content F
@@ -194,7 +194,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_idA$$APP_SPEC_TS_1$; } else { - $I18N_0$ = $localize \`Content A\`; + $I18N_0$ = $localize \`:meaningA|descA@@idA:Content A\`; } const $_c2$ = [${AttributeMarker.I18n}, "title"]; var $I18N_3$; @@ -207,19 +207,19 @@ describe('i18n support in the template compiler', () => { $I18N_3$ = $MSG_EXTERNAL_idB$$APP_SPEC_TS_4$; } else { - $I18N_3$ = $localize \`Title B\`; + $I18N_3$ = $localize \`:meaningB|descB@@idB:Title B\`; } const $_c5$ = ["title", $I18N_3$]; var $I18N_7$; if (ngI18nClosureMode) { /** - * @desc meaningC + * @meaning meaningC */ - const $MSG_EXTERNAL_4978592519614169666$$APP_SPEC_TS_8$ = goog.getMsg("Title C"); - $I18N_7$ = $MSG_EXTERNAL_4978592519614169666$$APP_SPEC_TS_8$; + const $MSG_EXTERNAL_6435899732746131543$$APP_SPEC_TS_8$ = goog.getMsg("Title C"); + $I18N_7$ = $MSG_EXTERNAL_6435899732746131543$$APP_SPEC_TS_8$; } else { - $I18N_7$ = $localize \`Title C\`; + $I18N_7$ = $localize \`:meaningC|@@6435899732746131543:Title C\`; } const $_c9$ = ["title", $I18N_7$]; var $I18N_11$; @@ -232,7 +232,7 @@ describe('i18n support in the template compiler', () => { $I18N_11$ = $MSG_EXTERNAL_5200291527729162531$$APP_SPEC_TS_12$; } else { - $I18N_11$ = $localize \`Title D\`; + $I18N_11$ = $localize \`:meaningD|descD@@5200291527729162531:Title D\`; } const $_c13$ = ["title", $I18N_11$]; var $I18N_15$; @@ -244,7 +244,7 @@ describe('i18n support in the template compiler', () => { $I18N_15$ = $MSG_EXTERNAL_idE$$APP_SPEC_TS_16$; } else { - $I18N_15$ = $localize \`Title E\`; + $I18N_15$ = $localize \`:meaningE@@idE:Title E\`; } const $_c17$ = ["title", $I18N_15$]; var $I18N_19$; @@ -253,7 +253,7 @@ describe('i18n support in the template compiler', () => { $I18N_19$ = $MSG_EXTERNAL_idF$$APP_SPEC_TS_20$; } else { - $I18N_19$ = $localize \`Title F\`; + $I18N_19$ = $localize \`:@@idF:Title F\`; } const $_c21$ = ["title", $I18N_19$]; var $I18N_23$; @@ -265,7 +265,7 @@ describe('i18n support in the template compiler', () => { $I18N_23$ = $MSG_EXTERNAL_idG$$APP_SPEC_TS_24$; } else { - $I18N_23$ = $localize \`Title G\`; + $I18N_23$ = $localize \`:[BACKUP_MESSAGE_ID:idH]desc@@idG:Title G\`; } const $_c25$ = ["title", $I18N_23$]; … @@ -365,7 +365,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_8809028065680254561$$APP_SPEC_TS_1$; } else { - $I18N_1$ = $localize \`introduction\`; + $I18N_1$ = $localize \`:m|d@@8809028065680254561:introduction\`; } const $_c1$ = ["title", $I18N_1$]; … @@ -402,7 +402,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_5526535577705876535$$APP_SPEC_TS_1$; } else { - $I18N_1$ = $localize \`static text\`; + $I18N_1$ = $localize \`:@@5526535577705876535:static text\`; } var $I18N_2$; if (ngI18nClosureMode) { @@ -416,7 +416,7 @@ describe('i18n support in the template compiler', () => { $I18N_2$ = $MSG_EXTERNAL_8977039798304050198$$APP_SPEC_TS_2$; } else { - $I18N_2$ = $localize \`intro $` + + $I18N_2$ = $localize \`:m|d@@8977039798304050198:intro $` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } var $I18N_3$; @@ -431,7 +431,7 @@ describe('i18n support in the template compiler', () => { $I18N_3$ = $MSG_EXTERNAL_7432761130955693041$$APP_SPEC_TS_3$; } else { - $I18N_3$ = $localize \`$` + + $I18N_3$ = $localize \`:m1|d1@@7432761130955693041:$` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c1$ = [ @@ -452,7 +452,7 @@ describe('i18n support in the template compiler', () => { $I18N_6$ = $MSG_EXTERNAL_7566208596013750546$$APP_SPEC_TS_6$; } else { - $I18N_6$ = $localize \`$` + + $I18N_6$ = $localize \`:m2|d2@@7566208596013750546:$` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: and $` + String.raw `{"\uFFFD1\uFFFD"}:INTERPOLATION_1: and again $` + String.raw `{"\uFFFD2\uFFFD"}:INTERPOLATION_2:\`; @@ -465,7 +465,7 @@ describe('i18n support in the template compiler', () => { $I18N_7$ = $MSG_EXTERNAL_6639222533406278123$$APP_SPEC_TS_7$; } else { - $I18N_7$ = $localize \`$` + + $I18N_7$ = $localize \`:@@6639222533406278123:$` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c3$ = [ @@ -516,7 +516,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_8977039798304050198$; } else { - $I18N_1$ = $localize \`intro $` + + $I18N_1$ = $localize \`:m|d@@8977039798304050198:intro $` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c3$ = ["title", $I18N_1$]; @@ -559,7 +559,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_8538466649243975456$$APP_SPEC_TS__1$; } else { - $I18N_1$ = $localize \`different scope $` + + $I18N_1$ = $localize \`:m|d@@8538466649243975456:different scope $` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c2$ = ["title", $I18N_1$]; @@ -609,7 +609,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_3462388422673575127$$APP_SPEC_TS_2$; } else { - $I18N_1$ = $localize \`$` + + $I18N_1$ = $localize \`:@@3462388422673575127:$` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: title\`; } const $_c3$ = ["title", $I18N_1$]; @@ -658,7 +658,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_5526535577705876535$$APP_SPEC_TS_1$; } else { - $I18N_1$ = $localize \`static text\`; + $I18N_1$ = $localize \`:@@5526535577705876535:static text\`; } var $I18N_2$; if (ngI18nClosureMode) { @@ -672,7 +672,7 @@ describe('i18n support in the template compiler', () => { $I18N_2$ = $MSG_EXTERNAL_8977039798304050198$$APP_SPEC_TS_2$; } else { - $I18N_2$ = $localize \`intro $` + + $I18N_2$ = $localize \`:m|d@@8977039798304050198:intro $` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } var $I18N_3$; @@ -687,7 +687,7 @@ describe('i18n support in the template compiler', () => { $I18N_3$ = $MSG_EXTERNAL_7432761130955693041$$APP_SPEC_TS_3$; } else { - $I18N_3$ = $localize \`$` + + $I18N_3$ = $localize \`:m1|d1@@7432761130955693041:$` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c1$ = [ @@ -708,7 +708,7 @@ describe('i18n support in the template compiler', () => { $I18N_6$ = $MSG_EXTERNAL_7566208596013750546$$APP_SPEC_TS_6$; } else { - $I18N_6$ = $localize \`$` + + $I18N_6$ = $localize \`:m2|d2@@7566208596013750546:$` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: and $` + String.raw `{"\uFFFD1\uFFFD"}:INTERPOLATION_1: and again $` + String.raw `{"\uFFFD2\uFFFD"}:INTERPOLATION_2:\`; @@ -721,7 +721,7 @@ describe('i18n support in the template compiler', () => { $I18N_7$ = $MSG_EXTERNAL_6639222533406278123$$APP_SPEC_TS_7$; } else { - $I18N_7$ = $localize \`$` + + $I18N_7$ = $localize \`:@@6639222533406278123:$` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c3$ = [ @@ -775,7 +775,7 @@ describe('i18n support in the template compiler', () => { $I18N_2$ = $MSG_EXTERNAL_8538466649243975456$$APP_SPEC_TS__3$; } else { - $I18N_2$ = $localize \`different scope $` + + $I18N_2$ = $localize \`:m|d@@8538466649243975456:different scope $` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c4$ = ["title", $I18N_2$]; @@ -827,7 +827,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_7727043314656808423$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`Element title\`; + $I18N_0$ = $localize \`:m|d@@7727043314656808423:Element title\`; } const $_c1$ = ["title", $I18N_0$]; var $I18N_2$; @@ -836,7 +836,7 @@ describe('i18n support in the template compiler', () => { $I18N_2$ = $MSG_EXTERNAL_4969674997806975147$$APP_SPEC_TS_2$; } else { - $I18N_2$ = $localize \`Some content\`; + $I18N_2$ = $localize \`:@@4969674997806975147:Some content\`; } … template: function MyComponent_Template(rf, ctx) { @@ -865,7 +865,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_ID_WITH_INVALID_CHARS$$APP_SPEC_TS_1$; } else { - $I18N_0$ = $localize \`Element title\`; + $I18N_0$ = $localize \`:@@ID.WITH.INVALID.CHARS:Element title\`; } const $_c1$ = ["title", $I18N_0$]; var $I18N_2$; @@ -874,7 +874,7 @@ describe('i18n support in the template compiler', () => { $I18N_2$ = $MSG_EXTERNAL_ID_WITH_INVALID_CHARS_2$$APP_SPEC_TS_4$; } else { - $I18N_2$ = $localize \` Some content \`; + $I18N_2$ = $localize \`:@@ID.WITH.INVALID.CHARS.2: Some content \`; } … `; @@ -922,11 +922,11 @@ describe('i18n support in the template compiler', () => { const output = String.raw ` var $I18N_0$; if (ngI18nClosureMode) { - const $MSG_EXTERNAL_0$ = goog.getMsg("Some text"); - $I18N_0$ = $MSG_EXTERNAL_0$; + const $MSG_APP_SPEC_TS_1$ = goog.getMsg("Some text"); + $I18N_0$ = $MSG_APP_SPEC_TS_1$; } else { - $I18N_0$ = $localize \`Some text\`; + $I18N_0$ = $localize \`:@@3784161717320915177:Some text\`; } `; verify(input, output); @@ -944,7 +944,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_4924931801512133405$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`Some text 'with single quotes', "with double quotes" and without quotes.\`; + $I18N_0$ = $localize \`:@@4924931801512133405:Some text 'with single quotes', "with double quotes" and without quotes.\`; } `; @@ -967,7 +967,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_4890179241114413722$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`My i18n block #1\`; + $I18N_0$ = $localize \`:@@4890179241114413722:My i18n block #1\`; } var $I18N_1$; if (ngI18nClosureMode) { @@ -975,7 +975,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_2413150872298537152$$APP_SPEC_TS_1$; } else { - $I18N_1$ = $localize \`My i18n block #2\`; + $I18N_1$ = $localize \`:@@2413150872298537152:My i18n block #2\`; } var $I18N_2$; if (ngI18nClosureMode) { @@ -983,7 +983,7 @@ describe('i18n support in the template compiler', () => { $I18N_2$ = $MSG_EXTERNAL_5023003143537152794$$APP_SPEC_TS_2$; } else { - $I18N_2$ = $localize \`My i18n block #3\`; + $I18N_2$ = $localize \`:@@5023003143537152794:My i18n block #3\`; } … template: function MyComponent_Template(rf, ctx) { @@ -1028,7 +1028,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_7597881511811528589$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \` Named interpolation: $` + + $I18N_0$ = $localize \`:@@7597881511811528589: Named interpolation: $` + String.raw `{"\uFFFD0\uFFFD"}:PH_A: Named interpolation with spaces: $` + String.raw `{"\uFFFD1\uFFFD"}:PH_B: \`; } @@ -1065,7 +1065,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_6749967533321674787$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`$` + + $I18N_0$ = $localize \`:@@6749967533321674787:$` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } … @@ -1096,15 +1096,15 @@ describe('i18n support in the template compiler', () => { const output = String.raw ` var $I18N_0$; if (ngI18nClosureMode) { - const $MSG_EXTERNAL_5146016486383316049$$APP_SPEC_TS_1$ = goog.getMsg(" {$interpolation} {$interpolation_1} {$interpolation_2} ", { + const $MSG_APP_SPEC_TS_1$$APP_SPEC_TS_1$ = goog.getMsg(" {$interpolation} {$interpolation_1} {$interpolation_2} ", { "interpolation": "\uFFFD0\uFFFD", "interpolation_1": "\uFFFD1\uFFFD", "interpolation_2": "\uFFFD2\uFFFD" }); - $I18N_0$ = $MSG_EXTERNAL_5146016486383316049$$APP_SPEC_TS_1$; + $I18N_0$ = $MSG_APP_SPEC_TS_1$$APP_SPEC_TS_1$; } else { - $I18N_0$ = $localize \` $` + + $I18N_0$ = $localize \`:@@1194644703943451474: $` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + String.raw `{"\uFFFD1\uFFFD"}:INTERPOLATION_1: $` + String.raw `{"\uFFFD2\uFFFD"}:INTERPOLATION_2: \`; @@ -1144,7 +1144,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_572579892698764378$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`My i18n block #$` + + $I18N_0$ = $localize \`:@@572579892698764378:My i18n block #$` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } var $I18N_1$; @@ -1155,7 +1155,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_609623417156596326$$APP_SPEC_TS_1$; } else { - $I18N_1$ = $localize \`My i18n block #$` + + $I18N_1$ = $localize \`:@@609623417156596326:My i18n block #$` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } var $I18N_2$; @@ -1166,7 +1166,7 @@ describe('i18n support in the template compiler', () => { $I18N_2$ = $MSG_EXTERNAL_3998119318957372120$$APP_SPEC_TS_2$; } else { - $I18N_2$ = $localize \`My i18n block #$` + + $I18N_2$ = $localize \`:@@3998119318957372120:My i18n block #$` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } … @@ -1228,7 +1228,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_7905233330103651696$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \` My i18n block #$` + + $I18N_0$ = $localize \`:@@7905233330103651696: My i18n block #$` + 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:\`; @@ -1246,7 +1246,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_5788821996131681377$$APP_SPEC_TS_1$; } else { - $I18N_1$ = $localize \` My i18n block #$` + + $I18N_1$ = $localize \`:@@5788821996131681377: My i18n block #$` + 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 @@ -1317,7 +1317,7 @@ describe('i18n support in the template compiler', () => { $I18N_2$ = $MSG_EXTERNAL_4782264005467235841$$APP_SPEC_TS_3$; } else { - $I18N_2$ = $localize \`Span title $` + + $I18N_2$ = $localize \`:@@4782264005467235841:Span title $` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: and $` + String.raw `{"\uFFFD1\uFFFD"}:INTERPOLATION_1:\`; } @@ -1332,7 +1332,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_4446430594603971069$$APP_SPEC_TS_5$; } else { - $I18N_0$ = $localize \` My i18n block #1 with value: $` + + $I18N_0$ = $localize \`:@@4446430594603971069: My i18n block #1 with value: $` + 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:\`; @@ -1345,7 +1345,7 @@ describe('i18n support in the template compiler', () => { $I18N_7$ = $MSG_EXTERNAL_2719594642740200058$$APP_SPEC_TS_8$; } else { - $I18N_7$ = $localize \`Span title $` + + $I18N_7$ = $localize \`:@@2719594642740200058:Span title $` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c9$ = ["title", $I18N_7$]; @@ -1359,7 +1359,7 @@ describe('i18n support in the template compiler', () => { $I18N_6$ = $MSG_EXTERNAL_2778714953278357902$$APP_SPEC_TS_10$; } else { - $I18N_6$ = $localize \` My i18n block #2 with value $` + + $I18N_6$ = $localize \`:@@2778714953278357902: My i18n block #2 with value $` + 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:\`; @@ -1429,7 +1429,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_7679414751795588050$$APP_SPEC_TS__1$; } else { - $I18N_1$ = $localize \` Some other content $` + + $I18N_1$ = $localize \`:@@7679414751795588050: Some other content $` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION: $` + String.raw `{"\uFFFD#3\uFFFD"}:START_TAG_DIV: More nested levels with bindings $` + String.raw `{"\uFFFD1\uFFFD"}:INTERPOLATION_1: $` + @@ -1498,7 +1498,7 @@ describe('i18n support in the template compiler', () => { $I18N_2$ = $MSG_EXTERNAL_2367729185105559721$$APP_SPEC_TS__2$; } else { - $I18N_2$ = $localize \`App logo #$` + + $I18N_2$ = $localize \`:@@2367729185105559721:App logo #$` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } const $_c4$ = ["title", $I18N_2$]; @@ -1613,7 +1613,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \` Some content $` + + $I18N_0$ = $localize \`:@@1221890473527419724: Some content $` + String.raw `{"\uFFFD*2:1\uFFFD\uFFFD#1:1\uFFFD"}:START_TAG_DIV_2: Some other content $` + String.raw `{"\uFFFD0:1\uFFFD"}:INTERPOLATION: $` + String.raw @@ -1696,7 +1696,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_119975189388320493$$APP_SPEC_TS__1$; } else { - $I18N_1$ = $localize \`Some other content $` + + $I18N_1$ = $localize \`:@@119975189388320493:Some other content $` + String.raw `{"\uFFFD#2\uFFFD"}:START_TAG_SPAN:$` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:$` + String.raw `{"\uFFFD/#2\uFFFD"}:CLOSE_TAG_SPAN:\`; @@ -1745,7 +1745,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_APP_SPEC_TS_2$; } else { - $I18N_1$ = $localize \`Hello\`; + $I18N_1$ = $localize \`:@@3902961887793684628:Hello\`; } … template: function MyComponent_Template(rf, ctx) { @@ -1775,7 +1775,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_4890179241114413722$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`My i18n block #1\`; + $I18N_0$ = $localize \`:@@4890179241114413722:My i18n block #1\`; } … template: function MyComponent_Template(rf, ctx) { @@ -1802,7 +1802,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}\`; + $I18N_0$ = $localize \`:@@8806993169187953163:{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}\`; } $I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$, { "VAR_SELECT": "\uFFFD0\uFFFD" @@ -1839,7 +1839,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_2413150872298537152$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`My i18n block #2\`; + $I18N_0$ = $localize \`:@@2413150872298537152:My i18n block #2\`; } var $I18N_1$; if (ngI18nClosureMode) { @@ -1847,7 +1847,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_4890179241114413722$$APP_SPEC_TS__1$; } else { - $I18N_1$ = $localize \`My i18n block #1\`; + $I18N_1$ = $localize \`:@@4890179241114413722:My i18n block #1\`; } function MyComponent_ng_template_0_Template(rf, ctx) { if (rf & 1) { @@ -1882,7 +1882,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_5295701706185791735$$APP_SPEC_TS_1$; } else { - $I18N_1$ = $localize \`Text #1\`; + $I18N_1$ = $localize \`:@@5295701706185791735:Text #1\`; } const $_c2$ = [${AttributeMarker.Styles}, "padding", "10px"]; var $I18N_3$; @@ -1891,7 +1891,7 @@ describe('i18n support in the template compiler', () => { $I18N_3$ = $MSG_EXTERNAL_4722270221386399294$$APP_SPEC_TS_3$; } else { - $I18N_3$ = $localize \`Text #2\`; + $I18N_3$ = $localize \`:@@4722270221386399294:Text #2\`; } … consts: 4, @@ -1927,7 +1927,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_355394464191978948$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`Some content: $` + + $I18N_0$ = $localize \`:@@355394464191978948:Some content: $` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } … @@ -1964,7 +1964,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_355394464191978948$$APP_SPEC_TS__0$; } else { - $I18N_0$ = $localize \`Some content: $` + + $I18N_0$ = $localize \`:@@355394464191978948:Some content: $` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } function MyComponent_ng_template_0_Template(rf, ctx) { @@ -2012,7 +2012,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_702706566400598764$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`$` + + $I18N_0$ = $localize \`:@@702706566400598764:$` + 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:$` + @@ -2067,7 +2067,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}\`; + $I18N_0$ = $localize \`:@@8806993169187953163:{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}\`; } $I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$, { "VAR_SELECT": "\uFFFD0\uFFFD" @@ -2078,7 +2078,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS__1$; } else { - $I18N_1$ = $localize \`{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_1$ = $localize \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; } $I18N_1$ = $r3$.ɵɵi18nPostprocess($I18N_1$, { "VAR_SELECT": "\uFFFD0\uFFFD" @@ -2163,7 +2163,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_2051477021417799640$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`$` + + $I18N_0$ = $localize \`:@@2051477021417799640:$` + String.raw `{"[\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 @@ -2221,7 +2221,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_0$ = $localize \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; } $I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$, { "VAR_SELECT": "\uFFFD0\uFFFD" @@ -2232,7 +2232,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS__1$; } else { - $I18N_1$ = $localize \`{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}\`; + $I18N_1$ = $localize \`:@@8806993169187953163:{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}\`; } $I18N_1$ = $r3$.ɵɵi18nPostprocess($I18N_1$, { "VAR_SELECT": "\uFFFD0\uFFFD" @@ -2287,7 +2287,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_4891196282781544695$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`$` + + $I18N_0$ = $localize \`:@@4891196282781544695:$` + String.raw `{"\uFFFD#2\uFFFD\uFFFD/#2\uFFFD"}:TAG_IMG: is my logo #1 \`; } var $I18N_2$; @@ -2298,7 +2298,7 @@ describe('i18n support in the template compiler', () => { $I18N_2$ = $MSG_EXTERNAL_461986953980355147$$APP_SPEC_TS__2$; } else { - $I18N_2$ = $localize \`$` + + $I18N_2$ = $localize \`:@@461986953980355147:$` + String.raw `{"\uFFFD#1\uFFFD\uFFFD/#1\uFFFD"}:TAG_IMG: is my logo #2 \`; } function MyComponent_ng_template_3_Template(rf, ctx) { @@ -2344,7 +2344,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_8537814667662432133$$APP_SPEC_TS__0$; } else { - $I18N_0$ = $localize \` Root content $` + + $I18N_0$ = $localize \`:@@8537814667662432133: Root content $` + String.raw `{"\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:\`; @@ -2371,7 +2371,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_6563391987554512024$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`Test\`; + $I18N_0$ = $localize \`:@@6563391987554512024:Test\`; } var $I18N_1$; if (ngI18nClosureMode) { @@ -2379,7 +2379,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_6563391987554512024$$APP_SPEC_TS_1$; } else { - $I18N_1$ = $localize \`Test\`; + $I18N_1$ = $localize \`:@@6563391987554512024:Test\`; } … `; @@ -2401,7 +2401,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_APP_SPEC_TS_1$; } else { - $I18N_0$ = $localize \` Hello $` + + $I18N_0$ = $localize \`:@@161173306924314453: Hello $` + String.raw `{"\uFFFD#2\uFFFD"}:START_TAG_NG_CONTAINER:there$` + String.raw `{"\uFFFD/#2\uFFFD"}:CLOSE_TAG_NG_CONTAINER:\`; } @@ -2437,7 +2437,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_APP_SPEC_TS_1$; } else { - $I18N_0$ = $localize \` Hello $` + + $I18N_0$ = $localize \`:@@1968828034476446869: Hello $` + 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:$` + @@ -2477,7 +2477,7 @@ describe('i18n support in the template compiler', () => { const $MSG_EXTERNAL_3308216566145348998$$APP_SPEC_TS___2$ = goog.getMsg("Content A"); $I18N_1$ = $MSG_EXTERNAL_3308216566145348998$$APP_SPEC_TS___2$; } else { - $I18N_1$ = $localize \`Content A\`; + $I18N_1$ = $localize \`:@@3308216566145348998:Content A\`; } function MyComponent_0_ng_template_0_Template(rf, ctx) { if (rf & 1) { @@ -2494,7 +2494,7 @@ describe('i18n support in the template compiler', () => { const $MSG_EXTERNAL_8349021389088127654$$APP_SPEC_TS__4$ = goog.getMsg("Content B"); $I18N_3$ = $MSG_EXTERNAL_8349021389088127654$$APP_SPEC_TS__4$; } else { - $I18N_3$ = $localize \`Content B\`; + $I18N_3$ = $localize \`:@@8349021389088127654:Content B\`; } function MyComponent_ng_container_1_Template(rf, ctx) { if (rf & 1) { @@ -2541,7 +2541,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_963542717423364282$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`\n Some text\n $` + + $I18N_0$ = $localize \`:@@963542717423364282:\n Some text\n $` + String.raw `{"\uFFFD#3\uFFFD"}:START_TAG_SPAN:Text inside span$` + String.raw `{"\uFFFD/#3\uFFFD"}:CLOSE_TAG_SPAN:\n \`; } @@ -2576,7 +2576,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_0$ = $localize \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; } $I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$, { "VAR_SELECT": "\uFFFD0\uFFFD" @@ -2612,7 +2612,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_4166854826696768832$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`{VAR_SELECT, select, single {'single quotes'} double {"double quotes"} other {other}}\`; + $I18N_0$ = $localize \`:@@4166854826696768832:{VAR_SELECT, select, single {'single quotes'} double {"double quotes"} other {other}}\`; } $I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$, { "VAR_SELECT": "\uFFFD0\uFFFD" @@ -2634,7 +2634,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}\`; + $I18N_0$ = $localize \`:@@8806993169187953163:{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}\`; } $I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$, { "VAR_SELECT": "\uFFFD0\uFFFD" @@ -2674,7 +2674,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_0$ = $localize \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; } $I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$, { "VAR_SELECT": "\uFFFD0\uFFFD" @@ -2688,7 +2688,7 @@ describe('i18n support in the template compiler', () => { $I18N_3$ = $MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$; } else { - $I18N_3$ = $localize \`{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}\`; + $I18N_3$ = $localize \`:@@8806993169187953163:{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}\`; } $I18N_3$ = $r3$.ɵɵi18nPostprocess($I18N_3$, { "VAR_SELECT": "\uFFFD0\uFFFD" @@ -2714,7 +2714,7 @@ describe('i18n support in the template compiler', () => { $I18N_5$ = $MSG_EXTERNAL_1922743304863699161$$APP_SPEC_TS__5$; } else { - $I18N_5$ = $localize \`{VAR_SELECT, select, 0 {no emails} 1 {one email} other {{INTERPOLATION} emails}}\`; + $I18N_5$ = $localize \`:@@1922743304863699161:{VAR_SELECT, select, 0 {no emails} 1 {one email} other {{INTERPOLATION} emails}}\`; } $I18N_5$ = $r3$.ɵɵi18nPostprocess($I18N_5$, { "VAR_SELECT": "\uFFFD0\uFFFD", @@ -2771,7 +2771,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_2949673783721159566$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`{VAR_SELECT, select, 10 {ten} 20 {twenty} other {{INTERPOLATION}}}\`; + $I18N_0$ = $localize \`:@@2949673783721159566:{VAR_SELECT, select, 10 {ten} 20 {twenty} other {{INTERPOLATION}}}\`; } $I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$, { "VAR_SELECT": "\uFFFD0\uFFFD", @@ -2810,7 +2810,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_2417296354340576868$$APP_SPEC_TS_1$; } else { - $I18N_1$ = $localize \`{VAR_SELECT, select, male {male - {START_BOLD_TEXT}male{CLOSE_BOLD_TEXT}} female {female {START_BOLD_TEXT}female{CLOSE_BOLD_TEXT}} other {{START_TAG_DIV}{START_ITALIC_TEXT}other{CLOSE_ITALIC_TEXT}{CLOSE_TAG_DIV}}}\`; + $I18N_1$ = $localize \`:@@2417296354340576868:{VAR_SELECT, select, male {male - {START_BOLD_TEXT}male{CLOSE_BOLD_TEXT}} female {female {START_BOLD_TEXT}female{CLOSE_BOLD_TEXT}} other {{START_TAG_DIV}{START_ITALIC_TEXT}other{CLOSE_ITALIC_TEXT}{CLOSE_TAG_DIV}}}\`; } $I18N_1$ = $r3$.ɵɵi18nPostprocess($I18N_1$, { "VAR_SELECT": "\uFFFD0\uFFFD", @@ -2836,7 +2836,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_5791551881115084301$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \` $` + + $I18N_0$ = $localize \`:@@5791551881115084301: $` + String.raw `{$I18N_1$}:ICU: $` + String.raw `{"\uFFFD#2\uFFFD"}:START_BOLD_TEXT:Other content$` + String.raw `{"\uFFFD/#2\uFFFD"}:CLOSE_BOLD_TEXT:$` + @@ -2881,7 +2881,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_6879461626778511059$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`{VAR_SELECT, select, male {male of age: {INTERPOLATION}} female {female} other {other}}\`; + $I18N_0$ = $localize \`:@@6879461626778511059:{VAR_SELECT, select, male {male of age: {INTERPOLATION}} female {female} other {other}}\`; } $I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$, { "VAR_SELECT": "\uFFFD0\uFFFD", @@ -2921,7 +2921,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_1$; } else { - $I18N_1$ = $localize \`{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_1$ = $localize \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; } $I18N_1$ = $r3$.ɵɵi18nPostprocess($I18N_1$, { "VAR_SELECT": "\uFFFD0\uFFFD" @@ -2932,7 +2932,7 @@ describe('i18n support in the template compiler', () => { $I18N_2$ = $MSG_EXTERNAL_7068143081688428291$$APP_SPEC_TS_2$; } else { - $I18N_2$ = $localize \`{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}\`; + $I18N_2$ = $localize \`:@@7068143081688428291:{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}\`; } $I18N_2$ = $r3$.ɵɵi18nPostprocess($I18N_2$, { "VAR_SELECT": "\uFFFD1\uFFFD" @@ -2946,7 +2946,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_2967249209167308918$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \` $` + + $I18N_0$ = $localize \`:@@2967249209167308918: $` + String.raw `{$I18N_1$}:ICU: $` + String.raw `{$I18N_2$}:ICU_1: \`; } … @@ -2988,7 +2988,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_APP_SPEC_TS_1$; } else { - $I18N_1$ = $localize \`{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_1$ = $localize \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; } $I18N_1$ = $r3$.ɵɵi18nPostprocess($I18N_1$, { "VAR_SELECT": "\uFFFD0\uFFFD" @@ -2999,7 +2999,7 @@ describe('i18n support in the template compiler', () => { $I18N_2$ = $MSG_APP_SPEC_TS_2$; } else { - $I18N_2$ = $localize \`{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_2$ = $localize \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; } $I18N_2$ = $r3$.ɵɵi18nPostprocess($I18N_2$, { "VAR_SELECT": "\uFFFD1\uFFFD" @@ -3011,7 +3011,7 @@ describe('i18n support in the template compiler', () => { $I18N_4$ = $MSG_APP_SPEC_TS__4$; } else { - $I18N_4$ = $localize \`{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_4$ = $localize \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; } $I18N_4$ = $r3$.ɵɵi18nPostprocess($I18N_4$, { "VAR_SELECT": "\uFFFD0:1\uFFFD" @@ -3027,7 +3027,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \` $` + + $I18N_0$ = $localize \`:@@7986645988117050801: $` + String.raw `{"\uFFFDI18N_EXP_ICU\uFFFD"}:ICU: $` + String.raw `{"\uFFFD#2\uFFFD"}:START_TAG_DIV: $` + String.raw `{"\uFFFDI18N_EXP_ICU\uFFFD"}:ICU: $` + String.raw @@ -3096,7 +3096,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_343563413083115114$$APP_SPEC_TS_0$; } else { - $I18N_1$ = $localize \`{VAR_SELECT_1, select, male {male of age: {VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}} female {female} other {other}}\`; + $I18N_1$ = $localize \`:@@343563413083115114:{VAR_SELECT_1, select, male {male of age: {VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}} female {female} other {other}}\`; } $I18N_1$ = $r3$.ɵɵi18nPostprocess($I18N_1$, { "VAR_SELECT": "\uFFFD0\uFFFD", @@ -3108,7 +3108,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_3052001905251380936$$APP_SPEC_TS_3$; } else { - $I18N_0$ = $localize \` $` + + $I18N_0$ = $localize \`:@@3052001905251380936: $` + String.raw `{$I18N_1$}:ICU: \`; } … consts: 2, @@ -3151,7 +3151,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_6870293071705078389$$APP_SPEC_TS_1$; } else { - $I18N_0$ = $localize \`{VAR_PLURAL, plural, =0 {zero} =2 {{INTERPOLATION} {VAR_SELECT, select, cat {cats} dog {dogs} other {animals}} !} other {other - {INTERPOLATION}}}\`; + $I18N_0$ = $localize \`:@@6870293071705078389:{VAR_PLURAL, plural, =0 {zero} =2 {{INTERPOLATION} {VAR_SELECT, select, cat {cats} dog {dogs} other {animals}} !} other {other - {INTERPOLATION}}}\`; } $I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$, { "VAR_SELECT": "\uFFFD0\uFFFD", @@ -3194,7 +3194,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_1$; } else { - $I18N_1$ = $localize \`{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_1$ = $localize \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; } $I18N_1$ = $r3$.ɵɵi18nPostprocess($I18N_1$, { "VAR_SELECT": "\uFFFD0\uFFFD" @@ -3206,7 +3206,7 @@ describe('i18n support in the template compiler', () => { $I18N_3$ = $MSG_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$; } else { - $I18N_3$ = $localize \`{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}\`; + $I18N_3$ = $localize \`:@@7068143081688428291:{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}\`; } $I18N_3$ = $r3$.ɵɵi18nPostprocess($I18N_3$, { "VAR_SELECT": "\uFFFD0:1\uFFFD" @@ -3222,7 +3222,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_1194472282609532229$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \` $` + + $I18N_0$ = $localize \`:@@1194472282609532229: $` + String.raw `{$I18N_1$}:ICU: $` + String.raw `{"\uFFFD*2:1\uFFFD\uFFFD#1:1\uFFFD"}:START_TAG_SPAN: $` + String.raw `{$I18N_3$}:ICU_1: $` + @@ -3280,7 +3280,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_7825031864601787094$$APP_SPEC_TS_1$; } else { - $I18N_1$ = $localize \`{VAR_SELECT, select, male {male {INTERPOLATION}} female {female {INTERPOLATION_1}} other {other}}\`; + $I18N_1$ = $localize \`:@@7825031864601787094:{VAR_SELECT, select, male {male {INTERPOLATION}} female {female {INTERPOLATION_1}} other {other}}\`; } $I18N_1$ = $r3$.ɵɵi18nPostprocess($I18N_1$, { "VAR_SELECT": "\uFFFD0\uFFFD", @@ -3294,7 +3294,7 @@ describe('i18n support in the template compiler', () => { $I18N_4$ = $MSG_EXTERNAL_2310343208266678305$$APP_SPEC_TS__3$; } else { - $I18N_4$ = $localize \`{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other: {INTERPOLATION}}}\`; + $I18N_4$ = $localize \`:@@2310343208266678305:{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other: {INTERPOLATION}}}\`; } $I18N_4$ = $r3$.ɵɵi18nPostprocess($I18N_4$, { "VAR_SELECT": "\uFFFD0:1\uFFFD", @@ -3311,7 +3311,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_7186042105600518133$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \` $` + + $I18N_0$ = $localize \`:@@7186042105600518133: $` + String.raw `{I18N_1}:ICU: $` + String.raw `{"\uFFFD*2:1\uFFFD\uFFFD#1:1\uFFFD"}:START_TAG_SPAN: $` + String.raw `{I18N_4}:ICU_1: $` + @@ -3370,7 +3370,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_EXTERNAL_6318060397235942326$$APP_SPEC_TS_0$; } else { - $I18N_0$ = $localize \`{VAR_SELECT, select, male {male {PH_A}} female {female {PH_B}} other {other {PH_WITH_SPACES}}}\`; + $I18N_0$ = $localize \`:@@6318060397235942326:{VAR_SELECT, select, male {male {PH_A}} female {female {PH_B}} other {other {PH_WITH_SPACES}}}\`; } $I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$, { "VAR_SELECT": "\uFFFD0\uFFFD", diff --git a/packages/compiler/src/render3/view/i18n/localize_utils.ts b/packages/compiler/src/render3/view/i18n/localize_utils.ts index b89323acf5..83865eb03f 100644 --- a/packages/compiler/src/render3/view/i18n/localize_utils.ts +++ b/packages/compiler/src/render3/view/i18n/localize_utils.ts @@ -9,6 +9,7 @@ import * as i18n from '../../../i18n/i18n_ast'; import * as o from '../../../output/output_ast'; import {serializeIcuNode} from './icu_serializer'; +import {metaFromI18nMessage, serializeI18nMeta} from './meta'; import {formatI18nPlaceholderName} from './util'; export function createLocalizeStatements( @@ -16,15 +17,13 @@ export function createLocalizeStatements( params: {[name: string]: o.Expression}): o.Statement[] { const statements = []; - // TODO: re-enable these comments when we have a plan on how to make them work so that Closure - // compiler doesn't complain about the JSDOC comments. - - // const jsdocComment = i18nMetaToDocStmt(metaFromI18nMessage(message)); - // if (jsdocComment !== null) { - // statements.push(jsdocComment); - // } + const metaBlock = serializeI18nMeta(metaFromI18nMessage(message)); const {messageParts, placeHolders} = serializeI18nMessageForLocalize(message); + + // Update first message part with metadata + messageParts[0] = `:${metaBlock}:${messageParts[0]}`; + statements.push(new o.ExpressionStatement(variable.set( o.localizedString(messageParts, placeHolders, placeHolders.map(ph => params[ph]))))); @@ -132,4 +131,4 @@ function processMessagePieces(pieces: MessagePiece[]): messageParts.push(''); } return {messageParts, placeHolders}; -} \ No newline at end of file +} diff --git a/packages/compiler/src/render3/view/i18n/meta.ts b/packages/compiler/src/render3/view/i18n/meta.ts index e75beed98c..9c61224d18 100644 --- a/packages/compiler/src/render3/view/i18n/meta.ts +++ b/packages/compiler/src/render3/view/i18n/meta.ts @@ -178,6 +178,23 @@ export function parseI18nMeta(meta?: string): I18nMeta { return {id, meaning, description}; } +/** + * Serialize the given `meta` into a string that can be used in a `$localize` tagged string metadata + * block. The format is the same as that parsed by `parseI18nMeta()`. + * + * @param meta The metadata to serialize + */ +export function serializeI18nMeta(meta: I18nMeta): string { + let metaBlock = meta.description || ''; + if (meta.meaning) { + metaBlock = `${meta.meaning}|${metaBlock}`; + } + if (meta.id) { + metaBlock = `${metaBlock}@@${meta.id}`; + } + return metaBlock; +} + // Converts i18n meta information for a message (id, description, meaning) // to a JsDoc statement formatted as expected by the Closure compiler. export function i18nMetaToDocStmt(meta: I18nMeta): o.JSDocCommentStmt|null { diff --git a/packages/compiler/test/render3/view/i18n_spec.ts b/packages/compiler/test/render3/view/i18n_spec.ts index a92d92e09e..14bae53b7a 100644 --- a/packages/compiler/test/render3/view/i18n_spec.ts +++ b/packages/compiler/test/render3/view/i18n_spec.ts @@ -5,8 +5,6 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {I18nMeta, parseI18nMeta} from '@angular/compiler/src/render3/view/i18n/meta'; - import {AST} from '../../../src/expression_parser/ast'; import {Lexer} from '../../../src/expression_parser/lexer'; import {Parser} from '../../../src/expression_parser/parser'; @@ -17,6 +15,7 @@ import {I18nContext} from '../../../src/render3/view/i18n/context'; import {serializeI18nMessageForGetMsg} from '../../../src/render3/view/i18n/get_msg_utils'; import {serializeIcuNode} from '../../../src/render3/view/i18n/icu_serializer'; import {serializeI18nMessageForLocalize} from '../../../src/render3/view/i18n/localize_utils'; +import {I18nMeta, parseI18nMeta, serializeI18nMeta} from '../../../src/render3/view/i18n/meta'; import {formatI18nPlaceholderName} from '../../../src/render3/view/i18n/util'; import {parseR3 as parse} from './util'; @@ -200,10 +199,8 @@ describe('Utils', () => { ([input, output]) => { expect(formatI18nPlaceholderName(input)).toEqual(output); }); }); - it('parseI18nMeta', () => { - const meta = (id?: string, meaning?: string, description?: string) => - ({id, meaning, description}); - const cases = [ + describe('metadata serialization', () => { + const metadataCases: [string, I18nMeta][] = [ ['', meta()], ['desc', meta('', '', 'desc')], ['desc@@id', meta('id', '', 'desc')], @@ -211,9 +208,20 @@ describe('Utils', () => { ['meaning|desc@@id', meta('id', 'meaning', 'desc')], ['@@id', meta('id', '', '')], ]; - cases.forEach(([input, output]) => { - expect(parseI18nMeta(input as string)).toEqual(output as I18nMeta, input); + + it('parseI18nMeta()', () => { + metadataCases.forEach( + ([input, output]) => { expect(parseI18nMeta(input)).toEqual(output, input); }); }); + + it('serializeI18nMeta()', () => { + metadataCases.forEach( + ([output, input]) => { expect(serializeI18nMeta(input)).toEqual(output, input); }); + }); + + function meta(id?: string, meaning?: string, description?: string): I18nMeta { + return {id, meaning, description}; + } }); }); diff --git a/packages/core/test/acceptance/i18n_spec.ts b/packages/core/test/acceptance/i18n_spec.ts index 04ceee3d02..8c2972d326 100644 --- a/packages/core/test/acceptance/i18n_spec.ts +++ b/packages/core/test/acceptance/i18n_spec.ts @@ -19,155 +19,141 @@ 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({'3667842621564887364': '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({'4848371593517801531': '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 {$USER_NAME}! Emails: {$AMOUNT_OF_EMAILS_RECEIVED} ': - ' Bonjour {$USER_NAME}! Emails: {$AMOUNT_OF_EMAILS_RECEIVED} ' - }); - const fixture = initWithTemplate(AppComp, ` + it('should support named interpolations', () => { + loadTranslations( + {'5429622669757720522': ' 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({'3771704108176831903': '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({'2564699219547368060': '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({ + '3552931695168961643': ' {$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 {$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 elements', () => { + loadTranslations({ + '4352419997312772902': + '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 {$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 removing elements', () => { + loadTranslations({'4038508921050216955': '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 {$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 moving elements', () => { + loadTranslations({ + '4352419997312772902': + '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: {$START_TAG_DIV}before{$START_TAG_SPAN}middle{$CLOSE_TAG_SPAN}after{$CLOSE_TAG_DIV}!': - 'Contenu: {$START_TAG_DIV}avant{$START_TAG_SPAN}milieu{$CLOSE_TAG_SPAN}après{$CLOSE_TAG_DIV}!' - }); - 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', - '{$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, ` + it('should support multiple i18n blocks', () => { + loadTranslations({ + '2954523302699461941': 'traduction {$INTERPOLATION}', + '6316306955288477512': 'start {$INTERPOLATION_1} middle {$INTERPOLATION} end', + '4435073550869683142': '{$START_TAG_E}{$CLOSE_TAG_E}{$START_TAG_C}traduction{$CLOSE_TAG_C}' + }); + const fixture = initWithTemplate(AppComp, `
trad {{name}} hello @@ -177,148 +163,140 @@ onlyInIvy('Ivy i18n logic')
`); - 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({ + '1789603390900945185': 'Section un', + '3131519605456222658': 'Section deux', + '1438862747497772785': '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({ + '1789603390900945185': 'Section un', + '3131519605456222658': 'Section deux', + '1438862747497772785': '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({'7582605960852813916': '\'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({'4691107123853093118': '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({'3771704108176831903': '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({'3667842621564887364': '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({'3771704108176831903': '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'); + }); - // Note: applying structural directives to is typically user error, but it - // is technically allowed, so we need to support it. - it('should handle structural directives on ng-template', () => { - loadTranslations({'Hello {$INTERPOLATION}': 'Bonjour {$INTERPOLATION}'}); - const fixture = initWithTemplate( - AppComp, `Hello {{ name }}`); + // Note: applying structural directives to is typically user error, but it + // is technically allowed, so we need to support it. + it('should handle structural directives on ng-template', () => { + loadTranslations({'3771704108176831903': '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({ - - '{$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, ` + it('should be able to act as child elements inside i18n block (plain text content)', () => { + loadTranslations({ + '3250373733165819471': + '{$START_TAG_NG_TEMPLATE} Bonjour {$CLOSE_TAG_NG_TEMPLATE}{$START_TAG_NG_CONTAINER} Au revoir {$CLOSE_TAG_NG_CONTAINER}' + }); + const fixture = initWithTemplate(AppComp, `
Hello @@ -329,17 +307,16 @@ onlyInIvy('Ivy i18n logic')
`); - 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({ - - '{$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, ` + it('should be able to act as child elements inside i18n block (text + tags)', () => { + loadTranslations({ + '7109932217076518742': + '{$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 @@ -350,44 +327,35 @@ onlyInIvy('Ivy i18n logic')
`); - 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 = '{$START_TAG_NG_TEMPLATE}Hello {$INTERPOLATION}{$CLOSE_TAG_NG_TEMPLATE}' + - '{$START_TAG_NG_CONTAINER}Bye {$INTERPOLATION}{$CLOSE_TAG_NG_CONTAINER}'; - - loadTranslations({ - - [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, ` + it('should be able to act as child elements inside i18n block (text + pipes)', () => { + loadTranslations({ + '2351903444091919458': + '{$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({ - - '{$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, ` + it('should be able to handle deep nested levels with templates', () => { + loadTranslations({ + '3582176345457676': + '{$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 @@ -407,21 +375,18 @@ onlyInIvy('Ivy i18n logic')
`); - 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({ - - '{$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, ` + it('should handle self-closing tags as content', () => { + loadTranslations( + {'3294715134260231285': '{$START_TAG_SPAN}Mon logo{$TAG_IMG}{$CLOSE_TAG_SPAN}'}); + const content = `My logo`; + const fixture = initWithTemplate(AppComp, ` ${content} @@ -430,24 +395,23 @@ onlyInIvy('Ivy i18n logic')
`); - 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
@@ -455,187 +419,160 @@ onlyInIvy('Ivy i18n logic') 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( + {'8806993169187953163': '{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( + {'8806993169187953163': '{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}'}); + const fixture = initWithTemplate( + AppComp, `
{count, select, 10 {ten} 20 {twenty} other {other}}
`); - it('with no i18n tag', () => { - loadTranslations({ + const element = fixture.nativeElement; + expect(element).toHaveText('autre'); + }); - '{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'); - }); - - 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, + it('multiple', () => { + loadTranslations({ + '3639715378617754400': + '{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}}}', + '484647350062685600': '{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( + {'8091236997374391053': '{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({ - - '{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, + it('inside HTML elements', () => { + loadTranslations({ + '3639715378617754400': + '{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}}}', + '484647350062685600': '{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({ - - '{VAR_SELECT, select, other {(name)}}': - '{VAR_SELECT, select, other {({$INTERPOLATION})}}' - }); - const fixture = - initWithTemplate(AppComp, `
{name, select, + it('inside template directives', () => { + loadTranslations({'484647350062685600': '{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({ - - '{VAR_SELECT, select, other {(name)}}': - '{VAR_SELECT, select, other {({$INTERPOLATION})}}' - }); - const fixture = initWithTemplate(AppComp, `{name, select, + it('inside ng-container', () => { + loadTranslations({'484647350062685600': '{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({ - - '{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}': - '{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}' - }); - const fixture = initWithTemplate( - AppComp, ` + it('inside ', () => { + loadTranslations( + {'8806993169187953163': '{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({ - - '{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, + it('nested', () => { + loadTranslations({ + '6934090145627876010': + '{VAR_PLURAL, plural, =0 {zero} other {{INTERPOLATION} {VAR_SELECT, select, cat {chats} dog {chiens} other {animaux}}!}}' + }); + const fixture = initWithTemplate(AppComp, `
{count, plural, =0 {zero} other {{{count}} {name, select, cat {cats} @@ -643,25 +580,22 @@ onlyInIvy('Ivy i18n logic') 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 translation = + '{VAR_PLURAL, plural, =0 {zero} =2 {{INTERPOLATION} {VAR_SELECT, select, ' + + 'cat {chats} dog {chiens} other {animaux}}!} other {other - {INTERPOLATION}}}'; + loadTranslations({'3182623695425530660': translation}); - const fixture = initWithTemplate(AppComp, `
{count, plural, + const fixture = initWithTemplate(AppComp, `
{count, plural, =0 {zero} =2 {{{count}} {name, select, cat {cats} @@ -670,25 +604,24 @@ onlyInIvy('Ivy i18n logic') }!} 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} @@ -696,94 +629,93 @@ onlyInIvy('Ivy i18n logic') =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, @@ -791,70 +723,68 @@ onlyInIvy('Ivy i18n logic') 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, @@ -866,36 +796,36 @@ onlyInIvy('Ivy i18n logic') }!} } `, - }) - 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 }} @@ -905,31 +835,31 @@ onlyInIvy('Ivy i18n logic') `, - }) - 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, @@ -938,32 +868,32 @@ onlyInIvy('Ivy i18n logic') 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, @@ -978,176 +908,174 @@ onlyInIvy('Ivy i18n logic')
` - }) - 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({'3667842621564887364': '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({'1721737630608625819': '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({'5990850149002293021': '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({'1721737630608625819': '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', '{$START_TAG_SPAN}content{$CLOSE_TAG_SPAN}': 'contenu'}); - const fixture = initWithTemplate( - AppComp, `
content
`); - expect(fixture.nativeElement.innerHTML).toEqual(`
contenu
`); - }); + it('on removed elements', () => { + loadTranslations({ + '3667842621564887364': 'texte', + '4851941758912026972': '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({'6353989911216474113': '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({'5108705942887033129': '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({'3771704108176831903': '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({ + '305276527729153743': '{$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!} @@ -1155,81 +1083,82 @@ onlyInIvy('Ivy i18n logic') 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({ + // Note that this translation switches the order of the expressions! + '2244013204777604001': 'début {$INTERPOLATION_1} milieu {$INTERPOLATION} fin', + '7297507129788731971': + '{VAR_PLURAL, plural, =0 {aucun {START_BOLD_TEXT}email{CLOSE_BOLD_TEXT}!} =1 {un {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} emails}}', + '7694415136422535717': ' 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({ + '3299467052279745802': 'Bonjour {$INTERPOLATION}', + '694232821652669635': '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({ - '{$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, ` + it('should support adding/moving/removing nodes', () => { + loadTranslations({ + '3934630822967503512': + '{$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, `
@@ -1239,20 +1168,20 @@ onlyInIvy('Ivy i18n logic')
`); - 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}} @@ -1260,32 +1189,31 @@ onlyInIvy('Ivy i18n logic')
` - }) - class Parent { - name: string = 'Parent'; - } - TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({ + }) + class Parent { + name: string = 'Parent'; + } + TestBed.configureTestingModule({declarations: [Parent, Child]}); + loadTranslations({ + '7180841260895623479': 'Enfant de {$INTERPOLATION}', + '2325314462315226487': + '{$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

`); + }); - '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: `
@@ -1293,381 +1221,341 @@ onlyInIvy('Ivy i18n logic')
` - }) - class Parent { - name: string = 'Parent'; - } - TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({ + }) + class Parent { + name: string = 'Parent'; + } + TestBed.configureTestingModule({declarations: [Parent, Child]}); + loadTranslations({ + '7180841260895623479': 'Enfant de {$INTERPOLATION}', + '7230149585615622873': '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( + {'9156403697449779168': '{$START_BOLD_TEXT}Bonjour{$CLOSE_BOLD_TEXT} monde!'}); + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual('
Bonjour monde!
'); + }); - TestBed.configureTestingModule({declarations: [Parent, Child, GrandChild]}); - loadTranslations({ + it('should be able to remove projected placeholders', () => { + @Component({selector: 'grand-child', template: '
'}) + class GrandChild { + } - '{$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!
'); - }); + @Component( + {selector: 'child', template: ''}) + class Child { + } - it('should be able to remove projected placeholders', () => { - @Component( - {selector: 'grand-child', template: '
'}) - class GrandChild { - } + @Component({selector: 'parent', template: `Hello World!`}) + class Parent { + name: string = 'Parent'; + } - @Component({ - selector: 'child', - template: '' - }) - class Child { - } + TestBed.configureTestingModule({declarations: [Parent, Child, GrandChild]}); + loadTranslations({'9156403697449779168': 'Bonjour monde!'}); + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual('
Bonjour monde!
'); + }); - @Component({selector: 'parent', template: `Hello World!`}) - class Parent { - name: string = 'Parent'; - } + it('should project translations with selectors', () => { + @Component({selector: 'child', template: ``}) + class Child { + } - 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 { - } - - @Component({ - selector: 'parent', - template: ` + @Component({ + selector: 'parent', + template: ` ` - }) - class Parent { - } + }) + class Parent { + } - TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({ + TestBed.configureTestingModule({declarations: [Parent, Child]}); + loadTranslations({'2827528023268681613': '{$START_TAG_SPAN}Contenu{$CLOSE_TAG_SPAN}'}); + 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({ + '4431461951818971024': + 'Contenu projeté depuis {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}' + }); - @Component({selector: 'parent', template: `{{name}}`}) - class Parent { - name: string = 'Parent'; - } - TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({ + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
Contenu projeté depuis Parent
`); - 'Content projected from {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}': - 'Contenu projeté depuis {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}' - }); + fixture.componentRef.instance.name = 'Parent component'; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
Contenu projeté depuis Parent component
`); + }); - const fixture = TestBed.createComponent(Parent); - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual(`
Contenu projeté depuis Parent
`); + it('should project content in i18n blocks with placeholders', () => { + @Component({ + selector: 'child', + template: `
Content projected from
` + }) + class Child { + } - fixture.componentRef.instance.name = 'Parent component'; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML) - .toEqual(`
Contenu projeté depuis Parent component
`); - }); + @Component({selector: 'parent', template: `{{name}}`}) + class Parent { + name: string = 'Parent'; + } + TestBed.configureTestingModule({declarations: [Parent, Child]}); + loadTranslations({ + '4431461951818971024': '{$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 content in i18n blocks with placeholders', () => { - @Component({ - selector: 'child', - template: `
Content projected from
` - }) - class Child { - } + it('should project translated content in i18n blocks', () => { + @Component( + {selector: 'child', template: `
Child content
`}) + class Child { + } - @Component({selector: 'parent', template: `{{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({ + '3861221266941684467': 'Contenu enfant {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}', + '6186458652454735259': 'et projection depuis {$INTERPOLATION}' + }); + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML) + .toEqual(`
Contenu enfant et projection depuis Parent
`); + }); - '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 bare ICU expressions', () => { + @Component({selector: 'child', template: '
'}) + 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({ - - '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 { - } - - @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({ + '3861221266941684467': 'Contenu enfant {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}', + '4986299685320703675': '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({ + '3861221266941684467': 'Contenu enfant', + '6186458652454735259': 'et projection depuis {$INTERPOLATION}' + }); + const fixture = TestBed.createComponent(Parent); + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual(`
Contenu enfant
`); + }); - @Component({ - selector: 'parent', - template: `and projection from {{name}}` - }) - class Parent { - name: string = 'Parent'; - } - TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({ + it('should display/destroy projected i18n content', () => { - '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', () => { - - @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({ - - '{$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, ` + TestBed.configureTestingModule({declarations: [TextDirective, DivQuery]}); + loadTranslations( + {'3395826043214005257': '{$START_TAG_NG_TEMPLATE}Contenu{$CLOSE_TAG_NG_TEMPLATE}'}); + const fixture = initWithTemplate(AppComp, `
@@ -1678,28 +1566,28 @@ onlyInIvy('Ivy i18n logic') `); - 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 @@ -1707,21 +1595,20 @@ onlyInIvy('Ivy i18n logic')
` - }) - 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 80d7231b1d..7f13592ff8 100644 --- a/packages/core/test/acceptance/view_container_ref_spec.ts +++ b/packages/core/test/acceptance/view_container_ref_spec.ts @@ -19,11 +19,9 @@ import {ivyEnabled, onlyInIvy} from '@angular/private/testing'; describe('ViewContainerRef', () => { const TRANSLATIONS: any = { - 'Bar': 'o', - '{$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}' + '6587679027921703718': 'o', + '1059980791999999199': 'F{$START_TAG_DIV}{$CLOSE_TAG_DIV}o', + '8808498902423973223': '{$START_TAG_DIV}{$CLOSE_TAG_DIV}{$START_TAG_BEFORE}{$CLOSE_TAG_BEFORE}' }; /** diff --git a/packages/core/test/bundling/hello_world_i18n/translations.ts b/packages/core/test/bundling/hello_world_i18n/translations.ts index 7ffe25a657..aabcc78c96 100644 --- a/packages/core/test/bundling/hello_world_i18n/translations.ts +++ b/packages/core/test/bundling/hello_world_i18n/translations.ts @@ -11,8 +11,8 @@ import '@angular/localize/init'; import {loadTranslations} from '@angular/localize'; const translations = { - 'Hello World!': 'Bonjour Monde!', - 'Hello Title!': 'Bonjour Titre!', + '6947830843539421219': 'Bonjour Monde!', + '6935800796474951272': 'Bonjour Titre!', }; loadTranslations(translations); diff --git a/packages/core/test/bundling/todo_i18n/translations.ts b/packages/core/test/bundling/todo_i18n/translations.ts index 370bad234a..8b2848e61e 100644 --- a/packages/core/test/bundling/todo_i18n/translations.ts +++ b/packages/core/test/bundling/todo_i18n/translations.ts @@ -11,19 +11,27 @@ import '@angular/localize/init'; import {loadTranslations} from '@angular/localize'; export const translations = { - 'What needs to be done?': `Qu'y a-t-il à faire ?`, - '{$START_HEADING_LEVEL1}todos{$CLOSE_HEADING_LEVEL1}{$TAG_INPUT}': + // What needs to be done? + '5102526651904871634': `Qu'y a-t-il à faire ?`, + // {$START_HEADING_LEVEL1}todos{$CLOSE_HEADING_LEVEL1}{$TAG_INPUT} + '8643091609122689720': '{$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}}', - '{$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', - 'Demonstrate {$value}': 'Démontrer {$value}', - 'Demonstrate zoneless change detection': 'Démontrer la détection des changements sans zonejs', - 'Demonstrate internationalization': `Démontrer l'internationalisation` + // {VAR_PLURAL, plural, =1 {item left} other {items left}} + '271375439086996113': '{VAR_PLURAL, plural, =1 {tâche restante} other {tâches restantes}}', + // {$START_TAG_STRONG}{$INTERPOLATION}{$CLOSE_TAG_STRONG}{$ICU} + '4169337202119891309': '{$START_TAG_STRONG}{$INTERPOLATION}{$CLOSE_TAG_STRONG} {$ICU}', + // Clear Completed + '3329962774478249377': ' Effacer terminés ', + // Demonstrate Components + '5738403869589701812': ' Démontrer les components', + // Demonstrate Structural Directives + '4405796757024158842': 'Démontrer les directives structurelles', + // Demonstrate {$value} + '2762077329405284613': 'Démontrer {$value}', + // Demonstrate zoneless change detection + '3484387157632222646': 'Démontrer la détection des changements sans zonejs', + // Demonstrate internationalization + '442837859415373816': `Démontrer l'internationalisation` }; loadTranslations(translations);