feat(ExtractorMerger): allow nested implicit tags
This commit is contained in:
parent
01bca41168
commit
1b04d70626
|
@ -54,8 +54,9 @@ enum _VisitorMode {
|
||||||
*/
|
*/
|
||||||
class _Visitor implements html.Visitor {
|
class _Visitor implements html.Visitor {
|
||||||
// <el i18n>...</el>
|
// <el i18n>...</el>
|
||||||
private _inI18nNode = false;
|
private _inI18nNode: boolean;
|
||||||
private _depth: number = 0;
|
private _depth: number;
|
||||||
|
private _inImplicitNode: boolean;
|
||||||
|
|
||||||
// <!--i18n-->...<!--/i18n-->
|
// <!--i18n-->...<!--/i18n-->
|
||||||
private _blockMeaningAndDesc: string;
|
private _blockMeaningAndDesc: string;
|
||||||
|
@ -64,7 +65,7 @@ class _Visitor implements html.Visitor {
|
||||||
private _inI18nBlock: boolean;
|
private _inI18nBlock: boolean;
|
||||||
|
|
||||||
// {<icu message>}
|
// {<icu message>}
|
||||||
private _inIcu = false;
|
private _inIcu: boolean;
|
||||||
|
|
||||||
private _msgCountAtSectionStart: number;
|
private _msgCountAtSectionStart: number;
|
||||||
private _errors: I18nError[];
|
private _errors: I18nError[];
|
||||||
|
@ -204,12 +205,15 @@ class _Visitor implements html.Visitor {
|
||||||
this._mayBeAddBlockChildren(el);
|
this._mayBeAddBlockChildren(el);
|
||||||
this._depth++;
|
this._depth++;
|
||||||
const wasInI18nNode = this._inI18nNode;
|
const wasInI18nNode = this._inI18nNode;
|
||||||
|
const wasInImplicitNode = this._inImplicitNode;
|
||||||
let childNodes: html.Node[];
|
let childNodes: html.Node[];
|
||||||
|
|
||||||
// Extract only top level nodes with the (implicit) "i18n" attribute if not in a block or an ICU
|
// Extract only top level nodes with the (implicit) "i18n" attribute if not in a block or an ICU
|
||||||
// message
|
// message
|
||||||
const i18nAttr = _getI18nAttr(el);
|
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 (!this._isInTranslatableSection && !this._inIcu) {
|
||||||
if (i18nAttr) {
|
if (i18nAttr) {
|
||||||
|
@ -217,7 +221,7 @@ class _Visitor implements html.Visitor {
|
||||||
this._inI18nNode = true;
|
this._inI18nNode = true;
|
||||||
const message = this._addMessage(el.children, i18nAttr.value);
|
const message = this._addMessage(el.children, i18nAttr.value);
|
||||||
childNodes = this._translateMessage(el, message);
|
childNodes = this._translateMessage(el, message);
|
||||||
} else if (isImplicitI18n) {
|
} else if (isTopLevelImplicit) {
|
||||||
// implicit translation
|
// implicit translation
|
||||||
this._inI18nNode = true;
|
this._inI18nNode = true;
|
||||||
const message = this._addMessage(el.children);
|
const message = this._addMessage(el.children);
|
||||||
|
@ -225,7 +229,7 @@ class _Visitor implements html.Visitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._mode == _VisitorMode.Extract) {
|
if (this._mode == _VisitorMode.Extract) {
|
||||||
const isTranslatable = i18nAttr || isImplicitI18n;
|
const isTranslatable = i18nAttr || isTopLevelImplicit;
|
||||||
if (isTranslatable) {
|
if (isTranslatable) {
|
||||||
this._openTranslatableSection(el);
|
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 = [];
|
childNodes = [];
|
||||||
el.children.forEach(child => {
|
el.children.forEach(child => {
|
||||||
const visited = child.visit(this, context);
|
const visited = child.visit(this, context);
|
||||||
|
@ -247,8 +251,7 @@ class _Visitor implements html.Visitor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (i18nAttr || isImplicitI18n) {
|
if (i18nAttr || isTopLevelImplicit) {
|
||||||
// TODO(vicb): we should probably allow nested implicit element (ie <div>)
|
|
||||||
this._reportError(
|
this._reportError(
|
||||||
el, 'Could not mark an element as translatable inside a translatable section');
|
el, 'Could not mark an element as translatable inside a translatable section');
|
||||||
}
|
}
|
||||||
|
@ -276,6 +279,7 @@ class _Visitor implements html.Visitor {
|
||||||
|
|
||||||
this._depth--;
|
this._depth--;
|
||||||
this._inI18nNode = wasInI18nNode;
|
this._inI18nNode = wasInI18nNode;
|
||||||
|
this._inImplicitNode = wasInImplicitNode;
|
||||||
|
|
||||||
if (this._mode === _VisitorMode.Merge) {
|
if (this._mode === _VisitorMode.Merge) {
|
||||||
// There are no childNodes in translatable sections - those nodes will be replace anyway
|
// 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._msgCountAtSectionStart = void 0;
|
||||||
this._errors = [];
|
this._errors = [];
|
||||||
this._messages = [];
|
this._messages = [];
|
||||||
|
this._inImplicitNode = false;
|
||||||
this._createI18nMessage = createI18nMessageFactory(interpolationConfig);
|
this._createI18nMessage = createI18nMessageFactory(interpolationConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -213,6 +213,17 @@ export function main() {
|
||||||
[['bold'], '', ''],
|
[['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', () => {
|
describe('implicit attributes', () => {
|
||||||
|
@ -288,12 +299,6 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('implicit elements', () => {
|
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', () => {
|
it('should report implicit element in translatable element', () => {
|
||||||
expect(extractErrors(`<p i18n><b></b></p>`, ['b'])).toEqual([
|
expect(extractErrors(`<p i18n><b></b></p>`, ['b'])).toEqual([
|
||||||
['Could not mark an element as translatable inside a translatable section', '<b>'],
|
['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 htmlParser = new HtmlParser();
|
||||||
const parseResult = htmlParser.parse(html, 'extractor spec', true);
|
const parseResult = htmlParser.parse(html, 'extractor spec', true);
|
||||||
if (parseResult.errors.length > 1) {
|
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;
|
return parseResult.rootNodes;
|
||||||
}
|
}
|
||||||
|
@ -390,13 +395,16 @@ function fakeTranslate(
|
||||||
function extract(
|
function extract(
|
||||||
html: string, implicitTags: string[] = [],
|
html: string, implicitTags: string[] = [],
|
||||||
implicitAttrs: {[k: string]: string[]} = {}): [string[], string, string][] {
|
implicitAttrs: {[k: string]: string[]} = {}): [string[], string, string][] {
|
||||||
const messages =
|
const result =
|
||||||
extractMessages(parseHtml(html), DEFAULT_INTERPOLATION_CONFIG, implicitTags, implicitAttrs)
|
extractMessages(parseHtml(html), DEFAULT_INTERPOLATION_CONFIG, implicitTags, implicitAttrs);
|
||||||
.messages;
|
|
||||||
|
if (result.errors.length > 0) {
|
||||||
|
throw new Error(`unexpected errors: ${result.errors.join('\n')}`);
|
||||||
|
}
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
// https://github.com/angular/clang-format/issues/35
|
// 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][];
|
message => [serializeI18nNodes(message.nodes), message.meaning, message.description, ]) as [string[], string, string][];
|
||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue