128 lines
4.3 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 * as i18n from '../../../i18n/i18n_ast';
import * as o from '../../../output/output_ast';
import {serializeIcuNode} from './icu_serializer';
import {i18nMetaToDocStmt, metaFromI18nMessage} from './meta';
import {formatI18nPlaceholderName} from './util';
export function createLocalizeStatements(
variable: o.ReadVarExpr, message: i18n.Message,
params: {[name: string]: o.Expression}): o.Statement[] {
const statements = [];
const jsdocComment = i18nMetaToDocStmt(metaFromI18nMessage(message));
if (jsdocComment !== null) {
statements.push(jsdocComment);
}
const {messageParts, placeHolders} = serializeI18nMessageForLocalize(message);
statements.push(new o.ExpressionStatement(variable.set(
o.localizedString(messageParts, placeHolders, placeHolders.map(ph => params[ph])))));
return statements;
}
class MessagePiece {
constructor(public text: string) {}
}
class LiteralPiece extends MessagePiece {}
class PlaceholderPiece extends MessagePiece {
constructor(name: string) { super(formatI18nPlaceholderName(name)); }
}
/**
* This visitor walks over an i18n tree, capturing literal strings and placeholders.
*
* The result can be used for generating the `$localize` tagged template literals.
*/
class LocalizeSerializerVisitor implements i18n.Visitor {
visitText(text: i18n.Text, context: MessagePiece[]): any {
context.push(new LiteralPiece(text.value));
}
visitContainer(container: i18n.Container, context: MessagePiece[]): any {
container.children.forEach(child => child.visit(this, context));
}
visitIcu(icu: i18n.Icu, context: MessagePiece[]): any {
context.push(new LiteralPiece(serializeIcuNode(icu)));
}
visitTagPlaceholder(ph: i18n.TagPlaceholder, context: MessagePiece[]): any {
context.push(new PlaceholderPiece(ph.startName));
if (!ph.isVoid) {
ph.children.forEach(child => child.visit(this, context));
context.push(new PlaceholderPiece(ph.closeName));
}
}
visitPlaceholder(ph: i18n.Placeholder, context: MessagePiece[]): any {
context.push(new PlaceholderPiece(ph.name));
}
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any {
context.push(new PlaceholderPiece(ph.name));
}
}
const serializerVisitor = new LocalizeSerializerVisitor();
/**
* Serialize an i18n message into two arrays: messageParts and placeholders.
*
* These arrays will be used to generate `$localize` tagged template literals.
*
* @param message The message to be serialized.
* @returns an object containing the messageParts and placeholders.
*/
export function serializeI18nMessageForLocalize(message: i18n.Message):
{messageParts: string[], placeHolders: string[]} {
const pieces: MessagePiece[] = [];
message.nodes.forEach(node => node.visit(serializerVisitor, pieces));
return processMessagePieces(pieces);
}
/**
* Convert the list of serialized MessagePieces into two arrays.
*
* One contains the literal string pieces and the other the placeholders that will be replaced by
* expressions when rendering `$localize` tagged template literals.
*
* @param pieces The pieces to process.
* @returns an object containing the messageParts and placeholders.
*/
function processMessagePieces(pieces: MessagePiece[]):
{messageParts: string[], placeHolders: string[]} {
const messageParts: string[] = [];
const placeHolders: string[] = [];
if (pieces[0] instanceof PlaceholderPiece) {
// The first piece was a placeholder so we need to add an initial empty message part.
messageParts.push('');
}
for (let i = 0; i < pieces.length; i++) {
const part = pieces[i];
if (part instanceof LiteralPiece) {
messageParts.push(part.text);
} else {
placeHolders.push(part.text);
if (pieces[i - 1] instanceof PlaceholderPiece) {
// There were two placeholders in a row, so we need to add an empty message part.
messageParts.push('');
}
}
}
if (pieces[pieces.length - 1] instanceof PlaceholderPiece) {
// The last piece was a placeholder so we need to add a final empty message part.
messageParts.push('');
}
return {messageParts, placeHolders};
}