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 {
|
||||
// <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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue