fix(i18n): parse ICU messages while normalizing templates
Fixes: - Inject the i18n specific HtmlParser into the directive normalizer, - Parse ICU messages while normalizing templates, - Normalize (visit) the content of ICU messages. 🎄🎁🎅
This commit is contained in:
parent
881eb894bc
commit
e74d8aaf92
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* 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 {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from './compile_metadata';
|
||||||
import {CompilerConfig} from './config';
|
import {CompilerConfig} from './config';
|
||||||
|
@ -102,11 +102,12 @@ export class DirectiveNormalizer {
|
||||||
templateAbsUrl: string): CompileTemplateMetadata {
|
templateAbsUrl: string): CompileTemplateMetadata {
|
||||||
const interpolationConfig = InterpolationConfig.fromArray(prenomData.interpolation);
|
const interpolationConfig = InterpolationConfig.fromArray(prenomData.interpolation);
|
||||||
const rootNodesAndErrors = this._htmlParser.parse(
|
const rootNodesAndErrors = this._htmlParser.parse(
|
||||||
template, stringify(prenomData.componentType), false, interpolationConfig);
|
template, stringify(prenomData.componentType), true, interpolationConfig);
|
||||||
if (rootNodesAndErrors.errors.length > 0) {
|
if (rootNodesAndErrors.errors.length > 0) {
|
||||||
const errorString = rootNodesAndErrors.errors.join('\n');
|
const errorString = rootNodesAndErrors.errors.join('\n');
|
||||||
throw new SyntaxError(`Template parse errors:\n${errorString}`);
|
throw new SyntaxError(`Template parse errors:\n${errorString}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const templateMetadataStyles = this.normalizeStylesheet(new CompileStylesheetMetadata({
|
const templateMetadataStyles = this.normalizeStylesheet(new CompileStylesheetMetadata({
|
||||||
styles: prenomData.styles,
|
styles: prenomData.styles,
|
||||||
styleUrls: prenomData.styleUrls,
|
styleUrls: prenomData.styleUrls,
|
||||||
|
@ -228,9 +229,13 @@ class TemplatePreparseVisitor implements html.Visitor {
|
||||||
return null;
|
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; }
|
visitComment(ast: html.Comment, context: any): any { return null; }
|
||||||
visitAttribute(ast: html.Attribute, context: any): any { return null; }
|
visitAttribute(ast: html.Attribute, context: any): any { return null; }
|
||||||
visitText(ast: html.Text, 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; }
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
private _openTranslatableSection(node: html.Node): void {
|
||||||
if (this._isInTranslatableSection) {
|
if (this._isInTranslatableSection) {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* 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 {AnimationParser} from '../animation/animation_parser';
|
||||||
import {CompilerConfig} from '../config';
|
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}"`);}
|
`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
|
* A set of providers that provide `JitCompiler` and its dependencies to use for
|
||||||
* template compilation.
|
* template compilation.
|
||||||
|
@ -52,17 +54,24 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
|
||||||
Console,
|
Console,
|
||||||
Lexer,
|
Lexer,
|
||||||
Parser,
|
Parser,
|
||||||
HtmlParser,
|
{
|
||||||
|
provide: baseHtmlParser,
|
||||||
|
useClass: HtmlParser,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
provide: i18n.I18NHtmlParser,
|
provide: i18n.I18NHtmlParser,
|
||||||
useFactory: (parser: HtmlParser, translations: string, format: string) =>
|
useFactory: (parser: HtmlParser, translations: string, format: string) =>
|
||||||
new i18n.I18NHtmlParser(parser, translations, format),
|
new i18n.I18NHtmlParser(parser, translations, format),
|
||||||
deps: [
|
deps: [
|
||||||
HtmlParser,
|
baseHtmlParser,
|
||||||
[new Optional(), new Inject(TRANSLATIONS)],
|
[new Optional(), new Inject(TRANSLATIONS)],
|
||||||
[new Optional(), new Inject(TRANSLATIONS_FORMAT)],
|
[new Optional(), new Inject(TRANSLATIONS_FORMAT)],
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: HtmlParser,
|
||||||
|
useExisting: i18n.I18NHtmlParser,
|
||||||
|
},
|
||||||
TemplateParser,
|
TemplateParser,
|
||||||
DirectiveNormalizer,
|
DirectiveNormalizer,
|
||||||
CompileMetadataResolver,
|
CompileMetadataResolver,
|
||||||
|
|
|
@ -144,14 +144,25 @@ function expectHtml(el: DebugElement, cssSelector: string): any {
|
||||||
<!-- /i18n -->
|
<!-- /i18n -->
|
||||||
|
|
||||||
<div id="i18n-15"><ng-container i18n>it <b>should</b> work</ng-container></div>
|
<div id="i18n-15"><ng-container i18n>it <b>should</b> work</ng-container></div>
|
||||||
|
|
||||||
<div id="i18n-16" i18n="@@i18n16">with an explicit ID</div>
|
<div id="i18n-16" i18n="@@i18n16">with an explicit ID</div>
|
||||||
<div id="i18n-17" i18n="@@i18n17">{count, plural, =0 {zero} =1 {one} =2 {two} other {<b>many</b>}}</div>
|
<div id="i18n-17" i18n="@@i18n17">{count, plural, =0 {zero} =1 {one} =2 {two} other {<b>many</b>}}</div>
|
||||||
|
|
||||||
|
<!-- make sure that ICU messages are not treated as text nodes -->
|
||||||
|
<div i18n="desc">{
|
||||||
|
response.getItemsList().length,
|
||||||
|
plural,
|
||||||
|
=0 {Found no results}
|
||||||
|
=1 {Found one result}
|
||||||
|
other {Found {{response.getItemsList().length}} results}
|
||||||
|
}</div>
|
||||||
`
|
`
|
||||||
})
|
})
|
||||||
class I18nComponent {
|
class I18nComponent {
|
||||||
count: number;
|
count: number;
|
||||||
sex: string;
|
sex: string;
|
||||||
sexB: string;
|
sexB: string;
|
||||||
|
response: any = {getItemsList: (): any[] => []};
|
||||||
}
|
}
|
||||||
|
|
||||||
class FrLocalization extends NgLocalization {
|
class FrLocalization extends NgLocalization {
|
||||||
|
@ -190,6 +201,7 @@ const XTB = `
|
||||||
<translation id="i18n16">avec un ID explicite</translation>
|
<translation id="i18n16">avec un ID explicite</translation>
|
||||||
<translation id="i18n17">{VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {<ph
|
<translation id="i18n17">{VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {<ph
|
||||||
name="START_BOLD_TEXT"><ex><b></ex></ph>beaucoup<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>} }</translation>
|
name="START_BOLD_TEXT"><ex><b></ex></ph>beaucoup<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>} }</translation>
|
||||||
|
<translation id="4085484936881858615">{VAR_PLURAL, plural, =0 {Pas de réponse} =1 {une réponse} other {<ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> réponse} }</translation>
|
||||||
</translationbundle>`;
|
</translationbundle>`;
|
||||||
|
|
||||||
// unused, for reference only
|
// unused, for reference only
|
||||||
|
@ -205,20 +217,21 @@ const XMB = `
|
||||||
<msg id="8670732454866344690">on translatable node</msg>
|
<msg id="8670732454866344690">on translatable node</msg>
|
||||||
<msg id="4593805537723189714">{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex><b></ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>} }</msg>
|
<msg id="4593805537723189714">{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex><b></ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>} }</msg>
|
||||||
<msg id="1746565782635215">
|
<msg id="1746565782635215">
|
||||||
<ph name="ICU"/>
|
<ph name="ICU"><ex>ICU</ex></ph>
|
||||||
</msg>
|
</msg>
|
||||||
<msg id="5868084092545682515">{VAR_SELECT, select, m {male} f {female} }</msg>
|
<msg id="5868084092545682515">{VAR_SELECT, select, m {male} f {female} }</msg>
|
||||||
<msg id="4851788426695310455"><ph name="INTERPOLATION"/></msg>
|
<msg id="4851788426695310455"><ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph></msg>
|
||||||
<msg id="9013357158046221374">sex = <ph name="INTERPOLATION"/></msg>
|
<msg id="9013357158046221374">sex = <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph></msg>
|
||||||
<msg id="8324617391167353662"><ph name="CUSTOM_NAME"/></msg>
|
<msg id="8324617391167353662"><ph name="CUSTOM_NAME"><ex>CUSTOM_NAME</ex></ph></msg>
|
||||||
<msg id="7685649297917455806">in a translatable section</msg>
|
<msg id="7685649297917455806">in a translatable section</msg>
|
||||||
<msg id="2387287228265107305">
|
<msg id="2387287228265107305">
|
||||||
<ph name="START_HEADING_LEVEL1"><ex><h1></ex></ph>Markers in html comments<ph name="CLOSE_HEADING_LEVEL1"><ex></h1></ex></ph>
|
<ph name="START_HEADING_LEVEL1"><ex><h1></ex></ph>Markers in html comments<ph name="CLOSE_HEADING_LEVEL1"><ex></h1></ex></ph>
|
||||||
<ph name="START_TAG_DIV"><ex><div></ex></ph><ph name="CLOSE_TAG_DIV"><ex></div></ex></ph>
|
<ph name="START_TAG_DIV"><ex><div></ex></ph><ph name="CLOSE_TAG_DIV"><ex></div></ex></ph>
|
||||||
<ph name="START_TAG_DIV_1"><ex><div></ex></ph><ph name="ICU"/><ph name="CLOSE_TAG_DIV"><ex></div></ex></ph>
|
<ph name="START_TAG_DIV_1"><ex><div></ex></ph><ph name="ICU"><ex>ICU</ex></ph><ph name="CLOSE_TAG_DIV"><ex></div></ex></ph>
|
||||||
</msg>
|
</msg>
|
||||||
<msg id="1491627405349178954">it <ph name="START_BOLD_TEXT"><ex><b></ex></ph>should<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> work</msg>
|
<msg id="1491627405349178954">it <ph name="START_BOLD_TEXT"><ex><b></ex></ph>should<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> work</msg>
|
||||||
<msg id="i18n16">with an explicit ID</msg>
|
<msg id="i18n16">with an explicit ID</msg>
|
||||||
<msg id="i18n17">{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex><b></ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>} }</msg>
|
<msg id="i18n17">{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex><b></ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>} }</msg>
|
||||||
|
<msg id="4085484936881858615">{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> results} }</msg>
|
||||||
</messagebundle>
|
</messagebundle>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -108,9 +108,12 @@ export const platformCoreDynamicTesting: (extraProviders?: any[]) => PlatformRef
|
||||||
provide: COMPILER_OPTIONS,
|
provide: COMPILER_OPTIONS,
|
||||||
useValue: {
|
useValue: {
|
||||||
providers: [
|
providers: [
|
||||||
MockPipeResolver, {provide: PipeResolver, useExisting: MockPipeResolver},
|
MockPipeResolver,
|
||||||
MockDirectiveResolver, {provide: DirectiveResolver, useExisting: MockDirectiveResolver},
|
{provide: PipeResolver, useExisting: MockPipeResolver},
|
||||||
MockNgModuleResolver, {provide: NgModuleResolver, useExisting: MockNgModuleResolver}
|
MockDirectiveResolver,
|
||||||
|
{provide: DirectiveResolver, useExisting: MockDirectiveResolver},
|
||||||
|
MockNgModuleResolver,
|
||||||
|
{provide: NgModuleResolver, useExisting: MockNgModuleResolver},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
multi: true
|
multi: true
|
||||||
|
|
Loading…
Reference in New Issue