refactor(compiler): move the MessagePiece classes into output_ast.ts (#38645)
The `MessagePiece` and derived classes, `LiteralPiece` and `PlaceholderPiece` need to be referenced in the `LocalizedString` output AST class, so that we can render the source-spans of each piece. PR Close #38645
This commit is contained in:
parent
109555b33a
commit
7a6a061a9e
|
@ -9,7 +9,6 @@
|
||||||
|
|
||||||
import {ParseSourceSpan} from '../parse_util';
|
import {ParseSourceSpan} from '../parse_util';
|
||||||
import {I18nMeta} from '../render3/view/i18n/meta';
|
import {I18nMeta} from '../render3/view/i18n/meta';
|
||||||
import {error} from '../util';
|
|
||||||
|
|
||||||
//// Types
|
//// Types
|
||||||
export enum TypeModifier {
|
export enum TypeModifier {
|
||||||
|
@ -516,11 +515,16 @@ export class LiteralExpr extends Expression {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export abstract class MessagePiece {
|
||||||
|
constructor(public text: string, public sourceSpan: ParseSourceSpan) {}
|
||||||
|
}
|
||||||
|
export class LiteralPiece extends MessagePiece {}
|
||||||
|
export class PlaceholderPiece extends MessagePiece {}
|
||||||
|
|
||||||
export class LocalizedString extends Expression {
|
export class LocalizedString extends Expression {
|
||||||
constructor(
|
constructor(
|
||||||
readonly metaBlock: I18nMeta, readonly messageParts: string[],
|
readonly metaBlock: I18nMeta, readonly messageParts: LiteralPiece[],
|
||||||
readonly placeHolderNames: string[], readonly expressions: Expression[],
|
readonly placeHolderNames: PlaceholderPiece[], readonly expressions: Expression[],
|
||||||
sourceSpan?: ParseSourceSpan|null) {
|
sourceSpan?: ParseSourceSpan|null) {
|
||||||
super(STRING_TYPE, sourceSpan);
|
super(STRING_TYPE, sourceSpan);
|
||||||
}
|
}
|
||||||
|
@ -563,7 +567,16 @@ export class LocalizedString extends Expression {
|
||||||
metaBlock = `${metaBlock}${LEGACY_ID_INDICATOR}${legacyId}`;
|
metaBlock = `${metaBlock}${LEGACY_ID_INDICATOR}${legacyId}`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return createCookedRawString(metaBlock, this.messageParts[0]);
|
return createCookedRawString(metaBlock, this.messageParts[0].text);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMessagePartSourceSpan(i: number): ParseSourceSpan|null {
|
||||||
|
return this.messageParts[i]?.sourceSpan ?? this.sourceSpan;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlaceholderSourceSpan(i: number): ParseSourceSpan {
|
||||||
|
return this.placeHolderNames[i]?.sourceSpan ?? this.expressions[i]?.sourceSpan ??
|
||||||
|
this.sourceSpan;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -574,9 +587,9 @@ export class LocalizedString extends Expression {
|
||||||
* @param messagePart The following message string after this placeholder
|
* @param messagePart The following message string after this placeholder
|
||||||
*/
|
*/
|
||||||
serializeI18nTemplatePart(partIndex: number): {cooked: string, raw: string} {
|
serializeI18nTemplatePart(partIndex: number): {cooked: string, raw: string} {
|
||||||
const placeholderName = this.placeHolderNames[partIndex - 1];
|
const placeholderName = this.placeHolderNames[partIndex - 1].text;
|
||||||
const messagePart = this.messageParts[partIndex];
|
const messagePart = this.messageParts[partIndex];
|
||||||
return createCookedRawString(placeholderName, messagePart);
|
return createCookedRawString(placeholderName, messagePart.text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1799,7 +1812,7 @@ export function literal(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function localizedString(
|
export function localizedString(
|
||||||
metaBlock: I18nMeta, messageParts: string[], placeholderNames: string[],
|
metaBlock: I18nMeta, messageParts: LiteralPiece[], placeholderNames: PlaceholderPiece[],
|
||||||
expressions: Expression[], sourceSpan?: ParseSourceSpan|null): LocalizedString {
|
expressions: Expression[], sourceSpan?: ParseSourceSpan|null): LocalizedString {
|
||||||
return new LocalizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan);
|
return new LocalizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
import * as i18n from '../../../i18n/i18n_ast';
|
import * as i18n from '../../../i18n/i18n_ast';
|
||||||
import * as o from '../../../output/output_ast';
|
import * as o from '../../../output/output_ast';
|
||||||
import {ParseSourceSpan} from '../../../parse_util';
|
import {ParseLocation, ParseSourceSpan} from '../../../parse_util';
|
||||||
|
|
||||||
import {serializeIcuNode} from './icu_serializer';
|
import {serializeIcuNode} from './icu_serializer';
|
||||||
import {formatI18nPlaceholderName} from './util';
|
import {formatI18nPlaceholderName} from './util';
|
||||||
|
@ -17,60 +17,55 @@ export function createLocalizeStatements(
|
||||||
params: {[name: string]: o.Expression}): o.Statement[] {
|
params: {[name: string]: o.Expression}): o.Statement[] {
|
||||||
const {messageParts, placeHolders} = serializeI18nMessageForLocalize(message);
|
const {messageParts, placeHolders} = serializeI18nMessageForLocalize(message);
|
||||||
const sourceSpan = getSourceSpan(message);
|
const sourceSpan = getSourceSpan(message);
|
||||||
const expressions = placeHolders.map(ph => params[ph]);
|
const expressions = placeHolders.map(ph => params[ph.text]);
|
||||||
const localizedString =
|
const localizedString =
|
||||||
o.localizedString(message, messageParts, placeHolders, expressions, sourceSpan);
|
o.localizedString(message, messageParts, placeHolders, expressions, sourceSpan);
|
||||||
const variableInitialization = variable.set(localizedString);
|
const variableInitialization = variable.set(localizedString);
|
||||||
return [new o.ExpressionStatement(variableInitialization)];
|
return [new o.ExpressionStatement(variableInitialization)];
|
||||||
}
|
}
|
||||||
|
|
||||||
class MessagePiece {
|
|
||||||
constructor(public text: string) {}
|
|
||||||
}
|
|
||||||
class LiteralPiece extends MessagePiece {}
|
|
||||||
class PlaceholderPiece extends MessagePiece {
|
|
||||||
constructor(name: string) {
|
|
||||||
super(formatI18nPlaceholderName(name, /* useCamelCase */ false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This visitor walks over an i18n tree, capturing literal strings and placeholders.
|
* This visitor walks over an i18n tree, capturing literal strings and placeholders.
|
||||||
*
|
*
|
||||||
* The result can be used for generating the `$localize` tagged template literals.
|
* The result can be used for generating the `$localize` tagged template literals.
|
||||||
*/
|
*/
|
||||||
class LocalizeSerializerVisitor implements i18n.Visitor {
|
class LocalizeSerializerVisitor implements i18n.Visitor {
|
||||||
visitText(text: i18n.Text, context: MessagePiece[]): any {
|
visitText(text: i18n.Text, context: o.MessagePiece[]): any {
|
||||||
if (context[context.length - 1] instanceof LiteralPiece) {
|
if (context[context.length - 1] instanceof o.LiteralPiece) {
|
||||||
// Two literal pieces in a row means that there was some comment node in-between.
|
// Two literal pieces in a row means that there was some comment node in-between.
|
||||||
context[context.length - 1].text += text.value;
|
context[context.length - 1].text += text.value;
|
||||||
} else {
|
} else {
|
||||||
context.push(new LiteralPiece(text.value));
|
context.push(new o.LiteralPiece(text.value, text.sourceSpan));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitContainer(container: i18n.Container, context: MessagePiece[]): any {
|
visitContainer(container: i18n.Container, context: o.MessagePiece[]): any {
|
||||||
container.children.forEach(child => child.visit(this, context));
|
container.children.forEach(child => child.visit(this, context));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitIcu(icu: i18n.Icu, context: MessagePiece[]): any {
|
visitIcu(icu: i18n.Icu, context: o.MessagePiece[]): any {
|
||||||
context.push(new LiteralPiece(serializeIcuNode(icu)));
|
context.push(new o.LiteralPiece(serializeIcuNode(icu), icu.sourceSpan));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitTagPlaceholder(ph: i18n.TagPlaceholder, context: MessagePiece[]): any {
|
visitTagPlaceholder(ph: i18n.TagPlaceholder, context: o.MessagePiece[]): any {
|
||||||
context.push(new PlaceholderPiece(ph.startName));
|
context.push(this.createPlaceholderPiece(ph.startName, ph.sourceSpan));
|
||||||
if (!ph.isVoid) {
|
if (!ph.isVoid) {
|
||||||
ph.children.forEach(child => child.visit(this, context));
|
ph.children.forEach(child => child.visit(this, context));
|
||||||
context.push(new PlaceholderPiece(ph.closeName));
|
context.push(this.createPlaceholderPiece(ph.closeName, ph.closeSourceSpan ?? ph.sourceSpan));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitPlaceholder(ph: i18n.Placeholder, context: MessagePiece[]): any {
|
visitPlaceholder(ph: i18n.Placeholder, context: o.MessagePiece[]): any {
|
||||||
context.push(new PlaceholderPiece(ph.name));
|
context.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan));
|
||||||
}
|
}
|
||||||
|
|
||||||
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any {
|
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any {
|
||||||
context.push(new PlaceholderPiece(ph.name));
|
context.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan));
|
||||||
|
}
|
||||||
|
|
||||||
|
private createPlaceholderPiece(name: string, sourceSpan: ParseSourceSpan): o.PlaceholderPiece {
|
||||||
|
return new o.PlaceholderPiece(
|
||||||
|
formatI18nPlaceholderName(name, /* useCamelCase */ false), sourceSpan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,8 +80,8 @@ const serializerVisitor = new LocalizeSerializerVisitor();
|
||||||
* @returns an object containing the messageParts and placeholders.
|
* @returns an object containing the messageParts and placeholders.
|
||||||
*/
|
*/
|
||||||
export function serializeI18nMessageForLocalize(message: i18n.Message):
|
export function serializeI18nMessageForLocalize(message: i18n.Message):
|
||||||
{messageParts: string[], placeHolders: string[]} {
|
{messageParts: o.LiteralPiece[], placeHolders: o.PlaceholderPiece[]} {
|
||||||
const pieces: MessagePiece[] = [];
|
const pieces: o.MessagePiece[] = [];
|
||||||
message.nodes.forEach(node => node.visit(serializerVisitor, pieces));
|
message.nodes.forEach(node => node.visit(serializerVisitor, pieces));
|
||||||
return processMessagePieces(pieces);
|
return processMessagePieces(pieces);
|
||||||
}
|
}
|
||||||
|
@ -107,31 +102,35 @@ function getSourceSpan(message: i18n.Message): ParseSourceSpan {
|
||||||
* @param pieces The pieces to process.
|
* @param pieces The pieces to process.
|
||||||
* @returns an object containing the messageParts and placeholders.
|
* @returns an object containing the messageParts and placeholders.
|
||||||
*/
|
*/
|
||||||
function processMessagePieces(pieces: MessagePiece[]):
|
function processMessagePieces(pieces: o.MessagePiece[]):
|
||||||
{messageParts: string[], placeHolders: string[]} {
|
{messageParts: o.LiteralPiece[], placeHolders: o.PlaceholderPiece[]} {
|
||||||
const messageParts: string[] = [];
|
const messageParts: o.LiteralPiece[] = [];
|
||||||
const placeHolders: string[] = [];
|
const placeHolders: o.PlaceholderPiece[] = [];
|
||||||
|
|
||||||
if (pieces[0] instanceof PlaceholderPiece) {
|
if (pieces[0] instanceof o.PlaceholderPiece) {
|
||||||
// The first piece was a placeholder so we need to add an initial empty message part.
|
// The first piece was a placeholder so we need to add an initial empty message part.
|
||||||
messageParts.push('');
|
messageParts.push(createEmptyMessagePart(pieces[0].sourceSpan.start));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < pieces.length; i++) {
|
for (let i = 0; i < pieces.length; i++) {
|
||||||
const part = pieces[i];
|
const part = pieces[i];
|
||||||
if (part instanceof LiteralPiece) {
|
if (part instanceof o.LiteralPiece) {
|
||||||
messageParts.push(part.text);
|
messageParts.push(part);
|
||||||
} else {
|
} else {
|
||||||
placeHolders.push(part.text);
|
placeHolders.push(part);
|
||||||
if (pieces[i - 1] instanceof PlaceholderPiece) {
|
if (pieces[i - 1] instanceof o.PlaceholderPiece) {
|
||||||
// There were two placeholders in a row, so we need to add an empty message part.
|
// There were two placeholders in a row, so we need to add an empty message part.
|
||||||
messageParts.push('');
|
messageParts.push(createEmptyMessagePart(part.sourceSpan.end));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pieces[pieces.length - 1] instanceof PlaceholderPiece) {
|
if (pieces[pieces.length - 1] instanceof o.PlaceholderPiece) {
|
||||||
// The last piece was a placeholder so we need to add a final empty message part.
|
// The last piece was a placeholder so we need to add a final empty message part.
|
||||||
messageParts.push('');
|
messageParts.push(createEmptyMessagePart(pieces[pieces.length - 1].sourceSpan.end));
|
||||||
}
|
}
|
||||||
return {messageParts, placeHolders};
|
return {messageParts, placeHolders};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createEmptyMessagePart(location: ParseLocation): o.LiteralPiece {
|
||||||
|
return new o.LiteralPiece('', new ParseSourceSpan(location, location));
|
||||||
|
}
|
||||||
|
|
|
@ -205,9 +205,12 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support ES5 localized strings', () => {
|
it('should support ES5 localized strings', () => {
|
||||||
expect(emitStmt(new o.ExpressionStatement(o.localizedString(
|
const messageParts =
|
||||||
{}, ['ab\\:c', 'd"e\'f'], ['ph1'],
|
[new o.LiteralPiece('ab\\:c', {} as any), new o.LiteralPiece('d"e\'f', {} as any)];
|
||||||
[o.literal(7, o.NUMBER_TYPE).plus(o.literal(8, o.NUMBER_TYPE))]))))
|
const placeholders = [new o.PlaceholderPiece('ph1', {} as any)];
|
||||||
|
const expressions = [o.literal(7, o.NUMBER_TYPE).plus(o.literal(8, o.NUMBER_TYPE))];
|
||||||
|
const localizedString = o.localizedString({}, messageParts, placeholders, expressions);
|
||||||
|
expect(emitStmt(new o.ExpressionStatement(localizedString)))
|
||||||
.toEqual(
|
.toEqual(
|
||||||
String.raw
|
String.raw
|
||||||
`$localize((this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e})(['ab\\:c', ':ph1:d"e\'f'], ['ab\\\\:c', ':ph1:d"e\'f']), (7 + 8));`);
|
`$localize((this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e})(['ab\\:c', ':ph1:d"e\'f'], ['ab\\\\:c', ':ph1:d"e\'f']), (7 + 8));`);
|
||||||
|
|
|
@ -254,9 +254,12 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support localized strings', () => {
|
it('should support localized strings', () => {
|
||||||
expect(emitStmt(new o.ExpressionStatement(o.localizedString(
|
const messageParts =
|
||||||
{}, ['ab\\:c', 'd"e\'f'], ['ph1'],
|
[new o.LiteralPiece('ab\\:c', {} as any), new o.LiteralPiece('d"e\'f', {} as any)];
|
||||||
[o.literal(7, o.NUMBER_TYPE).plus(o.literal(8, o.NUMBER_TYPE))]))))
|
const placeholders = [new o.PlaceholderPiece('ph1', {} as any)];
|
||||||
|
const expressions = [o.literal(7, o.NUMBER_TYPE).plus(o.literal(8, o.NUMBER_TYPE))];
|
||||||
|
const localizedString = o.localizedString({}, messageParts, placeholders, expressions);
|
||||||
|
expect(emitStmt(new o.ExpressionStatement(localizedString)))
|
||||||
.toEqual('$localize `ab\\\\:c${(7 + 8)}:ph1:d"e\'f`;');
|
.toEqual('$localize `ab\\\\:c${(7 + 8)}:ph1:d"e\'f`;');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {Lexer} from '../../../src/expression_parser/lexer';
|
||||||
import {Parser} from '../../../src/expression_parser/parser';
|
import {Parser} from '../../../src/expression_parser/parser';
|
||||||
import * as i18n from '../../../src/i18n/i18n_ast';
|
import * as i18n from '../../../src/i18n/i18n_ast';
|
||||||
import * as o from '../../../src/output/output_ast';
|
import * as o from '../../../src/output/output_ast';
|
||||||
|
import {ParseSourceSpan} from '../../../src/parse_util';
|
||||||
import * as t from '../../../src/render3/r3_ast';
|
import * as t from '../../../src/render3/r3_ast';
|
||||||
import {I18nContext} from '../../../src/render3/view/i18n/context';
|
import {I18nContext} from '../../../src/render3/view/i18n/context';
|
||||||
import {serializeI18nMessageForGetMsg} from '../../../src/render3/view/i18n/get_msg_utils';
|
import {serializeI18nMessageForGetMsg} from '../../../src/render3/view/i18n/get_msg_utils';
|
||||||
|
@ -221,64 +222,75 @@ describe('Utils', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('serializeI18nHead()', () => {
|
it('serializeI18nHead()', () => {
|
||||||
expect(o.localizedString(meta(), [''], [], []).serializeI18nHead())
|
expect(o.localizedString(meta(), [literal('')], [], []).serializeI18nHead())
|
||||||
.toEqual({cooked: '', raw: ''});
|
.toEqual({cooked: '', raw: ''});
|
||||||
expect(o.localizedString(meta('', '', 'desc'), [''], [], []).serializeI18nHead())
|
expect(o.localizedString(meta('', '', 'desc'), [literal('')], [], []).serializeI18nHead())
|
||||||
.toEqual({cooked: ':desc:', raw: ':desc:'});
|
.toEqual({cooked: ':desc:', raw: ':desc:'});
|
||||||
expect(o.localizedString(meta('id', '', 'desc'), [''], [], []).serializeI18nHead())
|
expect(o.localizedString(meta('id', '', 'desc'), [literal('')], [], []).serializeI18nHead())
|
||||||
.toEqual({cooked: ':desc@@id:', raw: ':desc@@id:'});
|
.toEqual({cooked: ':desc@@id:', raw: ':desc@@id:'});
|
||||||
expect(o.localizedString(meta('', 'meaning', 'desc'), [''], [], []).serializeI18nHead())
|
expect(
|
||||||
|
o.localizedString(meta('', 'meaning', 'desc'), [literal('')], [], []).serializeI18nHead())
|
||||||
.toEqual({cooked: ':meaning|desc:', raw: ':meaning|desc:'});
|
.toEqual({cooked: ':meaning|desc:', raw: ':meaning|desc:'});
|
||||||
expect(o.localizedString(meta('id', 'meaning', 'desc'), [''], [], []).serializeI18nHead())
|
expect(o.localizedString(meta('id', 'meaning', 'desc'), [literal('')], [], [])
|
||||||
|
.serializeI18nHead())
|
||||||
.toEqual({cooked: ':meaning|desc@@id:', raw: ':meaning|desc@@id:'});
|
.toEqual({cooked: ':meaning|desc@@id:', raw: ':meaning|desc@@id:'});
|
||||||
expect(o.localizedString(meta('id', '', ''), [''], [], []).serializeI18nHead())
|
expect(o.localizedString(meta('id', '', ''), [literal('')], [], []).serializeI18nHead())
|
||||||
.toEqual({cooked: ':@@id:', raw: ':@@id:'});
|
.toEqual({cooked: ':@@id:', raw: ':@@id:'});
|
||||||
|
|
||||||
// Escaping colons (block markers)
|
// Escaping colons (block markers)
|
||||||
expect(
|
expect(o.localizedString(meta('id:sub_id', 'meaning', 'desc'), [literal('')], [], [])
|
||||||
o.localizedString(meta('id:sub_id', 'meaning', 'desc'), [''], [], []).serializeI18nHead())
|
.serializeI18nHead())
|
||||||
.toEqual({cooked: ':meaning|desc@@id:sub_id:', raw: ':meaning|desc@@id\\:sub_id:'});
|
.toEqual({cooked: ':meaning|desc@@id:sub_id:', raw: ':meaning|desc@@id\\:sub_id:'});
|
||||||
expect(o.localizedString(meta('id', 'meaning:sub_meaning', 'desc'), [''], [], [])
|
expect(o.localizedString(meta('id', 'meaning:sub_meaning', 'desc'), [literal('')], [], [])
|
||||||
.serializeI18nHead())
|
.serializeI18nHead())
|
||||||
.toEqual(
|
.toEqual(
|
||||||
{cooked: ':meaning:sub_meaning|desc@@id:', raw: ':meaning\\:sub_meaning|desc@@id:'});
|
{cooked: ':meaning:sub_meaning|desc@@id:', raw: ':meaning\\:sub_meaning|desc@@id:'});
|
||||||
expect(o.localizedString(meta('id', 'meaning', 'desc:sub_desc'), [''], [], [])
|
expect(o.localizedString(meta('id', 'meaning', 'desc:sub_desc'), [literal('')], [], [])
|
||||||
.serializeI18nHead())
|
.serializeI18nHead())
|
||||||
.toEqual({cooked: ':meaning|desc:sub_desc@@id:', raw: ':meaning|desc\\:sub_desc@@id:'});
|
.toEqual({cooked: ':meaning|desc:sub_desc@@id:', raw: ':meaning|desc\\:sub_desc@@id:'});
|
||||||
expect(o.localizedString(meta('id', 'meaning', 'desc'), ['message source'], [], [])
|
expect(o.localizedString(meta('id', 'meaning', 'desc'), [literal('message source')], [], [])
|
||||||
.serializeI18nHead())
|
.serializeI18nHead())
|
||||||
.toEqual({
|
.toEqual({
|
||||||
cooked: ':meaning|desc@@id:message source',
|
cooked: ':meaning|desc@@id:message source',
|
||||||
raw: ':meaning|desc@@id:message source'
|
raw: ':meaning|desc@@id:message source'
|
||||||
});
|
});
|
||||||
expect(o.localizedString(meta('id', 'meaning', 'desc'), [':message source'], [], [])
|
expect(o.localizedString(meta('id', 'meaning', 'desc'), [literal(':message source')], [], [])
|
||||||
.serializeI18nHead())
|
.serializeI18nHead())
|
||||||
.toEqual({
|
.toEqual({
|
||||||
cooked: ':meaning|desc@@id::message source',
|
cooked: ':meaning|desc@@id::message source',
|
||||||
raw: ':meaning|desc@@id::message source'
|
raw: ':meaning|desc@@id::message source'
|
||||||
});
|
});
|
||||||
expect(o.localizedString(meta('', '', ''), ['message source'], [], []).serializeI18nHead())
|
expect(o.localizedString(meta('', '', ''), [literal('message source')], [], [])
|
||||||
|
.serializeI18nHead())
|
||||||
.toEqual({cooked: 'message source', raw: 'message source'});
|
.toEqual({cooked: 'message source', raw: 'message source'});
|
||||||
expect(o.localizedString(meta('', '', ''), [':message source'], [], []).serializeI18nHead())
|
expect(o.localizedString(meta('', '', ''), [literal(':message source')], [], [])
|
||||||
|
.serializeI18nHead())
|
||||||
.toEqual({cooked: ':message source', raw: '\\:message source'});
|
.toEqual({cooked: ':message source', raw: '\\:message source'});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('serializeI18nPlaceholderBlock()', () => {
|
it('serializeI18nPlaceholderBlock()', () => {
|
||||||
expect(o.localizedString(meta('', '', ''), ['', ''], [''], []).serializeI18nTemplatePart(1))
|
expect(o.localizedString(meta('', '', ''), [literal(''), literal('')], [literal('')], [])
|
||||||
|
.serializeI18nTemplatePart(1))
|
||||||
.toEqual({cooked: '', raw: ''});
|
.toEqual({cooked: '', raw: ''});
|
||||||
expect(
|
expect(o.localizedString(
|
||||||
o.localizedString(meta('', '', ''), ['', ''], ['abc'], []).serializeI18nTemplatePart(1))
|
meta('', '', ''), [literal(''), literal('')],
|
||||||
|
[new o.LiteralPiece('abc', {} as any)], [])
|
||||||
|
.serializeI18nTemplatePart(1))
|
||||||
.toEqual({cooked: ':abc:', raw: ':abc:'});
|
.toEqual({cooked: ':abc:', raw: ':abc:'});
|
||||||
expect(o.localizedString(meta('', '', ''), ['', 'message'], [''], [])
|
expect(
|
||||||
|
o.localizedString(meta('', '', ''), [literal(''), literal('message')], [literal('')], [])
|
||||||
.serializeI18nTemplatePart(1))
|
.serializeI18nTemplatePart(1))
|
||||||
.toEqual({cooked: 'message', raw: 'message'});
|
.toEqual({cooked: 'message', raw: 'message'});
|
||||||
expect(o.localizedString(meta('', '', ''), ['', 'message'], ['abc'], [])
|
expect(o.localizedString(
|
||||||
|
meta('', '', ''), [literal(''), literal('message')], [literal('abc')], [])
|
||||||
.serializeI18nTemplatePart(1))
|
.serializeI18nTemplatePart(1))
|
||||||
.toEqual({cooked: ':abc:message', raw: ':abc:message'});
|
.toEqual({cooked: ':abc:message', raw: ':abc:message'});
|
||||||
expect(o.localizedString(meta('', '', ''), ['', ':message'], [''], [])
|
expect(
|
||||||
|
o.localizedString(meta('', '', ''), [literal(''), literal(':message')], [literal('')], [])
|
||||||
.serializeI18nTemplatePart(1))
|
.serializeI18nTemplatePart(1))
|
||||||
.toEqual({cooked: ':message', raw: '\\:message'});
|
.toEqual({cooked: ':message', raw: '\\:message'});
|
||||||
expect(o.localizedString(meta('', '', ''), ['', ':message'], ['abc'], [])
|
expect(o.localizedString(
|
||||||
|
meta('', '', ''), [literal(''), literal(':message')], [literal('abc')], [])
|
||||||
.serializeI18nTemplatePart(1))
|
.serializeI18nTemplatePart(1))
|
||||||
.toEqual({cooked: ':abc::message', raw: ':abc::message'});
|
.toEqual({cooked: ':abc::message', raw: ':abc::message'});
|
||||||
});
|
});
|
||||||
|
@ -349,55 +361,106 @@ describe('serializeI18nMessageForLocalize', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should serialize plain text for `$localize()`', () => {
|
it('should serialize plain text for `$localize()`', () => {
|
||||||
expect(serialize('Some text')).toEqual({messageParts: ['Some text'], placeHolders: []});
|
expect(serialize('Some text'))
|
||||||
|
.toEqual({messageParts: [literal('Some text')], placeHolders: []});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should serialize text with interpolation for `$localize()`', () => {
|
it('should serialize text with interpolation for `$localize()`', () => {
|
||||||
expect(serialize('Some text {{ valueA }} and {{ valueB + valueC }} done')).toEqual({
|
expect(serialize('Some text {{ valueA }} and {{ valueB + valueC }} done')).toEqual({
|
||||||
messageParts: ['Some text ', ' and ', ' done'],
|
messageParts: [literal('Some text '), literal(' and '), literal(' done')],
|
||||||
placeHolders: ['INTERPOLATION', 'INTERPOLATION_1']
|
placeHolders: [placeholder('INTERPOLATION'), placeholder('INTERPOLATION_1')],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should compute source-spans when serializing text with interpolation for `$localize()`',
|
||||||
|
() => {
|
||||||
|
const {messageParts, placeHolders} =
|
||||||
|
serialize('Some text {{ valueA }} and {{ valueB + valueC }} done');
|
||||||
|
|
||||||
|
expect(messageParts[0].text).toEqual('Some text ');
|
||||||
|
expect(messageParts[0].sourceSpan.toString()).toEqual('Some text ');
|
||||||
|
expect(messageParts[1].text).toEqual(' and ');
|
||||||
|
expect(messageParts[1].sourceSpan.toString()).toEqual(' and ');
|
||||||
|
expect(messageParts[2].text).toEqual(' done');
|
||||||
|
expect(messageParts[2].sourceSpan.toString()).toEqual(' done');
|
||||||
|
|
||||||
|
expect(placeHolders[0].text).toEqual('INTERPOLATION');
|
||||||
|
expect(placeHolders[0].sourceSpan.toString()).toEqual('{{ valueA }}');
|
||||||
|
expect(placeHolders[1].text).toEqual('INTERPOLATION_1');
|
||||||
|
expect(placeHolders[1].sourceSpan.toString()).toEqual('{{ valueB + valueC }}');
|
||||||
|
});
|
||||||
|
|
||||||
it('should serialize text with interpolation at start for `$localize()`', () => {
|
it('should serialize text with interpolation at start for `$localize()`', () => {
|
||||||
expect(serialize('{{ valueA }} and {{ valueB + valueC }} done')).toEqual({
|
expect(serialize('{{ valueA }} and {{ valueB + valueC }} done')).toEqual({
|
||||||
messageParts: ['', ' and ', ' done'],
|
messageParts: [literal(''), literal(' and '), literal(' done')],
|
||||||
placeHolders: ['INTERPOLATION', 'INTERPOLATION_1']
|
placeHolders: [placeholder('INTERPOLATION'), placeholder('INTERPOLATION_1')],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should serialize text with interpolation at end for `$localize()`', () => {
|
it('should serialize text with interpolation at end for `$localize()`', () => {
|
||||||
expect(serialize('Some text {{ valueA }} and {{ valueB + valueC }}')).toEqual({
|
expect(serialize('Some text {{ valueA }} and {{ valueB + valueC }}')).toEqual({
|
||||||
messageParts: ['Some text ', ' and ', ''],
|
messageParts: [literal('Some text '), literal(' and '), literal('')],
|
||||||
placeHolders: ['INTERPOLATION', 'INTERPOLATION_1']
|
placeHolders: [placeholder('INTERPOLATION'), placeholder('INTERPOLATION_1')],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should serialize only interpolation for `$localize()`', () => {
|
it('should serialize only interpolation for `$localize()`', () => {
|
||||||
expect(serialize('{{ valueB + valueC }}'))
|
expect(serialize('{{ valueB + valueC }}')).toEqual({
|
||||||
.toEqual({messageParts: ['', ''], placeHolders: ['INTERPOLATION']});
|
messageParts: [literal(''), literal('')],
|
||||||
|
placeHolders: [placeholder('INTERPOLATION')]
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should serialize interpolation with named placeholder for `$localize()`', () => {
|
it('should serialize interpolation with named placeholder for `$localize()`', () => {
|
||||||
expect(serialize('{{ valueB + valueC // i18n(ph="PLACEHOLDER NAME") }}'))
|
expect(serialize('{{ valueB + valueC // i18n(ph="PLACEHOLDER NAME") }}')).toEqual({
|
||||||
.toEqual({messageParts: ['', ''], placeHolders: ['PLACEHOLDER_NAME']});
|
messageParts: [literal(''), literal('')],
|
||||||
|
placeHolders: [placeholder('PLACEHOLDER_NAME')]
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should serialize content with HTML tags for `$localize()`', () => {
|
it('should serialize content with HTML tags for `$localize()`', () => {
|
||||||
expect(serialize('A <span>B<div>C</div></span> D')).toEqual({
|
expect(serialize('A <span>B<div>C</div></span> D')).toEqual({
|
||||||
messageParts: ['A ', 'B', 'C', '', ' D'],
|
messageParts: [literal('A '), literal('B'), literal('C'), literal(''), literal(' D')],
|
||||||
placeHolders: ['START_TAG_SPAN', 'START_TAG_DIV', 'CLOSE_TAG_DIV', 'CLOSE_TAG_SPAN']
|
placeHolders: [
|
||||||
|
placeholder('START_TAG_SPAN'), placeholder('START_TAG_DIV'), placeholder('CLOSE_TAG_DIV'),
|
||||||
|
placeholder('CLOSE_TAG_SPAN')
|
||||||
|
]
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should compute source-spans when serializing content with HTML tags for `$localize()`',
|
||||||
|
() => {
|
||||||
|
const {messageParts, placeHolders} = serialize('A <span>B<div>C</div></span> D');
|
||||||
|
|
||||||
|
expect(messageParts[0].text).toEqual('A ');
|
||||||
|
expect(messageParts[0].sourceSpan.toString()).toEqual('A ');
|
||||||
|
expect(messageParts[1].text).toEqual('B');
|
||||||
|
expect(messageParts[1].sourceSpan.toString()).toEqual('B');
|
||||||
|
expect(messageParts[2].text).toEqual('C');
|
||||||
|
expect(messageParts[2].sourceSpan.toString()).toEqual('C');
|
||||||
|
expect(messageParts[3].text).toEqual('');
|
||||||
|
expect(messageParts[3].sourceSpan.toString()).toEqual('');
|
||||||
|
expect(messageParts[4].text).toEqual(' D');
|
||||||
|
expect(messageParts[4].sourceSpan.toString()).toEqual(' D');
|
||||||
|
|
||||||
|
expect(placeHolders[0].text).toEqual('START_TAG_SPAN');
|
||||||
|
expect(placeHolders[0].sourceSpan.toString()).toEqual('<span>');
|
||||||
|
expect(placeHolders[1].text).toEqual('START_TAG_DIV');
|
||||||
|
expect(placeHolders[1].sourceSpan.toString()).toEqual('<div>');
|
||||||
|
expect(placeHolders[2].text).toEqual('CLOSE_TAG_DIV');
|
||||||
|
expect(placeHolders[2].sourceSpan.toString()).toEqual('</div>');
|
||||||
|
expect(placeHolders[3].text).toEqual('CLOSE_TAG_SPAN');
|
||||||
|
expect(placeHolders[3].sourceSpan.toString()).toEqual('</span>');
|
||||||
|
});
|
||||||
|
|
||||||
it('should serialize simple ICU for `$localize()`', () => {
|
it('should serialize simple ICU for `$localize()`', () => {
|
||||||
expect(serialize('{age, plural, 10 {ten} other {other}}')).toEqual({
|
expect(serialize('{age, plural, 10 {ten} other {other}}')).toEqual({
|
||||||
messageParts: ['{VAR_PLURAL, plural, 10 {ten} other {other}}'],
|
messageParts: [literal('{VAR_PLURAL, plural, 10 {ten} other {other}}')],
|
||||||
placeHolders: []
|
placeHolders: []
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -408,7 +471,8 @@ describe('serializeI18nMessageForLocalize', () => {
|
||||||
'{age, plural, 10 {ten {size, select, 1 {one} 2 {two} other {2+}}} other {other}}'))
|
'{age, plural, 10 {ten {size, select, 1 {one} 2 {two} other {2+}}} other {other}}'))
|
||||||
.toEqual({
|
.toEqual({
|
||||||
messageParts: [
|
messageParts: [
|
||||||
'{VAR_PLURAL, plural, 10 {ten {VAR_SELECT, select, 1 {one} 2 {two} other {2+}}} other {other}}'
|
literal(
|
||||||
|
'{VAR_PLURAL, plural, 10 {ten {VAR_SELECT, select, 1 {one} 2 {two} other {2+}}} other {other}}')
|
||||||
],
|
],
|
||||||
placeHolders: []
|
placeHolders: []
|
||||||
});
|
});
|
||||||
|
@ -418,7 +482,8 @@ describe('serializeI18nMessageForLocalize', () => {
|
||||||
it('should serialize ICU with embedded HTML for `$localize()`', () => {
|
it('should serialize ICU with embedded HTML for `$localize()`', () => {
|
||||||
expect(serialize('{age, plural, 10 {<b>ten</b>} other {<div class="A">other</div>}}')).toEqual({
|
expect(serialize('{age, plural, 10 {<b>ten</b>} other {<div class="A">other</div>}}')).toEqual({
|
||||||
messageParts: [
|
messageParts: [
|
||||||
'{VAR_PLURAL, plural, 10 {{START_BOLD_TEXT}ten{CLOSE_BOLD_TEXT}} other {{START_TAG_DIV}other{CLOSE_TAG_DIV}}}'
|
literal(
|
||||||
|
'{VAR_PLURAL, plural, 10 {{START_BOLD_TEXT}ten{CLOSE_BOLD_TEXT}} other {{START_TAG_DIV}other{CLOSE_TAG_DIV}}}')
|
||||||
],
|
],
|
||||||
placeHolders: []
|
placeHolders: []
|
||||||
});
|
});
|
||||||
|
@ -427,7 +492,8 @@ describe('serializeI18nMessageForLocalize', () => {
|
||||||
it('should serialize ICU with embedded interpolation for `$localize()`', () => {
|
it('should serialize ICU with embedded interpolation for `$localize()`', () => {
|
||||||
expect(serialize('{age, plural, 10 {<b>ten</b>} other {{{age}} years old}}')).toEqual({
|
expect(serialize('{age, plural, 10 {<b>ten</b>} other {{{age}} years old}}')).toEqual({
|
||||||
messageParts: [
|
messageParts: [
|
||||||
'{VAR_PLURAL, plural, 10 {{START_BOLD_TEXT}ten{CLOSE_BOLD_TEXT}} other {{INTERPOLATION} years old}}'
|
literal(
|
||||||
|
'{VAR_PLURAL, plural, 10 {{START_BOLD_TEXT}ten{CLOSE_BOLD_TEXT}} other {{INTERPOLATION} years old}}')
|
||||||
],
|
],
|
||||||
placeHolders: []
|
placeHolders: []
|
||||||
});
|
});
|
||||||
|
@ -438,8 +504,11 @@ describe('serializeI18nMessageForLocalize', () => {
|
||||||
serialize(
|
serialize(
|
||||||
'{gender, select, male {male} female {female} other {other}}<div>{gender, select, male {male} female {female} other {other}}</div>'))
|
'{gender, select, male {male} female {female} other {other}}<div>{gender, select, male {male} female {female} other {other}}</div>'))
|
||||||
.toEqual({
|
.toEqual({
|
||||||
messageParts: ['', '', '', '', ''],
|
messageParts: [literal(''), literal(''), literal(''), literal(''), literal('')],
|
||||||
placeHolders: ['ICU', 'START_TAG_DIV', 'ICU', 'CLOSE_TAG_DIV']
|
placeHolders: [
|
||||||
|
placeholder('ICU'), placeholder('START_TAG_DIV'), placeholder('ICU'),
|
||||||
|
placeholder('CLOSE_TAG_DIV')
|
||||||
|
]
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -449,7 +518,8 @@ describe('serializeI18nMessageForLocalize', () => {
|
||||||
'{age, plural, 10 {ten {size, select, 1 {{{ varOne }}} 2 {{{ varTwo }}} other {2+}}} other {other}}'))
|
'{age, plural, 10 {ten {size, select, 1 {{{ varOne }}} 2 {{{ varTwo }}} other {2+}}} other {other}}'))
|
||||||
.toEqual({
|
.toEqual({
|
||||||
messageParts: [
|
messageParts: [
|
||||||
'{VAR_PLURAL, plural, 10 {ten {VAR_SELECT, select, 1 {{INTERPOLATION}} 2 {{INTERPOLATION_1}} other {2+}}} other {other}}'
|
literal(
|
||||||
|
'{VAR_PLURAL, plural, 10 {ten {VAR_SELECT, select, 1 {{INTERPOLATION}} 2 {{INTERPOLATION_1}} other {2+}}} other {other}}')
|
||||||
],
|
],
|
||||||
placeHolders: []
|
placeHolders: []
|
||||||
});
|
});
|
||||||
|
@ -486,3 +556,11 @@ describe('serializeIcuNode', () => {
|
||||||
.toEqual('{VAR_SELECT, select, 10 {ten} other {{INTERPOLATION} years old}}');
|
.toEqual('{VAR_SELECT, select, 10 {ten} other {{INTERPOLATION} years old}}');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function literal(text: string, span: any = jasmine.any(ParseSourceSpan)): o.LiteralPiece {
|
||||||
|
return new o.LiteralPiece(text, span);
|
||||||
|
}
|
||||||
|
|
||||||
|
function placeholder(name: string, span: any = jasmine.any(ParseSourceSpan)): o.PlaceholderPiece {
|
||||||
|
return new o.PlaceholderPiece(name, span);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue