fix(ivy): support for multiple ICU expressions in the same i18n block (#28083)
There were two issues with multiple ICU expressions in the same i18n block: - the regexp that was used to parse the text wasn't able to handle multiple ICU expressions, I've replaced it with parsing the text and searching for brackets (which is what we ended up doing in the end anyway) - we allocate node indexes for nodes generated by the ICU expressions which increases the expando value, but we would create the nodes for those cases during the update phase. In the mean time we would create some nodes during the creation phase (comment nodes for ICU expressions, text nodes, ...) with an auto increment index. This means that any node created after an ICU expression would get the following index value, but the ICU case nodes expected to use the same index as well... There was a mismatch between the auto generated index, and the expected index which was causing problems when we needed to select those nodes for updates later on. To fix it, I've added the expected node index to the list of mutate codes that we generate, and we do not use an auto increment value anymore. FW-905 #resolve PR Close #28083
This commit is contained in:
parent
47665c9937
commit
c61ea1d5bd
|
@ -395,21 +395,19 @@ function i18nStartFirstPass(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Even indexes are text (including bindings & ICU expressions)
|
// Even indexes are text (including bindings & ICU expressions)
|
||||||
const parts = value.split(ICU_REGEXP);
|
const parts = extractParts(value);
|
||||||
for (let j = 0; j < parts.length; j++) {
|
for (let j = 0; j < parts.length; j++) {
|
||||||
value = parts[j];
|
|
||||||
|
|
||||||
if (j & 1) {
|
if (j & 1) {
|
||||||
// Odd indexes are ICU expressions
|
// Odd indexes are ICU expressions
|
||||||
// Create the comment node that will anchor the ICU expression
|
// Create the comment node that will anchor the ICU expression
|
||||||
allocExpando(viewData);
|
allocExpando(viewData);
|
||||||
const icuNodeIndex = tView.blueprint.length - 1 - HEADER_OFFSET;
|
const icuNodeIndex = tView.blueprint.length - 1 - HEADER_OFFSET;
|
||||||
createOpCodes.push(
|
createOpCodes.push(
|
||||||
COMMENT_MARKER, ngDevMode ? `ICU ${icuNodeIndex}` : '',
|
COMMENT_MARKER, ngDevMode ? `ICU ${icuNodeIndex}` : '', icuNodeIndex,
|
||||||
parentIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild);
|
parentIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild);
|
||||||
|
|
||||||
// Update codes for the ICU expression
|
// Update codes for the ICU expression
|
||||||
const icuExpression = parseICUBlock(value.substr(1, value.length - 2));
|
const icuExpression = parts[j] as IcuExpression;
|
||||||
const mask = getBindingMask(icuExpression);
|
const mask = getBindingMask(icuExpression);
|
||||||
icuStart(icuExpressions, icuExpression, icuNodeIndex, icuNodeIndex);
|
icuStart(icuExpressions, icuExpression, icuNodeIndex, icuNodeIndex);
|
||||||
// Since this is recursive, the last TIcu that was pushed is the one we want
|
// Since this is recursive, the last TIcu that was pushed is the one we want
|
||||||
|
@ -422,19 +420,21 @@ function i18nStartFirstPass(
|
||||||
mask, // mask of all the bindings of this ICU expression
|
mask, // mask of all the bindings of this ICU expression
|
||||||
2, // skip 2 opCodes if not changed
|
2, // skip 2 opCodes if not changed
|
||||||
icuNodeIndex << I18nUpdateOpCode.SHIFT_REF | I18nUpdateOpCode.IcuUpdate, tIcuIndex);
|
icuNodeIndex << I18nUpdateOpCode.SHIFT_REF | I18nUpdateOpCode.IcuUpdate, tIcuIndex);
|
||||||
} else if (value !== '') {
|
} else if (parts[j] !== '') {
|
||||||
|
const text = parts[j] as string;
|
||||||
// Even indexes are text (including bindings)
|
// Even indexes are text (including bindings)
|
||||||
const hasBinding = value.match(BINDING_REGEXP);
|
const hasBinding = text.match(BINDING_REGEXP);
|
||||||
// Create text nodes
|
// Create text nodes
|
||||||
allocExpando(viewData);
|
allocExpando(viewData);
|
||||||
|
const textNodeIndex = tView.blueprint.length - 1 - HEADER_OFFSET;
|
||||||
createOpCodes.push(
|
createOpCodes.push(
|
||||||
// If there is a binding, the value will be set during update
|
// If there is a binding, the value will be set during update
|
||||||
hasBinding ? '' : value,
|
hasBinding ? '' : text, textNodeIndex,
|
||||||
parentIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild);
|
parentIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild);
|
||||||
|
|
||||||
if (hasBinding) {
|
if (hasBinding) {
|
||||||
addAllToArray(
|
addAllToArray(
|
||||||
generateBindingUpdateOpCodes(value, tView.blueprint.length - 1 - HEADER_OFFSET),
|
generateBindingUpdateOpCodes(text, tView.blueprint.length - 1 - HEADER_OFFSET),
|
||||||
updateOpCodes);
|
updateOpCodes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -580,8 +580,7 @@ function i18nEndFirstPass(tView: TView) {
|
||||||
|
|
||||||
// The last placeholder that was added before `i18nEnd`
|
// The last placeholder that was added before `i18nEnd`
|
||||||
const previousOrParentTNode = getPreviousOrParentTNode();
|
const previousOrParentTNode = getPreviousOrParentTNode();
|
||||||
const visitedPlaceholders =
|
const visitedPlaceholders = readCreateOpCodes(rootIndex, tI18n.create, tI18n.icus, viewData);
|
||||||
readCreateOpCodes(rootIndex, tI18n.create, tI18n.expandoStartIndex, viewData);
|
|
||||||
|
|
||||||
// Remove deleted placeholders
|
// Remove deleted placeholders
|
||||||
// The last placeholder that was added before `i18nEnd` is `previousOrParentTNode`
|
// The last placeholder that was added before `i18nEnd` is `previousOrParentTNode`
|
||||||
|
@ -593,7 +592,7 @@ function i18nEndFirstPass(tView: TView) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function readCreateOpCodes(
|
function readCreateOpCodes(
|
||||||
index: number, createOpCodes: I18nMutateOpCodes, expandoStartIndex: number,
|
index: number, createOpCodes: I18nMutateOpCodes, icus: TIcu[] | null,
|
||||||
viewData: LView): number[] {
|
viewData: LView): number[] {
|
||||||
const renderer = getLView()[RENDERER];
|
const renderer = getLView()[RENDERER];
|
||||||
let currentTNode: TNode|null = null;
|
let currentTNode: TNode|null = null;
|
||||||
|
@ -603,10 +602,10 @@ function readCreateOpCodes(
|
||||||
const opCode = createOpCodes[i];
|
const opCode = createOpCodes[i];
|
||||||
if (typeof opCode == 'string') {
|
if (typeof opCode == 'string') {
|
||||||
const textRNode = createTextNode(opCode, renderer);
|
const textRNode = createTextNode(opCode, renderer);
|
||||||
|
const textNodeIndex = createOpCodes[++i] as number;
|
||||||
ngDevMode && ngDevMode.rendererCreateTextNode++;
|
ngDevMode && ngDevMode.rendererCreateTextNode++;
|
||||||
previousTNode = currentTNode;
|
previousTNode = currentTNode;
|
||||||
currentTNode =
|
currentTNode = createNodeAtIndex(textNodeIndex, TNodeType.Element, textRNode, null, null);
|
||||||
createNodeAtIndex(expandoStartIndex++, TNodeType.Element, textRNode, null, null);
|
|
||||||
setIsParent(false);
|
setIsParent(false);
|
||||||
} else if (typeof opCode == 'number') {
|
} else if (typeof opCode == 'number') {
|
||||||
switch (opCode & I18nMutateOpCode.MASK_OPCODE) {
|
switch (opCode & I18nMutateOpCode.MASK_OPCODE) {
|
||||||
|
@ -658,14 +657,15 @@ function readCreateOpCodes(
|
||||||
switch (opCode) {
|
switch (opCode) {
|
||||||
case COMMENT_MARKER:
|
case COMMENT_MARKER:
|
||||||
const commentValue = createOpCodes[++i] as string;
|
const commentValue = createOpCodes[++i] as string;
|
||||||
|
const commentNodeIndex = createOpCodes[++i] as number;
|
||||||
ngDevMode && assertEqual(
|
ngDevMode && assertEqual(
|
||||||
typeof commentValue, 'string',
|
typeof commentValue, 'string',
|
||||||
`Expected "${commentValue}" to be a comment node value`);
|
`Expected "${commentValue}" to be a comment node value`);
|
||||||
const commentRNode = renderer.createComment(commentValue);
|
const commentRNode = renderer.createComment(commentValue);
|
||||||
ngDevMode && ngDevMode.rendererCreateComment++;
|
ngDevMode && ngDevMode.rendererCreateComment++;
|
||||||
previousTNode = currentTNode;
|
previousTNode = currentTNode;
|
||||||
currentTNode = createNodeAtIndex(
|
currentTNode =
|
||||||
expandoStartIndex++, TNodeType.IcuContainer, commentRNode, null, null);
|
createNodeAtIndex(commentNodeIndex, TNodeType.IcuContainer, commentRNode, null, null);
|
||||||
attachPatchData(commentRNode, viewData);
|
attachPatchData(commentRNode, viewData);
|
||||||
(currentTNode as TIcuContainerNode).activeCaseIndex = null;
|
(currentTNode as TIcuContainerNode).activeCaseIndex = null;
|
||||||
// We will add the case nodes later, during the update phase
|
// We will add the case nodes later, during the update phase
|
||||||
|
@ -673,6 +673,7 @@ function readCreateOpCodes(
|
||||||
break;
|
break;
|
||||||
case ELEMENT_MARKER:
|
case ELEMENT_MARKER:
|
||||||
const tagNameValue = createOpCodes[++i] as string;
|
const tagNameValue = createOpCodes[++i] as string;
|
||||||
|
const elementNodeIndex = createOpCodes[++i] as number;
|
||||||
ngDevMode && assertEqual(
|
ngDevMode && assertEqual(
|
||||||
typeof tagNameValue, 'string',
|
typeof tagNameValue, 'string',
|
||||||
`Expected "${tagNameValue}" to be an element node tag name`);
|
`Expected "${tagNameValue}" to be an element node tag name`);
|
||||||
|
@ -680,7 +681,7 @@ function readCreateOpCodes(
|
||||||
ngDevMode && ngDevMode.rendererCreateElement++;
|
ngDevMode && ngDevMode.rendererCreateElement++;
|
||||||
previousTNode = currentTNode;
|
previousTNode = currentTNode;
|
||||||
currentTNode = createNodeAtIndex(
|
currentTNode = createNodeAtIndex(
|
||||||
expandoStartIndex++, TNodeType.Element, elementRNode, tagNameValue, null);
|
elementNodeIndex, TNodeType.Element, elementRNode, tagNameValue, null);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unable to determine the type of mutate operation for "${opCode}"`);
|
throw new Error(`Unable to determine the type of mutate operation for "${opCode}"`);
|
||||||
|
@ -759,7 +760,7 @@ function readUpdateOpCodes(
|
||||||
icuTNode.activeCaseIndex = caseIndex !== -1 ? caseIndex : null;
|
icuTNode.activeCaseIndex = caseIndex !== -1 ? caseIndex : null;
|
||||||
|
|
||||||
// Add the nodes for the new case
|
// Add the nodes for the new case
|
||||||
readCreateOpCodes(-1, tIcu.create[caseIndex], tIcu.expandoStartIndex, viewData);
|
readCreateOpCodes(-1, tIcu.create[caseIndex], icus, viewData);
|
||||||
caseCreated = true;
|
caseCreated = true;
|
||||||
break;
|
break;
|
||||||
case I18nUpdateOpCode.IcuUpdate:
|
case I18nUpdateOpCode.IcuUpdate:
|
||||||
|
@ -1400,7 +1401,7 @@ function parseNodes(
|
||||||
icuCase.vars--;
|
icuCase.vars--;
|
||||||
} else {
|
} else {
|
||||||
icuCase.create.push(
|
icuCase.create.push(
|
||||||
ELEMENT_MARKER, tagName,
|
ELEMENT_MARKER, tagName, newIndex,
|
||||||
parentIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild);
|
parentIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild);
|
||||||
const elAttrs = element.attributes;
|
const elAttrs = element.attributes;
|
||||||
for (let i = 0; i < elAttrs.length; i++) {
|
for (let i = 0; i < elAttrs.length; i++) {
|
||||||
|
@ -1446,7 +1447,7 @@ function parseNodes(
|
||||||
const value = currentNode.textContent || '';
|
const value = currentNode.textContent || '';
|
||||||
const hasBinding = value.match(BINDING_REGEXP);
|
const hasBinding = value.match(BINDING_REGEXP);
|
||||||
icuCase.create.push(
|
icuCase.create.push(
|
||||||
hasBinding ? '' : value,
|
hasBinding ? '' : value, newIndex,
|
||||||
parentIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild);
|
parentIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild);
|
||||||
icuCase.remove.push(newIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove);
|
icuCase.remove.push(newIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove);
|
||||||
if (hasBinding) {
|
if (hasBinding) {
|
||||||
|
@ -1461,7 +1462,7 @@ function parseNodes(
|
||||||
const newLocal = ngDevMode ? `nested ICU ${nestedIcuIndex}` : '';
|
const newLocal = ngDevMode ? `nested ICU ${nestedIcuIndex}` : '';
|
||||||
// Create the comment node that will anchor the ICU expression
|
// Create the comment node that will anchor the ICU expression
|
||||||
icuCase.create.push(
|
icuCase.create.push(
|
||||||
COMMENT_MARKER, newLocal,
|
COMMENT_MARKER, newLocal, newIndex,
|
||||||
parentIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild);
|
parentIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild);
|
||||||
const nestedIcu = nestedIcus[nestedIcuIndex];
|
const nestedIcu = nestedIcus[nestedIcuIndex];
|
||||||
nestedIcusToCreate.push([nestedIcu, newIndex]);
|
nestedIcusToCreate.push([nestedIcu, newIndex]);
|
||||||
|
|
|
@ -50,6 +50,8 @@ const TRANSLATIONS: any = {
|
||||||
'{$startTagNgTemplate}{$startTagSpan}Bonjour{$closeTagSpan}{$closeTagNgTemplate}{$startTagNgContainer}{$startTagSpan_1}Bonjour{$closeTagSpan}{$closeTagNgContainer}',
|
'{$startTagNgTemplate}{$startTagSpan}Bonjour{$closeTagSpan}{$closeTagNgTemplate}{$startTagNgContainer}{$startTagSpan_1}Bonjour{$closeTagSpan}{$closeTagNgContainer}',
|
||||||
'{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}':
|
'{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}':
|
||||||
'{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autres}}',
|
'{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autres}}',
|
||||||
|
'{VAR_SELECT, select, 1 {one} 2 {two} other {more than two}}':
|
||||||
|
'{VAR_SELECT, select, 1 {un} 2 {deux} other {plus que deux}}',
|
||||||
'{VAR_SELECT, select, 10 {10 - {$startBoldText}ten{$closeBoldText}} 20 {20 - {$startItalicText}twenty{$closeItalicText}} other {{$startTagDiv}{$startUnderlinedText}other{$closeUnderlinedText}{$closeTagDiv}}}':
|
'{VAR_SELECT, select, 10 {10 - {$startBoldText}ten{$closeBoldText}} 20 {20 - {$startItalicText}twenty{$closeItalicText}} other {{$startTagDiv}{$startUnderlinedText}other{$closeUnderlinedText}{$closeTagDiv}}}':
|
||||||
'{VAR_SELECT, select, 10 {10 - {$startBoldText}dix{$closeBoldText}} 20 {20 - {$startItalicText}vingt{$closeItalicText}} other {{$startTagDiv}{$startUnderlinedText}autres{$closeUnderlinedText}{$closeTagDiv}}}',
|
'{VAR_SELECT, select, 10 {10 - {$startBoldText}dix{$closeBoldText}} 20 {20 - {$startItalicText}vingt{$closeItalicText}} other {{$startTagDiv}{$startUnderlinedText}autres{$closeUnderlinedText}{$closeTagDiv}}}',
|
||||||
'{VAR_SELECT_2, select, 10 {ten - {VAR_SELECT, select, 1 {one} 2 {two} other {more than two}}} 20 {twenty - {VAR_SELECT_1, select, 1 {one} 2 {two} other {more than two}}} other {other}}':
|
'{VAR_SELECT_2, select, 10 {ten - {VAR_SELECT, select, 1 {one} 2 {two} other {more than two}}} 20 {twenty - {VAR_SELECT_1, select, 1 {one} 2 {two} other {more than two}}} other {other}}':
|
||||||
|
@ -384,8 +386,7 @@ onlyInIvy('Ivy i18n logic').describe('i18n', function() {
|
||||||
expect(italicTags[0].innerHTML).toBe('vingt');
|
expect(italicTags[0].innerHTML).toBe('vingt');
|
||||||
});
|
});
|
||||||
|
|
||||||
fixmeIvy('FW-905: Multiple ICUs in one i18n block are not processed')
|
it('should handle multiple ICUs in one block', () => {
|
||||||
.it('should handle multiple ICUs in one block', () => {
|
|
||||||
const template = `
|
const template = `
|
||||||
<div i18n>
|
<div i18n>
|
||||||
{age, select, 10 {ten} 20 {twenty} other {other}} -
|
{age, select, 10 {ten} 20 {twenty} other {other}} -
|
||||||
|
@ -398,8 +399,7 @@ onlyInIvy('Ivy i18n logic').describe('i18n', function() {
|
||||||
expect(element).toHaveText('vingt - deux');
|
expect(element).toHaveText('vingt - deux');
|
||||||
});
|
});
|
||||||
|
|
||||||
fixmeIvy('FW-906: Multiple ICUs wrapped in HTML tags in one i18n block throw an error')
|
it('should handle multiple ICUs in one i18n block wrapped in HTML elements', () => {
|
||||||
.it('should handle multiple ICUs in one i18n block wrapped in HTML elements', () => {
|
|
||||||
const template = `
|
const template = `
|
||||||
<div i18n>
|
<div i18n>
|
||||||
<span>
|
<span>
|
||||||
|
@ -415,8 +415,8 @@ onlyInIvy('Ivy i18n logic').describe('i18n', function() {
|
||||||
const element = fixture.nativeElement.firstChild;
|
const element = fixture.nativeElement.firstChild;
|
||||||
const spans = element.getElementsByTagName('span');
|
const spans = element.getElementsByTagName('span');
|
||||||
expect(spans.length).toBe(2);
|
expect(spans.length).toBe(2);
|
||||||
expect(spans[0].innerHTML).toBe('vingt');
|
expect(spans[0]).toHaveText('vingt');
|
||||||
expect(spans[1].innerHTML).toBe('deux');
|
expect(spans[1]).toHaveText('deux');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle ICUs inside a template in i18n block', () => {
|
it('should handle ICUs inside a template in i18n block', () => {
|
||||||
|
|
|
@ -84,8 +84,10 @@ describe('Runtime i18n', () => {
|
||||||
expect(opCodes).toEqual({
|
expect(opCodes).toEqual({
|
||||||
vars: 1,
|
vars: 1,
|
||||||
expandoStartIndex: nbConsts,
|
expandoStartIndex: nbConsts,
|
||||||
create:
|
create: [
|
||||||
['simple text', index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild],
|
'simple text', nbConsts,
|
||||||
|
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild
|
||||||
|
],
|
||||||
update: [],
|
update: [],
|
||||||
icus: null
|
icus: null
|
||||||
});
|
});
|
||||||
|
@ -106,20 +108,25 @@ describe('Runtime i18n', () => {
|
||||||
expandoStartIndex: nbConsts,
|
expandoStartIndex: nbConsts,
|
||||||
create: [
|
create: [
|
||||||
'Hello ',
|
'Hello ',
|
||||||
|
nbConsts,
|
||||||
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
elementIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Select,
|
elementIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Select,
|
||||||
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
'world',
|
'world',
|
||||||
|
nbConsts + 1,
|
||||||
elementIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
elementIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
elementIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.ElementEnd,
|
elementIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.ElementEnd,
|
||||||
' and ',
|
' and ',
|
||||||
|
nbConsts + 2,
|
||||||
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
elementIndex2 << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Select,
|
elementIndex2 << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Select,
|
||||||
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
'universe',
|
'universe',
|
||||||
|
nbConsts + 3,
|
||||||
elementIndex2 << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
elementIndex2 << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
elementIndex2 << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.ElementEnd,
|
elementIndex2 << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.ElementEnd,
|
||||||
'!',
|
'!',
|
||||||
|
nbConsts + 4,
|
||||||
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
],
|
],
|
||||||
update: [],
|
update: [],
|
||||||
|
@ -136,7 +143,8 @@ describe('Runtime i18n', () => {
|
||||||
expect(opCodes).toEqual({
|
expect(opCodes).toEqual({
|
||||||
vars: 1,
|
vars: 1,
|
||||||
expandoStartIndex: nbConsts,
|
expandoStartIndex: nbConsts,
|
||||||
create: ['', index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild],
|
create:
|
||||||
|
['', nbConsts, index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild],
|
||||||
update: [
|
update: [
|
||||||
0b1, // bindings mask
|
0b1, // bindings mask
|
||||||
4, // if no update, skip 4
|
4, // if no update, skip 4
|
||||||
|
@ -157,7 +165,8 @@ describe('Runtime i18n', () => {
|
||||||
expect(opCodes).toEqual({
|
expect(opCodes).toEqual({
|
||||||
vars: 1,
|
vars: 1,
|
||||||
expandoStartIndex: nbConsts,
|
expandoStartIndex: nbConsts,
|
||||||
create: ['', index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild],
|
create:
|
||||||
|
['', nbConsts, index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild],
|
||||||
update: [
|
update: [
|
||||||
0b11, // bindings mask
|
0b11, // bindings mask
|
||||||
8, // if no update, skip 8
|
8, // if no update, skip 8
|
||||||
|
@ -192,10 +201,12 @@ describe('Runtime i18n', () => {
|
||||||
expandoStartIndex: nbConsts,
|
expandoStartIndex: nbConsts,
|
||||||
create: [
|
create: [
|
||||||
'',
|
'',
|
||||||
|
nbConsts,
|
||||||
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
2 << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Select,
|
2 << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Select,
|
||||||
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
'!',
|
'!',
|
||||||
|
nbConsts + 1,
|
||||||
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
],
|
],
|
||||||
update: [
|
update: [
|
||||||
|
@ -223,10 +234,12 @@ describe('Runtime i18n', () => {
|
||||||
spanElement << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Select,
|
spanElement << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Select,
|
||||||
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
'before',
|
'before',
|
||||||
|
nbConsts,
|
||||||
spanElement << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
spanElement << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
bElementSubTemplate << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Select,
|
bElementSubTemplate << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Select,
|
||||||
spanElement << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
spanElement << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
'after',
|
'after',
|
||||||
|
nbConsts + 1,
|
||||||
spanElement << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
spanElement << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
spanElement << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.ElementEnd,
|
spanElement << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.ElementEnd,
|
||||||
],
|
],
|
||||||
|
@ -249,6 +262,7 @@ describe('Runtime i18n', () => {
|
||||||
bElement << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Select,
|
bElement << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Select,
|
||||||
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
'middle',
|
'middle',
|
||||||
|
nbConsts,
|
||||||
bElement << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
bElement << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
bElement << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.ElementEnd,
|
bElement << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.ElementEnd,
|
||||||
],
|
],
|
||||||
|
@ -268,7 +282,7 @@ describe('Runtime i18n', () => {
|
||||||
const opCodes = getOpCodes(() => { i18nStart(index, MSG_DIV); }, null, nbConsts, index);
|
const opCodes = getOpCodes(() => { i18nStart(index, MSG_DIV); }, null, nbConsts, index);
|
||||||
const tIcuIndex = 0;
|
const tIcuIndex = 0;
|
||||||
const icuCommentNodeIndex = index + 1;
|
const icuCommentNodeIndex = index + 1;
|
||||||
const firstTextNode = index + 2;
|
const firstTextNodeIndex = index + 2;
|
||||||
const bElementNodeIndex = index + 3;
|
const bElementNodeIndex = index + 3;
|
||||||
const iElementNodeIndex = index + 3;
|
const iElementNodeIndex = index + 3;
|
||||||
const spanElementNodeIndex = index + 3;
|
const spanElementNodeIndex = index + 3;
|
||||||
|
@ -279,7 +293,7 @@ describe('Runtime i18n', () => {
|
||||||
vars: 5,
|
vars: 5,
|
||||||
expandoStartIndex: nbConsts,
|
expandoStartIndex: nbConsts,
|
||||||
create: [
|
create: [
|
||||||
COMMENT_MARKER, 'ICU 1',
|
COMMENT_MARKER, 'ICU 1', icuCommentNodeIndex,
|
||||||
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild
|
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild
|
||||||
],
|
],
|
||||||
update: [
|
update: [
|
||||||
|
@ -300,49 +314,53 @@ describe('Runtime i18n', () => {
|
||||||
create: [
|
create: [
|
||||||
[
|
[
|
||||||
'no ',
|
'no ',
|
||||||
|
firstTextNodeIndex,
|
||||||
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
ELEMENT_MARKER,
|
ELEMENT_MARKER,
|
||||||
'b',
|
'b',
|
||||||
|
bElementNodeIndex,
|
||||||
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
bElementNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Attr,
|
bElementNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Attr,
|
||||||
'title',
|
'title',
|
||||||
'none',
|
'none',
|
||||||
'emails',
|
'emails',
|
||||||
|
innerTextNode,
|
||||||
bElementNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
bElementNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
'!',
|
'!',
|
||||||
|
lastTextNode,
|
||||||
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'one ',
|
'one ', firstTextNodeIndex,
|
||||||
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
ELEMENT_MARKER, 'i',
|
ELEMENT_MARKER, 'i', iElementNodeIndex,
|
||||||
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
'email',
|
'email', innerTextNode,
|
||||||
iElementNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild
|
iElementNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'',
|
'', firstTextNodeIndex,
|
||||||
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
ELEMENT_MARKER, 'span',
|
ELEMENT_MARKER, 'span', spanElementNodeIndex,
|
||||||
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
'emails',
|
'emails', innerTextNode,
|
||||||
spanElementNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild
|
spanElementNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
remove: [
|
remove: [
|
||||||
[
|
[
|
||||||
firstTextNode << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
firstTextNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
||||||
innerTextNode << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
innerTextNode << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
||||||
bElementNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
bElementNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
||||||
lastTextNode << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
lastTextNode << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
firstTextNode << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
firstTextNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
||||||
innerTextNode << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
innerTextNode << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
||||||
iElementNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
iElementNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
firstTextNode << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
firstTextNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
||||||
innerTextNode << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
innerTextNode << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
||||||
spanElementNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
spanElementNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
||||||
]
|
]
|
||||||
|
@ -354,7 +372,7 @@ describe('Runtime i18n', () => {
|
||||||
3, // skip 3 if not changed
|
3, // skip 3 if not changed
|
||||||
-1, // binding index
|
-1, // binding index
|
||||||
' ', // text string to concatenate to the binding value
|
' ', // text string to concatenate to the binding value
|
||||||
firstTextNode << I18nUpdateOpCode.SHIFT_REF | I18nUpdateOpCode.Text,
|
firstTextNodeIndex << I18nUpdateOpCode.SHIFT_REF | I18nUpdateOpCode.Text,
|
||||||
0b10, // mask for the title attribute binding
|
0b10, // mask for the title attribute binding
|
||||||
4, // skip 4 if not changed
|
4, // skip 4 if not changed
|
||||||
-2, // binding index
|
-2, // binding index
|
||||||
|
@ -380,10 +398,10 @@ describe('Runtime i18n', () => {
|
||||||
const index = 0;
|
const index = 0;
|
||||||
const opCodes = getOpCodes(() => { i18nStart(index, MSG_DIV); }, null, nbConsts, index);
|
const opCodes = getOpCodes(() => { i18nStart(index, MSG_DIV); }, null, nbConsts, index);
|
||||||
const icuCommentNodeIndex = index + 1;
|
const icuCommentNodeIndex = index + 1;
|
||||||
const firstTextNode = index + 2;
|
const firstTextNodeIndex = index + 2;
|
||||||
const nestedIcuCommentNodeIndex = index + 3;
|
const nestedIcuCommentNodeIndex = index + 3;
|
||||||
const lastTextNode = index + 4;
|
const lastTextNodeIndex = index + 4;
|
||||||
const nestedTextNode = index + 5;
|
const nestedTextNodeIndex = index + 5;
|
||||||
const tIcuIndex = 1;
|
const tIcuIndex = 1;
|
||||||
const nestedTIcuIndex = 0;
|
const nestedTIcuIndex = 0;
|
||||||
|
|
||||||
|
@ -391,7 +409,7 @@ describe('Runtime i18n', () => {
|
||||||
vars: 6,
|
vars: 6,
|
||||||
expandoStartIndex: nbConsts,
|
expandoStartIndex: nbConsts,
|
||||||
create: [
|
create: [
|
||||||
COMMENT_MARKER, 'ICU 1',
|
COMMENT_MARKER, 'ICU 1', icuCommentNodeIndex,
|
||||||
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild
|
index << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild
|
||||||
],
|
],
|
||||||
update: [
|
update: [
|
||||||
|
@ -407,27 +425,30 @@ describe('Runtime i18n', () => {
|
||||||
{
|
{
|
||||||
type: 0,
|
type: 0,
|
||||||
vars: [1, 1, 1],
|
vars: [1, 1, 1],
|
||||||
expandoStartIndex: lastTextNode + 1,
|
expandoStartIndex: lastTextNodeIndex + 1,
|
||||||
childIcus: [[], [], []],
|
childIcus: [[], [], []],
|
||||||
cases: ['cat', 'dog', 'other'],
|
cases: ['cat', 'dog', 'other'],
|
||||||
create: [
|
create: [
|
||||||
[
|
[
|
||||||
'cats', nestedIcuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT |
|
'cats', nestedTextNodeIndex, nestedIcuCommentNodeIndex
|
||||||
|
<< I18nMutateOpCode.SHIFT_PARENT |
|
||||||
I18nMutateOpCode.AppendChild
|
I18nMutateOpCode.AppendChild
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'dogs', nestedIcuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT |
|
'dogs', nestedTextNodeIndex, nestedIcuCommentNodeIndex
|
||||||
|
<< I18nMutateOpCode.SHIFT_PARENT |
|
||||||
I18nMutateOpCode.AppendChild
|
I18nMutateOpCode.AppendChild
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'animals', nestedIcuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT |
|
'animals', nestedTextNodeIndex, nestedIcuCommentNodeIndex
|
||||||
|
<< I18nMutateOpCode.SHIFT_PARENT |
|
||||||
I18nMutateOpCode.AppendChild
|
I18nMutateOpCode.AppendChild
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
remove: [
|
remove: [
|
||||||
[nestedTextNode << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove],
|
[nestedTextNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove],
|
||||||
[nestedTextNode << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove],
|
[nestedTextNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove],
|
||||||
[nestedTextNode << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove]
|
[nestedTextNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove]
|
||||||
],
|
],
|
||||||
update: [[], [], []]
|
update: [[], [], []]
|
||||||
},
|
},
|
||||||
|
@ -439,23 +460,23 @@ describe('Runtime i18n', () => {
|
||||||
cases: ['0', 'other'],
|
cases: ['0', 'other'],
|
||||||
create: [
|
create: [
|
||||||
[
|
[
|
||||||
'zero',
|
'zero', firstTextNodeIndex,
|
||||||
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild
|
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'',
|
'', firstTextNodeIndex,
|
||||||
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
COMMENT_MARKER, 'nested ICU 0',
|
COMMENT_MARKER, 'nested ICU 0', nestedIcuCommentNodeIndex,
|
||||||
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild,
|
||||||
'!',
|
'!', lastTextNodeIndex,
|
||||||
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild
|
icuCommentNodeIndex << I18nMutateOpCode.SHIFT_PARENT | I18nMutateOpCode.AppendChild
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
remove: [
|
remove: [
|
||||||
[firstTextNode << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove],
|
[firstTextNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove],
|
||||||
[
|
[
|
||||||
firstTextNode << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
firstTextNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
||||||
lastTextNode << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
lastTextNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
||||||
0 << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.RemoveNestedIcu,
|
0 << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.RemoveNestedIcu,
|
||||||
nestedIcuCommentNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
nestedIcuCommentNodeIndex << I18nMutateOpCode.SHIFT_REF | I18nMutateOpCode.Remove,
|
||||||
]
|
]
|
||||||
|
@ -467,7 +488,7 @@ describe('Runtime i18n', () => {
|
||||||
3, // skip 3 if not changed
|
3, // skip 3 if not changed
|
||||||
-1, // binding index
|
-1, // binding index
|
||||||
' ', // text string to concatenate to the binding value
|
' ', // text string to concatenate to the binding value
|
||||||
firstTextNode << I18nUpdateOpCode.SHIFT_REF | I18nUpdateOpCode.Text,
|
firstTextNodeIndex << I18nUpdateOpCode.SHIFT_REF | I18nUpdateOpCode.Text,
|
||||||
0b10, // mask for inner ICU main binding
|
0b10, // mask for inner ICU main binding
|
||||||
3, // skip 3 if not changed
|
3, // skip 3 if not changed
|
||||||
-2, // inner ICU main binding
|
-2, // inner ICU main binding
|
||||||
|
@ -656,6 +677,45 @@ describe('Runtime i18n', () => {
|
||||||
expect(fixture.html).toEqual('<div><!--ICU 2--></div>');
|
expect(fixture.html).toEqual('<div><!--ICU 2--></div>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('for multiple ICU expressions', () => {
|
||||||
|
const MSG_DIV = `{<7B>0<EFBFBD>, plural,
|
||||||
|
=0 {no <b title="none">emails</b>!}
|
||||||
|
=1 {one <i>email</i>}
|
||||||
|
other {<EFBFBD>0<EFBFBD> <span title="<22>1<EFBFBD>">emails</span>}
|
||||||
|
} - {<EFBFBD>0<EFBFBD>, select,
|
||||||
|
other {(<EFBFBD>0<EFBFBD>)}
|
||||||
|
}`;
|
||||||
|
const fixture = prepareFixture(() => {
|
||||||
|
elementStart(0, 'div');
|
||||||
|
i18n(1, MSG_DIV);
|
||||||
|
elementEnd();
|
||||||
|
}, null, 2);
|
||||||
|
|
||||||
|
// Template should be empty because there is no update template function
|
||||||
|
expect(fixture.html).toEqual('<div><!--ICU 2--> - <!--ICU 8--></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('for multiple ICU expressions inside html', () => {
|
||||||
|
const MSG_DIV = `<EFBFBD>#2<>{<7B>0<EFBFBD>, plural,
|
||||||
|
=0 {no <b title="none">emails</b>!}
|
||||||
|
=1 {one <i>email</i>}
|
||||||
|
other {<EFBFBD>0<EFBFBD> <span title="<22>1<EFBFBD>">emails</span>}
|
||||||
|
}<EFBFBD>/#2<EFBFBD><EFBFBD>#3<EFBFBD>{<EFBFBD>0<EFBFBD>, select,
|
||||||
|
other {(<EFBFBD>0<EFBFBD>)}
|
||||||
|
}<EFBFBD>/#3<EFBFBD>`;
|
||||||
|
const fixture = prepareFixture(() => {
|
||||||
|
elementStart(0, 'div');
|
||||||
|
i18nStart(1, MSG_DIV);
|
||||||
|
element(2, 'span');
|
||||||
|
element(3, 'span');
|
||||||
|
i18nEnd();
|
||||||
|
elementEnd();
|
||||||
|
}, null, 4);
|
||||||
|
|
||||||
|
// Template should be empty because there is no update template function
|
||||||
|
expect(fixture.html).toEqual('<div><span><!--ICU 4--></span><span><!--ICU 9--></span></div>');
|
||||||
|
});
|
||||||
|
|
||||||
it('for ICU expressions inside templates', () => {
|
it('for ICU expressions inside templates', () => {
|
||||||
const MSG_DIV = `<EFBFBD>*2:1<><31>#1:1<>{<7B>0:1<>, plural,
|
const MSG_DIV = `<EFBFBD>*2:1<><31>#1:1<>{<7B>0:1<>, plural,
|
||||||
=0 {no <b title="none">emails</b>!}
|
=0 {no <b title="none">emails</b>!}
|
||||||
|
@ -1014,6 +1074,118 @@ describe('Runtime i18n', () => {
|
||||||
expect(fixture.html).toEqual('<div>no <b title="none">emails</b>!<!--ICU 4--></div>');
|
expect(fixture.html).toEqual('<div>no <b title="none">emails</b>!<!--ICU 4--></div>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('for multiple ICU expressions', () => {
|
||||||
|
const MSG_DIV = `{<7B>0<EFBFBD>, plural,
|
||||||
|
=0 {no <b title="none">emails</b>!}
|
||||||
|
=1 {one <i>email</i>}
|
||||||
|
other {<EFBFBD>0<EFBFBD> <span title="<22>1<EFBFBD>">emails</span>}
|
||||||
|
} - {<EFBFBD>0<EFBFBD>, select,
|
||||||
|
other {(<EFBFBD>0<EFBFBD>)}
|
||||||
|
}`;
|
||||||
|
const ctx = {value0: 0, value1: 'emails label'};
|
||||||
|
|
||||||
|
const fixture = prepareFixture(
|
||||||
|
() => {
|
||||||
|
elementStart(0, 'div');
|
||||||
|
i18n(1, MSG_DIV);
|
||||||
|
elementEnd();
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
i18nExp(bind(ctx.value0));
|
||||||
|
i18nExp(bind(ctx.value1));
|
||||||
|
i18nApply(1);
|
||||||
|
},
|
||||||
|
2, 2);
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual('<div>no <b title="none">emails</b>!<!--ICU 4--> - (0)<!--ICU 10--></div>');
|
||||||
|
|
||||||
|
// Change detection cycle, no model changes
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual('<div>no <b title="none">emails</b>!<!--ICU 4--> - (0)<!--ICU 10--></div>');
|
||||||
|
|
||||||
|
ctx.value0 = 1;
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html).toEqual('<div>one <i>email</i><!--ICU 4--> - (1)<!--ICU 10--></div>');
|
||||||
|
|
||||||
|
ctx.value0 = 10;
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual(
|
||||||
|
'<div>10 <span title="emails label">emails</span><!--ICU 4--> - (10)<!--ICU 10--></div>');
|
||||||
|
|
||||||
|
ctx.value1 = '10 emails';
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual(
|
||||||
|
'<div>10 <span title="10 emails">emails</span><!--ICU 4--> - (10)<!--ICU 10--></div>');
|
||||||
|
|
||||||
|
ctx.value0 = 0;
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual('<div>no <b title="none">emails</b>!<!--ICU 4--> - (0)<!--ICU 10--></div>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('for multiple ICU expressions', () => {
|
||||||
|
const MSG_DIV = `<EFBFBD>#2<>{<7B>0<EFBFBD>, plural,
|
||||||
|
=0 {no <b title="none">emails</b>!}
|
||||||
|
=1 {one <i>email</i>}
|
||||||
|
other {<EFBFBD>0<EFBFBD> <span title="<22>1<EFBFBD>">emails</span>}
|
||||||
|
}<EFBFBD>/#2<EFBFBD><EFBFBD>#3<EFBFBD>{<EFBFBD>0<EFBFBD>, select,
|
||||||
|
other {(<EFBFBD>0<EFBFBD>)}
|
||||||
|
}<EFBFBD>/#3<EFBFBD>`;
|
||||||
|
const ctx = {value0: 0, value1: 'emails label'};
|
||||||
|
|
||||||
|
const fixture = prepareFixture(
|
||||||
|
() => {
|
||||||
|
elementStart(0, 'div');
|
||||||
|
i18nStart(1, MSG_DIV);
|
||||||
|
element(2, 'span');
|
||||||
|
element(3, 'span');
|
||||||
|
i18nEnd();
|
||||||
|
elementEnd();
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
i18nExp(bind(ctx.value0));
|
||||||
|
i18nExp(bind(ctx.value1));
|
||||||
|
i18nApply(1);
|
||||||
|
},
|
||||||
|
4, 2);
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual(
|
||||||
|
'<div><span>no <b title="none">emails</b>!<!--ICU 6--></span><span>(0)<!--ICU 11--></span></div>');
|
||||||
|
|
||||||
|
// Change detection cycle, no model changes
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual(
|
||||||
|
'<div><span>no <b title="none">emails</b>!<!--ICU 6--></span><span>(0)<!--ICU 11--></span></div>');
|
||||||
|
|
||||||
|
ctx.value0 = 1;
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual(
|
||||||
|
'<div><span>one <i>email</i><!--ICU 6--></span><span>(1)<!--ICU 11--></span></div>');
|
||||||
|
|
||||||
|
ctx.value0 = 10;
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual(
|
||||||
|
'<div><span>10 <span title="emails label">emails</span><!--ICU 6--></span><span>(10)<!--ICU 11--></span></div>');
|
||||||
|
|
||||||
|
ctx.value1 = '10 emails';
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual(
|
||||||
|
'<div><span>10 <span title="10 emails">emails</span><!--ICU 6--></span><span>(10)<!--ICU 11--></span></div>');
|
||||||
|
|
||||||
|
ctx.value0 = 0;
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual(
|
||||||
|
'<div><span>no <b title="none">emails</b>!<!--ICU 6--></span><span>(0)<!--ICU 11--></span></div>');
|
||||||
|
});
|
||||||
|
|
||||||
it('for nested ICU expressions', () => {
|
it('for nested ICU expressions', () => {
|
||||||
const MSG_DIV = `{<7B>0<EFBFBD>, plural,
|
const MSG_DIV = `{<7B>0<EFBFBD>, plural,
|
||||||
=0 {zero}
|
=0 {zero}
|
||||||
|
|
Loading…
Reference in New Issue