parent
43148d8233
commit
5267115481
|
@ -164,12 +164,12 @@ class _HtmlTokenizer {
|
||||||
this.tokenizeExpansionForms) {
|
this.tokenizeExpansionForms) {
|
||||||
this._consumeExpansionCaseStart();
|
this._consumeExpansionCaseStart();
|
||||||
|
|
||||||
} else if (this._peek === $RBRACE && this._isInExpansionCase() &&
|
} else if (
|
||||||
this.tokenizeExpansionForms) {
|
this._peek === $RBRACE && this._isInExpansionCase() && this.tokenizeExpansionForms) {
|
||||||
this._consumeExpansionCaseEnd();
|
this._consumeExpansionCaseEnd();
|
||||||
|
|
||||||
} else if (this._peek === $RBRACE && this._isInExpansionForm() &&
|
} else if (
|
||||||
this.tokenizeExpansionForms) {
|
this._peek === $RBRACE && this._isInExpansionForm() && this.tokenizeExpansionForms) {
|
||||||
this._consumeExpansionFormEnd();
|
this._consumeExpansionFormEnd();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -214,8 +214,8 @@ class _HtmlTokenizer {
|
||||||
if (isBlank(end)) {
|
if (isBlank(end)) {
|
||||||
end = this._getLocation();
|
end = this._getLocation();
|
||||||
}
|
}
|
||||||
var token = new HtmlToken(this._currentTokenType, parts,
|
var token = new HtmlToken(
|
||||||
new ParseSourceSpan(this._currentTokenStart, end));
|
this._currentTokenType, parts, new ParseSourceSpan(this._currentTokenStart, end));
|
||||||
this.tokens.push(token);
|
this.tokens.push(token);
|
||||||
this._currentTokenStart = null;
|
this._currentTokenStart = null;
|
||||||
this._currentTokenType = null;
|
this._currentTokenType = null;
|
||||||
|
@ -240,9 +240,11 @@ class _HtmlTokenizer {
|
||||||
this._column++;
|
this._column++;
|
||||||
}
|
}
|
||||||
this._index++;
|
this._index++;
|
||||||
this._peek = this._index >= this._length ? $EOF : StringWrapper.charCodeAt(this._input, this._index);
|
this._peek =
|
||||||
this._nextPeek =
|
this._index >= this._length ? $EOF : StringWrapper.charCodeAt(this._input, this._index);
|
||||||
this._index + 1 >= this._length ? $EOF : StringWrapper.charCodeAt(this._input, this._index + 1);
|
this._nextPeek = this._index + 1 >= this._length ?
|
||||||
|
$EOF :
|
||||||
|
StringWrapper.charCodeAt(this._input, this._index + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _attemptCharCode(charCode: number): boolean {
|
private _attemptCharCode(charCode: number): boolean {
|
||||||
|
@ -264,8 +266,8 @@ class _HtmlTokenizer {
|
||||||
private _requireCharCode(charCode: number) {
|
private _requireCharCode(charCode: number) {
|
||||||
var location = this._getLocation();
|
var location = this._getLocation();
|
||||||
if (!this._attemptCharCode(charCode)) {
|
if (!this._attemptCharCode(charCode)) {
|
||||||
throw this._createError(unexpectedCharacterErrorMsg(this._peek),
|
throw this._createError(
|
||||||
this._getSpan(location, location));
|
unexpectedCharacterErrorMsg(this._peek), this._getSpan(location, location));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import {BaseException} from '../facade/exceptions';
|
import {BaseException} from '../facade/exceptions';
|
||||||
import {HtmlAst, HtmlAstVisitor, HtmlAttrAst, HtmlCommentAst, HtmlElementAst, HtmlExpansionAst, HtmlExpansionCaseAst, HtmlTextAst, htmlVisitAll} from '../html_ast';
|
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.
|
* Expands special forms into elements.
|
||||||
|
@ -20,25 +23,25 @@ import {HtmlAst, HtmlAstVisitor, HtmlAttrAst, HtmlCommentAst, HtmlElementAst, Ht
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* <ul [ngPlural]="messages.length">
|
* <ul [ngPlural]="messages.length">
|
||||||
* <template [ngPluralCase]="0"><li i18n="plural_0">zero</li></template>
|
* <template [ngPluralCase]="'=0'"><li i18n="plural_=0">zero</li></template>
|
||||||
* <template [ngPluralCase]="1"><li i18n="plural_1">one</li></template>
|
* <template [ngPluralCase]="'=1'"><li i18n="plural_=1">one</li></template>
|
||||||
* <template [ngPluralCase]="other"><li i18n="plural_other">more than one</li></template>
|
* <template [ngPluralCase]="'other'"><li i18n="plural_other">more than one</li></template>
|
||||||
* </ul>
|
* </ul>
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export function expandNodes(nodes: HtmlAst[]): ExpansionResult {
|
export function expandNodes(nodes: HtmlAst[]): ExpansionResult {
|
||||||
let e = new _Expander();
|
let e = new _Expander();
|
||||||
let n = htmlVisitAll(e, nodes);
|
let n = htmlVisitAll(e, nodes);
|
||||||
return new ExpansionResult(n, e.expanded);
|
return new ExpansionResult(n, e.expanded, e.errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ExpansionResult {
|
export class ExpansionResult {
|
||||||
constructor(public nodes: HtmlAst[], public expanded: boolean) {}
|
constructor(public nodes: HtmlAst[], public expanded: boolean, public errors: ParseError[]) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _Expander implements HtmlAstVisitor {
|
class _Expander implements HtmlAstVisitor {
|
||||||
expanded: boolean = false;
|
expanded: boolean = false;
|
||||||
constructor() {}
|
errors: ParseError[] = [];
|
||||||
|
|
||||||
visitElement(ast: HtmlElementAst, context: any): any {
|
visitElement(ast: HtmlElementAst, context: any): any {
|
||||||
return new HtmlElementAst(
|
return new HtmlElementAst(
|
||||||
|
@ -54,7 +57,7 @@ class _Expander implements HtmlAstVisitor {
|
||||||
|
|
||||||
visitExpansion(ast: HtmlExpansionAst, context: any): any {
|
visitExpansion(ast: HtmlExpansionAst, context: any): any {
|
||||||
this.expanded = true;
|
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 {
|
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 => {
|
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 "=<number>" or one of ${PLURAL_CASES.join(", ")}`));
|
||||||
|
}
|
||||||
let expansionResult = expandNodes(c.expression);
|
let expansionResult = expandNodes(c.expression);
|
||||||
|
expansionResult.errors.forEach(e => errors.push(e));
|
||||||
let i18nAttrs = expansionResult.expanded ?
|
let i18nAttrs = expansionResult.expanded ?
|
||||||
[] :
|
[] :
|
||||||
[new HtmlAttrAst('i18n', `${ast.type}_${c.value}`, c.valueSourceSpan)];
|
[new HtmlAttrAst('i18n', `${ast.type}_${c.value}`, c.valueSourceSpan)];
|
||||||
|
|
|
@ -111,7 +111,10 @@ export class I18nHtmlParser implements HtmlParser {
|
||||||
if (res.errors.length > 0) {
|
if (res.errors.length > 0) {
|
||||||
return res;
|
return res;
|
||||||
} else {
|
} 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) :
|
return this.errors.length > 0 ? new HtmlParseTreeResult([], this.errors) :
|
||||||
new HtmlParseTreeResult(nodes, []);
|
new HtmlParseTreeResult(nodes, []);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,14 +19,6 @@ export class ExtractionResult {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes duplicate messages.
|
* 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[] {
|
export function removeDuplicates(messages: Message[]): Message[] {
|
||||||
let uniq: {[key: string]: Message} = {};
|
let uniq: {[key: string]: Message} = {};
|
||||||
|
@ -113,8 +105,9 @@ export class MessageExtractor {
|
||||||
if (res.errors.length > 0) {
|
if (res.errors.length > 0) {
|
||||||
return new ExtractionResult([], res.errors);
|
return new ExtractionResult([], res.errors);
|
||||||
} else {
|
} else {
|
||||||
this._recurse(expandNodes(res.rootNodes).nodes);
|
let expanded = expandNodes(res.rootNodes);
|
||||||
return new ExtractionResult(this.messages, this.errors);
|
this._recurse(expanded.nodes);
|
||||||
|
return new ExtractionResult(this.messages, this.errors.concat(expanded.errors));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -485,98 +485,65 @@ export function main() {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("expansion forms", () => {
|
describe('expansion forms', () => {
|
||||||
it("should parse an expansion form", () => {
|
it('should parse an expansion form', () => {
|
||||||
expect(tokenizeAndHumanizeParts('{one.two, three, =4 {four} =5 {five} foo {bar} }', true))
|
expect(tokenizeAndHumanizeParts('{one.two, three, =4 {four} =5 {five} foo {bar} }', true))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
[HtmlTokenType.EXPANSION_FORM_START],
|
[HtmlTokenType.EXPANSION_FORM_START], [HtmlTokenType.RAW_TEXT, 'one.two'],
|
||||||
[HtmlTokenType.RAW_TEXT, 'one.two'],
|
[HtmlTokenType.RAW_TEXT, 'three'], [HtmlTokenType.EXPANSION_CASE_VALUE, '=4'],
|
||||||
[HtmlTokenType.RAW_TEXT, 'three'],
|
[HtmlTokenType.EXPANSION_CASE_EXP_START], [HtmlTokenType.TEXT, 'four'],
|
||||||
[HtmlTokenType.EXPANSION_CASE_VALUE, '=4'],
|
[HtmlTokenType.EXPANSION_CASE_EXP_END], [HtmlTokenType.EXPANSION_CASE_VALUE, '=5'],
|
||||||
[HtmlTokenType.EXPANSION_CASE_EXP_START],
|
[HtmlTokenType.EXPANSION_CASE_EXP_START], [HtmlTokenType.TEXT, 'five'],
|
||||||
[HtmlTokenType.TEXT, 'four'],
|
[HtmlTokenType.EXPANSION_CASE_EXP_END], [HtmlTokenType.EXPANSION_CASE_VALUE, 'foo'],
|
||||||
[HtmlTokenType.EXPANSION_CASE_EXP_END],
|
[HtmlTokenType.EXPANSION_CASE_EXP_START], [HtmlTokenType.TEXT, 'bar'],
|
||||||
[HtmlTokenType.EXPANSION_CASE_VALUE, '=5'],
|
[HtmlTokenType.EXPANSION_CASE_EXP_END], [HtmlTokenType.EXPANSION_FORM_END],
|
||||||
[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]
|
[HtmlTokenType.EOF]
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should parse an expansion form with text elements surrounding it", () => {
|
it('should parse an expansion form with text elements surrounding it', () => {
|
||||||
expect(tokenizeAndHumanizeParts('before{one.two, three, =4 {four}}after', true))
|
expect(tokenizeAndHumanizeParts('before{one.two, three, =4 {four}}after', true)).toEqual([
|
||||||
.toEqual([
|
[HtmlTokenType.TEXT, 'before'], [HtmlTokenType.EXPANSION_FORM_START],
|
||||||
[HtmlTokenType.TEXT, "before"],
|
[HtmlTokenType.RAW_TEXT, 'one.two'], [HtmlTokenType.RAW_TEXT, 'three'],
|
||||||
[HtmlTokenType.EXPANSION_FORM_START],
|
[HtmlTokenType.EXPANSION_CASE_VALUE, '=4'], [HtmlTokenType.EXPANSION_CASE_EXP_START],
|
||||||
[HtmlTokenType.RAW_TEXT, 'one.two'],
|
[HtmlTokenType.TEXT, 'four'], [HtmlTokenType.EXPANSION_CASE_EXP_END],
|
||||||
[HtmlTokenType.RAW_TEXT, 'three'],
|
[HtmlTokenType.EXPANSION_FORM_END], [HtmlTokenType.TEXT, 'after'], [HtmlTokenType.EOF]
|
||||||
[HtmlTokenType.EXPANSION_CASE_VALUE, '=4'],
|
]);
|
||||||
[HtmlTokenType.EXPANSION_CASE_EXP_START],
|
});
|
||||||
[HtmlTokenType.TEXT, 'four'],
|
|
||||||
[HtmlTokenType.EXPANSION_CASE_EXP_END],
|
it('should parse an expansion forms with elements in it', () => {
|
||||||
[HtmlTokenType.EXPANSION_FORM_END],
|
expect(tokenizeAndHumanizeParts('{one.two, three, =4 {four <b>a</b>}}', true)).toEqual([
|
||||||
[HtmlTokenType.TEXT, "after"],
|
[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]
|
[HtmlTokenType.EOF]
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should parse an expansion forms with elements in it", () => {
|
it('should parse an expansion forms with interpolation in it', () => {
|
||||||
expect(tokenizeAndHumanizeParts('{one.two, three, =4 {four <b>a</b>}}', true))
|
expect(tokenizeAndHumanizeParts('{one.two, three, =4 {four {{a}}}}', true)).toEqual([
|
||||||
.toEqual([
|
[HtmlTokenType.EXPANSION_FORM_START], [HtmlTokenType.RAW_TEXT, 'one.two'],
|
||||||
[HtmlTokenType.EXPANSION_FORM_START],
|
[HtmlTokenType.RAW_TEXT, 'three'], [HtmlTokenType.EXPANSION_CASE_VALUE, '=4'],
|
||||||
[HtmlTokenType.RAW_TEXT, 'one.two'],
|
[HtmlTokenType.EXPANSION_CASE_EXP_START], [HtmlTokenType.TEXT, 'four {{a}}'],
|
||||||
[HtmlTokenType.RAW_TEXT, 'three'],
|
[HtmlTokenType.EXPANSION_CASE_EXP_END], [HtmlTokenType.EXPANSION_FORM_END],
|
||||||
[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]
|
[HtmlTokenType.EOF]
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should parse an expansion forms with interpolation in it", () => {
|
it('should parse nested expansion forms', () => {
|
||||||
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", () => {
|
|
||||||
expect(tokenizeAndHumanizeParts(`{one.two, three, =4 { {xx, yy, =x {one}} }}`, true))
|
expect(tokenizeAndHumanizeParts(`{one.two, three, =4 { {xx, yy, =x {one}} }}`, true))
|
||||||
.toEqual([
|
.toEqual([
|
||||||
[HtmlTokenType.EXPANSION_FORM_START],
|
[HtmlTokenType.EXPANSION_FORM_START], [HtmlTokenType.RAW_TEXT, 'one.two'],
|
||||||
[HtmlTokenType.RAW_TEXT, 'one.two'],
|
[HtmlTokenType.RAW_TEXT, 'three'], [HtmlTokenType.EXPANSION_CASE_VALUE, '=4'],
|
||||||
[HtmlTokenType.RAW_TEXT, 'three'],
|
|
||||||
[HtmlTokenType.EXPANSION_CASE_VALUE, '=4'],
|
|
||||||
[HtmlTokenType.EXPANSION_CASE_EXP_START],
|
[HtmlTokenType.EXPANSION_CASE_EXP_START],
|
||||||
|
|
||||||
[HtmlTokenType.EXPANSION_FORM_START],
|
[HtmlTokenType.EXPANSION_FORM_START], [HtmlTokenType.RAW_TEXT, 'xx'],
|
||||||
[HtmlTokenType.RAW_TEXT, 'xx'],
|
[HtmlTokenType.RAW_TEXT, 'yy'], [HtmlTokenType.EXPANSION_CASE_VALUE, '=x'],
|
||||||
[HtmlTokenType.RAW_TEXT, 'yy'],
|
[HtmlTokenType.EXPANSION_CASE_EXP_START], [HtmlTokenType.TEXT, 'one'],
|
||||||
[HtmlTokenType.EXPANSION_CASE_VALUE, '=x'],
|
[HtmlTokenType.EXPANSION_CASE_EXP_END], [HtmlTokenType.EXPANSION_FORM_END],
|
||||||
[HtmlTokenType.EXPANSION_CASE_EXP_START],
|
|
||||||
[HtmlTokenType.TEXT, 'one'],
|
|
||||||
[HtmlTokenType.EXPANSION_CASE_EXP_END],
|
|
||||||
[HtmlTokenType.EXPANSION_FORM_END],
|
|
||||||
[HtmlTokenType.TEXT, ' '],
|
[HtmlTokenType.TEXT, ' '],
|
||||||
|
|
||||||
[HtmlTokenType.EXPANSION_CASE_EXP_END], [HtmlTokenType.EXPANSION_FORM_END],
|
[HtmlTokenType.EXPANSION_CASE_EXP_END], [HtmlTokenType.EXPANSION_FORM_END],
|
||||||
|
|
|
@ -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 {HtmlTokenType} from '@angular/compiler/src/html_lexer';
|
||||||
import {HtmlParser, HtmlParseTreeResult, HtmlTreeError} from '@angular/compiler/src/html_parser';
|
import {HtmlParseTreeResult, HtmlParser, HtmlTreeError} from '@angular/compiler/src/html_parser';
|
||||||
import {
|
|
||||||
HtmlElementAst,
|
|
||||||
HtmlAttrAst,
|
|
||||||
HtmlTextAst,
|
|
||||||
HtmlCommentAst,
|
|
||||||
HtmlExpansionAst,
|
|
||||||
HtmlExpansionCaseAst
|
|
||||||
} from '@angular/compiler/src/html_ast';
|
|
||||||
import {ParseError} from '@angular/compiler/src/parse_util';
|
import {ParseError} from '@angular/compiler/src/parse_util';
|
||||||
|
|
||||||
import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './html_ast_spec_utils';
|
import {humanizeDom, humanizeDomSourceSpans, humanizeLineColumn} from './html_ast_spec_utils';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
|
@ -238,8 +231,7 @@ export function main() {
|
||||||
`<div>before{messages.length, plural, =0 {You have <b>no</b> messages} =1 {One {{message}}}}after</div>`,
|
`<div>before{messages.length, plural, =0 {You have <b>no</b> messages} =1 {One {{message}}}}after</div>`,
|
||||||
'TestComp', true);
|
'TestComp', true);
|
||||||
|
|
||||||
expect(humanizeDom(parsed))
|
expect(humanizeDom(parsed)).toEqual([
|
||||||
.toEqual([
|
|
||||||
[HtmlElementAst, 'div', 0],
|
[HtmlElementAst, 'div', 0],
|
||||||
[HtmlTextAst, 'before', 1],
|
[HtmlTextAst, 'before', 1],
|
||||||
[HtmlExpansionAst, 'messages.length', 'plural'],
|
[HtmlExpansionAst, 'messages.length', 'plural'],
|
||||||
|
@ -263,16 +255,14 @@ export function main() {
|
||||||
it('should parse out nested expansion forms', () => {
|
it('should parse out nested expansion forms', () => {
|
||||||
let parsed = parser.parse(
|
let parsed = parser.parse(
|
||||||
`{messages.length, plural, =0 { {p.gender, gender, =m {m}} }}`, 'TestComp', true);
|
`{messages.length, plural, =0 { {p.gender, gender, =m {m}} }}`, 'TestComp', true);
|
||||||
expect(humanizeDom(parsed))
|
expect(humanizeDom(parsed)).toEqual([
|
||||||
.toEqual([
|
|
||||||
[HtmlExpansionAst, 'messages.length', 'plural'],
|
[HtmlExpansionAst, 'messages.length', 'plural'],
|
||||||
[HtmlExpansionCaseAst, '=0'],
|
[HtmlExpansionCaseAst, '=0'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let firstCase = (<any>parsed.rootNodes[0]).cases[0];
|
let firstCase = (<any>parsed.rootNodes[0]).cases[0];
|
||||||
|
|
||||||
expect(humanizeDom(new HtmlParseTreeResult(firstCase.expression, [])))
|
expect(humanizeDom(new HtmlParseTreeResult(firstCase.expression, []))).toEqual([
|
||||||
.toEqual([
|
|
||||||
[HtmlExpansionAst, 'p.gender', 'gender'],
|
[HtmlExpansionAst, 'p.gender', 'gender'],
|
||||||
[HtmlExpansionCaseAst, '=m'],
|
[HtmlExpansionCaseAst, '=m'],
|
||||||
[HtmlTextAst, ' ', 0],
|
[HtmlTextAst, ' ', 0],
|
||||||
|
|
|
@ -21,8 +21,7 @@ export function main() {
|
||||||
|
|
||||||
let msgs = '';
|
let msgs = '';
|
||||||
StringMapWrapper.forEach(
|
StringMapWrapper.forEach(
|
||||||
messages, (v: any /** TODO #9100 */, k: any /** TODO #9100 */) => msgs +=
|
messages, (v: string, k: string) => msgs += `<msg id="${k}">${v}</msg>`);
|
||||||
`<msg id="${k}">${v}</msg>`);
|
|
||||||
let res = deserializeXmb(`<message-bundle>${msgs}</message-bundle>`, 'someUrl');
|
let res = deserializeXmb(`<message-bundle>${msgs}</message-bundle>`, 'someUrl');
|
||||||
|
|
||||||
return new I18nHtmlParser(
|
return new I18nHtmlParser(
|
||||||
|
@ -175,13 +174,12 @@ export function main() {
|
||||||
|
|
||||||
it('should handle the plural expansion form', () => {
|
it('should handle the plural expansion form', () => {
|
||||||
let translations: {[key: string]: string} = {};
|
let translations: {[key: string]: string} = {};
|
||||||
translations[id(new Message('zero<ph name="e1">bold</ph>', "plural_=0", null))] =
|
translations[id(new Message('zero<ph name="e1">bold</ph>', 'plural_=0', null))] =
|
||||||
'ZERO<ph name="e1">BOLD</ph>';
|
'ZERO<ph name="e1">BOLD</ph>';
|
||||||
|
|
||||||
let res = parse(`{messages.length, plural,=0 {zero<b>bold</b>}}`, translations);
|
let res = parse(`{messages.length, plural,=0 {zero<b>bold</b>}}`, translations);
|
||||||
|
|
||||||
expect(humanizeDom(res))
|
expect(humanizeDom(res)).toEqual([
|
||||||
.toEqual([
|
|
||||||
[HtmlElementAst, 'ul', 0],
|
[HtmlElementAst, 'ul', 0],
|
||||||
[HtmlAttrAst, '[ngPlural]', 'messages.length'],
|
[HtmlAttrAst, '[ngPlural]', 'messages.length'],
|
||||||
[HtmlElementAst, 'template', 1],
|
[HtmlElementAst, 'template', 1],
|
||||||
|
@ -195,24 +193,18 @@ export function main() {
|
||||||
|
|
||||||
it('should handle nested expansion forms', () => {
|
it('should handle nested expansion forms', () => {
|
||||||
let translations: {[key: string]: string} = {};
|
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);
|
let res = parse(`{messages.length, plural, =0 { {p.gender, gender, =m {m}} }}`, translations);
|
||||||
|
|
||||||
expect(humanizeDom(res))
|
expect(humanizeDom(res)).toEqual([
|
||||||
.toEqual([
|
[HtmlElementAst, 'ul', 0], [HtmlAttrAst, '[ngPlural]', 'messages.length'],
|
||||||
[HtmlElementAst, 'ul', 0],
|
[HtmlElementAst, 'template', 1], [HtmlAttrAst, 'ngPluralCase', '=0'],
|
||||||
[HtmlAttrAst, '[ngPlural]', 'messages.length'],
|
|
||||||
[HtmlElementAst, 'template', 1],
|
|
||||||
[HtmlAttrAst, 'ngPluralCase', '=0'],
|
|
||||||
[HtmlElementAst, 'li', 2],
|
[HtmlElementAst, 'li', 2],
|
||||||
|
|
||||||
[HtmlElementAst, 'ul', 3],
|
[HtmlElementAst, 'ul', 3], [HtmlAttrAst, '[ngSwitch]', 'p.gender'],
|
||||||
[HtmlAttrAst, '[ngSwitch]', 'p.gender'],
|
[HtmlElementAst, 'template', 4], [HtmlAttrAst, 'ngSwitchWhen', '=m'],
|
||||||
[HtmlElementAst, 'template', 4],
|
[HtmlElementAst, 'li', 5], [HtmlTextAst, 'M', 6],
|
||||||
[HtmlAttrAst, 'ngSwitchWhen', '=m'],
|
|
||||||
[HtmlElementAst, 'li', 5],
|
|
||||||
[HtmlTextAst, 'M', 6],
|
|
||||||
|
|
||||||
[HtmlTextAst, ' ', 3]
|
[HtmlTextAst, ' ', 3]
|
||||||
]);
|
]);
|
||||||
|
@ -220,7 +212,7 @@ export function main() {
|
||||||
|
|
||||||
it('should correctly set source code positions', () => {
|
it('should correctly set source code positions', () => {
|
||||||
let translations: {[key: string]: string} = {};
|
let translations: {[key: string]: string} = {};
|
||||||
translations[id(new Message('<ph name="e0">bold</ph>', "plural_=0", null))] =
|
translations[id(new Message('<ph name="e0">bold</ph>', 'plural_=0', null))] =
|
||||||
'<ph name="e0">BOLD</ph>';
|
'<ph name="e0">BOLD</ph>';
|
||||||
|
|
||||||
let nodes = parse(`{messages.length, plural,=0 {<b>bold</b>}}`, translations).rootNodes;
|
let nodes = parse(`{messages.length, plural,=0 {<b>bold</b>}}`, translations).rootNodes;
|
||||||
|
@ -259,12 +251,11 @@ export function main() {
|
||||||
|
|
||||||
it('should handle other special forms', () => {
|
it('should handle other special forms', () => {
|
||||||
let translations: {[key: string]: string} = {};
|
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);
|
let res = parse(`{person.gender, gender,=male {m}}`, translations);
|
||||||
|
|
||||||
expect(humanizeDom(res))
|
expect(humanizeDom(res)).toEqual([
|
||||||
.toEqual([
|
|
||||||
[HtmlElementAst, 'ul', 0],
|
[HtmlElementAst, 'ul', 0],
|
||||||
[HtmlAttrAst, '[ngSwitch]', 'person.gender'],
|
[HtmlAttrAst, '[ngSwitch]', 'person.gender'],
|
||||||
[HtmlElementAst, 'template', 1],
|
[HtmlElementAst, 'template', 1],
|
||||||
|
@ -321,6 +312,15 @@ export function main() {
|
||||||
.toEqual(['Invalid interpolation name \'99\'']);
|
.toEqual(['Invalid interpolation name \'99\'']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 "=<number>" or one of zero, one, two, few, many, other`,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('implicit translation', () => {
|
describe('implicit translation', () => {
|
||||||
it('should support attributes', () => {
|
it('should support attributes', () => {
|
||||||
let translations: {[key: string]: string} = {};
|
let translations: {[key: string]: string} = {};
|
||||||
|
@ -333,11 +333,9 @@ export function main() {
|
||||||
|
|
||||||
it('should support attributes with meaning and description', () => {
|
it('should support attributes with meaning and description', () => {
|
||||||
let translations: {[key: string]: string} = {};
|
let translations: {[key: string]: string} = {};
|
||||||
translations[id(new Message('some message', 'meaning', 'description'))] =
|
translations[id(new Message('some message', 'meaning', 'description'))] = 'another message';
|
||||||
'another message';
|
|
||||||
|
|
||||||
expect(
|
expect(humanizeDom(parse(
|
||||||
humanizeDom(parse(
|
|
||||||
'<i18n-el value=\'some message\' i18n-value=\'meaning|description\'></i18n-el>',
|
'<i18n-el value=\'some message\' i18n-value=\'meaning|description\'></i18n-el>',
|
||||||
translations, [], {'i18n-el': ['value']})))
|
translations, [], {'i18n-el': ['value']})))
|
||||||
.toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlAttrAst, 'value', 'another message']]);
|
.toEqual([[HtmlElementAst, 'i18n-el', 0], [HtmlAttrAst, 'value', 'another message']]);
|
||||||
|
@ -362,7 +360,6 @@ export function main() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function humanizeErrors(errors: ParseError[]): string[] {
|
function humanizeErrors(errors: ParseError[]): string[] {
|
||||||
|
|
|
@ -151,8 +151,9 @@ export function main() {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should extract messages from expansion forms", () => {
|
it('should extract messages from expansion forms', () => {
|
||||||
let res = extractor.extract(`
|
let res = extractor.extract(
|
||||||
|
`
|
||||||
<div>
|
<div>
|
||||||
{messages.length, plural,
|
{messages.length, plural,
|
||||||
=0 {You have <b>no</b> messages}
|
=0 {You have <b>no</b> messages}
|
||||||
|
@ -160,13 +161,12 @@ export function main() {
|
||||||
other {You have messages}
|
other {You have messages}
|
||||||
}
|
}
|
||||||
</div>`,
|
</div>`,
|
||||||
"someurl");
|
'someurl');
|
||||||
|
|
||||||
expect(res.messages)
|
expect(res.messages).toEqual([
|
||||||
.toEqual([
|
new Message('You have <ph name="e1">no</ph> messages', 'plural_=0', null),
|
||||||
new Message('You have <ph name="e1">no</ph> messages', "plural_=0", null),
|
new Message('You have one message', 'plural_=1', null),
|
||||||
new Message('You have one message', "plural_=1", null),
|
new Message('You have messages', 'plural_other', null),
|
||||||
new Message('You have messages', "plural_other", null),
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -235,6 +235,14 @@ export function main() {
|
||||||
expect(res.errors.length).toEqual(1);
|
expect(res.errors.length).toEqual(1);
|
||||||
expect(res.errors[0].msg).toEqual('Unexpected character "s"');
|
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 "=<number>" or one of zero, one, two, few, many, other');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue