2017-05-26 15:28:06 -04:00
# Internationalization (i18n)
2017-02-22 13:09:39 -05:00
Angular's _internationalization_ (_i18n_) tools help make your app available in multiple languages.
2017-04-21 20:21:45 -04:00
Try this < live-example name = "i18n" title = "i18n Example in Spanish" > live example< / live-example >
2017-02-22 13:09:39 -05:00
of a JIT-compiled app, translated into Spanish.
{@a angular-i18n}
## Angular and _i18n_ template translation
2017-04-12 15:53:18 -04:00
Application internationalization is a challenging, many-faceted effort that
2017-02-22 13:09:39 -05:00
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.
2017-04-10 11:51:13 -04:00
< div class = "l-sub-section" >
2017-03-27 11:08:53 -04:00
2017-02-22 13:09:39 -05:00
Practitioners of _internationalization_ refer to a translatable text as a "_message_".
2017-05-02 02:01:20 -04:00
This page uses the words "_text_" and "_message_" interchangeably and in the combination, "_text message_".
2017-03-27 11:08:53 -04:00
2017-04-10 11:51:13 -04:00
< / div >
2017-03-27 11:08:53 -04:00
2017-02-22 13:09:39 -05:00
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.
2017-04-12 15:53:18 -04:00
1. A translator edits that file, translating the extracted text messages into the target language,
2017-02-22 13:09:39 -05:00
and returns the file to you.
2017-04-12 15:53:18 -04:00
1. The Angular compiler imports the completed translation files,
2017-02-22 13:09:39 -05:00
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}
feat(common): drop use of the Intl API to improve browser support (#18284)
BREAKING CHANGE: Because of multiple bugs and browser inconsistencies, we have dropped the intl api in favor of data exported from the Unicode Common Locale Data Repository (CLDR).
Unfortunately we had to change the i18n pipes (date, number, currency, percent) and there are some breaking changes.
1. I18n pipes
* Breaking change:
- By default Angular now only contains locale data for the language `en-US`, if you set the value of `LOCALE_ID` to another locale, you will have to import new locale data for this language because we don't use the intl API anymore.
* Features:
- you don't need to use the intl polyfill for Angular anymore.
- all i18n pipes now have an additional last parameter `locale` which allows you to use a specific locale instead of the one defined in the token `LOCALE_ID` (whose value is `en-US` by default).
- the new locale data extracted from CLDR are now available to developers as well and can be used through an API (which should be especially useful for library authors).
- you can still use the old pipes for now, but their names have been changed and they are no longer included in the `CommonModule`. To use them, you will have to import the `DeprecatedI18NPipesModule` after the `CommonModule` (the order is important):
```ts
import { NgModule } from '@angular/core';
import { CommonModule, DeprecatedI18NPipesModule } from '@angular/common';
@NgModule({
imports: [
CommonModule,
// import deprecated module after
DeprecatedI18NPipesModule
]
})
export class AppModule { }
```
Dont forget that you will still need to import the intl API polyfill if you want to use those deprecated pipes.
2. Date pipe
* Breaking changes:
- the predefined formats (`short`, `shortTime`, `shortDate`, `medium`, ...) now use the patterns given by CLDR (like it was in AngularJS) instead of the ones from the intl API. You might notice some changes, e.g. `shortDate` will be `8/15/17` instead of `8/15/2017` for `en-US`.
- the narrow version of eras is now `GGGGG` instead of `G`, the format `G` is now similar to `GG` and `GGG`.
- the narrow version of months is now `MMMMM` instead of `L`, the format `L` is now the short standalone version of months.
- the narrow version of the week day is now `EEEEE` instead of `E`, the format `E` is now similar to `EE` and `EEE`.
- the timezone `z` will now fallback to `O` and output `GMT+1` instead of the complete zone name (e.g. `Pacific Standard Time`), this is because the quantity of data required to have all the zone names in all of the existing locales is too big.
- the timezone `Z` will now output the ISO8601 basic format, e.g. `+0100`, you should now use `ZZZZ` to get `GMT+01:00`.
| Field type | Format | Example value | v4 | v5 |
|------------|---------------|-----------------------|----|---------------|
| Eras | Narrow | A for AD | G | GGGGG |
| Months | Narrow | S for September | L | MMMMM |
| Week day | Narrow | M for Monday | E | EEEEE |
| Timezone | Long location | Pacific Standard Time | z | Not available |
| Timezone | Long GMT | GMT+01:00 | Z | ZZZZ |
* Features
- new predefined formats `long`, `full`, `longTime`, `fullTime`.
- the format `yyy` is now supported, e.g. the year `52` will be `052` and the year `2017` will be `2017`.
- standalone months are now supported with the formats `L` to `LLLLL`.
- week of the year is now supported with the formats `w` and `ww`, e.g. weeks `5` and `05`.
- week of the month is now supported with the format `W`, e.g. week `3`.
- fractional seconds are now supported with the format `S` to `SSS`.
- day periods for AM/PM now supports additional formats `aa`, `aaa`, `aaaa` and `aaaaa`. The formats `a` to `aaa` are similar, while `aaaa` is the wide version if available (e.g. `ante meridiem` for `am`), or equivalent to `a` otherwise, and `aaaaa` is the narrow version (e.g. `a` for `am`).
- extra day periods are now supported with the formats `b` to `bbbbb` (and `B` to `BBBBB` for the standalone equivalents), e.g. `morning`, `noon`, `afternoon`, ....
- the short non-localized timezones are now available with the format `O` to `OOOO`. The formats `O` to `OOO` will output `GMT+1` while the format `OOOO` will be `GMT+01:00`.
- the ISO8601 basic time zones are now available with the formats `Z` to `ZZZZZ`. The formats `Z` to `ZZZ` will output `+0100`, while the format `ZZZZ` will be `GMT+01:00` and `ZZZZZ` will be `+01:00`.
* Bug fixes
- the date pipe will now work exactly the same across all browsers, which will fix a lot of bugs for safari and IE.
- eras can now be used on their own without the date, e.g. the format `GG` will be `AD` instead of `8 15, 2017 AD`.
3. Currency pipe
* Breaking change:
- the default value for `symbolDisplay` is now `symbol` instead of `code`. This means that by default you will see `$4.99` for `en-US` instead of `USD4.99` previously.
* Deprecation:
- the second parameter of the currency pipe (`symbolDisplay`) is no longer a boolean, it now takes the values `code`, `symbol` or `symbol-narrow`. A boolean value is still valid for now, but it is deprecated and it will print a warning message in the console.
* Features:
- you can now choose between `code`, `symbol` or `symbol-narrow` which gives you access to more options for some currencies (e.g. the canadian dollar with the code `CAD` has the symbol `CA$` and the symbol-narrow `$`).
4. Percent pipe
* Breaking change
- if you don't specify the number of digits to round to, the local format will be used (and it usually rounds numbers to 0 digits, instead of not rounding previously), e.g. `{{ 3.141592 | percent }}` will output `314%` for the locale `en-US` instead of `314.1592%` previously.
Fixes #10809, #9524, #7008, #9324, #7590, #6724, #3429, #17576, #17478, #17319, #17200, #16838, #16624, #16625, #16591, #14131, #12632, #11376, #11187
PR Close #18284
2017-08-22 14:30:59 -04:00
## i18n pipes
Angular pipes can help you with internationalization: the `DatePipe` , `CurrencyPipe` , `DecimalPipe`
and `PercentPipe` use locale data to format your data based on your `LOCALE_ID` .
By default Angular only contains locale data for the language `en-US` , if you set the value of
`LOCALE_ID` to another locale, you will have to import new locale data for this language:
< code-example path = "i18n/src/app/app.locale_data.ts" region = "import-locale" title = "src/app/app.locale_data.ts" linenums = "false" >
< / code-example >
< div class = "l-sub-section" >
2017-08-28 14:35:10 -04:00
Note that the files in `@angular/common/locales` contain most of the locale data that you will
feat(common): drop use of the Intl API to improve browser support (#18284)
BREAKING CHANGE: Because of multiple bugs and browser inconsistencies, we have dropped the intl api in favor of data exported from the Unicode Common Locale Data Repository (CLDR).
Unfortunately we had to change the i18n pipes (date, number, currency, percent) and there are some breaking changes.
1. I18n pipes
* Breaking change:
- By default Angular now only contains locale data for the language `en-US`, if you set the value of `LOCALE_ID` to another locale, you will have to import new locale data for this language because we don't use the intl API anymore.
* Features:
- you don't need to use the intl polyfill for Angular anymore.
- all i18n pipes now have an additional last parameter `locale` which allows you to use a specific locale instead of the one defined in the token `LOCALE_ID` (whose value is `en-US` by default).
- the new locale data extracted from CLDR are now available to developers as well and can be used through an API (which should be especially useful for library authors).
- you can still use the old pipes for now, but their names have been changed and they are no longer included in the `CommonModule`. To use them, you will have to import the `DeprecatedI18NPipesModule` after the `CommonModule` (the order is important):
```ts
import { NgModule } from '@angular/core';
import { CommonModule, DeprecatedI18NPipesModule } from '@angular/common';
@NgModule({
imports: [
CommonModule,
// import deprecated module after
DeprecatedI18NPipesModule
]
})
export class AppModule { }
```
Dont forget that you will still need to import the intl API polyfill if you want to use those deprecated pipes.
2. Date pipe
* Breaking changes:
- the predefined formats (`short`, `shortTime`, `shortDate`, `medium`, ...) now use the patterns given by CLDR (like it was in AngularJS) instead of the ones from the intl API. You might notice some changes, e.g. `shortDate` will be `8/15/17` instead of `8/15/2017` for `en-US`.
- the narrow version of eras is now `GGGGG` instead of `G`, the format `G` is now similar to `GG` and `GGG`.
- the narrow version of months is now `MMMMM` instead of `L`, the format `L` is now the short standalone version of months.
- the narrow version of the week day is now `EEEEE` instead of `E`, the format `E` is now similar to `EE` and `EEE`.
- the timezone `z` will now fallback to `O` and output `GMT+1` instead of the complete zone name (e.g. `Pacific Standard Time`), this is because the quantity of data required to have all the zone names in all of the existing locales is too big.
- the timezone `Z` will now output the ISO8601 basic format, e.g. `+0100`, you should now use `ZZZZ` to get `GMT+01:00`.
| Field type | Format | Example value | v4 | v5 |
|------------|---------------|-----------------------|----|---------------|
| Eras | Narrow | A for AD | G | GGGGG |
| Months | Narrow | S for September | L | MMMMM |
| Week day | Narrow | M for Monday | E | EEEEE |
| Timezone | Long location | Pacific Standard Time | z | Not available |
| Timezone | Long GMT | GMT+01:00 | Z | ZZZZ |
* Features
- new predefined formats `long`, `full`, `longTime`, `fullTime`.
- the format `yyy` is now supported, e.g. the year `52` will be `052` and the year `2017` will be `2017`.
- standalone months are now supported with the formats `L` to `LLLLL`.
- week of the year is now supported with the formats `w` and `ww`, e.g. weeks `5` and `05`.
- week of the month is now supported with the format `W`, e.g. week `3`.
- fractional seconds are now supported with the format `S` to `SSS`.
- day periods for AM/PM now supports additional formats `aa`, `aaa`, `aaaa` and `aaaaa`. The formats `a` to `aaa` are similar, while `aaaa` is the wide version if available (e.g. `ante meridiem` for `am`), or equivalent to `a` otherwise, and `aaaaa` is the narrow version (e.g. `a` for `am`).
- extra day periods are now supported with the formats `b` to `bbbbb` (and `B` to `BBBBB` for the standalone equivalents), e.g. `morning`, `noon`, `afternoon`, ....
- the short non-localized timezones are now available with the format `O` to `OOOO`. The formats `O` to `OOO` will output `GMT+1` while the format `OOOO` will be `GMT+01:00`.
- the ISO8601 basic time zones are now available with the formats `Z` to `ZZZZZ`. The formats `Z` to `ZZZ` will output `+0100`, while the format `ZZZZ` will be `GMT+01:00` and `ZZZZZ` will be `+01:00`.
* Bug fixes
- the date pipe will now work exactly the same across all browsers, which will fix a lot of bugs for safari and IE.
- eras can now be used on their own without the date, e.g. the format `GG` will be `AD` instead of `8 15, 2017 AD`.
3. Currency pipe
* Breaking change:
- the default value for `symbolDisplay` is now `symbol` instead of `code`. This means that by default you will see `$4.99` for `en-US` instead of `USD4.99` previously.
* Deprecation:
- the second parameter of the currency pipe (`symbolDisplay`) is no longer a boolean, it now takes the values `code`, `symbol` or `symbol-narrow`. A boolean value is still valid for now, but it is deprecated and it will print a warning message in the console.
* Features:
- you can now choose between `code`, `symbol` or `symbol-narrow` which gives you access to more options for some currencies (e.g. the canadian dollar with the code `CAD` has the symbol `CA$` and the symbol-narrow `$`).
4. Percent pipe
* Breaking change
- if you don't specify the number of digits to round to, the local format will be used (and it usually rounds numbers to 0 digits, instead of not rounding previously), e.g. `{{ 3.141592 | percent }}` will output `314%` for the locale `en-US` instead of `314.1592%` previously.
Fixes #10809, #9524, #7008, #9324, #7590, #6724, #3429, #17576, #17478, #17319, #17200, #16838, #16624, #16625, #16591, #14131, #12632, #11376, #11187
PR Close #18284
2017-08-22 14:30:59 -04:00
need, but some advanced formatting options might only be available in the extra dataset that you can
2017-08-28 14:35:10 -04:00
import from `@angular/common/locales/extra` :
feat(common): drop use of the Intl API to improve browser support (#18284)
BREAKING CHANGE: Because of multiple bugs and browser inconsistencies, we have dropped the intl api in favor of data exported from the Unicode Common Locale Data Repository (CLDR).
Unfortunately we had to change the i18n pipes (date, number, currency, percent) and there are some breaking changes.
1. I18n pipes
* Breaking change:
- By default Angular now only contains locale data for the language `en-US`, if you set the value of `LOCALE_ID` to another locale, you will have to import new locale data for this language because we don't use the intl API anymore.
* Features:
- you don't need to use the intl polyfill for Angular anymore.
- all i18n pipes now have an additional last parameter `locale` which allows you to use a specific locale instead of the one defined in the token `LOCALE_ID` (whose value is `en-US` by default).
- the new locale data extracted from CLDR are now available to developers as well and can be used through an API (which should be especially useful for library authors).
- you can still use the old pipes for now, but their names have been changed and they are no longer included in the `CommonModule`. To use them, you will have to import the `DeprecatedI18NPipesModule` after the `CommonModule` (the order is important):
```ts
import { NgModule } from '@angular/core';
import { CommonModule, DeprecatedI18NPipesModule } from '@angular/common';
@NgModule({
imports: [
CommonModule,
// import deprecated module after
DeprecatedI18NPipesModule
]
})
export class AppModule { }
```
Dont forget that you will still need to import the intl API polyfill if you want to use those deprecated pipes.
2. Date pipe
* Breaking changes:
- the predefined formats (`short`, `shortTime`, `shortDate`, `medium`, ...) now use the patterns given by CLDR (like it was in AngularJS) instead of the ones from the intl API. You might notice some changes, e.g. `shortDate` will be `8/15/17` instead of `8/15/2017` for `en-US`.
- the narrow version of eras is now `GGGGG` instead of `G`, the format `G` is now similar to `GG` and `GGG`.
- the narrow version of months is now `MMMMM` instead of `L`, the format `L` is now the short standalone version of months.
- the narrow version of the week day is now `EEEEE` instead of `E`, the format `E` is now similar to `EE` and `EEE`.
- the timezone `z` will now fallback to `O` and output `GMT+1` instead of the complete zone name (e.g. `Pacific Standard Time`), this is because the quantity of data required to have all the zone names in all of the existing locales is too big.
- the timezone `Z` will now output the ISO8601 basic format, e.g. `+0100`, you should now use `ZZZZ` to get `GMT+01:00`.
| Field type | Format | Example value | v4 | v5 |
|------------|---------------|-----------------------|----|---------------|
| Eras | Narrow | A for AD | G | GGGGG |
| Months | Narrow | S for September | L | MMMMM |
| Week day | Narrow | M for Monday | E | EEEEE |
| Timezone | Long location | Pacific Standard Time | z | Not available |
| Timezone | Long GMT | GMT+01:00 | Z | ZZZZ |
* Features
- new predefined formats `long`, `full`, `longTime`, `fullTime`.
- the format `yyy` is now supported, e.g. the year `52` will be `052` and the year `2017` will be `2017`.
- standalone months are now supported with the formats `L` to `LLLLL`.
- week of the year is now supported with the formats `w` and `ww`, e.g. weeks `5` and `05`.
- week of the month is now supported with the format `W`, e.g. week `3`.
- fractional seconds are now supported with the format `S` to `SSS`.
- day periods for AM/PM now supports additional formats `aa`, `aaa`, `aaaa` and `aaaaa`. The formats `a` to `aaa` are similar, while `aaaa` is the wide version if available (e.g. `ante meridiem` for `am`), or equivalent to `a` otherwise, and `aaaaa` is the narrow version (e.g. `a` for `am`).
- extra day periods are now supported with the formats `b` to `bbbbb` (and `B` to `BBBBB` for the standalone equivalents), e.g. `morning`, `noon`, `afternoon`, ....
- the short non-localized timezones are now available with the format `O` to `OOOO`. The formats `O` to `OOO` will output `GMT+1` while the format `OOOO` will be `GMT+01:00`.
- the ISO8601 basic time zones are now available with the formats `Z` to `ZZZZZ`. The formats `Z` to `ZZZ` will output `+0100`, while the format `ZZZZ` will be `GMT+01:00` and `ZZZZZ` will be `+01:00`.
* Bug fixes
- the date pipe will now work exactly the same across all browsers, which will fix a lot of bugs for safari and IE.
- eras can now be used on their own without the date, e.g. the format `GG` will be `AD` instead of `8 15, 2017 AD`.
3. Currency pipe
* Breaking change:
- the default value for `symbolDisplay` is now `symbol` instead of `code`. This means that by default you will see `$4.99` for `en-US` instead of `USD4.99` previously.
* Deprecation:
- the second parameter of the currency pipe (`symbolDisplay`) is no longer a boolean, it now takes the values `code`, `symbol` or `symbol-narrow`. A boolean value is still valid for now, but it is deprecated and it will print a warning message in the console.
* Features:
- you can now choose between `code`, `symbol` or `symbol-narrow` which gives you access to more options for some currencies (e.g. the canadian dollar with the code `CAD` has the symbol `CA$` and the symbol-narrow `$`).
4. Percent pipe
* Breaking change
- if you don't specify the number of digits to round to, the local format will be used (and it usually rounds numbers to 0 digits, instead of not rounding previously), e.g. `{{ 3.141592 | percent }}` will output `314%` for the locale `en-US` instead of `314.1592%` previously.
Fixes #10809, #9524, #7008, #9324, #7590, #6724, #3429, #17576, #17478, #17319, #17200, #16838, #16624, #16625, #16591, #14131, #12632, #11376, #11187
PR Close #18284
2017-08-22 14:30:59 -04:00
< code-example path = "i18n/src/app/app.locale_data_extra.ts" region = "import-locale-extra" title = "src/app/app.locale_data_extra.ts" linenums = "false" >
< / code-example >
< / div >
2017-02-22 13:09:39 -05:00
## 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.
2017-04-10 11:51:13 -04:00
< div class = "alert is-helpful" >
2017-02-22 13:09:39 -05:00
2017-04-12 15:53:18 -04:00
`i18n` is not an Angular _directive_ .
2017-02-22 13:09:39 -05:00
It's a custom _attribute_ , recognized by Angular tools and compilers.
After translation, the compiler removes it.
2017-04-10 11:51:13 -04:00
< / div >
2017-02-22 13:09:39 -05:00
2017-04-12 15:53:18 -04:00
In the accompanying sample, an `<h1>` tag displays a simple English language greeting
2017-02-22 13:09:39 -05:00
that you translate into Spanish:
2017-04-21 20:21:45 -04:00
< code-example path = "i18n/src/app/app.component.1.html" region = "greeting" title = "src/app/app.component.html" linenums = "false" >
2017-05-02 02:01:20 -04:00
< / code-example >
Add the `i18n` attribute to the tag to mark it for translation.
2017-03-27 11:08:53 -04:00
2017-05-02 02:01:20 -04:00
< code-example path = "i18n/src/app/app.component.1.html" region = "i18n-attribute" title = "src/app/app.component.html" linenums = "false" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
{@a help-translator}
2017-03-31 19:57:13 -04:00
2017-05-02 02:01:20 -04:00
### Help the translator with a _description_ and _meaning_
2017-03-31 19:57:13 -04:00
2017-05-02 02:01:20 -04:00
In order to translate it accurately, the translator may
need a description of the message.
Assign a description to the i18n attribute:
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
< code-example path = "i18n/src/app/app.component.1.html" region = "i18n-attribute-desc" title = "src/app/app.component.html" linenums = "false" >
< / code-example >
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
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 > `):
2017-03-27 11:08:53 -04:00
2017-05-02 02:01:20 -04:00
< code-example path = "i18n/src/app/app.component.1.html" region = "i18n-attribute-meaning" title = "src/app/app.component.html" linenums = "false" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
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.
2017-03-31 19:57:13 -04:00
2017-05-02 02:01:20 -04:00
{@a custom-id}
2017-04-12 15:53:18 -04:00
2017-05-02 02:01:20 -04:00
### Set a custom _id_ to improve search and maintenance
2017-04-12 15:53:18 -04:00
2017-05-02 02:01:20 -04:00
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:
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
< code-example path = "i18n/src/locale/messages.es.xlf.html" region = "generated-id" linenums = "false" >
< / code-example >
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
This _id_ is obscure and difficult for humans to read or remember.
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
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 ).
2017-03-27 11:08:53 -04:00
2017-05-02 02:01:20 -04:00
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" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
Now the extractor tool and compiler will generate a translation unit with _your custom id_ and never change it.
2017-03-31 19:57:13 -04:00
2017-05-02 02:01:20 -04:00
< code-example path = "i18n/src/locale/messages.es.xlf.html" region = "custom-id" linenums = "false" >
< / code-example >
2017-03-31 19:57:13 -04:00
2017-10-02 10:35:27 -04:00
Here is the `i18n` attribute with a _description_ , followed by the custom `id` :
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
< code-example path = 'i18n/src/app/app.component.1.html' region = 'i18n-attribute-id' title = 'app/app.component.html' linenums = "false" >
< / code-example >
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
Here is a _meaning_ and a _description_ and the _id_ at the end:
2017-03-27 11:08:53 -04:00
2017-05-02 02:01:20 -04:00
< code-example path = 'i18n/src/app/app.component.1.html' region = 'i18n-attribute-meaning-and-id' title = 'app/app.component.html' linenums = "false" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
< div class = "l-sub-section" >
2017-03-31 19:57:13 -04:00
2017-05-02 02:01:20 -04:00
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.
2017-03-31 19:57:13 -04:00
2017-05-02 02:01:20 -04:00
For example:
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
```html
< p i18n = "@@myId" > Hello< / p >
< p i18n = "@@myId" > Good bye< / p >
```
2017-04-12 15:53:18 -04:00
2017-05-02 02:01:20 -04:00
with the translation:
```xml
< trans-unit id = "myId" datatype = "html" >
< source > Hello< / source >
< target state = "new" > Hola< / target >
< / trans-unit >
```
2017-04-12 15:53:18 -04:00
2017-05-02 02:01:20 -04:00
Both `<p>` elements will contain the text `Hola` .
< / div >
{@a no-element}
2017-04-12 15:53:18 -04:00
2017-02-22 13:09:39 -05:00
### Translate text without creating an element
2017-09-01 08:50:58 -04:00
If there is a stretch of text that you'd like to translate, you could wrap it in a `<span>` tag.
But if you don't want to create a new DOM element merely to facilitate translation,
you can wrap the text in an `<ng-container>` element.
The `<ng-container>` will be transformed into an html comment:
2017-02-22 13:09:39 -05:00
2017-04-21 20:21:45 -04:00
< code-example path = "i18n/src/app/app.component.html" region = "i18n-ng-container" title = "src/app/app.component.html" linenums = "false" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
{@a translate-attributes}
2017-03-31 19:57:13 -04:00
2017-05-02 02:01:20 -04:00
## Add _i18n_ translation attributes
2017-02-22 13:09:39 -05:00
You've added an image to your template. You care about accessibility too so you add a `title` attribute:
2017-04-21 20:21:45 -04:00
< code-example path = "i18n/src/app/app.component.1.html" region = "i18n-title" title = "src/app/app.component.html" linenums = "false" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-04-12 15:53:18 -04:00
The `title` attribute needs to be translated.
2017-02-22 13:09:39 -05:00
Angular i18n support has more translation attributes in the form,`i18n-x`, where `x` is the
2017-04-12 15:53:18 -04:00
name of the attribute to translate.
2017-02-22 13:09:39 -05:00
To translate the `title` on the `img` tag from the previous example, write:
2017-04-21 20:21:45 -04:00
< code-example path = "i18n/src/app/app.component.html" region = "i18n-title-translate" title = "src/app/app.component.html" linenums = "false" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
You can also assign a meaning and a description with the `i18n-x="<meaning>|<description>"` syntax.
{@a cardinality}
2017-03-31 19:57:13 -04:00
2017-02-22 13:09:39 -05:00
## Handle singular and plural
Different languages have different pluralization rules.
2017-04-12 15:53:18 -04:00
Suppose your application says something about a collection of wolves.
2017-02-22 13:09:39 -05:00
In English, depending upon the number of wolves, you could display "no wolves", "one wolf", "two wolves", or "a wolf pack".
2017-04-12 15:53:18 -04:00
Other languages might express the _cardinality_ differently.
2017-02-22 13:09:39 -05:00
Here's how you could mark up the component template to display the phrase appropriate to the number of wolves:
2017-04-21 20:21:45 -04:00
< code-example path = "i18n/src/app/app.component.html" region = "i18n-plural" title = "src/app/app.component.html" linenums = "false" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-04-12 15:53:18 -04:00
* The first parameter is the key. It is bound to the component property (`wolves`)
2017-02-22 13:09:39 -05:00
that determines the number of wolves.
* The second parameter identifies this as a `plural` translation type.
2017-04-12 15:53:18 -04:00
* The third parameter defines a pluralization pattern consisting of pluralization
2017-02-22 13:09:39 -05:00
categories and their matching values.
Pluralization categories include:
2017-03-31 19:57:13 -04:00
2017-05-02 02:01:20 -04:00
* =0 (or any other number)
* zero
* one
* two
2017-02-22 13:09:39 -05:00
* few
2017-05-02 02:01:20 -04:00
* many
2017-02-22 13:09:39 -05:00
* other
Put the default _English_ translation in braces (`{}`) next to the pluralization category.
2017-05-02 02:01:20 -04:00
2017-02-22 13:09:39 -05:00
* When you're talking about one wolf, you could write `=1 {one wolf}` .
2017-03-31 19:57:13 -04:00
2017-02-22 13:09:39 -05:00
* For zero wolves, you could write `=0 {no wolves}` .
2017-05-02 02:01:20 -04:00
2017-04-12 15:53:18 -04:00
* For two wolves, you could write `=2 {two wolves}` .
2017-02-22 13:09:39 -05:00
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}` .
2017-04-10 11:51:13 -04:00
< div class = "l-sub-section" >
2017-03-27 11:08:53 -04:00
2017-02-22 13:09:39 -05:00
This syntax conforms to the
2017-04-24 14:23:45 -04:00
< a href = "http://userguide.icu-project.org/formatparse/messages" title = "ICU Message Format" > ICU Message Format< / a >
2017-04-12 15:53:18 -04:00
that derives from the
2017-04-24 14:23:45 -04:00
< a href = "http://cldr.unicode.org/" title = "CLDR" > Common Locale Data Repository (CLDR)< / a > ,
2017-02-22 13:09:39 -05:00
which specifies the
2017-05-02 02:01:20 -04:00
< a href = "http://cldr.unicode.org/index/cldr-spec/plural-rules" title = "Pluralization Rules" > pluralization rules< / a > .
2017-02-22 13:09:39 -05:00
2017-04-10 11:51:13 -04:00
< / div >
2017-03-27 11:08:53 -04:00
2017-02-22 13:09:39 -05:00
{@a select}
2017-03-31 19:57:13 -04:00
2017-02-22 13:09:39 -05:00
## Select among alternative texts
2017-05-02 02:01:20 -04:00
2017-02-22 13:09:39 -05:00
The application displays different text depending upon whether the hero is male or female.
These text alternatives require translation too.
2017-04-12 15:53:18 -04:00
You can handle this with a `select` translation.
2017-02-22 13:09:39 -05:00
A `select` also follows the
2017-04-24 14:23:45 -04:00
< a href = "http://userguide.icu-project.org/formatparse/messages" title = "ICU Message Format" > ICU message syntax< / a > .
2017-02-22 13:09:39 -05:00
You choose among alternative translation based on a string value instead of a number.
2017-04-12 15:53:18 -04:00
The following format message in the component template binds to the component's `gender`
2017-02-22 13:09:39 -05:00
property, which outputs either an "m" or an "f".
The message maps those values to the appropriate translation:
2017-04-21 20:21:45 -04:00
< code-example path = "i18n/src/app/app.component.html" region = "i18n-select" title = "src/app/app.component.html" linenums = "false" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
## Nesting pluralization and selection expressions
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
You can also nest different ICU expressions together. For example:
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
< code-example path = "i18n/src/app/app.component.html" region = "i18n-nested" title = "src/app/app.component.html" >
< / code-example >
2017-03-31 19:57:13 -04:00
2017-05-02 02:01:20 -04:00
{@a ng-xi18n}
2017-03-31 19:57:13 -04:00
2017-02-22 13:09:39 -05:00
## Create a translation source file with the _ng-xi18n_ tool
2017-04-12 15:53:18 -04:00
Use the **_ng-xi18n_ extraction tool** to extract the `i18n` -marked texts
2017-02-22 13:09:39 -05:00
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" >
2017-03-31 19:57:13 -04:00
npm install @angular/compiler -cli @angular/platform -server --save
2017-02-22 13:09:39 -05:00
< / 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" >
2017-03-31 19:57:13 -04:00
./node_modules/.bin/ng-xi18n
2017-02-22 13:09:39 -05:00
< / code-example >
2017-04-10 11:51:13 -04:00
< div class = "l-sub-section" >
2017-03-27 11:08:53 -04:00
2017-02-22 13:09:39 -05:00
Windows users may have to quote the command like this: `"./node_modules/.bin/ng-xi18n"`
2017-03-27 11:08:53 -04:00
2017-04-10 11:51:13 -04:00
< / div >
2017-03-27 11:08:53 -04:00
2017-02-22 13:09:39 -05:00
By default, the tool generates a translation file named ** `messages.xlf` ** in the
2017-05-02 02:01:20 -04:00
< a href = "https://en.wikipedia.org/wiki/XLIFF" > XML Localization Interchange File Format (XLIFF, version 1.2)< / a > .
2017-02-22 13:09:39 -05:00
{@a other-formats}
2017-03-31 19:57:13 -04:00
2017-02-22 13:09:39 -05:00
### Other translation formats
2017-05-02 02:01:20 -04:00
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 > .
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
You can specify your choice of format _explicitly_ with the `--i18nFormat` flag as illustrated in these example commands
2017-03-30 15:04:18 -04:00
2017-02-22 13:09:39 -05:00
< code-example language = "sh" class = "code-shell" >
2017-05-02 02:01:20 -04:00
./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
2017-02-22 13:09:39 -05:00
< / code-example >
2017-05-02 02:01:20 -04:00
The sample in _this_ guide sticks with the default _XLIFF 1.2_ format.
2017-02-22 13:09:39 -05:00
{@a ng-xi18n-options}
2017-03-31 19:57:13 -04:00
2017-02-22 13:09:39 -05:00
### Other options
2017-05-02 02:01:20 -04:00
2017-04-12 15:53:18 -04:00
You may have to specify additional options.
For example, if the `tsconfig.json` TypeScript configuration
2017-02-22 13:09:39 -05:00
file is located somewhere other than in the root folder,
you must identify the path to it with the `-p` option:
2017-03-30 15:04:18 -04:00
2017-02-22 13:09:39 -05:00
< code-example language = "sh" class = "code-shell" >
2017-03-31 19:57:13 -04:00
./node_modules/.bin/ng-xi18n -p path/to/tsconfig.json
./node_modules/.bin/ng-xi18n --i18nFormat=xmb -p path/to/tsconfig.json
2017-02-22 13:09:39 -05:00
< / code-example >
{@a npm-i18n-script}
2017-03-31 19:57:13 -04:00
2017-02-22 13:09:39 -05:00
### 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:
2017-03-30 15:04:18 -04:00
2017-02-22 13:09:39 -05:00
< code-example format = '.' language = 'sh' >
2017-03-31 19:57:13 -04:00
"scripts": {
2017-04-12 15:53:18 -04:00
"i18n": "ng-xi18n",
2017-03-31 19:57:13 -04:00
...
}
2017-02-22 13:09:39 -05:00
< / code-example >
Now you can issue command variations such as these:
2017-03-30 15:04:18 -04:00
2017-02-22 13:09:39 -05:00
< code-example language = "sh" class = "code-shell" >
2017-03-31 19:57:13 -04:00
npm run i18n
npm run i18n -- -p path/to/tsconfig.json
npm run i18n -- --i18nFormat=xmb -p path/to/tsconfig.json
2017-02-22 13:09:39 -05:00
< / code-example >
2017-04-12 15:53:18 -04:00
Note the `--` flag before the options.
2017-02-22 13:09:39 -05:00
It tells _npm_ to pass every flag thereafter to `ng-xi18n` .
{@a translate}
## Translate text messages
2017-04-12 15:53:18 -04:00
The `ng-xi18n` command generates a translation source file
2017-02-22 13:09:39 -05:00
in the project root folder named `messages.xlf` .
2017-04-12 15:53:18 -04:00
The next step is to translate the English language template
2017-02-22 13:09:39 -05:00
text into the specific language translation
2017-05-02 02:01:20 -04:00
files. The guide sample creates a Spanish translation file.
2017-02-22 13:09:39 -05:00
{@a localization-folder}
2017-03-31 19:57:13 -04:00
2017-02-22 13:09:39 -05:00
### 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.
2017-04-12 15:53:18 -04:00
One approach is to dedicate a folder to localization and store related assets,
such as internationalization files, there.
2017-03-27 11:08:53 -04:00
2017-04-10 11:51:13 -04:00
< div class = "l-sub-section" >
2017-03-27 11:08:53 -04:00
2017-04-12 15:53:18 -04:00
Localization and internationalization are
2017-04-24 14:23:45 -04:00
< a href = "https://en.wikipedia.org/wiki/Internationalization_and_localization" > different but closely related terms< / a > .
2017-03-27 11:08:53 -04:00
2017-04-10 11:51:13 -04:00
< / div >
2017-03-27 11:08:53 -04:00
2017-05-02 02:01:20 -04:00
This guide follows that suggestion. It has a `locale` folder under `src/` .
2017-02-22 13:09:39 -05:00
Assets within the folder carry a filename extension that matches a language-culture code from a
2017-04-24 14:23:45 -04:00
< a href = "https://msdn.microsoft.com/en-us/library/ee825488(v=cs.20).aspx" > well-known codeset< / a > .
2017-02-22 13:09:39 -05:00
2017-04-12 15:53:18 -04:00
Make a copy of the `messages.xlf` file, put it in the `locale` folder and
2017-02-22 13:09:39 -05:00
rename it `messages.es.xlf` for the Spanish language translation.
Do the same for each target language.
2017-04-12 15:53:18 -04:00
{@a translate-text-nodes}
2017-02-22 13:09:39 -05:00
### 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
2017-04-24 14:23:45 -04:00
< a href = "https://en.wikipedia.org/wiki/XLIFF#Editors" > many XLIFF file editors< / a > .
2017-02-22 13:09:39 -05:00
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:
2017-04-21 20:21:45 -04:00
< code-example path = "i18n/src/locale/messages.es.xlf.html" region = "translated-hello" title = "src/locale/messages.es.xlf (<trans-unit>)" linenums = "false" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
This XML element represents the translation of the `<h1>` greeting tag you marked with the `i18n` attribute.
2017-03-31 19:57:13 -04:00
2017-05-02 02:01:20 -04:00
< div class = "l-sub-section" >
2017-03-31 19:57:13 -04:00
2017-08-02 18:56:01 -04:00
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.
2017-05-02 02:01:20 -04:00
< / div >
2017-02-22 13:09:39 -05:00
Using the _source_ , _description_ , and _meaning_ elements to guide your translation,
replace the `<target/>` tag with the Spanish greeting:
2017-04-21 20:21:45 -04:00
< 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" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
Translate the other text nodes the same way:
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
< 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 >
2017-02-22 13:09:39 -05:00
2017-04-10 11:51:13 -04:00
< div class = "alert is-important" >
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
**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.
2017-02-22 13:09:39 -05:00
Change either factor and the `id` changes as well.
2017-05-02 02:01:20 -04:00
See the ** [translation file maintenance discussion ](#maintenance )**.
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
This is why you should ** [specify custom ids ](#custom-id "Set a custom id" )** and avoid tool generated ids.
2017-02-22 13:09:39 -05:00
2017-04-10 11:51:13 -04:00
< / div >
2017-02-22 13:09:39 -05:00
{@a translate-plural-select}
2017-03-31 19:57:13 -04:00
2017-02-22 13:09:39 -05:00
## Translate _plural_ and _select_
Translating _plural_ and _select_ messages is a little tricky.
2017-04-12 15:53:18 -04:00
The `<source>` tag is empty for `plural` and `select` translation
2017-02-22 13:09:39 -05:00
units, which makes them hard to correlate with the original template.
2017-04-12 15:53:18 -04:00
The `XLIFF` format doesn't yet support the ICU rules.
2017-02-22 13:09:39 -05:00
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.
2017-03-31 19:57:13 -04:00
2017-04-12 15:53:18 -04:00
{@a translate-plural}
### Translate _plural_
2017-02-22 13:09:39 -05:00
To translate a `plural` , translate its ICU format match values:
2017-04-21 20:21:45 -04:00
< code-example path = "i18n/src/locale/messages.es.xlf.html" region = "translated-plural" title = "src/locale/messages.es.xlf (<trans-unit>)" linenums = "false" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-04-12 15:53:18 -04:00
{@a translate-select}
### Translate _select_
2017-02-22 13:09:39 -05:00
The `select` behaves a little differently. Here again is the ICU format message in the component template:
2017-04-21 20:21:45 -04:00
< code-example path = "i18n/src/app/app.component.html" region = "i18n-select" title = "src/app/app.component.html" linenums = "false" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
The extraction tool broke that into _two_ translation units.
The first unit contains the text that was _outside_ the `select` .
2017-04-12 15:53:18 -04:00
In place of the `select` is a placeholder, `<x id="ICU">` , that represents the `select` message.
2017-02-22 13:09:39 -05:00
Translate the text and leave the placeholder where it is.
2017-04-21 20:21:45 -04:00
< code-example path = "i18n/src/locale/messages.es.xlf.html" region = "translate-select-1" title = "src/locale/messages.es.xlf (<trans-unit>)" linenums = "false" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
The second translation unit, immediately below the first one, contains the `select` message. Translate that.
2017-04-21 20:21:45 -04:00
< code-example path = "i18n/src/locale/messages.es.xlf.html" region = "translate-select-2" title = "src/locale/messages.es.xlf (<trans-unit>)" linenums = "false" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
Here they are together, after translation:
2017-04-21 20:21:45 -04:00
< code-example path = "i18n/src/locale/messages.es.xlf.html" region = "translated-select" title = "src/locale/messages.es.xlf (<trans-unit>)" linenums = "false" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
### Translate a nested expression
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
A nested expression is not different from the previous ones. As in the previous example, we have _two_ translation units.
2017-03-30 15:04:18 -04:00
2017-05-02 02:01:20 -04:00
The first one contains the text outside the nested expression:
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
< 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 >
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
The second unit contains the complete nested expression:
2017-03-31 19:57:13 -04:00
2017-05-02 02:01:20 -04:00
< 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 >
2017-03-31 19:57:13 -04:00
2017-05-02 02:01:20 -04:00
And both together:
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
< 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 >
2017-03-30 15:04:18 -04:00
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
The entire template translation is complete. It's
time to incorporate that translation into the application.
2017-03-31 19:57:13 -04:00
2017-05-02 02:01:20 -04:00
< a id = 'app-pre-translation' > < / a >
2017-03-31 19:57:13 -04:00
2017-02-22 13:09:39 -05:00
### The app before translation
When the previous steps finish, the sample app _and_ its translation file are as follows:
2017-03-27 11:08:53 -04:00
< code-tabs >
2017-04-21 20:21:45 -04:00
< code-pane title = "src/app/app.component.html" path = "i18n/src/app/app.component.html" >
2017-03-27 11:08:53 -04:00
< / code-pane >
2017-04-21 20:21:45 -04:00
< code-pane title = "src/app/app.component.ts" path = "i18n/src/app/app.component.ts" >
2017-03-27 11:08:53 -04:00
< / code-pane >
2017-04-21 20:21:45 -04:00
< code-pane title = "src/app/app.module.ts" path = "i18n/src/app/app.module.ts" >
2017-03-27 11:08:53 -04:00
< / code-pane >
2017-04-21 20:21:45 -04:00
< code-pane title = "src/main.ts" path = "i18n/src/main.1.ts" >
2017-03-27 11:08:53 -04:00
< / code-pane >
2017-04-21 20:21:45 -04:00
< code-pane title = "src/locale/messages.es.xlf" path = "i18n/src/locale/messages.es.xlf.html" >
2017-03-27 11:08:53 -04:00
< / code-pane >
< / code-tabs >
2017-02-22 13:09:39 -05:00
{@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.
2017-04-12 15:53:18 -04:00
The process is the same whether the file is in `.xlf` format or
2017-05-02 02:01:20 -04:00
in another format that Angular understands, such as `.xtb` .
2017-02-22 13:09:39 -05:00
You provide the Angular compiler with three new pieces of information:
2017-03-31 19:57:13 -04:00
2017-04-12 15:53:18 -04:00
* The translation file.
* The translation file format.
2017-04-24 14:23:45 -04:00
* The < a href = "https://en.wikipedia.org/wiki/XLIFF" > _Locale ID_</ a >
2017-04-12 15:53:18 -04:00
(`es` or `en-US` for instance).
2017-02-22 13:09:39 -05:00
_How_ you provide this information depends upon whether you compile with
2017-04-12 15:53:18 -04:00
the JIT (_Just-in-Time_) compiler or the AOT (_Ahead-of-Time_) compiler.
2017-02-22 13:09:39 -05:00
2017-03-11 10:36:40 -05:00
* With [JIT ](guide/i18n#jit ), you provide the information at bootstrap time.
2017-04-12 15:53:18 -04:00
* With [AOT ](guide/i18n#aot ), you pass the information as `ngc` options.
2017-02-22 13:09:39 -05:00
{@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:
2017-04-21 20:21:45 -04:00
< code-example path = "i18n/src/index.html" region = "i18n" title = "index.html (launch script)" linenums = "false" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
In this sample, the user's language is hard-coded as a global `document.locale` variable
2017-02-22 13:09:39 -05:00
in the `index.html` .
{@a text-plugin}
2017-03-31 19:57:13 -04:00
2017-04-12 15:53:18 -04:00
### SystemJS text plugin
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
< 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 >
2017-02-22 13:09:39 -05:00
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:
2017-04-21 20:21:45 -04:00
< code-example path = "i18n/src/systemjs-text-plugin.js" title = "src/systemjs-text-plugin.js" linenums = "false" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-04-12 15:53:18 -04:00
{@a create-translation-providers}
2017-02-22 13:09:39 -05:00
### 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.
2017-05-02 02:01:20 -04:00
* `TRANSLATIONS_FORMAT` is the format of the file: `xlf` , `xlf2` , or `xtb` .
2017-02-22 13:09:39 -05:00
* `LOCALE_ID` is the locale of the target language.
2017-04-12 15:53:18 -04:00
The `getTranslationProviders()` function in the following `src/app/i18n-providers.ts`
2017-02-22 13:09:39 -05:00
creates those providers based on the user's _locale_
and the corresponding translation file:
2017-05-02 02:01:20 -04:00
< code-example path = "i18n/src/app/i18n-providers.ts" region = "without-missing-translation" title = "src/app/i18n-providers.ts" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
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.
2017-04-12 15:53:18 -04:00
1. It creates a transaction filename from the locale according to the name and location convention
2017-03-11 10:36:40 -05:00
[described earlier ](guide/i18n#localization-folder ).
2017-02-22 13:09:39 -05:00
2017-04-12 15:53:18 -04:00
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 ).
2017-02-22 13:09:39 -05:00
1. The callback composes a providers array with the three translation providers.
2017-04-12 15:53:18 -04:00
1. Finally, `getTranslationProviders()` returns the entire effort as a promise.
2017-05-02 02:01:20 -04:00
< 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 >
2017-04-12 15:53:18 -04:00
2017-05-02 02:01:20 -04:00
{@a bootstrap-the-app}
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
### Bootstrap with translation providers
2017-02-22 13:09:39 -05:00
2017-04-12 15:53:18 -04:00
The Angular `bootstrapModule()` method has a second _options_ parameter
2017-02-22 13:09:39 -05:00
that can influence the behavior of the compiler.
2017-04-12 15:53:18 -04:00
You'll create an _options_ object with the translation providers from `getTranslationProviders()`
2017-02-22 13:09:39 -05:00
and pass it to `bootstrapModule` .
Open the `src/main.ts` and modify the bootstrap code as follows:
2017-04-21 20:21:45 -04:00
< code-example path = "i18n/src/main.ts" title = "src/main.ts" linenums = "false" >
2017-03-27 11:08:53 -04:00
< / code-example >
2017-02-22 13:09:39 -05:00
2017-04-12 15:53:18 -04:00
Notice that it waits for the `getTranslationProviders()` promise to resolve before
2017-02-22 13:09:39 -05:00
bootstrapping the app.
The app is now _internationalized_ for English and Spanish and there is a clear path for adding
more languages.
{@a aot}
2017-04-12 15:53:18 -04:00
### _Internationalization_ with the AOT compiler
2017-02-22 13:09:39 -05:00
2017-04-12 15:53:18 -04:00
The JIT compiler translates the application into the target language
2017-02-22 13:09:39 -05:00
while compiling dynamically in the browser.
2017-04-12 15:53:18 -04:00
That's flexible but may not be fast enough for your users.
2017-02-22 13:09:39 -05:00
2017-04-12 15:53:18 -04:00
The AOT (_Ahead-of-Time_) compiler is part of a build process that
2017-02-22 13:09:39 -05:00
produces a small, fast, ready-to-run application package.
2017-04-12 15:53:18 -04:00
When you internationalize with the AOT compiler, you pre-build
2017-02-22 13:09:39 -05:00
a separate application package for each
2017-04-12 15:53:18 -04:00
language. Then in the host web page, in this case `index.html` ,
2017-02-22 13:09:39 -05:00
you determine which language the user needs
and serve the appropriate application package.
2017-05-02 02:01:20 -04:00
This guide doesn't cover how to build multiple application packages and
2017-02-22 13:09:39 -05:00
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.
2017-04-12 15:53:18 -04:00
Internationalization with the AOT compiler requires
some setup specifically for AOT compilation.
Start with the application project as shown
2017-03-11 10:36:40 -05:00
[just before merging the translation file ](guide/i18n#app-pre-translation )
2017-05-02 02:01:20 -04:00
and refer to the [AOT guide ](guide/aot-compiler ) to make it _AOT-ready_ .
2017-02-22 13:09:39 -05:00
2017-04-12 15:53:18 -04:00
Next, issue an `ngc` compile command for each supported language, including English.
2017-02-22 13:09:39 -05:00
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:
2017-03-31 19:57:13 -04:00
2017-04-12 15:53:18 -04:00
* `--i18nFile` : the path to the translation file.
* `--locale` : the name of the locale.
* `--i18nFormat` : the format of the localization file.
2017-02-22 13:09:39 -05:00
2017-04-12 15:53:18 -04:00
For this sample, the Spanish language command would be:
2017-03-30 15:04:18 -04:00
2017-02-22 13:09:39 -05:00
< code-example language = "sh" class = "code-shell" >
2017-03-31 19:57:13 -04:00
./node_modules/.bin/ngc --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf
2017-02-22 13:09:39 -05:00
< / code-example >
2017-04-10 11:51:13 -04:00
< div class = "l-sub-section" >
2017-03-27 11:08:53 -04:00
2017-02-22 13:09:39 -05:00
Windows users may have to quote the command:
2017-03-30 15:04:18 -04:00
2017-02-22 13:09:39 -05:00
< code-example language = "sh" class = "code-shell" >
2017-03-31 19:57:13 -04:00
"./node_modules/.bin/ngc" --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf
2017-02-22 13:09:39 -05:00
< / code-example >
2017-05-02 02:01:20 -04:00
< / div >
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
### Report missing translations
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
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:
2017-03-27 11:08:53 -04:00
2017-05-02 02:01:20 -04:00
* Error
* Warning (default)
* Ignore
2017-03-27 11:08:53 -04:00
2017-05-02 02:01:20 -04:00
To change the behavior in JIT, you can use the following configuration:
2017-03-27 11:08:53 -04:00
2017-05-02 02:01:20 -04:00
< 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 >
2017-03-31 19:57:13 -04:00
2017-05-02 02:01:20 -04:00
To change the behavior in AOT, add the `--missingTranslation` flag to the compilation command:
2017-03-31 19:57:13 -04:00
2017-05-02 02:01:20 -04:00
< 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
2017-04-12 15:53:18 -04:00
As the application evolves, you will change the _i18n_ markup
2017-02-22 13:09:39 -05:00
and re-run the `ng-xi18n` extraction tool many times.
2017-05-02 02:01:20 -04:00
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.
2017-02-22 13:09:39 -05:00
2017-04-12 15:53:18 -04:00
After an `id` changes, the translation files are no longer in sync.
2017-05-02 02:01:20 -04:00
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.
2017-02-22 13:09:39 -05:00
2017-05-02 02:01:20 -04:00
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** ,
2017-02-22 13:09:39 -05:00
especially the English source `messages.xlf` .
The difference between the old and the new `messages.xlf` file
2017-05-02 02:01:20 -04:00
will help you find and update `ids` and other changes across your translation files.