From e74d8aaf9261349dd0a5b53e298c573404005b48 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Thu, 22 Dec 2016 10:17:41 -0800 Subject: [PATCH] fix(i18n): parse ICU messages while normalizing templates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: - Inject the i18n specific HtmlParser into the directive normalizer, - Parse ICU messages while normalizing templates, - Normalize (visit) the content of ICU messages. 🎄🎁🎅 --- .../compiler/src/directive_normalizer.ts | 13 +++++++---- .../compiler/src/i18n/extractor_merger.ts | 2 +- .../compiler/src/jit/compiler_factory.ts | 15 +++++++++--- .../compiler/test/i18n/integration_spec.ts | 23 +++++++++++++++---- modules/@angular/compiler/testing/index.ts | 9 +++++--- 5 files changed, 46 insertions(+), 16 deletions(-) diff --git a/modules/@angular/compiler/src/directive_normalizer.ts b/modules/@angular/compiler/src/directive_normalizer.ts index 1bcee76dd15996..c6aebe26ab6d46 100644 --- a/modules/@angular/compiler/src/directive_normalizer.ts +++ b/modules/@angular/compiler/src/directive_normalizer.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, ViewEncapsulation} from '@angular/core'; +import {ViewEncapsulation} from '@angular/core'; import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from './compile_metadata'; import {CompilerConfig} from './config'; @@ -102,11 +102,12 @@ export class DirectiveNormalizer { templateAbsUrl: string): CompileTemplateMetadata { const interpolationConfig = InterpolationConfig.fromArray(prenomData.interpolation); const rootNodesAndErrors = this._htmlParser.parse( - template, stringify(prenomData.componentType), false, interpolationConfig); + template, stringify(prenomData.componentType), true, interpolationConfig); if (rootNodesAndErrors.errors.length > 0) { const errorString = rootNodesAndErrors.errors.join('\n'); throw new SyntaxError(`Template parse errors:\n${errorString}`); } + const templateMetadataStyles = this.normalizeStylesheet(new CompileStylesheetMetadata({ styles: prenomData.styles, styleUrls: prenomData.styleUrls, @@ -228,9 +229,13 @@ class TemplatePreparseVisitor implements html.Visitor { return null; } + visitExpansion(ast: html.Expansion, context: any): any { html.visitAll(this, ast.cases); } + + visitExpansionCase(ast: html.ExpansionCase, context: any): any { + html.visitAll(this, ast.expression); + } + visitComment(ast: html.Comment, context: any): any { return null; } visitAttribute(ast: html.Attribute, context: any): any { return null; } visitText(ast: html.Text, context: any): any { return null; } - visitExpansion(ast: html.Expansion, context: any): any { return null; } - visitExpansionCase(ast: html.ExpansionCase, context: any): any { return null; } } diff --git a/modules/@angular/compiler/src/i18n/extractor_merger.ts b/modules/@angular/compiler/src/i18n/extractor_merger.ts index b4a81016b7ce1e..84e1f489aab035 100644 --- a/modules/@angular/compiler/src/i18n/extractor_merger.ts +++ b/modules/@angular/compiler/src/i18n/extractor_merger.ts @@ -422,7 +422,7 @@ class _Visitor implements html.Visitor { } /** - * Marks the start of a section, see `_endSection` + * Marks the start of a section, see `_closeTranslatableSection` */ private _openTranslatableSection(node: html.Node): void { if (this._isInTranslatableSection) { diff --git a/modules/@angular/compiler/src/jit/compiler_factory.ts b/modules/@angular/compiler/src/jit/compiler_factory.ts index 8f19e817c6d097..2f8be8e3d05c65 100644 --- a/modules/@angular/compiler/src/jit/compiler_factory.ts +++ b/modules/@angular/compiler/src/jit/compiler_factory.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core'; +import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, OpaqueToken, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core'; import {AnimationParser} from '../animation/animation_parser'; import {CompilerConfig} from '../config'; @@ -40,6 +40,8 @@ const _NO_RESOURCE_LOADER: ResourceLoader = { `No ResourceLoader implementation has been provided. Can't read the url "${url}"`);} }; +const baseHtmlParser = new OpaqueToken('HtmlParser'); + /** * A set of providers that provide `JitCompiler` and its dependencies to use for * template compilation. @@ -52,17 +54,24 @@ export const COMPILER_PROVIDERS: Array|{[k: string]: any}|any[]> = Console, Lexer, Parser, - HtmlParser, + { + provide: baseHtmlParser, + useClass: HtmlParser, + }, { provide: i18n.I18NHtmlParser, useFactory: (parser: HtmlParser, translations: string, format: string) => new i18n.I18NHtmlParser(parser, translations, format), deps: [ - HtmlParser, + baseHtmlParser, [new Optional(), new Inject(TRANSLATIONS)], [new Optional(), new Inject(TRANSLATIONS_FORMAT)], ] }, + { + provide: HtmlParser, + useExisting: i18n.I18NHtmlParser, + }, TemplateParser, DirectiveNormalizer, CompileMetadataResolver, diff --git a/modules/@angular/compiler/test/i18n/integration_spec.ts b/modules/@angular/compiler/test/i18n/integration_spec.ts index 7823c426341edd..1bcd64aad46c3f 100644 --- a/modules/@angular/compiler/test/i18n/integration_spec.ts +++ b/modules/@angular/compiler/test/i18n/integration_spec.ts @@ -144,14 +144,25 @@ function expectHtml(el: DebugElement, cssSelector: string): any {
it should work
+
with an explicit ID
{count, plural, =0 {zero} =1 {one} =2 {two} other {many}}
+ + +
{ + response.getItemsList().length, + plural, + =0 {Found no results} + =1 {Found one result} + other {Found {{response.getItemsList().length}} results} +}
` }) class I18nComponent { count: number; sex: string; sexB: string; + response: any = {getItemsList: (): any[] => []}; } class FrLocalization extends NgLocalization { @@ -190,6 +201,7 @@ const XTB = ` avec un ID explicite {VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {<b>beaucoup</b>} } + {VAR_PLURAL, plural, =0 {Pas de réponse} =1 {une réponse} other {INTERPOLATION réponse} } `; // unused, for reference only @@ -205,20 +217,21 @@ const XMB = ` on translatable node {VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<b>many</b>} } - + ICU {VAR_SELECT, select, m {male} f {female} } - - sex = - + INTERPOLATION + sex = INTERPOLATION + CUSTOM_NAME in a translatable section <h1>Markers in html comments</h1> <div></div> - <div></div> + <div>ICU</div> it <b>should</b> work with an explicit ID {VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<b>many</b>} } + {VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found INTERPOLATION results} } `; diff --git a/modules/@angular/compiler/testing/index.ts b/modules/@angular/compiler/testing/index.ts index 1824bbdc9144fb..6f2ad57269bbed 100644 --- a/modules/@angular/compiler/testing/index.ts +++ b/modules/@angular/compiler/testing/index.ts @@ -108,9 +108,12 @@ export const platformCoreDynamicTesting: (extraProviders?: any[]) => PlatformRef provide: COMPILER_OPTIONS, useValue: { providers: [ - MockPipeResolver, {provide: PipeResolver, useExisting: MockPipeResolver}, - MockDirectiveResolver, {provide: DirectiveResolver, useExisting: MockDirectiveResolver}, - MockNgModuleResolver, {provide: NgModuleResolver, useExisting: MockNgModuleResolver} + MockPipeResolver, + {provide: PipeResolver, useExisting: MockPipeResolver}, + MockDirectiveResolver, + {provide: DirectiveResolver, useExisting: MockDirectiveResolver}, + MockNgModuleResolver, + {provide: NgModuleResolver, useExisting: MockNgModuleResolver}, ] }, multi: true