diff --git a/aio/content/examples/i18n/e2e-spec.ts b/aio/content/examples/i18n/e2e-spec.ts
index 3dcca5a670..f1d6686e77 100644
--- a/aio/content/examples/i18n/e2e-spec.ts
+++ b/aio/content/examples/i18n/e2e-spec.ts
@@ -30,4 +30,8 @@ describe('i18n E2E Tests', () => {
expect(element.all(by.css('span')).get(1).getText()).toBe('El heroe es mujer');
});
+ it('should display the nested expression', function() {
+ expect(element.all(by.css('span')).get(2).getText()).toBe('Aquí tenemos: 3 mujeres');
+ });
+
});
diff --git a/aio/content/examples/i18n/messages.xlf b/aio/content/examples/i18n/messages.xlf
index dee8b65aca..50f99d2820 100644
--- a/aio/content/examples/i18n/messages.xlf
+++ b/aio/content/examples/i18n/messages.xlf
@@ -2,8 +2,10 @@
-
- Hello i18n!
+
-
-
-
-
-
-
@@ -42,6 +38,14 @@ I don't output any element either
+
+ Here we have:
+
+
+
+
+
+
diff --git a/aio/content/examples/i18n/src/app/app.component.1.html b/aio/content/examples/i18n/src/app/app.component.1.html
index cc7d4f1155..ef8c968d31 100644
--- a/aio/content/examples/i18n/src/app/app.component.1.html
+++ b/aio/content/examples/i18n/src/app/app.component.1.html
@@ -10,6 +10,23 @@
Hello i18n!
+
+Hello i18n!
+
+
+
+Hello i18n!
+
+
+
+Hello i18n!
+
+
+
+Hello i18n!
+
+
+Contact GitHub API Training Shop Blog About
diff --git a/aio/content/examples/i18n/src/app/app.component.html b/aio/content/examples/i18n/src/app/app.component.html
index 39ace24f79..36d0188982 100644
--- a/aio/content/examples/i18n/src/app/app.component.html
+++ b/aio/content/examples/i18n/src/app/app.component.html
@@ -1,6 +1,8 @@
-Hello i18n!
+
+ Hello i18n!
+
@@ -31,4 +33,11 @@ I don't output any element either
The hero is {gender, select, m {male} f {female}}
-
+
+
+Here we have: {count, plural,
+ =0 {no one}
+ =1 {one {gender, select, male {man} female {woman}}}
+ other {{{heroes.length}} {gender, select, male {men} female {women}}}
+}
+
diff --git a/aio/content/examples/i18n/src/app/app.component.ts b/aio/content/examples/i18n/src/app/app.component.ts
index 09b8722e9b..6e091ffdd0 100644
--- a/aio/content/examples/i18n/src/app/app.component.ts
+++ b/aio/content/examples/i18n/src/app/app.component.ts
@@ -10,6 +10,8 @@ export class AppComponent {
gender = 'f';
fly = true;
logo = 'https://angular.io/resources/images/logos/angular/angular.png';
+ count = 3;
+ heroes: string[] = ['Magneta', 'Celeritas', 'Dynama'];
inc(i: number) {
this.wolves = Math.min(5, Math.max(0, this.wolves + i));
}
diff --git a/aio/content/examples/i18n/src/app/i18n-providers.ts b/aio/content/examples/i18n/src/app/i18n-providers.ts
index f0bb662dc3..0e820bbcfd 100644
--- a/aio/content/examples/i18n/src/app/i18n-providers.ts
+++ b/aio/content/examples/i18n/src/app/i18n-providers.ts
@@ -1,5 +1,7 @@
-// #docregion
-import { TRANSLATIONS, TRANSLATIONS_FORMAT, LOCALE_ID } from '@angular/core';
+// #docplaster
+// #docregion without-missing-translation
+import { TRANSLATIONS, TRANSLATIONS_FORMAT, LOCALE_ID, MissingTranslationStrategy } from '@angular/core';
+import { CompilerConfig } from '@angular/compiler';
export function getTranslationProviders(): Promise {
@@ -17,13 +19,18 @@ export function getTranslationProviders(): Promise {
// Ex: 'locale/messages.es.xlf`
const translationFile = `./locale/messages.${locale}.xlf`;
+ // #docregion missing-translation
return getTranslationsWithSystemJs(translationFile)
.then( (translations: string ) => [
{ provide: TRANSLATIONS, useValue: translations },
{ provide: TRANSLATIONS_FORMAT, useValue: 'xlf' },
- { provide: LOCALE_ID, useValue: locale }
+ { provide: LOCALE_ID, useValue: locale },
+ // #enddocregion without-missing-translation
+ { provide: CompilerConfig, useValue: new CompilerConfig({ missingTranslation: MissingTranslationStrategy.Error }) }
+ // #docregion without-missing-translation
])
.catch(() => noProviders); // ignore if file not found
+ // #enddocregion missing-translation
}
declare var System: any;
@@ -31,3 +38,4 @@ declare var System: any;
function getTranslationsWithSystemJs(file: string) {
return System.import(file + '!text'); // relies on text plugin
}
+// #enddocregion without-missing-translation
diff --git a/aio/content/examples/i18n/src/locale/messages.es.xlf b/aio/content/examples/i18n/src/locale/messages.es.xlf
index 7b813c38dd..f2d454b5e6 100644
--- a/aio/content/examples/i18n/src/locale/messages.es.xlf
+++ b/aio/content/examples/i18n/src/locale/messages.es.xlf
@@ -2,7 +2,7 @@
-
+
+
+ Here we have:
+ Aquí tenemos:
+
+
+
+
+ {count, plural,
+ =0 { nadie }
+ =1 {{gender, select, m {un hombre} f {una mujer}}}
+ other {{{heroes.length}} {gender, select, m {hombres} f {mujeres}}}
+ }
+
+
diff --git a/aio/content/examples/i18n/src/locale/messages.es.xlf.html b/aio/content/examples/i18n/src/locale/messages.es.xlf.html
index a6cdccc6c1..1c94311234 100644
--- a/aio/content/examples/i18n/src/locale/messages.es.xlf.html
+++ b/aio/content/examples/i18n/src/locale/messages.es.xlf.html
@@ -5,7 +5,9 @@
-
+
+
+
+
I don't output any element
No genero ningún elemento
@@ -48,6 +52,34 @@
+
+ Here we have:
+
+
+
+
+
+
+
+
+
+ Here we have:
+ Aquí tenemos:
+
+
+
+
+
+
+ {count, plural,
+ =0 { nadie }
+ =1 {{gender, select, m {un hombre} f {una mujer}}}
+ other {{{heroes.length}} {gender, select, m {hombres} f {mujeres}}}
+ }
+
+
+
+
diff --git a/aio/content/guide/i18n.md b/aio/content/guide/i18n.md
index 1d2348f30b..1541d83f33 100644
--- a/aio/content/guide/i18n.md
+++ b/aio/content/guide/i18n.md
@@ -1,19 +1,12 @@
# Internationalization (i18n)
-{@a top}
-
-
Angular's _internationalization_ (_i18n_) tools help make your app available in multiple languages.
Try this 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
@@ -23,20 +16,13 @@ Angular's _i18n_ internationalization facilities can help.
This page describes the _i18n_ tools available to assist translation of component template text
into multiple languages.
-
-
-
-
Practitioners of _internationalization_ refer to a translatable text as a "_message_".
-This page uses the words "_text_" and "_message_" interchangably and in the combination, "_text message_".
-
+This page uses the words "_text_" and "_message_" interchangeably and in the combination, "_text message_".
-
-
The _i18n_ template translation process has four phases:
1. Mark static text messages in your component templates for translation.
@@ -52,11 +38,8 @@ 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.
@@ -65,73 +48,112 @@ Place it on every element tag whose fixed text should be translated.
-
-
`i18n` is not an Angular _directive_.
It's a custom _attribute_, recognized by Angular tools and compilers.
After translation, the compiler removes it.
-
-
-
In the accompanying sample, an `` tag displays a simple English language greeting
that you translate into Spanish:
-
-
-
Add the `i18n` attribute to the tag to mark it for translation.
-
-
-
-
{@a help-translator}
-
-### Help the translator with a _description_ and _intent_
+### 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:
-
-
-
-
In order to deliver a correct translation, the translator may need to
-know your _intent_—the true _meaning_ of the text
-within _this particular_ application context.
-In front of the description, add some contextual meaning to the assigned string,
-separating it from the description with the `|` character (`|`):
+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 (`|`):
-
-
+
-
-
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 facilitiate contextually-specific translations.
+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:
+
+
+
+
+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 `@@`**.
+
+
+
+
+Now the extractor tool and compiler will generate a translation unit with _your custom id_ and never change it.
+
+
+
+
+Here is the `i18n` attribute with a _definition_, followed by the custom `id`:
+
+
+
+
+Here is a _meaning_ and a _description_ and the _id_ at the end:
+
+
+
+
+
+
+ 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
+
Hello
+
Good bye
+ ```
+
+ with the translation:
+
+ ```xml
+
+ Hello
+ Hola
+
+ ```
+
+ Both `
` elements will contain the text `Hola`.
+
+
+
{@a no-element}
-
### Translate text without creating an element
Suppose there is a stretch of text that you'd like to translate.
@@ -140,58 +162,37 @@ 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 `` element. The `` is never renderered:
-
+(1) Wrap the text in an `` element. The `` is never rendered:
-
-
-
(2) Wrap the text in a pair of HTML comments:
-
-
-
-
-
{@a translate-attributes}
-
-## Add _i18n-..._ translation attributes
+## Add _i18n_ translation attributes
You've added an image to your template. You care about accessibility too so you add a `title` attribute:
-
-
-
-
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:
-
-
-
-
You can also assign a meaning and a description with the `i18n-x="|"` syntax.
-
-
{@a cardinality}
-
## Handle singular and plural
Different languages have different pluralization rules.
@@ -202,13 +203,9 @@ 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:
-
-
-
-
* 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.
@@ -217,43 +214,41 @@ categories and their matching values.
Pluralization categories include:
-* =0
-* =1
-* =5
+* =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}`.
-
-
-
{@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.
@@ -266,17 +261,18 @@ The following format message in the component template binds to the component's
property, which outputs either an "m" or an "f".
The message maps those values to the appropriate translation:
-
-
+## Nesting pluralization and selection expressions
+You can also nest different ICU expressions together. For 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
@@ -285,63 +281,46 @@ 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:
-
npm install @angular/compiler-cli @angular/platform-server --save
-
-
-
Open a terminal window at the root of the application project and enter the `ng-xi18n` command:
-
./node_modules/.bin/ng-xi18n
-
-
-
-
-
Windows users may have to quote the command like this: `"./node_modules/.bin/ng-xi18n"`
-
-
-
By default, the tool generates a translation file named **`messages.xlf`** in the
-XML Localisation Interchange File Format (XLIFF, version 1.2) .
-
+XML Localization Interchange File Format (XLIFF, version 1.2) .
{@a other-formats}
-
### Other translation formats
-You can generate a file named **`messages.xmb`** in the
-XML Message Bundle (XMB) format
-by adding the `--i18nFormat=xmb` flag.
+Angular i18n tooling supports XLIFF 1.2 and XLIFF 2 as well as the
+XML Message Bundle (XMB) .
+You can specify your choice of format _explicitly_ with the `--i18nFormat` flag as illustrated in these example commands
- ./node_modules/.bin/ng-xi18n --i18nFormat=xmb
-
+./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
-
-
-This sample sticks with the _XLIFF_ format.
-
+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,
@@ -350,15 +329,10 @@ you must identify the path to it with the `-p` option:
./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}
-
### Add an _npm_ script for convenience
Consider adding a convenience shortcut to the `scripts` section of the `package.json`
@@ -371,8 +345,6 @@ to make the command easier to remember and run:
}
-
-
Now you can issue command variations such as these:
@@ -381,28 +353,21 @@ Now you can issue command variations such as these:
npm run i18n -- --i18nFormat=xmb -p path/to/tsconfig.json
-
-
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 cookbook sample creates a Spanish translation file.
-
+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
@@ -413,16 +378,12 @@ such as internationalization files, there.
-
-
-This cookbook follows that suggestion. It has a `locale` folder under the `src/`.
+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
well-known codeset .
@@ -432,7 +393,6 @@ 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
@@ -441,50 +401,41 @@ using one of the
This sample file is easy to translate without a special editor or knowledge of Spanish.
Open `messages.es.xlf` and find the first `` section:
-
-
-
-
This XML element represents the translation of the `` greeting tag you marked with the `i18n` attribute.
+
+
+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.
+
+
+
Using the _source_, _description_, and _meaning_ elements to guide your translation,
replace the ` ` tag with the Spanish greeting:
-
-
-
-
-
-
-
-Note that the tool generates the `id`. **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](guide/i18n#maintenance)**.
-
-
-
-
-
-
Translate the other text nodes the same way:
-
-
+
+**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.
+
+
{@a translate-plural-select}
-
## Translate _plural_ and _select_
Translating _plural_ and _select_ messages is a little tricky.
@@ -496,121 +447,92 @@ 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:
-
-
-
-
{@a translate-select}
-
### Translate _select_
The `select` behaves a little differently. Here again is the ICU format message in the component template:
-
-
-
-
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, ``, that represents the `select` message.
Translate the text and leave the placeholder where it is.
-
-
-
-
The second translation unit, immediately below the first one, contains the `select` message. Translate that.
-
-
-
-
Here they are together, after translation:
-
-
+### 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:
-
+
+
+
+The second unit contains the complete nested expression:
+
+
+
+
+And both together:
+
+
+
The entire template translation is complete. It's
time to incorporate that translation into the application.
-
-
-
-
-
-
+
### The app before translation
When the previous steps finish, the sample app _and_ its translation file are as follows:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{@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 `.xlif` or `.xtb`.
+in another format that Angular understands, such as `.xtb`.
You provide the Angular compiler with three new pieces of information:
@@ -625,11 +547,8 @@ 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.
@@ -643,20 +562,22 @@ Translation with the JIT compiler is a dynamic process of:
Open `index.html` and revise the launch script as follows:
-
-
-
-In this sample, the user's language is hardcoded as a global `document.locale` variable
+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
+
+
+ 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).
+
+
+
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.
@@ -666,33 +587,26 @@ 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:
-
-
-
{@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`, `xlif`, or `xtb`.
+* `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:
-
-
+
-
-
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.
@@ -709,10 +623,15 @@ Notice that it appends `!text` to the filename, telling SystemJS to use the [tex
1. Finally, `getTranslationProviders()` returns the entire effort as a promise.
+
+
+ The `LOCALE_ID` has to be a valid locale id as explained in [here](http://userguide.icu-project.org/locale).
+
+
+
{@a bootstrap-the-app}
-
-### Bootstrap the app with translation providers
+### Bootstrap with translation providers
The Angular `bootstrapModule()` method has a second _options_ parameter
that can influence the behavior of the compiler.
@@ -722,22 +641,16 @@ and pass it to `bootstrapModule`.
Open the `src/main.ts` and modify the bootstrap code as follows:
-
-
-
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
@@ -752,7 +665,7 @@ 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 cookbook doesn't cover how to build multiple application packages and
+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.
@@ -760,7 +673,7 @@ 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 cookbook](guide/aot-compiler) to make it _AOT-ready_.
+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.
@@ -775,45 +688,65 @@ For this sample, the Spanish language command would be:
./node_modules/.bin/ngc --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf
-
-
-
-
-
Windows users may have to quote the command:
"./node_modules/.bin/ngc" --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf
-
-
-
+### 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:
+
+
+ { provide: CompilerConfig, useValue: new CompilerConfig({ missingTranslation: MissingTranslationStrategy.Error }) }
+
+
+A good place to use it is the translation providers:
+
+
+
+To change the behavior in AOT, add the `--missingTranslation` flag to the compilation command:
+
+
+ ./node_modules/.bin/ngc --i18nFile=./locale/messages.es.xlf --locale=es --i18nFormat=xlf --missingTranslation=error
+
{@a maintenance}
-
-## Translation file maintenance and _id_ changes
+## 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 trigger
-generation of new `id`s for the affected translation units.
+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.
-**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.
+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.
-**Commit all translation message files to source control**,
+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
- help you find and update `id` changes across your translation files.
+will help you find and update `ids` and other changes across your translation files.
+