refactor(ivy): update the compiler to emit $localize tags (#31609)

This commit changes the Angular compiler (ivy-only) to generate `$localize`
tagged strings for component templates that use `i18n` attributes.

BREAKING CHANGE

Since `$localize` is a global function, it must be included in any applications
that use i18n. This is achieved by importing the `@angular/localize` package
into an appropriate bundle, where it will be executed before the renderer
needs to call `$localize`. For CLI based projects, this is best done in
the `polyfills.ts` file.

```ts
import '@angular/localize';
```

For non-CLI applications this could be added as a script to the index.html
file or another suitable script file.

PR Close #31609
This commit is contained in:
Pete Bacon Darwin
2019-07-30 18:02:17 +01:00
committed by Misko Hevery
parent b21397bde9
commit fa79f51645
35 changed files with 1173 additions and 583 deletions

View File

@ -27,6 +27,7 @@ ts_library(
"//packages/core/src/reflection",
"//packages/core/src/util",
"//packages/core/testing",
"//packages/localize",
"//packages/platform-browser",
"//packages/platform-browser-dynamic",
"//packages/platform-browser/animations",

View File

@ -5,7 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import '@angular/localize';
import {registerLocaleData} from '@angular/common';
import localeRo from '@angular/common/locales/ro';
import {Component, ContentChild, ContentChildren, Directive, HostBinding, Input, LOCALE_ID, QueryList, TemplateRef, Type, ViewChild, ViewContainerRef, ɵi18nConfigureLocalize, Pipe, PipeTransform} from '@angular/core';
@ -1130,6 +1130,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
TestBed.configureTestingModule({declarations: [ClsDir, MyApp]});
ɵi18nConfigureLocalize({
translations: {
// Not that this translation switches the order of the expressions!
'start {$interpolation} middle {$interpolation_1} end':
'début {$interpolation_1} milieu {$interpolation} fin',
'{VAR_PLURAL, plural, =0 {no {START_BOLD_TEXT}emails{CLOSE_BOLD_TEXT}!} =1 {one {START_ITALIC_TEXT}email{CLOSE_ITALIC_TEXT}} other {{INTERPOLATION} emails}}':

View File

@ -17,6 +17,7 @@ ng_module(
"//packages/common",
"//packages/core",
"//packages/core/test/bundling/util:reflect_metadata",
"//packages/localize",
],
)
@ -47,6 +48,7 @@ ts_library(
"//packages/compiler",
"//packages/core",
"//packages/core/testing",
"//packages/localize",
"//packages/private/testing",
],
)

View File

@ -5,8 +5,10 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import '@angular/core/test/bundling/util/src/reflect_metadata';
// Make the `$localize()` global function available to the compiled templates, and the direct calls
// below. This would normally be done inside the application `polyfills.ts` file.
import '@angular/localize';
/**
* TODO(ocombe): replace this with the real runtime i18n service configuration
* For now we define inline translations that are added with the function `ɵi18nConfigureLocalize`,
@ -16,31 +18,26 @@ import '@angular/core/test/bundling/util/src/reflect_metadata';
*/
import './translations';
import {CommonModule} from '@angular/common';
import {Component, Injectable, NgModule, ViewEncapsulation, ɵmarkDirty as markDirty, ɵrenderComponent as renderComponent, ɵɵi18nLocalize as localize} from '@angular/core';
import {Component, Injectable, NgModule, ViewEncapsulation, ɵmarkDirty as markDirty, ɵrenderComponent as renderComponent} from '@angular/core';
class Todo {
editing: boolean;
// TODO(issue/24571): remove '!'.
private _title !: string;
get title() { return this._title; }
set title(value: string) { this._title = value.trim(); }
constructor(title: string, public completed: boolean = false) {
this.editing = false;
this.title = title;
}
constructor(private _title: string, public completed: boolean = false) { this.editing = false; }
}
@Injectable({providedIn: 'root'})
class TodoStore {
todos: Array<Todo> = [
new Todo(localize('Demonstrate Components')),
new Todo(localize('Demonstrate Structural Directives'), true),
new Todo($localize `Demonstrate Components`),
new Todo($localize `Demonstrate Structural Directives`, true),
// Using a placeholder
new Todo(localize('Demonstrate {$value}', {value: 'NgModules'})),
new Todo(localize('Demonstrate zoneless change detection')),
new Todo(localize('Demonstrate internationalization')),
new Todo($localize `Demonstrate ${'NgModules'}:value:`),
new Todo($localize `Demonstrate zoneless change detection`),
new Todo($localize `Demonstrate internationalization`),
];
private getWithCompleted(completed: boolean) {

View File

@ -19,6 +19,12 @@ describe('functional test for todo i18n', () => {
BUNDLES.forEach(bundle => {
describe(bundle, () => {
it('should render todo i18n', withBody('<todo-app></todo-app>', async() => {
// We need to delete the dummy `$localize` that was added because of the import of
// `@angular/core` at the top of this file.
// Also to clear out the translations from the previous test.
// This would not be needed in normal applications since the import of
// `@angular/localize` would be in polyfill.ts before any other import.
($localize as any) = undefined;
require(path.join(PACKAGE, bundle));
const toDoAppComponent = getComponent(document.querySelector('todo-app') !);
expect(document.body.textContent).toContain('liste de tâches');

View File

@ -5,8 +5,7 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import '@angular/localize';
import {AfterContentInit, AfterViewInit, Component, ContentChildren, Directive, Input, QueryList, ViewChildren, ɵivyEnabled as ivyEnabled} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {isCommentNode} from '@angular/platform-browser/testing/src/browser_util';