refactor(ivy): update the compiler to emit $localize tags (#31609)

This commit changes the Angular compiler (ivy-only) to generate `$localize`
tagged strings for component templates that use `i18n` attributes.

BREAKING CHANGE

Since `$localize` is a global function, it must be included in any applications
that use i18n. This is achieved by importing the `@angular/localize` package
into an appropriate bundle, where it will be executed before the renderer
needs to call `$localize`. For CLI based projects, this is best done in
the `polyfills.ts` file.

```ts
import '@angular/localize';
```

For non-CLI applications this could be added as a script to the index.html
file or another suitable script file.

PR Close #31609
This commit is contained in:
Pete Bacon Darwin
2019-07-30 18:02:17 +01:00
committed by Misko Hevery
parent b21397bde9
commit fa79f51645
35 changed files with 1173 additions and 583 deletions

View File

@ -361,6 +361,19 @@ export abstract class AbstractEmitterVisitor implements o.StatementVisitor, o.Ex
return null;
}
visitLocalizedString(ast: o.LocalizedString, ctx: EmitterVisitorContext): any {
ctx.print(ast, '$localize `' + ast.messageParts[0]);
for (let i = 1; i < ast.messageParts.length; i++) {
ctx.print(ast, '${');
ast.expressions[i - 1].visitExpression(this, ctx);
// Add the placeholder name annotation to support runtime inlining
ctx.print(ast, `}:${ast.placeHolderNames[i - 1]}:`);
ctx.print(ast, ast.messageParts[i]);
}
ctx.print(ast, '`');
return null;
}
abstract visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any;
visitConditionalExpr(ast: o.ConditionalExpr, ctx: EmitterVisitorContext): any {

View File

@ -480,6 +480,26 @@ export class LiteralExpr extends Expression {
}
export class LocalizedString extends Expression {
constructor(
public messageParts: string[], public placeHolderNames: string[],
public expressions: Expression[], sourceSpan?: ParseSourceSpan|null) {
super(STRING_TYPE, sourceSpan);
}
isEquivalent(e: Expression): boolean {
// return e instanceof LocalizedString && this.message === e.message;
return false;
}
isConstant() { return false; }
visitExpression(visitor: ExpressionVisitor, context: any): any {
return visitor.visitLocalizedString(this, context);
}
}
export class ExternalExpr extends Expression {
constructor(
public value: ExternalReference, type?: Type|null, public typeParams: Type[]|null = null,
@ -749,6 +769,7 @@ export interface ExpressionVisitor {
visitInvokeFunctionExpr(ast: InvokeFunctionExpr, context: any): any;
visitInstantiateExpr(ast: InstantiateExpr, context: any): any;
visitLiteralExpr(ast: LiteralExpr, context: any): any;
visitLocalizedString(ast: LocalizedString, context: any): any;
visitExternalExpr(ast: ExternalExpr, context: any): any;
visitConditionalExpr(ast: ConditionalExpr, context: any): any;
visitNotExpr(ast: NotExpr, context: any): any;
@ -1074,6 +1095,14 @@ export class AstTransformer implements StatementVisitor, ExpressionVisitor {
visitLiteralExpr(ast: LiteralExpr, context: any): any { return this.transformExpr(ast, context); }
visitLocalizedString(ast: LocalizedString, context: any): any {
return this.transformExpr(
new LocalizedString(
ast.messageParts, ast.placeHolderNames,
this.visitAllExpressions(ast.expressions, context), ast.sourceSpan),
context);
}
visitExternalExpr(ast: ExternalExpr, context: any): any {
return this.transformExpr(ast, context);
}
@ -1291,6 +1320,9 @@ export class RecursiveAstVisitor implements StatementVisitor, ExpressionVisitor
visitLiteralExpr(ast: LiteralExpr, context: any): any {
return this.visitExpression(ast, context);
}
visitLocalizedString(ast: LocalizedString, context: any): any {
return this.visitExpression(ast, context);
}
visitExternalExpr(ast: ExternalExpr, context: any): any {
if (ast.typeParams) {
ast.typeParams.forEach(type => type.visitType(this, context));
@ -1551,6 +1583,12 @@ export function literal(
return new LiteralExpr(value, type, sourceSpan);
}
export function localizedString(
messageParts: string[], placeholderNames: string[], expressions: Expression[],
sourceSpan?: ParseSourceSpan | null): LocalizedString {
return new LocalizedString(messageParts, placeholderNames, expressions, sourceSpan);
}
export function isNull(exp: Expression): boolean {
return exp instanceof LiteralExpr && exp.value === null;
}

View File

@ -5,11 +5,7 @@
* 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 {CompileReflector} from '../compile_reflector';
import * as o from './output_ast';
import {debugOutputAstAsTypeScript} from './ts_emitter';
@ -239,6 +235,7 @@ class StatementInterpreter implements o.StatementVisitor, o.ExpressionVisitor {
return new clazz(...args);
}
visitLiteralExpr(ast: o.LiteralExpr, ctx: _ExecutionContext): any { return ast.value; }
visitLocalizedString(ast: o.LocalizedString, context: any): any { return null; }
visitExternalExpr(ast: o.ExternalExpr, ctx: _ExecutionContext): any {
return this.reflector.resolveExternalReference(ast.value);
}