fix(i18n): translate attributes inside elements marked for translation
This commit is contained in:

committed by
Matias Niemelä

parent
1c929ae244
commit
d7f2a3c71b
@ -53,20 +53,22 @@ enum _VisitorMode {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
class _Visitor implements html.Visitor {
|
class _Visitor implements html.Visitor {
|
||||||
|
private _depth: number;
|
||||||
|
|
||||||
// <el i18n>...</el>
|
// <el i18n>...</el>
|
||||||
private _inI18nNode: boolean;
|
private _inI18nNode: boolean;
|
||||||
private _depth: number;
|
|
||||||
private _inImplicitNode: boolean;
|
private _inImplicitNode: boolean;
|
||||||
|
|
||||||
// <!--i18n-->...<!--/i18n-->
|
// <!--i18n-->...<!--/i18n-->
|
||||||
|
private _inI18nBlock: boolean;
|
||||||
private _blockMeaningAndDesc: string;
|
private _blockMeaningAndDesc: string;
|
||||||
private _blockChildren: html.Node[];
|
private _blockChildren: html.Node[];
|
||||||
private _blockStartDepth: number;
|
private _blockStartDepth: number;
|
||||||
private _inI18nBlock: boolean;
|
|
||||||
|
|
||||||
// {<icu message>}
|
// {<icu message>}
|
||||||
private _inIcu: boolean;
|
private _inIcu: boolean;
|
||||||
|
|
||||||
|
// set to void 0 when not in a section
|
||||||
private _msgCountAtSectionStart: number;
|
private _msgCountAtSectionStart: number;
|
||||||
private _errors: I18nError[];
|
private _errors: I18nError[];
|
||||||
private _mode: _VisitorMode;
|
private _mode: _VisitorMode;
|
||||||
@ -208,50 +210,31 @@ class _Visitor implements html.Visitor {
|
|||||||
this._depth++;
|
this._depth++;
|
||||||
const wasInI18nNode = this._inI18nNode;
|
const wasInI18nNode = this._inI18nNode;
|
||||||
const wasInImplicitNode = this._inImplicitNode;
|
const wasInImplicitNode = this._inImplicitNode;
|
||||||
let childNodes: html.Node[];
|
let childNodes: html.Node[] = [];
|
||||||
|
let translatedChildNodes: html.Node[];
|
||||||
|
|
||||||
// Extract only top level nodes with the (implicit) "i18n" attribute if not in a block or an ICU
|
// Extract:
|
||||||
// message
|
// - top level nodes with the (implicit) "i18n" attribute if not already in a section
|
||||||
|
// - ICU messages
|
||||||
const i18nAttr = _getI18nAttr(el);
|
const i18nAttr = _getI18nAttr(el);
|
||||||
|
const i18nMeta = i18nAttr ? i18nAttr.value : '';
|
||||||
const isImplicit = this._implicitTags.some(tag => el.name === tag) && !this._inIcu &&
|
const isImplicit = this._implicitTags.some(tag => el.name === tag) && !this._inIcu &&
|
||||||
!this._isInTranslatableSection;
|
!this._isInTranslatableSection;
|
||||||
const isTopLevelImplicit = !wasInImplicitNode && isImplicit;
|
const isTopLevelImplicit = !wasInImplicitNode && isImplicit;
|
||||||
this._inImplicitNode = this._inImplicitNode || isImplicit;
|
this._inImplicitNode = wasInImplicitNode || isImplicit;
|
||||||
|
|
||||||
if (!this._isInTranslatableSection && !this._inIcu) {
|
if (!this._isInTranslatableSection && !this._inIcu) {
|
||||||
if (i18nAttr) {
|
if (i18nAttr || isTopLevelImplicit) {
|
||||||
// explicit translation
|
|
||||||
this._inI18nNode = true;
|
this._inI18nNode = true;
|
||||||
const message = this._addMessage(el.children, i18nAttr.value);
|
const message = this._addMessage(el.children, i18nMeta);
|
||||||
childNodes = this._translateMessage(el, message);
|
translatedChildNodes = this._translateMessage(el, message);
|
||||||
} else if (isTopLevelImplicit) {
|
|
||||||
// implicit translation
|
|
||||||
this._inI18nNode = true;
|
|
||||||
const message = this._addMessage(el.children);
|
|
||||||
childNodes = this._translateMessage(el, message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._mode == _VisitorMode.Extract) {
|
if (this._mode == _VisitorMode.Extract) {
|
||||||
const isTranslatable = i18nAttr || isTopLevelImplicit;
|
const isTranslatable = i18nAttr || isTopLevelImplicit;
|
||||||
if (isTranslatable) {
|
if (isTranslatable) this._openTranslatableSection(el);
|
||||||
this._openTranslatableSection(el);
|
|
||||||
}
|
|
||||||
html.visitAll(this, el.children);
|
html.visitAll(this, el.children);
|
||||||
if (isTranslatable) {
|
if (isTranslatable) this._closeTranslatableSection(el, el.children);
|
||||||
this._closeTranslatableSection(el, el.children);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._mode === _VisitorMode.Merge && !i18nAttr && !isTopLevelImplicit) {
|
|
||||||
childNodes = [];
|
|
||||||
el.children.forEach(child => {
|
|
||||||
const visited = child.visit(this, context);
|
|
||||||
if (visited && !this._isInTranslatableSection) {
|
|
||||||
// Do not add the children from translatable sections (= i18n blocks here)
|
|
||||||
// They will be added when the section is close (i.e. on `<!-- /i18n -->`)
|
|
||||||
childNodes = childNodes.concat(visited);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (i18nAttr || isTopLevelImplicit) {
|
if (i18nAttr || isTopLevelImplicit) {
|
||||||
@ -263,19 +246,18 @@ class _Visitor implements html.Visitor {
|
|||||||
// Descend into child nodes for extraction
|
// Descend into child nodes for extraction
|
||||||
html.visitAll(this, el.children);
|
html.visitAll(this, el.children);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this._mode == _VisitorMode.Merge) {
|
if (this._mode === _VisitorMode.Merge) {
|
||||||
// Translate attributes in ICU messages
|
const visitNodes = translatedChildNodes || el.children;
|
||||||
childNodes = [];
|
visitNodes.forEach(child => {
|
||||||
el.children.forEach(child => {
|
const visited = child.visit(this, context);
|
||||||
const visited = child.visit(this, context);
|
if (visited && !this._isInTranslatableSection) {
|
||||||
if (visited && !this._isInTranslatableSection) {
|
// Do not add the children from translatable sections (= i18n blocks here)
|
||||||
// Do not add the children from translatable sections (= i18n blocks here)
|
// They will be added later in this loop when the block closes (i.e. on `<!-- /i18n -->`)
|
||||||
// They will be added when the section is close (i.e. on `<!-- /i18n -->`)
|
childNodes = childNodes.concat(visited);
|
||||||
childNodes = childNodes.concat(visited);
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._visitAttributesOf(el);
|
this._visitAttributesOf(el);
|
||||||
@ -285,7 +267,6 @@ class _Visitor implements html.Visitor {
|
|||||||
this._inImplicitNode = wasInImplicitNode;
|
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
|
|
||||||
const translatedAttrs = this._translateAttributes(el);
|
const translatedAttrs = this._translateAttributes(el);
|
||||||
return new html.Element(
|
return new html.Element(
|
||||||
el.name, translatedAttrs, childNodes, el.sourceSpan, el.startSourceSpan,
|
el.name, translatedAttrs, childNodes, el.sourceSpan, el.startSourceSpan,
|
||||||
@ -432,7 +413,7 @@ class _Visitor implements html.Visitor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A translatable section could be:
|
* A translatable section could be:
|
||||||
* - a translatable element,
|
* - the content of translatable element,
|
||||||
* - nodes between `<!-- i18n -->` and `<!-- /i18n -->` comments
|
* - nodes between `<!-- i18n -->` and `<!-- /i18n -->` comments
|
||||||
*/
|
*/
|
||||||
private get _isInTranslatableSection(): boolean {
|
private get _isInTranslatableSection(): boolean {
|
||||||
|
@ -210,5 +210,56 @@ const XMB = `
|
|||||||
<ph name="START_TAG_DIV_1"><ex><div></ex></ph><ph name="ICU"/><ph name="CLOSE_TAG_DIV"><ex></div></ex></ph>
|
<ph name="START_TAG_DIV_1"><ex><div></ex></ph><ph name="ICU"/><ph name="CLOSE_TAG_DIV"><ex></div></ex></ph>
|
||||||
</msg>
|
</msg>
|
||||||
<msg id="1491627405349178954">it <ph name="START_BOLD_TEXT"><ex><b></ex></ph>should<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> work</msg>
|
<msg id="1491627405349178954">it <ph name="START_BOLD_TEXT"><ex><b></ex></ph>should<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> work</msg>
|
||||||
</messagebundle>
|
</messagebundle>`;
|
||||||
|
|
||||||
|
const HTML = `
|
||||||
|
<div>
|
||||||
|
<h1 i18n>i18n attribute on tags</h1>
|
||||||
|
|
||||||
|
<div id="i18n-1"><p i18n>nested</p></div>
|
||||||
|
|
||||||
|
<div id="i18n-2"><p i18n="different meaning|">nested</p></div>
|
||||||
|
|
||||||
|
<div id="i18n-3"><p i18n><i>with placeholders</i></p></div>
|
||||||
|
<div id="i18n-3b"><p i18n><i class="preserved-on-placeholders">with placeholders</i></p></div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p id="i18n-4" i18n-title title="on not translatable node"></p>
|
||||||
|
<p id="i18n-5" i18n i18n-title title="on translatable node"></p>
|
||||||
|
<p id="i18n-6" i18n-title title></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- no ph below because the ICU node is the only child of the div, i.e. no text nodes -->
|
||||||
|
<div i18n id="i18n-7">{count, plural, =0 {zero} =1 {one} =2 {two} other {<b>many</b>}}</div>
|
||||||
|
|
||||||
|
<div i18n id="i18n-8">
|
||||||
|
{sex, select, m {male} f {female}}
|
||||||
|
</div>
|
||||||
|
<div i18n id="i18n-8b">
|
||||||
|
{sexB, select, m {male} f {female}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div i18n id="i18n-9">{{ "count = " + count }}</div>
|
||||||
|
<div i18n id="i18n-10">sex = {{ sex }}</div>
|
||||||
|
<div i18n id="i18n-11">{{ "custom name" //i18n(ph="CUSTOM_NAME") }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- i18n -->
|
||||||
|
<h1 id="i18n-12" >Markers in html comments</h1>
|
||||||
|
<div id="i18n-13" i18n-title title="in a translatable section"></div>
|
||||||
|
<div id="i18n-14">{count, plural, =0 {zero} =1 {one} =2 {two} other {<b>many</b>}}</div>
|
||||||
|
<!-- /i18n -->
|
||||||
|
|
||||||
|
<div id="i18n-15"><ng-container i18n>it <b>should</b> work</ng-container></div>
|
||||||
|
|
||||||
|
<!-- make sure that ICU messages are not treated as text nodes -->
|
||||||
|
<div i18n="desc">{
|
||||||
|
response.getItemsList().length,
|
||||||
|
plural,
|
||||||
|
=0 {Found no results}
|
||||||
|
=1 {Found one result}
|
||||||
|
other {Found {{response.getItemsList().length}} results}
|
||||||
|
}</div>
|
||||||
|
|
||||||
|
<div i18n id="i18n-18">foo<a i18n-title title="in a translatable section">bar</a></div>
|
||||||
`;
|
`;
|
||||||
|
Reference in New Issue
Block a user