docs(i18n): add new features (#2740)
This commit is contained in:
parent
4a536732bc
commit
9fd3fa037d
|
@ -1,4 +1,4 @@
|
|||
'use strict'; // necessary for es6 output in node
|
||||
'use strict'; // necessary for es6 output in node
|
||||
|
||||
import { browser, element, by } from 'protractor';
|
||||
|
||||
|
@ -8,8 +8,26 @@ describe('i18n E2E Tests', () => {
|
|||
browser.get('');
|
||||
});
|
||||
|
||||
it('should display i18n translated welcome: Bonjour i18n!', function () {
|
||||
expect(element(by.css('h1')).getText()).toEqual('Bonjour i18n!');
|
||||
it('should display i18n translated welcome: ¡Hola i18n!', function () {
|
||||
expect(element(by.css('h1')).getText()).toEqual('¡Hola i18n!');
|
||||
});
|
||||
|
||||
it('should display the node texts without elements', function () {
|
||||
expect(element(by.css('my-app')).getText()).toContain('No genero ningún elemento');
|
||||
expect(element(by.css('my-app')).getText()).toContain('Yo tampoco genero ningún elemento');
|
||||
});
|
||||
|
||||
it('should display the translated title attribute', function () {
|
||||
const title = element(by.css('img')).getAttribute('title');
|
||||
expect(title).toBe('Logo de Angular 2');
|
||||
});
|
||||
|
||||
it('should display the plural of: a horde of wolves', function () {
|
||||
expect(element.all(by.css('span')).get(0).getText()).toBe('ningún lobo');
|
||||
});
|
||||
|
||||
it('should display the select of gender', function () {
|
||||
expect(element.all(by.css('span')).get(1).getText()).toBe('El heroe es mujer');
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
**/*.ngfactory.ts
|
||||
**/*.metadata.json
|
||||
**/messages.xlf
|
||||
dist
|
||||
!app/tsconfig.json
|
||||
!rollup.js
|
||||
!rollup.js
|
||||
|
|
|
@ -9,3 +9,7 @@
|
|||
<!--#docregion i18n-attribute-desc-->
|
||||
<h1 i18n="An introduction header for this sample">Hello i18n!</h1>
|
||||
<!--#enddocregion i18n-attribute-desc-->
|
||||
|
||||
<!--#docregion i18n-title-->
|
||||
<img [src]="logo" title="Angular 2 logo">
|
||||
<!--#enddocregion i18n-title-->
|
||||
|
|
|
@ -1,4 +1,34 @@
|
|||
<!--#docregion-->
|
||||
<!--#docregion i18n-attribute-meaning-->
|
||||
<h1 i18n="User welcome|An introduction header for this sample">Hello i18n!</h1>
|
||||
<!--#enddocregion i18n-attribute-meaning-->
|
||||
<!--#enddocregion i18n-attribute-meaning-->
|
||||
|
||||
<!--#docregion i18n-ng-container-->
|
||||
<ng-container i18n>I don't output any element</ng-container>
|
||||
<!--#enddocregion i18n-ng-container-->
|
||||
|
||||
<br />
|
||||
|
||||
<!--#docregion i18n-with-comment-->
|
||||
<!--i18n: optional meaning|optional description -->
|
||||
I don't output any element either
|
||||
<!--/i18n-->
|
||||
<!--#enddocregion i18n-with-comment-->
|
||||
|
||||
<br />
|
||||
|
||||
<!--#docregion i18n-title-translate-->
|
||||
<img [src]="logo" i18n-title title="Angular 2 logo" />
|
||||
<!--#enddocregion i18n-title-translate-->
|
||||
<br>
|
||||
<button (click)="inc(1)">+</button> <button (click)="inc(-1)">-</button>
|
||||
<!--#docregion i18n-plural-->
|
||||
<span i18n>{wolves, plural, =0 {no wolves} =1 {one wolf} =2 {two wolves} other {a wolf pack}}</span>
|
||||
<!--#enddocregion i18n-plural-->
|
||||
({{wolves}})
|
||||
<br><br>
|
||||
<button (click)="male()">♂</button> <button (click)="female()">♀</button>
|
||||
<!--#docregion i18n-select-->
|
||||
<span i18n>The hero is {gender, select, m {male} f {female}}</span>
|
||||
<!--#enddocregion i18n-select-->
|
||||
<br>
|
||||
|
|
|
@ -6,5 +6,15 @@ import { Component } from '@angular/core';
|
|||
selector: 'my-app',
|
||||
templateUrl: 'app.component.html'
|
||||
})
|
||||
export class AppComponent { }
|
||||
export class AppComponent {
|
||||
wolves = 0;
|
||||
gender = 'f';
|
||||
fly = true;
|
||||
logo = 'https://angular.io/resources/images/logos/angular2/angular.png';
|
||||
inc(i: number) {
|
||||
this.wolves = Math.min(5, Math.max(0, this.wolves + i));
|
||||
}
|
||||
male() { this.gender = 'm'; }
|
||||
female() { this.gender = 'f'; }
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ export function getTranslationProviders(): Promise<Object[]> {
|
|||
return Promise.resolve(noProviders);
|
||||
}
|
||||
|
||||
// Ex: 'locale/messages.fr.xlf`
|
||||
// Ex: 'locale/messages.es.xlf`
|
||||
const translationFile = `./locale/messages.${locale}.xlf`;
|
||||
|
||||
return getTranslationsWithSystemJs(translationFile)
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<!-- #docregion i18n -->
|
||||
<script>
|
||||
// Get the locale id somehow
|
||||
document.locale = 'fr';
|
||||
document.locale = 'es';
|
||||
|
||||
// Map to the text plugin
|
||||
System.config({
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<file source-language="en" datatype="plaintext" original="ng2.template">
|
||||
<body>
|
||||
<trans-unit id="af2ccf4b5dba59616e92cf1531505af02da8f6d2" datatype="html">
|
||||
<source>Hello i18n!</source>
|
||||
<target>¡Hola i18n!</target>
|
||||
<note priority="1" from="description">An introduction header for this sample</note>
|
||||
<note priority="1" from="meaning">User welcome</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ba0cc104d3d69bf669f97b8d96a4c5d8d9559aa3" datatype="html">
|
||||
<source>I don't output any element</source>
|
||||
<target>No genero ningún elemento</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="df3cf8b55cb528cf8c00167e0b119bfb828538b5" datatype="html">
|
||||
<source>
|
||||
I don't output any element either
|
||||
</source>
|
||||
<target>Yo tampoco genero ningún elemento</target>
|
||||
<note priority="1" from="description">optional description</note>
|
||||
<note priority="1" from="meaning">optional meaning</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="35ab5d6121ecbe0decdda638571a5a55ac77d5c4" datatype="html">
|
||||
<source>Angular 2 logo</source>
|
||||
<target>Logo de Angular 2</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="6e22e74e8cbd3095560cfe08993c4fdfa3c50eb0" datatype="html">
|
||||
<source/>
|
||||
<target>{wolves, plural, =0 {ningún lobo} =1 {un lobo} =2 {dos lobos} other {una horda de lobos}}</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="61cafedb85466ab789b3ae817bba1a545468ee1c" datatype="html">
|
||||
<source>The hero is <x id="ICU"/></source>
|
||||
<target>El heroe es <x id="ICU"/></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="14c7055d67771a3b7b6888d282ac092896be06b6" datatype="html">
|
||||
<source/>
|
||||
<target>{gender, select, m {hombre} f {mujer}}</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -0,0 +1,54 @@
|
|||
<!-- The `messages.es.xlf` after translation for documentation purposes -->
|
||||
<!-- #docregion -->
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<file source-language="en" datatype="plaintext" original="ng2.template">
|
||||
<body>
|
||||
<!-- #docregion translated-hello -->
|
||||
<trans-unit id="af2ccf4b5dba59616e92cf1531505af02da8f6d2" datatype="html">
|
||||
<source>Hello i18n!</source>
|
||||
<target>¡Hola i18n!</target>
|
||||
<note priority="1" from="description">An introduction header for this sample</note>
|
||||
<note priority="1" from="meaning">User welcome</note>
|
||||
</trans-unit>
|
||||
<!-- #enddocregion translated-hello -->
|
||||
<!-- #docregion translated-other-nodes -->
|
||||
<trans-unit id="ba0cc104d3d69bf669f97b8d96a4c5d8d9559aa3" datatype="html">
|
||||
<source>I don't output any element</source>
|
||||
<target>No genero ningún elemento</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="df3cf8b55cb528cf8c00167e0b119bfb828538b5" datatype="html">
|
||||
<source>I don't output any element either</source>
|
||||
<target>Yo tampoco genero ningún elemento</target>
|
||||
<note priority="1" from="description">optional description</note>
|
||||
<note priority="1" from="meaning">optional meaning</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="35ab5d6121ecbe0decdda638571a5a55ac77d5c4" datatype="html">
|
||||
<source>Angular 2 logo</source>
|
||||
<target>Logo de Angular 2</target>
|
||||
</trans-unit>
|
||||
<!-- #enddocregion translated-other-nodes -->
|
||||
<!-- #docregion translated-plural -->
|
||||
<trans-unit id="6e22e74e8cbd3095560cfe08993c4fdfa3c50eb0" datatype="html">
|
||||
<source/>
|
||||
<target>{wolves, plural, =0 {ningún lobo} =1 {un lobo} =2 {dos lobos} other {una horda de lobos}}</target>
|
||||
</trans-unit>
|
||||
<!-- #enddocregion translated-plural -->
|
||||
<!-- #docregion translated-select -->
|
||||
<!-- #docregion translate-select-1 -->
|
||||
<trans-unit id="61cafedb85466ab789b3ae817bba1a545468ee1c" datatype="html">
|
||||
<source>The hero is <x id="ICU"/></source>
|
||||
<target>El heroe es <x id="ICU"/></target>
|
||||
</trans-unit>
|
||||
<!-- #enddocregion translate-select-1 -->
|
||||
<!-- #docregion translate-select-2 -->
|
||||
<trans-unit id="14c7055d67771a3b7b6888d282ac092896be06b6" datatype="html">
|
||||
<source/>
|
||||
<target>{gender, select, m {hombre} f {mujer}}</target>
|
||||
</trans-unit>
|
||||
<!-- #enddocregion translate-select-2 -->
|
||||
<!-- #enddocregion translated-select -->
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<file source-language="en" datatype="plaintext" original="ng2.template">
|
||||
<body>
|
||||
<trans-unit id="af2ccf4b5dba59616e92cf1531505af02da8f6d2" datatype="html">
|
||||
<source>Hello i18n!</source>
|
||||
<target>Bonjour i18n!</target>
|
||||
<note priority="1" from="description">An introduction header for this sample</note>
|
||||
<note priority="1" from="meaning">User welcome</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -1,17 +0,0 @@
|
|||
<!-- The `messages.fr.xlf` after translation for documentation purposes -->
|
||||
<!-- #docregion -->
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<file source-language="en" datatype="plaintext" original="ng2.template">
|
||||
<body>
|
||||
<!-- #docregion trans-unit -->
|
||||
<trans-unit id="af2ccf4b5dba59616e92cf1531505af02da8f6d2" datatype="html">
|
||||
<source>Hello i18n!</source>
|
||||
<target>Bonjour i18n!</target>
|
||||
<note priority="1" from="description">An introduction header for this sample</note>
|
||||
<note priority="1" from="meaning">User welcome</note>
|
||||
</trans-unit>
|
||||
<!-- #enddocregion trans-unit -->
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -1,7 +0,0 @@
|
|||
<!-- #docregion -->
|
||||
<trans-unit id="af2ccf4b5dba59616e92cf1531505af02da8f6d2" datatype="html">
|
||||
<source>Hello i18n!</source>
|
||||
<target/>
|
||||
<note priority="1" from="description">An introduction header for this sample</note>
|
||||
<note priority="1" from="meaning">User welcome</note>
|
||||
</trans-unit>
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<file source-language="en" datatype="plaintext" original="ng2.template">
|
||||
<body>
|
||||
<trans-unit id="af2ccf4b5dba59616e92cf1531505af02da8f6d2" datatype="html">
|
||||
<source>Hello i18n!</source>
|
||||
<target/>
|
||||
<note priority="1" from="description">An introduction header for this sample</note>
|
||||
<note priority="1" from="meaning">User welcome</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ba0cc104d3d69bf669f97b8d96a4c5d8d9559aa3" datatype="html">
|
||||
<source>I don't output any element</source>
|
||||
<target/>
|
||||
</trans-unit>
|
||||
<trans-unit id="df3cf8b55cb528cf8c00167e0b119bfb828538b5" datatype="html">
|
||||
<source>
|
||||
I don't output any element either
|
||||
</source>
|
||||
<target/>
|
||||
<note priority="1" from="description">optional description</note>
|
||||
<note priority="1" from="meaning">optional meaning</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="35ab5d6121ecbe0decdda638571a5a55ac77d5c4" datatype="html">
|
||||
<source>Angular 2 logo</source>
|
||||
<target/>
|
||||
</trans-unit>
|
||||
<trans-unit id="6e22e74e8cbd3095560cfe08993c4fdfa3c50eb0" datatype="html">
|
||||
<source/>
|
||||
<target/>
|
||||
</trans-unit>
|
||||
<trans-unit id="61cafedb85466ab789b3ae817bba1a545468ee1c" datatype="html">
|
||||
<source>The hero is <x id="ICU"/></source>
|
||||
<target/>
|
||||
</trans-unit>
|
||||
<trans-unit id="14c7055d67771a3b7b6888d282ac092896be06b6" datatype="html">
|
||||
<source/>
|
||||
<target/>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
|
@ -4,8 +4,8 @@
|
|||
"app/**/*.css",
|
||||
"app/**/*.html",
|
||||
"app/**/*.ts",
|
||||
"locale/messages.xlf",
|
||||
"locale/messages.fr.xlf",
|
||||
"messages.xlf",
|
||||
"locale/messages.*.xlf",
|
||||
|
||||
"!**/*.[1].*",
|
||||
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
include ../_util-fns
|
||||
|
||||
a#top
|
||||
:marked
|
||||
Angular's _internationalization_ ("_i18n_") tools help make your app available in multiple languages.
|
||||
|
||||
<a id="top"></a>
|
||||
## Table of contents
|
||||
|
||||
|
||||
* [Angular and i18n template translation](#angular-i18n)
|
||||
* [Mark text with the _i18n_ attribute](#i18n-attribute)
|
||||
* [Create a translation source file with the _ng-xi18n_ tool](#ng-xi18n)
|
||||
* [Add _i18n-..._ translation attributes](#translate-attributes)
|
||||
* [Handle singular and plural](#cardinality)
|
||||
* [Select among alternative texts](#select)
|
||||
* [Use the **_ng-xi18n_ extraction tool** to create a translation source file](#ng-xi18n)
|
||||
* [Translate](#translate)
|
||||
* [Merge the completed translation file into the app](#merge)
|
||||
* [JiT configuration](#jit)
|
||||
* [AoT configuration](#aot)
|
||||
* [Translation file maintenance and _id_ changes](#maintenance)
|
||||
|
||||
:marked
|
||||
**Try this** <live-example name="cb-i18n">live example</live-example> of a JiT-compiled app, translated into French.
|
||||
**Try this** <live-example name="cb-i18n" title="i18n Example in Spanish">live example</live-example>
|
||||
of a JiT-compiled app, translated into Spanish.
|
||||
|
||||
|
||||
a#angular-i18n
|
||||
|
@ -67,7 +72,7 @@ a#i18n-attribute
|
|||
It will be removed by the compiler _after_ translation.
|
||||
|
||||
:marked
|
||||
In the accompanying sample, an `<h1>` tag displays a simple English language greeting which you will translate to French:
|
||||
In the accompanying sample, an `<h1>` tag displays a simple English language greeting which you will translate to Spanish:
|
||||
+makeExample('cb-i18n/ts/app/app.component.1.html', 'greeting', 'app/app.component.html')(format=".")
|
||||
:marked
|
||||
Add the `i18n` attribute to the tag to mark it for translation.
|
||||
|
@ -75,29 +80,132 @@ a#i18n-attribute
|
|||
+makeExample('cb-i18n/ts/app/app.component.1.html', 'i18n-attribute', 'app/app.component.html')(format=".")
|
||||
|
||||
:marked
|
||||
### Help the translator with a _description_ and _intent_
|
||||
|
||||
The translator may need a description of the message to translate it accurately.
|
||||
Assign a description to the i18n attribute:
|
||||
|
||||
+makeExample('cb-i18n/ts/app/app.component.1.html', 'i18n-attribute-desc', 'app/app.component.html')(format=".")
|
||||
|
||||
:marked
|
||||
The true _meaning_ of the text may require some application context.
|
||||
Add a contextual meaning to the assigned string, separating it from the description with the `|` character:
|
||||
The translator may need to know your _intent_ —
|
||||
the true _meaning_ of the text within _this particular_ application context —
|
||||
in order to deliver a correct translation.
|
||||
Add some contextual meaning to the assigned string, in front of the description,
|
||||
separating it from the description with the `|` character (`<meaning>|<description>`):
|
||||
|
||||
+makeExample('cb-i18n/ts/app/app.component.html', 'i18n-attribute-meaning', 'app/app.component.html')(format=".")
|
||||
|
||||
:marked
|
||||
While all appearance of a message with the _same_ meaning should have the _same_ translation,
|
||||
While all appearances of a message with the _same_ meaning have the _same_ translation,
|
||||
a message with *different meanings* could have different translations.
|
||||
The Angular extraction tool preserves both the _meaning_ and the _description_ in the translation source file
|
||||
to facilitiate contextually-specific translations.
|
||||
|
||||
### Translate text without creating an element
|
||||
|
||||
Suppose there is a stretch of text that you'd like to translate.
|
||||
You could wrap it in a `<span>` but for some reason (CSS comes to mind)
|
||||
you don't want to create a new DOM element merely to facilitate translation.
|
||||
|
||||
Here are two techniques to try.
|
||||
|
||||
(1) Wrap the text in an `<ng-container>` element. The `<ng-container>` is never renderered:
|
||||
|
||||
+makeExample('cb-i18n/ts/app/app.component.html', 'i18n-ng-container', 'app/app.component.html')(format=".")
|
||||
|
||||
:marked
|
||||
(2) Wrap the text in a pair of HTML comments:
|
||||
|
||||
+makeExample('cb-i18n/ts/app/app.component.html', 'i18n-with-comment', 'app/app.component.html')(format=".")
|
||||
|
||||
.l-main-section
|
||||
a#translate-attributes
|
||||
:marked
|
||||
## Add _i18n-..._ translation attributes
|
||||
You've added an image to your template. You care about accessibility too so you add a `title` tag:
|
||||
|
||||
+makeExample('cb-i18n/ts/app/app.component.1.html', 'i18n-title', 'app/app.component.html')(format=".")
|
||||
|
||||
:marked
|
||||
The `title` attribute needs to be translated.
|
||||
Angular i18n support has more translation attributes in the form,`i18n-x`, where `x` is the
|
||||
name of the attribute to translate.
|
||||
|
||||
To translate the `title` on the `img` tag from the previous example, write:
|
||||
|
||||
+makeExample('cb-i18n/ts/app/app.component.html', 'i18n-title-translate', 'app/app.component.html')(format=".")
|
||||
|
||||
:marked
|
||||
You can also assign a meaning and a description with the `i18n-x="<meaning>|<description>"` syntax.
|
||||
|
||||
.l-main-section
|
||||
a#cardinality
|
||||
:marked
|
||||
## Handle singular and plural
|
||||
|
||||
Different languages have different pluralization rules.
|
||||
|
||||
Suppose your application says something about a collection of wolves.
|
||||
In English, depending upon the number of wolves, you could display "no wolves", "one wolf", "two wolves", or "a wolf pack".
|
||||
Other languages might express the _cardinality_ differently.
|
||||
|
||||
Here's how you could markup the component template to display the phrase appropriate to the number of wolves:
|
||||
|
||||
+makeExample('cb-i18n/ts/app/app.component.html', 'i18n-plural', 'app/app.component.html')(format=".")
|
||||
|
||||
:marked
|
||||
* The first parameter is the key. It will be bound to the component property (`wolves`) that determines the number of wolves.
|
||||
* The second parameter indentifies this as a `plural` translation type.
|
||||
* The third parameter defines a pluralization pattern consisting of pluralization categories and their matching values.
|
||||
|
||||
Pluralization categories include:
|
||||
* =0
|
||||
* =1
|
||||
* =5
|
||||
* few
|
||||
* other
|
||||
|
||||
Put the default _English_ translation in braces (`{}`) next to the pluralization category.
|
||||
* When you're talking about one wolf, you could write `=1 {one wolf}`.
|
||||
* For zero wolves, you could write `=0 {no wolves}`.
|
||||
* For two wolves, you could write `=2 {two wolves}`.
|
||||
|
||||
You could keep this up for three, four, and every other number of wolves.
|
||||
Or you could specify the **`other`** category as a catch-all for any unmatched cardinality
|
||||
and write something like: `other {a wolf pack}`.
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
This syntax conforms to the
|
||||
<a href="http://userguide.icu-project.org/formatparse/messages" target="_blank" title="ICU Message Format">ICU Message Format</a>
|
||||
which derives from the
|
||||
<a href="http://cldr.unicode.org/" target="_blank" title="CLDR">Common Locale Data Repository (CLDR)</a>
|
||||
which specifies the
|
||||
<a href="http://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules" target="_blank" title="Pluralization Rules">pluralization rules</a>.
|
||||
|
||||
a#select
|
||||
:marked
|
||||
## Select
|
||||
The application displays different text depending upon whether the hero is male or female.
|
||||
These text alternatives require translation too.
|
||||
|
||||
You can handle this with a `select` translation.
|
||||
A `select` also follows the
|
||||
<a href="http://userguide.icu-project.org/formatparse/messages" target="_blank" title="ICU Message Format">ICU message syntax</a>.
|
||||
You choose among alternative translation based on a string value instead of a number.
|
||||
|
||||
The following format message in the component template binds to the component's `gender` property which outputs either an 'm' or an 'f'.
|
||||
The message maps those values to the appropriate translation:
|
||||
|
||||
+makeExample('cb-i18n/ts/app/app.component.html', 'i18n-select', 'app/app.component.html')(format=".")
|
||||
|
||||
a#ng-xi18n
|
||||
.l-main-section
|
||||
:marked
|
||||
## Create a translation source file with the _ng-xi18n_ tool
|
||||
|
||||
Use the `ng-xi18n` extraction tool to extract the `i18n`-marked texts
|
||||
Use the **_ng-xi18n_ extraction tool** to extract the `i18n`-marked texts
|
||||
into a translation source file in an industry standard format.
|
||||
|
||||
This is an Angular CLI tool in the `@angular/compiler-cli` npm package.
|
||||
|
@ -112,48 +220,68 @@ code-example(language="sh" class="code-shell").
|
|||
code-example(language="sh" class="code-shell").
|
||||
./node_modules/.bin/ng-xi18n
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
Windows users may have to quote the command: like this `"./node_modules/.bin/ng-xi18n"`
|
||||
|
||||
:marked
|
||||
By default the tool generates a translation file named **`messages.xlf`** in the
|
||||
<a href="https://en.wikipedia.org/wiki/XLIFF" target="_blank">XML Localisation Interchange File Format (XLIFF, version 1.2)</a>.
|
||||
|
||||
code-example(language="sh" class="code-shell").
|
||||
./node_modules/.bin/ng-xi18n --i18nFormat=xmb
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
Windows users may have to quote the command:
|
||||
code-example(language="sh").
|
||||
"./node_modules/.bin/ng-xi18n"
|
||||
:marked
|
||||
Consider adding a convenience shortcut to the `scripts` section of the `package.json`
|
||||
to make the command easier to remember and run:
|
||||
code-example(format='.').
|
||||
"scripts": {
|
||||
"i18n": "ng-xi18n",
|
||||
...
|
||||
}
|
||||
:marked
|
||||
Now you can enter:
|
||||
code-example(language="sh" class="code-shell").
|
||||
npm run i18n
|
||||
|
||||
a#other-formats
|
||||
:marked
|
||||
### Other translation formats
|
||||
|
||||
You can generate a file named **`messages.xmb`** in the
|
||||
<a href="http://cldr.unicode.org/development/development-process/design-proposals/xmb" target="_blank">XML Message Bundle (XMB)</a> format
|
||||
by adding the `--i18nFormat=xmb` switch.
|
||||
by adding the `--i18nFormat=xmb` flag.
|
||||
|
||||
code-example(language="sh" class="code-shell").
|
||||
./node_modules/.bin/ng-xi18n --i18nFormat=xmb
|
||||
|
||||
:marked
|
||||
This sample sticks with the _XLIFF_ format.
|
||||
|
||||
a#ng-xi18n-options
|
||||
:marked
|
||||
### Other options
|
||||
You may have to specify additional options.
|
||||
For example, if the `tsconfig.json` TypeScript configuration file is located other than in the root folder,
|
||||
you must identify the path to it with the `-p` option:
|
||||
code-example(language="sh" class="code-shell").
|
||||
./node_modules/.bin/ng-xi18n -p path/to/tsconfig.json
|
||||
./node_modules/.bin/ng-xi18n --i18nFormat=xmb -p path/to/tsconfig.json
|
||||
|
||||
|
||||
a#npm-i18n-script
|
||||
:marked
|
||||
### Add an _npm_ script for convenience
|
||||
|
||||
Consider adding a convenience shortcut to the `scripts` section of the `package.json`
|
||||
to make the command easier to remember and run:
|
||||
code-example(format='.' language='sh' ).
|
||||
"scripts": {
|
||||
"i18n": "ng-xi18n",
|
||||
...
|
||||
}
|
||||
:marked
|
||||
Now you can issue command variations such as these:
|
||||
code-example(language="sh" class="code-shell").
|
||||
npm run i18n
|
||||
npm run i18n -- -p path/to/tsconfig.json
|
||||
npm run i18n -- --i18nFormat=xmb -p path/to/tsconfig.json
|
||||
:marked
|
||||
Note the `--` flag before the options.
|
||||
It tells _npm_ that every flag thereafter should be passed through to `ng-xi18n`.
|
||||
|
||||
a#translate
|
||||
.l-main-section
|
||||
:marked
|
||||
## Translate _le message textuel_
|
||||
## Translate _los mensajes de texto_
|
||||
|
||||
The `ng-xi18n` command generated a translation source file in the project root folder named `messages.xlf`.
|
||||
The next step is to translate the English language template text into the specific language translation
|
||||
files. The cookbook sample creates a French translation file.
|
||||
files. The cookbook sample creates a Spanish translation file.
|
||||
|
||||
a#localization-folder
|
||||
:marked
|
||||
|
@ -169,39 +297,88 @@ a#localization-folder
|
|||
Localization and internationalization are
|
||||
<a href="https://en.wikipedia.org/wiki/Internationalization_and_localization" target="_blank">different but closely related terms</a>.
|
||||
:marked
|
||||
This sample follows that suggestion. It has `locale` folder immediately under the project root.
|
||||
This sample follows that suggestion. It has a `locale` folder under the project root.
|
||||
Assets within the folder carry a filename extension that matches a language-culture code from a
|
||||
<a href="https://msdn.microsoft.com/en-us/library/ee825488(v=cs.20).aspx" target="_blank">well-known codeset</a>.
|
||||
|
||||
Move `messages.xlf` into the `locale` folder where it will become the source for all language-specific translations.
|
||||
Then make a copy for the French language named `messages.fr.xlf` .
|
||||
Make a copy of the `messages.xlf` file in the `locale` folder and
|
||||
rename it `messages.es.xlf`for the Spanish language translation.
|
||||
Do the same for each target language.
|
||||
|
||||
Follow the same convention for each target language.
|
||||
|
||||
### Translate
|
||||
In the real world, you send the `messages.fr.xlf` file to a French translator who would fill in the translations
|
||||
using one of the
|
||||
### Translate text nodes
|
||||
In the real world, you send the `messages.es.xlf` file to a Spanish translator who fills in the translations
|
||||
using one of the
|
||||
<a href="https://en.wikipedia.org/wiki/XLIFF#Editors" target="_blank">many XLIFF file editors</a>.
|
||||
|
||||
This sample file is easy to translate without a special editor or knowledge of French.
|
||||
Open `messages.fr.xlf` and find the `<trans-unit>` section:
|
||||
This sample file is easy to translate without a special editor or knowledge of Spanish.
|
||||
Open `messages.es.xlf` and find the first `<trans-unit>` section:
|
||||
|
||||
+makeExample('cb-i18n/ts/locale/trans-unit.html', '', 'locale/messages.fr.xlf (<trans-unit>)')(format=".")
|
||||
+makeExample('cb-i18n/ts/locale/messages.es.xlf.html', 'translated-hello', 'locale/messages.es.xlf (<trans-unit>)')(format=".")
|
||||
:marked
|
||||
This XML element represents the translation of the `<h1>` greeting tag you marked with the `i18n` attribute.
|
||||
|
||||
Using the _source_, _description_, and _meaning_ elements to guide your translation,
|
||||
replace the `<target/>` tag with the French greeting:
|
||||
+makeExample('cb-i18n/ts/locale/messages.fr.xlf.html', 'trans-unit', 'locale/messages.fr.xlf (<trans-unit>, after translation)')(format=".")
|
||||
:marked
|
||||
Note that the `id` is generated by the tool. Don't touch it.
|
||||
Its value depends on the content of the message and its assigned meaning.
|
||||
Change either factor and the `id` changes as well.
|
||||
.alert.is-helpful
|
||||
replace the `<target/>` tag with the Spanish greeting:
|
||||
+makeExample('cb-i18n/ts/locale/messages.es.xlf.html', 'translated-hello', 'locale/messages.es.xlf (<trans-unit>, after translation)')(format=".")
|
||||
|
||||
.alert.is-important
|
||||
:marked
|
||||
Repeat the extraction process whenever you add new app messages or edit existing ones.
|
||||
Be careful not to lose the previous translations.
|
||||
Specialized software can help manage the change process.
|
||||
Note that the `id` is generated by the tool. **Don't touch it.**
|
||||
Its value depends on the content of the message and its assigned meaning.
|
||||
Change either factor and the `id` changes as well.
|
||||
See the **[translation file maintenance discussion](#maintenance)** below.
|
||||
|
||||
:marked
|
||||
Translate the other text nodes the same way:
|
||||
|
||||
+makeExample('cb-i18n/ts/locale/messages.es.xlf.html', 'translated-other-nodes', 'locale/messages.es.xlf (<trans-unit>)')(format=".")
|
||||
|
||||
a#translate-plural-select
|
||||
:marked
|
||||
## Translate _plural_ and _select_
|
||||
Translating _plural_ and _select_ messages is a little tricky.
|
||||
|
||||
The `<source>` tag is empty for `plural` and `select` translation units which makes them hard to correlate with the original template.
|
||||
The ICU rules are not yet supported in the `XLIFF` format; they soon will be.
|
||||
The ICU rules _are_ supported in the `XMB` format.
|
||||
|
||||
You'll just have to look for them in relation to other translation units that you recognize from elsewhere in the source template.
|
||||
In this example, you know the translation unit for the `select` must be just below the translation unit for the logo.
|
||||
|
||||
:marked
|
||||
### Translate _plural_
|
||||
To translate a `plural`, translate its ICU format match values:
|
||||
|
||||
+makeExample('cb-i18n/ts/locale/messages.es.xlf.html', 'translated-plural', 'locale/messages.es.xlf (<trans-unit>)')(format=".")
|
||||
|
||||
:marked
|
||||
### Translate _select_
|
||||
The `select` behaves a little differently. Here again is the ICU format message in the component template:
|
||||
|
||||
+makeExample('cb-i18n/ts/app/app.component.html', 'i18n-select', 'app/app.component.html')(format=".")
|
||||
|
||||
:marked
|
||||
The extraction tool broke that into _two_ translation units.
|
||||
|
||||
The first unit contains the text that was _outside_ the `select`.
|
||||
In place of the `select` is a placeholder, `<x id="ICU">`, that represents the `select` message.
|
||||
Translate the text and leave the placeholder where it is.
|
||||
|
||||
+makeExample('cb-i18n/ts/locale/messages.es.xlf.html', 'translate-select-1', 'locale/messages.es.xlf (<trans-unit>)')(format=".")
|
||||
|
||||
:marked
|
||||
The second translation unit, immediately below the first one, contains the `select` message. Translate that.
|
||||
|
||||
+makeExample('cb-i18n/ts/locale/messages.es.xlf.html', 'translate-select-2', 'locale/messages.es.xlf (<trans-unit>)')(format=".")
|
||||
|
||||
:marked
|
||||
Here they are together, after translation:
|
||||
|
||||
+makeExample('cb-i18n/ts/locale/messages.es.xlf.html', 'translated-select', 'locale/messages.es.xlf (<trans-unit>)')(format=".")
|
||||
|
||||
.l-main-content
|
||||
:marked
|
||||
The entire template translation is complete. It's time to incorporate that translation in the application.
|
||||
|
||||
#app-pre-translation
|
||||
:marked
|
||||
|
@ -214,13 +391,13 @@ a#localization-folder
|
|||
cb-i18n/ts/app/app.component.ts,
|
||||
cb-i18n/ts/app/app.module.ts,
|
||||
cb-i18n/ts/app/main.1.ts,
|
||||
cb-i18n/ts/locale/messages.fr.xlf.html
|
||||
cb-i18n/ts/locale/messages.es.xlf.html
|
||||
`, '', `
|
||||
app/app.component.html,
|
||||
app/app.component.ts,
|
||||
app/app.module.ts,
|
||||
app/main.ts,
|
||||
locale/messages.fr.xlf
|
||||
locale/messages.es.xlf
|
||||
`)
|
||||
|
||||
a#merge
|
||||
|
@ -237,7 +414,7 @@ a#merge
|
|||
* the translation file
|
||||
* the translation file format
|
||||
* the <a href="https://en.wikipedia.org/wiki/XLIFF" target="_blank">_Locale ID_</a>
|
||||
(`fr` or `en-US` for instance)
|
||||
(`es` or `en-US` for instance)
|
||||
|
||||
_How_ you provide this information depends upon whether you compile with
|
||||
the JiT (_Just-in-Time_) compiler or the AoT (_Ahead-of-Time_) compiler.
|
||||
|
@ -320,7 +497,7 @@ a#text-plugin
|
|||
Notice that it waits for the `getTranslationProviders` promise to resolve before
|
||||
bootstrapping the app.
|
||||
|
||||
The app is now _internationalized_ for English and French and there is a clear path for adding
|
||||
The app is now _internationalized_ for English and Spanish and there is a clear path for adding
|
||||
more languages.
|
||||
|
||||
a#aot
|
||||
|
@ -352,12 +529,27 @@ a#aot
|
|||
* `--locale`: the name of the locale
|
||||
* `--i18nFormat`: the format of the localization file
|
||||
|
||||
For this sample, the French language command would be
|
||||
For this sample, the Spanish language command would be
|
||||
code-example(language="sh" class="code-shell").
|
||||
./node_modules/.bin/ngc --i18nFile=./locale/messages.fr.xlf --locale=fr --i18nFormat=xlf
|
||||
./node_modules/.bin/ngc --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
Windows users may have to quote the command:
|
||||
code-example(language="sh" class="code-shell").
|
||||
"./node_modules/.bin/ngc" --i18nFile=./locale/messages.fr.xlf --locale=fr --i18nFormat=xlf
|
||||
"./node_modules/.bin/ngc" --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf
|
||||
|
||||
a#maintenance
|
||||
:marked
|
||||
## Translation file maintenance and _id_ changes
|
||||
|
||||
As the application evolves, you will change the _i18n_ markup and re-run the `ng-xi18n` extraction tool many times.
|
||||
The _new_ markup that you add is not a problem.
|
||||
But _most_ changes to _existing_ markup will trigger generation of _new_ `id`s for the affected translation units.
|
||||
|
||||
After an `id` changes, the translation files are no longer in-sync.
|
||||
**All translated versions of the application will fail** during re-compilation.
|
||||
The error messages identify the old `id`s that are no longer valid but they don't tell you what the new `id`s should be.
|
||||
|
||||
**Commit all translation message files to source control**, especially the English source `messages.xlf`.
|
||||
The difference between the old and the new `messages.xlf` file will help you find and update `id` changes across your translation files.
|
||||
|
|
|
@ -5,6 +5,11 @@ block includes
|
|||
The Angular documentation is a living document with continuous improvements.
|
||||
This log calls attention to recent significant changes.
|
||||
|
||||
## Internationalization: pluralization and _select_ (2016-11-30)
|
||||
The [Internationalization (i18n)](i18n.html) guide explains how to handle pluralization and
|
||||
translation of alternative texts with `select`.
|
||||
The sample demonstrates these features too.
|
||||
|
||||
## Testing: karma file updates (2016-11-30)
|
||||
* karma.config + karma-test-shim can handle multiple spec source paths;
|
||||
see quickstart issue: [angular/quickstart#294](https://github.com/angular/quickstart/issues/294)
|
||||
|
|
Loading…
Reference in New Issue