angular-docs-cn/packages/core/test/render3/i18n_spec.ts

1742 lines
59 KiB
TypeScript
Raw Normal View History

/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* 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 {NgForOfContext} from '@angular/common';
import {Component} from '../../src/core';
import {defineComponent} from '../../src/render3/definition';
import {I18nExpInstruction, I18nInstruction, i18nApply, i18nExpMapping, i18nInterpolation1, i18nInterpolation2, i18nInterpolation3, i18nInterpolation4, i18nInterpolation5, i18nInterpolation6, i18nInterpolation7, i18nInterpolation8, i18nInterpolationV, i18nMapping} from '../../src/render3/i18n';
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, projection, projectionDef, text, textBinding} from '../../src/render3/instructions';
import {RenderFlags} from '../../src/render3/interfaces/definition';
import {NgForOf} from './common_with_def';
import {ComponentFixture, TemplateFixture} from './render_util';
describe('Runtime i18n', () => {
it('should support html elements', () => {
// Html tags are replaced by placeholders.
// Open tag placeholders are never re-used (closing tag placeholders can be).
const MSG_DIV_SECTION_1 =
`{$START_C}trad 1{$END_C}{$START_A}trad 2{$START_B}trad 3{$END_B}{$END_A}`;
let i18n_1: I18nInstruction[][];
// Initial template:
// <div i18n>
// <a>
// <b></b>
// <remove-me></remove-me>
// </a>
// <c></c>
// </div>
// Translated to:
// <div i18n>
// <c>trad 1</c>
// <a>
// trad 2
// <b>trad 3</b>
// </a>
// </div>
function createTemplate() {
if (!i18n_1) {
i18n_1 = i18nMapping(
MSG_DIV_SECTION_1, [{'START_A': 1, 'START_B': 2, 'START_REMOVE_ME': 3, 'START_C': 4}]);
}
elementStart(0, 'div');
{ // Start of translated section 1
// - i18n sections do not contain any text() instruction
elementStart(1, 'a'); // START_A
{
element(2, 'b'); // START_B
element(3, 'remove-me'); // START_REMOVE_ME
}
elementEnd();
element(4, 'c'); // START_C
} // End of translated section 1
elementEnd();
i18nApply(1, i18n_1[0]);
}
const fixture = new TemplateFixture(createTemplate);
expect(fixture.html).toEqual('<div><c>trad 1</c><a>trad 2<b>trad 3</b></a></div>');
});
it('should support expressions', () => {
const MSG_DIV_SECTION_1 = `start {$EXP_2} middle {$EXP_1} end`;
let i18n_1: I18nInstruction[][];
class MyApp {
exp1 = '1';
exp2 = '2';
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <div i18n>
// {{exp1}} {{exp2}}
// </div>
// Translated to:
// <div i18n>
// start {{exp2}} middle {{exp1}} end
// </div>
template: (rf: RenderFlags, ctx: MyApp) => {
if (rf & RenderFlags.Create) {
if (!i18n_1) {
i18n_1 = i18nMapping(MSG_DIV_SECTION_1, null, [{'EXP_1': 1, 'EXP_2': 2}]);
}
elementStart(0, 'div');
{
// Start of translated section 1
// One text node is added per expression in the interpolation
text(1); // EXP_1
text(2); // EXP_2
// End of translated section 1
}
elementEnd();
i18nApply(1, i18n_1[0]);
}
if (rf & RenderFlags.Update) {
textBinding(1, bind(ctx.exp1));
textBinding(2, bind(ctx.exp2));
}
}
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html).toEqual('<div>start 2 middle 1 end</div>');
// Change detection cycle, no model changes
fixture.update();
expect(fixture.html).toEqual('<div>start 2 middle 1 end</div>');
// Change the expressions
fixture.component.exp1 = 'expr 1';
fixture.component.exp2 = 'expr 2';
fixture.update();
expect(fixture.html).toEqual('<div>start expr 2 middle expr 1 end</div>');
});
it('should support expressions on removed nodes', () => {
const MSG_DIV_SECTION_1 = `message`;
let i18n_1: I18nInstruction[][];
class MyApp {
exp1 = '1';
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <div i18n>
// {{exp1}}
// </div>
// Translated to:
// <div i18n>
// message
// </div>
template: (rf: RenderFlags, ctx: MyApp) => {
if (rf & RenderFlags.Create) {
if (!i18n_1) {
i18n_1 = i18nMapping(MSG_DIV_SECTION_1, null, [{'EXP_1': 1}]);
}
elementStart(0, 'div');
{
// Start of translated section 1
text(1); // EXP_1 will be removed
// End of translated section 1
}
elementEnd();
i18nApply(1, i18n_1[0]);
}
if (rf & RenderFlags.Update) {
textBinding(1, bind(ctx.exp1));
}
}
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html).toEqual('<div>message</div>');
// Change detection cycle, no model changes
fixture.update();
expect(fixture.html).toEqual('<div>message</div>');
// Change the expressions
fixture.component.exp1 = 'expr 1';
fixture.update();
expect(fixture.html).toEqual('<div>message</div>');
});
it('should support expressions in attributes', () => {
const MSG_DIV_SECTION_1 = `start {$EXP_2} middle {$EXP_1} end`;
const i18n_1 = i18nExpMapping(MSG_DIV_SECTION_1, {'EXP_1': 0, 'EXP_2': 1});
class MyApp {
exp1: any = '1';
exp2: any = '2';
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <div i18n i18n-title title="{{exp1}}{{exp2}}"></div>
// Translated to:
// <div i18n i18n-title title="start {{exp2}} middle {{exp1}} end"></div>
template: (rf: RenderFlags, ctx: MyApp) => {
if (rf & RenderFlags.Create) {
element(0, 'div'); // translated section 1
}
if (rf & RenderFlags.Update) {
elementProperty(0, 'title', i18nInterpolation2(i18n_1, ctx.exp1, ctx.exp2));
}
}
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html).toEqual('<div title="start 2 middle 1 end"></div>');
// Change detection cycle, no model changes
fixture.update();
expect(fixture.html).toEqual('<div title="start 2 middle 1 end"></div>');
// Change the expressions
fixture.component.exp1 = function test() {};
fixture.component.exp2 = null;
fixture.update();
expect(fixture.html).toEqual('<div title="start middle test end"></div>');
});
it('should support both html elements, expressions and expressions in attributes', () => {
const MSG_DIV_SECTION_1 = `{$EXP_1} {$START_P}trad {$EXP_2}{$END_P}`;
const MSG_ATTR_1 = `start {$EXP_2} middle {$EXP_1} end`;
let i18n_1: I18nInstruction[][];
let i18n_2: I18nExpInstruction[];
class MyApp {
exp1 = '1';
exp2 = '2';
exp3 = '3';
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <div i18n i18n-title title="{{exp1}}{{exp2}}">
// {{exp1}}
// <remove-me-1>
// <remove-me-2></remove-me-2>
// <remove-me-3></remove-me-3>
// </remove-me-1>
// <p>
// {{exp2}}
// </p>
// {{exp3}}
// </div>
// Translated to:
// <div i18n i18n-title title="start {{exp2}} middle {{exp1}} end">
// {{exp1}}
// <p>
// trad {{exp2}}
// </p>
// </div>
template: (rf: RenderFlags, ctx: MyApp) => {
if (rf & RenderFlags.Create) {
if (!i18n_1) {
i18n_1 = i18nMapping(
MSG_DIV_SECTION_1, [{
'START_REMOVE_ME_1': 2,
'START_REMOVE_ME_2': 3,
'START_REMOVE_ME_3': 4,
'START_P': 5
}],
[{'EXP_1': 1, 'EXP_2': 6, 'EXP_3': 7}]);
}
if (!i18n_2) {
i18n_2 = i18nExpMapping(MSG_ATTR_1, {'EXP_1': 0, 'EXP_2': 1});
}
elementStart(0, 'div');
{
// Start of translated section 1
text(1); // EXP_1
elementStart(2, 'remove-me-1'); // START_REMOVE_ME_1
{
element(3, 'remove-me-2'); // START_REMOVE_ME_2
element(4, 'remove-me-3'); // START_REMOVE_ME_3
}
elementEnd();
elementStart(5, 'p'); // START_P
{ text(6); } // EXP_2
elementEnd();
text(7); // EXP_3
// End of translated section 1
}
elementEnd();
i18nApply(1, i18n_1[0]);
}
if (rf & RenderFlags.Update) {
textBinding(1, bind(ctx.exp1));
textBinding(6, bind(ctx.exp2));
textBinding(7, bind(ctx.exp3));
elementProperty(0, 'title', i18nInterpolation2(i18n_2, ctx.exp1, ctx.exp2));
}
}
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html).toEqual('<div title="start 2 middle 1 end">1 <p>trad 2</p></div>');
// Change detection cycle, no model changes
fixture.update();
expect(fixture.html).toEqual('<div title="start 2 middle 1 end">1 <p>trad 2</p></div>');
// Change the expressions
fixture.component.exp1 = 'expr 1';
fixture.component.exp2 = 'expr 2';
fixture.update();
expect(fixture.html)
.toEqual('<div title="start expr 2 middle expr 1 end">expr 1 <p>trad expr 2</p></div>');
});
it('should support multiple i18n elements', () => {
const MSG_DIV_SECTION_1 = `trad {$EXP_1}`;
const MSG_DIV_SECTION_2 = `{$START_C}trad{$END_C}`;
const MSG_ATTR_1 = `start {$EXP_2} middle {$EXP_1} end`;
let i18n_1: I18nInstruction[][];
let i18n_2: I18nInstruction[][];
let i18n_3: I18nExpInstruction[];
class MyApp {
exp1 = '1';
exp2 = '2';
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <div>
// <a i18n>
// {{exp1}}
// </a>
// hello
// <b i18n i18n-title title="{{exp1}}{{exp2}}">
// <c></c>
// </b>
// </div>
// Translated to:
// <div>
// <a i18n>
// trad {{exp1}}
// </a>
// hello
// <b i18n i18n-title title="start {{exp2}} middle {{exp1}} end">
// <c>trad</c>
// </b>
// </div>
template: (rf: RenderFlags, ctx: MyApp) => {
if (rf & RenderFlags.Create) {
if (!i18n_1) {
i18n_1 = i18nMapping(MSG_DIV_SECTION_1, null, [{'EXP_1': 2}]);
}
if (!i18n_2) {
i18n_2 = i18nMapping(MSG_DIV_SECTION_2, [{'START_C': 5}]);
}
if (!i18n_3) {
i18n_3 = i18nExpMapping(MSG_ATTR_1, {'EXP_1': 0, 'EXP_2': 1});
}
elementStart(0, 'div');
{
elementStart(1, 'a');
{
// Start of translated section 1
text(2); // EXP_1
// End of translated section 1
}
elementEnd();
text(3, 'hello');
elementStart(4, 'b');
{
// Start of translated section 2
element(5, 'c'); // START_C
// End of translated section 2
}
elementEnd();
}
elementEnd();
i18nApply(2, i18n_1[0]);
i18nApply(5, i18n_2[0]);
}
if (rf & RenderFlags.Update) {
textBinding(2, bind(ctx.exp1));
elementProperty(4, 'title', i18nInterpolation2(i18n_3, ctx.exp1, ctx.exp2));
}
}
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html)
.toEqual('<div><a>trad 1</a>hello<b title="start 2 middle 1 end"><c>trad</c></b></div>');
// Change detection cycle, no model changes
fixture.update();
expect(fixture.html)
.toEqual('<div><a>trad 1</a>hello<b title="start 2 middle 1 end"><c>trad</c></b></div>');
// Change the expressions
fixture.component.exp1 = 'expr 1';
fixture.component.exp2 = 'expr 2';
fixture.update();
expect(fixture.html)
.toEqual(
'<div><a>trad expr 1</a>hello<b title="start expr 2 middle expr 1 end"><c>trad</c></b></div>');
});
describe('view containers / embedded templates', () => {
it('should support containers', () => {
const MSG_DIV_SECTION_1 = `valeur: {$EXP_1}`;
// The indexes are based on the main template function
let i18n_1: I18nInstruction[][];
class MyApp {
exp1 = '1';
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// before (
// % if (condition) { // with i18n
// value: {{exp1}}
// % }
// ) after
// Translated :
// before (
// % if (condition) { // with i18n
// valeur: {{exp1}}
// % }
// ) after
template: (rf: RenderFlags, myApp: MyApp) => {
if (rf & RenderFlags.Create) {
if (!i18n_1) {
i18n_1 = i18nMapping(MSG_DIV_SECTION_1, null, [{'EXP_1': 0}]);
}
text(0, 'before (');
container(1);
text(2, ') after');
}
if (rf & RenderFlags.Update) {
containerRefreshStart(1);
{
let rf0 = embeddedViewStart(0);
if (rf0 & RenderFlags.Create) {
// Start of translated section 1
text(0); // EXP_1
// End of translated section 1
i18nApply(0, i18n_1[0]);
}
if (rf0 & RenderFlags.Update) {
textBinding(0, bind(myApp.exp1));
}
embeddedViewEnd();
}
containerRefreshEnd();
}
}
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html).toEqual('before (valeur: 1) after');
// Change detection cycle, no model changes
fixture.update();
expect(fixture.html).toEqual('before (valeur: 1) after');
});
it('should support ng-container', () => {
const MSG_DIV_SECTION_1 = `{$START_B}{$END_B}`;
// With ng-container the i18n node doesn't create any element at runtime which means that
// its children are not the only children of their parent, some nodes which are not
// translated might also be the children of the same parent.
// This is why we need to pass the `lastChildIndex` to `i18nMapping`
let i18n_1: I18nInstruction[][];
// Initial template:
// <div i18n>
// <a></a>
// <ng-container i18n>
// <b></b>
// <c></c>
// </ng-container>
// <d></d>
// </div>
// Translated to:
// <div i18n>
// <a></a>
// <ng-container i18n>
// <b></b>
// </ng-container>
// <d></d>
// </div>
function createTemplate() {
if (!i18n_1) {
i18n_1 = i18nMapping(MSG_DIV_SECTION_1, [{'START_B': 2, 'START_C': 3}], null, null, 4);
}
elementStart(0, 'div');
{
element(1, 'a');
{
// Start of translated section 1
element(2, 'b'); // START_B
element(3, 'c'); // START_C
// End of translated section 1
}
element(4, 'd');
}
elementEnd();
i18nApply(2, i18n_1[0]);
}
const fixture = new TemplateFixture(createTemplate);
expect(fixture.html).toEqual('<div><a></a><b></b><d></d></div>');
});
it('should support embedded templates', () => {
const MSG_DIV_SECTION_1 = `{$START_LI}valeur: {$EXP_1}!{$END_LI}`;
// The indexes are based on each template function
let i18n_1: I18nInstruction[][];
class MyApp {
items: string[] = ['1', '2'];
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <ul i18n>
// <li *ngFor="let item of items">value: {{item}}</li>
// </ul>
// Translated to:
// <ul i18n>
// <li *ngFor="let item of items">valeur: {{item}}!</li>
// </ul>
template: (rf: RenderFlags, myApp: MyApp) => {
if (rf & RenderFlags.Create) {
if (!i18n_1) {
i18n_1 = i18nMapping(
MSG_DIV_SECTION_1, [{'START_LI': 1}, {'START_LI': 0}], [null, {'EXP_1': 1}],
['START_LI']);
}
elementStart(0, 'ul');
{
// Start of translated section 1
container(1, liTemplate, null, ['ngForOf', '']); // START_LI
// End of translated section 1
}
elementEnd();
i18nApply(1, i18n_1[0]);
}
if (rf & RenderFlags.Update) {
elementProperty(1, 'ngForOf', bind(myApp.items));
}
function liTemplate(rf1: RenderFlags, row: NgForOfContext<string>) {
if (rf1 & RenderFlags.Create) {
// This is a container so the whole template is a translated section
// Start of translated section 2
elementStart(0, 'li'); // START_LI
{ text(1); } // EXP_1
elementEnd();
// End of translated section 2
i18nApply(0, i18n_1[1]);
}
if (rf1 & RenderFlags.Update) {
textBinding(1, bind(row.$implicit));
}
}
},
directives: () => [NgForOf]
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html).toEqual('<ul><li>valeur: 1!</li><li>valeur: 2!</li></ul>');
// Change detection cycle, no model changes
fixture.update();
expect(fixture.html).toEqual('<ul><li>valeur: 1!</li><li>valeur: 2!</li></ul>');
// Remove the last item
fixture.component.items.length = 1;
fixture.update();
expect(fixture.html).toEqual('<ul><li>valeur: 1!</li></ul>');
// Change an item
fixture.component.items[0] = 'one';
fixture.update();
expect(fixture.html).toEqual('<ul><li>valeur: one!</li></ul>');
// Add an item
fixture.component.items.push('two');
fixture.update();
expect(fixture.html).toEqual('<ul><li>valeur: one!</li><li>valeur: two!</li></ul>');
});
it('should support sibling embedded templates', () => {
const MSG_DIV_SECTION_1 =
`{$START_LI_0}valeur: {$EXP_1}!{$END_LI_0}{$START_LI_1}valeur bis: {$EXP_2}!{$END_LI_1}`;
// The indexes are based on each template function
let i18n_1: I18nInstruction[][];
class MyApp {
items: string[] = ['1', '2'];
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <ul i18n>
// <li *ngFor="let item of items">value: {{item}}</li>
// <li *ngFor="let item of items">value bis: {{item}}</li>
// </ul>
// Translated to:
// <ul i18n>
// <li *ngFor="let item of items">valeur: {{item}}!</li>
// <li *ngFor="let item of items">valeur bis: {{item}}!</li>
// </ul>
template: (rf: RenderFlags, myApp: MyApp) => {
if (rf & RenderFlags.Create) {
if (!i18n_1) {
i18n_1 = i18nMapping(
MSG_DIV_SECTION_1,
[{'START_LI_0': 1, 'START_LI_1': 2}, {'START_LI_0': 0}, {'START_LI_1': 0}],
[null, {'EXP_1': 1}, {'EXP_2': 1}], ['START_LI_0', 'START_LI_1']);
}
elementStart(0, 'ul');
{
// Start of translated section 1
container(1, liTemplate, null, ['ngForOf', '']); // START_LI_0
container(2, liTemplateBis, null, ['ngForOf', '']); // START_LI_1
// End of translated section 1
}
elementEnd();
i18nApply(1, i18n_1[0]);
}
if (rf & RenderFlags.Update) {
elementProperty(1, 'ngForOf', bind(myApp.items));
elementProperty(2, 'ngForOf', bind(myApp.items));
}
function liTemplate(rf1: RenderFlags, row: NgForOfContext<string>) {
if (rf1 & RenderFlags.Create) {
// This is a container so the whole template is a translated section
// Start of translated section 2
elementStart(0, 'li'); // START_LI_0
{ text(1); } // EXP_1
elementEnd();
// End of translated section 2
i18nApply(0, i18n_1[1]);
}
if (rf1 & RenderFlags.Update) {
textBinding(1, bind(row.$implicit));
}
}
function liTemplateBis(rf1: RenderFlags, row: NgForOfContext<string>) {
if (rf1 & RenderFlags.Create) {
// This is a container so the whole template is a translated section
// Start of translated section 3
elementStart(0, 'li'); // START_LI_1
{ text(1); } // EXP_2
elementEnd();
// End of translated section 3
i18nApply(0, i18n_1[2]);
}
if (rf1 & RenderFlags.Update) {
textBinding(1, bind(row.$implicit));
}
}
},
directives: () => [NgForOf]
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html)
.toEqual(
'<ul><li>valeur: 1!</li><li>valeur: 2!</li><li>valeur bis: 1!</li><li>valeur bis: 2!</li></ul>');
// Change detection cycle, no model changes
fixture.update();
expect(fixture.html)
.toEqual(
'<ul><li>valeur: 1!</li><li>valeur: 2!</li><li>valeur bis: 1!</li><li>valeur bis: 2!</li></ul>');
// Remove the last item
fixture.component.items.length = 1;
fixture.update();
expect(fixture.html).toEqual('<ul><li>valeur: 1!</li><li>valeur bis: 1!</li></ul>');
// Change an item
fixture.component.items[0] = 'one';
fixture.update();
expect(fixture.html).toEqual('<ul><li>valeur: one!</li><li>valeur bis: one!</li></ul>');
// Add an item
fixture.component.items.push('two');
fixture.update();
expect(fixture.html)
.toEqual(
'<ul><li>valeur: one!</li><li>valeur: two!</li><li>valeur bis: one!</li><li>valeur bis: two!</li></ul>');
});
it('should support changing the order of multiple template roots in the same template', () => {
const MSG_DIV_SECTION_1 =
`{$START_LI_1}valeur bis: {$EXP_2}!{$END_LI_1}{$START_LI_0}valeur: {$EXP_1}!{$END_LI_0}`;
// The indexes are based on each template function
let i18n_1: I18nInstruction[][];
class MyApp {
items: string[] = ['1', '2'];
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <ul i18n>
// <li *ngFor="let item of items">value: {{item}}</li>
// <li *ngFor="let item of items">value bis: {{item}}</li>
// </ul>
// Translated to:
// <ul i18n>
// <li *ngFor="let item of items">valeur bis: {{item}}!</li>
// <li *ngFor="let item of items">valeur: {{item}}!</li>
// </ul>
template: (rf: RenderFlags, myApp: MyApp) => {
if (rf & RenderFlags.Create) {
if (!i18n_1) {
i18n_1 = i18nMapping(
MSG_DIV_SECTION_1,
[{'START_LI_0': 1, 'START_LI_1': 2}, {'START_LI_0': 0}, {'START_LI_1': 0}],
[null, {'EXP_1': 1}, {'EXP_2': 1}], ['START_LI_0', 'START_LI_1']);
}
elementStart(0, 'ul');
{
// Start of translated section 1
container(1, liTemplate, null, ['ngForOf', '']); // START_LI_0
container(2, liTemplateBis, null, ['ngForOf', '']); // START_LI_1
// End of translated section 1
}
elementEnd();
i18nApply(1, i18n_1[0]);
}
if (rf & RenderFlags.Update) {
elementProperty(1, 'ngForOf', bind(myApp.items));
elementProperty(2, 'ngForOf', bind(myApp.items));
}
function liTemplate(rf1: RenderFlags, row: NgForOfContext<string>) {
if (rf1 & RenderFlags.Create) {
// This is a container so the whole template is a translated section
// Start of translated section 2
elementStart(0, 'li'); // START_LI_0
{ text(1); } // EXP_1
elementEnd();
// End of translated section 2
i18nApply(0, i18n_1[1]);
}
if (rf1 & RenderFlags.Update) {
textBinding(1, bind(row.$implicit));
}
}
function liTemplateBis(rf1: RenderFlags, row: NgForOfContext<string>) {
if (rf1 & RenderFlags.Create) {
// This is a container so the whole template is a translated section
// Start of translated section 3
elementStart(0, 'li'); // START_LI_1
{ text(1); } // EXP_2
elementEnd();
// End of translated section 3
i18nApply(0, i18n_1[2]);
}
if (rf1 & RenderFlags.Update) {
textBinding(1, bind(row.$implicit));
}
}
},
directives: () => [NgForOf]
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html)
.toEqual(
'<ul><li>valeur bis: 1!</li><li>valeur bis: 2!</li><li>valeur: 1!</li><li>valeur: 2!</li></ul>');
// Change detection cycle, no model changes
fixture.update();
expect(fixture.html)
.toEqual(
'<ul><li>valeur bis: 1!</li><li>valeur bis: 2!</li><li>valeur: 1!</li><li>valeur: 2!</li></ul>');
// Remove the last item
fixture.component.items.length = 1;
fixture.update();
expect(fixture.html).toEqual('<ul><li>valeur bis: 1!</li><li>valeur: 1!</li></ul>');
// Change an item
fixture.component.items[0] = 'one';
fixture.update();
expect(fixture.html).toEqual('<ul><li>valeur bis: one!</li><li>valeur: one!</li></ul>');
// Add an item
fixture.component.items.push('two');
fixture.update();
expect(fixture.html)
.toEqual(
'<ul><li>valeur bis: one!</li><li>valeur bis: two!</li><li>valeur: one!</li><li>valeur: two!</li></ul>');
});
it('should support nested embedded templates', () => {
const MSG_DIV_SECTION_1 = `{$START_LI}{$START_SPAN}valeur: {$EXP_1}!{$END_SPAN}{$END_LI}`;
// The indexes are based on each template function
let i18n_1: I18nInstruction[][];
class MyApp {
items: string[] = ['1', '2'];
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <ul i18n>
// <li *ngFor="let item of items">
// <span *ngFor="let item of items">value: {{item}}</span>
// </li>
// </ul>
// Translated to:
// <ul i18n>
// <li *ngFor="let item of items">
// <span *ngFor="let item of items">valeur: {{item}}!</span>
// </li>
// </ul>
template: (rf: RenderFlags, myApp: MyApp) => {
if (rf & RenderFlags.Create) {
if (!i18n_1) {
i18n_1 = i18nMapping(
MSG_DIV_SECTION_1,
[{'START_LI': 1}, {'START_LI': 0, 'START_SPAN': 1}, {'START_SPAN': 0}],
[null, null, {'EXP_1': 1}], ['START_LI', 'START_SPAN']);
}
elementStart(0, 'ul');
{
// Start of translated section 1
container(1, liTemplate, null, ['ngForOf', '']); // START_LI
// End of translated section 1
}
elementEnd();
i18nApply(1, i18n_1[0]);
}
if (rf & RenderFlags.Update) {
elementProperty(1, 'ngForOf', bind(myApp.items));
}
function liTemplate(rf1: RenderFlags, row: NgForOfContext<string>) {
if (rf1 & RenderFlags.Create) {
// This is a container so the whole template is a translated section
// Start of translated section 2
elementStart(0, 'li'); // START_LI
{
container(1, spanTemplate, null, ['ngForOf', '']); // START_SPAN
}
elementEnd();
// End of translated section 2
i18nApply(0, i18n_1[1]);
}
if (rf1 & RenderFlags.Update) {
elementProperty(1, 'ngForOf', bind(myApp.items));
}
}
function spanTemplate(rf1: RenderFlags, row: NgForOfContext<string>) {
if (rf1 & RenderFlags.Create) {
// This is a container so the whole template is a translated section
// Start of translated section 3
elementStart(0, 'span'); // START_SPAN
{ text(1); } // EXP_1
elementEnd();
// End of translated section 3
i18nApply(0, i18n_1[2]);
}
if (rf1 & RenderFlags.Update) {
textBinding(1, bind(row.$implicit));
}
}
},
directives: () => [NgForOf]
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html)
.toEqual(
'<ul><li><span>valeur: 1!</span><span>valeur: 2!</span></li><li><span>valeur: 1!</span><span>valeur: 2!</span></li></ul>');
// Change detection cycle, no model changes
fixture.update();
expect(fixture.html)
.toEqual(
'<ul><li><span>valeur: 1!</span><span>valeur: 2!</span></li><li><span>valeur: 1!</span><span>valeur: 2!</span></li></ul>');
// Remove the last item
fixture.component.items.length = 1;
fixture.update();
expect(fixture.html).toEqual('<ul><li><span>valeur: 1!</span></li></ul>');
// Change an item
fixture.component.items[0] = 'one';
fixture.update();
expect(fixture.html).toEqual('<ul><li><span>valeur: one!</span></li></ul>');
// Add an item
fixture.component.items.push('two');
fixture.update();
expect(fixture.html)
.toEqual(
'<ul><li><span>valeur: one!</span><span>valeur: two!</span></li><li><span>valeur: one!</span><span>valeur: two!</span></li></ul>');
});
it('should be able to move template roots around', () => {
const MSG_DIV_SECTION_1 =
`{$START_LI_0}début{$END_LI_0}{$START_LI_1}valeur: {$EXP_1}{$END_LI_1}fin`;
// The indexes are based on each template function
let i18n_1: I18nInstruction[][];
class MyApp {
items: string[] = ['first', 'second'];
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <ul i18n>
// <li>start</li>
// <li *ngFor="let item of items">value: {{item}}</li>
// <li>delete me</li>
// </ul>
// Translated to:
// <ul i18n>
// <li>début</li>
// <li *ngFor="let item of items">valeur: {{item}}</li>
// fin
// </ul>
template: (rf: RenderFlags, myApp: MyApp) => {
if (rf & RenderFlags.Create) {
if (!i18n_1) {
i18n_1 = i18nMapping(
MSG_DIV_SECTION_1,
[{'START_LI_0': 1, 'START_LI_1': 2, 'START_LI_2': 3}, {'START_LI_1': 0}],
[null, {'EXP_1': 1}], ['START_LI_1']);
}
elementStart(0, 'ul');
{
// Start of translated section 1
element(1, 'li'); // START_LI_0
container(2, liTemplate, null, ['ngForOf', '']); // START_LI_1
elementStart(3, 'li'); // START_LI_2
{ text(4, 'delete me'); }
elementEnd();
// End of translated section 1
}
elementEnd();
i18nApply(1, i18n_1[0]);
}
if (rf & RenderFlags.Update) {
elementProperty(2, 'ngForOf', bind(myApp.items));
}
function liTemplate(rf1: RenderFlags, row: NgForOfContext<string>) {
if (rf1 & RenderFlags.Create) {
// This is a container so the whole template is a translated section
// Start of translated section 2
elementStart(0, 'li'); // START_LI_1
{ text(1); } // EXP_1
elementEnd();
// End of translated section 2
i18nApply(0, i18n_1[1]);
}
if (rf1 & RenderFlags.Update) {
textBinding(1, bind(row.$implicit));
}
}
},
directives: () => [NgForOf]
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html)
.toEqual('<ul><li>début</li><li>valeur: first</li><li>valeur: second</li>fin</ul>');
// Change detection cycle, no model changes
fixture.update();
expect(fixture.html)
.toEqual('<ul><li>début</li><li>valeur: first</li><li>valeur: second</li>fin</ul>');
// Remove the last item
fixture.component.items.length = 1;
fixture.update();
expect(fixture.html).toEqual('<ul><li>début</li><li>valeur: first</li>fin</ul>');
// Change an item
fixture.component.items[0] = 'one';
fixture.update();
expect(fixture.html).toEqual('<ul><li>début</li><li>valeur: one</li>fin</ul>');
// Add an item
fixture.component.items.push('two');
fixture.update();
expect(fixture.html)
.toEqual('<ul><li>début</li><li>valeur: one</li><li>valeur: two</li>fin</ul>');
});
it('should be able to remove template roots', () => {
const MSG_DIV_SECTION_1 = `loop`;
// The indexes are based on each template function
let i18n_1: I18nInstruction[][];
class MyApp {
items: string[] = ['first', 'second'];
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <ul i18n>
// <li *ngFor="let item of items">value: {{item}}</li>
// </ul>
// Translated to:
// <ul i18n>
// loop
// </ul>
template: (rf: RenderFlags, myApp: MyApp) => {
if (rf & RenderFlags.Create) {
if (!i18n_1) {
i18n_1 = i18nMapping(
MSG_DIV_SECTION_1, [{'START_LI': 1}, {'START_LI': 0}], [null, {'EXP_1': 1}],
['START_LI']);
}
elementStart(0, 'ul');
{
// Start of translated section 1
container(1, liTemplate, undefined, ['ngForOf', '']); // START_LI
// End of translated section 1
}
elementEnd();
i18nApply(1, i18n_1[0]);
}
if (rf & RenderFlags.Update) {
elementProperty(1, 'ngForOf', bind(myApp.items));
}
function liTemplate(rf1: RenderFlags, row: NgForOfContext<string>) {
if (rf1 & RenderFlags.Create) {
// This is a container so the whole template is a translated section
// Start of translated section 2
elementStart(0, 'li'); // START_LI
{ text(1); } // EXP_1
elementEnd();
// End of translated section 2
i18nApply(0, i18n_1[1]);
}
if (rf1 & RenderFlags.Update) {
textBinding(1, bind(row.$implicit));
}
}
},
directives: () => [NgForOf]
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html).toEqual('<ul>loop</ul>');
// Change detection cycle, no model changes
fixture.update();
expect(fixture.html).toEqual('<ul>loop</ul>');
// Remove the last item
fixture.component.items.length = 1;
fixture.update();
expect(fixture.html).toEqual('<ul>loop</ul>');
// Change an item
fixture.component.items[0] = 'one';
fixture.update();
expect(fixture.html).toEqual('<ul>loop</ul>');
// Add an item
fixture.component.items.push('two');
fixture.update();
expect(fixture.html).toEqual('<ul>loop</ul>');
});
});
describe('projection', () => {
it('should project the translations', () => {
@Component({selector: 'child', template: '<p><ng-content></ng-content></p>'})
class Child {
static ngComponentDef = defineComponent({
type: Child,
selectors: [['child']],
factory: () => new Child(),
template: (rf: RenderFlags, cmp: Child) => {
if (rf & RenderFlags.Create) {
projectionDef();
elementStart(0, 'p');
{ projection(1); }
elementEnd();
}
}
});
}
const MSG_DIV_SECTION_1 =
`{$START_CHILD}Je suis projeté depuis {$START_B}{$EXP_1}{$END_B}{$END_CHILD}`;
let i18n_1: I18nInstruction[][];
const MSG_ATTR_1 = `Enfant de {$EXP_1}`;
let i18n_2: I18nExpInstruction[];
@Component({
selector: 'parent',
template: `
<div i18n>
<child>I am projected from <b i18n-title title="Child of {{name}}">{{name}}<remove-me-1></remove-me-1></b><remove-me-2></remove-me-2></child>
<remove-me-3></remove-me-3>
</div>`
// Translated to:
// <div i18n>
// <child>
// Je suis projeté depuis <b i18n-title title="Enfant de {{name}}">{{name}}</b>
// </child>
// </div>
})
class Parent {
name: string = 'Parent';
static ngComponentDef = defineComponent({
type: Parent,
selectors: [['parent']],
directives: [Child],
factory: () => new Parent(),
template: (rf: RenderFlags, cmp: Parent) => {
if (rf & RenderFlags.Create) {
if (!i18n_1) {
i18n_1 = i18nMapping(
MSG_DIV_SECTION_1, [{
'START_CHILD': 1,
'START_B': 2,
'START_REMOVE_ME_1': 4,
'START_REMOVE_ME_2': 5,
'START_REMOVE_ME_3': 6
}],
[{'EXP_1': 3}]);
}
if (!i18n_2) {
i18n_2 = i18nExpMapping(MSG_ATTR_1, {'EXP_1': 0});
}
elementStart(0, 'div');
{
// Start of translated section 1
elementStart(1, 'child'); // START_CHILD
{
elementStart(2, 'b'); // START_B
{
text(3); // EXP_1
element(4, 'remove-me-1'); // START_REMOVE_ME_1
}
elementEnd();
element(5, 'remove-me-2'); // START_REMOVE_ME_2
}
elementEnd();
element(6, 'remove-me-3'); // START_REMOVE_ME_3
// End of translated section 1
}
elementEnd();
i18nApply(1, i18n_1[0]);
}
if (rf & RenderFlags.Update) {
elementProperty(2, 'title', i18nInterpolation1(i18n_2, cmp.name));
textBinding(3, bind(cmp.name));
}
}
});
}
const fixture = new ComponentFixture(Parent);
expect(fixture.html)
.toEqual(
'<div><child><p>Je suis projeté depuis <b title="Enfant de Parent">Parent</b></p></child></div>');
});
it('should project a translated i18n block', () => {
@Component({selector: 'child', template: '<p><ng-content></ng-content></p>'})
class Child {
static ngComponentDef = defineComponent({
type: Child,
selectors: [['child']],
factory: () => new Child(),
template: (rf: RenderFlags, cmp: Child) => {
if (rf & RenderFlags.Create) {
projectionDef();
elementStart(0, 'p');
{ projection(1); }
elementEnd();
}
}
});
}
const MSG_DIV_SECTION_1 = `Je suis projeté depuis {$EXP_1}`;
let i18n_1: I18nInstruction[][];
const MSG_ATTR_1 = `Enfant de {$EXP_1}`;
let i18n_2: I18nExpInstruction[];
@Component({
selector: 'parent',
template: `
<div>
<child><any></any><b i18n i18n-title title="Child of {{name}}">I am projected from {{name}}</b><any></any></child>
</div>`
// Translated to:
// <div>
// <child>
// <any></any>
// <b i18n i18n-title title="Enfant de {{name}}">Je suis projeté depuis {{name}}</b>
// <any></any>
// </child>
// </div>
})
class Parent {
name: string = 'Parent';
static ngComponentDef = defineComponent({
type: Parent,
selectors: [['parent']],
directives: [Child],
factory: () => new Parent(),
template: (rf: RenderFlags, cmp: Parent) => {
if (rf & RenderFlags.Create) {
if (!i18n_1) {
i18n_1 = i18nMapping(MSG_DIV_SECTION_1, null, [{'EXP_1': 4}]);
}
if (!i18n_2) {
i18n_2 = i18nExpMapping(MSG_ATTR_1, {'EXP_1': 0});
}
elementStart(0, 'div');
{
elementStart(1, 'child');
{
element(2, 'any');
elementStart(3, 'b');
{
// Start of translated section 1
text(4); // EXP_1
// End of translated section 1
}
elementEnd();
element(5, 'any');
}
elementEnd();
}
elementEnd();
i18nApply(4, i18n_1[0]);
}
if (rf & RenderFlags.Update) {
elementProperty(3, 'title', i18nInterpolation1(i18n_2, cmp.name));
textBinding(4, bind(cmp.name));
}
}
});
}
const fixture = new ComponentFixture(Parent);
expect(fixture.html)
.toEqual(
'<div><child><p><any></any><b title="Enfant de Parent">Je suis projeté depuis Parent</b><any></any></p></child></div>');
});
it('should re-project translations when multiple projections', () => {
@Component({selector: 'grand-child', template: '<div><ng-content></ng-content></div>'})
class GrandChild {
static ngComponentDef = defineComponent({
type: GrandChild,
selectors: [['grand-child']],
factory: () => new GrandChild(),
template: (rf: RenderFlags, cmp: Child) => {
if (rf & RenderFlags.Create) {
projectionDef();
elementStart(0, 'div');
{ projection(1); }
elementEnd();
}
}
});
}
@Component(
{selector: 'child', template: '<grand-child><ng-content></ng-content></grand-child>'})
class Child {
static ngComponentDef = defineComponent({
type: Child,
selectors: [['child']],
directives: [GrandChild],
factory: () => new Child(),
template: (rf: RenderFlags, cmp: Child) => {
if (rf & RenderFlags.Create) {
projectionDef();
elementStart(0, 'grand-child');
{ projection(1); }
elementEnd();
}
}
});
}
const MSG_DIV_SECTION_1 = `{$START_B}Bonjour{$END_B} Monde!`;
let i18n_1: I18nInstruction[][];
@Component({
selector: 'parent',
template: `<child i18n><b>Hello</b> World!</child>`
// Translated to:
// <child i18n><grand-child><div><b>Bonjour</b> Monde!</div></grand-child></child>
})
class Parent {
name: string = 'Parent';
static ngComponentDef = defineComponent({
type: Parent,
selectors: [['parent']],
directives: [Child],
factory: () => new Parent(),
template: (rf: RenderFlags, cmp: Parent) => {
if (rf & RenderFlags.Create) {
if (!i18n_1) {
i18n_1 = i18nMapping(MSG_DIV_SECTION_1, [{'START_B': 1}]);
}
elementStart(0, 'child');
{
// Start of translated section 1
element(1, 'b'); // START_B
// End of translated section 1
}
elementEnd();
i18nApply(1, i18n_1[0]);
}
}
});
}
const fixture = new ComponentFixture(Parent);
expect(fixture.html)
.toEqual('<child><grand-child><div><b>Bonjour</b> Monde!</div></grand-child></child>');
});
it('should project translations with selectors', () => {
@Component({
selector: 'child',
template: `
<ng-content select="span"></ng-content>
`
})
class Child {
static ngComponentDef = defineComponent({
type: Child,
selectors: [['child']],
factory: () => new Child(),
template: (rf: RenderFlags, cmp: Child) => {
if (rf & RenderFlags.Create) {
projectionDef([[['span']]], ['span']);
projection(0, 1);
}
}
});
}
const MSG_DIV_SECTION_1 = `{$START_SPAN_0}Contenu{$END_SPAN_0}`;
let i18n_1: I18nInstruction[][];
@Component({
selector: 'parent',
template: `
<child i18n>
<span title="keepMe"></span>
<span title="deleteMe"></span>
</child>
`
// Translated to:
// <child i18n><span title="keepMe">Contenu</span></child>
})
class Parent {
static ngComponentDef = defineComponent({
type: Parent,
selectors: [['parent']],
directives: [Child],
factory: () => new Parent(),
template: (rf: RenderFlags, cmp: Parent) => {
if (rf & RenderFlags.Create) {
if (!i18n_1) {
i18n_1 = i18nMapping(MSG_DIV_SECTION_1, [{'START_SPAN_0': 1, 'START_SPAN_1': 2}]);
}
elementStart(0, 'child');
{
// Start of translated section 1
element(1, 'span', ['title', 'keepMe']); // START_SPAN_0
element(2, 'span', ['title', 'deleteMe']); // START_SPAN_1
// End of translated section 1
}
elementEnd();
i18nApply(1, i18n_1[0]);
}
}
});
}
const fixture = new ComponentFixture(Parent);
expect(fixture.html).toEqual('<child><span title="keepMe">Contenu</span></child>');
});
});
describe('i18nInterpolation', () => {
it('i18nInterpolation should return the same value as i18nInterpolationV', () => {
const MSG_DIV_SECTION_1 = `start {$EXP_2} middle {$EXP_1} end`;
const i18n_1 = i18nExpMapping(MSG_DIV_SECTION_1, {'EXP_1': 0, 'EXP_2': 1});
let interpolation;
let interpolationV;
class MyApp {
exp1: any = '1';
exp2: any = '2';
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <div i18n i18n-title title="{{exp1}}{{exp2}}"></div>
// Translated to:
// <div i18n i18n-title title="start {{exp2}} middle {{exp1}} end"></div>
template: (rf: RenderFlags, ctx: MyApp) => {
if (rf & RenderFlags.Create) {
element(0, 'div'); // translated section 1
}
if (rf & RenderFlags.Update) {
interpolation = i18nInterpolation2(i18n_1, ctx.exp1, ctx.exp2);
interpolationV = i18nInterpolationV(i18n_1, [ctx.exp1, ctx.exp2]);
elementProperty(0, 'title', interpolation);
}
}
});
}
const fixture = new ComponentFixture(MyApp);
expect(interpolation).toBeDefined();
expect(interpolation).toEqual(interpolationV);
});
it('i18nInterpolation3 should work', () => {
const MSG_DIV_SECTION_1 = `start {$EXP_1} _ {$EXP_2} _ {$EXP_3} end`;
const i18n_1 = i18nExpMapping(MSG_DIV_SECTION_1, {'EXP_1': 0, 'EXP_2': 1, 'EXP_3': 2});
class MyApp {
exp1: any = '1';
exp2: any = '2';
exp3: any = '3';
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <div i18n i18n-title title="{{exp1}}{{exp2}}{{exp3}}"></div>
// Translated to:
// <div i18n i18n-title title="start {{exp1}} _ {{exp2}} _ {{exp3}} end"></div>
template: (rf: RenderFlags, ctx: MyApp) => {
if (rf & RenderFlags.Create) {
element(0, 'div'); // translated section 1
}
if (rf & RenderFlags.Update) {
elementProperty(0, 'title', i18nInterpolation3(i18n_1, ctx.exp1, ctx.exp2, ctx.exp3));
}
}
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html).toEqual('<div title="start 1 _ 2 _ 3 end"></div>');
});
it('i18nInterpolation4 should work', () => {
const MSG_DIV_SECTION_1 = `start {$EXP_1} _ {$EXP_2} _ {$EXP_3} _ {$EXP_4} end`;
const i18n_1 =
i18nExpMapping(MSG_DIV_SECTION_1, {'EXP_1': 0, 'EXP_2': 1, 'EXP_3': 2, 'EXP_4': 3});
class MyApp {
exp1: any = '1';
exp2: any = '2';
exp3: any = '3';
exp4: any = '4';
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <div i18n i18n-title title="{{exp1}}{{exp2}}{{exp3}}{{exp4}}"></div>
// Translated to:
// <div i18n i18n-title title="start {{exp1}} _ {{exp2}} _ {{exp3}} _ {{exp4}} end"></div>
template: (rf: RenderFlags, ctx: MyApp) => {
if (rf & RenderFlags.Create) {
element(0, 'div'); // translated section 1
}
if (rf & RenderFlags.Update) {
elementProperty(
0, 'title', i18nInterpolation4(i18n_1, ctx.exp1, ctx.exp2, ctx.exp3, ctx.exp4));
}
}
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html).toEqual('<div title="start 1 _ 2 _ 3 _ 4 end"></div>');
});
it('i18nInterpolation5 should work', () => {
const MSG_DIV_SECTION_1 = `start {$EXP_1} _ {$EXP_2} _ {$EXP_3} _ {$EXP_4} _ {$EXP_5} end`;
const i18n_1 = i18nExpMapping(
MSG_DIV_SECTION_1, {'EXP_1': 0, 'EXP_2': 1, 'EXP_3': 2, 'EXP_4': 3, 'EXP_5': 4});
class MyApp {
exp1: any = '1';
exp2: any = '2';
exp3: any = '3';
exp4: any = '4';
exp5: any = '5';
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <div i18n i18n-title title="{{exp1}}{{exp2}}{{exp3}}{{exp4}}{{exp5}}"></div>
// Translated to:
// <div i18n i18n-title title="start {{exp1}} _ {{exp2}} _ {{exp3}} _ {{exp4}} _ {{exp5}}
// end"></div>
template: (rf: RenderFlags, ctx: MyApp) => {
if (rf & RenderFlags.Create) {
element(0, 'div'); // translated section 1
}
if (rf & RenderFlags.Update) {
elementProperty(
0, 'title',
i18nInterpolation5(i18n_1, ctx.exp1, ctx.exp2, ctx.exp3, ctx.exp4, ctx.exp5));
}
}
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html).toEqual('<div title="start 1 _ 2 _ 3 _ 4 _ 5 end"></div>');
});
it('i18nInterpolation6 should work', () => {
const MSG_DIV_SECTION_1 =
`start {$EXP_1} _ {$EXP_2} _ {$EXP_3} _ {$EXP_4} _ {$EXP_5} _ {$EXP_6} end`;
const i18n_1 = i18nExpMapping(
MSG_DIV_SECTION_1,
{'EXP_1': 0, 'EXP_2': 1, 'EXP_3': 2, 'EXP_4': 3, 'EXP_5': 4, 'EXP_6': 5});
class MyApp {
exp1: any = '1';
exp2: any = '2';
exp3: any = '3';
exp4: any = '4';
exp5: any = '5';
exp6: any = '6';
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <div i18n i18n-title title="{{exp1}}{{exp2}}{{exp3}}{{exp4}}{{exp5}}{{exp6}}"></div>
// Translated to:
// <div i18n i18n-title title="start {{exp1}} _ {{exp2}} _ {{exp3}} _ {{exp4}} _ {{exp5}}
// _ {{exp6}} end"></div>
template: (rf: RenderFlags, ctx: MyApp) => {
if (rf & RenderFlags.Create) {
element(0, 'div'); // translated section 1
}
if (rf & RenderFlags.Update) {
elementProperty(
0, 'title',
i18nInterpolation6(
i18n_1, ctx.exp1, ctx.exp2, ctx.exp3, ctx.exp4, ctx.exp5, ctx.exp6));
}
}
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html).toEqual('<div title="start 1 _ 2 _ 3 _ 4 _ 5 _ 6 end"></div>');
});
it('i18nInterpolation7 should work', () => {
const MSG_DIV_SECTION_1 =
`start {$EXP_1} _ {$EXP_2} _ {$EXP_3} _ {$EXP_4} _ {$EXP_5} _ {$EXP_6} _ {$EXP_7} end`;
const i18n_1 = i18nExpMapping(
MSG_DIV_SECTION_1,
{'EXP_1': 0, 'EXP_2': 1, 'EXP_3': 2, 'EXP_4': 3, 'EXP_5': 4, 'EXP_6': 5, 'EXP_7': 6});
class MyApp {
exp1: any = '1';
exp2: any = '2';
exp3: any = '3';
exp4: any = '4';
exp5: any = '5';
exp6: any = '6';
exp7: any = '7';
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <div i18n i18n-title
// title="{{exp1}}{{exp2}}{{exp3}}{{exp4}}{{exp5}}{{exp6}}{{exp7}}"></div>
// Translated to:
// <div i18n i18n-title title="start {{exp1}} _ {{exp2}} _ {{exp3}} _ {{exp4}} _ {{exp5}}
// _ {{exp6}} _ {{exp7}} end"></div>
template: (rf: RenderFlags, ctx: MyApp) => {
if (rf & RenderFlags.Create) {
element(0, 'div'); // translated section 1
}
if (rf & RenderFlags.Update) {
elementProperty(
0, 'title', i18nInterpolation7(
i18n_1, ctx.exp1, ctx.exp2, ctx.exp3, ctx.exp4, ctx.exp5,
ctx.exp6, ctx.exp7));
}
}
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html).toEqual('<div title="start 1 _ 2 _ 3 _ 4 _ 5 _ 6 _ 7 end"></div>');
});
it('i18nInterpolation8 should work', () => {
const MSG_DIV_SECTION_1 =
`start {$EXP_1} _ {$EXP_2} _ {$EXP_3} _ {$EXP_4} _ {$EXP_5} _ {$EXP_6} _ {$EXP_7} _ {$EXP_8} end`;
const i18n_1 = i18nExpMapping(MSG_DIV_SECTION_1, {
'EXP_1': 0,
'EXP_2': 1,
'EXP_3': 2,
'EXP_4': 3,
'EXP_5': 4,
'EXP_6': 5,
'EXP_7': 6,
'EXP_8': 7
});
class MyApp {
exp1: any = '1';
exp2: any = '2';
exp3: any = '3';
exp4: any = '4';
exp5: any = '5';
exp6: any = '6';
exp7: any = '7';
exp8: any = '8';
static ngComponentDef = defineComponent({
type: MyApp,
factory: () => new MyApp(),
selectors: [['my-app']],
// Initial template:
// <div i18n i18n-title
// title="{{exp1}}{{exp2}}{{exp3}}{{exp4}}{{exp5}}{{exp6}}{{exp7}}{{exp8}}"></div>
// Translated to:
// <div i18n i18n-title title="start {{exp1}} _ {{exp2}} _ {{exp3}} _ {{exp4}} _ {{exp5}}
// _ {{exp6}} _ {{exp7}} _ {{exp8}} end"></div>
template: (rf: RenderFlags, ctx: MyApp) => {
if (rf & RenderFlags.Create) {
element(0, 'div'); // translated section 1
}
if (rf & RenderFlags.Update) {
elementProperty(
0, 'title', i18nInterpolation8(
i18n_1, ctx.exp1, ctx.exp2, ctx.exp3, ctx.exp4, ctx.exp5,
ctx.exp6, ctx.exp7, ctx.exp8));
}
}
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html).toEqual('<div title="start 1 _ 2 _ 3 _ 4 _ 5 _ 6 _ 7 _ 8 end"></div>');
});
});
});