Skip to content
Permalink
Browse files

feat(common): drop use of the Intl API to improve browser support

BREAKING CHANGE: Because of multiple bugs and browser inconsistencies, we have dropped the intl api in favor of data exported from the Unicode Common Locale Data Repository (CLDR).
Unfortunately we had to change the i18n pipes (date, number, currency, percent) and there are some breaking changes.

## I18n pipes
### Breaking change
By default Angular now only contains locale data for the language `en-US`, if you set the value of `LOCALE_ID` to another locale, you will have to import new locale data for this language because we don't use the intl API anymore.

### Features
- angular doesn't need the intl polyfill anymore
- all i18n pipes now have an additional last parameter `locale` which allows you to use a specific locale instead of the one defined in the token `LOCALE_ID` (whose value is `en-US` by default).
- the new locale data extracted from CLDR are now available to developers as well and can be used through an API (which should be especially useful for library authors).
- you can still use the old pipes for now, but their names have been changed and they are no longer included in the `CommonModule`. To use them, you will have to import the `DeprecatedI18NPipesModule` after the `CommonModule` (the order is important):

```ts
import { NgModule } from '@angular/core';
import { CommonModule, DeprecatedI18NPipesModule } from '@angular/common';
 
@NgModule({
  imports: [
    CommonModule,
    // import deprecated module after
    DeprecatedI18NPipesModule
  ]
})
export class AppModule { }
```

Dont forget that you will still need to import the intl API polyfill if you want to use those deprecated pipes.


## Date pipe
### Breaking changes
- predefined formats `short`, and `shortDate` now use a short year, e.g. `8/15/17` instead of `8/15/2017`.
- the narrow version of eras is now `GGGGG` instead of `G`, the format `G` is now similar to `GG` and `GGG`.
- the narrow version of months is now `MMMMM` instead of `L`, the format `L` is now the short standalone version of months.
- the narrow version of the week day is now `EEEEE` instead of `E`, the format `E` is now similar to `EE` and `EEE`.
- the timezone `z` will now fallback to `O` and output `GMT+1` instead of the complete zone name (e.g. `Pacific Standard Time`), this is because the quantity of data required to have all the zone names in all of the existing locales is too big.
- the timezone `Z` will now output the ISO8601 basic format, e.g. `+0100`, you should now use `ZZZZ` to get `GMT+01:00`.

| Field type | Format        | Example value         | v4 | v5            |
|------------|---------------|-----------------------|----|---------------|
| Eras       | Narrow        | A for AD              | G  | GGGGG         |
| Months     | Narrow        | S for September       | L  | MMMMM         |
| Week day   | Narrow        | M for Monday          | E  | EEEEE         |
| Timezone   | Long location | Pacific Standard Time | z  | Not available |
| Timezone   | Long GMT      | GMT+01:00             | Z  | ZZZZ          |

### Features
- new predefined formats `long`, `full`, `longTime`, `fullTime`.
- the format `yyy` is now supported, e.g. the year `52` will be `052` and the year `2017` will be `2017`.
- standalone months are now supported with the formats `L` to `LLLLL`.
- week of the year is now supported with the formats `w` and `ww`, e.g. weeks `5` and `05`.
- week of the month is now supported with the format `W`, e.g. week `3`.
- fractional seconds are now supported with the format `S` to `SSS`.
- day periods for AM/PM now supports additional formats `aa`, `aaa`, `aaaa` and `aaaaa`. The formats `a` to `aaa` are similar, while `aaaa` is the wide version if available (e.g. `ante meridiem` for `am`), or equivalent to `a` otherwise, and `aaaaa` is the narrow version (e.g. `a` for `am`).
- extra day periods are now supported with the formats `b` to `bbbbb` (and `B` to `BBBBB` for the standalone equivalents), e.g. `morning`, `noon`, `afternoon`, ....
- the short non-localized timezones are now available with the format `O` to `OOOO`. The formats `O` to `OOO` will output `GMT+1` while the format `OOOO` will be `GMT+01:00`.
- the ISO8601 basic time zones are now available with the formats `Z` to `ZZZZZ`. The formats `Z` to `ZZZ` will output `+0100`, while the format `ZZZZ` will be `GMT+01:00` and `ZZZZZ` will be `+01:00`.

