Compare commits
45 Commits
8.2.0-rc.0
...
4.2.3
Author | SHA1 | Date | |
---|---|---|---|
8a547eeee0 | |||
4211432fc8 | |||
b8c39cdf71 | |||
9c7a84de51 | |||
dbc6a4cb12 | |||
784410e3c8 | |||
90a5a1ef43 | |||
301f99cd6c | |||
64e63b9422 | |||
b192dd5761 | |||
96aa3bb135 | |||
8abc1df2c1 | |||
f5eb528a5c | |||
5cf06a9f3f | |||
ab90f63575 | |||
150d271f79 | |||
a686eb2c9e | |||
bfa788935a | |||
64fa100a71 | |||
5fae987bfa | |||
9c4cda1c7d | |||
d9cbe56b63 | |||
86df7108b0 | |||
e7a4f92be7 | |||
76af452d29 | |||
11dfb685f4 | |||
d363aa0aa4 | |||
209d74c342 | |||
fec8f6febe | |||
ee9daaf4c8 | |||
4164369db4 | |||
747b6a61b8 | |||
9ed836c939 | |||
e4c82443f9 | |||
a2f232166b | |||
668f9ede65 | |||
b784829512 | |||
ad4fee7053 | |||
2d31e17251 | |||
39cff565ee | |||
203c5ba1b3 | |||
eda7bb5c3e | |||
1480a30050 | |||
d6087f75e2 | |||
dc084a5bdf |
@ -3,7 +3,7 @@ jobs:
|
||||
build:
|
||||
working_directory: ~/ng
|
||||
docker:
|
||||
- image: alexeagle/ngcontainer
|
||||
- image: angular/ngcontainer
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
|
64
.github/ISSUE_TEMPLATE.md
vendored
@ -1,39 +1,57 @@
|
||||
<!--
|
||||
IF YOU DON'T FILL OUT THE FOLLOWING INFORMATION WE MIGHT CLOSE YOUR ISSUE WITHOUT INVESTIGATING
|
||||
PLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.
|
||||
|
||||
ISSUES MISSING IMPORTANT INFORMATION MIGHT BE CLOSED WITHOUT INVESTIGATION.
|
||||
-->
|
||||
|
||||
**I'm submitting a ...** (check one with "x")
|
||||
```
|
||||
[ ] bug report => search github for a similar issue or PR before submitting
|
||||
[ ] feature request
|
||||
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
|
||||
```
|
||||
## I'm submitting a ...
|
||||
<!-- Check one of the following options with "x" -->
|
||||
<pre><code>
|
||||
[ ] Regression (behavior that used to work and stopped working in a new release)
|
||||
[ ] Bug report <!-- Please search github for a similar issue or PR before submitting -->
|
||||
[ ] Feature request
|
||||
[ ] Documentation issue or request
|
||||
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
|
||||
</code></pre>
|
||||
|
||||
**Current behavior**
|
||||
<!-- Describe how the bug manifests. -->
|
||||
## Current behavior
|
||||
<!-- Describe how the issue manifests. -->
|
||||
|
||||
**Expected behavior**
|
||||
<!-- Describe what the behavior would be without the bug. -->
|
||||
|
||||
**Minimal reproduction of the problem with instructions**
|
||||
## Expected behavior
|
||||
<!-- Describe what the desired behavior would be. -->
|
||||
|
||||
|
||||
## Minimal reproduction of the problem with instructions
|
||||
<!--
|
||||
If the current behavior is a bug or you can illustrate your feature request better with an example,
|
||||
please provide the *STEPS TO REPRODUCE* and if possible a *MINIMAL DEMO* of the problem via
|
||||
For bug reports please provide the *STEPS TO REPRODUCE* and if possible a *MINIMAL DEMO* of the problem via
|
||||
https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
|
||||
-->
|
||||
|
||||
**What is the motivation / use case for changing the behavior?**
|
||||
<!-- Describe the motivation or the concrete use case -->
|
||||
## What is the motivation / use case for changing the behavior?
|
||||
<!-- Describe the motivation or the concrete use case. -->
|
||||
|
||||
**Please tell us about your environment:**
|
||||
<!-- Operating system, IDE, package manager, HTTP server, ... -->
|
||||
|
||||
* **Angular version:** 2.0.X
|
||||
## Please tell us about your environment
|
||||
|
||||
<pre><code>
|
||||
Angular version: X.Y.Z
|
||||
<!-- Check whether this is still an issue in the most recent Angular version -->
|
||||
|
||||
* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
|
||||
<!-- All browsers where this could be reproduced -->
|
||||
Browser:
|
||||
- [ ] Chrome (desktop) version XX
|
||||
- [ ] Chrome (Android) version XX
|
||||
- [ ] Chrome (iOS) version XX
|
||||
- [ ] Firefox version XX
|
||||
- [ ] Safari (desktop) version XX
|
||||
- [ ] Safari (iOS) version XX
|
||||
- [ ] IE version XX
|
||||
- [ ] Edge version XX
|
||||
|
||||
* **Language:** [all | TypeScript X.X | ES6/7 | ES5]
|
||||
For Tooling issues:
|
||||
- Node version: XX <!-- use `node --version` -->
|
||||
- Platform: <!-- Mac, Linux, Windows -->
|
||||
|
||||
* **Node (for AoT issues):** `node --version` =
|
||||
Others:
|
||||
<!-- Anything else relevant? Operating system version, IDE, package manager, HTTP server, ... -->
|
||||
</code></pre>
|
||||
|
27
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,10 +1,15 @@
|
||||
**Please check if the PR fulfills these requirements**
|
||||
## PR Checklist
|
||||
Does please check if your PR fulfills the following requirements:
|
||||
|
||||
- [ ] The commit message follows our guidelines: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit
|
||||
- [ ] Tests for the changes have been added (for bug fixes / features)
|
||||
- [ ] Docs have been added / updated (for bug fixes / features)
|
||||
|
||||
|
||||
**What kind of change does this PR introduce?** (check one with "x")
|
||||
## PR Type
|
||||
What kind of change does this PR introduce?
|
||||
|
||||
<!-- Please check the one that applies to this PR using "x". -->
|
||||
```
|
||||
[ ] Bugfix
|
||||
[ ] Feature
|
||||
@ -12,25 +17,27 @@
|
||||
[ ] Refactoring (no functional changes, no api changes)
|
||||
[ ] Build related changes
|
||||
[ ] CI related changes
|
||||
[ ] Documentation content changes
|
||||
[ ] angular.io application / infrastructure changes
|
||||
[ ] Other... Please describe:
|
||||
```
|
||||
|
||||
**What is the current behavior?** (You can also link to an open issue here)
|
||||
## What is the current behavior?
|
||||
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->
|
||||
|
||||
Issue Number: N/A
|
||||
|
||||
|
||||
|
||||
**What is the new behavior?**
|
||||
## What is the new behavior?
|
||||
|
||||
|
||||
|
||||
**Does this PR introduce a breaking change?** (check one with "x")
|
||||
## Does this PR introduce a breaking change?
|
||||
```
|
||||
[ ] Yes
|
||||
[ ] No
|
||||
```
|
||||
|
||||
If this PR contains a breaking change, please describe the impact and migration path for existing applications: ...
|
||||
<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->
|
||||
|
||||
|
||||
**Other information**:
|
||||
|
||||
## Other information
|
||||
|
34
CHANGELOG.md
@ -1,3 +1,37 @@
|
||||
<a name="4.2.3"></a>
|
||||
## [4.2.3](https://github.com/angular/angular/compare/4.2.1...4.2.3) (2017-06-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **aio:** add missing redirect rule for `/styleguide` ([64e63b9](https://github.com/angular/angular/commit/64e63b9)), closes [#17542](https://github.com/angular/angular/issues/17542)
|
||||
* **aio:** always cover the whole footer with its background ([86df710](https://github.com/angular/angular/commit/86df710)), closes [#17465](https://github.com/angular/angular/issues/17465)
|
||||
* **aio:** do not log messages in production ([ab90f63](https://github.com/angular/angular/commit/ab90f63)), closes [#17453](https://github.com/angular/angular/issues/17453)
|
||||
* **aio:** ensure that API filter page can display 3 columns in wide view ([203c5ba](https://github.com/angular/angular/commit/203c5ba)), closes [#17251](https://github.com/angular/angular/issues/17251)
|
||||
* **aio:** fix buttons in "Home" and "Features" ([2d31e17](https://github.com/angular/angular/commit/2d31e17)), closes [#17448](https://github.com/angular/angular/issues/17448)
|
||||
* **aio:** fix scrolling to elements near the bottom of the page ([a2f2321](https://github.com/angular/angular/commit/a2f2321)), closes [#17452](https://github.com/angular/angular/issues/17452)
|
||||
* **aio:** fix trackBy demo in template-syntax article ([e7a4f92](https://github.com/angular/angular/commit/e7a4f92))
|
||||
* **aio:** make search results better ([ad4fee7](https://github.com/angular/angular/commit/ad4fee7)), closes [#17417](https://github.com/angular/angular/issues/17417)
|
||||
* **aio:** make the footer links clickable on all browsers ([d363aa0](https://github.com/angular/angular/commit/d363aa0)), closes [#17460](https://github.com/angular/angular/issues/17460)
|
||||
* **aio:** remove gap between sidenav menus ([e543272](https://github.com/angular/angular/commit/e543272)), closes [#17394](https://github.com/angular/angular/issues/17394)
|
||||
* **aio:** remove outline from search input on focus ([d6087f7](https://github.com/angular/angular/commit/d6087f7)), closes [#17396](https://github.com/angular/angular/issues/17396)
|
||||
* **aio:** show search results when search box gets focus ([668f9ed](https://github.com/angular/angular/commit/668f9ed))
|
||||
* **aio:** specify large image for PWA splash-screen ([64fa100](https://github.com/angular/angular/commit/64fa100))
|
||||
* **aio:** tidy up layout of api filter page ([eda7bb5](https://github.com/angular/angular/commit/eda7bb5)), closes [#17395](https://github.com/angular/angular/issues/17395)
|
||||
* **aio:** use locally hosted lunr library ([b784829](https://github.com/angular/angular/commit/b784829))
|
||||
* **animations:** compute removal node height correctly ([185075d](https://github.com/angular/angular/commit/185075d))
|
||||
* **animations:** do not treat a `0` animation state as `void` ([451257a](https://github.com/angular/angular/commit/451257a))
|
||||
* **animations:** properly collect :enter nodes in a partially updated collection ([6ca4692](https://github.com/angular/angular/commit/6ca4692)), closes [#17440](https://github.com/angular/angular/issues/17440)
|
||||
* **animations:** remove duplicate license header ([b192dd5](https://github.com/angular/angular/commit/b192dd5))
|
||||
* **forms:** temp roll back breaking change with min/max directives ([b8c39cd](https://github.com/angular/angular/commit/b8c39cd)), closes [#17491](https://github.com/angular/angular/issues/17491)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **aio:** add iphone pwa features ([8abc1df](https://github.com/angular/angular/commit/8abc1df))
|
||||
|
||||
|
||||
|
||||
<a name="4.2.2"></a>
|
||||
## [4.2.2](https://github.com/angular/angular/compare/4.2.1...4.2.2) (2017-06-12)
|
||||
|
||||
|
@ -3,10 +3,10 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
||||
git_repository(
|
||||
name = "io_bazel_rules_typescript",
|
||||
remote = "https://github.com/bazelbuild/rules_typescript.git",
|
||||
tag = "0.0.3",
|
||||
commit = "804c5da",
|
||||
)
|
||||
|
||||
load("@io_bazel_rules_typescript//:defs.bzl", "node_repositories", "yarn_install")
|
||||
load("@io_bazel_rules_typescript//:defs.bzl", "node_repositories", "npm_install")
|
||||
|
||||
node_repositories()
|
||||
yarn_install(package_json = "//:package.json")
|
||||
npm_install(package_json = "//:package.json")
|
||||
|
@ -31,6 +31,7 @@
|
||||
"environmentSource": "environments/environment.ts",
|
||||
"environments": {
|
||||
"dev": "environments/environment.ts",
|
||||
"stage": "environments/environment.stage.ts",
|
||||
"prod": "environments/environment.prod.ts"
|
||||
}
|
||||
}
|
||||
|
3
aio/.gitignore
vendored
@ -43,3 +43,6 @@ protractor-results*.txt
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# copied dependencies
|
||||
src/assets/js/lunr*
|
@ -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');
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -2,8 +2,10 @@
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<file source-language="en" datatype="plaintext" original="ng2.template">
|
||||
<body>
|
||||
<trans-unit id="af2ccf4b5dba59616e92cf1531505af02da8f6d2" datatype="html">
|
||||
<source>Hello i18n!</source>
|
||||
<trans-unit id="introductionHeader" datatype="html">
|
||||
<source>
|
||||
Hello i18n!
|
||||
</source>
|
||||
<target/>
|
||||
<note priority="1" from="description">An introduction header for this sample</note>
|
||||
<note priority="1" from="meaning">User welcome</note>
|
||||
@ -24,12 +26,6 @@ I don't output any element either
|
||||
<source>Angular logo</source>
|
||||
<target/>
|
||||
</trans-unit>
|
||||
<trans-unit id="2579611bfcccd75bcd41fac90150d27d6ebb30b8" datatype="html">
|
||||
<source>
|
||||
<x id="START_TAG_SPAN" ctype="x-span"/><x id="ICU"/><x id="CLOSE_TAG_SPAN" ctype="x-span"/>
|
||||
</source>
|
||||
<target/>
|
||||
</trans-unit>
|
||||
<trans-unit id="6e22e74e8cbd3095560cfe08993c4fdfa3c50eb0" datatype="html">
|
||||
<source/>
|
||||
<target/>
|
||||
@ -42,6 +38,14 @@ I don't output any element either
|
||||
<source/>
|
||||
<target/>
|
||||
</trans-unit>
|
||||
<trans-unit id="2cf9a08c5b6e3612572a2a36dd46563013848382" datatype="html">
|
||||
<source>Here we have: <x id="ICU"/></source>
|
||||
<target/>
|
||||
</trans-unit>
|
||||
<trans-unit id="db1b921b55301ce3957e382090729562002da036" datatype="html">
|
||||
<source/>
|
||||
<target/>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
@ -10,6 +10,23 @@
|
||||
<h1 i18n="An introduction header for this sample">Hello i18n!</h1>
|
||||
<!--#enddocregion i18n-attribute-desc-->
|
||||
|
||||
<!--#docregion i18n-attribute-meaning-->
|
||||
<h1 i18n="site header|An introduction header for this sample">Hello i18n!</h1>
|
||||
<!--#enddocregion i18n-attribute-meaning-->
|
||||
|
||||
<!--#docregion i18n-attribute-id-->
|
||||
<h1 i18n="An introduction header for this sample@@introductionHeader">Hello i18n!</h1>
|
||||
<!--#enddocregion i18n-attribute-id-->
|
||||
|
||||
<!--#docregion i18n-attribute-meaning-and-id-->
|
||||
<h1 i18n="site header|An introduction header for this sample@@introductionHeader">Hello i18n!</h1>
|
||||
<!--#enddocregion i18n-attribute-meaning-and-id-->
|
||||
|
||||
<!--#docregion i18n-attribute-solo-id-->
|
||||
<h1 i18n="@@introductionHeader">Hello i18n!</h1>
|
||||
<!--#enddocregion i18n-attribute-solo-id-->
|
||||
|
||||
<!--#docregion i18n-title-->
|
||||
<img [src]="logo" title="Angular logo">
|
||||
<!--#enddocregion i18n-title-->
|
||||
Contact GitHub API Training Shop Blog About
|
||||
|
@ -1,6 +1,8 @@
|
||||
<!--#docregion-->
|
||||
<!--#docregion i18n-attribute-meaning-->
|
||||
<h1 i18n="User welcome|An introduction header for this sample">Hello i18n!</h1>
|
||||
<h1 i18n="User welcome|An introduction header for this sample@@introductionHeader">
|
||||
Hello i18n!
|
||||
</h1>
|
||||
<!--#enddocregion i18n-attribute-meaning-->
|
||||
|
||||
<!--#docregion i18n-ng-container-->
|
||||
@ -31,4 +33,11 @@ I don't output any element either
|
||||
<!--#docregion i18n-select-->
|
||||
<span i18n>The hero is {gender, select, m {male} f {female}}</span>
|
||||
<!--#enddocregion i18n-select-->
|
||||
<br>
|
||||
<br><br>
|
||||
<!--#docregion i18n-nested-->
|
||||
<span i18n>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}}}
|
||||
}</span>
|
||||
<!--#enddocregion i18n-nested-->
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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<Object[]> {
|
||||
|
||||
@ -17,13 +19,18 @@ export function getTranslationProviders(): Promise<Object[]> {
|
||||
// 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
|
||||
|
@ -2,7 +2,7 @@
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<file source-language="en" datatype="plaintext" original="ng2.template">
|
||||
<body>
|
||||
<trans-unit id="af2ccf4b5dba59616e92cf1531505af02da8f6d2" datatype="html">
|
||||
<trans-unit id="introductionHeader" datatype="html">
|
||||
<source>Hello i18n!</source>
|
||||
<target>¡Hola i18n!</target>
|
||||
<note priority="1" from="description">An introduction header for this sample</note>
|
||||
@ -36,6 +36,20 @@ I don't output any element either
|
||||
<source/>
|
||||
<target>{gender, select, m {hombre} f {mujer}}</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="2cf9a08c5b6e3612572a2a36dd46563013848382" datatype="html">
|
||||
<source>Here we have: <x id="ICU"/></source>
|
||||
<target>Aquí tenemos: <x id="ICU"/></target>
|
||||
</trans-unit>
|
||||
<trans-unit id="db1b921b55301ce3957e382090729562002da036" datatype="html">
|
||||
<source/>
|
||||
<target>
|
||||
{count, plural,
|
||||
=0 { nadie }
|
||||
=1 {{gender, select, m {un hombre} f {una mujer}}}
|
||||
other {{{heroes.length}} {gender, select, m {hombres} f {mujeres}}}
|
||||
}
|
||||
</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
@ -5,7 +5,9 @@
|
||||
<file source-language="en" datatype="plaintext" original="ng2.template">
|
||||
<body>
|
||||
<!-- #docregion translated-hello -->
|
||||
<trans-unit id="af2ccf4b5dba59616e92cf1531505af02da8f6d2" datatype="html">
|
||||
<!-- #docregion custom-id -->
|
||||
<trans-unit id="introductionHeader" datatype="html">
|
||||
<!-- #enddocregion custom-id -->
|
||||
<source>Hello i18n!</source>
|
||||
<target>¡Hola i18n!</target>
|
||||
<note priority="1" from="description">An introduction header for this sample</note>
|
||||
@ -13,7 +15,9 @@
|
||||
</trans-unit>
|
||||
<!-- #enddocregion translated-hello -->
|
||||
<!-- #docregion translated-other-nodes -->
|
||||
<!-- #docregion generated-id -->
|
||||
<trans-unit id="ba0cc104d3d69bf669f97b8d96a4c5d8d9559aa3" datatype="html">
|
||||
<!-- #enddocregion generated-id -->
|
||||
<source>I don't output any element</source>
|
||||
<target>No genero ningún elemento</target>
|
||||
</trans-unit>
|
||||
@ -48,6 +52,34 @@
|
||||
</trans-unit>
|
||||
<!-- #enddocregion translate-select-2 -->
|
||||
<!-- #enddocregion translated-select -->
|
||||
<trans-unit id="db04527df562d12c8607eab2b5723ef6e2066ba0" datatype="html">
|
||||
<source>Here we have: <x id="ICU"/></source>
|
||||
<target/>
|
||||
</trans-unit>
|
||||
<trans-unit id="000058be4e6f08b685d1d0a70f9da68067df7379" datatype="html">
|
||||
<source/>
|
||||
<target/>
|
||||
</trans-unit>
|
||||
<!-- #docregion translate-nested -->
|
||||
<!-- #docregion translate-nested-1 -->
|
||||
<trans-unit id="2cf9a08c5b6e3612572a2a36dd46563013848382" datatype="html">
|
||||
<source>Here we have: <x id="ICU"/></source>
|
||||
<target>Aquí tenemos: <x id="ICU"/></target>
|
||||
</trans-unit>
|
||||
<!-- #enddocregion translate-nested-1 -->
|
||||
<!-- #docregion translate-nested-2 -->
|
||||
<trans-unit id="db1b921b55301ce3957e382090729562002da036" datatype="html">
|
||||
<source/>
|
||||
<target>
|
||||
{count, plural,
|
||||
=0 { nadie }
|
||||
=1 {{gender, select, m {un hombre} f {una mujer}}}
|
||||
other {{{heroes.length}} {gender, select, m {hombres} f {mujeres}}}
|
||||
}
|
||||
</target>
|
||||
</trans-unit>
|
||||
<!-- #enddocregion translate-nested-2 -->
|
||||
<!-- #enddocregion translate-nested -->
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
@ -36,6 +36,7 @@
|
||||
<a href="#inputs-and-outputs">Inputs and outputs</a><br>
|
||||
<a href="#pipes">Pipes</a><br>
|
||||
<a href="#safe-navigation-operator">Safe navigation operator <i>?.</i></a><br>
|
||||
<a href="#non-null-assertion-operator">Non-null assertion operator <i>!.</i></a><br>
|
||||
<a href="#enums">Enums</a><br>
|
||||
|
||||
<!-- Interpolation and expressions -->
|
||||
@ -803,6 +804,12 @@ The null hero's name is {{nullHero && nullHero.name}}
|
||||
<!-- #enddocregion safe-6 -->
|
||||
</div>
|
||||
|
||||
|
||||
<a class="to-toc" href="#toc">top</a>
|
||||
|
||||
<!-- non-null assertion operator -->
|
||||
<hr><h2 id="non-null-assertion-operator">Non-null assertion operator <i>!.</i></h2>
|
||||
|
||||
<div>
|
||||
<!-- #docregion non-null-assertion-1 -->
|
||||
<!--No hero, no text -->
|
||||
|
@ -127,6 +127,7 @@ export class AppComponent implements AfterViewInit, OnInit {
|
||||
resetHeroes() {
|
||||
this.heroes = Hero.heroes.map(hero => hero.clone());
|
||||
this.currentHero = this.heroes[0];
|
||||
this.hero = this.currentHero;
|
||||
this.heroesWithTrackByCountReset = 0;
|
||||
}
|
||||
|
||||
@ -172,8 +173,8 @@ function trackChanges(views: QueryList<ElementRef>, changed: () => void) {
|
||||
let oldRefs = views.toArray();
|
||||
views.changes.subscribe((changes: QueryList<ElementRef>) => {
|
||||
const changedRefs = changes.toArray();
|
||||
// Is every changed ElemRef the same as old and in the same position
|
||||
const isSame = oldRefs.every((v, i) => v === changedRefs[i]);
|
||||
// Check if every changed Element is the same as old and in the same position
|
||||
const isSame = oldRefs.every((v, i) => v.nativeElement === changedRefs[i].nativeElement);
|
||||
if (!isSame) {
|
||||
oldRefs = changedRefs;
|
||||
// wait a tick because called after views are constructed
|
||||
|
@ -41,7 +41,7 @@
|
||||
"raw-loader": "^0.5.1",
|
||||
"rimraf": "^2.5.2",
|
||||
"style-loader": "^0.13.1",
|
||||
"typescript": "~2.0.10",
|
||||
"typescript": "~2.3.1",
|
||||
"webpack": "2.2.1",
|
||||
"webpack-dev-server": "2.4.1",
|
||||
"webpack-merge": "^3.0.0"
|
||||
|
@ -1,27 +1,21 @@
|
||||
# Form Validation
|
||||
|
||||
{@a top}
|
||||
|
||||
|
||||
|
||||
Improve overall data quality by validating user input for accuracy and completeness.
|
||||
|
||||
This cookbook shows how to validate user input in the UI and display useful validation messages
|
||||
using first the template-driven forms and then the reactive forms approach.
|
||||
This page shows how to validate user input in the UI and display useful validation messages
|
||||
using first the Template Driven Forms and then the Reactive Forms approach.
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
Read more about these choices in the [Forms](guide/forms)
|
||||
and the [Reactive Forms](guide/reactive-forms) guides.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{@a toc}
|
||||
|
||||
{@a live-example}
|
||||
|
||||
|
||||
@ -34,17 +28,29 @@ and the [Reactive Forms](guide/reactive-forms) guides.
|
||||
|
||||
|
||||
|
||||
{@a template1}
|
||||
## Built-in validators
|
||||
|
||||
Angular forms include a number of built-in validator functions, which are functions
|
||||
that help you check common user input in forms. In addition to the built-in
|
||||
validators covered here of `minlength`, `maxlength`,
|
||||
and `required`, there are others such as `min`, `max`, `email` and `pattern`
|
||||
for Template Driven as well as Reactive Forms.
|
||||
For a full list of built-in validators,
|
||||
see the [Validators](api/forms/Validators) API reference.
|
||||
|
||||
|
||||
## Simple template-driven forms
|
||||
|
||||
In the template-driven approach, you arrange
|
||||
|
||||
|
||||
|
||||
## Simple Template Driven Forms
|
||||
|
||||
In the Template Driven approach, you arrange
|
||||
[form elements](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Forms_in_HTML) in the component's template.
|
||||
|
||||
You add Angular form directives (mostly directives beginning `ng...`) to help
|
||||
Angular construct a corresponding internal control model that implements form functionality.
|
||||
In template-drive forms, the control model is _implicit_ in the template.
|
||||
In Template Driven forms, the control model is _implicit_ in the template.
|
||||
|
||||
To validate user input, you add [HTML validation attributes](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation)
|
||||
to the elements. Angular interprets those as well, adding validator functions to the control model.
|
||||
@ -76,7 +82,8 @@ This gives you a reference to the Angular `NgModel` directive
|
||||
associated with this control that you can use _in the template_
|
||||
to check for control states such as `valid` and `dirty`.
|
||||
|
||||
* The `*ngIf` on the `<div>` element reveals a set of nested message `divs` but only if there are "name" errors and
|
||||
* The `*ngIf` on the `<div>` element reveals a set of nested message `divs`
|
||||
but only if there are `name` errors and
|
||||
the control is either `dirty` or `touched`.
|
||||
|
||||
* Each nested `<div>` can present a custom message for one of the possible validation errors.
|
||||
@ -112,9 +119,9 @@ as well as other code to support the view.
|
||||
|
||||
|
||||
|
||||
Use this template-driven validation technique when working with static forms with simple, standard validation rules.
|
||||
Use this Template Driven validation technique when working with static forms with simple, standard validation rules.
|
||||
|
||||
Here are the complete files for the first version of `HeroFormTemplateCompononent` in the template-driven approach:
|
||||
Here are the complete files for the first version of `HeroFormTemplateCompononent` in the Template Driven approach:
|
||||
|
||||
|
||||
<code-tabs>
|
||||
@ -132,10 +139,8 @@ Here are the complete files for the first version of `HeroFormTemplateCompononen
|
||||
|
||||
|
||||
|
||||
{@a template2}
|
||||
|
||||
|
||||
## Template-driven forms with validation messages in code
|
||||
## Template Driven Forms with validation messages in code
|
||||
|
||||
While the layout is straightforward,
|
||||
there are obvious shortcomings with the way it's handling validation messages:
|
||||
@ -152,7 +157,7 @@ In this example, you can move the logic and the messages into the component with
|
||||
the template and component.
|
||||
|
||||
Here's the hero name again, excerpted from the revised template
|
||||
(Template 2), next to the original version:
|
||||
(template 2), next to the original version:
|
||||
|
||||
<code-tabs>
|
||||
|
||||
@ -174,14 +179,14 @@ The `<input>` element HTML is almost the same. There are noteworthy differences:
|
||||
|
||||
* There's a new attribute, `forbiddenName`, that is actually a custom validation directive.
|
||||
It invalidates the control if the user enters "bob" in the name `<input>`([try it](guide/form-validation#live-example)).
|
||||
See the [custom validation](guide/form-validation#custom-validation) section later in this cookbook for more information
|
||||
See the [custom validation](guide/form-validation#custom-validation) section later in this page for more information
|
||||
on custom validation directives.
|
||||
|
||||
* The `#name` template variable is gone because the app no longer refers to the Angular control for this element.
|
||||
|
||||
* Binding to the new `formErrors.name` property is sufficent to display all name validation error messages.
|
||||
|
||||
* Binding to the new `formErrors.name` property is sufficient to display all name validation error messages.
|
||||
|
||||
|
||||
{@a component-class}
|
||||
|
||||
|
||||
@ -219,7 +224,7 @@ the name of that variable as a string (`'heroForm'` in this case).
|
||||
* The `heroForm` object changes several times during the life of the component, most notably when you add a new hero.
|
||||
Periodically inspecting it reveals these changes.
|
||||
|
||||
* Angular calls the `ngAfterViewChecked` [lifecycle hook method](guide/lifecycle-hooks#afterview)
|
||||
* Angular calls the `ngAfterViewChecked()` [lifecycle hook method](guide/lifecycle-hooks#afterview)
|
||||
when anything changes in the view.
|
||||
That's the right time to see if there's a new `heroForm` object.
|
||||
|
||||
@ -246,7 +251,7 @@ For each field, the `onValueChanged` handler does the following:
|
||||
* If such a control exists _and_ it's been changed ("dirty")
|
||||
_and_ it's invalid, the handler composes a consolidated error message for all of the control's errors.
|
||||
|
||||
Next, the component needs some error messages of course—a set for each validated property with
|
||||
Next, the component needs some error messages—a set for each validated property with
|
||||
one message per validation rule:
|
||||
|
||||
<code-example path="form-validation/src/app/template/hero-form-template2.component.ts" region="messages" title="template/hero-form-template2.component.ts (messages)" linenums="false">
|
||||
@ -278,8 +283,6 @@ Each field has approximately the same number of lines no matter its number of va
|
||||
The component also grows proportionally, at the rate of one line per validated field
|
||||
and one line per validation message.
|
||||
|
||||
Both trends are manageable.
|
||||
|
||||
Now that the messages are in code, you have more flexibility and can compose messages more efficiently.
|
||||
You can refactor the messages out of the component, perhaps to a service class that retrieves them from the server.
|
||||
In short, there are more opportunities to improve message handling now that text and logic have moved from template to code.
|
||||
@ -288,14 +291,14 @@ In short, there are more opportunities to improve message handling now that text
|
||||
{@a formmodule}
|
||||
|
||||
|
||||
### _FormModule_ and template-driven forms
|
||||
### _FormModule_ and Template Driven forms
|
||||
|
||||
Angular has two different forms modules—`FormsModule` and
|
||||
`ReactiveFormsModule`—that correspond with the
|
||||
two approaches to form development. Both modules come
|
||||
from the same `@angular/forms` library package.
|
||||
|
||||
You've been reviewing the "Template-driven" approach which requires the `FormsModule`.
|
||||
You've been reviewing the Template Driven approach which requires the `FormsModule`.
|
||||
Here's how you imported it in the `HeroFormTemplateModule`.
|
||||
|
||||
|
||||
@ -323,9 +326,9 @@ They're not germane to the validation story. Look at the [live example](guide/fo
|
||||
{@a reactive}
|
||||
|
||||
|
||||
## Reactive forms with validation in code
|
||||
## Reactive Forms with validation in code
|
||||
|
||||
In the template-driven approach, you markup the template with form elements, validation attributes,
|
||||
In the Template Driven approach, you mark up the template with form elements, validation attributes,
|
||||
and `ng...` directives from the Angular `FormsModule`.
|
||||
At runtime, Angular interprets the template and derives its _form control model_.
|
||||
|
||||
@ -334,23 +337,21 @@ You create the form control model in code. You write the template with form elem
|
||||
and `form...` directives from the Angular `ReactiveFormsModule`.
|
||||
At runtime, Angular binds the template elements to your control model based on your instructions.
|
||||
|
||||
This approach requires a bit more effort. *You have to write the control model and manage it*.
|
||||
|
||||
This allows you to do the following:
|
||||
|
||||
* Add, change, and remove validation functions on the fly.
|
||||
* Manipulate the control model dynamically from within the component.
|
||||
* [Test](guide/form-validation#testing) validation and control logic with isolated unit tests.
|
||||
|
||||
The following cookbook sample re-writes the hero form in _reactive forms_ style.
|
||||
The following sample re-writes the hero form in Reactive Forms style.
|
||||
|
||||
|
||||
{@a reactive-forms-module}
|
||||
|
||||
|
||||
### Switch to the _ReactiveFormsModule_
|
||||
The reactive forms classes and directives come from the Angular `ReactiveFormsModule`, not the `FormsModule`.
|
||||
The application module for the reactive forms feature in this sample looks like this:
|
||||
The Reactive Forms classes and directives come from the Angular `ReactiveFormsModule`, not the `FormsModule`.
|
||||
The application module for the Reactive Forms feature in this sample looks like this:
|
||||
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.module.ts" title="src/app/reactive/hero-form-reactive.module.ts" linenums="false">
|
||||
|
||||
@ -358,7 +359,7 @@ The application module for the reactive forms feature in this sample looks like
|
||||
|
||||
|
||||
|
||||
The reactive forms feature module and component are in the `src/app/reactive` folder.
|
||||
The Reactive Forms feature module and component are in the `src/app/reactive` folder.
|
||||
Focus on the `HeroFormReactiveComponent` there, starting with its template.
|
||||
|
||||
|
||||
@ -378,8 +379,8 @@ The `heroForm` is the control model that the component class builds and maintain
|
||||
|
||||
|
||||
|
||||
Next, modify the template HTML elements to match the _reactive forms_ style.
|
||||
Here is the "name" portion of the template again, revised for reactive forms and compared with the template-driven version:
|
||||
Next, modify the template HTML elements to match the Reactive Forms style.
|
||||
Here is the "name" portion of the template again, revised for Reactive Forms and compared with the Template Driven version:
|
||||
|
||||
<code-tabs>
|
||||
|
||||
@ -405,15 +406,13 @@ but rather for css styling and accessibility.
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
A future version of reactive forms will add the `required` HTML validation attribute to the DOM element
|
||||
(and perhaps the `aria-required` attribute) when the control has the `required` validator function.
|
||||
Currently, Reactive Forms doesn't add the `required` or `aria-required`
|
||||
HTML validation attribute to the DOM element
|
||||
when the control has the `required` validator function.
|
||||
|
||||
Until then, apply the `required` attribute _and_ add the `Validator.required` function
|
||||
to the control model, as you'll see below.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@ -426,15 +425,6 @@ The reactive approach does not use data binding to move data into and out of the
|
||||
That's all in code.
|
||||
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
The retreat from data binding is a principle of the reactive paradigm rather than a technical limitation.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{@a reactive-component-class}
|
||||
|
||||
@ -447,7 +437,7 @@ Angular no longer derives the control model from the template so you can no long
|
||||
You can create the Angular form control model explicitly with
|
||||
the help of the `FormBuilder` class.
|
||||
|
||||
Here's the section of code devoted to that process, paired with the template-driven code it replaces:
|
||||
Here's the section of code devoted to that process, paired with the Template Driven code it replaces:
|
||||
|
||||
<code-tabs>
|
||||
|
||||
@ -507,18 +497,17 @@ discussed in a separate [section below](guide/form-validation#custom-validation)
|
||||
|
||||
Learn more about `FormBuilder` in the [Introduction to FormBuilder](guide/reactive-forms#formbuilder) section of Reactive Forms guide.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{@a committing-changes}
|
||||
|
||||
|
||||
#### Committing hero value changes
|
||||
|
||||
In two-way data binding, the user's changes flow automatically from the controls back to the data model properties.
|
||||
Reactive forms do not use data binding to update data model properties.
|
||||
A Reactive Forms component should not use data binding to
|
||||
automatically update data model properties.
|
||||
The developer decides _when and how_ to update the data model from control values.
|
||||
|
||||
This sample updates the model twice:
|
||||
@ -533,18 +522,6 @@ The `onSubmit()` method simply replaces the `hero` object with the combined valu
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
This example is lucky in that the `heroForm.value` properties _just happen_ to
|
||||
correspond _exactly_ to the hero data object properties.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
The `addHero()` method discards pending changes and creates a brand new `hero` model object.
|
||||
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.ts" region="add-hero" title="form-validation/src/app/reactive/hero-form-reactive.component.ts" linenums="false">
|
||||
@ -556,7 +533,7 @@ The `addHero()` method discards pending changes and creates a brand new `hero` m
|
||||
Then it calls `buildForm()` again which replaces the previous `heroForm` control model with a new one.
|
||||
The `<form>` tag's `[formGroup]` binding refreshes the page with the new control model.
|
||||
|
||||
Here's the complete reactive component file, compared to the two template-driven component files.
|
||||
Here's the complete reactive component file, compared to the two Template Driven component files.
|
||||
|
||||
<code-tabs>
|
||||
|
||||
@ -581,7 +558,7 @@ Here's the complete reactive component file, compared to the two template-driven
|
||||
|
||||
|
||||
Run the [live example](guide/form-validation#live-example) to see how the reactive form behaves,
|
||||
and to compare all of the files in this cookbook sample.
|
||||
and to compare all of the files in this sample.
|
||||
|
||||
|
||||
</div>
|
||||
@ -594,7 +571,7 @@ and to compare all of the files in this cookbook sample.
|
||||
|
||||
## Custom validation
|
||||
This cookbook sample has a custom `forbiddenNameValidator()` function that's applied to both the
|
||||
template-driven and the reactive form controls. It's in the `src/app/shared` folder
|
||||
Template Driven and the reactive form controls. It's in the `src/app/shared` folder
|
||||
and declared in the `SharedModule`.
|
||||
|
||||
Here's the `forbiddenNameValidator()` function:
|
||||
@ -623,7 +600,7 @@ and whose value is an arbitrary dictionary of values that you could insert into
|
||||
|
||||
|
||||
### Custom validation directive
|
||||
In the reactive forms component, the `'name'` control's validator function list
|
||||
In the Reactive Forms component, the `'name'` control's validator function list
|
||||
has a `forbiddenNameValidator` at the bottom.
|
||||
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.ts" region="name-validators" title="reactive/hero-form-reactive.component.ts (name validators)" linenums="false">
|
||||
@ -632,7 +609,7 @@ has a `forbiddenNameValidator` at the bottom.
|
||||
|
||||
|
||||
|
||||
In the _template-driven_ example, the `<input>` has the selector (`forbiddenName`)
|
||||
In the Template Driven example, the `<input>` has the selector (`forbiddenName`)
|
||||
of a custom _attribute directive_, which rejects "bob".
|
||||
|
||||
<code-example path="form-validation/src/app/template/hero-form-template2.component.html" region="name-input" title="template/hero-form-template2.component.html (name input)" linenums="false">
|
||||
@ -703,7 +680,7 @@ see [Attribute Directives](guide/attribute-directives).
|
||||
|
||||
## Testing Considerations
|
||||
|
||||
You can write _isolated unit tests_ of validation and control logic in _Reactive Forms_.
|
||||
You can write _isolated unit tests_ of validation and control logic in Reactive Forms.
|
||||
|
||||
_Isolated unit tests_ probe the component class directly, independent of its
|
||||
interactions with its template, the DOM, other dependencies, or Angular itself.
|
||||
@ -711,8 +688,8 @@ interactions with its template, the DOM, other dependencies, or Angular itself.
|
||||
Such tests have minimal setup, are quick to write, and easy to maintain.
|
||||
They do not require the `Angular TestBed` or asynchronous testing practices.
|
||||
|
||||
That's not possible with _template-driven_ forms.
|
||||
The template-driven approach relies on Angular to produce the control model and
|
||||
That's not possible with Template Driven forms.
|
||||
The Template Driven approach relies on Angular to produce the control model and
|
||||
to derive validation rules from the HTML validation attributes.
|
||||
You must use the `Angular TestBed` to create component test instances,
|
||||
write asynchronous tests, and interact with the DOM.
|
||||
|
@ -407,6 +407,9 @@ like the Observable-based version.
|
||||
|
||||
</div>
|
||||
|
||||
First, make sure to import the `toPromise` operator of the RxJS library.
|
||||
|
||||
<code-example path="http/src/app/toh/hero.service.promise.ts" region="rxjs-imports" title="src/app/toh/hero.service.promise.ts (import rxjs)" linenums="false"></code-example>
|
||||
|
||||
Here is a comparison of the `HeroService` using Promises versus Observables,
|
||||
highlighting just the parts that are different.
|
||||
|
@ -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 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
|
||||
@ -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.
|
||||
|
||||
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
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_".
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
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.
|
||||
|
||||
<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 _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:
|
||||
|
||||
|
||||
<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 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 (`<meaning>|<description>`):
|
||||
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.html" region="i18n-attribute-meaning" title="src/app/app.component.html" linenums="false">
|
||||
|
||||
<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 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:
|
||||
|
||||
<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.
|
||||
@ -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 `<ng-container>` element. The `<ng-container>` is never renderered:
|
||||
|
||||
(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
|
||||
## 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.
|
||||
@ -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:
|
||||
|
||||
|
||||
<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.
|
||||
@ -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}`.
|
||||
|
||||
|
||||
<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://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules" title="Pluralization Rules">pluralization rules</a>.
|
||||
|
||||
<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.
|
||||
|
||||
@ -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:
|
||||
|
||||
|
||||
<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
|
||||
@ -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:
|
||||
|
||||
|
||||
<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 Localisation Interchange File Format (XLIFF, version 1.2)</a>.
|
||||
|
||||
<a href="https://en.wikipedia.org/wiki/XLIFF">XML Localization Interchange File Format (XLIFF, version 1.2)</a>.
|
||||
|
||||
{@a other-formats}
|
||||
|
||||
|
||||
### Other translation formats
|
||||
|
||||
You can generate a file named **`messages.xmb`** in the
|
||||
<a href="http://cldr.unicode.org/development/development-process/design-proposals/xmb" >XML Message Bundle (XMB)</a> format
|
||||
by adding the `--i18nFormat=xmb` flag.
|
||||
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=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
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
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:
|
||||
<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`
|
||||
@ -371,8 +345,6 @@ to make the command easier to remember and run:
|
||||
}
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
Now you can issue command variations such as these:
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
@ -381,28 +353,21 @@ Now you can issue command variations such as these:
|
||||
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 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.
|
||||
|
||||
<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 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
|
||||
<a href="https://msdn.microsoft.com/en-us/library/ee825488(v=cs.20).aspx">well-known codeset</a>.
|
||||
|
||||
@ -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 `<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>
|
||||
|
||||
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
|
||||
|
||||
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)**.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
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.
|
||||
|
||||
@ -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:
|
||||
|
||||
|
||||
<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.
|
||||
|
||||
<div class='l-main-content'>
|
||||
The first one contains the text outside the nested expression:
|
||||
|
||||
</div>
|
||||
<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.
|
||||
|
||||
|
||||
<div id='app-pre-translation'>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<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 `.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:
|
||||
|
||||
<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 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
|
||||
|
||||
<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.
|
||||
@ -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:
|
||||
|
||||
<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`, `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:
|
||||
|
||||
<code-example path="i18n/src/app/i18n-providers.ts" title="src/app/i18n-providers.ts">
|
||||
|
||||
<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.
|
||||
@ -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.
|
||||
|
||||
<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 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:
|
||||
|
||||
<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
|
||||
@ -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:
|
||||
|
||||
<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}
|
||||
|
||||
|
||||
## 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.
|
||||
|
||||
|
@ -12,6 +12,13 @@ component class instance (the *component*) and its user-facing template.
|
||||
You may be familiar with the component/template duality from your experience with model-view-controller (MVC) or model-view-viewmodel (MVVM).
|
||||
In Angular, the component plays the part of the controller/viewmodel, and the template represents the view.
|
||||
|
||||
This page is a comprehensive technical reference to the Angular template language.
|
||||
It explains basic principles of the template language and describes most of the syntax that you'll encounter elsewhere in the documentation.
|
||||
|
||||
Many code snippets illustrate the points and concepts, all of them available
|
||||
in the <live-example title="Template Syntax Live Code"></live-example>.
|
||||
|
||||
|
||||
{@a html}
|
||||
## HTML in templates
|
||||
|
||||
@ -1927,6 +1934,7 @@ The display is blank, but the app keeps rolling without errors.
|
||||
</code-example>
|
||||
|
||||
It works perfectly with long property paths such as `a?.b?.c?.d`.
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
@ -1935,22 +1943,36 @@ It works perfectly with long property paths such as `a?.b?.c?.d`.
|
||||
|
||||
### The non-null assertion operator ( <span class="syntax">!</span> )
|
||||
|
||||
The Angular **non-null assertion operator (`!`)** is a post-fix operator that asserts that the preceeding property path
|
||||
will never be null or undefined.
|
||||
As of Typescript 2.0, you can enforce [strict null checking](http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html "Strict null checking in TypeScript") with the `--strictNullChecks` flag. TypeScript then ensures that no variable is _unintentionally_ null or undefined.
|
||||
|
||||
Unlike the [_safe navigation operator_](guide/template-syntax#safe-navigation-operator "Safe naviation operator (?.)")
|
||||
the **non-null assertion operator** does not guard against a null or undefined; rather, it informs the TypeScript type
|
||||
checker that there is something it might be unaware of that ensures that this property path is defined. This prevents
|
||||
TypeScript from reporting that the path is possibly null or undefined when strict null checking is enabled.
|
||||
In this mode, typed variables disallow null and undefined by default. The type checker throws an error if you leave a variable unassigned or try to assign null or undefined to a variable whose type disallows null and undefined.
|
||||
|
||||
For example, if you use [*ngIf](guide/template-syntax#ngIf) to check if `hero` is defined, you can assert the uses of
|
||||
`hero` are defined in the body of the template.
|
||||
The type checker also throws an error if it can't determine whether a variable will be null or undefined at runtime.
|
||||
You may know that can't happen but the type checker doesn't know.
|
||||
You tell the type checker that it can't happen by applying the post-fix
|
||||
[_non-null assertion operator (!)_]((http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator "Non-null assertion operator").
|
||||
|
||||
The _Angular_ **non-null assertion operator (`!`)** serves the same purpose in an Angular template.
|
||||
|
||||
For example, after you use [*ngIf](guide/template-syntax#ngIf) to check that `hero` is defined, you can assert that
|
||||
`hero` properties are also defined.
|
||||
|
||||
<code-example path="template-syntax/src/app/app.component.html" region="non-null-assertion-1" title="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The Angular **non-null assertion operator (`!`)** is like TypeScript's _non-null assertion operator (!)_
|
||||
introduced in [TypeScript 2.0](http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html).
|
||||
When the Angular compiler turns your template into TypeScript code,
|
||||
it prevents TypeScript from reporting that `hero.name` might be null or undefined.
|
||||
|
||||
Unlike the [_safe navigation operator_](guide/template-syntax#safe-navigation-operator "Safe naviation operator (?.)"),
|
||||
the **non-null assertion operator** does not guard against null or undefined.
|
||||
Rather it tells the TypeScript type checker to suspend strict null checks for a specific property expression.
|
||||
|
||||
You'll need this template operator when you turn on strict null checks. It's optional otherwise.
|
||||
|
||||
|
||||
<a href="#top-of-page">back to top</a>
|
||||
|
||||
<hr/>
|
||||
|
||||
## Summary
|
||||
You've completed this survey of template syntax.
|
||||
|
BIN
aio/content/images/bios/_no-one.jpg
Normal file
After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 13 KiB |
BIN
aio/content/images/bios/andrewseguin.jpg
Executable file
After Width: | Height: | Size: 7.3 KiB |
BIN
aio/content/images/bios/angular-gde-bio-placeholder.jpg
Normal file
After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 8.7 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 7.0 KiB |
BIN
aio/content/images/bios/chrisnoring.jpg
Executable file
After Width: | Height: | Size: 8.1 KiB |
BIN
aio/content/images/bios/christianweyer.jpg
Executable file
After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 12 KiB |
BIN
aio/content/images/bios/cironunes.jpg
Executable file
After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 12 KiB |
BIN
aio/content/images/bios/danwahlin.jpg
Executable file
After Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 8.8 KiB |
BIN
aio/content/images/bios/deborah.jpg
Normal file → Executable file
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 10 KiB |
BIN
aio/content/images/bios/filipbech.jpg
Executable file
After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 8.7 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 9.9 KiB |
BIN
aio/content/images/bios/jecelynyeen.jpg
Executable file
After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.2 KiB |
BIN
aio/content/images/bios/jeffwhelpley.jpg
Normal file → Executable file
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 10 KiB |
BIN
aio/content/images/bios/jeremywilken.jpg
Executable file
After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 12 KiB |
BIN
aio/content/images/bios/joeeames.jpg
Executable file
After Width: | Height: | Size: 9.7 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 7.5 KiB |
BIN
aio/content/images/bios/jorgeucano.jpg
Executable file
After Width: | Height: | Size: 7.3 KiB |
BIN
aio/content/images/bios/josue.jpg
Executable file
After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 7.8 KiB |
BIN
aio/content/images/bios/lukas.jpg
Executable file
After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 9.5 KiB |
BIN
aio/content/images/bios/maximsalnikov.jpg
Executable file
After Width: | Height: | Size: 7.6 KiB |
BIN
aio/content/images/bios/michaelprentice.jpg
Executable file
After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 6.5 KiB |
BIN
aio/content/images/bios/mickey_mouse.jpg
Executable file
After Width: | Height: | Size: 12 KiB |
BIN
aio/content/images/bios/mike-brocchi.jpg
Executable file
After Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 7.4 KiB |
BIN
aio/content/images/bios/minko.jpg
Executable file
After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 11 KiB |
BIN
aio/content/images/bios/ocombe.jpg
Executable file
After Width: | Height: | Size: 9.4 KiB |
BIN
aio/content/images/bios/pascalprecht.jpg
Normal file → Executable file
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 7.1 KiB |
BIN
aio/content/images/bios/raul.jpg
Normal file
After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.3 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 10 KiB |
BIN
aio/content/images/bios/sanderelias.jpg
Executable file
After Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 9.3 KiB |