feat(ICU): enable ICU extraction even when when in is not used
BREAKING CHANGES: "{" is used a a delimiter for ICU messages then it could not be used in text nodes. "{" should be escaped as "{{ '{' }}" Before: <span>some { valid } text</span> After: <span>some { invalid } text<span> <!-- throw parse error --> <span>some {{ '{' }} valid } text</span>
This commit is contained in:
@ -6,10 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {BaseException} from '../facade/exceptions';
|
||||
import {HtmlAst, HtmlAstVisitor, HtmlAttrAst, HtmlCommentAst, HtmlElementAst, HtmlExpansionAst, HtmlExpansionCaseAst, HtmlTextAst, htmlVisitAll} from '../html_ast';
|
||||
import {ParseError} from '../parse_util';
|
||||
import {I18nError} from './shared';
|
||||
import {HtmlAst, HtmlAstVisitor, HtmlAttrAst, HtmlCommentAst, HtmlElementAst, HtmlExpansionAst, HtmlExpansionCaseAst, HtmlTextAst, htmlVisitAll} from './html_ast';
|
||||
import {ParseError, ParseSourceSpan} from './parse_util';
|
||||
|
||||
// http://cldr.unicode.org/index/cldr-spec/plural-rules
|
||||
const PLURAL_CASES: string[] = ['zero', 'one', 'two', 'few', 'many', 'other'];
|
||||
@ -46,6 +44,10 @@ export class ExpansionResult {
|
||||
constructor(public nodes: HtmlAst[], public expanded: boolean, public errors: ParseError[]) {}
|
||||
}
|
||||
|
||||
export class ExpansionError extends ParseError {
|
||||
constructor(span: ParseSourceSpan, errorMsg: string) { super(span, errorMsg); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand expansion forms (plural, select) to directives
|
||||
*
|
||||
@ -74,14 +76,14 @@ class _Expander implements HtmlAstVisitor {
|
||||
}
|
||||
|
||||
visitExpansionCase(ast: HtmlExpansionCaseAst, context: any): any {
|
||||
throw new BaseException('Should not be reached');
|
||||
throw new Error('Should not be reached');
|
||||
}
|
||||
}
|
||||
|
||||
function _expandPluralForm(ast: HtmlExpansionAst, errors: ParseError[]): HtmlElementAst {
|
||||
const children = ast.cases.map(c => {
|
||||
if (PLURAL_CASES.indexOf(c.value) == -1 && !c.value.match(/^=\d+$/)) {
|
||||
errors.push(new I18nError(
|
||||
errors.push(new ExpansionError(
|
||||
c.valueSourceSpan,
|
||||
`Plural cases should be "=<number>" or one of ${PLURAL_CASES.join(", ")}`));
|
||||
}
|
@ -14,7 +14,6 @@ import {HtmlToken, HtmlTokenType, tokenizeHtml} from './html_lexer';
|
||||
import {ParseError, ParseSourceSpan} from './parse_util';
|
||||
import {getHtmlTagDefinition, getNsPrefix, mergeNsAndName} from './html_tags';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './interpolation_config';
|
||||
import {Parser as ExpressionParser} from './expression_parser/parser';
|
||||
|
||||
export class HtmlTreeError extends ParseError {
|
||||
static create(elementName: string, span: ParseSourceSpan, msg: string): HtmlTreeError {
|
||||
@ -34,9 +33,9 @@ export class HtmlParser {
|
||||
sourceContent: string, sourceUrl: string, parseExpansionForms: boolean = false,
|
||||
interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG):
|
||||
HtmlParseTreeResult {
|
||||
var tokensAndErrors =
|
||||
const tokensAndErrors =
|
||||
tokenizeHtml(sourceContent, sourceUrl, parseExpansionForms, interpolationConfig);
|
||||
var treeAndErrors = new TreeBuilder(tokensAndErrors.tokens).build();
|
||||
const treeAndErrors = new TreeBuilder(tokensAndErrors.tokens).build();
|
||||
return new HtmlParseTreeResult(
|
||||
treeAndErrors.rootNodes,
|
||||
(<ParseError[]>tokensAndErrors.errors).concat(treeAndErrors.errors));
|
||||
@ -82,7 +81,7 @@ class TreeBuilder {
|
||||
}
|
||||
|
||||
private _advance(): HtmlToken {
|
||||
var prev = this.peek;
|
||||
const prev = this.peek;
|
||||
if (this.index < this.tokens.length - 1) {
|
||||
// Note: there is always an EOF token at the end
|
||||
this.index++;
|
||||
@ -104,17 +103,17 @@ class TreeBuilder {
|
||||
}
|
||||
|
||||
private _consumeComment(token: HtmlToken) {
|
||||
var text = this._advanceIf(HtmlTokenType.RAW_TEXT);
|
||||
const text = this._advanceIf(HtmlTokenType.RAW_TEXT);
|
||||
this._advanceIf(HtmlTokenType.COMMENT_END);
|
||||
var value = isPresent(text) ? text.parts[0].trim() : null;
|
||||
const value = isPresent(text) ? text.parts[0].trim() : null;
|
||||
this._addToParent(new HtmlCommentAst(value, token.sourceSpan));
|
||||
}
|
||||
|
||||
private _consumeExpansion(token: HtmlToken) {
|
||||
let switchValue = this._advance();
|
||||
const switchValue = this._advance();
|
||||
|
||||
let type = this._advance();
|
||||
let cases: HtmlExpansionCaseAst[] = [];
|
||||
const type = this._advance();
|
||||
const cases: HtmlExpansionCaseAst[] = [];
|
||||
|
||||
// read =
|
||||
while (this.peek.type === HtmlTokenType.EXPANSION_CASE_VALUE) {
|
||||
@ -131,13 +130,13 @@ class TreeBuilder {
|
||||
}
|
||||
this._advance();
|
||||
|
||||
let mainSourceSpan = new ParseSourceSpan(token.sourceSpan.start, this.peek.sourceSpan.end);
|
||||
const mainSourceSpan = new ParseSourceSpan(token.sourceSpan.start, this.peek.sourceSpan.end);
|
||||
this._addToParent(new HtmlExpansionAst(
|
||||
switchValue.parts[0], type.parts[0], cases, mainSourceSpan, switchValue.sourceSpan));
|
||||
}
|
||||
|
||||
private _parseExpansionCase(): HtmlExpansionCaseAst {
|
||||
let value = this._advance();
|
||||
const value = this._advance();
|
||||
|
||||
// read {
|
||||
if (this.peek.type !== HtmlTokenType.EXPANSION_CASE_EXP_START) {
|
||||
@ -147,30 +146,30 @@ class TreeBuilder {
|
||||
}
|
||||
|
||||
// read until }
|
||||
let start = this._advance();
|
||||
const start = this._advance();
|
||||
|
||||
let exp = this._collectExpansionExpTokens(start);
|
||||
const exp = this._collectExpansionExpTokens(start);
|
||||
if (isBlank(exp)) return null;
|
||||
|
||||
let end = this._advance();
|
||||
const end = this._advance();
|
||||
exp.push(new HtmlToken(HtmlTokenType.EOF, [], end.sourceSpan));
|
||||
|
||||
// parse everything in between { and }
|
||||
let parsedExp = new TreeBuilder(exp).build();
|
||||
const parsedExp = new TreeBuilder(exp).build();
|
||||
if (parsedExp.errors.length > 0) {
|
||||
this.errors = this.errors.concat(<HtmlTreeError[]>parsedExp.errors);
|
||||
return null;
|
||||
}
|
||||
|
||||
let sourceSpan = new ParseSourceSpan(value.sourceSpan.start, end.sourceSpan.end);
|
||||
let expSourceSpan = new ParseSourceSpan(start.sourceSpan.start, end.sourceSpan.end);
|
||||
const sourceSpan = new ParseSourceSpan(value.sourceSpan.start, end.sourceSpan.end);
|
||||
const expSourceSpan = new ParseSourceSpan(start.sourceSpan.start, end.sourceSpan.end);
|
||||
return new HtmlExpansionCaseAst(
|
||||
value.parts[0], parsedExp.rootNodes, sourceSpan, value.sourceSpan, expSourceSpan);
|
||||
}
|
||||
|
||||
private _collectExpansionExpTokens(start: HtmlToken): HtmlToken[] {
|
||||
let exp: HtmlToken[] = [];
|
||||
let expansionFormStack = [HtmlTokenType.EXPANSION_CASE_EXP_START];
|
||||
const exp: HtmlToken[] = [];
|
||||
const expansionFormStack = [HtmlTokenType.EXPANSION_CASE_EXP_START];
|
||||
|
||||
while (true) {
|
||||
if (this.peek.type === HtmlTokenType.EXPANSION_FORM_START ||
|
||||
@ -213,7 +212,7 @@ class TreeBuilder {
|
||||
private _consumeText(token: HtmlToken) {
|
||||
let text = token.parts[0];
|
||||
if (text.length > 0 && text[0] == '\n') {
|
||||
let parent = this._getParentElement();
|
||||
const parent = this._getParentElement();
|
||||
if (isPresent(parent) && parent.children.length == 0 &&
|
||||
getHtmlTagDefinition(parent.name).ignoreFirstLf) {
|
||||
text = text.substring(1);
|
||||
@ -227,7 +226,7 @@ class TreeBuilder {
|
||||
|
||||
private _closeVoidElement(): void {
|
||||
if (this.elementStack.length > 0) {
|
||||
let el = ListWrapper.last(this.elementStack);
|
||||
const el = ListWrapper.last(this.elementStack);
|
||||
|
||||
if (getHtmlTagDefinition(el.name).isVoid) {
|
||||
this.elementStack.pop();
|
||||
@ -236,14 +235,14 @@ class TreeBuilder {
|
||||
}
|
||||
|
||||
private _consumeStartTag(startTagToken: HtmlToken) {
|
||||
var prefix = startTagToken.parts[0];
|
||||
var name = startTagToken.parts[1];
|
||||
var attrs: HtmlAttrAst[] = [];
|
||||
const prefix = startTagToken.parts[0];
|
||||
const name = startTagToken.parts[1];
|
||||
const attrs: HtmlAttrAst[] = [];
|
||||
while (this.peek.type === HtmlTokenType.ATTR_NAME) {
|
||||
attrs.push(this._consumeAttr(this._advance()));
|
||||
}
|
||||
var fullName = getElementFullName(prefix, name, this._getParentElement());
|
||||
var selfClosing = false;
|
||||
const fullName = getElementFullName(prefix, name, this._getParentElement());
|
||||
let selfClosing = false;
|
||||
// Note: There could have been a tokenizer error
|
||||
// so that we don't get a token for the end tag...
|
||||
if (this.peek.type === HtmlTokenType.TAG_OPEN_END_VOID) {
|
||||
@ -258,9 +257,9 @@ class TreeBuilder {
|
||||
this._advance();
|
||||
selfClosing = false;
|
||||
}
|
||||
var end = this.peek.sourceSpan.start;
|
||||
let span = new ParseSourceSpan(startTagToken.sourceSpan.start, end);
|
||||
var el = new HtmlElementAst(fullName, attrs, [], span, span, null);
|
||||
const end = this.peek.sourceSpan.start;
|
||||
const span = new ParseSourceSpan(startTagToken.sourceSpan.start, end);
|
||||
const el = new HtmlElementAst(fullName, attrs, [], span, span, null);
|
||||
this._pushElement(el);
|
||||
if (selfClosing) {
|
||||
this._popElement(fullName);
|
||||
@ -270,7 +269,7 @@ class TreeBuilder {
|
||||
|
||||
private _pushElement(el: HtmlElementAst) {
|
||||
if (this.elementStack.length > 0) {
|
||||
var parentEl = ListWrapper.last(this.elementStack);
|
||||
const parentEl = ListWrapper.last(this.elementStack);
|
||||
if (getHtmlTagDefinition(parentEl.name).isClosedByChild(el.name)) {
|
||||
this.elementStack.pop();
|
||||
}
|
||||
@ -280,7 +279,7 @@ class TreeBuilder {
|
||||
const {parent, container} = this._getParentElementSkippingContainers();
|
||||
|
||||
if (isPresent(parent) && tagDef.requireExtraParent(parent.name)) {
|
||||
var newParent = new HtmlElementAst(
|
||||
const newParent = new HtmlElementAst(
|
||||
tagDef.parentToAdd, [], [], el.sourceSpan, el.startSourceSpan, el.endSourceSpan);
|
||||
this._insertBeforeContainer(parent, container, newParent);
|
||||
}
|
||||
@ -290,7 +289,7 @@ class TreeBuilder {
|
||||
}
|
||||
|
||||
private _consumeEndTag(endTagToken: HtmlToken) {
|
||||
var fullName =
|
||||
const fullName =
|
||||
getElementFullName(endTagToken.parts[0], endTagToken.parts[1], this._getParentElement());
|
||||
|
||||
if (this._getParentElement()) {
|
||||
@ -309,7 +308,7 @@ class TreeBuilder {
|
||||
|
||||
private _popElement(fullName: string): boolean {
|
||||
for (let stackIndex = this.elementStack.length - 1; stackIndex >= 0; stackIndex--) {
|
||||
let el = this.elementStack[stackIndex];
|
||||
const el = this.elementStack[stackIndex];
|
||||
if (el.name == fullName) {
|
||||
ListWrapper.splice(this.elementStack, stackIndex, this.elementStack.length - stackIndex);
|
||||
return true;
|
||||
@ -323,11 +322,11 @@ class TreeBuilder {
|
||||
}
|
||||
|
||||
private _consumeAttr(attrName: HtmlToken): HtmlAttrAst {
|
||||
var fullName = mergeNsAndName(attrName.parts[0], attrName.parts[1]);
|
||||
var end = attrName.sourceSpan.end;
|
||||
var value = '';
|
||||
const fullName = mergeNsAndName(attrName.parts[0], attrName.parts[1]);
|
||||
let end = attrName.sourceSpan.end;
|
||||
let value = '';
|
||||
if (this.peek.type === HtmlTokenType.ATTR_VALUE) {
|
||||
var valueToken = this._advance();
|
||||
const valueToken = this._advance();
|
||||
value = valueToken.parts[0];
|
||||
end = valueToken.sourceSpan.end;
|
||||
}
|
||||
@ -358,7 +357,7 @@ class TreeBuilder {
|
||||
}
|
||||
|
||||
private _addToParent(node: HtmlAst) {
|
||||
var parent = this._getParentElement();
|
||||
const parent = this._getParentElement();
|
||||
if (isPresent(parent)) {
|
||||
parent.children.push(node);
|
||||
} else {
|
||||
@ -381,7 +380,7 @@ class TreeBuilder {
|
||||
} else {
|
||||
if (parent) {
|
||||
// replace the container with the new node in the children
|
||||
let index = parent.children.indexOf(container);
|
||||
const index = parent.children.indexOf(container);
|
||||
parent.children[index] = node;
|
||||
} else {
|
||||
this.rootNodes.push(node);
|
||||
|
@ -14,14 +14,12 @@ import {HtmlAst, HtmlAstVisitor, HtmlAttrAst, HtmlCommentAst, HtmlElementAst, Ht
|
||||
import {HtmlParseTreeResult, HtmlParser} from '../html_parser';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../interpolation_config';
|
||||
import {ParseError, ParseSourceSpan} from '../parse_util';
|
||||
|
||||
import {expandNodes} from './expander';
|
||||
import {Message, id} from './message';
|
||||
import {I18N_ATTR, I18N_ATTR_PREFIX, I18nError, Part, dedupePhName, extractPhNameFromInterpolation, messageFromAttribute, messageFromI18nAttribute, partition} from './shared';
|
||||
|
||||
const _PLACEHOLDER_ELEMENT = 'ph';
|
||||
const _NAME_ATTR = 'name';
|
||||
let _PLACEHOLDER_EXPANDED_REGEXP = /<ph(\s)+name=("(\w)+")><\/ph>/gi;
|
||||
const _PLACEHOLDER_EXPANDED_REGEXP = /<ph(\s)+name=("(\w)+")><\/ph>/gi;
|
||||
|
||||
/**
|
||||
* Creates an i18n-ed version of the parsed template.
|
||||
@ -72,9 +70,7 @@ export class I18nHtmlParser implements HtmlParser {
|
||||
return res;
|
||||
}
|
||||
|
||||
const expanded = expandNodes(res.rootNodes);
|
||||
const nodes = this._recurse(expanded.nodes);
|
||||
this.errors.push(...expanded.errors);
|
||||
const nodes = this._recurse(res.rootNodes);
|
||||
|
||||
return this.errors.length > 0 ? new HtmlParseTreeResult([], this.errors) :
|
||||
new HtmlParseTreeResult(nodes, []);
|
||||
|
@ -7,33 +7,26 @@
|
||||
*/
|
||||
|
||||
import {Inject, Injectable, OpaqueToken, Optional, SecurityContext} from '@angular/core';
|
||||
|
||||
import {Console, MAX_INTERPOLATION_VALUES} from '../core_private';
|
||||
|
||||
import {ListWrapper, StringMapWrapper, SetWrapper,} from '../src/facade/collection';
|
||||
import {RegExpWrapper, isPresent, StringWrapper, isBlank, isArray} from '../src/facade/lang';
|
||||
import {RegExpWrapper, isPresent, StringWrapper, isBlank} from '../src/facade/lang';
|
||||
import {BaseException} from '../src/facade/exceptions';
|
||||
import {AST, Interpolation, ASTWithSource, TemplateBinding, RecursiveAstVisitor, BindingPipe, ParserError} from './expression_parser/ast';
|
||||
import {Parser} from './expression_parser/parser';
|
||||
import {CompileDirectiveMetadata, CompilePipeMetadata, CompileMetadataWithType, CompileTokenMetadata,} from './compile_metadata';
|
||||
import {HtmlParser} from './html_parser';
|
||||
import {HtmlParser, HtmlParseTreeResult} from './html_parser';
|
||||
import {splitNsName, mergeNsAndName} from './html_tags';
|
||||
import {ParseSourceSpan, ParseError, ParseErrorLevel} from './parse_util';
|
||||
import {InterpolationConfig} from './interpolation_config';
|
||||
|
||||
import {ElementAst, BoundElementPropertyAst, BoundEventAst, ReferenceAst, TemplateAst, TemplateAstVisitor, templateVisitAll, TextAst, BoundTextAst, EmbeddedTemplateAst, AttrAst, NgContentAst, PropertyBindingType, DirectiveAst, BoundDirectivePropertyAst, ProviderAst, ProviderAstType, VariableAst} from './template_ast';
|
||||
import {CssSelector, SelectorMatcher} from './selector';
|
||||
|
||||
import {ElementSchemaRegistry} from './schema/element_schema_registry';
|
||||
import {preparseElement, PreparsedElementType} from './template_preparser';
|
||||
|
||||
import {isStyleUrlResolvable} from './style_url_resolver';
|
||||
|
||||
import {HtmlAstVisitor, HtmlElementAst, HtmlAttrAst, HtmlTextAst, HtmlCommentAst, HtmlExpansionAst, HtmlExpansionCaseAst, htmlVisitAll} from './html_ast';
|
||||
|
||||
import {splitAtColon} from './util';
|
||||
import {identifierToken, Identifiers} from './identifiers';
|
||||
|
||||
import {expandNodes} from './expander';
|
||||
import {ProviderElementContext, ProviderViewContext} from './provider_parser';
|
||||
|
||||
// Group 1 = "bind-"
|
||||
@ -113,11 +106,18 @@ export class TemplateParser {
|
||||
if (component.template) {
|
||||
interpolationConfig = InterpolationConfig.fromArray(component.template.interpolation);
|
||||
}
|
||||
const htmlAstWithErrors =
|
||||
this._htmlParser.parse(template, templateUrl, false, interpolationConfig);
|
||||
let htmlAstWithErrors =
|
||||
this._htmlParser.parse(template, templateUrl, true, interpolationConfig);
|
||||
const errors: ParseError[] = htmlAstWithErrors.errors;
|
||||
let result: TemplateAst[];
|
||||
|
||||
if (errors.length == 0) {
|
||||
// Transform ICU messages to angular directives
|
||||
const expandedHtmlAst = expandNodes(htmlAstWithErrors.rootNodes);
|
||||
errors.push(...expandedHtmlAst.errors);
|
||||
htmlAstWithErrors = new HtmlParseTreeResult(expandedHtmlAst.nodes, errors);
|
||||
}
|
||||
|
||||
if (htmlAstWithErrors.rootNodes.length > 0) {
|
||||
const uniqDirectives = <CompileDirectiveMetadata[]>removeDuplicates(directives);
|
||||
const uniqPipes = <CompilePipeMetadata[]>removeDuplicates(pipes);
|
||||
@ -137,10 +137,12 @@ export class TemplateParser {
|
||||
if (errors.length > 0) {
|
||||
return new TemplateParseResult(result, errors);
|
||||
}
|
||||
|
||||
if (isPresent(this.transforms)) {
|
||||
this.transforms.forEach(
|
||||
(transform: TemplateAstVisitor) => { result = templateVisitAll(transform, result); });
|
||||
}
|
||||
|
||||
return new TemplateParseResult(result, errors);
|
||||
}
|
||||
|
||||
|
@ -6,12 +6,13 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {HtmlAttrAst, HtmlElementAst, HtmlTextAst} from '@angular/compiler/src/html_ast';
|
||||
import {HtmlParser} from '@angular/compiler/src/html_parser';
|
||||
import {ExpansionResult, expandNodes} from '@angular/compiler/src/i18n/expander';
|
||||
import {ParseError} from '@angular/compiler/src/parse_util';
|
||||
import {humanizeNodes} from '@angular/compiler/test/html_ast_spec_utils';
|
||||
import {ddescribe, describe, expect, iit, it} from '@angular/core/testing/testing_internal';
|
||||
import {ddescribe, describe, expect, iit, it} from '../../core/testing/testing_internal';
|
||||
import {ExpansionResult, expandNodes} from '../src/expander';
|
||||
import {HtmlAttrAst, HtmlElementAst, HtmlTextAst} from '../src/html_ast';
|
||||
import {HtmlParser} from '../src/html_parser';
|
||||
import {ParseError} from '../src/parse_util';
|
||||
|
||||
import {humanizeNodes} from './html_ast_spec_utils';
|
||||
|
||||
export function main() {
|
||||
describe('Expander', () => {
|
@ -30,8 +30,6 @@ var MOCK_SCHEMA_REGISTRY = [{
|
||||
useValue: new MockSchemaRegistry({'invalidProp': false}, {'mappedAttr': 'mappedProp'})
|
||||
}];
|
||||
|
||||
let zeConsole = console;
|
||||
|
||||
export function main() {
|
||||
var ngIf: CompileDirectiveMetadata;
|
||||
var parse:
|
||||
@ -864,19 +862,18 @@ There is no directive with "exportAs" set to "dirA" ("<div [ERROR ->]#a="dirA"><
|
||||
});
|
||||
|
||||
it('should report duplicate reference names', () => {
|
||||
expect(() => parse('<div #a></div><div #a></div>', []))
.toThrowError(
|
||||
`Template parse errors:
|
||||
expect(() => parse('<div #a></div><div #a></div>', []))
|
||||
.toThrowError(`Template parse errors:
|
||||
Reference "#a" is defined several times ("<div #a></div><div [ERROR ->]#a></div>"): TestComp@0:19`);
|
||||
|
||||
});
|
||||
|
||||
it(
|
||||
'should not throw error when there is same reference name in different templates',
|
||||
() => {
|
||||
expect(() => parse('<div #a><template #a><span>OK</span></template></div>', []))
|
||||
.not.toThrowError();
|
||||
it('should not throw error when there is same reference name in different templates',
|
||||
() => {
|
||||
expect(() => parse('<div #a><template #a><span>OK</span></template></div>', []))
|
||||
.not.toThrowError();
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
it('should assign references with empty value to components', () => {
|
||||
var dirA = CompileDirectiveMetadata.create({
|
||||
@ -1506,6 +1503,38 @@ The pipe 'test' could not be found ("[ERROR ->]{{a | test}}"): TestComp@0:0`);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('ICU messages', () => {
|
||||
it('should expand plural messages', () => {
|
||||
const shortForm = '{ count, plural, =0 {small} many {big} }';
|
||||
const expandedForm = '<ng-container [ngPlural]="count">' +
|
||||
'<template ngPluralCase="=0">small</template>' +
|
||||
'<template ngPluralCase="many">big</template>' +
|
||||
'</ng-container>';
|
||||
|
||||
expect(humanizeTplAst(parse(shortForm, []))).toEqual(humanizeTplAst(parse(expandedForm, [
|
||||
])));
|
||||
});
|
||||
|
||||
it('should expand other messages', () => {
|
||||
const shortForm = '{ sex, gender, =f {foo} other {bar} }';
|
||||
const expandedForm = '<ng-container [ngSwitch]="sex">' +
|
||||
'<template ngSwitchCase="=f">foo</template>' +
|
||||
'<template ngSwitchCase="other">bar</template>' +
|
||||
'</ng-container>';
|
||||
|
||||
expect(humanizeTplAst(parse(shortForm, []))).toEqual(humanizeTplAst(parse(expandedForm, [
|
||||
])));
|
||||
});
|
||||
|
||||
it('should be possible to escape ICU messages', () => {
|
||||
const escapedForm = 'escaped {{ "{" }} }';
|
||||
|
||||
expect(humanizeTplAst(parse(escapedForm, []))).toEqual([
|
||||
[BoundTextAst, 'escaped {{ "{" }} }'],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user