diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts
index 9eab539f48..bcb1f76c97 100644
--- a/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts
+++ b/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts
@@ -3415,14 +3415,57 @@ describe('i18n support in the template compiler', () => {
});
describe('errors', () => {
+ const verifyNestedSectionsError = (errorThrown: any, expectedErrorText: string) => {
+ expect(errorThrown.ngParseErrors.length).toBe(1);
+ const msg = errorThrown.ngParseErrors[0].toString();
+ expect(msg).toContain(
+ 'Cannot mark an element as translatable inside of a translatable section. Please remove the nested i18n marker.');
+ expect(msg).toContain(expectedErrorText);
+ expect(msg).toMatch(/app\/spec\.ts\@\d+\:\d+/);
+ };
+
it('should throw on nested i18n sections', () => {
const files = getAppFilesWithTemplate(`
-
+
`);
- expect(() => compile(files, angularFiles))
- .toThrowError(
- 'Could not mark an element as translatable inside of a translatable section');
+ try {
+ compile(files, angularFiles);
+ } catch (error) {
+ verifyNestedSectionsError(error, '[ERROR ->]Some content
');
+ }
});
+ it('should throw on nested i18n sections with tags in between', () => {
+ const files = getAppFilesWithTemplate(`
+
+ `);
+ try {
+ compile(files, angularFiles);
+ } catch (error) {
+ verifyNestedSectionsError(error, '[ERROR ->]Some content
');
+ }
+ });
+
+ it('should throw on nested i18n sections represented with s', () => {
+ const files = getAppFilesWithTemplate(`
+
+
+ Some content
+
+
+ `);
+ try {
+ compile(files, angularFiles);
+ } catch (error) {
+ verifyNestedSectionsError(
+ error, '[ERROR ->]Some content');
+ }
+ });
});
});
diff --git a/packages/compiler/src/render3/r3_template_transform.ts b/packages/compiler/src/render3/r3_template_transform.ts
index 41c2452089..4df6909321 100644
--- a/packages/compiler/src/render3/r3_template_transform.ts
+++ b/packages/compiler/src/render3/r3_template_transform.ts
@@ -80,11 +80,21 @@ class HtmlAstToIvyAst implements html.Visitor {
errors: ParseError[] = [];
styles: string[] = [];
styleUrls: string[] = [];
+ private inI18nBlock: boolean = false;
constructor(private bindingParser: BindingParser) {}
// HTML visitor
visitElement(element: html.Element): t.Node|null {
+ const isI18nRootElement = isI18nRootNode(element.i18n);
+ if (isI18nRootElement) {
+ if (this.inI18nBlock) {
+ this.reportError(
+ 'Cannot mark an element as translatable inside of a translatable section. Please remove the nested i18n marker.',
+ element.sourceSpan);
+ }
+ this.inI18nBlock = true;
+ }
const preparsedElement = preparseElement(element);
if (preparsedElement.type === PreparsedElementType.SCRIPT) {
return null;
@@ -209,7 +219,7 @@ class HtmlAstToIvyAst implements html.Visitor {
// For s with structural directives on them, avoid passing i18n information to
// the wrapping template to prevent unnecessary i18n instructions from being generated. The
// necessary i18n meta information will be extracted from child elements.
- const i18n = isTemplateElement && isI18nRootNode(element.i18n) ? undefined : element.i18n;
+ const i18n = isTemplateElement && isI18nRootElement ? undefined : element.i18n;
// TODO(pk): test for this case
parsedElement = new t.Template(
@@ -218,6 +228,9 @@ class HtmlAstToIvyAst implements html.Visitor {
templateVariables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan,
i18n);
}
+ if (isI18nRootElement) {
+ this.inI18nBlock = false;
+ }
return parsedElement;
}
diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts
index da3eb85fc9..71e4c7078b 100644
--- a/packages/compiler/src/render3/view/template.ts
+++ b/packages/compiler/src/render3/view/template.ts
@@ -535,10 +535,6 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver
const isI18nRootElement: boolean =
isI18nRootNode(element.i18n) && !isSingleI18nIcu(element.i18n);
- if (isI18nRootElement && this.i18n) {
- throw new Error(`Could not mark an element as translatable inside of a translatable section`);
- }
-
const i18nAttrs: (t.TextAttribute | t.BoundAttribute)[] = [];
const outputAttrs: t.TextAttribute[] = [];
let ngProjectAsAttr: t.TextAttribute|undefined;