From 9188751adccf09721e8d87b84561e331828bc134 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 2 Oct 2019 18:17:56 +0100 Subject: [PATCH] fix(ivy): i18n - do not render message ids unnecessarily (#32867) In an attempt to be compatible with previous translation files the Angular compiler was generating instructions that always included the message id. This was because it was not possible to accurately re-generate the id from the calls to `$localize()` alone. In line with https://hackmd.io/EQF4_-atSXK4XWg8eAha2g this commit changes the compiler so that it only renders ids if they are "custom" ones provided by the template author. NOTE: When translating messages generated by the Angular compiler from i18n tags in templates, the `$localize.translate()` function will compute message ids, if no custom id is provided, using a common digest function that only relies upon the information available in the `$localize()` calls. This computed message id will not be the same as the message ids stored in legacy translation files. Such files will need to be migrated to use the new common digest function. This only affects developers who have been trialling `$localize`, have been calling `loadTranslations()`, and are not exclusively using custom ids in their templates. PR Close #32867 --- .../compliance/r3_view_compiler_i18n_spec.ts | 184 ++++---- packages/compiler/src/i18n/digest.ts | 2 +- packages/compiler/src/i18n/i18n_ast.ts | 3 +- .../compiler/src/render3/view/i18n/meta.ts | 14 +- packages/compiler/test/i18n/digest_spec.ts | 1 + .../compiler/test/render3/view/i18n_spec.ts | 4 +- packages/core/test/acceptance/i18n_spec.ts | 413 ++++++++++++------ .../acceptance/view_container_ref_spec.ts | 34 +- .../bundling/hello_world_i18n/translations.ts | 5 +- .../test/bundling/todo_i18n/translations.ts | 35 +- packages/localize/src/utils/messages.ts | 5 + 11 files changed, 439 insertions(+), 261 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 5b331533ee..58b3937361 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 @@ -219,7 +219,7 @@ describe('i18n support in the template compiler', () => { $I18N_7$ = $MSG_EXTERNAL_6435899732746131543$$APP_SPEC_TS_8$; } else { - $I18N_7$ = $localize \`:meaningC|@@6435899732746131543:Title C\`; + $I18N_7$ = $localize \`:meaningC|: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 \`:meaningD|descD@@5200291527729162531:Title D\`; + $I18N_11$ = $localize \`:meaningD|descD:Title D\`; } const $_c13$ = ["title", $I18N_11$]; var $I18N_15$; @@ -365,7 +365,7 @@ describe('i18n support in the template compiler', () => { $I18N_1$ = $MSG_EXTERNAL_8809028065680254561$$APP_SPEC_TS_1$; } else { - $I18N_1$ = $localize \`:m|d@@8809028065680254561:introduction\`; + $I18N_1$ = $localize \`:m|d: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 \`:@@5526535577705876535:static text\`; + $I18N_1$ = $localize \`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 \`:m|d@@8977039798304050198:intro $` + + $I18N_2$ = $localize \`:m|d: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 \`:m1|d1@@7432761130955693041:$` + + $I18N_3$ = $localize \`:m1|d1:$` + 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 \`:m2|d2@@7566208596013750546:$` + + $I18N_6$ = $localize \`:m2|d2:$` + 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 \`:@@6639222533406278123:$` + + $I18N_7$ = $localize \`$` + 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 \`:m|d@@8977039798304050198:intro $` + + $I18N_1$ = $localize \`:m|d: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 \`:m|d@@8538466649243975456:different scope $` + + $I18N_1$ = $localize \`:m|d: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 \`:@@3462388422673575127:$` + + $I18N_1$ = $localize \`$` + 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 \`:@@5526535577705876535:static text\`; + $I18N_1$ = $localize \`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 \`:m|d@@8977039798304050198:intro $` + + $I18N_2$ = $localize \`:m|d: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 \`:m1|d1@@7432761130955693041:$` + + $I18N_3$ = $localize \`:m1|d1:$` + 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 \`:m2|d2@@7566208596013750546:$` + + $I18N_6$ = $localize \`:m2|d2:$` + 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 \`:@@6639222533406278123:$` + + $I18N_7$ = $localize \`$` + 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 \`:m|d@@8538466649243975456:different scope $` + + $I18N_2$ = $localize \`:m|d: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 \`:m|d@@7727043314656808423:Element title\`; + $I18N_0$ = $localize \`:m|d: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 \`:@@4969674997806975147:Some content\`; + $I18N_2$ = $localize \`Some content\`; } … template: function MyComponent_Template(rf, ctx) { @@ -926,7 +926,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_APP_SPEC_TS_1$; } else { - $I18N_0$ = $localize \`:@@3784161717320915177:Some text\`; + $I18N_0$ = $localize \`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 \`:@@4924931801512133405:Some text 'with single quotes', "with double quotes" and without quotes.\`; + $I18N_0$ = $localize \`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 \`:@@4890179241114413722:My i18n block #1\`; + $I18N_0$ = $localize \`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 \`:@@2413150872298537152:My i18n block #2\`; + $I18N_1$ = $localize \`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 \`:@@5023003143537152794:My i18n block #3\`; + $I18N_2$ = $localize \`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 \`:@@7597881511811528589: Named interpolation: $` + + $I18N_0$ = $localize \` 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 \`:@@6749967533321674787:$` + + $I18N_0$ = $localize \`$` + String.raw `{"\uFFFD0\uFFFD"}:INTERPOLATION:\`; } … @@ -1104,7 +1104,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_APP_SPEC_TS_1$$APP_SPEC_TS_1$; } else { - $I18N_0$ = $localize \`:@@1194644703943451474: $` + + $I18N_0$ = $localize \` $` + 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 \`:@@572579892698764378:My i18n block #$` + + $I18N_0$ = $localize \`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 \`:@@609623417156596326:My i18n block #$` + + $I18N_1$ = $localize \`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 \`:@@3998119318957372120:My i18n block #$` + + $I18N_2$ = $localize \`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 \`:@@7905233330103651696: My i18n block #$` + + $I18N_0$ = $localize \` 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 \`:@@5788821996131681377: My i18n block #$` + + $I18N_1$ = $localize \` 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 \`:@@4782264005467235841:Span title $` + + $I18N_2$ = $localize \`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 \`:@@4446430594603971069: My i18n block #1 with value: $` + + $I18N_0$ = $localize \` 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 \`:@@2719594642740200058:Span title $` + + $I18N_7$ = $localize \`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 \`:@@2778714953278357902: My i18n block #2 with value $` + + $I18N_6$ = $localize \` 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 \`:@@7679414751795588050: Some other content $` + + $I18N_1$ = $localize \` 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 \`:@@2367729185105559721:App logo #$` + + $I18N_2$ = $localize \`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 \`:@@1221890473527419724: Some content $` + + $I18N_0$ = $localize \` 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 \`:@@119975189388320493:Some other content $` + + $I18N_1$ = $localize \`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 \`:@@3902961887793684628:Hello\`; + $I18N_1$ = $localize \`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 \`:@@4890179241114413722:My i18n block #1\`; + $I18N_0$ = $localize \`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 \`:@@8806993169187953163:{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}\`; + $I18N_0$ = $localize \`{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 \`:@@2413150872298537152:My i18n block #2\`; + $I18N_0$ = $localize \`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 \`:@@4890179241114413722:My i18n block #1\`; + $I18N_1$ = $localize \`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 \`:@@5295701706185791735:Text #1\`; + $I18N_1$ = $localize \`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 \`:@@4722270221386399294:Text #2\`; + $I18N_3$ = $localize \`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 \`:@@355394464191978948:Some content: $` + + $I18N_0$ = $localize \`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 \`:@@355394464191978948:Some content: $` + + $I18N_0$ = $localize \`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 \`:@@702706566400598764:$` + + $I18N_0$ = $localize \`$` + 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 \`:@@8806993169187953163:{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}\`; + $I18N_0$ = $localize \`{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 \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_1$ = $localize \`{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 \`:@@2051477021417799640:$` + + $I18N_0$ = $localize \`$` + 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 \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_0$ = $localize \`{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 \`:@@8806993169187953163:{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}\`; + $I18N_1$ = $localize \`{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 \`:@@4891196282781544695:$` + + $I18N_0$ = $localize \`$` + 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 \`:@@461986953980355147:$` + + $I18N_2$ = $localize \`$` + 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 \`:@@8537814667662432133: Root content $` + + $I18N_0$ = $localize \` 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 \`:@@6563391987554512024:Test\`; + $I18N_0$ = $localize \`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 \`:@@6563391987554512024:Test\`; + $I18N_1$ = $localize \`Test\`; } … `; @@ -2401,7 +2401,7 @@ describe('i18n support in the template compiler', () => { $I18N_0$ = $MSG_APP_SPEC_TS_1$; } else { - $I18N_0$ = $localize \`:@@161173306924314453: Hello $` + + $I18N_0$ = $localize \` 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 \`:@@1968828034476446869: Hello $` + + $I18N_0$ = $localize \` 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 \`:@@3308216566145348998:Content A\`; + $I18N_1$ = $localize \`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 \`:@@8349021389088127654:Content B\`; + $I18N_3$ = $localize \`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 \`:@@963542717423364282:\n Some text\n $` + + $I18N_0$ = $localize \`\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 \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_0$ = $localize \`{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 \`:@@4166854826696768832:{VAR_SELECT, select, single {'single quotes'} double {"double quotes"} other {other}}\`; + $I18N_0$ = $localize \`{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 \`:@@8806993169187953163:{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}\`; + $I18N_0$ = $localize \`{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 \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_0$ = $localize \`{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 \`:@@8806993169187953163:{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}\`; + $I18N_3$ = $localize \`{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 \`:@@1922743304863699161:{VAR_SELECT, select, 0 {no emails} 1 {one email} other {{INTERPOLATION} emails}}\`; + $I18N_5$ = $localize \`{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 \`:@@2949673783721159566:{VAR_SELECT, select, 10 {ten} 20 {twenty} other {{INTERPOLATION}}}\`; + $I18N_0$ = $localize \`{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 \`:@@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$ = $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$ = $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 \`:@@5791551881115084301: $` + + $I18N_0$ = $localize \` $` + 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 \`:@@6879461626778511059:{VAR_SELECT, select, male {male of age: {INTERPOLATION}} female {female} other {other}}\`; + $I18N_0$ = $localize \`{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 \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_1$ = $localize \`{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 \`:@@7068143081688428291:{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}\`; + $I18N_2$ = $localize \`{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 \`:@@2967249209167308918: $` + + $I18N_0$ = $localize \` $` + 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 \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_1$ = $localize \`{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 \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_2$ = $localize \`{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 \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_4$ = $localize \`{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 \`:@@7986645988117050801: $` + + $I18N_0$ = $localize \` $` + 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 \`:@@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$ = $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$ = $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 \`:@@3052001905251380936: $` + + $I18N_0$ = $localize \` $` + 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 \`:@@6870293071705078389:{VAR_PLURAL, plural, =0 {zero} =2 {{INTERPOLATION} {VAR_SELECT, select, cat {cats} dog {dogs} other {animals}} !} other {other - {INTERPOLATION}}}\`; + $I18N_0$ = $localize \`{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 \`:@@7842238767399919809:{VAR_SELECT, select, male {male} female {female} other {other}}\`; + $I18N_1$ = $localize \`{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 \`:@@7068143081688428291:{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}\`; + $I18N_3$ = $localize \`{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 \`:@@1194472282609532229: $` + + $I18N_0$ = $localize \` $` + 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 \`:@@7825031864601787094:{VAR_SELECT, select, male {male {INTERPOLATION}} female {female {INTERPOLATION_1}} other {other}}\`; + $I18N_1$ = $localize \`{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 \`:@@2310343208266678305:{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other: {INTERPOLATION}}}\`; + $I18N_4$ = $localize \`{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 \`:@@7186042105600518133: $` + + $I18N_0$ = $localize \` $` + 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 \`:@@6318060397235942326:{VAR_SELECT, select, male {male {PH_A}} female {female {PH_B}} other {other {PH_WITH_SPACES}}}\`; + $I18N_0$ = $localize \`{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/i18n/digest.ts b/packages/compiler/src/i18n/digest.ts index 459461715d..6c735e2a20 100644 --- a/packages/compiler/src/i18n/digest.ts +++ b/packages/compiler/src/i18n/digest.ts @@ -157,7 +157,7 @@ export function fingerprint(str: string): [number, number] { return [hi, lo]; } -export function computeMsgId(msg: string, meaning: string): string { +export function computeMsgId(msg: string, meaning: string = ''): string { let [hi, lo] = fingerprint(msg); if (meaning) { diff --git a/packages/compiler/src/i18n/i18n_ast.ts b/packages/compiler/src/i18n/i18n_ast.ts index 02ddf74733..03663ab030 100644 --- a/packages/compiler/src/i18n/i18n_ast.ts +++ b/packages/compiler/src/i18n/i18n_ast.ts @@ -10,6 +10,7 @@ import {ParseSourceSpan} from '../parse_util'; export class Message { sources: MessageSpan[]; + id: string = this.customId; /** * @param nodes message AST @@ -22,7 +23,7 @@ export class Message { constructor( public nodes: Node[], public placeholders: {[phName: string]: string}, public placeholderToMessage: {[phName: string]: Message}, public meaning: string, - public description: string, public id: string) { + public description: string, public customId: string) { if (nodes.length) { this.sources = [{ filePath: nodes[0].sourceSpan.start.file.url, diff --git a/packages/compiler/src/render3/view/i18n/meta.ts b/packages/compiler/src/render3/view/i18n/meta.ts index f627d846b0..1b14d4b8b8 100644 --- a/packages/compiler/src/render3/view/i18n/meta.ts +++ b/packages/compiler/src/render3/view/i18n/meta.ts @@ -18,6 +18,7 @@ import {I18N_ATTR, I18N_ATTR_PREFIX, hasI18nAttrs, icuFromI18nMessage} from './u export type I18nMeta = { id?: string, + customId?: string, description?: string, meaning?: string }; @@ -47,7 +48,7 @@ export class I18nMetaVisitor implements html.Visitor { const parsed: I18nMeta = typeof meta === 'string' ? parseI18nMeta(meta) : metaFromI18nMessage(meta as i18n.Message); const message = this._createI18nMessage( - nodes, parsed.meaning || '', parsed.description || '', parsed.id || '', visitNodeFn); + nodes, parsed.meaning || '', parsed.description || '', parsed.customId || '', visitNodeFn); if (!message.id) { // generate (or restore) message id if not specified in template message.id = typeof meta !== 'string' && (meta as i18n.Message).id || decimalDigest(message); @@ -140,6 +141,7 @@ export function processI18nMeta( export function metaFromI18nMessage(message: i18n.Message, id: string | null = null): I18nMeta { return { id: typeof id === 'string' ? id : message.id || '', + customId: message.customId, meaning: message.meaning || '', description: message.description || '' }; @@ -160,7 +162,7 @@ const I18N_ID_SEPARATOR = '@@'; * @returns Object with id, meaning and description fields */ export function parseI18nMeta(meta?: string): I18nMeta { - let id: string|undefined; + let customId: string|undefined; let meaning: string|undefined; let description: string|undefined; @@ -168,14 +170,14 @@ export function parseI18nMeta(meta?: string): I18nMeta { const idIndex = meta.indexOf(I18N_ID_SEPARATOR); const descIndex = meta.indexOf(I18N_MEANING_SEPARATOR); let meaningAndDesc: string; - [meaningAndDesc, id] = + [meaningAndDesc, customId] = (idIndex > -1) ? [meta.slice(0, idIndex), meta.slice(idIndex + 2)] : [meta, '']; [meaning, description] = (descIndex > -1) ? [meaningAndDesc.slice(0, descIndex), meaningAndDesc.slice(descIndex + 1)] : ['', meaningAndDesc]; } - return {id, meaning, description}; + return {customId, meaning, description}; } /** @@ -190,8 +192,8 @@ export function serializeI18nHead(meta: I18nMeta, messagePart: string): string { if (meta.meaning) { metaBlock = `${meta.meaning}|${metaBlock}`; } - if (meta.id) { - metaBlock = `${metaBlock}@@${meta.id}`; + if (meta.customId) { + metaBlock = `${metaBlock}@@${meta.customId}`; } if (metaBlock === '') { // There is no metaBlock, so we must ensure that any starting colon is escaped. diff --git a/packages/compiler/test/i18n/digest_spec.ts b/packages/compiler/test/i18n/digest_spec.ts index 22bd7b6eb6..9cb788efac 100644 --- a/packages/compiler/test/i18n/digest_spec.ts +++ b/packages/compiler/test/i18n/digest_spec.ts @@ -20,6 +20,7 @@ import {computeMsgId, digest, sha1} from '../../src/i18n/digest'; meaning: '', description: '', sources: [], + customId: 'i', })).toEqual('i'); }); }); diff --git a/packages/compiler/test/render3/view/i18n_spec.ts b/packages/compiler/test/render3/view/i18n_spec.ts index 6e06ef9e50..c69ad86693 100644 --- a/packages/compiler/test/render3/view/i18n_spec.ts +++ b/packages/compiler/test/render3/view/i18n_spec.ts @@ -241,8 +241,8 @@ describe('Utils', () => { expect(serializeI18nTemplatePart('abc', ':message')).toEqual(':abc::message'); }); - function meta(id?: string, meaning?: string, description?: string): I18nMeta { - return {id, meaning, description}; + function meta(customId?: string, meaning?: string, description?: string): I18nMeta { + return {customId, meaning, description}; } }); }); diff --git a/packages/core/test/acceptance/i18n_spec.ts b/packages/core/test/acceptance/i18n_spec.ts index 8c2972d326..fc7cd220e8 100644 --- a/packages/core/test/acceptance/i18n_spec.ts +++ b/packages/core/test/acceptance/i18n_spec.ts @@ -13,10 +13,11 @@ import localeRo from '@angular/common/locales/ro'; import {Component, ContentChild, ContentChildren, Directive, HostBinding, Input, LOCALE_ID, QueryList, TemplateRef, Type, ViewChild, ViewContainerRef, Pipe, PipeTransform} from '@angular/core'; import {setDelayProjection} from '@angular/core/src/render3/instructions/projection'; import {TestBed} from '@angular/core/testing'; -import {loadTranslations} from '@angular/localize'; +import {loadTranslations, clearTranslations} from '@angular/localize'; import {By} from '@angular/platform-browser'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {onlyInIvy} from '@angular/private/testing'; +import {computeMsgId} from '@angular/compiler'; onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { @@ -24,16 +25,19 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { TestBed.configureTestingModule({declarations: [AppComp, DirectiveWithTplRef, UppercasePipe]}); }); - afterEach(() => { setDelayProjection(false); }); + afterEach(() => { + setDelayProjection(false); + clearTranslations(); + }); it('should translate text', () => { - loadTranslations({'3667842621564887364': 'texte'}); + loadTranslations({[computeMsgId('text')]: 'texte'}); const fixture = initWithTemplate(AppComp, `
text
`); expect(fixture.nativeElement.innerHTML).toEqual(`
texte
`); }); it('should support interpolations', () => { - loadTranslations({'4848371593517801531': 'Bonjour {$INTERPOLATION}!'}); + loadTranslations({[computeMsgId('Hello {$INTERPOLATION}!')]: 'Bonjour {$INTERPOLATION}!'}); const fixture = initWithTemplate(AppComp, `
Hello {{name}}!
`); expect(fixture.nativeElement.innerHTML).toEqual(`
Bonjour Angular!
`); fixture.componentRef.instance.name = `John`; @@ -42,8 +46,10 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('should support named interpolations', () => { - loadTranslations( - {'5429622669757720522': ' Bonjour {$USER_NAME}! Emails: {$AMOUNT_OF_EMAILS_RECEIVED} '}); + loadTranslations({ + [computeMsgId(' Hello {$USER_NAME}! Emails: {$AMOUNT_OF_EMAILS_RECEIVED} ')]: + ' Bonjour {$USER_NAME}! Emails: {$AMOUNT_OF_EMAILS_RECEIVED} ' + }); const fixture = initWithTemplate(AppComp, `
Hello {{ name // i18n(ph="user_name") }}! @@ -58,7 +64,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('should support interpolations with custom interpolation config', () => { - loadTranslations({'3771704108176831903': 'Bonjour {$INTERPOLATION}'}); + loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'}); const interpolation = ['{%', '%}'] as[string, string]; TestBed.overrideComponent(AppComp, {set: {interpolation}}); const fixture = initWithTemplate(AppComp, `
Hello {% name %}
`); @@ -68,7 +74,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('should support &ngsp; in translatable sections', () => { // note: the `` unicode symbol represents the `&ngsp;` in translations - loadTranslations({'2564699219547368060': 'texte ||'}); + loadTranslations({[computeMsgId('text ||')]: 'texte ||'}); const fixture = initWithTemplate(AppCompWithWhitespaces, `
text |&ngsp;|
`); expect(fixture.nativeElement.innerHTML).toEqual(`
texte | |
`); @@ -76,7 +82,8 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('should support interpolations with complex expressions', () => { loadTranslations({ - '3552931695168961643': ' {$INTERPOLATION} - {$INTERPOLATION_1} - {$INTERPOLATION_2} (fr) ' + [computeMsgId(' {$INTERPOLATION} - {$INTERPOLATION_1} - {$INTERPOLATION_2} ')]: + ' {$INTERPOLATION} - {$INTERPOLATION_1} - {$INTERPOLATION_2} (fr) ' }); const fixture = initWithTemplate(AppComp, `
@@ -99,7 +106,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('should support elements', () => { loadTranslations({ - '4352419997312772902': + [computeMsgId( + '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( @@ -109,7 +118,11 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('should support removing elements', () => { - loadTranslations({'4038508921050216955': 'Bonjour {$START_TAG_SPAN}monde{$CLOSE_TAG_SPAN}'}); + loadTranslations({ + [computeMsgId( + '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) @@ -118,7 +131,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('should support moving elements', () => { loadTranslations({ - '4352419997312772902': + [computeMsgId( + '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( @@ -129,7 +144,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('should support template directives', () => { loadTranslations({ - '3546490957396057142': + [computeMsgId( + '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( @@ -149,9 +166,12 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { 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}' + [computeMsgId('trad {$INTERPOLATION}')]: 'traduction {$INTERPOLATION}', + [computeMsgId('start {$INTERPOLATION} middle {$INTERPOLATION_1} end')]: + 'start {$INTERPOLATION_1} middle {$INTERPOLATION} end', + [computeMsgId( + '{$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, `
@@ -170,9 +190,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('should support multiple sibling i18n blocks', () => { loadTranslations({ - '1789603390900945185': 'Section un', - '3131519605456222658': 'Section deux', - '1438862747497772785': 'Section trois', + [computeMsgId('Section 1')]: 'Section un', + [computeMsgId('Section 2')]: 'Section deux', + [computeMsgId('Section 3')]: 'Section trois', }); const fixture = initWithTemplate(AppComp, `
@@ -186,9 +206,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('should support multiple sibling i18n blocks inside of a template directive', () => { loadTranslations({ - '1789603390900945185': 'Section un', - '3131519605456222658': 'Section deux', - '1438862747497772785': 'Section trois', + [computeMsgId('Section 1')]: 'Section un', + [computeMsgId('Section 2')]: 'Section deux', + [computeMsgId('Section 3')]: 'Section trois', }); const fixture = initWithTemplate(AppComp, `
    @@ -204,7 +224,10 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('should properly escape quotes in content', () => { - loadTranslations({'7582605960852813916': '\'Guillemets simples\' et "Guillemets doubles"'}); + loadTranslations({ + [computeMsgId('\'Single quotes\' and "Double quotes"')]: + '\'Guillemets simples\' et "Guillemets doubles"' + }); const fixture = initWithTemplate(AppComp, `
    'Single quotes' and "Double quotes"
    `); @@ -213,7 +236,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('should correctly bind to context in nested template', () => { - loadTranslations({'4691107123853093118': 'Article {$INTERPOLATION}'}); + loadTranslations({[computeMsgId('Item {$INTERPOLATION}')]: 'Article {$INTERPOLATION}'}); const fixture = initWithTemplate(AppComp, `
    Item {{ id }}
    @@ -233,13 +256,13 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('should handle i18n attribute with directives', () => { - loadTranslations({'3771704108176831903': 'Bonjour {$INTERPOLATION}'}); + loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'}); const fixture = initWithTemplate(AppComp, `
    Hello {{ name }}
    `); expect(fixture.nativeElement.firstChild).toHaveText('Bonjour Angular'); }); it('should work correctly with event listeners', () => { - loadTranslations({'Hello {$INTERPOLATION}': 'Bonjour {$INTERPOLATION}'}); + loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'}); @Component( {selector: 'app-comp', template: `
    Hello {{ name }}
    `}) @@ -266,13 +289,13 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { describe('ng-container and ng-template support', () => { it('should support ng-container', () => { - loadTranslations({'3667842621564887364': 'texte'}); + loadTranslations({[computeMsgId('text')]: 'texte'}); const fixture = initWithTemplate(AppComp, `text`); expect(fixture.nativeElement.innerHTML).toEqual(`texte`); }); it('should handle single translation message within ng-template', () => { - loadTranslations({'3771704108176831903': 'Bonjour {$INTERPOLATION}'}); + loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'}); const fixture = initWithTemplate(AppComp, `Hello {{ name }}`); @@ -283,7 +306,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { // 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}'}); + loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'}); const fixture = initWithTemplate( AppComp, `Hello {{ name }}`); @@ -293,7 +316,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('should be able to act as child elements inside i18n block (plain text content)', () => { loadTranslations({ - '3250373733165819471': + [computeMsgId( + '{$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, ` @@ -313,7 +338,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('should be able to act as child elements inside i18n block (text + tags)', () => { loadTranslations({ - '7109932217076518742': + [computeMsgId( + '{$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, ` @@ -336,7 +363,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('should be able to act as child elements inside i18n block (text + pipes)', () => { loadTranslations({ - '2351903444091919458': + [computeMsgId( + '{$START_TAG_NG_TEMPLATE}Hello {$INTERPOLATION}{$CLOSE_TAG_NG_TEMPLATE}{$START_TAG_NG_CONTAINER}Bye {$INTERPOLATION}{$CLOSE_TAG_NG_CONTAINER}', + '')]: '{$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, ` @@ -352,7 +381,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('should be able to handle deep nested levels with templates', () => { loadTranslations({ - '3582176345457676': + [computeMsgId( + '{$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, ` @@ -383,8 +414,10 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('should handle self-closing tags as content', () => { - loadTranslations( - {'3294715134260231285': '{$START_TAG_SPAN}Mon logo{$TAG_IMG}{$CLOSE_TAG_SPAN}'}); + loadTranslations({ + [computeMsgId('{$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, ` @@ -404,6 +437,10 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('should correctly find context for an element inside i18n section in ', () => { + loadTranslations({ + [computeMsgId('{$START_LINK}Not logged in{$CLOSE_LINK}')]: + '{$START_LINK}Not logged in{$CLOSE_LINK}' + }); @Directive({selector: '[myDir]'}) class Dir { condition = true; @@ -438,8 +475,10 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { describe('should support ICU expressions', () => { it('with no root node', () => { - loadTranslations( - {'8806993169187953163': '{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}'}); + loadTranslations({ + [computeMsgId('{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}}`); @@ -448,8 +487,10 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('with no i18n tag', () => { - loadTranslations( - {'8806993169187953163': '{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}'}); + loadTranslations({ + [computeMsgId('{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}}
    `); @@ -459,9 +500,13 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('multiple', () => { loadTranslations({ - '3639715378617754400': + [computeMsgId( + '{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}}}', - '484647350062685600': '{VAR_SELECT, select, other {({INTERPOLATION})}}' + [computeMsgId('{VAR_SELECT, select, other {({INTERPOLATION})}}')]: + '{VAR_SELECT, select, other {({INTERPOLATION})}}', + [computeMsgId('{$ICU} - {$ICU_1}')]: '{$ICU} - {$ICU_1}', }); const fixture = initWithTemplate(AppComp, `
    {count, plural, =0 {no emails!} @@ -487,8 +532,10 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('with custom interpolation config', () => { - loadTranslations( - {'8091236997374391053': '{VAR_SELECT, select, 10 {dix} other {{INTERPOLATION}}}'}); + loadTranslations({ + [computeMsgId('{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 = @@ -499,9 +546,16 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('inside HTML elements', () => { loadTranslations({ - '3639715378617754400': + [computeMsgId( + '{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}}}', - '484647350062685600': '{VAR_SELECT, select, other {({INTERPOLATION})}}' + [computeMsgId('{VAR_SELECT, select, other {({INTERPOLATION})}}')]: + '{VAR_SELECT, select, other {({INTERPOLATION})}}', + [computeMsgId( + '{$START_TAG_SPAN_1}{$ICU}{$CLOSE_TAG_SPAN} - ' + + '{$START_TAG_SPAN_1}{$ICU_1}{$CLOSE_TAG_SPAN}')]: + '{$START_TAG_SPAN_1}{$ICU}{$CLOSE_TAG_SPAN} - {$START_TAG_SPAN_1}{$ICU_1}{$CLOSE_TAG_SPAN}', }); const fixture = initWithTemplate(AppComp, `
    {count, plural, =0 {no emails!} @@ -529,7 +583,12 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('inside template directives', () => { - loadTranslations({'484647350062685600': '{VAR_SELECT, select, other {({INTERPOLATION})}}'}); + loadTranslations({ + [computeMsgId('{$START_TAG_SPAN}{$ICU}{$CLOSE_TAG_SPAN}')]: + '{$START_TAG_SPAN}{$ICU}{$CLOSE_TAG_SPAN}', + [computeMsgId('{VAR_SELECT, select, other {({INTERPOLATION})}}')]: + '{VAR_SELECT, select, other {({INTERPOLATION})}}' + }); const fixture = initWithTemplate(AppComp, `
    {name, select, other {({{name}})} }
    `); @@ -546,7 +605,10 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('inside ng-container', () => { - loadTranslations({'484647350062685600': '{VAR_SELECT, select, other {({INTERPOLATION})}}'}); + loadTranslations({ + [computeMsgId('{VAR_SELECT, select, other {({INTERPOLATION})}}')]: + '{VAR_SELECT, select, other {({INTERPOLATION})}}' + }); const fixture = initWithTemplate(AppComp, `{name, select, other {({{name}})} }`); @@ -554,8 +616,10 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('inside ', () => { - loadTranslations( - {'8806993169187953163': '{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}'}); + loadTranslations({ + [computeMsgId('{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}')]: + '{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}' + }); const fixture = initWithTemplate( AppComp, ` ` + @@ -569,7 +633,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('nested', () => { loadTranslations({ - '6934090145627876010': + [computeMsgId( + '{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 {chiens} other {animaux}}!}}' }); const fixture = initWithTemplate(AppComp, `
    {count, plural, @@ -589,11 +655,12 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); 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}); + loadTranslations({ + [computeMsgId( + '{VAR_PLURAL, plural, =0 {zero} =2 {{INTERPOLATION} {VAR_SELECT, select, cat {cats} dog {dogs} other {animals}}!} other {other - {INTERPOLATION}}}', + '')]: + '{VAR_PLURAL, plural, =0 {zero} =2 {{INTERPOLATION} {VAR_SELECT, select, cat {chats} dog {chiens} other {animaux}}!} other {autre - {INTERPOLATION}}}' + }); const fixture = initWithTemplate(AppComp, `
    {count, plural, =0 {zero} @@ -613,15 +680,19 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { fixture.componentRef.instance.count = 4; fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual(`
    other - 4
    `); + expect(fixture.nativeElement.innerHTML).toEqual(`
    autre - 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', () => { + loadTranslations({ + [computeMsgId( + '{VAR_PLURAL, plural, =0 {no email} =one {one email} =few {a few emails} =other {lots of emails}}')]: + '{VAR_PLURAL, plural, =0 {no email} =one {one email} =few {a few emails} =other {lots of emails}}' + }); + 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} @@ -629,34 +700,38 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { =other {lots of emails} }`); - expect(fixture.nativeElement.innerHTML).toEqual('no email'); + expect(fixture.nativeElement.innerHTML).toEqual('no email'); - // Change detection cycle, no model changes - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual('no email'); + // Change detection cycle, no model changes + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual('no email'); - fixture.componentInstance.count = 3; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual('a few emails'); + fixture.componentInstance.count = 3; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual('a few emails'); - fixture.componentInstance.count = 1; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual('one email'); + fixture.componentInstance.count = 1; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual('one email'); - fixture.componentInstance.count = 10; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual('a few emails'); + fixture.componentInstance.count = 10; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual('a few emails'); - fixture.componentInstance.count = 20; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual('lots of emails'); + fixture.componentInstance.count = 20; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual('lots of emails'); - fixture.componentInstance.count = 0; - fixture.detectChanges(); - expect(fixture.nativeElement.innerHTML).toEqual('no email'); - }); + fixture.componentInstance.count = 0; + fixture.detectChanges(); + expect(fixture.nativeElement.innerHTML).toEqual('no email'); + }); it('projection', () => { + loadTranslations({ + [computeMsgId('{VAR_PLURAL, plural, =1 {one} other {at least {INTERPOLATION} .}}')]: + '{VAR_PLURAL, plural, =1 {one} other {at least {INTERPOLATION} .}}' + }); @Component({selector: 'child', template: '
    '}) class Child { } @@ -675,7 +750,6 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { value = 3; } TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({}); const fixture = TestBed.createComponent(Parent); fixture.detectChanges(); @@ -684,6 +758,10 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('with empty values', () => { + loadTranslations({ + [computeMsgId('{VAR_SELECT, select, 10 {} 20 {twenty} other {other}}')]: + '{VAR_SELECT, select, 10 {} 20 {twenty} other {other}}' + }); const fixture = initWithTemplate(AppComp, `{count, select, 10 {} 20 {twenty} other {other}}`); const element = fixture.nativeElement; @@ -691,6 +769,10 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('inside a container when creating a view via vcr.createEmbeddedView', () => { + loadTranslations({ + [computeMsgId('{VAR_PLURAL, plural, =1 {ONE} other {OTHER}}')]: + '{VAR_PLURAL, plural, =1 {ONE} other {OTHER}}' + }); @Directive({ selector: '[someDir]', }) @@ -757,6 +839,14 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('with nested ICU expression and inside a container when creating a view via vcr.createEmbeddedView', () => { + loadTranslations({ + [computeMsgId( + '{VAR_PLURAL, plural, =1 {ONE} other {{INTERPOLATION} ' + + '{VAR_SELECT, select, cat {cats} dog {dogs} other {animals}}!}}')]: + '{VAR_PLURAL, plural, =1 {ONE} other {{INTERPOLATION} ' + + '{VAR_SELECT, select, cat {cats} dog {dogs} other {animals}}!}}' + }); + let dir: Dir|null = null; @Directive({ selector: '[someDir]', @@ -823,6 +913,13 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('with nested containers', () => { + loadTranslations({ + [computeMsgId('{VAR_SELECT, select, A {A } B {B } other {C }}')]: + '{VAR_SELECT, select, A {A } B {B } other {C }}', + [computeMsgId('{VAR_SELECT, select, A1 {A1 } B1 {B1 } other {C1 }}')]: + '{VAR_SELECT, select, A1 {A1 } B1 {B1 } other {C1 }}', + [computeMsgId(' {$ICU} ')]: ' {$ICU} ', + }); @Component({ selector: 'comp', template: ` @@ -857,6 +954,13 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('with named interpolations', () => { + loadTranslations({ + [computeMsgId( + '{VAR_SELECT, select, A {A - {PH_A}} ' + + 'B {B - {PH_B}} other {other - {PH_WITH_SPACES}}}')]: + '{VAR_SELECT, select, A {A (translated) - {PH_A}} ' + + 'B {B (translated) - {PH_B}} other {other (translated) - {PH_WITH_SPACES}}}', + }); @Component({ selector: 'comp', template: ` @@ -881,16 +985,22 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { const fixture = TestBed.createComponent(Comp); fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.innerHTML).toContain('A - Type A'); + expect(fixture.debugElement.nativeElement.innerHTML).toContain('A (translated) - Type A'); 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 (translated) - Type A'); + expect(fixture.debugElement.nativeElement.innerHTML).toContain('other (translated) - Type C'); }); it('should work inside an ngTemplateOutlet inside an ngFor', () => { + loadTranslations({ + [computeMsgId('{VAR_SELECT, select, A {A } B {B } other {other - {PH_WITH_SPACES}}}')]: + '{VAR_SELECT, select, A {A } B {B } other {other - {PH_WITH_SPACES}}}', + [computeMsgId('{$ICU} ')]: '{$ICU} ' + }); + @Component({ selector: 'app', template: ` @@ -925,13 +1035,13 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { describe('should support attributes', () => { it('text', () => { - loadTranslations({'3667842621564887364': 'texte'}); + loadTranslations({[computeMsgId('text')]: 'texte'}); const fixture = initWithTemplate(AppComp, `
    `); expect(fixture.nativeElement.innerHTML).toEqual(`
    `); }); it('interpolations', () => { - loadTranslations({'1721737630608625819': 'bonjour {$INTERPOLATION}'}); + loadTranslations({[computeMsgId('hello {$INTERPOLATION}')]: 'bonjour {$INTERPOLATION}'}); const fixture = initWithTemplate(AppComp, `
    `); expect(fixture.nativeElement.innerHTML).toEqual(`
    `); @@ -942,14 +1052,14 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('with pipes', () => { - loadTranslations({'5990850149002293021': 'bonjour {$INTERPOLATION}'}); + loadTranslations({[computeMsgId('hello {$INTERPOLATION}')]: 'bonjour {$INTERPOLATION}'}); const fixture = initWithTemplate( AppComp, `
    `); expect(fixture.nativeElement.innerHTML).toEqual(`
    `); }); it('multiple attributes', () => { - loadTranslations({'1721737630608625819': 'bonjour {$INTERPOLATION}'}); + loadTranslations({[computeMsgId('hello {$INTERPOLATION}')]: 'bonjour {$INTERPOLATION}'}); const fixture = initWithTemplate( AppComp, ``); @@ -964,8 +1074,8 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('on removed elements', () => { loadTranslations({ - '3667842621564887364': 'texte', - '4851941758912026972': 'contenu', + [computeMsgId('text')]: 'texte', + [computeMsgId('{$START_TAG_SPAN}content{$CLOSE_TAG_SPAN}')]: 'contenu', }); const fixture = initWithTemplate(AppComp, `
    content
    `); @@ -973,7 +1083,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('with custom interpolation config', () => { - loadTranslations({'6353989911216474113': 'Bonjour {$INTERPOLATION}'}); + loadTranslations({[computeMsgId('Hello {$INTERPOLATION}', 'm')]: 'Bonjour {$INTERPOLATION}'}); const interpolation = ['{%', '%}'] as[string, string]; TestBed.overrideComponent(AppComp, {set: {interpolation}}); const fixture = @@ -984,7 +1094,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('in nested template', () => { - loadTranslations({'5108705942887033129': 'Article {$INTERPOLATION}'}); + loadTranslations({[computeMsgId('Item {$INTERPOLATION}', 'm')]: 'Article {$INTERPOLATION}'}); const fixture = initWithTemplate(AppComp, `
    @@ -998,7 +1108,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('should add i18n attributes on self-closing tags', () => { - loadTranslations({'3771704108176831903': 'Bonjour {$INTERPOLATION}'}); + loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'}); const fixture = initWithTemplate(AppComp, ``); @@ -1007,6 +1117,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('should apply i18n attributes during second template pass', () => { + loadTranslations({[computeMsgId('Set')]: 'Set'}); @Directive({ selector: '[test]', inputs: ['test'], @@ -1044,13 +1155,13 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('with complex expressions', () => { loadTranslations({ - '305276527729153743': '{$INTERPOLATION} - {$INTERPOLATION_1} - {$INTERPOLATION_2} (fr)' + [computeMsgId('{$INTERPOLATION} - {$INTERPOLATION_1} - {$INTERPOLATION_2}')]: + '{$INTERPOLATION} - {$INTERPOLATION_1} - {$INTERPOLATION_2} (fr)' }); const fixture = initWithTemplate(AppComp, `
    `); - // the `obj` field is not yet defined, so 2nd and 3rd interpolations return empty - // strings + // 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 = { @@ -1092,10 +1203,12 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { 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': + [computeMsgId('start {$INTERPOLATION} middle {$INTERPOLATION_1} end')]: + 'début {$INTERPOLATION_1} milieu {$INTERPOLATION} fin', + [computeMsgId( + '{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}}', - '7694415136422535717': ' traduction: {$ICU} ' + [computeMsgId(' trad: {$ICU} ')]: ' traduction: {$ICU} ' }); const fixture = TestBed.createComponent(MyApp); fixture.detectChanges(); @@ -1138,8 +1251,8 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { TestBed.configureTestingModule({declarations: [AppComp, MyComp]}); loadTranslations({ - '3299467052279745802': 'Bonjour {$INTERPOLATION}', - '694232821652669635': 'fonctionne', + [computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}', + [computeMsgId('works')]: 'fonctionne', }); const fixture = initWithTemplate( AppComp, @@ -1155,8 +1268,19 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { 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}' + [computeMsgId( + '{$START_TAG_DIV2}{$CLOSE_TAG_DIV2}' + + '{$START_TAG_DIV3}{$CLOSE_TAG_DIV3}' + + '{$START_TAG_DIV4}{$CLOSE_TAG_DIV4}' + + '{$START_TAG_DIV5}{$CLOSE_TAG_DIV5}' + + '{$START_TAG_DIV6}{$CLOSE_TAG_DIV6}' + + '{$START_TAG_DIV7}{$CLOSE_TAG_DIV7}' + + '{$START_TAG_DIV8}{$CLOSE_TAG_DIV8}')]: '{$START_TAG_DIV2}{$CLOSE_TAG_DIV2}' + + '{$START_TAG_DIV8}{$CLOSE_TAG_DIV8}' + + '{$START_TAG_DIV4}{$CLOSE_TAG_DIV4}' + + '{$START_TAG_DIV5}{$CLOSE_TAG_DIV5}Bonjour monde' + + '{$START_TAG_DIV3}{$CLOSE_TAG_DIV3}' + + '{$START_TAG_DIV7}{$CLOSE_TAG_DIV7}' }); const fixture = initWithTemplate(AppComp, `
    @@ -1195,8 +1319,13 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { } TestBed.configureTestingModule({declarations: [Parent, Child]}); loadTranslations({ - '7180841260895623479': 'Enfant de {$INTERPOLATION}', - '2325314462315226487': + [computeMsgId('Child of {$INTERPOLATION}')]: 'Enfant de {$INTERPOLATION}', + [computeMsgId( + '{$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); @@ -1227,8 +1356,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { } TestBed.configureTestingModule({declarations: [Parent, Child]}); loadTranslations({ - '7180841260895623479': 'Enfant de {$INTERPOLATION}', - '7230149585615622873': 'Je suis projeté depuis {$INTERPOLATION}' + [computeMsgId('Child of {$INTERPOLATION}')]: 'Enfant de {$INTERPOLATION}', + [computeMsgId('I am projected from {$INTERPOLATION}')]: + 'Je suis projeté depuis {$INTERPOLATION}' }); const fixture = TestBed.createComponent(Parent); fixture.detectChanges(); @@ -1267,8 +1397,10 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { } TestBed.configureTestingModule({declarations: [Parent, Child, GrandChild]}); - loadTranslations( - {'9156403697449779168': '{$START_BOLD_TEXT}Bonjour{$CLOSE_BOLD_TEXT} monde!'}); + loadTranslations({ + [computeMsgId('{$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) @@ -1291,7 +1423,8 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { } TestBed.configureTestingModule({declarations: [Parent, Child, GrandChild]}); - loadTranslations({'9156403697449779168': 'Bonjour monde!'}); + loadTranslations( + {[computeMsgId('{$START_BOLD_TEXT}Hello{$CLOSE_BOLD_TEXT} World!')]: 'Bonjour monde!'}); const fixture = TestBed.createComponent(Parent); fixture.detectChanges(); expect(fixture.nativeElement.innerHTML) @@ -1316,7 +1449,10 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { } TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({'2827528023268681613': '{$START_TAG_SPAN}Contenu{$CLOSE_TAG_SPAN}'}); + loadTranslations({ + [computeMsgId('{$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) @@ -1337,7 +1473,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { } TestBed.configureTestingModule({declarations: [Parent, Child]}); loadTranslations({ - '4431461951818971024': + [computeMsgId('Content projected from {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}')]: 'Contenu projeté depuis {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}' }); @@ -1366,7 +1502,8 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { } TestBed.configureTestingModule({declarations: [Parent, Child]}); loadTranslations({ - '4431461951818971024': '{$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT} a projeté le contenu' + [computeMsgId('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(); @@ -1386,8 +1523,10 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { } TestBed.configureTestingModule({declarations: [Parent, Child]}); loadTranslations({ - '3861221266941684467': 'Contenu enfant {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}', - '6186458652454735259': 'et projection depuis {$INTERPOLATION}' + [computeMsgId('Child content {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}')]: + 'Contenu enfant {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}', + [computeMsgId('and projection from {$INTERPOLATION}')]: + 'et projection depuis {$INTERPOLATION}' }); const fixture = TestBed.createComponent(Parent); fixture.detectChanges(); @@ -1396,6 +1535,10 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('should project bare ICU expressions', () => { + loadTranslations({ + [computeMsgId('{VAR_PLURAL, plural, =1 {one} other {at least {INTERPOLATION} .}}')]: + '{VAR_PLURAL, plural, =1 {one} other {at least {INTERPOLATION} .}}' + }); @Component({selector: 'child', template: '
    '}) class Child { } @@ -1414,7 +1557,6 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { value = 3; } TestBed.configureTestingModule({declarations: [Parent, Child]}); - loadTranslations({}); const fixture = TestBed.createComponent(Parent); fixture.detectChanges(); @@ -1438,8 +1580,11 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { } TestBed.configureTestingModule({declarations: [Parent, Child]}); loadTranslations({ - '3861221266941684467': 'Contenu enfant {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}', - '4986299685320703675': 'et projection depuis {$ICU}' + [computeMsgId('{VAR_SELECT, select, angular {Angular} other {{INTERPOLATION}}}')]: + '{VAR_SELECT, select, angular {Angular} other {{INTERPOLATION}}}', + [computeMsgId('Child content {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}')]: + 'Contenu enfant {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}', + [computeMsgId('and projection from {$ICU}')]: 'et projection depuis {$ICU}' }); const fixture = TestBed.createComponent(Parent); fixture.detectChanges(); @@ -1466,8 +1611,10 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { } TestBed.configureTestingModule({declarations: [Parent, Child]}); loadTranslations({ - '3861221266941684467': 'Contenu enfant', - '6186458652454735259': 'et projection depuis {$INTERPOLATION}' + [computeMsgId('Child content {$START_TAG_NG_CONTENT}{$CLOSE_TAG_NG_CONTENT}')]: + 'Contenu enfant', + [computeMsgId('and projection from {$INTERPOLATION}')]: + 'et projection depuis {$INTERPOLATION}' }); const fixture = TestBed.createComponent(Parent); fixture.detectChanges(); @@ -1476,6 +1623,10 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { it('should display/destroy projected i18n content', () => { + loadTranslations({ + [computeMsgId('{VAR_SELECT, select, A {A} B {B} other {other}}')]: + '{VAR_SELECT, select, A {A} B {B} other {other}}' + }); @Component({ selector: 'app', template: ` @@ -1553,8 +1704,15 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { } TestBed.configureTestingModule({declarations: [TextDirective, DivQuery]}); - loadTranslations( - {'3395826043214005257': '{$START_TAG_NG_TEMPLATE}Contenu{$CLOSE_TAG_NG_TEMPLATE}'}); + loadTranslations({ + [computeMsgId( + '{$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, ` @@ -1586,6 +1744,13 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { }); it('should not alloc expando slots when there is no new variable to create', () => { + loadTranslations({ + [computeMsgId('{$START_TAG_DIV} Some content {$CLOSE_TAG_DIV}')]: + '{$START_TAG_DIV} Some content {$CLOSE_TAG_DIV}', + [computeMsgId( + '{$START_TAG_SPAN_1}{$ICU}{$CLOSE_TAG_SPAN} - {$START_TAG_SPAN_1}{$ICU_1}{$CLOSE_TAG_SPAN}')]: + '{$START_TAG_SPAN_1}{$ICU}{$CLOSE_TAG_SPAN} - {$START_TAG_SPAN_1}{$ICU_1}{$CLOSE_TAG_SPAN}', + }); @Component({ template: `
    diff --git a/packages/core/test/acceptance/view_container_ref_spec.ts b/packages/core/test/acceptance/view_container_ref_spec.ts index 7f13592ff8..8cfa217073 100644 --- a/packages/core/test/acceptance/view_container_ref_spec.ts +++ b/packages/core/test/acceptance/view_container_ref_spec.ts @@ -7,23 +7,18 @@ */ import {CommonModule, DOCUMENT} from '@angular/common'; +import {computeMsgId} from '@angular/compiler'; import {Compiler, Component, ComponentFactoryResolver, Directive, DoCheck, ElementRef, EmbeddedViewRef, ErrorHandler, NO_ERRORS_SCHEMA, NgModule, OnInit, Pipe, PipeTransform, QueryList, RendererFactory2, RendererType2, Sanitizer, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core'; import {Input} from '@angular/core/src/metadata'; import {ngDevModeResetPerfCounters} from '@angular/core/src/util/ng_dev_mode'; import {TestBed, TestComponentRenderer} from '@angular/core/testing'; -import {loadTranslations} from '@angular/localize'; +import {clearTranslations, loadTranslations} from '@angular/localize'; import {By, DomSanitizer} from '@angular/platform-browser'; import {expect} from '@angular/platform-browser/testing/src/matchers'; import {ivyEnabled, onlyInIvy} from '@angular/private/testing'; describe('ViewContainerRef', () => { - const TRANSLATIONS: any = { - '6587679027921703718': 'o', - '1059980791999999199': 'F{$START_TAG_DIV}{$CLOSE_TAG_DIV}o', - '8808498902423973223': '{$START_TAG_DIV}{$CLOSE_TAG_DIV}{$START_TAG_BEFORE}{$CLOSE_TAG_BEFORE}' - }; - /** * Gets the inner HTML of the given element with all HTML comments and Angular internal * reflect attributes omitted. This makes HTML comparisons easier and less verbose. @@ -34,7 +29,6 @@ describe('ViewContainerRef', () => { } beforeEach(() => { - loadTranslations(TRANSLATIONS); TestBed.configureTestingModule({ declarations: [ StructDir, ViewContainerRefComp, ViewContainerRefApp, DestroyCasesComp, ConstructorDir, @@ -43,6 +37,8 @@ describe('ViewContainerRef', () => { }); }); + afterEach(() => clearTranslations()); + describe('create', () => { it('should support view queries inside embedded views created in dir constructors', () => { @@ -349,6 +345,12 @@ describe('ViewContainerRef', () => { onlyInIvy('Ivy i18n logic') .it('when ViewContainerRef is on an element inside a ng-container with i18n', () => { + loadTranslations({ + [computeMsgId('Bar')]: 'o', + [computeMsgId( + '{$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', + }); executeTest(` Bar @@ -368,11 +370,17 @@ describe('ViewContainerRef', () => { }); onlyInIvy('Ivy i18n logic') - .it('when ViewContainerRef is on an element, and i18n is on the parent ViewContainerRef', - () => { - executeTest(` + .it('when ViewContainerRef is on an element, and i18n is on the parent ViewContainerRef', () => { + loadTranslations({ + [computeMsgId( + '{$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}oo{$CLOSE_TAG_BEFORE}', + [computeMsgId('{VAR_SELECT, select, other {|{INTERPOLATION}|}}')]: + '{VAR_SELECT, select, other {|{INTERPOLATION}|}}', + }); + executeTest(` - Foo + F @@ -382,7 +390,7 @@ describe('ViewContainerRef', () => {
    `); - }); + }); }); describe('length', () => { diff --git a/packages/core/test/bundling/hello_world_i18n/translations.ts b/packages/core/test/bundling/hello_world_i18n/translations.ts index aabcc78c96..c21b35c3fa 100644 --- a/packages/core/test/bundling/hello_world_i18n/translations.ts +++ b/packages/core/test/bundling/hello_world_i18n/translations.ts @@ -8,11 +8,12 @@ // Make the `$localize()` global function available to the compiled templates, and the direct calls // below. This would normally be done inside the application `polyfills.ts` file. import '@angular/localize/init'; +import {computeMsgId} from '@angular/compiler'; import {loadTranslations} from '@angular/localize'; const translations = { - '6947830843539421219': 'Bonjour Monde!', - '6935800796474951272': 'Bonjour Titre!', + [computeMsgId('Hello World!')]: 'Bonjour Monde!', + [computeMsgId('Hello Title!')]: '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 8b2848e61e..6e8ca9f1f3 100644 --- a/packages/core/test/bundling/todo_i18n/translations.ts +++ b/packages/core/test/bundling/todo_i18n/translations.ts @@ -8,30 +8,25 @@ // Make the `$localize()` global function available to the compiled templates, and the direct calls // below. This would normally be done inside the application `polyfills.ts` file. import '@angular/localize/init'; + +import {computeMsgId} from '@angular/compiler'; import {loadTranslations} from '@angular/localize'; export const translations = { - // What needs to be done? - '5102526651904871634': `Qu'y a-t-il à faire ?`, - // {$START_HEADING_LEVEL1}todos{$CLOSE_HEADING_LEVEL1}{$TAG_INPUT} - '8643091609122689720': + [computeMsgId('What needs to be done?', '')]: `Qu'y a-t-il à faire ?`, + [computeMsgId('{$START_HEADING_LEVEL1}todos{$CLOSE_HEADING_LEVEL1}{$TAG_INPUT}', '')]: '{$START_HEADING_LEVEL1}liste de tâches{$CLOSE_HEADING_LEVEL1}{$TAG_INPUT}', - // {VAR_PLURAL, plural, =1 {item left} other {items left}} - '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` + [computeMsgId('{VAR_PLURAL, plural, =1 {item left} other {items left}}', '')]: + '{VAR_PLURAL, plural, =1 {tâche restante} other {tâches restantes}}', + [computeMsgId('{$START_TAG_STRONG}{$INTERPOLATION}{$CLOSE_TAG_STRONG}{$ICU}', '')]: + '{$START_TAG_STRONG}{$INTERPOLATION}{$CLOSE_TAG_STRONG} {$ICU}', + [computeMsgId('Clear Completed', '')]: ' Effacer terminés ', + [computeMsgId('Demonstrate Components', '')]: ' Démontrer les components', + [computeMsgId('Demonstrate Structural Directives', '')]: 'Démontrer les directives structurelles', + [computeMsgId('Demonstrate {$value}', '')]: 'Démontrer {$value}', + [computeMsgId('Demonstrate zoneless change detection', '')]: + 'Démontrer la détection des changements sans zonejs', + [computeMsgId('Demonstrate internationalization', '')]: `Démontrer l'internationalisation` }; loadTranslations(translations); diff --git a/packages/localize/src/utils/messages.ts b/packages/localize/src/utils/messages.ts index 79b7b79356..0ebcc1d141 100644 --- a/packages/localize/src/utils/messages.ts +++ b/packages/localize/src/utils/messages.ts @@ -70,6 +70,10 @@ export interface ParsedMessage { * A human readable rendering of the message */ messageString: string; + /** + * The meaning of the `message`, used to distinguish identical `messageString`s. + */ + meaning: string; } /** @@ -94,6 +98,7 @@ export function parseMessage( messageId: metadata.id || computeMsgId(messageString, metadata.meaning || ''), substitutions, messageString, + meaning: metadata.meaning || '', }; }