)
this._reportError(
el, 'Could not mark an element as translatable inside a translatable section');
}
}
this._extractFromAttributes(el, messages);
if (useSection) {
this._startSection(messages);
htmlVisitAll(this, el.children, messages);
this._endSection(messages, el.children);
} else {
htmlVisitAll(this, el.children, messages);
}
this._depth--;
this._inI18nNode = wasInI18nNode;
}
visitAttr(ast: HtmlAttrAst, messages: AstMessage[]): any { throw new Error('unreachable code'); }
private _extractFromAttributes(el: HtmlElementAst, messages: AstMessage[]): void {
const explicitAttrNameToValue: Map
= new Map();
const implicitAttrNames: string[] = this._implicitAttrs[el.name] || [];
el.attrs.filter(attr => attr.name.startsWith(I18N_ATTR_PREFIX))
.forEach(
attr => explicitAttrNameToValue.set(
attr.name.substring(I18N_ATTR_PREFIX.length), attr.value));
el.attrs.forEach(attr => {
if (explicitAttrNameToValue.has(attr.name)) {
this._addMessage(messages, [attr], explicitAttrNameToValue.get(attr.name));
} else if (implicitAttrNames.some(name => attr.name === name)) {
this._addMessage(messages, [attr]);
}
});
}
private _addMessage(messages: AstMessage[], ast: HtmlAst[], meaningAndDesc?: string): void {
if (ast.length == 0 ||
ast.length == 1 && ast[0] instanceof HtmlAttrAst && !(ast[0]).value) {
// Do not create empty messages
return;
}
messages.push(new AstMessage(ast, meaning(meaningAndDesc), description(meaningAndDesc)));
}
/**
* Add the node as a child of the block when:
* - we are in a block,
* - we are not inside a ICU message (those are handled separately),
* - the node is a "direct child" of the block
*/
private _mayBeAddBlockChildren(ast: HtmlAst): void {
if (this._inI18nBlock && !this._inIcu && this._depth == this._blockStartDepth) {
this._blockChildren.push(ast);
}
}
/**
* Marks the start of a section, see `_endSection`
*/
private _startSection(messages: AstMessage[]): void {
if (this._sectionStartIndex !== void 0) {
throw new Error('Unexpected section start');
}
this._sectionStartIndex = messages.length;
}
/**
* Terminates a section.
*
* If a section has only one significant children (comments not significant) then we should not
* keep the message
* from this children:
*
* `{ICU message}
` would produce two messages:
* - one for the content with meaning and description,
* - another one for the ICU message.
*
* In this case the last message is discarded as it contains less information (the AST is
* otherwise identical).
*
* Note that we should still keep messages extracted from attributes inside the section (ie in the
* ICU message here)
*/
private _endSection(messages: AstMessage[], directChildren: HtmlAst[]): void {
if (this._sectionStartIndex === void 0) {
throw new Error('Unexpected section end');
}
const startIndex = this._sectionStartIndex;
const significantChildren: number = directChildren.reduce(
(count: number, node: HtmlAst): number => count + (node instanceof HtmlCommentAst ? 0 : 1),
0);
if (significantChildren == 1) {
for (let i = startIndex; i < messages.length; i++) {
let ast = messages[i].nodes;
if (!(ast.length == 1 && ast[0] instanceof HtmlAttrAst)) {
messages.splice(i, 1);
break;
}
}
}
this._sectionStartIndex = void 0;
}
private _reportError(astNode: HtmlAst, msg: string): void {
this._errors.push(new I18nError(astNode.sourceSpan, msg));
}
}
export class AstMessage {
constructor(public nodes: HtmlAst[], public meaning: string, public description: string) {}
}