diff --git a/modules/@angular/compiler/src/html_lexer.ts b/modules/@angular/compiler/src/html_lexer.ts
index 978bbee545..7ceca3712c 100644
--- a/modules/@angular/compiler/src/html_lexer.ts
+++ b/modules/@angular/compiler/src/html_lexer.ts
@@ -164,12 +164,12 @@ class _HtmlTokenizer {
this.tokenizeExpansionForms) {
this._consumeExpansionCaseStart();
- } else if (this._peek === $RBRACE && this._isInExpansionCase() &&
- this.tokenizeExpansionForms) {
+ } else if (
+ this._peek === $RBRACE && this._isInExpansionCase() && this.tokenizeExpansionForms) {
this._consumeExpansionCaseEnd();
- } else if (this._peek === $RBRACE && this._isInExpansionForm() &&
- this.tokenizeExpansionForms) {
+ } else if (
+ this._peek === $RBRACE && this._isInExpansionForm() && this.tokenizeExpansionForms) {
this._consumeExpansionFormEnd();
} else {
@@ -214,8 +214,8 @@ class _HtmlTokenizer {
if (isBlank(end)) {
end = this._getLocation();
}
- var token = new HtmlToken(this._currentTokenType, parts,
- new ParseSourceSpan(this._currentTokenStart, end));
+ var token = new HtmlToken(
+ this._currentTokenType, parts, new ParseSourceSpan(this._currentTokenStart, end));
this.tokens.push(token);
this._currentTokenStart = null;
this._currentTokenType = null;
@@ -240,9 +240,11 @@ class _HtmlTokenizer {
this._column++;
}
this._index++;
- this._peek = this._index >= this._length ? $EOF : StringWrapper.charCodeAt(this._input, this._index);
- this._nextPeek =
- this._index + 1 >= this._length ? $EOF : StringWrapper.charCodeAt(this._input, this._index + 1);
+ this._peek =
+ this._index >= this._length ? $EOF : StringWrapper.charCodeAt(this._input, this._index);
+ this._nextPeek = this._index + 1 >= this._length ?
+ $EOF :
+ StringWrapper.charCodeAt(this._input, this._index + 1);
}
private _attemptCharCode(charCode: number): boolean {
@@ -264,8 +266,8 @@ class _HtmlTokenizer {
private _requireCharCode(charCode: number) {
var location = this._getLocation();
if (!this._attemptCharCode(charCode)) {
- throw this._createError(unexpectedCharacterErrorMsg(this._peek),
- this._getSpan(location, location));
+ throw this._createError(
+ unexpectedCharacterErrorMsg(this._peek), this._getSpan(location, location));
}
}
@@ -656,14 +658,14 @@ class _HtmlTokenizer {
private _isInExpansionCase(): boolean {
return this._expansionCaseStack.length > 0 &&
- this._expansionCaseStack[this._expansionCaseStack.length - 1] ===
- HtmlTokenType.EXPANSION_CASE_EXP_START;
+ 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;
+ this._expansionCaseStack[this._expansionCaseStack.length - 1] ===
+ HtmlTokenType.EXPANSION_FORM_START;
}
}
diff --git a/modules/@angular/compiler/src/i18n/expander.ts b/modules/@angular/compiler/src/i18n/expander.ts
index ed28256ef4..612da747f2 100644
--- a/modules/@angular/compiler/src/i18n/expander.ts
+++ b/modules/@angular/compiler/src/i18n/expander.ts
@@ -1,7 +1,10 @@
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';
-
+// http://cldr.unicode.org/index/cldr-spec/plural-rules
+const PLURAL_CASES: string[] = ['zero', 'one', 'two', 'few', 'many', 'other'];
/**
* Expands special forms into elements.
@@ -20,25 +23,25 @@ import {HtmlAst, HtmlAstVisitor, HtmlAttrAst, HtmlCommentAst, HtmlElementAst, Ht
*
* ```
*
- * - zero
- * - one
- * - more than one
+ * - zero
+ * - one
+ * - more than one
*
* ```
*/
export function expandNodes(nodes: HtmlAst[]): ExpansionResult {
let e = new _Expander();
let n = htmlVisitAll(e, nodes);
- return new ExpansionResult(n, e.expanded);
+ return new ExpansionResult(n, e.expanded, e.errors);
}
export class ExpansionResult {
- constructor(public nodes: HtmlAst[], public expanded: boolean) {}
+ constructor(public nodes: HtmlAst[], public expanded: boolean, public errors: ParseError[]) {}
}
class _Expander implements HtmlAstVisitor {
expanded: boolean = false;
- constructor() {}
+ errors: ParseError[] = [];
visitElement(ast: HtmlElementAst, context: any): any {
return new HtmlElementAst(
@@ -54,7 +57,7 @@ class _Expander implements HtmlAstVisitor {
visitExpansion(ast: HtmlExpansionAst, context: any): any {
this.expanded = true;
- return ast.type == 'plural' ? _expandPluralForm(ast) : _expandDefaultForm(ast);
+ return ast.type == 'plural' ? _expandPluralForm(ast, this.errors) : _expandDefaultForm(ast);
}
visitExpansionCase(ast: HtmlExpansionCaseAst, context: any): any {
@@ -62,9 +65,15 @@ class _Expander implements HtmlAstVisitor {
}
}
-function _expandPluralForm(ast: HtmlExpansionAst): HtmlElementAst {
+function _expandPluralForm(ast: HtmlExpansionAst, errors: ParseError[]): HtmlElementAst {
let children = ast.cases.map(c => {
+ if (PLURAL_CASES.indexOf(c.value) == -1 && !c.value.match(/^=\d+$/)) {
+ errors.push(new I18nError(
+ c.valueSourceSpan,
+ `Plural cases should be "=" or one of ${PLURAL_CASES.join(", ")}`));
+ }
let expansionResult = expandNodes(c.expression);
+ expansionResult.errors.forEach(e => errors.push(e));
let i18nAttrs = expansionResult.expanded ?
[] :
[new HtmlAttrAst('i18n', `${ast.type}_${c.value}`, c.valueSourceSpan)];
diff --git a/modules/@angular/compiler/src/i18n/i18n_html_parser.ts b/modules/@angular/compiler/src/i18n/i18n_html_parser.ts
index 1fe5df99e2..7091c42079 100644
--- a/modules/@angular/compiler/src/i18n/i18n_html_parser.ts
+++ b/modules/@angular/compiler/src/i18n/i18n_html_parser.ts
@@ -111,7 +111,10 @@ export class I18nHtmlParser implements HtmlParser {
if (res.errors.length > 0) {
return res;
} else {
- let nodes = this._recurse(expandNodes(res.rootNodes).nodes);
+ let expanded = expandNodes(res.rootNodes);
+ let nodes = this._recurse(expanded.nodes);
+ this.errors = this.errors.concat(expanded.errors);
+
return this.errors.length > 0 ? new HtmlParseTreeResult([], this.errors) :
new HtmlParseTreeResult(nodes, []);
}
diff --git a/modules/@angular/compiler/src/i18n/message_extractor.ts b/modules/@angular/compiler/src/i18n/message_extractor.ts
index 4ef2aeff44..ad2559cbe1 100644
--- a/modules/@angular/compiler/src/i18n/message_extractor.ts
+++ b/modules/@angular/compiler/src/i18n/message_extractor.ts
@@ -19,14 +19,6 @@ export class ExtractionResult {
/**
* Removes duplicate messages.
- *
- * E.g.
- *
- * ```
- * var m = [new Message("message", "meaning", "desc1"), new Message("message", "meaning",
- * "desc2")];
- * expect(removeDuplicates(m)).toEqual([new Message("message", "meaning", "desc1")]);
- * ```
*/
export function removeDuplicates(messages: Message[]): Message[] {
let uniq: {[key: string]: Message} = {};
@@ -113,8 +105,9 @@ export class MessageExtractor {
if (res.errors.length > 0) {
return new ExtractionResult([], res.errors);
} else {
- this._recurse(expandNodes(res.rootNodes).nodes);
- return new ExtractionResult(this.messages, this.errors);
+ let expanded = expandNodes(res.rootNodes);
+ this._recurse(expanded.nodes);
+ return new ExtractionResult(this.messages, this.errors.concat(expanded.errors));
}
}
diff --git a/modules/@angular/compiler/test/html_lexer_spec.ts b/modules/@angular/compiler/test/html_lexer_spec.ts
index b7f5fa0b84..b8acecb93f 100644
--- a/modules/@angular/compiler/test/html_lexer_spec.ts
+++ b/modules/@angular/compiler/test/html_lexer_spec.ts
@@ -485,98 +485,65 @@ export function main() {
});
- describe("expansion forms", () => {
- it("should parse an expansion form", () => {
+ describe('expansion forms', () => {
+ it('should parse an expansion form', () => {
expect(tokenizeAndHumanizeParts('{one.two, three, =4 {four} =5 {five} foo {bar} }', 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.TEXT, 'four'],
- [HtmlTokenType.EXPANSION_CASE_EXP_END],
- [HtmlTokenType.EXPANSION_CASE_VALUE, '=5'],
- [HtmlTokenType.EXPANSION_CASE_EXP_START],
- [HtmlTokenType.TEXT, 'five'],
- [HtmlTokenType.EXPANSION_CASE_EXP_END],
- [HtmlTokenType.EXPANSION_CASE_VALUE, 'foo'],
- [HtmlTokenType.EXPANSION_CASE_EXP_START],
- [HtmlTokenType.TEXT, 'bar'],
- [HtmlTokenType.EXPANSION_CASE_EXP_END],
- [HtmlTokenType.EXPANSION_FORM_END],
+ [HtmlTokenType.EXPANSION_FORM_START], [HtmlTokenType.RAW_TEXT, 'one.two'],
+ [HtmlTokenType.RAW_TEXT, 'three'], [HtmlTokenType.EXPANSION_CASE_VALUE, '=4'],
+ [HtmlTokenType.EXPANSION_CASE_EXP_START], [HtmlTokenType.TEXT, 'four'],
+ [HtmlTokenType.EXPANSION_CASE_EXP_END], [HtmlTokenType.EXPANSION_CASE_VALUE, '=5'],
+ [HtmlTokenType.EXPANSION_CASE_EXP_START], [HtmlTokenType.TEXT, 'five'],
+ [HtmlTokenType.EXPANSION_CASE_EXP_END], [HtmlTokenType.EXPANSION_CASE_VALUE, 'foo'],
+ [HtmlTokenType.EXPANSION_CASE_EXP_START], [HtmlTokenType.TEXT, 'bar'],
+ [HtmlTokenType.EXPANSION_CASE_EXP_END], [HtmlTokenType.EXPANSION_FORM_END],
[HtmlTokenType.EOF]
]);
});
- it("should parse an expansion form with text elements surrounding it", () => {
- expect(tokenizeAndHumanizeParts('before{one.two, three, =4 {four}}after', true))
- .toEqual([
- [HtmlTokenType.TEXT, "before"],
- [HtmlTokenType.EXPANSION_FORM_START],
- [HtmlTokenType.RAW_TEXT, 'one.two'],
- [HtmlTokenType.RAW_TEXT, 'three'],
- [HtmlTokenType.EXPANSION_CASE_VALUE, '=4'],
- [HtmlTokenType.EXPANSION_CASE_EXP_START],
- [HtmlTokenType.TEXT, 'four'],
- [HtmlTokenType.EXPANSION_CASE_EXP_END],
- [HtmlTokenType.EXPANSION_FORM_END],
- [HtmlTokenType.TEXT, "after"],
- [HtmlTokenType.EOF]
- ]);
+ it('should parse an expansion form with text elements surrounding it', () => {
+ expect(tokenizeAndHumanizeParts('before{one.two, three, =4 {four}}after', true)).toEqual([
+ [HtmlTokenType.TEXT, 'before'], [HtmlTokenType.EXPANSION_FORM_START],
+ [HtmlTokenType.RAW_TEXT, 'one.two'], [HtmlTokenType.RAW_TEXT, 'three'],
+ [HtmlTokenType.EXPANSION_CASE_VALUE, '=4'], [HtmlTokenType.EXPANSION_CASE_EXP_START],
+ [HtmlTokenType.TEXT, 'four'], [HtmlTokenType.EXPANSION_CASE_EXP_END],
+ [HtmlTokenType.EXPANSION_FORM_END], [HtmlTokenType.TEXT, 'after'], [HtmlTokenType.EOF]
+ ]);
});
- it("should parse an expansion forms with elements in it", () => {
- expect(tokenizeAndHumanizeParts('{one.two, three, =4 {four a}}', 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.TEXT, 'four '],
- [HtmlTokenType.TAG_OPEN_START, null, 'b'],
- [HtmlTokenType.TAG_OPEN_END],
- [HtmlTokenType.TEXT, 'a'],
- [HtmlTokenType.TAG_CLOSE, null, 'b'],
- [HtmlTokenType.EXPANSION_CASE_EXP_END],
- [HtmlTokenType.EXPANSION_FORM_END],
- [HtmlTokenType.EOF]
- ]);
+ it('should parse an expansion forms with elements in it', () => {
+ expect(tokenizeAndHumanizeParts('{one.two, three, =4 {four a}}', 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.TEXT, 'four '],
+ [HtmlTokenType.TAG_OPEN_START, null, 'b'], [HtmlTokenType.TAG_OPEN_END],
+ [HtmlTokenType.TEXT, 'a'], [HtmlTokenType.TAG_CLOSE, null, 'b'],
+ [HtmlTokenType.EXPANSION_CASE_EXP_END], [HtmlTokenType.EXPANSION_FORM_END],
+ [HtmlTokenType.EOF]
+ ]);
});
- it("should parse an expansion forms with interpolation in it", () => {
- expect(tokenizeAndHumanizeParts('{one.two, three, =4 {four {{a}}}}', 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.TEXT, 'four {{a}}'],
- [HtmlTokenType.EXPANSION_CASE_EXP_END],
- [HtmlTokenType.EXPANSION_FORM_END],
- [HtmlTokenType.EOF]
- ]);
+ it('should parse an expansion forms with interpolation in it', () => {
+ expect(tokenizeAndHumanizeParts('{one.two, three, =4 {four {{a}}}}', 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.TEXT, 'four {{a}}'],
+ [HtmlTokenType.EXPANSION_CASE_EXP_END], [HtmlTokenType.EXPANSION_FORM_END],
+ [HtmlTokenType.EOF]
+ ]);
});
- it("should parse nested expansion forms", () => {
+ 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_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.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],
diff --git a/modules/@angular/compiler/test/html_parser_spec.ts b/modules/@angular/compiler/test/html_parser_spec.ts
index 630c0725c9..9ee67ef0bd 100644
--- a/modules/@angular/compiler/test/html_parser_spec.ts
+++ b/modules/@angular/compiler/test/html_parser_spec.ts
@@ -1,15 +1,8 @@
-import {HtmlAst, HtmlAstVisitor, HtmlAttrAst, HtmlCommentAst, HtmlElementAst, HtmlExpansionAst, HtmlExpansionCaseAst, HtmlTextAst, htmlVisitAll} from '@angular/compiler/src/html_ast';
+import {HtmlAttrAst, HtmlCommentAst, HtmlElementAst, HtmlExpansionAst, HtmlExpansionCaseAst, HtmlTextAst} from '@angular/compiler/src/html_ast';
import {HtmlTokenType} from '@angular/compiler/src/html_lexer';
-import {HtmlParser, HtmlParseTreeResult, HtmlTreeError} from '@angular/compiler/src/html_parser';
-import {
- HtmlElementAst,
- HtmlAttrAst,
- HtmlTextAst,
- HtmlCommentAst,
- HtmlExpansionAst,
- HtmlExpansionCaseAst
-} from '@angular/compiler/src/html_ast';
+import {HtmlParseTreeResult, HtmlParser, HtmlTreeError} from '@angular/compiler/src/html_parser';
import {ParseError} from '@angular/compiler/src/parse_util';
+
import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './html_ast_spec_utils';
export function main() {
@@ -238,15 +231,14 @@ export function main() {
`before{messages.length, plural, =0 {You have no messages} =1 {One {{message}}}}after
`,
'TestComp', true);
- expect(humanizeDom(parsed))
- .toEqual([
- [HtmlElementAst, 'div', 0],
- [HtmlTextAst, 'before', 1],
- [HtmlExpansionAst, 'messages.length', 'plural'],
- [HtmlExpansionCaseAst, '=0'],
- [HtmlExpansionCaseAst, '=1'],
- [HtmlTextAst, 'after', 1],
- ]);
+ expect(humanizeDom(parsed)).toEqual([
+ [HtmlElementAst, 'div', 0],
+ [HtmlTextAst, 'before', 1],
+ [HtmlExpansionAst, 'messages.length', 'plural'],
+ [HtmlExpansionCaseAst, '=0'],
+ [HtmlExpansionCaseAst, '=1'],
+ [HtmlTextAst, 'after', 1],
+ ]);
let cases = (parsed.rootNodes[0]).children[1].cases;
expect(humanizeDom(new HtmlParseTreeResult(cases[0].expression, []))).toEqual([
@@ -263,20 +255,18 @@ export function main() {
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'],
- ]);
+ expect(humanizeDom(parsed)).toEqual([
+ [HtmlExpansionAst, 'messages.length', 'plural'],
+ [HtmlExpansionCaseAst, '=0'],
+ ]);
let firstCase = (parsed.rootNodes[0]).cases[0];
- expect(humanizeDom(new HtmlParseTreeResult(firstCase.expression, [])))
- .toEqual([
- [HtmlExpansionAst, 'p.gender', 'gender'],
- [HtmlExpansionCaseAst, '=m'],
- [HtmlTextAst, ' ', 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', () => {
diff --git a/modules/@angular/compiler/test/i18n/i18n_html_parser_spec.ts b/modules/@angular/compiler/test/i18n/i18n_html_parser_spec.ts
index e1c2bf0fb1..1a265eeb8d 100644
--- a/modules/@angular/compiler/test/i18n/i18n_html_parser_spec.ts
+++ b/modules/@angular/compiler/test/i18n/i18n_html_parser_spec.ts
@@ -21,8 +21,7 @@ export function main() {
let msgs = '';
StringMapWrapper.forEach(
- messages, (v: any /** TODO #9100 */, k: any /** TODO #9100 */) => msgs +=
- `${v}`);
+ messages, (v: string, k: string) => msgs += `${v}`);
let res = deserializeXmb(`${msgs}`, 'someUrl');
return new I18nHtmlParser(
@@ -175,52 +174,45 @@ export function main() {
it('should handle the plural expansion form', () => {
let translations: {[key: string]: string} = {};
- translations[id(new Message('zerobold', "plural_=0", null))] =
+ translations[id(new Message('zerobold', 'plural_=0', null))] =
'ZEROBOLD';
let res = parse(`{messages.length, plural,=0 {zerobold}}`, translations);
- expect(humanizeDom(res))
- .toEqual([
- [HtmlElementAst, 'ul', 0],
- [HtmlAttrAst, '[ngPlural]', 'messages.length'],
- [HtmlElementAst, 'template', 1],
- [HtmlAttrAst, 'ngPluralCase', '=0'],
- [HtmlElementAst, 'li', 2],
- [HtmlTextAst, 'ZERO', 3],
- [HtmlElementAst, 'b', 3],
- [HtmlTextAst, 'BOLD', 4],
- ]);
+ expect(humanizeDom(res)).toEqual([
+ [HtmlElementAst, 'ul', 0],
+ [HtmlAttrAst, '[ngPlural]', 'messages.length'],
+ [HtmlElementAst, 'template', 1],
+ [HtmlAttrAst, 'ngPluralCase', '=0'],
+ [HtmlElementAst, 'li', 2],
+ [HtmlTextAst, 'ZERO', 3],
+ [HtmlElementAst, 'b', 3],
+ [HtmlTextAst, 'BOLD', 4],
+ ]);
});
it('should handle nested expansion forms', () => {
let translations: {[key: string]: string} = {};
- translations[id(new Message('m', "gender_=m", null))] = 'M';
+ 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],
+ 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],
+ [HtmlElementAst, 'ul', 3], [HtmlAttrAst, '[ngSwitch]', 'p.gender'],
+ [HtmlElementAst, 'template', 4], [HtmlAttrAst, 'ngSwitchWhen', '=m'],
+ [HtmlElementAst, 'li', 5], [HtmlTextAst, 'M', 6],
- [HtmlTextAst, ' ', 3]
- ]);
+ [HtmlTextAst, ' ', 3]
+ ]);
});
it('should correctly set source code positions', () => {
let translations: {[key: string]: string} = {};
- translations[id(new Message('bold', "plural_=0", null))] =
+ translations[id(new Message('bold', 'plural_=0', null))] =
'BOLD';
let nodes = parse(`{messages.length, plural,=0 {bold}}`, translations).rootNodes;
@@ -259,19 +251,18 @@ export function main() {
it('should handle other special forms', () => {
let translations: {[key: string]: string} = {};
- translations[id(new Message('m', "gender_=male", null))] = 'M';
+ translations[id(new Message('m', 'gender_=male', null))] = 'M';
let res = parse(`{person.gender, gender,=male {m}}`, translations);
- expect(humanizeDom(res))
- .toEqual([
- [HtmlElementAst, 'ul', 0],
- [HtmlAttrAst, '[ngSwitch]', 'person.gender'],
- [HtmlElementAst, 'template', 1],
- [HtmlAttrAst, 'ngSwitchWhen', '=male'],
- [HtmlElementAst, 'li', 2],
- [HtmlTextAst, 'M', 3],
- ]);
+ expect(humanizeDom(res)).toEqual([
+ [HtmlElementAst, 'ul', 0],
+ [HtmlAttrAst, '[ngSwitch]', 'person.gender'],
+ [HtmlElementAst, 'template', 1],
+ [HtmlAttrAst, 'ngSwitchWhen', '=male'],
+ [HtmlElementAst, 'li', 2],
+ [HtmlTextAst, 'M', 3],
+ ]);
});
describe('errors', () => {
@@ -321,45 +312,51 @@ export function main() {
.toEqual(['Invalid interpolation name \'99\'']);
});
- describe('implicit translation', () => {
- it('should support attributes', () => {
- let translations: {[key: string]: string} = {};
- translations[id(new Message('some message', null, null))] = 'another message';
+ it('should error on unknown plural cases', () => {
+ let mid = id(new Message('-', 'plural_unknown', null));
+ expect(humanizeErrors(parse('{n, plural, unknown {-}}', {mid: ''}).errors)).toEqual([
+ `Cannot find message for id '${mid}', content '-'.`,
+ `Plural cases should be "=" or one of zero, one, two, few, many, other`,
+ ]);
+ });
+ });
- expect(humanizeDom(parse('', translations, [], {
- 'i18n-el': ['value']
- }))).toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlAttrAst, 'value', 'another message']]);
- });
+ describe('implicit translation', () => {
+ it('should support attributes', () => {
+ let translations: {[key: string]: string} = {};
+ translations[id(new Message('some message', null, null))] = 'another message';
- it('should support attributes with meaning and description', () => {
- let translations: {[key: string]: string} = {};
- translations[id(new Message('some message', 'meaning', 'description'))] =
- 'another message';
+ expect(humanizeDom(parse('', translations, [], {
+ 'i18n-el': ['value']
+ }))).toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlAttrAst, 'value', 'another message']]);
+ });
- expect(
- humanizeDom(parse(
- '',
- translations, [], {'i18n-el': ['value']})))
- .toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlAttrAst, 'value', 'another message']]);
- });
+ it('should support attributes with meaning and description', () => {
+ let translations: {[key: string]: string} = {};
+ translations[id(new Message('some message', 'meaning', 'description'))] = 'another message';
- it('should support elements', () => {
- let translations: {[key: string]: string} = {};
- translations[id(new Message('message', null, null))] = 'another message';
+ expect(humanizeDom(parse(
+ '',
+ translations, [], {'i18n-el': ['value']})))
+ .toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlAttrAst, 'value', 'another message']]);
+ });
- expect(humanizeDom(parse('message', translations, ['i18n-el'])))
- .toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlTextAst, 'another message', 1]]);
- });
+ it('should support elements', () => {
+ let translations: {[key: string]: string} = {};
+ translations[id(new Message('message', null, null))] = 'another message';
- it('should support elements with meaning and description', () => {
- let translations: {[key: string]: string} = {};
- translations[id(new Message('message', 'meaning', 'description'))] = 'another message';
+ expect(humanizeDom(parse('message', translations, ['i18n-el'])))
+ .toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlTextAst, 'another message', 1]]);
+ });
- expect(humanizeDom(parse(
- 'message', translations,
- ['i18n-el'])))
- .toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlTextAst, 'another message', 1]]);
- });
+ it('should support elements with meaning and description', () => {
+ let translations: {[key: string]: string} = {};
+ translations[id(new Message('message', 'meaning', 'description'))] = 'another message';
+
+ expect(humanizeDom(parse(
+ 'message', translations,
+ ['i18n-el'])))
+ .toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlTextAst, 'another message', 1]]);
});
});
});
diff --git a/modules/@angular/compiler/test/i18n/message_extractor_spec.ts b/modules/@angular/compiler/test/i18n/message_extractor_spec.ts
index 4b0cbb45d8..438a5b0249 100644
--- a/modules/@angular/compiler/test/i18n/message_extractor_spec.ts
+++ b/modules/@angular/compiler/test/i18n/message_extractor_spec.ts
@@ -151,8 +151,9 @@ export function main() {
]);
});
- it("should extract messages from expansion forms", () => {
- let res = extractor.extract(`
+ it('should extract messages from expansion forms', () => {
+ let res = extractor.extract(
+ `
{messages.length, plural,
=0 {You have no messages}
@@ -160,14 +161,13 @@ export function main() {
other {You have messages}
}
`,
- "someurl");
+ 'someurl');
- expect(res.messages)
- .toEqual([
- new Message('You have no messages', "plural_=0", null),
- new Message('You have one message', "plural_=1", null),
- new Message('You have messages', "plural_other", null),
- ]);
+ expect(res.messages).toEqual([
+ new Message('You have no messages', 'plural_=0', null),
+ new Message('You have one message', 'plural_=1', null),
+ new Message('You have messages', 'plural_other', null),
+ ]);
});
it('should remove duplicate messages', () => {
@@ -235,6 +235,14 @@ export function main() {
expect(res.errors.length).toEqual(1);
expect(res.errors[0].msg).toEqual('Unexpected character "s"');
});
+
+ it('should return parse errors on unknown plural cases', () => {
+ let res = extractor.extract('{n, plural, unknown {-}}', 'someUrl');
+ expect(res.errors.length).toEqual(1);
+ expect(res.errors[0].msg)
+ .toEqual(
+ 'Plural cases should be "=" or one of zero, one, two, few, many, other');
+ });
});
});
}