Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,46 @@ describe('i18n support in the template compiler', () => {
verify(input, output);
});

it('should support complex expressions in interpolation', () => {
const input = `
<div i18n-title title="{{valueA.getRawValue()?.getTitle()}} title"></div>
`;

const output = String.raw `
const $_c0$ = [${AttributeMarker.I18n}, "title"];
var $I18N_1$;
if (ngI18nClosureMode) {
const $MSG_EXTERNAL_3462388422673575127$$APP_SPEC_TS_2$ = goog.getMsg("{$interpolation} title", {
"interpolation": "\uFFFD0\uFFFD"
});
$I18N_1$ = $MSG_EXTERNAL_3462388422673575127$$APP_SPEC_TS_2$;
}
else {
$I18N_1$ = $localize \`$` +
String.raw `{"\uFFFD0\uFFFD"}:interpolation: title\`;
}
const $_c3$ = ["title", $I18N_1$];
consts: 2,
vars: 1,
template: function MyComponent_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelementStart(0, "div", $_c0$);
$r3$.ɵɵi18nAttributes(1, $_c3$);
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
var $tmp_0_0$ = null;
const $currVal_0$ = ($tmp_0_0$ = ctx.valueA.getRawValue()) == null ? null : $tmp_0_0$.getTitle();
$r3$.ɵɵi18nExp($currVal_0$);
$r3$.ɵɵi18nApply(1);
}
}
`;

verify(input, output);
});

