feat(ExtractorMerger): allow nested implicit tags

This commit is contained in:
Victor Berchet 2016-08-05 14:14:31 -07:00 committed by Alex Rickabaugh
parent 01bca41168
commit 1b04d70626
2 changed files with 33 additions and 20 deletions

View File

@ -54,8 +54,9 @@ enum _VisitorMode {
*/
class _Visitor implements html.Visitor {
// <el i18n>...</el>
private _inI18nNode = false;
private _depth: number = 0;
private _inI18nNode: boolean;
private _depth: number;
private _inImplicitNode: boolean;
// <!--i18n-->...<!--/i18n-->
private _blockMeaningAndDesc: string;
@ -64,7 +65,7 @@ class _Visitor implements html.Visitor {
private _inI18nBlock: boolean;
// {<icu message>}
private _inIcu = false;
private _inIcu: boolean;
private _msgCountAtSectionStart: number;
private _errors: I18nError[];
@ -204,12 +205,15 @@ class _Visitor implements html.Visitor {
this._mayBeAddBlockChildren(el);
this._depth++;
const wasInI18nNode = this._inI18nNode;
const wasInImplicitNode = this._inImplicitNode;
let childNodes: html.Node[];
// Extract only top level nodes with the (implicit) "i18n" attribute if not in a block or an ICU
// message
const i18nAttr = _getI18nAttr(el);
const isImplicitI18n = this._implicitTags.some((tag: string): boolean => el.name === tag);
const isImplicit = this._implicitTags.some((tag: string): boolean => el.name === tag);
const isTopLevelImplicit = !wasInImplicitNode && isImplicit;
this._inImplicitNode = this._inImplicitNode || isImplicit;
if (!this._isInTranslatableSection && !this._inIcu) {
if (i18nAttr) {
@ -217,7 +221,7 @@ class _Visitor implements html.Visitor {
this._inI18nNode = true;
const message = this._addMessage(el.children, i18nAttr.value);
childNodes = this._translateMessage(el, message);
} else if (isImplicitI18n) {
} else if (isTopLevelImplicit) {
// implicit translation
this._inI18nNode = true;
const message = this._addMessage(el.children);
@ -225,7 +229,7 @@ class _Visitor implements html.Visitor {
}
if (this._mode == _VisitorMode.Extract) {
const isTranslatable = i18nAttr || isImplicitI18n;
const isTranslatable = i18nAttr || isTopLevelImplicit;
if (isTranslatable) {
this._openTranslatableSection(el);
}
@ -235,7 +239,7 @@ class _Visitor implements html.Visitor {
}
}
if (this._mode === _VisitorMode.Merge && !i18nAttr && !isImplicitI18n) {
if (this._mode === _VisitorMode.Merge && !i18nAttr && !isTopLevelImplicit) {
childNodes = [];
el.children.forEach(child => {
const visited = child.visit(this, context);
@ -247,8 +251,7 @@ class _Visitor implements html.Visitor {
});
}
} else {
if (i18nAttr || isImplicitI18n) {
// TODO(vicb): we should probably allow nested implicit element (ie <div>)
if (i18nAttr || isTopLevelImplicit) {
this._reportError(
el, 'Could not mark an element as translatable inside a translatable section');
}
@ -276,6 +279,7 @@ class _Visitor implements html.Visitor {
this._depth--;
this._inI18nNode = wasInI18nNode;
this._inImplicitNode = wasInImplicitNode;
if (this._mode === _VisitorMode.Merge) {
// There are no childNodes in translatable sections - those nodes will be replace anyway
@ -299,6 +303,7 @@ class _Visitor implements html.Visitor {
this._msgCountAtSectionStart = void 0;
this._errors = [];
this._messages = [];
this._inImplicitNode = false;
this._createI18nMessage = createI18nMessageFactory(interpolationConfig);
}

View File

@ -213,6 +213,17 @@ export function main() {
[['bold'], '', ''],
]);
});
it('should allow nested implicit elements', () => {
let result: any[];
expect(() => {result = extract('<div>outer<div>inner</div></div>', ['div'])}).not.toThrow();
expect(result).toEqual([
[['outer', '<ph tag name="START_TAG_DIV">inner</ph name="CLOSE_TAG_DIV">'], '', ''],
]);
});
});
describe('implicit attributes', () => {
@ -288,12 +299,6 @@ export function main() {
});
describe('implicit elements', () => {
it('should report nested implicit elements', () => {
expect(extractErrors(`<p><b></b></p>`, ['p', 'b'])).toEqual([
['Could not mark an element as translatable inside a translatable section', '<b>'],
]);
});
it('should report implicit element in translatable element', () => {
expect(extractErrors(`<p i18n><b></b></p>`, ['b'])).toEqual([
['Could not mark an element as translatable inside a translatable section', '<b>'],
@ -358,7 +363,7 @@ function parseHtml(html: string): html.Node[] {
const htmlParser = new HtmlParser();
const parseResult = htmlParser.parse(html, 'extractor spec', true);
if (parseResult.errors.length > 1) {
throw Error(`unexpected parse errors: ${parseResult.errors.join('\n')}`);
throw new Error(`unexpected parse errors: ${parseResult.errors.join('\n')}`);
}
return parseResult.rootNodes;
}
@ -390,13 +395,16 @@ function fakeTranslate(
function extract(
html: string, implicitTags: string[] = [],
implicitAttrs: {[k: string]: string[]} = {}): [string[], string, string][] {
const messages =
extractMessages(parseHtml(html), DEFAULT_INTERPOLATION_CONFIG, implicitTags, implicitAttrs)
.messages;
const result =
extractMessages(parseHtml(html), DEFAULT_INTERPOLATION_CONFIG, implicitTags, implicitAttrs);
if (result.errors.length > 0) {
throw new Error(`unexpected errors: ${result.errors.join('\n')}`);
}
// clang-format off
// https://github.com/angular/clang-format/issues/35
return messages.map(
return result.messages.map(
message => [serializeI18nNodes(message.nodes), message.meaning, message.description, ]) as [string[], string, string][];
// clang-format on
}