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 d7cd299ada..9b8cd9c8c2 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
@@ -3832,4 +3832,102 @@ $` + String.raw`{$I18N_4$}:ICU:\`;
}
});
});
+
+ describe('namespaces', () => {
+ it('should handle namespaces inside i18n blocks', () => {
+ const input = `
+
+ `;
+
+ const output = String.raw`
+ var $I18N_0$;
+ if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) {
+ const $MSG_EXTERNAL_7128002169381370313$$APP_SPEC_TS_1$ = goog.getMsg("{$startTagXhtmlDiv} Count: {$startTagXhtmlSpan}5{$closeTagXhtmlSpan}{$closeTagXhtmlDiv}", {
+ "startTagXhtmlDiv": "\uFFFD#3\uFFFD",
+ "startTagXhtmlSpan": "\uFFFD#4\uFFFD",
+ "closeTagXhtmlSpan": "\uFFFD/#4\uFFFD",
+ "closeTagXhtmlDiv": "\uFFFD/#3\uFFFD"
+ });
+ $I18N_0$ = $MSG_EXTERNAL_7128002169381370313$$APP_SPEC_TS_1$;
+ }
+ else {
+ $I18N_0$ = $localize \`$` +
+ String.raw`{"\uFFFD#3\uFFFD"}:START_TAG__XHTML_DIV: Count: $` +
+ String.raw`{"\uFFFD#4\uFFFD"}:START_TAG__XHTML_SPAN:5$` +
+ String.raw`{"\uFFFD/#4\uFFFD"}:CLOSE_TAG__XHTML_SPAN:$` +
+ String.raw`{"\uFFFD/#3\uFFFD"}:CLOSE_TAG__XHTML_DIV:\`;
+ }
+ …
+ function MyComponent_Template(rf, ctx) {
+ if (rf & 1) {
+ $r3$.ɵɵnamespaceSVG();
+ $r3$.ɵɵelementStart(0, "svg", 0);
+ $r3$.ɵɵelementStart(1, "foreignObject");
+ $r3$.ɵɵi18nStart(2, $I18N_0$);
+ $r3$.ɵɵnamespaceHTML();
+ $r3$.ɵɵelementStart(3, "div", 1);
+ $r3$.ɵɵelement(4, "span");
+ $r3$.ɵɵelementEnd();
+ $r3$.ɵɵi18nEnd();
+ $r3$.ɵɵelementEnd();
+ $r3$.ɵɵelementEnd();
+ }
+ }
+ `;
+
+ verify(input, output);
+ });
+
+ it('should handle namespaces on i18n block containers', () => {
+ const input = `
+
+ `;
+
+ const output = String.raw`
+ var $I18N_0$;
+ if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) {
+ const $MSG_EXTERNAL_7428861019045796010$$APP_SPEC_TS_1$ = goog.getMsg(" Count: {$startTagXhtmlSpan}5{$closeTagXhtmlSpan}", {
+ "startTagXhtmlSpan": "\uFFFD#4\uFFFD",
+ "closeTagXhtmlSpan": "\uFFFD/#4\uFFFD"
+ });
+ $I18N_0$ = $MSG_EXTERNAL_7428861019045796010$$APP_SPEC_TS_1$;
+ }
+ else {
+ $I18N_0$ = $localize \` Count: $` +
+ String.raw`{"\uFFFD#4\uFFFD"}:START_TAG__XHTML_SPAN:5$` +
+ String.raw`{"\uFFFD/#4\uFFFD"}:CLOSE_TAG__XHTML_SPAN:\`;
+ }
+ …
+ function MyComponent_Template(rf, ctx) {
+ if (rf & 1) {
+ $r3$.ɵɵnamespaceSVG();
+ $r3$.ɵɵelementStart(0, "svg", 0);
+ $r3$.ɵɵelementStart(1, "foreignObject");
+ $r3$.ɵɵnamespaceHTML();
+ $r3$.ɵɵelementStart(2, "div", 1);
+ $r3$.ɵɵi18nStart(3, $I18N_0$);
+ $r3$.ɵɵelement(4, "span");
+ $r3$.ɵɵi18nEnd();
+ $r3$.ɵɵelementEnd();
+ $r3$.ɵɵelementEnd();
+ $r3$.ɵɵelementEnd();
+ }
+ }
+ `;
+
+ verify(input, output, {verbose: true});
+ });
+ });
});
diff --git a/packages/core/test/acceptance/i18n_spec.ts b/packages/core/test/acceptance/i18n_spec.ts
index 6a272efaab..59c6f0e465 100644
--- a/packages/core/test/acceptance/i18n_spec.ts
+++ b/packages/core/test/acceptance/i18n_spec.ts
@@ -9,15 +9,15 @@
// below. This would normally be done inside the application `polyfills.ts` file.
import '@angular/localize/init';
-import {CommonModule, registerLocaleData} from '@angular/common';
+import {CommonModule, DOCUMENT, registerLocaleData} from '@angular/common';
import localeEs from '@angular/common/locales/es';
import localeRo from '@angular/common/locales/ro';
import {computeMsgId} from '@angular/compiler';
-import {Component, ContentChild, ContentChildren, Directive, ElementRef, HostBinding, Input, LOCALE_ID, NO_ERRORS_SCHEMA, Pipe, PipeTransform, QueryList, TemplateRef, Type, ViewChild, ViewContainerRef} from '@angular/core';
+import {Component, ContentChild, ContentChildren, Directive, ElementRef, HostBinding, Input, LOCALE_ID, NO_ERRORS_SCHEMA, Pipe, PipeTransform, QueryList, RendererFactory2, TemplateRef, Type, ViewChild, ViewContainerRef, ɵsetDocument} from '@angular/core';
import {setDelayProjection} from '@angular/core/src/render3/instructions/projection';
import {TestBed} from '@angular/core/testing';
import {clearTranslations, loadTranslations} from '@angular/localize';
-import {By} from '@angular/platform-browser';
+import {By, ɵDomRendererFactory2 as DomRendererFactory2} from '@angular/platform-browser';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {onlyInIvy} from '@angular/private/testing';
import {BehaviorSubject} from 'rxjs';
@@ -530,6 +530,75 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
});
});
+ describe('should work correctly with namespaces', () => {
+ beforeEach(() => {
+ function _document(): any {
+ // Tell Ivy about the global document
+ ɵsetDocument(document);
+ return document;
+ }
+
+ TestBed.configureTestingModule({
+ providers: [
+ {provide: DOCUMENT, useFactory: _document, deps: []},
+ // TODO(FW-811): switch back to default server renderer (i.e. remove the line below)
+ // once it starts to support Ivy namespace format (URIs) correctly. For now, use
+ // `DomRenderer` that supports Ivy namespace format.
+ {provide: RendererFactory2, useClass: DomRendererFactory2}
+ ],
+ });
+ });
+
+ it('should handle namespaces inside i18n blocks', () => {
+ loadTranslations({
+ [computeMsgId(
+ '{$START_TAG__XHTML_DIV} Hello ' +
+ '{$START_TAG__XHTML_SPAN}world{$CLOSE_TAG__XHTML_SPAN}{$CLOSE_TAG__XHTML_DIV}')]:
+ '{$START_TAG__XHTML_DIV} Bonjour ' +
+ '{$START_TAG__XHTML_SPAN}monde{$CLOSE_TAG__XHTML_SPAN}{$CLOSE_TAG__XHTML_DIV}'
+ });
+
+ const fixture = initWithTemplate(AppComp, `
+
+ `);
+
+ const element = fixture.nativeElement;
+ expect(element.textContent.trim()).toBe('Bonjour monde');
+ expect(element.querySelector('svg').namespaceURI).toBe('http://www.w3.org/2000/svg');
+ expect(element.querySelector('div').namespaceURI).toBe('http://www.w3.org/1999/xhtml');
+ expect(element.querySelector('span').namespaceURI).toBe('http://www.w3.org/1999/xhtml');
+ });
+
+ it('should handle namespaces on i18n block containers', () => {
+ loadTranslations({
+ [computeMsgId(' Hello {$START_TAG__XHTML_SPAN}world{$CLOSE_TAG__XHTML_SPAN}')]:
+ ' Bonjour {$START_TAG__XHTML_SPAN}monde{$CLOSE_TAG__XHTML_SPAN}'
+ });
+
+ const fixture = initWithTemplate(AppComp, `
+
+ `);
+
+ const element = fixture.nativeElement;
+ expect(element.textContent.trim()).toBe('Bonjour monde');
+ expect(element.querySelector('svg').namespaceURI).toBe('http://www.w3.org/2000/svg');
+ expect(element.querySelector('div').namespaceURI).toBe('http://www.w3.org/1999/xhtml');
+ expect(element.querySelector('span').namespaceURI).toBe('http://www.w3.org/1999/xhtml');
+ });
+ });
+
describe('should support ICU expressions', () => {
it('with no root node', () => {
loadTranslations({