diff --git a/packages/core/src/render3/i18n.ts b/packages/core/src/render3/i18n.ts index 6c64793290..78e011c7b4 100644 --- a/packages/core/src/render3/i18n.ts +++ b/packages/core/src/render3/i18n.ts @@ -76,10 +76,11 @@ export function i18nMapping( expressions?: (PlaceholderMap | null)[] | null, templateRoots?: string[] | null, lastChildIndex?: number | null): I18nInstruction[][] { const translationParts = translation.split(i18nTagRegex); - const instructions: I18nInstruction[][] = []; + const nbTemplates = templateRoots ? templateRoots.length + 1 : 1; + const instructions: I18nInstruction[][] = (new Array(nbTemplates)).fill(undefined); generateMappingInstructions( - 0, translationParts, instructions, elements, expressions, templateRoots, lastChildIndex); + 0, 0, translationParts, instructions, elements, expressions, templateRoots, lastChildIndex); return instructions; } @@ -90,7 +91,9 @@ export function i18nMapping( * * See `i18nMapping()` for more details. * - * @param index The current index in `translationParts`. + * @param tmplIndex The order of appearance of the template. + * 0 for the root template, following indexes match the order in `templateRoots`. + * @param partIndex The current index in `translationParts`. * @param translationParts The translation string split into an array of placeholders and text * elements. * @param instructions The current list of instructions to update. @@ -102,13 +105,14 @@ export function i18nMapping( * generating the instructions for their parent template. * @param lastChildIndex The index of the last child of the i18n node. Used when the i18n block is * an ng-container. + * * @returns the current index in `translationParts` */ function generateMappingInstructions( - index: number, translationParts: string[], instructions: I18nInstruction[][], - elements: (PlaceholderMap | null)[] | null, expressions?: (PlaceholderMap | null)[] | null, - templateRoots?: string[] | null, lastChildIndex?: number | null): number { - const tmplIndex = instructions.length; + tmplIndex: number, partIndex: number, translationParts: string[], + instructions: I18nInstruction[][], elements: (PlaceholderMap | null)[] | null, + expressions?: (PlaceholderMap | null)[] | null, templateRoots?: string[] | null, + lastChildIndex?: number | null): number { const tmplInstructions: I18nInstruction[] = []; const phVisited: string[] = []; let openedTagCount = 0; @@ -118,20 +122,20 @@ function generateMappingInstructions( let currentExpressions: PlaceholderMap|null = expressions && expressions[tmplIndex] ? expressions[tmplIndex] : null; - instructions.push(tmplInstructions); + instructions[tmplIndex] = tmplInstructions; - for (; index < translationParts.length; index++) { - const value = translationParts[index]; + for (; partIndex < translationParts.length; partIndex++) { + // The value can either be text or the name of a placeholder (element/template root/expression) + const value = translationParts[partIndex]; // Odd indexes are placeholders - if (index & 1) { + if (partIndex & 1) { let phIndex; if (currentElements && currentElements[value] !== undefined) { phIndex = currentElements[value]; - // The placeholder represents a DOM element - // Add an instruction to move the element - const isTemplateRoot = templateRoots && templateRoots[tmplIndex] === value; - if (isTemplateRoot) { + // The placeholder represents a DOM element, add an instruction to move it + let templateRootIndex = templateRoots ? templateRoots.indexOf(value) : -1; + if (templateRootIndex !== -1 && (templateRootIndex + 1) !== tmplIndex) { // This is a template root, it has no closing tag, not treating it as an element tmplInstructions.push(phIndex | I18nInstructions.TemplateRoot); } else { @@ -141,8 +145,7 @@ function generateMappingInstructions( phVisited.push(value); } else if (currentExpressions && currentExpressions[value] !== undefined) { phIndex = currentExpressions[value]; - // The placeholder represents an expression - // Add an instruction to move the expression + // The placeholder represents an expression, add an instruction to move it tmplInstructions.push(phIndex | I18nInstructions.Expression); phVisited.push(value); } else { @@ -163,11 +166,13 @@ function generateMappingInstructions( maxIndex = phIndex; } - if (templateRoots && templateRoots.indexOf(value) !== -1 && - templateRoots.indexOf(value) >= tmplIndex) { - index = generateMappingInstructions( - index, translationParts, instructions, elements, expressions, templateRoots, - lastChildIndex); + if (templateRoots) { + const newTmplIndex = templateRoots.indexOf(value) + 1; + if (newTmplIndex !== 0 && newTmplIndex !== tmplIndex) { + partIndex = generateMappingInstructions( + newTmplIndex, partIndex, translationParts, instructions, elements, expressions, + templateRoots, lastChildIndex); + } } } else if (value) { @@ -237,7 +242,7 @@ function generateMappingInstructions( } } - return index; + return partIndex; } function appendI18nNode(node: LNode, parentNode: LNode, previousNode: LNode) { diff --git a/packages/core/test/render3/i18n_spec.ts b/packages/core/test/render3/i18n_spec.ts index d185583385..0c96c43d20 100644 --- a/packages/core/test/render3/i18n_spec.ts +++ b/packages/core/test/render3/i18n_spec.ts @@ -720,6 +720,116 @@ describe('Runtime i18n', () => { '