### Bug fixes
- the date pipe will now work exactly the same across all browsers, which will fix a lot of bugs for safari and IE.
- eras can now be used on their own without the date, e.g. the format `GG` will be `AD` instead of `8 15, 2017 AD`.


## Currency pipe
### Breaking changes
- the second parameter of the currency pipe (`symbolDisplay`) is no longer a boolean, it now takes the values `code`, `symbol` or `symbol-narrow`.
- the default value for `symbolDisplay` is now `symbol` instead of `code`. This means that by default you will see `$4.99` for `en-US` instead of `USD4.99` previously.

### Features
- you can now choose between `code`, `symbol` or `symbol-narrow` which gives you access to more options for some currencies (e.g. the canadian dollar with the code `CAD` has the symbol `CA$` and the symbol-narrow `$`).


## Percent pipe
### Breaking change
- if you don't specify the number of digits to round to, the local format will be used (and it usually rounds numbers to 0 digits, instead of not rounding previously), e.g. `{{ 3.141592 | percent }}` will output `314%` for the locale `en-US` instead of `314.1592%` previously.

-----

Fixes angular#10809, angular#9524, angular#7008, angular#9324, angular#7590, angular#6724, angular#3429, angular#17576, angular#17478, angular#17319, angular#17200, angular#16838, angular#16624, angular#16625, angular#16591, angular#14131, angular#12632, angular#11376, angular#11187
  • Loading branch information...
ocombe committed Jul 20, 2017
1 parent 382b873 commit 146f2116682c2b3f2619813b7e8944bdd2147853
Showing with 3,100 additions and 729 deletions.
  1. +6 −0 aio/content/examples/i18n/src/app/app.locale_data.ts
  2. +7 −0 aio/content/examples/i18n/src/app/app.locale_data_extra.ts
  3. +1 −1 aio/content/examples/pipes/e2e-spec.ts
  4. +1 −1 aio/content/guide/browser-support.md
  5. +22 −0 aio/content/guide/i18n.md
  6. +0 −18 aio/content/guide/pipes.md
  7. +6 −1 build.sh
  8. +1 −0 packages/common/BUILD.bazel
  9. +20 −0 packages/common/i18n_data/tsconfig-build.json
  10. +7 −2 packages/common/src/common.ts
  11. +12 −2 packages/common/src/common_module.ts
  12. +1 −1 packages/common/src/directives/ng_plural.ts
  13. +602 −0 packages/common/src/i18n/format_date.ts
  14. +377 −0 packages/common/src/i18n/format_number.ts
  15. +22 −0 packages/common/src/i18n/locale_data.ts
  16. +594 −0 packages/common/src/i18n/locale_data_api.ts
  17. +76 −0 packages/common/src/i18n/localization.ts
  18. +0 −401 packages/common/src/localization.ts
  19. +105 −72 packages/common/src/pipes/date_pipe.ts
  20. +145 −0 packages/common/src/pipes/deprecated/date_pipe.ts
  21. +27 −0 packages/common/src/pipes/deprecated/index.ts
  22. +1 −6 packages/common/src/pipes/{ → deprecated}/intl.ts
  23. +164 −0 packages/common/src/pipes/deprecated/number_pipe.ts
  24. +9 −5 packages/common/src/pipes/i18n_plural_pipe.ts
  25. +69 −84 packages/common/src/pipes/number_pipe.ts
  26. +51 −0 packages/common/test/i18n/locale_data_api_spec.ts
  27. +12 −2 packages/common/test/{ → i18n}/localization_spec.ts
  28. +198 −99 packages/common/test/pipes/date_pipe_spec.ts
  29. +208 −0 packages/common/test/pipes/deprecated/date_pipe_spec.ts
  30. +109 −0 packages/common/test/pipes/deprecated/number_pipe_spec.ts
  31. +44 −24 packages/common/test/pipes/number_pipe_spec.ts
  32. +2 −1 packages/examples/common/pipes/ts/date_pipe.ts
  33. +3 −2 packages/examples/common/pipes/ts/number_pipe.ts
  34. +2 −1 packages/tsconfig.json
  35. +18 −0 tools/gulp-tasks/cldr/extract.js
  36. +178 −6 tools/public_api_guard/common/common.d.ts
@@ -0,0 +1,6 @@
// #docregion import-locale
import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/i18n_data/locale_fr';

