parent
22ae2d0976
commit
c6244d1470
|
@ -125,8 +125,7 @@ class _HtmlTokenizer {
|
|||
private currentTokenStart: ParseLocation;
|
||||
private currentTokenType: HtmlTokenType;
|
||||
|
||||
private inExpansionCase: boolean = false;
|
||||
private inExpansionForm: boolean = false;
|
||||
private expansionCaseStack = [];
|
||||
|
||||
tokens: HtmlToken[] = [];
|
||||
errors: HtmlTokenError[] = [];
|
||||
|
@ -169,10 +168,12 @@ class _HtmlTokenizer {
|
|||
} else if (this.peek === $EQ && this.tokenizeExpansionForms) {
|
||||
this._consumeExpansionCaseStart();
|
||||
|
||||
} else if (this.peek === $RBRACE && this.inExpansionCase && this.tokenizeExpansionForms) {
|
||||
} else if (this.peek === $RBRACE && this.isInExpansionCase() &&
|
||||
this.tokenizeExpansionForms) {
|
||||
this._consumeExpansionCaseEnd();
|
||||
|
||||
} else if (this.peek === $RBRACE && !this.inExpansionCase && this.tokenizeExpansionForms) {
|
||||
} else if (this.peek === $RBRACE && this.isInExpansionForm() &&
|
||||
this.tokenizeExpansionForms) {
|
||||
this._consumeExpansionFormEnd();
|
||||
|
||||
} else {
|
||||
|
@ -551,7 +552,7 @@ class _HtmlTokenizer {
|
|||
this._requireCharCode($COMMA);
|
||||
this._attemptCharCodeUntilFn(isNotWhitespace);
|
||||
|
||||
this.inExpansionForm = true;
|
||||
this.expansionCaseStack.push(HtmlTokenType.EXPANSION_FORM_START);
|
||||
}
|
||||
|
||||
private _consumeExpansionCaseStart() {
|
||||
|
@ -567,7 +568,7 @@ class _HtmlTokenizer {
|
|||
this._endToken([], this._getLocation());
|
||||
this._attemptCharCodeUntilFn(isNotWhitespace);
|
||||
|
||||
this.inExpansionCase = true;
|
||||
this.expansionCaseStack.push(HtmlTokenType.EXPANSION_CASE_EXP_START);
|
||||
}
|
||||
|
||||
private _consumeExpansionCaseEnd() {
|
||||
|
@ -576,7 +577,7 @@ class _HtmlTokenizer {
|
|||
this._endToken([], this._getLocation());
|
||||
this._attemptCharCodeUntilFn(isNotWhitespace);
|
||||
|
||||
this.inExpansionCase = false;
|
||||
this.expansionCaseStack.pop();
|
||||
}
|
||||
|
||||
private _consumeExpansionFormEnd() {
|
||||
|
@ -584,7 +585,7 @@ class _HtmlTokenizer {
|
|||
this._requireCharCode($RBRACE);
|
||||
this._endToken([]);
|
||||
|
||||
this.inExpansionForm = false;
|
||||
this.expansionCaseStack.pop();
|
||||
}
|
||||
|
||||
private _consumeText() {
|
||||
|
@ -622,7 +623,9 @@ class _HtmlTokenizer {
|
|||
if (this.peek === $LT || this.peek === $EOF) return true;
|
||||
if (this.tokenizeExpansionForms) {
|
||||
if (isSpecialFormStart(this.peek, this.nextPeek)) return true;
|
||||
if (this.peek === $RBRACE && !interpolation && this.inExpansionForm) return true;
|
||||
if (this.peek === $RBRACE && !interpolation &&
|
||||
(this.isInExpansionCase() || this.isInExpansionForm()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -648,6 +651,18 @@ class _HtmlTokenizer {
|
|||
this.tokens = ListWrapper.slice(this.tokens, 0, nbTokens);
|
||||
}
|
||||
}
|
||||
|
||||
private isInExpansionCase(): boolean {
|
||||
return this.expansionCaseStack.length > 0 &&
|
||||
this.expansionCaseStack[this.expansionCaseStack.length - 1] ===
|
||||
HtmlTokenType.EXPANSION_CASE_EXP_START;
|
||||
}
|
||||
|
||||
private isInExpansionForm(): boolean {
|
||||
return this.expansionCaseStack.length > 0 &&
|
||||
this.expansionCaseStack[this.expansionCaseStack.length - 1] ===
|
||||
HtmlTokenType.EXPANSION_FORM_START;
|
||||
}
|
||||
}
|
||||
|
||||
function isNotWhitespace(code: number): boolean {
|
||||
|
|
|
@ -124,40 +124,9 @@ class TreeBuilder {
|
|||
|
||||
// read =
|
||||
while (this.peek.type === HtmlTokenType.EXPANSION_CASE_VALUE) {
|
||||
let value = this._advance();
|
||||
|
||||
// read {
|
||||
let exp = [];
|
||||
if (this.peek.type !== HtmlTokenType.EXPANSION_CASE_EXP_START) {
|
||||
this.errors.push(HtmlTreeError.create(null, this.peek.sourceSpan,
|
||||
`Invalid expansion form. Missing '{'.,`));
|
||||
return;
|
||||
}
|
||||
|
||||
// read until }
|
||||
let start = this._advance();
|
||||
while (this.peek.type !== HtmlTokenType.EXPANSION_CASE_EXP_END) {
|
||||
exp.push(this._advance());
|
||||
if (this.peek.type === HtmlTokenType.EOF) {
|
||||
this.errors.push(
|
||||
HtmlTreeError.create(null, start.sourceSpan, `Invalid expansion form. Missing '}'.`));
|
||||
return;
|
||||
}
|
||||
}
|
||||
let end = this._advance();
|
||||
exp.push(new HtmlToken(HtmlTokenType.EOF, [], end.sourceSpan));
|
||||
|
||||
// parse everything in between { and }
|
||||
let parsedExp = new TreeBuilder(exp).build();
|
||||
if (parsedExp.errors.length > 0) {
|
||||
this.errors = this.errors.concat(<HtmlTreeError[]>parsedExp.errors);
|
||||
return;
|
||||
}
|
||||
|
||||
let sourceSpan = new ParseSourceSpan(value.sourceSpan.start, end.sourceSpan.end);
|
||||
let expSourceSpan = new ParseSourceSpan(start.sourceSpan.start, end.sourceSpan.end);
|
||||
cases.push(new HtmlExpansionCaseAst(value.parts[0], parsedExp.rootNodes, sourceSpan,
|
||||
value.sourceSpan, expSourceSpan));
|
||||
let expCase = this._parseExpansionCase();
|
||||
if (isBlank(expCase)) return; // error
|
||||
cases.push(expCase);
|
||||
}
|
||||
|
||||
// read the final }
|
||||
|
@ -173,6 +142,80 @@ class TreeBuilder {
|
|||
mainSourceSpan, switchValue.sourceSpan));
|
||||
}
|
||||
|
||||
private _parseExpansionCase(): HtmlExpansionCaseAst {
|
||||
let value = this._advance();
|
||||
|
||||
// read {
|
||||
if (this.peek.type !== HtmlTokenType.EXPANSION_CASE_EXP_START) {
|
||||
this.errors.push(HtmlTreeError.create(null, this.peek.sourceSpan,
|
||||
`Invalid expansion form. Missing '{'.,`));
|
||||
return null;
|
||||
}
|
||||
|
||||
// read until }
|
||||
let start = this._advance();
|
||||
|
||||
let exp = this._collectExpansionExpTokens(start);
|
||||
if (isBlank(exp)) return null;
|
||||
|
||||
let end = this._advance();
|
||||
exp.push(new HtmlToken(HtmlTokenType.EOF, [], end.sourceSpan));
|
||||
|
||||
// parse everything in between { and }
|
||||
let 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);
|
||||
return new HtmlExpansionCaseAst(value.parts[0], parsedExp.rootNodes, sourceSpan,
|
||||
value.sourceSpan, expSourceSpan);
|
||||
}
|
||||
|
||||
private _collectExpansionExpTokens(start: HtmlToken): HtmlToken[] {
|
||||
let exp = [];
|
||||
let expansionFormStack = [HtmlTokenType.EXPANSION_CASE_EXP_START];
|
||||
|
||||
while (true) {
|
||||
if (this.peek.type === HtmlTokenType.EXPANSION_FORM_START ||
|
||||
this.peek.type === HtmlTokenType.EXPANSION_CASE_EXP_START) {
|
||||
expansionFormStack.push(this.peek.type);
|
||||
}
|
||||
|
||||
if (this.peek.type === HtmlTokenType.EXPANSION_CASE_EXP_END) {
|
||||
if (lastOnStack(expansionFormStack, HtmlTokenType.EXPANSION_CASE_EXP_START)) {
|
||||
expansionFormStack.pop();
|
||||
if (expansionFormStack.length == 0) return exp;
|
||||
|
||||
} else {
|
||||
this.errors.push(
|
||||
HtmlTreeError.create(null, start.sourceSpan, `Invalid expansion form. Missing '}'.`));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.peek.type === HtmlTokenType.EXPANSION_FORM_END) {
|
||||
if (lastOnStack(expansionFormStack, HtmlTokenType.EXPANSION_FORM_START)) {
|
||||
expansionFormStack.pop();
|
||||
} else {
|
||||
this.errors.push(
|
||||
HtmlTreeError.create(null, start.sourceSpan, `Invalid expansion form. Missing '}'.`));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.peek.type === HtmlTokenType.EOF) {
|
||||
this.errors.push(
|
||||
HtmlTreeError.create(null, start.sourceSpan, `Invalid expansion form. Missing '}'.`));
|
||||
return null;
|
||||
}
|
||||
|
||||
exp.push(this._advance());
|
||||
}
|
||||
}
|
||||
|
||||
private _consumeText(token: HtmlToken) {
|
||||
let text = token.parts[0];
|
||||
if (text.length > 0 && text[0] == '\n') {
|
||||
|
@ -321,3 +364,7 @@ function getElementFullName(prefix: string, localName: string,
|
|||
|
||||
return mergeNsAndName(prefix, localName);
|
||||
}
|
||||
|
||||
function lastOnStack(stack: any[], element: any): boolean {
|
||||
return stack.length > 0 && stack[stack.length - 1] === element;
|
||||
}
|
|
@ -254,6 +254,10 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
visitExpansion(ast: HtmlExpansionAst, context: any): any { return null; }
|
||||
|
||||
visitExpansionCase(ast: HtmlExpansionCaseAst, context: any): any { return null; }
|
||||
|
||||
visitText(ast: HtmlTextAst, parent: ElementContext): any {
|
||||
var ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR);
|
||||
var expr = this._parseInterpolation(ast.value, ast.sourceSpan);
|
||||
|
@ -270,7 +274,7 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
|||
|
||||
visitComment(ast: HtmlCommentAst, context: any): any { return null; }
|
||||
|
||||
visitElement(element: HtmlElementAst, component: ElementContext): any {
|
||||
visitElement(element: HtmlElementAst, parent: ElementContext): any {
|
||||
var nodeName = element.name;
|
||||
var preparsedElement = preparseElement(element);
|
||||
if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
||||
|
@ -773,7 +777,6 @@ class NonBindableVisitor implements HtmlAstVisitor {
|
|||
return new TextAst(ast.value, ngContentIndex, ast.sourceSpan);
|
||||
}
|
||||
visitExpansion(ast: HtmlExpansionAst, context: any): any { return ast; }
|
||||
|
||||
visitExpansionCase(ast: HtmlExpansionCaseAst, context: any): any { return ast; }
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
|
||||
|
||||
/**
|
||||
* Expands special forms into elements.
|
||||
*
|
||||
|
@ -35,7 +36,18 @@ import {BaseException} from 'angular2/src/facade/exceptions';
|
|||
* </ul>
|
||||
* ```
|
||||
*/
|
||||
export class Expander implements HtmlAstVisitor {
|
||||
export function expandNodes(nodes: HtmlAst[]): ExpansionResult {
|
||||
let e = new _Expander();
|
||||
let n = htmlVisitAll(e, nodes);
|
||||
return new ExpansionResult(n, e.expanded);
|
||||
}
|
||||
|
||||
export class ExpansionResult {
|
||||
constructor(public nodes: HtmlAst[], public expanded: boolean) {}
|
||||
}
|
||||
|
||||
class _Expander implements HtmlAstVisitor {
|
||||
expanded: boolean = false;
|
||||
constructor() {}
|
||||
|
||||
visitElement(ast: HtmlElementAst, context: any): any {
|
||||
|
@ -50,6 +62,7 @@ export class Expander implements HtmlAstVisitor {
|
|||
visitComment(ast: HtmlCommentAst, context: any): any { return ast; }
|
||||
|
||||
visitExpansion(ast: HtmlExpansionAst, context: any): any {
|
||||
this.expanded = true;
|
||||
return ast.type == "plural" ? _expandPluralForm(ast) : _expandDefaultForm(ast);
|
||||
}
|
||||
|
||||
|
@ -59,36 +72,44 @@ export class Expander implements HtmlAstVisitor {
|
|||
}
|
||||
|
||||
function _expandPluralForm(ast: HtmlExpansionAst): HtmlElementAst {
|
||||
let children = ast.cases.map(
|
||||
c => new HtmlElementAst(
|
||||
`template`,
|
||||
let children = ast.cases.map(c => {
|
||||
let expansionResult = expandNodes(c.expression);
|
||||
let i18nAttrs = expansionResult.expanded ?
|
||||
[] :
|
||||
[new HtmlAttrAst("i18n", `${ast.type}_${c.value}`, c.valueSourceSpan)];
|
||||
|
||||
return new HtmlElementAst(`template`,
|
||||
[
|
||||
new HtmlAttrAst("[ngPluralCase]", c.value, c.valueSourceSpan),
|
||||
new HtmlAttrAst("ngPluralCase", c.value, c.valueSourceSpan),
|
||||
],
|
||||
[
|
||||
new HtmlElementAst(
|
||||
`li`, [new HtmlAttrAst("i18n", `${ast.type}_${c.value}`, c.valueSourceSpan)],
|
||||
c.expression, c.sourceSpan, c.sourceSpan, c.sourceSpan)
|
||||
new HtmlElementAst(`li`, i18nAttrs, expansionResult.nodes,
|
||||
c.sourceSpan, c.sourceSpan, c.sourceSpan)
|
||||
],
|
||||
c.sourceSpan, c.sourceSpan, c.sourceSpan));
|
||||
c.sourceSpan, c.sourceSpan, c.sourceSpan);
|
||||
});
|
||||
let switchAttr = new HtmlAttrAst("[ngPlural]", ast.switchValue, ast.switchValueSourceSpan);
|
||||
return new HtmlElementAst("ul", [switchAttr], children, ast.sourceSpan, ast.sourceSpan,
|
||||
ast.sourceSpan);
|
||||
}
|
||||
|
||||
function _expandDefaultForm(ast: HtmlExpansionAst): HtmlElementAst {
|
||||
let children = ast.cases.map(
|
||||
c => new HtmlElementAst(
|
||||
`template`,
|
||||
let children = ast.cases.map(c => {
|
||||
let expansionResult = expandNodes(c.expression);
|
||||
let i18nAttrs = expansionResult.expanded ?
|
||||
[] :
|
||||
[new HtmlAttrAst("i18n", `${ast.type}_${c.value}`, c.valueSourceSpan)];
|
||||
|
||||
return new HtmlElementAst(`template`,
|
||||
[
|
||||
new HtmlAttrAst("[ngSwitchWhen]", c.value, c.valueSourceSpan),
|
||||
new HtmlAttrAst("ngSwitchWhen", c.value, c.valueSourceSpan),
|
||||
],
|
||||
[
|
||||
new HtmlElementAst(
|
||||
`li`, [new HtmlAttrAst("i18n", `${ast.type}_${c.value}`, c.valueSourceSpan)],
|
||||
c.expression, c.sourceSpan, c.sourceSpan, c.sourceSpan)
|
||||
new HtmlElementAst(`li`, i18nAttrs, expansionResult.nodes,
|
||||
c.sourceSpan, c.sourceSpan, c.sourceSpan)
|
||||
],
|
||||
c.sourceSpan, c.sourceSpan, c.sourceSpan));
|
||||
c.sourceSpan, c.sourceSpan, c.sourceSpan);
|
||||
});
|
||||
let switchAttr = new HtmlAttrAst("[ngSwitch]", ast.switchValue, ast.switchValueSourceSpan);
|
||||
return new HtmlElementAst("ul", [switchAttr], children, ast.sourceSpan, ast.sourceSpan,
|
||||
ast.sourceSpan);
|
||||
|
|
|
@ -16,7 +16,7 @@ import {RegExpWrapper, NumberWrapper, isPresent} from 'angular2/src/facade/lang'
|
|||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {Parser} from 'angular2/src/compiler/expression_parser/parser';
|
||||
import {Message, id} from './message';
|
||||
import {Expander} from './expander';
|
||||
import {expandNodes} from './expander';
|
||||
import {
|
||||
messageFromAttribute,
|
||||
I18nError,
|
||||
|
@ -126,21 +126,16 @@ export class I18nHtmlParser implements HtmlParser {
|
|||
parseExpansionForms: boolean = false): HtmlParseTreeResult {
|
||||
this.errors = [];
|
||||
|
||||
let res = this._htmlParser.parse(sourceContent, sourceUrl, parseExpansionForms);
|
||||
let res = this._htmlParser.parse(sourceContent, sourceUrl, true);
|
||||
if (res.errors.length > 0) {
|
||||
return res;
|
||||
} else {
|
||||
let nodes = this._recurse(this._expandNodes(res.rootNodes));
|
||||
let nodes = this._recurse(expandNodes(res.rootNodes).nodes);
|
||||
return this.errors.length > 0 ? new HtmlParseTreeResult([], this.errors) :
|
||||
new HtmlParseTreeResult(nodes, []);
|
||||
}
|
||||
}
|
||||
|
||||
private _expandNodes(nodes: HtmlAst[]): HtmlAst[] {
|
||||
let e = new Expander();
|
||||
return htmlVisitAll(e, nodes);
|
||||
}
|
||||
|
||||
private _processI18nPart(p: Part): HtmlAst[] {
|
||||
try {
|
||||
return p.hasI18n ? this._mergeI18Part(p) : this._recurseIntoI18nPart(p);
|
||||
|
@ -155,9 +150,11 @@ export class I18nHtmlParser implements HtmlParser {
|
|||
}
|
||||
|
||||
private _mergeI18Part(p: Part): HtmlAst[] {
|
||||
let messageId = id(p.createMessage(this._parser));
|
||||
let message = p.createMessage(this._parser);
|
||||
let messageId = id(message);
|
||||
if (!StringMapWrapper.contains(this._messages, messageId)) {
|
||||
throw new I18nError(p.sourceSpan, `Cannot find message for id '${messageId}'`);
|
||||
throw new I18nError(
|
||||
p.sourceSpan, `Cannot find message for id '${messageId}', content '${message.content}'.`);
|
||||
}
|
||||
|
||||
let parsedMessage = this._messages[messageId];
|
||||
|
@ -294,14 +291,17 @@ export class I18nHtmlParser implements HtmlParser {
|
|||
}
|
||||
|
||||
let i18n = i18ns[0];
|
||||
let messageId = id(messageFromAttribute(this._parser, el, i18n));
|
||||
let message = messageFromAttribute(this._parser, el, i18n);
|
||||
let messageId = id(message);
|
||||
|
||||
if (StringMapWrapper.contains(this._messages, messageId)) {
|
||||
let updatedMessage = this._replaceInterpolationInAttr(attr, this._messages[messageId]);
|
||||
res.push(new HtmlAttrAst(attr.name, updatedMessage, attr.sourceSpan));
|
||||
|
||||
} else {
|
||||
throw new I18nError(attr.sourceSpan, `Cannot find message for id '${messageId}'`);
|
||||
throw new I18nError(
|
||||
attr.sourceSpan,
|
||||
`Cannot find message for id '${messageId}', content '${message.content}'.`);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
|
|
|
@ -13,7 +13,7 @@ import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
|||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {Parser} from 'angular2/src/compiler/expression_parser/parser';
|
||||
import {Message, id} from './message';
|
||||
import {Expander} from './expander';
|
||||
import {expandNodes} from './expander';
|
||||
import {
|
||||
I18nError,
|
||||
Part,
|
||||
|
@ -126,16 +126,11 @@ export class MessageExtractor {
|
|||
if (res.errors.length > 0) {
|
||||
return new ExtractionResult([], res.errors);
|
||||
} else {
|
||||
this._recurse(this._expandNodes(res.rootNodes));
|
||||
this._recurse(expandNodes(res.rootNodes).nodes);
|
||||
return new ExtractionResult(this.messages, this.errors);
|
||||
}
|
||||
}
|
||||
|
||||
private _expandNodes(nodes: HtmlAst[]): HtmlAst[] {
|
||||
let e = new Expander();
|
||||
return htmlVisitAll(e, nodes);
|
||||
}
|
||||
|
||||
private _extractMessagesFromPart(p: Part): void {
|
||||
if (p.hasI18n) {
|
||||
this.messages.push(p.createMessage(this._parser));
|
||||
|
|
|
@ -646,6 +646,31 @@ export function main() {
|
|||
[HtmlTokenType.EOF]
|
||||
]);
|
||||
});
|
||||
|
||||
it("should parse nested expansion forms", () => {
|
||||
expect(tokenizeAndHumanizeParts(`{one.two, three, =4 { {xx, yy, =x {one}} }}`, true))
|
||||
.toEqual([
|
||||
[HtmlTokenType.EXPANSION_FORM_START],
|
||||
[HtmlTokenType.RAW_TEXT, 'one.two'],
|
||||
[HtmlTokenType.RAW_TEXT, 'three'],
|
||||
[HtmlTokenType.EXPANSION_CASE_VALUE, '4'],
|
||||
[HtmlTokenType.EXPANSION_CASE_EXP_START],
|
||||
|
||||
[HtmlTokenType.EXPANSION_FORM_START],
|
||||
[HtmlTokenType.RAW_TEXT, 'xx'],
|
||||
[HtmlTokenType.RAW_TEXT, 'yy'],
|
||||
[HtmlTokenType.EXPANSION_CASE_VALUE, 'x'],
|
||||
[HtmlTokenType.EXPANSION_CASE_EXP_START],
|
||||
[HtmlTokenType.TEXT, 'one'],
|
||||
[HtmlTokenType.EXPANSION_CASE_EXP_END],
|
||||
[HtmlTokenType.EXPANSION_FORM_END],
|
||||
[HtmlTokenType.TEXT, ' '],
|
||||
|
||||
[HtmlTokenType.EXPANSION_CASE_EXP_END],
|
||||
[HtmlTokenType.EXPANSION_FORM_END],
|
||||
[HtmlTokenType.EOF]
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
|
|
|
@ -271,6 +271,27 @@ export function main() {
|
|||
.toEqual([[HtmlTextAst, 'One {{message}}', 0]]);
|
||||
});
|
||||
|
||||
it("should parse out nested expansion forms", () => {
|
||||
let parsed = parser.parse(`{messages.length, plural, =0 { {p.gender, gender, =m {m}} }}`,
|
||||
'TestComp', true);
|
||||
|
||||
|
||||
expect(humanizeDom(parsed))
|
||||
.toEqual([
|
||||
[HtmlExpansionAst, 'messages.length', 'plural'],
|
||||
[HtmlExpansionCaseAst, '0'],
|
||||
]);
|
||||
|
||||
let firstCase = (<any>parsed.rootNodes[0]).cases[0];
|
||||
|
||||
expect(humanizeDom(new HtmlParseTreeResult(firstCase.expression, [])))
|
||||
.toEqual([
|
||||
[HtmlExpansionAst, 'p.gender', 'gender'],
|
||||
[HtmlExpansionCaseAst, 'm'],
|
||||
[HtmlTextAst, ' ', 0],
|
||||
]);
|
||||
});
|
||||
|
||||
it("should error when expansion form is not closed", () => {
|
||||
let p = parser.parse(`{messages.length, plural, =0 {one}`, 'TestComp', true);
|
||||
expect(humanizeErrors(p.errors))
|
||||
|
|
|
@ -188,7 +188,7 @@ export function main() {
|
|||
expect(res[1].sourceSpan.start.offset).toEqual(10);
|
||||
});
|
||||
|
||||
it("should handle the plural special form", () => {
|
||||
it("should handle the plural expansion form", () => {
|
||||
let translations: {[key: string]: string} = {};
|
||||
translations[id(new Message('zero<ph name="e1">bold</ph>', "plural_0", null))] =
|
||||
'ZERO<ph name="e1">BOLD</ph>';
|
||||
|
@ -200,7 +200,7 @@ export function main() {
|
|||
[HtmlElementAst, 'ul', 0],
|
||||
[HtmlAttrAst, '[ngPlural]', 'messages.length'],
|
||||
[HtmlElementAst, 'template', 1],
|
||||
[HtmlAttrAst, '[ngPluralCase]', '0'],
|
||||
[HtmlAttrAst, 'ngPluralCase', '0'],
|
||||
[HtmlElementAst, 'li', 2],
|
||||
[HtmlTextAst, 'ZERO', 3],
|
||||
[HtmlElementAst, 'b', 3],
|
||||
|
@ -208,6 +208,31 @@ export function main() {
|
|||
]);
|
||||
});
|
||||
|
||||
it("should handle nested expansion forms", () => {
|
||||
let translations: {[key: string]: string} = {};
|
||||
translations[id(new Message('m', "gender_m", null))] = 'M';
|
||||
|
||||
let res = parse(`{messages.length, plural, =0 { {p.gender, gender, =m {m}} }}`, translations);
|
||||
|
||||
expect(humanizeDom(res))
|
||||
.toEqual([
|
||||
[HtmlElementAst, 'ul', 0],
|
||||
[HtmlAttrAst, '[ngPlural]', 'messages.length'],
|
||||
[HtmlElementAst, 'template', 1],
|
||||
[HtmlAttrAst, 'ngPluralCase', '0'],
|
||||
[HtmlElementAst, 'li', 2],
|
||||
|
||||
[HtmlElementAst, 'ul', 3],
|
||||
[HtmlAttrAst, '[ngSwitch]', 'p.gender'],
|
||||
[HtmlElementAst, 'template', 4],
|
||||
[HtmlAttrAst, 'ngSwitchWhen', 'm'],
|
||||
[HtmlElementAst, 'li', 5],
|
||||
[HtmlTextAst, 'M', 6],
|
||||
|
||||
[HtmlTextAst, ' ', 3]
|
||||
]);
|
||||
});
|
||||
|
||||
it("should correctly set source code positions", () => {
|
||||
let translations: {[key: string]: string} = {};
|
||||
translations[id(new Message('<ph name="e0">bold</ph>', "plural_0", null))] =
|
||||
|
@ -258,7 +283,7 @@ export function main() {
|
|||
[HtmlElementAst, 'ul', 0],
|
||||
[HtmlAttrAst, '[ngSwitch]', 'person.gender'],
|
||||
[HtmlElementAst, 'template', 1],
|
||||
[HtmlAttrAst, '[ngSwitchWhen]', 'male'],
|
||||
[HtmlAttrAst, 'ngSwitchWhen', 'male'],
|
||||
[HtmlElementAst, 'li', 2],
|
||||
[HtmlTextAst, 'M', 3],
|
||||
]);
|
||||
|
@ -273,13 +298,13 @@ export function main() {
|
|||
it("should error when no matching message (attr)", () => {
|
||||
let mid = id(new Message("some message", null, null));
|
||||
expect(humanizeErrors(parse("<div value='some message' i18n-value></div>", {}).errors))
|
||||
.toEqual([`Cannot find message for id '${mid}'`]);
|
||||
.toEqual([`Cannot find message for id '${mid}', content 'some message'.`]);
|
||||
});
|
||||
|
||||
it("should error when no matching message (text)", () => {
|
||||
let mid = id(new Message("some message", null, null));
|
||||
expect(humanizeErrors(parse("<div i18n>some message</div>", {}).errors))
|
||||
.toEqual([`Cannot find message for id '${mid}'`]);
|
||||
.toEqual([`Cannot find message for id '${mid}', content 'some message'.`]);
|
||||
});
|
||||
|
||||
it("should error when a non-placeholder element appears in translation", () => {
|
||||
|
|
Loading…
Reference in New Issue