From 6da1446afcde6fb5a4aa98ff3cc60fbd2d7e92c0 Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Tue, 9 Jul 2019 17:21:31 -0700 Subject: [PATCH] fix(ivy): handle &ngsp; in i18n translations correctly (#31479) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prior to this commit, the `` unicode symbol that represents `&ngsp` in translations was not handled correctly, i.e. was not replaced with a whitespace, thus appearing on a screen. This commit adds post-processing and replaces the mentioned symbol with a whitespace. PR Close #31479 --- packages/core/src/render3/i18n.ts | 14 +++++++++++++- packages/core/test/acceptance/i18n_spec.ts | 16 ++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/core/src/render3/i18n.ts b/packages/core/src/render3/i18n.ts index ae19790c0b..9e55eb569c 100644 --- a/packages/core/src/render3/i18n.ts +++ b/packages/core/src/render3/i18n.ts @@ -403,7 +403,7 @@ function i18nStartFirstPass( const icuExpressions: TIcu[] = []; const templateTranslation = getTranslationForTemplate(message, subTemplateIndex); - const msgParts = templateTranslation.split(PH_REGEXP); + const msgParts = replaceNgsp(templateTranslation).split(PH_REGEXP); for (let i = 0; i < msgParts.length; i++) { let value = msgParts[i]; if (i & 1) { @@ -1296,6 +1296,18 @@ function parseNodes( } } +/** + * Angular Dart introduced &ngsp; as a placeholder for non-removable space, see: + * https://github.com/dart-lang/angular/blob/0bb611387d29d65b5af7f9d2515ab571fd3fbee4/_tests/test/compiler/preserve_whitespace_test.dart#L25-L32 + * In Angular Dart &ngsp; is converted to the 0xE500 PUA (Private Use Areas) unicode character + * and later on replaced by a space. We are re-implementing the same idea here, since translations + * might contain this special character. + */ +const NGSP_UNICODE_REGEXP = /\uE500/g; +function replaceNgsp(value: string): string { + return value.replace(NGSP_UNICODE_REGEXP, ' '); +} + let TRANSLATIONS: {[key: string]: string} = {}; export interface I18nLocalizeOptions { translations: {[key: string]: string}; } diff --git a/packages/core/test/acceptance/i18n_spec.ts b/packages/core/test/acceptance/i18n_spec.ts index b4677823b4..e1b0c722ee 100644 --- a/packages/core/test/acceptance/i18n_spec.ts +++ b/packages/core/test/acceptance/i18n_spec.ts @@ -48,6 +48,14 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => { expect(fixture.nativeElement.innerHTML).toBe('
Bonjour Angular
'); }); + it('should support &ngsp; in translatable sections', () => { + // note: the `` unicode symbol represents the `&ngsp;` in translations + ɵi18nConfigureLocalize({translations: {'text ||': 'texte ||'}}); + const fixture = initWithTemplate(AppCompWithWhitespaces, `
text |&ngsp;|
`); + + expect(fixture.nativeElement.innerHTML).toEqual(`
texte | |
`); + }); + it('should support interpolations with complex expressions', () => { ɵi18nConfigureLocalize({ translations: @@ -1503,6 +1511,14 @@ class AppComp { count = 0; } +@Component({ + selector: 'app-comp-with-whitespaces', + template: ``, + preserveWhitespaces: true, +}) +class AppCompWithWhitespaces { +} + @Directive({ selector: '[tplRef]', })