refactor(common): refactor i18n code to ease tree shaking (#18907)

PR Close #18907
This commit is contained in:
Victor Berchet 2017-08-23 17:10:27 -07:00 committed by Miško Hevery
parent 4c45347635
commit 832876d0a1
9 changed files with 85 additions and 98 deletions

View File

@ -13,8 +13,8 @@
*/ */
export * from './location/index'; export * from './location/index';
export {NgLocaleLocalization, NgLocalization} from './i18n/localization'; export {NgLocaleLocalization, NgLocalization} from './i18n/localization';
export {Plural, LOCALE_DATA} from './i18n/locale_data'; export {registerLocaleData} 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 {Plural, 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 {CURRENCIES} from './i18n/currencies'; export {CURRENCIES} from './i18n/currencies';
export {parseCookieValue as ɵparseCookieValue} from './cookie'; export {parseCookieValue as ɵparseCookieValue} from './cookie';
export {CommonModule, DeprecatedI18NPipesModule} from './common_module'; export {CommonModule, DeprecatedI18NPipesModule} from './common_module';

View File

@ -6,17 +6,55 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
/** @experimental */
export enum Plural {
Zero,
One,
Two,
Few,
Many,
Other,
}
/** /**
* @experimental i18n support is experimental. * @experimental i18n support is experimental.
*/ */
export const LOCALE_DATA: {[localeId: string]: any} = {}; export const LOCALE_DATA: {[localeId: string]: any} = {};
/**
* Register global data to be used internally by Angular. See the
* {@linkDocs guide/i18n#i18n-pipes "I18n guide"} to know how to import additional locale data.
*
* @experimental i18n support is experimental.
*/
export function registerLocaleData(data: any, extraData?: any) {
const localeId = data[LocaleDataIndex.LocaleId].toLowerCase();
LOCALE_DATA[localeId] = data;
if (extraData) {
LOCALE_DATA[localeId][LocaleDataIndex.ExtraData] = extraData;
}
}
/**
* Index of each type of locale data from the locale data array
*/
export const enum LocaleDataIndex {
LocaleId = 0,
DayPeriodsFormat,
DayPeriodsStandalone,
DaysFormat,
DaysStandalone,
MonthsFormat,
MonthsStandalone,
Eras,
FirstDayOfWeek,
WeekendRange,
DateFormat,
TimeFormat,
DateTimeFormat,
NumberSymbols,
NumberFormats,
CurrencySymbol,
CurrencyName,
PluralCase,
ExtraData
}
/**
* Index of each type of locale data from the extra locale data array
*/
export const enum ExtraLocaleDataIndex {
ExtraDayPeriodFormats = 0,
ExtraDayPeriodStandalone,
ExtraDayPeriodsRules
}

View File

@ -8,7 +8,7 @@
import {CURRENCIES} from './currencies'; import {CURRENCIES} from './currencies';
import localeEn from './locale_en'; import localeEn from './locale_en';
import {LOCALE_DATA, Plural} from './locale_data'; import {LOCALE_DATA, LocaleDataIndex, ExtraLocaleDataIndex} from './locale_data';
/** /**
* The different format styles that can be used to represent numbers. * The different format styles that can be used to represent numbers.
@ -23,6 +23,16 @@ export enum NumberFormatStyle {
Scientific Scientific
} }
/** @experimental */
export enum Plural {
Zero = 0,
One = 1,
Two = 2,
Few = 3,
Many = 4,
Other = 5,
}
/** /**
* Some languages use two different forms of strings (standalone and format) depending on the * Some languages use two different forms of strings (standalone and format) depending on the
* context. * context.
@ -130,40 +140,6 @@ export enum WeekDay {
Saturday Saturday
} }
/**
* Use this enum to find the index of each type of locale data from the locale data array
*/
enum LocaleDataIndex {
LocaleId = 0,
DayPeriodsFormat,
DayPeriodsStandalone,
DaysFormat,
DaysStandalone,
MonthsFormat,
MonthsStandalone,
Eras,
FirstDayOfWeek,
WeekendRange,
DateFormat,
TimeFormat,
DateTimeFormat,
NumberSymbols,
NumberFormats,
CurrencySymbol,
CurrencyName,
PluralCase,
ExtraData
}
/**
* Use this enum to find the index of each type of locale data from the extra locale data array
*/
enum ExtraLocaleDataIndex {
ExtraDayPeriodFormats = 0,
ExtraDayPeriodStandalone,
ExtraDayPeriodsRules
}
/** /**
* The locale id for the chosen locale (e.g `en-GB`). * The locale id for the chosen locale (e.g `en-GB`).
* *
@ -537,6 +513,7 @@ export function findLocaleData(locale: string): any {
// let's try to find a parent locale // let's try to find a parent locale
const parentLocale = normalizedLocale.split('-')[0]; const parentLocale = normalizedLocale.split('-')[0];
match = LOCALE_DATA[parentLocale]; match = LOCALE_DATA[parentLocale];
if (match) { if (match) {
return match; return match;
} }
@ -545,8 +522,7 @@ export function findLocaleData(locale: string): any {
return localeEn; return localeEn;
} }
throw new Error( throw new Error(`Missing locale data for the locale "${locale}".`);
`Missing locale data for the locale "${locale}". Use "registerLocaleData" to load new data. See the "I18n guide" on angular.io to know more.`);
} }
/** /**
@ -560,17 +536,3 @@ export function findCurrencySymbol(code: string, format: 'wide' | 'narrow') {
const symbol = currency[0] || code; const symbol = currency[0] || code;
return format === 'wide' ? symbol : currency[1] || symbol; return format === 'wide' ? symbol : currency[1] || symbol;
} }
/**
* Register global data to be used internally by Angular. See the
* {@linkDocs guide/i18n#i18n-pipes "I18n guide"} to know how to import additional locale data.
*
* @experimental i18n support is experimental.
*/
export function registerLocaleData(data: any, extraData?: any) {
const localeId = data[LocaleDataIndex.LocaleId].toLowerCase();
LOCALE_DATA[localeId] = data;
if (extraData) {
LOCALE_DATA[localeId][LocaleDataIndex.ExtraData] = extraData;
}
}

View File

@ -7,8 +7,7 @@
*/ */
import {Inject, Injectable, LOCALE_ID} from '@angular/core'; import {Inject, Injectable, LOCALE_ID} from '@angular/core';
import {Plural} from './locale_data'; import {Plural, getLocalePluralCase} from './locale_data_api';
import {getLocalePluralCase} from './locale_data_api';
/** /**
* @experimental * @experimental

View File

@ -10,7 +10,8 @@ import localeCaESVALENCIA from '../../i18n_data/locale_ca-ES-VALENCIA';
import localeEn from '../../i18n_data/locale_en'; import localeEn from '../../i18n_data/locale_en';
import localeFr from '../../i18n_data/locale_fr'; import localeFr from '../../i18n_data/locale_fr';
import localeFrCA from '../../i18n_data/locale_fr-CA'; import localeFrCA from '../../i18n_data/locale_fr-CA';
import {registerLocaleData, findLocaleData} from '../../src/i18n/locale_data_api'; import {findLocaleData} from '../../src/i18n/locale_data_api';
import {registerLocaleData} from '../../src/i18n/locale_data';
export function main() { export function main() {
describe('locale data api', () => { describe('locale data api', () => {

View File

@ -13,7 +13,7 @@ import localeFr from '../../i18n_data/locale_fr';
import {LOCALE_ID} from '@angular/core'; import {LOCALE_ID} from '@angular/core';
import {TestBed, inject} from '@angular/core/testing'; import {TestBed, inject} from '@angular/core/testing';
import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../../src/i18n/localization'; import {NgLocaleLocalization, NgLocalization, getPluralCategory} from '../../src/i18n/localization';
import {registerLocaleData} from '../../src/i18n/locale_data_api'; import {registerLocaleData} from '../../src/i18n/locale_data';
export function main() { export function main() {
describe('l10n', () => { describe('l10n', () => {

View File

@ -56,7 +56,7 @@ export function main() {
}); });
describe('transform with custom locales', () => { describe('transform with custom locales', () => {
it('should return the correct format for es-US in IE11', () => { it('should return the correct format for es-US', () => {
const pipe = new DecimalPipe('es-US'); const pipe = new DecimalPipe('es-US');
expect(pipe.transform('9999999.99', '1.2-2')).toEqual('9,999,999.99'); expect(pipe.transform('9999999.99', '1.2-2')).toEqual('9,999,999.99');
}); });

View File

@ -58,7 +58,7 @@ module.exports = (gulp, done) => {
console.log(`${index + 1}/${LOCALES.length}`); console.log(`${index + 1}/${LOCALES.length}`);
console.log(`\t${I18N_DATA_FOLDER}/locale_${locale}.ts`); console.log(`\t${I18N_DATA_FOLDER}/locale_${locale}.ts`);
fs.writeFileSync(`${RELATIVE_I18N_DATA_FOLDER}/locale_${locale}.ts`, generateLocale(locale, localeData, '@angular/common')); fs.writeFileSync(`${RELATIVE_I18N_DATA_FOLDER}/locale_${locale}.ts`, generateLocale(locale, localeData));
console.log(`\t${I18N_DATA_EXTRA_FOLDER}/locale_${locale}.ts`); console.log(`\t${I18N_DATA_EXTRA_FOLDER}/locale_${locale}.ts`);
fs.writeFileSync(`${RELATIVE_I18N_DATA_EXTRA_FOLDER}/locale_${locale}.ts`, generateLocaleExtra(locale, localeData)); fs.writeFileSync(`${RELATIVE_I18N_DATA_EXTRA_FOLDER}/locale_${locale}.ts`, generateLocaleExtra(locale, localeData));
}); });
@ -66,7 +66,7 @@ module.exports = (gulp, done) => {
// additional "en" file that will be included in common // additional "en" file that will be included in common
console.log(`Writing file ${I18N_FOLDER}/locale_en.ts`); console.log(`Writing file ${I18N_FOLDER}/locale_en.ts`);
fs.writeFileSync(`${RELATIVE_I18N_FOLDER}/locale_en.ts`, generateLocale('en', new cldrJs('en'), './locale_data')); fs.writeFileSync(`${RELATIVE_I18N_FOLDER}/locale_en.ts`, generateLocale('en', new cldrJs('en')));
console.log(`Writing file ${I18N_FOLDER}/currencies.ts`); console.log(`Writing file ${I18N_FOLDER}/currencies.ts`);
fs.writeFileSync(`${RELATIVE_I18N_FOLDER}/currencies.ts`, generateCurrencies()); fs.writeFileSync(`${RELATIVE_I18N_FOLDER}/currencies.ts`, generateCurrencies());
@ -87,7 +87,7 @@ module.exports = (gulp, done) => {
/** /**
* Generate file that contains basic locale data * Generate file that contains basic locale data
*/ */
function generateLocale(locale, localeData, ngLocalePath) { function generateLocale(locale, localeData) {
// [ localeId, dateTime, number, currency, pluralCase ] // [ localeId, dateTime, number, currency, pluralCase ]
let data = stringify([ let data = stringify([
locale, locale,
@ -103,8 +103,6 @@ function generateLocale(locale, localeData, ngLocalePath) {
data = data.substring(0, data.lastIndexOf(']')) + `, ${getPluralFunction(locale)}]`; data = data.substring(0, data.lastIndexOf(']')) + `, ${getPluralFunction(locale)}]`;
return `${HEADER} return `${HEADER}
import {Plural} from '${ngLocalePath}';
export default ${data}; export default ${data};
`; `;
} }
@ -450,24 +448,28 @@ function toRegExp(s) {
*/ */
function getPluralFunction(locale) { function getPluralFunction(locale) {
let fn = cldr.extractPluralRuleFunction(locale).toString(); let fn = cldr.extractPluralRuleFunction(locale).toString();
if (fn === EMPTY_RULE) { if (fn === EMPTY_RULE) {
fn = DEFAULT_RULE; fn = DEFAULT_RULE;
} }
return fn fn = fn
.replace( .replace(
toRegExp('function anonymous(n\n/**/) {\n'), toRegExp('function anonymous(n\n/**/) {\n'),
'function(n: number): Plural {\n ') 'function(n: number): number {\n ')
.replace(toRegExp('var'), 'let') .replace(toRegExp('var'), 'let')
.replace(toRegExp('if(typeof n==="string")n=parseInt(n,10);'), '') .replace(toRegExp('if(typeof n==="string")n=parseInt(n,10);'), '')
.replace(toRegExp('"zero"'), ' Plural.Zero')
.replace(toRegExp('"one"'), ' Plural.One')
.replace(toRegExp('"two"'), ' Plural.Two')
.replace(toRegExp('"few"'), ' Plural.Few')
.replace(toRegExp('"many"'), ' Plural.Many')
.replace(toRegExp('"other"'), ' Plural.Other')
.replace(toRegExp('\n}'), ';\n}'); .replace(toRegExp('\n}'), ';\n}');
return normalizePluralRule();
// The replacement values must match the `Plural` enum from common.
// We do not use the enum directly to avoid depending on that package.
return fn
.replace(toRegExp('"zero"'), ' 0')
.replace(toRegExp('"one"'), ' 1')
.replace(toRegExp('"two"'), ' 2')
.replace(toRegExp('"few"'), ' 3')
.replace(toRegExp('"many"'), ' 4')
.replace(toRegExp('"other"'), ' 5');
} }
/** /**
@ -484,13 +486,6 @@ function stringify(obj) {
return util.inspect(obj, {depth: null, maxArrayLength: null}) return util.inspect(obj, {depth: null, maxArrayLength: null})
} }
/**
* Transform a string to camelCase
*/
function toCamelCase(str) {
return str.replace(/-+([a-z0-9A-Z])/g, (...m) => m[1].toUpperCase());
}
/** /**
* To create smaller locale files, we remove duplicated data. * To create smaller locale files, we remove duplicated data.
* To be make this work we need to store similar data in arrays, if some value in an array * To be make this work we need to store similar data in arrays, if some value in an array

View File

@ -69,9 +69,6 @@ export declare class DeprecatedPercentPipe implements PipeTransform {
/** @stable */ /** @stable */
export declare const DOCUMENT: InjectionToken<Document>; export declare const DOCUMENT: InjectionToken<Document>;
/** @experimental */
export declare function findLocaleData(locale: string): any;
/** @experimental */ /** @experimental */
export declare enum FormatWidth { export declare enum FormatWidth {
Short = 0, Short = 0,
@ -182,11 +179,6 @@ export declare class JsonPipe implements PipeTransform {
transform(value: any): string; transform(value: any): string;
} }
/** @experimental */
export declare const LOCALE_DATA: {
[localeId: string]: any;
};
/** @stable */ /** @stable */
export declare class Location { export declare class Location {
constructor(platformStrategy: LocationStrategy); constructor(platformStrategy: LocationStrategy);