feat(I18N Expander): do not add extra <ul> & <li> around ICU messages (#9283)

fixes #9072
This commit is contained in:
Victor Berchet
2016-06-17 11:38:24 -07:00
committed by GitHub
parent 7498050421
commit 721f53f0d6
6 changed files with 162 additions and 155 deletions

View File

@ -4,25 +4,23 @@ import {ParseLocation} from '@angular/compiler/src/parse_util';
import {BaseException} from '../src/facade/exceptions';
export function humanizeDom(parseResult: HtmlParseTreeResult): any[] {
export function humanizeDom(
parseResult: HtmlParseTreeResult, addSourceSpan: boolean = false): any[] {
if (parseResult.errors.length > 0) {
var errorString = parseResult.errors.join('\n');
throw new BaseException(`Unexpected parse errors:\n${errorString}`);
}
var humanizer = new _Humanizer(false);
htmlVisitAll(humanizer, parseResult.rootNodes);
return humanizer.result;
return humanizeNodes(parseResult.rootNodes, addSourceSpan);
}
export function humanizeDomSourceSpans(parseResult: HtmlParseTreeResult): any[] {
if (parseResult.errors.length > 0) {
var errorString = parseResult.errors.join('\n');
throw new BaseException(`Unexpected parse errors:\n${errorString}`);
}
return humanizeDom(parseResult, true);
}
var humanizer = new _Humanizer(true);
htmlVisitAll(humanizer, parseResult.rootNodes);
export function humanizeNodes(nodes: HtmlAst[], addSourceSpan: boolean = false): any[] {
var humanizer = new _Humanizer(addSourceSpan);
htmlVisitAll(humanizer, nodes);
return humanizer.result;
}

View File

@ -0,0 +1,99 @@
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';
export function main() {
describe('Expander', () => {
function expand(template: string): ExpansionResult {
const htmlParser = new HtmlParser();
const res = htmlParser.parse(template, 'url', true);
return expandNodes(res.rootNodes);
}
it('should handle the plural expansion form', () => {
const res = expand(`{messages.length, plural,=0 {zero<b>bold</b>}}`);
expect(humanizeNodes(res.nodes)).toEqual([
[HtmlElementAst, 'ng-container', 0],
[HtmlAttrAst, '[ngPlural]', 'messages.length'],
[HtmlElementAst, 'template', 1],
[HtmlAttrAst, 'ngPluralCase', '=0'],
[HtmlTextAst, 'zero', 2],
[HtmlElementAst, 'b', 2],
[HtmlTextAst, 'bold', 3],
]);
});
it('should handle nested expansion forms', () => {
const res = expand(`{messages.length, plural, =0 { {p.gender, gender, =m {m}} }}`);
expect(humanizeNodes(res.nodes)).toEqual([
[HtmlElementAst, 'ng-container', 0],
[HtmlAttrAst, '[ngPlural]', 'messages.length'],
[HtmlElementAst, 'template', 1],
[HtmlAttrAst, 'ngPluralCase', '=0'],
[HtmlElementAst, 'ng-container', 2],
[HtmlAttrAst, '[ngSwitch]', 'p.gender'],
[HtmlElementAst, 'template', 3],
[HtmlAttrAst, 'ngSwitchCase', '=m'],
[HtmlTextAst, 'm', 4],
[HtmlTextAst, ' ', 2],
]);
});
it('should correctly set source code positions', () => {
const nodes = expand(`{messages.length, plural,=0 {<b>bold</b>}}`).nodes;
const container: HtmlElementAst = <HtmlElementAst>nodes[0];
expect(container.sourceSpan.start.col).toEqual(0);
expect(container.sourceSpan.end.col).toEqual(42);
expect(container.startSourceSpan.start.col).toEqual(0);
expect(container.startSourceSpan.end.col).toEqual(42);
expect(container.endSourceSpan.start.col).toEqual(0);
expect(container.endSourceSpan.end.col).toEqual(42);
const switchExp = container.attrs[0];
expect(switchExp.sourceSpan.start.col).toEqual(1);
expect(switchExp.sourceSpan.end.col).toEqual(16);
const template: HtmlElementAst = <HtmlElementAst>container.children[0];
expect(template.sourceSpan.start.col).toEqual(25);
expect(template.sourceSpan.end.col).toEqual(41);
const switchCheck = template.attrs[0];
expect(switchCheck.sourceSpan.start.col).toEqual(25);
expect(switchCheck.sourceSpan.end.col).toEqual(28);
const b: HtmlElementAst = <HtmlElementAst>template.children[0];
expect(b.sourceSpan.start.col).toEqual(29);
expect(b.endSourceSpan.end.col).toEqual(40);
});
it('should handle other special forms', () => {
const res = expand(`{person.gender, gender,=male {m}}`);
expect(humanizeNodes(res.nodes)).toEqual([
[HtmlElementAst, 'ng-container', 0],
[HtmlAttrAst, '[ngSwitch]', 'person.gender'],
[HtmlElementAst, 'template', 1],
[HtmlAttrAst, 'ngSwitchCase', '=male'],
[HtmlTextAst, 'm', 2],
]);
});
describe('errors', () => {
it('should error on unknown plural cases', () => {
expect(humanizeErrors(expand('{n, plural, unknown {-}}').errors)).toEqual([
`Plural cases should be "=<number>" or one of zero, one, two, few, many, other`,
]);
});
});
});
}
function humanizeErrors(errors: ParseError[]): string[] {
return errors.map(error => error.msg);
}

View File

@ -172,99 +172,6 @@ export function main() {
expect(res[1].sourceSpan.start.offset).toEqual(10);
});
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>';
let res = parse(`{messages.length, plural,=0 {zero<b>bold</b>}}`, 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],
]);
});
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))] =
'<ph name="e0">BOLD</ph>';
let nodes = parse(`{messages.length, plural,=0 {<b>bold</b>}}`, translations).rootNodes;
let ul: HtmlElementAst = <HtmlElementAst>nodes[0];
expect(ul.sourceSpan.start.col).toEqual(0);
expect(ul.sourceSpan.end.col).toEqual(42);
expect(ul.startSourceSpan.start.col).toEqual(0);
expect(ul.startSourceSpan.end.col).toEqual(42);
expect(ul.endSourceSpan.start.col).toEqual(0);
expect(ul.endSourceSpan.end.col).toEqual(42);
let switchExp = ul.attrs[0];
expect(switchExp.sourceSpan.start.col).toEqual(1);
expect(switchExp.sourceSpan.end.col).toEqual(16);
let template: HtmlElementAst = <HtmlElementAst>ul.children[0];
expect(template.sourceSpan.start.col).toEqual(25);
expect(template.sourceSpan.end.col).toEqual(41);
let switchCheck = template.attrs[0];
expect(switchCheck.sourceSpan.start.col).toEqual(25);
expect(switchCheck.sourceSpan.end.col).toEqual(28);
let li: HtmlElementAst = <HtmlElementAst>template.children[0];
expect(li.sourceSpan.start.col).toEqual(25);
expect(li.sourceSpan.end.col).toEqual(41);
let b: HtmlElementAst = <HtmlElementAst>li.children[0];
expect(b.sourceSpan.start.col).toEqual(29);
expect(b.sourceSpan.end.col).toEqual(32);
});
it('should handle other special forms', () => {
let translations: {[key: string]: string} = {};
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],
]);
});
describe('errors', () => {
it('should error when giving an invalid template', () => {
expect(humanizeErrors(parse('<a>a</b>', {}).errors)).toEqual([
@ -311,14 +218,6 @@ export function main() {
humanizeErrors(parse('<div value=\'hi {{a}}\' i18n-value></div>', translations).errors))
.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', () => {

View File

@ -154,7 +154,9 @@ export function main() {
]);
});
it('should extract messages from expansion forms', () => {
// TODO(vicb) - this should be extracted to a single message
// see https://github.com/angular/angular/issues/9067
xit('should extract messages from expansion forms', () => {
let res = extractor.extract(
`
<div>