registerLocaleData(localeFr);
// #enddocregion import-locale
@@ -0,0 +1,7 @@
// #docregion import-locale-extra
import { registerLocaleData } from '@angular/common';
import localeEnGB from '@angular/common/i18n_data/locale_en-GB';
import localeEnGBExtra from '@angular/common/i18n_data/extra/locale_en-GB';

registerLocaleData(localeEnGB, localeEnGBExtra);
// #enddocregion import-locale-extra
@@ -28,7 +28,7 @@ describe('Pipes', function () {

it('should be able to toggle birthday formats', function () {
let birthDayEle = element(by.css('hero-birthday2 > p'));
expect(birthDayEle.getText()).toEqual(`The hero's birthday is 4/15/1988`);
expect(birthDayEle.getText()).toEqual(`The hero's birthday is 4/15/88`);
let buttonEle = element(by.cssContainingText('hero-birthday2 > button', 'Toggle Format'));
expect(buttonEle.isDisplayed()).toBe(true);
buttonEle.click().then(function() {
@@ -347,7 +347,7 @@ Here are the features which may require additional polyfills:

<td>

[Date](api/common/DatePipe), [currency](api/common/CurrencyPipe), [decimal](api/common/DecimalPipe) and [percent](api/common/PercentPipe) pipes
If you use the following deprecated i18n pipes: [date](api/common/DeprecatedDatePipe), [currency](api/common/DeprecatedCurrencyPipe), [decimal](api/common/DeprecatedDecimalPipe) and [percent](api/common/DeprecatedPercentPipe)
</td>

<td>
@@ -40,6 +40,28 @@ You need to build and deploy a separate version of the application for each supp

{@a i18n-attribute}

## i18n pipes

Angular pipes can help you with internationalization: the `DatePipe`, `CurrencyPipe`, `DecimalPipe`
and `PercentPipe` use locale data to format your data based on your `LOCALE_ID`.

By default Angular only contains locale data for the language `en-US`, if you set the value of
`LOCALE_ID` to another locale, you will have to import new locale data for this language:

<code-example path="i18n/src/app/app.locale_data.ts" region="import-locale" title="src/app/app.locale_data.ts" linenums="false">
</code-example>

<div class="l-sub-section">

Note that the files in `@angular/common/i18n_data` contain most of the locale data that you will
need, but some advanced formatting options might only be available in the extra dataset that you can
import from `@angular/common/i18n_data/extra`:

<code-example path="i18n/src/app/app.locale_data_extra.ts" region="import-locale-extra" title="src/app/app.locale_data_extra.ts" linenums="false">
</code-example>

</div>

## Mark text with the _i18n_ attribute

The Angular `i18n` attribute is a marker for translatable content.
@@ -46,24 +46,6 @@ Inside the interpolation expression, you flow the component's `birthday` value t
function on the right. All pipes work this way.


<div class="l-sub-section">



The `Date` and `Currency` pipes need the *ECMAScript Internationalization API*.
Safari and other older browsers don't support it. You can add support with a polyfill.


<code-example language="html">
&lt;script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.en"&gt;&lt;/script&gt;

</code-example>



</div>




## Built-in pipes
@@ -86,7 +86,7 @@ done
#######################################
isIgnoredDirectory() {
name=$(basename ${1})
if [[ -f "${1}" || "${name}" == "src" || "${name}" == "test" || "${name}" == "integrationtest" ]]; then
if [[ -f "${1}" || "${name}" == "src" || "${name}" == "test" || "${name}" == "integrationtest" || "${name}" == "i18n_data" ]]; then
return 0
else
return 1
@@ -470,6 +470,11 @@ do
minify ${BUNDLES_DIR}

) 2>&1 | grep -v "as external dependency"

if [[ ${PACKAGE} == "common" ]]; then
echo "====== Copy i18n locale data"
rsync -a --exclude=*.d.ts --exclude=*.metadata.json ${OUT_DIR}/i18n_data/ ${NPM_DIR}/i18n_data
fi
else
echo "====== Copy ${PACKAGE} node tool"
rsync -a ${OUT_DIR}/ ${NPM_DIR}
@@ -5,6 +5,7 @@ ts_library(
name = "common",
srcs = glob(["**/*.ts"], exclude=[
"http/**",
"i18n/**",
"test/**",
"testing/**",
]),
@@ -0,0 +1,20 @@
{
"compilerOptions": {
"baseUrl": ".",
"declaration": true,
"stripInternal": true,
"experimentalDecorators": true,
"module": "es2015",
"moduleResolution": "node",
"outDir": "../../../dist/packages/common/i18n_data",
"paths": {
"@angular/common": ["../../../dist/packages/common"],
"@angular/core": ["../../../dist/packages/core"]
},
"sourceMap": true,
"inlineSources": true,
"target": "es5",
"skipLibCheck": true,
"lib": ["es2015", "dom"]
}
}
@@ -12,11 +12,16 @@
* Entry point for all public APIs of the common package.
*/
export * from './location/index';
export {NgLocaleLocalization, NgLocalization} from './localization';
export {NgLocaleLocalization, NgLocalization} from './i18n/localization';
export {Plural, LOCALE_DATA} from './i18n/locale_data';
export {findLocaleData, registerLocaleData, NumberFormatStyle, FormStyle, Time, TranslationWidth, FormatWidth, NumberSymbol, WeekDay, getLocaleDayPeriods, getLocaleDayNames, getLocaleMonthNames, getLocaleId, getLocaleEraNames, getLocaleWeekEndRange, getLocaleFirstDayOfWeek, getLocaleDateFormat, getLocaleDateTimeFormat, getLocaleExtraDayPeriodRules, getLocaleExtraDayPeriods, getLocalePluralCase, getLocaleTimeFormat, getLocaleNumberSymbol, getLocaleNumberFormat, getLocaleCurrencyName, getLocaleCurrencySymbol} from './i18n/locale_data_api';
export {AVAILABLE_LOCALES} from './i18n/available_locales';
export {CURRENCIES} from './i18n/currencies';
export {parseCookieValue as ɵparseCookieValue} from './cookie';
export {CommonModule} from './common_module';
export {CommonModule, DeprecatedI18NPipesModule} from './common_module';
export {NgClass, NgFor, NgForOf, NgForOfContext, NgIf, NgIfContext, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet, NgComponentOutlet} from './directives/index';
export {DOCUMENT} from './dom_tokens';
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe, TitleCasePipe} from './pipes/index';
export {DeprecatedDatePipe, DeprecatedCurrencyPipe, DeprecatedDecimalPipe, DeprecatedPercentPipe} from './pipes/deprecated/index';
export {PLATFORM_BROWSER_ID as ɵPLATFORM_BROWSER_ID, PLATFORM_SERVER_ID as ɵPLATFORM_SERVER_ID, PLATFORM_WORKER_APP_ID as ɵPLATFORM_WORKER_APP_ID, PLATFORM_WORKER_UI_ID as ɵPLATFORM_WORKER_UI_ID, isPlatformBrowser, isPlatformServer, isPlatformWorkerApp, isPlatformWorkerUi} from './platform_id';
export {VERSION} from './version';
@@ -8,8 +8,9 @@

import {NgModule} from '@angular/core';

import {COMMON_DEPRECATED_DIRECTIVES, COMMON_DIRECTIVES} from './directives/index';
import {NgLocaleLocalization, NgLocalization} from './localization';
import {COMMON_DIRECTIVES} from './directives/index';
import {NgLocaleLocalization, NgLocalization} from './i18n/localization';
import {COMMON_DEPRECATED_I18N_PIPES} from './pipes/deprecated/index';
import {COMMON_PIPES} from './pipes/index';


@@ -29,3 +30,12 @@ import {COMMON_PIPES} from './pipes/index';
})
export class CommonModule {
}

/**
* A module to contain deprecated i18n pipes.
*
* @deprecated from v5
*/
@NgModule({declarations: [COMMON_DEPRECATED_I18N_PIPES], exports: [COMMON_DEPRECATED_I18N_PIPES]})
export class DeprecatedI18NPipesModule {
}
@@ -8,7 +8,7 @@

import {Attribute, Directive, Host, Input, TemplateRef, ViewContainerRef} from '@angular/core';

import {NgLocalization, getPluralCategory} from '../localization';
import {NgLocalization, getPluralCategory} from '../i18n/localization';

import {SwitchView} from './ng_switch';

Oops, something went wrong.

0 comments on commit 146f211

Please sign in to comment.
You can’t perform that action at this time.