753 lines
28 KiB
Markdown
753 lines
28 KiB
Markdown
# Internationalization (i18n)
|
|
|
|
Angular's _internationalization_ (_i18n_) tools help make your app available in multiple languages.
|
|
|
|
Try this <live-example name="i18n" title="i18n Example in Spanish">live example</live-example>
|
|
of a JIT-compiled app, translated into Spanish.
|
|
|
|
{@a angular-i18n}
|
|
|
|
## Angular and _i18n_ template translation
|
|
|
|
Application internationalization is a challenging, many-faceted effort that
|
|
takes dedication and enduring commitment.
|
|
Angular's _i18n_ internationalization facilities can help.
|
|
|
|
This page describes the _i18n_ tools available to assist translation of component template text
|
|
into multiple languages.
|
|
|
|
<div class="l-sub-section">
|
|
|
|
Practitioners of _internationalization_ refer to a translatable text as a "_message_".
|
|
This page uses the words "_text_" and "_message_" interchangeably and in the combination, "_text message_".
|
|
|
|
</div>
|
|
|
|
The _i18n_ template translation process has four phases:
|
|
|
|
1. Mark static text messages in your component templates for translation.
|
|
|
|
1. An angular _i18n_ tool extracts the marked messages into an industry standard translation source file.
|
|
|
|
1. A translator edits that file, translating the extracted text messages into the target language,
|
|
and returns the file to you.
|
|
|
|
1. The Angular compiler imports the completed translation files,
|
|
replaces the original messages with translated text, and generates a new version of the application
|
|
in the target language.
|
|
|
|
You need to build and deploy a separate version of the application for each supported language.
|
|
|
|
{@a i18n-attribute}
|
|
|
|
## Mark text with the _i18n_ attribute
|
|
|
|
The Angular `i18n` attribute is a marker for translatable content.
|
|
Place it on every element tag whose fixed text should be translated.
|
|
|
|
|
|
<div class="alert is-helpful">
|
|
|
|
`i18n` is not an Angular _directive_.
|
|
It's a custom _attribute_, recognized by Angular tools and compilers.
|
|
After translation, the compiler removes it.
|
|
|
|
</div>
|
|
|
|
In the accompanying sample, an `<h1>` tag displays a simple English language greeting
|
|
that you translate into Spanish:
|
|
|
|
<code-example path="i18n/src/app/app.component.1.html" region="greeting" title="src/app/app.component.html" linenums="false">
|
|
</code-example>
|
|
|
|
Add the `i18n` attribute to the tag to mark it for translation.
|
|
|
|
<code-example path="i18n/src/app/app.component.1.html" region="i18n-attribute" title="src/app/app.component.html" linenums="false">
|
|
</code-example>
|
|
|
|
{@a help-translator}
|
|
|
|
### Help the translator with a _description_ and _meaning_
|
|
|
|
In order to translate it accurately, the translator may
|
|
need a description of the message.
|
|
Assign a description to the i18n attribute:
|
|
|
|
<code-example path="i18n/src/app/app.component.1.html" region="i18n-attribute-desc" title="src/app/app.component.html" linenums="false">
|
|
</code-example>
|
|
|
|
In order to deliver a correct translation, the translator may need to
|
|
know the _meaning_ or _intent_ of the text within _this particular_ application context.
|
|
|
|
You add context by beginning the string with the _meaning_ and
|
|
separating it from the _description_ with the `|` character (`<meaning>|<description>`):
|
|
|
|
<code-example path="i18n/src/app/app.component.1.html" region="i18n-attribute-meaning" title="src/app/app.component.html" linenums="false">
|
|
</code-example>
|
|
|
|
While all appearances of a message with the _same_ meaning have the _same_ translation,
|
|
a message with *a variety of possible meanings* could have different translations.
|
|
The Angular extraction tool preserves both the _meaning_ and the _description_ in the translation source file
|
|
to facilitate contextually-specific translations.
|
|
|
|
|
|
{@a custom-id}
|
|
|
|
### Set a custom _id_ to improve search and maintenance
|
|
|
|
The angular _i18n_ extractor tool generates a file with a _translation unit_ entry for each `i18n` attribute in a template. By default, it assigns each translation unit a unique _id_ such as this one:
|
|
|
|
<code-example path="i18n/src/locale/messages.es.xlf.html" region="generated-id" linenums="false">
|
|
</code-example>
|
|
|
|
This _id_ is obscure and difficult for humans to read or remember.
|
|
|
|
Worse, when you change the translatable text, perhaps to fix a typo,
|
|
the extractor tool generates a new _id_ for that translation.
|
|
You will lose the translation unless you update it with the new _id_.
|
|
That [complicates maintenance](#maintenance).
|
|
|
|
Consider specifying your own, meaningful _id_ in the `i18n` attribute, **prefixed with `@@`**.
|
|
|
|
<code-example path='i18n/src/app/app.component.1.html' region='i18n-attribute-solo-id' title='app/app.component.html' linenums="false">
|
|
</code-example>
|
|
|
|
Now the extractor tool and compiler will generate a translation unit with _your custom id_ and never change it.
|
|
|
|
<code-example path="i18n/src/locale/messages.es.xlf.html" region="custom-id" linenums="false">
|
|
</code-example>
|
|
|
|
Here is the `i18n` attribute with a _definition_, followed by the custom `id`:
|
|
|
|
<code-example path='i18n/src/app/app.component.1.html' region='i18n-attribute-id' title='app/app.component.html' linenums="false">
|
|
</code-example>
|
|
|
|
Here is a _meaning_ and a _description_ and the _id_ at the end:
|
|
|
|
<code-example path='i18n/src/app/app.component.1.html' region='i18n-attribute-meaning-and-id' title='app/app.component.html' linenums="false">
|
|
</code-example>
|
|
|
|
<div class="l-sub-section">
|
|
|
|
Be sure to define _unique_ custom ids. If you use the same id for 2 _different_ blocks of text, only the first one will be extracted,
|
|
and its translation used in both blocks of text.
|
|
|
|
For example:
|
|
|
|
```html
|
|
<p i18n="@@myId">Hello</p>
|
|
<p i18n="@@myId">Good bye</p>
|
|
```
|
|
|
|
with the translation:
|
|
|
|
```xml
|
|
<trans-unit id="myId" datatype="html">
|
|
<source>Hello</source>
|
|
<target state="new">Hola</target>
|
|
</trans-unit>
|
|
```
|
|
|
|
Both `<p>` elements will contain the text `Hola`.
|
|
|
|
</div>
|
|
|
|
{@a no-element}
|
|
|
|
### 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>` tag 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 rendered:
|
|
|
|
<code-example path="i18n/src/app/app.component.html" region="i18n-ng-container" title="src/app/app.component.html" linenums="false">
|
|
</code-example>
|
|
|
|
(2) Wrap the text in a pair of HTML comments:
|
|
|
|
<code-example path="i18n/src/app/app.component.html" region="i18n-with-comment" title="src/app/app.component.html" linenums="false">
|
|
</code-example>
|
|
|
|
{@a translate-attributes}
|
|
|
|
## Add _i18n_ translation attributes
|
|
You've added an image to your template. You care about accessibility too so you add a `title` attribute:
|
|
|
|
<code-example path="i18n/src/app/app.component.1.html" region="i18n-title" title="src/app/app.component.html" linenums="false">
|
|
</code-example>
|
|
|
|
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:
|
|
|
|
<code-example path="i18n/src/app/app.component.html" region="i18n-title-translate" title="src/app/app.component.html" linenums="false">
|
|
</code-example>
|
|
|
|
You can also assign a meaning and a description with the `i18n-x="<meaning>|<description>"` syntax.
|
|
|
|
{@a cardinality}
|
|
|
|
## 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 mark up the component template to display the phrase appropriate to the number of wolves:
|
|
|
|
<code-example path="i18n/src/app/app.component.html" region="i18n-plural" title="src/app/app.component.html" linenums="false">
|
|
</code-example>
|
|
|
|
* The first parameter is the key. It is bound to the component property (`wolves`)
|
|
that determines the number of wolves.
|
|
* The second parameter identifies 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 (or any other number)
|
|
* zero
|
|
* one
|
|
* two
|
|
* few
|
|
* many
|
|
* 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}`.
|
|
|
|
<div class="l-sub-section">
|
|
|
|
This syntax conforms to the
|
|
<a href="http://userguide.icu-project.org/formatparse/messages" title="ICU Message Format">ICU Message Format</a>
|
|
that derives from the
|
|
<a href="http://cldr.unicode.org/" title="CLDR">Common Locale Data Repository (CLDR)</a>,
|
|
which specifies the
|
|
<a href="http://cldr.unicode.org/index/cldr-spec/plural-rules" title="Pluralization Rules">pluralization rules</a>.
|
|
|
|
</div>
|
|
|
|
{@a select}
|
|
|
|
## Select among alternative texts
|
|
|
|
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" 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:
|
|
|
|
<code-example path="i18n/src/app/app.component.html" region="i18n-select" title="src/app/app.component.html" linenums="false">
|
|
</code-example>
|
|
|
|
## Nesting pluralization and selection expressions
|
|
|
|
You can also nest different ICU expressions together. For example:
|
|
|
|
<code-example path="i18n/src/app/app.component.html" region="i18n-nested" title="src/app/app.component.html">
|
|
</code-example>
|
|
|
|
{@a ng-xi18n}
|
|
|
|
## Create a translation source file with the _ng-xi18n_ tool
|
|
|
|
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.
|
|
If you haven't already installed the CLI and its `platform-server` peer dependency, do so now:
|
|
|
|
<code-example language="sh" class="code-shell">
|
|
npm install @angular/compiler-cli @angular/platform-server --save
|
|
</code-example>
|
|
|
|
Open a terminal window at the root of the application project and enter the `ng-xi18n` command:
|
|
|
|
<code-example language="sh" class="code-shell">
|
|
./node_modules/.bin/ng-xi18n
|
|
</code-example>
|
|
|
|
<div class="l-sub-section">
|
|
|
|
Windows users may have to quote the command like this: `"./node_modules/.bin/ng-xi18n"`
|
|
|
|
</div>
|
|
|
|
By default, the tool generates a translation file named **`messages.xlf`** in the
|
|
<a href="https://en.wikipedia.org/wiki/XLIFF">XML Localization Interchange File Format (XLIFF, version 1.2)</a>.
|
|
|
|
{@a other-formats}
|
|
|
|
### Other translation formats
|
|
|
|
Angular i18n tooling supports XLIFF 1.2 and XLIFF 2 as well as the
|
|
<a href="http://cldr.unicode.org/development/development-process/design-proposals/xmb" >XML Message Bundle (XMB)</a>.
|
|
|
|
You can specify your choice of format _explicitly_ with the `--i18nFormat` flag as illustrated in these example commands
|
|
|
|
<code-example language="sh" class="code-shell">
|
|
./node_modules/.bin/ng-xi18n --i18nFormat=xlf --outFile=messages.xlf
|
|
./node_modules/.bin/ng-xi18n --i18nFormat=xlf2 --outFile=messages.xliff2.xlf
|
|
./node_modules/.bin/ng-xi18n --i18nFormat=xmb --outFile=messages.xmb
|
|
</code-example>
|
|
|
|
The sample in _this_ guide sticks with the default _XLIFF 1.2_ format.
|
|
|
|
{@a ng-xi18n-options}
|
|
|
|
### Other options
|
|
|
|
You may have to specify additional options.
|
|
For example, if the `tsconfig.json` TypeScript configuration
|
|
file is located somewhere 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
|
|
</code-example>
|
|
|
|
{@a npm-i18n-script}
|
|
|
|
### 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",
|
|
...
|
|
}
|
|
</code-example>
|
|
|
|
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
|
|
</code-example>
|
|
|
|
Note the `--` flag before the options.
|
|
It tells _npm_ to pass every flag thereafter to `ng-xi18n`.
|
|
|
|
{@a translate}
|
|
|
|
## Translate text messages
|
|
|
|
The `ng-xi18n` command generates 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 guide sample creates a Spanish translation file.
|
|
|
|
{@a localization-folder}
|
|
|
|
### Create a localization folder
|
|
|
|
You will probably translate into more than one other language so it's a good idea
|
|
for the project structure to reflect your entire internationalization effort.
|
|
|
|
One approach is to dedicate a folder to localization and store related assets,
|
|
such as internationalization files, there.
|
|
|
|
<div class="l-sub-section">
|
|
|
|
Localization and internationalization are
|
|
<a href="https://en.wikipedia.org/wiki/Internationalization_and_localization">different but closely related terms</a>.
|
|
|
|
</div>
|
|
|
|
This guide follows that suggestion. It has a `locale` folder under `src/`.
|
|
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">well-known codeset</a>.
|
|
|
|
Make a copy of the `messages.xlf` file, put it in the `locale` folder and
|
|
rename it `messages.es.xlf`for the Spanish language translation.
|
|
Do the same for each target language.
|
|
|
|
{@a translate-text-nodes}
|
|
|
|
### 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">many XLIFF file editors</a>.
|
|
|
|
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:
|
|
|
|
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translated-hello" title="src/locale/messages.es.xlf (<trans-unit>)" linenums="false">
|
|
</code-example>
|
|
|
|
This XML element represents the translation of the `<h1>` greeting tag you marked with the `i18n` attribute.
|
|
|
|
<div class="l-sub-section">
|
|
|
|
Note that the translation unit `id=introductionHeader` is derived from the _custom_ `id`](#custom-id "Set a custom id") that you set earlier, but **without the `@@` prefix** required in the source HTML.
|
|
|
|
</div>
|
|
|
|
Using the _source_, _description_, and _meaning_ elements to guide your translation,
|
|
replace the `<target/>` tag with the Spanish greeting:
|
|
|
|
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translated-hello" title="src/locale/messages.es.xlf (<trans-unit>, after translation)" linenums="false">
|
|
</code-example>
|
|
|
|
Translate the other text nodes the same way:
|
|
|
|
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translated-other-nodes" title="src/locale/messages.es.xlf (<trans-unit>)" linenums="false">
|
|
</code-example>
|
|
|
|
<div class="alert is-important">
|
|
|
|
**The tool generated the `id`s for _these_ translation units. Don't touch them.**
|
|
Each `id` depends upon 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)**.
|
|
|
|
This is why you should **[specify custom ids](#custom-id "Set a custom id")** and avoid tool generated ids.
|
|
|
|
</div>
|
|
|
|
{@a translate-plural-select}
|
|
|
|
## 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 `XLIFF` format doesn't yet support the ICU rules.
|
|
However, the `XMB` format does support the ICU rules.
|
|
|
|
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.
|
|
|
|
{@a translate-plural}
|
|
|
|
### Translate _plural_
|
|
To translate a `plural`, translate its ICU format match values:
|
|
|
|
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translated-plural" title="src/locale/messages.es.xlf (<trans-unit>)" linenums="false">
|
|
</code-example>
|
|
|
|
{@a translate-select}
|
|
|
|
### Translate _select_
|
|
The `select` behaves a little differently. Here again is the ICU format message in the component template:
|
|
|
|
<code-example path="i18n/src/app/app.component.html" region="i18n-select" title="src/app/app.component.html" linenums="false">
|
|
</code-example>
|
|
|
|
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.
|
|
|
|
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translate-select-1" title="src/locale/messages.es.xlf (<trans-unit>)" linenums="false">
|
|
</code-example>
|
|
|
|
The second translation unit, immediately below the first one, contains the `select` message. Translate that.
|
|
|
|
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translate-select-2" title="src/locale/messages.es.xlf (<trans-unit>)" linenums="false">
|
|
</code-example>
|
|
|
|
Here they are together, after translation:
|
|
|
|
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translated-select" title="src/locale/messages.es.xlf (<trans-unit>)" linenums="false">
|
|
</code-example>
|
|
|
|
### Translate a nested expression
|
|
|
|
A nested expression is not different from the previous ones. As in the previous example, we have _two_ translation units.
|
|
|
|
The first one contains the text outside the nested expression:
|
|
|
|
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translate-nested-1" title="src/locale/messages.es.xlf (<trans-unit>)" linenums="false">
|
|
</code-example>
|
|
|
|
The second unit contains the complete nested expression:
|
|
|
|
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translate-nested-2" title="src/locale/messages.es.xlf (<trans-unit>)" linenums="false">
|
|
</code-example>
|
|
|
|
And both together:
|
|
|
|
<code-example path="i18n/src/locale/messages.es.xlf.html" region="translate-nested" title="src/locale/messages.es.xlf (<trans-unit>)" linenums="false">
|
|
</code-example>
|
|
|
|
|
|
|
|
The entire template translation is complete. It's
|
|
time to incorporate that translation into the application.
|
|
|
|
<a id='app-pre-translation'></a>
|
|
|
|
### The app before translation
|
|
|
|
When the previous steps finish, the sample app _and_ its translation file are as follows:
|
|
|
|
<code-tabs>
|
|
<code-pane title="src/app/app.component.html" path="i18n/src/app/app.component.html">
|
|
</code-pane>
|
|
<code-pane title="src/app/app.component.ts" path="i18n/src/app/app.component.ts">
|
|
</code-pane>
|
|
<code-pane title="src/app/app.module.ts" path="i18n/src/app/app.module.ts">
|
|
</code-pane>
|
|
<code-pane title="src/main.ts" path="i18n/src/main.1.ts">
|
|
</code-pane>
|
|
<code-pane title="src/locale/messages.es.xlf" path="i18n/src/locale/messages.es.xlf.html">
|
|
</code-pane>
|
|
</code-tabs>
|
|
|
|
{@a merge}
|
|
|
|
## Merge the completed translation file into the app
|
|
|
|
To merge the translated text into component templates,
|
|
compile the application with the completed translation file.
|
|
The process is the same whether the file is in `.xlf` format or
|
|
in another format that Angular understands, such as `.xtb`.
|
|
|
|
You provide the Angular compiler with three new pieces of information:
|
|
|
|
* The translation file.
|
|
* The translation file format.
|
|
* The <a href="https://en.wikipedia.org/wiki/XLIFF">_Locale ID_</a>
|
|
(`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.
|
|
|
|
* With [JIT](guide/i18n#jit), you provide the information at bootstrap time.
|
|
* With [AOT](guide/i18n#aot), you pass the information as `ngc` options.
|
|
|
|
{@a jit}
|
|
|
|
### Merge with the JIT compiler
|
|
|
|
The JIT compiler compiles the application in the browser as the application loads.
|
|
Translation with the JIT compiler is a dynamic process of:
|
|
|
|
1. Determining the language version for the current user.
|
|
2. Importing the appropriate language translation file as a string constant.
|
|
3. Creating corresponding translation providers to guide the JIT compiler.
|
|
4. Bootstrapping the application with those providers.
|
|
|
|
Open `index.html` and revise the launch script as follows:
|
|
|
|
<code-example path="i18n/src/index.html" region="i18n" title="index.html (launch script)" linenums="false">
|
|
</code-example>
|
|
|
|
In this sample, the user's language is hard-coded as a global `document.locale` variable
|
|
in the `index.html`.
|
|
|
|
{@a text-plugin}
|
|
|
|
### SystemJS text plugin
|
|
|
|
<div class="alert is-important">
|
|
|
|
This plugin only applies to an application using SystemJS. If you are using the Angular CLI, please refer to their
|
|
[docs](https://github.com/angular/angular-cli/wiki/xi18n).
|
|
|
|
</div>
|
|
|
|
Notice the SystemJS mapping of `text` to a `systemjs-text-plugin.js`.
|
|
With the help of a text plugin, SystemJS can read any file as raw text and
|
|
return the contents as a string.
|
|
You'll need it to import the language translation file.
|
|
|
|
SystemJS doesn't ship with a raw text plugin but it's easy to add.
|
|
Create the following `systemjs-text-plugin.js` in the `src/` folder:
|
|
|
|
<code-example path="i18n/src/systemjs-text-plugin.js" title="src/systemjs-text-plugin.js" linenums="false">
|
|
</code-example>
|
|
|
|
{@a create-translation-providers}
|
|
|
|
### Create translation providers
|
|
|
|
Three providers tell the JIT compiler how to translate the template texts for a particular language
|
|
while compiling the application:
|
|
|
|
* `TRANSLATIONS` is a string containing the content of the translation file.
|
|
* `TRANSLATIONS_FORMAT` is the format of the file: `xlf`, `xlf2`, or `xtb`.
|
|
* `LOCALE_ID` is the locale of the target language.
|
|
|
|
The `getTranslationProviders()` function in the following `src/app/i18n-providers.ts`
|
|
creates those providers based on the user's _locale_
|
|
and the corresponding translation file:
|
|
|
|
<code-example path="i18n/src/app/i18n-providers.ts" region="without-missing-translation" title="src/app/i18n-providers.ts">
|
|
</code-example>
|
|
|
|
1. It gets the locale from the global `document.locale` variable that was set in `index.html`.
|
|
|
|
1. If there is no locale or the language is U.S. English (`en-US`), there is no need to translate.
|
|
The function returns an empty `noProviders` array as a `Promise`.
|
|
It must return a `Promise` because this function could read a translation file asynchronously from the server.
|
|
|
|
1. It creates a transaction filename from the locale according to the name and location convention
|
|
[described earlier](guide/i18n#localization-folder).
|
|
|
|
1. The `getTranslationsWithSystemJs()` method reads the translation and returns the contents as a string.
|
|
Notice that it appends `!text` to the filename, telling SystemJS to use the [text plugin](guide/i18n#text-plugin).
|
|
|
|
1. The callback composes a providers array with the three translation providers.
|
|
|
|
1. Finally, `getTranslationProviders()` returns the entire effort as a promise.
|
|
|
|
<div class="alert is-important">
|
|
|
|
The `LOCALE_ID` has to be a valid locale id as explained in [here](http://userguide.icu-project.org/locale).
|
|
|
|
</div>
|
|
|
|
{@a bootstrap-the-app}
|
|
|
|
### Bootstrap with translation providers
|
|
|
|
The Angular `bootstrapModule()` method has a second _options_ parameter
|
|
that can influence the behavior of the compiler.
|
|
|
|
You'll create an _options_ object with the translation providers from `getTranslationProviders()`
|
|
and pass it to `bootstrapModule`.
|
|
Open the `src/main.ts` and modify the bootstrap code as follows:
|
|
|
|
<code-example path="i18n/src/main.ts" title="src/main.ts" linenums="false">
|
|
</code-example>
|
|
|
|
Notice that it waits for the `getTranslationProviders()` promise to resolve before
|
|
bootstrapping the app.
|
|
|
|
The app is now _internationalized_ for English and Spanish and there is a clear path for adding
|
|
more languages.
|
|
|
|
{@a aot}
|
|
|
|
### _Internationalization_ with the AOT compiler
|
|
|
|
The JIT compiler translates the application into the target language
|
|
while compiling dynamically in the browser.
|
|
That's flexible but may not be fast enough for your users.
|
|
|
|
The AOT (_Ahead-of-Time_) compiler is part of a build process that
|
|
produces a small, fast, ready-to-run application package.
|
|
When you internationalize with the AOT compiler, you pre-build
|
|
a separate application package for each
|
|
language. Then in the host web page, in this case `index.html`,
|
|
you determine which language the user needs
|
|
and serve the appropriate application package.
|
|
|
|
This guide doesn't cover how to build multiple application packages and
|
|
serve them according to the user's language preference.
|
|
It does explain the few steps necessary to tell the AOT compiler to apply a translations file.
|
|
|
|
Internationalization with the AOT compiler requires
|
|
some setup specifically for AOT compilation.
|
|
Start with the application project as shown
|
|
[just before merging the translation file](guide/i18n#app-pre-translation)
|
|
and refer to the [AOT guide](guide/aot-compiler) to make it _AOT-ready_.
|
|
|
|
Next, issue an `ngc` compile command for each supported language, including English.
|
|
The result is a separate version of the application for each language.
|
|
|
|
Tell AOT how to translate by adding three options to the `ngc` command:
|
|
|
|
* `--i18nFile`: the path to the translation file.
|
|
* `--locale`: the name of the locale.
|
|
* `--i18nFormat`: the format of the localization file.
|
|
|
|
For this sample, the Spanish language command would be:
|
|
|
|
<code-example language="sh" class="code-shell">
|
|
./node_modules/.bin/ngc --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf
|
|
</code-example>
|
|
|
|
<div class="l-sub-section">
|
|
|
|
Windows users may have to quote the command:
|
|
|
|
<code-example language="sh" class="code-shell">
|
|
"./node_modules/.bin/ngc" --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf
|
|
</code-example>
|
|
|
|
</div>
|
|
|
|
### Report missing translations
|
|
|
|
If you forgot to provide a translation, the build will succeed with a warning that you might easily overlook.
|
|
You can configure the Angular compiler for different "missing translation" behaviors:
|
|
|
|
* Error
|
|
* Warning (default)
|
|
* Ignore
|
|
|
|
To change the behavior in JIT, you can use the following configuration:
|
|
|
|
<code-example language="typescript">
|
|
{ provide: CompilerConfig, useValue: new CompilerConfig({ missingTranslation: MissingTranslationStrategy.Error }) }
|
|
</code-example>
|
|
|
|
A good place to use it is the translation providers:
|
|
|
|
<code-example path="i18n/src/app/i18n-providers.ts" region="missing-translation" title="src/app/i18n-providers.ts"></code-example>
|
|
|
|
To change the behavior in AOT, add the `--missingTranslation` flag to the compilation command:
|
|
|
|
<code-example language="sh" class="code-shell">
|
|
./node_modules/.bin/ngc --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf --missingTranslation=error
|
|
</code-example>
|
|
|
|
{@a maintenance}
|
|
|
|
## 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 the `id` _can be a serious problem!_
|
|
|
|
If the `id` is generated by the tool, _most_ changes to _existing_ markup
|
|
cause the tool to generate a _new_ `id` for the affected translation unit.
|
|
|
|
After an `id` changes, the translation files are no longer in sync.
|
|
Because of that, you get some warning messages during re-compilation.
|
|
The warning messages identify that some translations are missing, but they don't tell you which
|
|
old `ids` are no longer valid.
|
|
|
|
If you use a [custom id](#custom-id "Set a custom id"),
|
|
the tooling preserves the custom `id` as you make changes to the corresponding translation unit. **Use custom _ids_ unless you have a very good reason to do otherwise.**
|
|
|
|
Whether you use generated or custom `ids`, **always 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 `ids` and other changes across your translation files.
|
|
|