it('should support interpolation', () => {
const input = `
<div id="dynamic-1"
Expand Down Expand Up @@ -1055,22 +1095,25 @@ describe('i18n support in the template compiler', () => {
<div i18n>
{{ valueA | async }}
{{ valueA?.a?.b }}
{{ valueA.getRawValue()?.getTitle() }}
</div>
`;

const output = String.raw `
var $I18N_0$;
if (ngI18nClosureMode) {
const $MSG_EXTERNAL_1482713963707913023$$APP_SPEC_TS_0$ = goog.getMsg(" {$interpolation} {$interpolation_1} ", {
const $MSG_EXTERNAL_5146016486383316049$$APP_SPEC_TS_1$ = goog.getMsg(" {$interpolation} {$interpolation_1} {$interpolation_2} ", {
"interpolation": "\uFFFD0\uFFFD",
"interpolation_1": "\uFFFD1\uFFFD"
"interpolation_1": "\uFFFD1\uFFFD",
"interpolation_2": "\uFFFD2\uFFFD"
});
$I18N_0$ = $MSG_EXTERNAL_1482713963707913023$$APP_SPEC_TS_0$;
$I18N_0$ = $MSG_EXTERNAL_5146016486383316049$$APP_SPEC_TS_1$;
}
else {
$I18N_0$ = $localize \` $` +
String.raw `{"\uFFFD0\uFFFD"}:interpolation: $` +
String.raw `{"\uFFFD1\uFFFD"}:interpolation_1: \`;
String.raw `{"\uFFFD1\uFFFD"}:interpolation_1: $` +
String.raw `{"\uFFFD2\uFFFD"}:interpolation_2: \`;
}
template: function MyComponent_Template(rf, ctx) {
Expand All @@ -1081,8 +1124,10 @@ describe('i18n support in the template compiler', () => {
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
var $tmp_2_0$ = null;
const $currVal_2$ = ($tmp_2_0$ = ctx.valueA.getRawValue()) == null ? null : $tmp_2_0$.getTitle();
$r3$.ɵɵselect(1);
$r3$.ɵɵi18nExp($r3$.ɵɵpipeBind1(2, 2, ctx.valueA))(ctx.valueA == null ? null : ctx.valueA.a == null ? null : ctx.valueA.a.b);
$r3$.ɵɵi18nExp($r3$.ɵɵpipeBind1(2, 3, ctx.valueA))(ctx.valueA == null ? null : ctx.valueA.a == null ? null : ctx.valueA.a.b)($currVal_2$);
$r3$.ɵɵi18nApply(1);
}
}
Expand Down Expand Up @@ -2615,9 +2660,9 @@ describe('i18n support in the template compiler', () => {
function MyComponent_div_2_Template(rf, ctx) {
if (rf & 1) {
$r3$.ɵɵelementStart(0, "div", $_c2$);
i0.ɵɵtext(1, " ");
$r3$.ɵɵtext(1, " ");
$r3$.ɵɵi18n(2, $I18N_3$);
i0.ɵɵtext(3, " ");
$r3$.ɵɵtext(3, " ");
$r3$.ɵɵelementEnd();
}
if (rf & 2) {
Expand Down
9 changes: 1 addition & 8 deletions packages/compiler/src/render3/view/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
hasBindings = true;
bindings.push({
sourceSpan: element.sourceSpan,
value: () => this.convertExpressionBinding(expression)
value: () => this.convertPropertyBinding(expression)
});
});
}
Expand Down Expand Up @@ -1155,19 +1155,12 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
this._bindingScope.getOrCreateSharedContextVar(0);
}

private convertExpressionBinding(value: AST): o.Expression {
const convertedPropertyBinding = convertPropertyBinding(
this, this.getImplicitReceiverExpr(), value, this.bindingContext(), BindingForm.TrySimple);
return convertedPropertyBinding.currValExpr;
}

private convertPropertyBinding(value: AST): o.Expression {
const convertedPropertyBinding = convertPropertyBinding(
this, this.getImplicitReceiverExpr(), value, this.bindingContext(), BindingForm.TrySimple,
() => error('Unexpected interpolation'));
const valExpr = convertedPropertyBinding.currValExpr;
this._tempVariables.push(...convertedPropertyBinding.stmts);

return valExpr;
}

Expand Down
46 changes: 39 additions & 7 deletions packages/core/test/acceptance/i18n_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,27 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
});

it('should support interpolations with complex expressions', () => {
loadTranslations(
{'{$interpolation} - {$interpolation_1}': '{$interpolation} - {$interpolation_1} (fr)'});
const fixture =
initWithTemplate(AppComp, `<div i18n>{{ name | uppercase }} - {{ obj?.a?.b }}</div>`);
expect(fixture.nativeElement.innerHTML).toEqual(`<div>ANGULAR - (fr)</div>`);
fixture.componentRef.instance.obj = {a: {b: 'value'}};
loadTranslations({
' {$interpolation} - {$interpolation_1} - {$interpolation_2} ':
' {$interpolation} - {$interpolation_1} - {$interpolation_2} (fr) '
});
const fixture = initWithTemplate(AppComp, `
<div i18n>
{{ name | uppercase }} -
{{ obj?.a?.b }} -
{{ obj?.getA()?.b }}
</div>
`);
// the `obj` field is not yet defined, so 2nd and 3rd interpolations return empty strings
expect(fixture.nativeElement.innerHTML).toEqual(`<div> ANGULAR - - (fr) </div>`);

fixture.componentRef.instance.obj = {
a: {b: 'value 1'},
getA: () => ({b: 'value 2'}),
};
fixture.detectChanges();
expect(fixture.nativeElement.innerHTML).toEqual(`<div>ANGULAR - value (fr)</div>`);
expect(fixture.nativeElement.innerHTML)
.toEqual(`<div> ANGULAR - value 1 - value 2 (fr) </div>`);
});

it('should support elements', () => {
Expand Down Expand Up @@ -1057,6 +1070,25 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
expect(fixture.debugElement.children[0].children[0].references.ref.test).toBe('Set');
expect(fixture.debugElement.children[1].children[0].references.ref.test).toBe('Set');
});

it('with complex expressions', () => {
loadTranslations({
'{$interpolation} - {$interpolation_1} - {$interpolation_2}':
'{$interpolation} - {$interpolation_1} - {$interpolation_2} (fr)'
});
const fixture = initWithTemplate(AppComp, `
<div i18n-title title="{{ name | uppercase }} - {{ obj?.a?.b }} - {{ obj?.getA()?.b }}"></div>
`);
// the `obj` field is not yet defined, so 2nd and 3rd interpolations return empty strings
expect(fixture.nativeElement.firstChild.title).toEqual(`ANGULAR - - (fr)`);

fixture.componentRef.instance.obj = {
a: {b: 'value 1'},
getA: () => ({b: 'value 2'}),
};
fixture.detectChanges();
expect(fixture.nativeElement.firstChild.title).toEqual(`ANGULAR - value 1 - value 2 (fr)`);
});
});

it('should work with directives and host bindings', () => {
Expand Down