fix(ivy): i18n - support colons in $localize metadata (#32867)

Metadata blocks are delimited by colons. Previously the code naively just
looked for the next colon in the string as the end marker.

This commit supports escaping colons within the metadata content.
The Angular compiler has been updated to add escaping as required.

PR Close #32867
This commit is contained in:
Pete Bacon Darwin
2019-10-02 18:17:56 +01:00
committed by atscott
parent 9b15588188
commit d24ade91b8
8 changed files with 183 additions and 58 deletions

View File

@ -8,7 +8,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 {serializeI18nMetaBlock, serializeI18nPlaceholderBlock} from '@angular/compiler/src/render3/view/i18n/meta';
import {serializeI18nHead, serializeI18nTemplatePart} from '@angular/compiler/src/render3/view/i18n/meta';
import * as ts from 'typescript';
import {DefaultImportRecorder, ImportRewriter, NOOP_DEFAULT_IMPORT_RECORDER, NoopImportRewriter} from '../../imports';
@ -529,18 +529,18 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor {
*/
function visitLocalizedString(ast: LocalizedString, context: Context, visitor: ExpressionVisitor) {
let template: ts.TemplateLiteral;
const headPart = `${serializeI18nMetaBlock(ast.metaBlock)}${ast.messageParts[0]}`;
const metaBlock = serializeI18nHead(ast.metaBlock, ast.messageParts[0]);
if (ast.messageParts.length === 1) {
template = ts.createNoSubstitutionTemplateLiteral(headPart);
template = ts.createNoSubstitutionTemplateLiteral(metaBlock);
} else {
const head = ts.createTemplateHead(headPart);
const head = ts.createTemplateHead(metaBlock);
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(
serializeI18nPlaceholderBlock(ast.placeHolderNames[i - 1]) + ast.messageParts[i])));
const templatePart =
serializeI18nTemplatePart(ast.placeHolderNames[i - 1], ast.messageParts[i]);
const templateMiddle = ts.createTemplateMiddle(templatePart);
spans.push(ts.createTemplateSpan(resolvedExpression, templateMiddle));
}
if (spans.length > 0) {
// The last span is supposed to have a tail rather than a middle