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:

committed by
Misko Hevery

parent
b21397bde9
commit
fa79f51645
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import {ArrayType, AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinType, BuiltinTypeName, CastExpr, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, Expression, ExpressionStatement, ExpressionType, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, MapType, NotExpr, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, Type, TypeVisitor, TypeofExpr, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler';
|
||||
import {LocalizedString} from '@angular/compiler/src/output/output_ast';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {DefaultImportRecorder, ImportRewriter, NOOP_DEFAULT_IMPORT_RECORDER, NoopImportRewriter} from '../../imports';
|
||||
@ -249,6 +250,10 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor
|
||||
return expr;
|
||||
}
|
||||
|
||||
visitLocalizedString(ast: LocalizedString, context: Context): ts.Expression {
|
||||
return visitLocalizedString(ast, context, this);
|
||||
}
|
||||
|
||||
visitExternalExpr(ast: ExternalExpr, context: Context): ts.PropertyAccessExpression
|
||||
|ts.Identifier {
|
||||
if (ast.value.moduleName === null || ast.value.name === null) {
|
||||
@ -435,6 +440,10 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor {
|
||||
return ts.createLiteral(ast.value as string);
|
||||
}
|
||||
|
||||
visitLocalizedString(ast: LocalizedString, context: Context): ts.Expression {
|
||||
return visitLocalizedString(ast, context, this);
|
||||
}
|
||||
|
||||
visitExternalExpr(ast: ExternalExpr, context: Context): ts.TypeNode {
|
||||
if (ast.value.moduleName === null || ast.value.name === null) {
|
||||
throw new Error(`Import unknown module or symbol`);
|
||||
@ -512,3 +521,44 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor {
|
||||
return ts.createTypeQueryNode(expr as ts.Identifier);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper to reduce duplication, since this functionality is required in both
|
||||
* `ExpressionTranslatorVisitor` and `TypeTranslatorVisitor`.
|
||||
*/
|
||||
function visitLocalizedString(ast: LocalizedString, context: Context, visitor: ExpressionVisitor) {
|
||||
let template: ts.TemplateLiteral;
|
||||
if (ast.messageParts.length === 1) {
|
||||
template = ts.createNoSubstitutionTemplateLiteral(ast.messageParts[0]);
|
||||
} else {
|
||||
const head = ts.createTemplateHead(ast.messageParts[0]);
|
||||
const spans: ts.TemplateSpan[] = [];
|
||||
for (let i = 1; i < ast.messageParts.length; i++) {
|
||||
const resolvedExpression = ast.expressions[i - 1].visitExpression(visitor, context);
|
||||
spans.push(ts.createTemplateSpan(
|
||||
resolvedExpression, ts.createTemplateMiddle(prefixWithPlaceholderMarker(
|
||||
ast.messageParts[i], ast.placeHolderNames[i - 1]))));
|
||||
}
|
||||
if (spans.length > 0) {
|
||||
// The last span is supposed to have a tail rather than a middle
|
||||
spans[spans.length - 1].literal.kind = ts.SyntaxKind.TemplateTail;
|
||||
}
|
||||
template = ts.createTemplateExpression(head, spans);
|
||||
}
|
||||
return ts.createTaggedTemplate(ts.createIdentifier('$localize'), template);
|
||||
}
|
||||
|
||||
/**
|
||||
* We want our tagged literals to include placeholder name information to aid runtime translation.
|
||||
*
|
||||
* The expressions are marked with placeholder names by postfixing the expression with
|
||||
* `:placeHolderName:`. To achieve this, we actually "prefix" the message part that follows the
|
||||
* expression.
|
||||
*
|
||||
* @param messagePart the message part that follows the current expression.
|
||||
* @param placeHolderName the name of the placeholder for the current expression.
|
||||
* @returns the prefixed message part.
|
||||
*/
|
||||
function prefixWithPlaceholderMarker(messagePart: string, placeHolderName: string) {
|
||||
return `:${placeHolderName}:${messagePart}`;
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import {AssertNotNull, BinaryOperator, BinaryOperatorExpr, BuiltinMethod, BuiltinVar, CastExpr, ClassStmt, CommaExpr, CommentStmt, ConditionalExpr, DeclareFunctionStmt, DeclareVarStmt, ExpressionStatement, ExpressionVisitor, ExternalExpr, ExternalReference, FunctionExpr, IfStmt, InstantiateExpr, InvokeFunctionExpr, InvokeMethodExpr, JSDocCommentStmt, LiteralArrayExpr, LiteralExpr, LiteralMapExpr, NotExpr, ParseSourceFile, ParseSourceSpan, PartialModule, ReadKeyExpr, ReadPropExpr, ReadVarExpr, ReturnStatement, Statement, StatementVisitor, StmtModifier, ThrowStmt, TryCatchStmt, TypeofExpr, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr} from '@angular/compiler';
|
||||
import {LocalizedString} from '@angular/compiler/src/output/output_ast';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {error} from './util';
|
||||
@ -535,6 +536,10 @@ export class NodeEmitterVisitor implements StatementVisitor, ExpressionVisitor {
|
||||
|
||||
visitLiteralExpr(expr: LiteralExpr) { return this.record(expr, createLiteral(expr.value)); }
|
||||
|
||||
visitLocalizedString(expr: LocalizedString, context: any) {
|
||||
throw new Error('localized strings are not supported in pre-ivy mode.');
|
||||
}
|
||||
|
||||
visitExternalExpr(expr: ExternalExpr) {
|
||||
return this.record(expr, this._visitIdentifier(expr.value));
|
||||
}
|
||||
|
Reference in New Issue
Block a user