2016-05-31 18:22:59 -04:00
|
|
|
import {BaseException} from '../facade/exceptions';
|
2016-06-08 19:38:52 -04:00
|
|
|
import {HtmlAst, HtmlAstVisitor, HtmlAttrAst, HtmlCommentAst, HtmlElementAst, HtmlExpansionAst, HtmlExpansionCaseAst, HtmlTextAst, htmlVisitAll} from '../html_ast';
|
2016-06-09 17:53:03 -04:00
|
|
|
import {ParseError} from '../parse_util';
|
|
|
|
import {I18nError} from './shared';
|
2016-06-08 19:38:52 -04:00
|
|
|
|
2016-06-09 17:53:03 -04:00
|
|
|
// http://cldr.unicode.org/index/cldr-spec/plural-rules
|
|
|
|
const PLURAL_CASES: string[] = ['zero', 'one', 'two', 'few', 'many', 'other'];
|
2016-04-13 19:01:25 -04:00
|
|
|
|
2016-04-12 14:46:49 -04:00
|
|
|
/**
|
|
|
|
* Expands special forms into elements.
|
|
|
|
*
|
|
|
|
* For example,
|
|
|
|
*
|
|
|
|
* ```
|
|
|
|
* { messages.length, plural,
|
|
|
|
* =0 {zero}
|
|
|
|
* =1 {one}
|
2016-06-09 16:48:53 -04:00
|
|
|
* other {more than one}
|
2016-04-12 14:46:49 -04:00
|
|
|
* }
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* will be expanded into
|
|
|
|
*
|
|
|
|
* ```
|
|
|
|
* <ul [ngPlural]="messages.length">
|
2016-06-09 17:53:03 -04:00
|
|
|
* <template [ngPluralCase]="'=0'"><li i18n="plural_=0">zero</li></template>
|
|
|
|
* <template [ngPluralCase]="'=1'"><li i18n="plural_=1">one</li></template>
|
|
|
|
* <template [ngPluralCase]="'other'"><li i18n="plural_other">more than one</li></template>
|
2016-04-12 14:46:49 -04:00
|
|
|
* </ul>
|
|
|
|
* ```
|
|
|
|
*/
|
2016-04-13 19:01:25 -04:00
|
|
|
export function expandNodes(nodes: HtmlAst[]): ExpansionResult {
|
|
|
|
let e = new _Expander();
|
|
|
|
let n = htmlVisitAll(e, nodes);
|
2016-06-09 17:53:03 -04:00
|
|
|
return new ExpansionResult(n, e.expanded, e.errors);
|
2016-04-13 19:01:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
export class ExpansionResult {
|
2016-06-09 17:53:03 -04:00
|
|
|
constructor(public nodes: HtmlAst[], public expanded: boolean, public errors: ParseError[]) {}
|
2016-04-13 19:01:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
class _Expander implements HtmlAstVisitor {
|
|
|
|
expanded: boolean = false;
|
2016-06-09 17:53:03 -04:00
|
|
|
errors: ParseError[] = [];
|
2016-04-12 14:46:49 -04:00
|
|
|
|
|
|
|
visitElement(ast: HtmlElementAst, context: any): any {
|
2016-06-08 19:38:52 -04:00
|
|
|
return new HtmlElementAst(
|
|
|
|
ast.name, ast.attrs, htmlVisitAll(this, ast.children), ast.sourceSpan, ast.startSourceSpan,
|
|
|
|
ast.endSourceSpan);
|
2016-04-12 14:46:49 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
visitAttr(ast: HtmlAttrAst, context: any): any { return ast; }
|
|
|
|
|
|
|
|
visitText(ast: HtmlTextAst, context: any): any { return ast; }
|
|
|
|
|
|
|
|
visitComment(ast: HtmlCommentAst, context: any): any { return ast; }
|
|
|
|
|
|
|
|
visitExpansion(ast: HtmlExpansionAst, context: any): any {
|
2016-04-13 19:01:25 -04:00
|
|
|
this.expanded = true;
|
2016-06-09 17:53:03 -04:00
|
|
|
return ast.type == 'plural' ? _expandPluralForm(ast, this.errors) : _expandDefaultForm(ast);
|
2016-04-12 14:46:49 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
visitExpansionCase(ast: HtmlExpansionCaseAst, context: any): any {
|
2016-06-08 19:38:52 -04:00
|
|
|
throw new BaseException('Should not be reached');
|
2016-04-12 14:46:49 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-09 17:53:03 -04:00
|
|
|
function _expandPluralForm(ast: HtmlExpansionAst, errors: ParseError[]): HtmlElementAst {
|
2016-04-13 19:01:25 -04:00
|
|
|
let children = ast.cases.map(c => {
|
2016-06-09 17:53:03 -04:00
|
|
|
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(", ")}`));
|
|
|
|
}
|
2016-04-13 19:01:25 -04:00
|
|
|
let expansionResult = expandNodes(c.expression);
|
2016-06-09 17:53:03 -04:00
|
|
|
expansionResult.errors.forEach(e => errors.push(e));
|
2016-04-13 19:01:25 -04:00
|
|
|
let i18nAttrs = expansionResult.expanded ?
|
2016-06-08 19:38:52 -04:00
|
|
|
[] :
|
|
|
|
[new HtmlAttrAst('i18n', `${ast.type}_${c.value}`, c.valueSourceSpan)];
|
2016-04-13 19:01:25 -04:00
|
|
|
|
2016-06-08 19:38:52 -04:00
|
|
|
return new HtmlElementAst(
|
|
|
|
`template`,
|
|
|
|
[
|
|
|
|
new HtmlAttrAst('ngPluralCase', c.value, c.valueSourceSpan),
|
|
|
|
],
|
|
|
|
[new HtmlElementAst(
|
|
|
|
`li`, i18nAttrs, expansionResult.nodes, c.sourceSpan, c.sourceSpan, c.sourceSpan)],
|
|
|
|
c.sourceSpan, c.sourceSpan, c.sourceSpan);
|
2016-04-13 19:01:25 -04:00
|
|
|
});
|
2016-06-08 19:38:52 -04:00
|
|
|
let switchAttr = new HtmlAttrAst('[ngPlural]', ast.switchValue, ast.switchValueSourceSpan);
|
|
|
|
return new HtmlElementAst(
|
|
|
|
'ul', [switchAttr], children, ast.sourceSpan, ast.sourceSpan, ast.sourceSpan);
|
2016-04-12 14:46:49 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function _expandDefaultForm(ast: HtmlExpansionAst): HtmlElementAst {
|
2016-04-13 19:01:25 -04:00
|
|
|
let children = ast.cases.map(c => {
|
|
|
|
let expansionResult = expandNodes(c.expression);
|
|
|
|
let i18nAttrs = expansionResult.expanded ?
|
2016-06-08 19:38:52 -04:00
|
|
|
[] :
|
|
|
|
[new HtmlAttrAst('i18n', `${ast.type}_${c.value}`, c.valueSourceSpan)];
|
2016-04-13 19:01:25 -04:00
|
|
|
|
2016-06-08 19:38:52 -04:00
|
|
|
return new HtmlElementAst(
|
|
|
|
`template`,
|
|
|
|
[
|
|
|
|
new HtmlAttrAst('ngSwitchWhen', c.value, c.valueSourceSpan),
|
|
|
|
],
|
|
|
|
[new HtmlElementAst(
|
|
|
|
`li`, i18nAttrs, expansionResult.nodes, c.sourceSpan, c.sourceSpan, c.sourceSpan)],
|
|
|
|
c.sourceSpan, c.sourceSpan, c.sourceSpan);
|
2016-04-13 19:01:25 -04:00
|
|
|
});
|
2016-06-08 19:38:52 -04:00
|
|
|
let switchAttr = new HtmlAttrAst('[ngSwitch]', ast.switchValue, ast.switchValueSourceSpan);
|
|
|
|
return new HtmlElementAst(
|
|
|
|
'ul', [switchAttr], children, ast.sourceSpan, ast.sourceSpan, ast.sourceSpan);
|
2016-04-28 20:50:03 -04:00
|
|
|
}
|