2017-07-20 04:54:07 -04:00
|
|
|
/**
|
|
|
|
* @license
|
2020-05-19 15:08:49 -04:00
|
|
|
* Copyright Google LLC All Rights Reserved.
|
2017-07-20 04:54:07 -04:00
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
|
|
|
const fs = require('fs');
|
|
|
|
const path = require('path');
|
2018-01-18 08:04:41 -05:00
|
|
|
const stringify = require('./util').stringify;
|
2017-07-20 04:54:07 -04:00
|
|
|
// used to extract plural rules
|
|
|
|
const cldr = require('cldr');
|
|
|
|
// used to extract all other cldr data
|
|
|
|
const cldrJs = require('cldrjs');
|
2019-11-08 17:14:27 -05:00
|
|
|
// used to call to clang-format
|
|
|
|
const shelljs = require('shelljs');
|
2017-07-20 04:54:07 -04:00
|
|
|
|
2019-05-17 10:13:31 -04:00
|
|
|
const COMMON_PACKAGE = 'packages/common';
|
|
|
|
const CORE_PACKAGE = 'packages/core';
|
|
|
|
const I18N_FOLDER = `${COMMON_PACKAGE}/src/i18n`;
|
|
|
|
const I18N_CORE_FOLDER = `${CORE_PACKAGE}/src/i18n`;
|
|
|
|
const I18N_DATA_FOLDER = `${COMMON_PACKAGE}/locales`;
|
2017-07-20 04:54:07 -04:00
|
|
|
const I18N_DATA_EXTRA_FOLDER = `${I18N_DATA_FOLDER}/extra`;
|
2019-10-31 13:29:41 -04:00
|
|
|
const I18N_GLOBAL_FOLDER = `${I18N_DATA_FOLDER}/global`;
|
2017-07-20 04:54:07 -04:00
|
|
|
const RELATIVE_I18N_FOLDER = path.resolve(__dirname, `../../../${I18N_FOLDER}`);
|
2019-05-17 10:13:31 -04:00
|
|
|
const RELATIVE_I18N_CORE_FOLDER = path.resolve(__dirname, `../../../${I18N_CORE_FOLDER}`);
|
2017-07-20 04:54:07 -04:00
|
|
|
const RELATIVE_I18N_DATA_FOLDER = path.resolve(__dirname, `../../../${I18N_DATA_FOLDER}`);
|
2019-11-02 11:33:23 -04:00
|
|
|
const RELATIVE_I18N_DATA_EXTRA_FOLDER =
|
|
|
|
path.resolve(__dirname, `../../../${I18N_DATA_EXTRA_FOLDER}`);
|
2019-10-31 13:29:41 -04:00
|
|
|
const RELATIVE_I18N_GLOBAL_FOLDER = path.resolve(__dirname, `../../../${I18N_GLOBAL_FOLDER}`);
|
|
|
|
const DEFAULT_RULE = 'function anonymous(n) {\nreturn"other"\n}';
|
|
|
|
const EMPTY_RULE = 'function anonymous(n) {\n\n}';
|
2017-07-20 04:54:07 -04:00
|
|
|
const WEEK_DAYS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
|
|
|
|
const HEADER = `/**
|
|
|
|
* @license
|
2020-05-19 15:08:49 -04:00
|
|
|
* Copyright Google LLC All Rights Reserved.
|
2017-07-20 04:54:07 -04:00
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
|
|
|
// THIS CODE IS GENERATED - DO NOT MODIFY
|
|
|
|
// See angular/tools/gulp-tasks/cldr/extract.js
|
|
|
|
`;
|
|
|
|
|
2017-09-22 13:51:03 -04:00
|
|
|
// tslint:disable:no-console
|
2017-07-20 04:54:07 -04:00
|
|
|
module.exports = (gulp, done) => {
|
2019-11-06 14:23:11 -05:00
|
|
|
const cldrData = require('cldr-data');
|
2017-07-20 04:54:07 -04:00
|
|
|
const LOCALES = cldrData.availableLocales;
|
|
|
|
|
|
|
|
console.log(`Loading CLDR data...`);
|
2019-11-22 08:07:30 -05:00
|
|
|
cldrJs.load(cldrData.all().concat(cldrData('scriptMetadata')));
|
2017-07-20 04:54:07 -04:00
|
|
|
|
|
|
|
console.log(`Writing locale files`);
|
|
|
|
if (!fs.existsSync(RELATIVE_I18N_FOLDER)) {
|
|
|
|
fs.mkdirSync(RELATIVE_I18N_FOLDER);
|
|
|
|
}
|
|
|
|
if (!fs.existsSync(RELATIVE_I18N_DATA_FOLDER)) {
|
|
|
|
fs.mkdirSync(RELATIVE_I18N_DATA_FOLDER);
|
|
|
|
}
|
|
|
|
if (!fs.existsSync(RELATIVE_I18N_DATA_EXTRA_FOLDER)) {
|
|
|
|
fs.mkdirSync(RELATIVE_I18N_DATA_EXTRA_FOLDER);
|
|
|
|
}
|
2019-10-31 13:29:41 -04:00
|
|
|
if (!fs.existsSync(RELATIVE_I18N_GLOBAL_FOLDER)) {
|
|
|
|
fs.mkdirSync(RELATIVE_I18N_GLOBAL_FOLDER);
|
|
|
|
}
|
2018-01-26 05:06:13 -05:00
|
|
|
|
|
|
|
console.log(`Writing file ${I18N_FOLDER}/currencies.ts`);
|
|
|
|
fs.writeFileSync(`${RELATIVE_I18N_FOLDER}/currencies.ts`, generateCurrenciesFile());
|
|
|
|
|
|
|
|
const baseCurrencies = generateBaseCurrencies(new cldrJs('en'));
|
|
|
|
// additional "en" file that will be included in common
|
2019-05-17 10:13:31 -04:00
|
|
|
console.log(`Writing file ${I18N_CORE_FOLDER}/locale_en.ts`);
|
2018-01-26 05:06:13 -05:00
|
|
|
const localeEnFile = generateLocale('en', new cldrJs('en'), baseCurrencies);
|
2019-05-17 10:13:31 -04:00
|
|
|
fs.writeFileSync(`${RELATIVE_I18N_CORE_FOLDER}/locale_en.ts`, localeEnFile);
|
2018-01-26 05:06:13 -05:00
|
|
|
|
2017-07-20 04:54:07 -04:00
|
|
|
LOCALES.forEach((locale, index) => {
|
|
|
|
const localeData = new cldrJs(locale);
|
|
|
|
|
|
|
|
console.log(`${index + 1}/${LOCALES.length}`);
|
2017-08-28 14:35:35 -04:00
|
|
|
console.log(`\t${I18N_DATA_FOLDER}/${locale}.ts`);
|
2019-11-02 11:33:23 -04:00
|
|
|
fs.writeFileSync(
|
|
|
|
`${RELATIVE_I18N_DATA_FOLDER}/${locale}.ts`,
|
|
|
|
locale === 'en' ? localeEnFile : generateLocale(locale, localeData, baseCurrencies));
|
2017-08-28 14:35:35 -04:00
|
|
|
console.log(`\t${I18N_DATA_EXTRA_FOLDER}/${locale}.ts`);
|
2019-11-02 11:33:23 -04:00
|
|
|
fs.writeFileSync(
|
|
|
|
`${RELATIVE_I18N_DATA_EXTRA_FOLDER}/${locale}.ts`, generateLocaleExtra(locale, localeData));
|
2019-11-07 14:31:14 -05:00
|
|
|
console.log(`\t${I18N_GLOBAL_FOLDER}/${locale}.js`);
|
2019-10-31 13:29:41 -04:00
|
|
|
fs.writeFileSync(
|
|
|
|
`${RELATIVE_I18N_GLOBAL_FOLDER}/${locale}.js`,
|
|
|
|
generateGlobalLocale(
|
|
|
|
locale, locale === 'en' ? new cldrJs('en') : localeData, baseCurrencies));
|
|
|
|
|
2017-07-20 04:54:07 -04:00
|
|
|
});
|
|
|
|
console.log(`${LOCALES.length} locale files generated.`);
|
|
|
|
|
|
|
|
console.log(`All i18n cldr files have been generated, formatting files..."`);
|
2019-11-08 17:14:27 -05:00
|
|
|
shelljs.exec(
|
2019-12-14 05:39:41 -05:00
|
|
|
`yarn clang-format -i ${I18N_DATA_FOLDER}/**/*.ts ${I18N_DATA_FOLDER}/*.ts ${I18N_FOLDER}/currencies.ts ${I18N_CORE_FOLDER}/locale_en.ts ${I18N_GLOBAL_FOLDER}/*.js`,
|
|
|
|
{silent: true});
|
2019-11-08 17:14:27 -05:00
|
|
|
done();
|
2017-07-20 04:54:07 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2019-10-31 13:29:41 -04:00
|
|
|
* Generate contents for the basic locale data file
|
2017-07-20 04:54:07 -04:00
|
|
|
*/
|
2018-01-26 05:06:13 -05:00
|
|
|
function generateLocale(locale, localeData, baseCurrencies) {
|
2019-10-31 13:29:41 -04:00
|
|
|
return `${HEADER}
|
|
|
|
const u = undefined;
|
|
|
|
|
|
|
|
${getPluralFunction(locale)}
|
|
|
|
|
|
|
|
export default ${generateBasicLocaleString(locale, localeData, baseCurrencies)};
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate the contents for the extra data file
|
|
|
|
*/
|
|
|
|
function generateLocaleExtra(locale, localeData) {
|
|
|
|
return `${HEADER}
|
|
|
|
const u = undefined;
|
|
|
|
|
|
|
|
export default ${generateDayPeriodsSupplementalString(locale, localeData)};
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generated the contents for the global locale file
|
|
|
|
*/
|
|
|
|
function generateGlobalLocale(locale, localeData, baseCurrencies) {
|
|
|
|
const basicLocaleData = generateBasicLocaleString(locale, localeData, baseCurrencies);
|
|
|
|
const extraLocaleData = generateDayPeriodsSupplementalString(locale, localeData);
|
|
|
|
const data = basicLocaleData.replace(/\]$/, `, ${extraLocaleData}]`);
|
|
|
|
return `${HEADER}
|
|
|
|
(function(global) {
|
|
|
|
global.ng = global.ng || {};
|
|
|
|
global.ng.common = global.ng.common || {};
|
|
|
|
global.ng.common.locales = global.ng.common.locales || {};
|
2020-05-12 14:10:01 -04:00
|
|
|
const u = undefined;
|
2019-10-31 13:29:41 -04:00
|
|
|
${getPluralFunction(locale, false)}
|
2019-11-07 14:31:14 -05:00
|
|
|
global.ng.common.locales['${normalizeLocale(locale)}'] = ${data};
|
2019-10-31 13:29:41 -04:00
|
|
|
})(typeof globalThis !== 'undefined' && globalThis || typeof global !== 'undefined' && global || typeof window !== 'undefined' && window);
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Collect up the basic locale data [ localeId, dateTime, number, currency, pluralCase ].
|
|
|
|
*/
|
|
|
|
function generateBasicLocaleString(locale, localeData, baseCurrencies) {
|
2019-12-14 05:39:41 -05:00
|
|
|
let data = stringify(
|
|
|
|
[
|
|
|
|
locale,
|
|
|
|
...getDateTimeTranslations(localeData),
|
|
|
|
...getDateTimeSettings(localeData),
|
|
|
|
...getNumberSettings(localeData),
|
|
|
|
...getCurrencySettings(locale, localeData),
|
|
|
|
generateLocaleCurrencies(localeData, baseCurrencies),
|
|
|
|
getDirectionality(localeData),
|
|
|
|
],
|
|
|
|
true)
|
|
|
|
// We remove "undefined" added by spreading arrays when there is no value
|
|
|
|
.replace(/undefined/g, 'u');
|
2017-07-20 04:54:07 -04:00
|
|
|
|
|
|
|
// adding plural function after, because we don't want it as a string
|
2019-10-31 13:29:41 -04:00
|
|
|
data = data.replace(/\]$/, ', plural]');
|
|
|
|
return data;
|
2017-07-20 04:54:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-10-31 13:29:41 -04:00
|
|
|
* Collect up the day period rules, and extended day period data.
|
2017-07-20 04:54:07 -04:00
|
|
|
*/
|
2019-10-31 13:29:41 -04:00
|
|
|
function generateDayPeriodsSupplementalString(locale, localeData) {
|
2017-07-20 04:54:07 -04:00
|
|
|
const dayPeriods = getDayPeriodsNoAmPm(localeData);
|
|
|
|
const dayPeriodRules = getDayPeriodRules(localeData);
|
|
|
|
|
|
|
|
let dayPeriodsSupplemental = [];
|
|
|
|
if (Object.keys(dayPeriods.format.narrow).length) {
|
|
|
|
const keys = Object.keys(dayPeriods.format.narrow);
|
|
|
|
|
|
|
|
if (keys.length !== Object.keys(dayPeriodRules).length) {
|
|
|
|
throw new Error(`Error: locale ${locale} has not the correct number of day period rules`);
|
|
|
|
}
|
|
|
|
|
|
|
|
const dayPeriodsFormat = removeDuplicates([
|
2019-11-02 11:33:23 -04:00
|
|
|
objectValues(dayPeriods.format.narrow), objectValues(dayPeriods.format.abbreviated),
|
2017-07-20 04:54:07 -04:00
|
|
|
objectValues(dayPeriods.format.wide)
|
|
|
|
]);
|
|
|
|
|
|
|
|
const dayPeriodsStandalone = removeDuplicates([
|
|
|
|
objectValues(dayPeriods['stand-alone'].narrow),
|
|
|
|
objectValues(dayPeriods['stand-alone'].abbreviated),
|
|
|
|
objectValues(dayPeriods['stand-alone'].wide)
|
|
|
|
]);
|
|
|
|
|
|
|
|
const rules = keys.map(key => dayPeriodRules[key]);
|
|
|
|
dayPeriodsSupplemental = [...removeDuplicates([dayPeriodsFormat, dayPeriodsStandalone]), rules];
|
|
|
|
}
|
2019-10-31 13:29:41 -04:00
|
|
|
return stringify(dayPeriodsSupplemental).replace(/undefined/g, 'u');
|
2017-07-20 04:54:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-01-26 05:06:13 -05:00
|
|
|
* Generate a list of currencies to be used as a based for other currencies
|
|
|
|
* e.g.: {'ARS': [, '$'], 'AUD': ['A$', '$'], ...}
|
2017-07-20 04:54:07 -04:00
|
|
|
*/
|
2018-01-26 05:06:13 -05:00
|
|
|
function generateBaseCurrencies(localeData, addDigits) {
|
|
|
|
const currenciesData = localeData.main('numbers/currencies');
|
2018-01-29 16:40:07 -05:00
|
|
|
const fractions = new cldrJs('en').get(`supplemental/currencyData/fractions`);
|
2018-01-26 05:06:13 -05:00
|
|
|
const currencies = {};
|
2017-07-20 04:54:07 -04:00
|
|
|
Object.keys(currenciesData).forEach(key => {
|
|
|
|
let symbolsArray = [];
|
|
|
|
const symbol = currenciesData[key].symbol;
|
|
|
|
const symbolNarrow = currenciesData[key]['symbol-alt-narrow'];
|
|
|
|
if (symbol && symbol !== key) {
|
|
|
|
symbolsArray.push(symbol);
|
|
|
|
}
|
|
|
|
if (symbolNarrow && symbolNarrow !== symbol) {
|
|
|
|
if (symbolsArray.length > 0) {
|
|
|
|
symbolsArray.push(symbolNarrow);
|
|
|
|
} else {
|
2018-04-09 08:15:00 -04:00
|
|
|
symbolsArray = [undefined, symbolNarrow];
|
2017-07-20 04:54:07 -04:00
|
|
|
}
|
|
|
|
}
|
2018-01-29 16:40:07 -05:00
|
|
|
if (addDigits && fractions[key] && fractions[key]['_digits']) {
|
|
|
|
const digits = parseInt(fractions[key]['_digits'], 10);
|
|
|
|
if (symbolsArray.length === 2) {
|
|
|
|
symbolsArray.push(digits);
|
|
|
|
} else if (symbolsArray.length === 1) {
|
2018-04-09 08:15:00 -04:00
|
|
|
symbolsArray = [...symbolsArray, undefined, digits];
|
2018-01-29 16:40:07 -05:00
|
|
|
} else {
|
2018-04-09 08:15:00 -04:00
|
|
|
symbolsArray = [undefined, undefined, digits];
|
2018-01-29 16:40:07 -05:00
|
|
|
}
|
|
|
|
}
|
2017-07-20 04:54:07 -04:00
|
|
|
if (symbolsArray.length > 0) {
|
2018-01-26 05:06:13 -05:00
|
|
|
currencies[key] = symbolsArray;
|
2017-07-20 04:54:07 -04:00
|
|
|
}
|
|
|
|
});
|
2018-01-26 05:06:13 -05:00
|
|
|
return currencies;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* To minimize the file even more, we only output the differences compared to the base currency
|
|
|
|
*/
|
|
|
|
function generateLocaleCurrencies(localeData, baseCurrencies) {
|
|
|
|
const currenciesData = localeData.main('numbers/currencies');
|
|
|
|
const currencies = {};
|
|
|
|
Object.keys(currenciesData).forEach(code => {
|
|
|
|
let symbolsArray = [];
|
|
|
|
const symbol = currenciesData[code].symbol;
|
|
|
|
const symbolNarrow = currenciesData[code]['symbol-alt-narrow'];
|
|
|
|
if (symbol && symbol !== code) {
|
|
|
|
symbolsArray.push(symbol);
|
|
|
|
}
|
|
|
|
if (symbolNarrow && symbolNarrow !== symbol) {
|
|
|
|
if (symbolsArray.length > 0) {
|
|
|
|
symbolsArray.push(symbolNarrow);
|
|
|
|
} else {
|
2018-04-09 08:15:00 -04:00
|
|
|
symbolsArray = [undefined, symbolNarrow];
|
2018-01-26 05:06:13 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if locale data are different, set the value
|
|
|
|
if ((baseCurrencies[code] || []).toString() !== symbolsArray.toString()) {
|
|
|
|
currencies[code] = symbolsArray;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return currencies;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate a file that contains the list of currencies and their symbols
|
|
|
|
*/
|
|
|
|
function generateCurrenciesFile() {
|
|
|
|
const baseCurrencies = generateBaseCurrencies(new cldrJs('en'), true);
|
2017-07-20 04:54:07 -04:00
|
|
|
|
|
|
|
return `${HEADER}
|
2018-01-26 05:06:13 -05:00
|
|
|
export type CurrenciesSymbols = [string] | [string | undefined, string];
|
|
|
|
|
2018-01-18 08:04:41 -05:00
|
|
|
/** @internal */
|
2018-01-29 16:40:07 -05:00
|
|
|
export const CURRENCIES_EN: {[code: string]: CurrenciesSymbols | [string | undefined, string | undefined, number]} = ${stringify(baseCurrencies, true)};
|
2017-07-20 04:54:07 -04:00
|
|
|
`;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns data for the chosen day periods
|
2019-11-02 11:33:23 -04:00
|
|
|
* @returns {
|
|
|
|
* format: {narrow / abbreviated / wide: [...]},
|
|
|
|
* stand-alone: {narrow / abbreviated / wide: [...]}
|
|
|
|
* }
|
2017-07-20 04:54:07 -04:00
|
|
|
*/
|
|
|
|
function getDayPeriods(localeData, dayPeriodsList) {
|
|
|
|
const dayPeriods = localeData.main(`dates/calendars/gregorian/dayPeriods`);
|
|
|
|
const result = {};
|
|
|
|
// cleaning up unused keys
|
2019-11-02 11:33:23 -04:00
|
|
|
Object.keys(dayPeriods).forEach(key1 => { // format / stand-alone
|
2017-07-20 04:54:07 -04:00
|
|
|
result[key1] = {};
|
|
|
|
Object.keys(dayPeriods[key1]).forEach(key2 => { // narrow / abbreviated / wide
|
|
|
|
result[key1][key2] = {};
|
|
|
|
Object.keys(dayPeriods[key1][key2]).forEach(key3 => {
|
|
|
|
if (dayPeriodsList.indexOf(key3) !== -1) {
|
|
|
|
result[key1][key2][key3] = dayPeriods[key1][key2][key3];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the basic day periods (am/pm)
|
|
|
|
*/
|
|
|
|
function getDayPeriodsAmPm(localeData) {
|
|
|
|
return getDayPeriods(localeData, ['am', 'pm']);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the extra day periods (without am/pm)
|
|
|
|
*/
|
|
|
|
function getDayPeriodsNoAmPm(localeData) {
|
2019-11-02 11:33:23 -04:00
|
|
|
return getDayPeriods(localeData, [
|
|
|
|
'noon', 'midnight', 'morning1', 'morning2', 'afternoon1', 'afternoon2', 'evening1', 'evening2',
|
|
|
|
'night1', 'night2'
|
|
|
|
]);
|
2017-07-20 04:54:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns date-related translations for a locale
|
2019-11-02 11:33:23 -04:00
|
|
|
* @returns [ dayPeriodsFormat, dayPeriodsStandalone, daysFormat, dayStandalone, monthsFormat,
|
|
|
|
* monthsStandalone, eras ]
|
2017-07-20 04:54:07 -04:00
|
|
|
* each value: [ narrow, abbreviated, wide, short? ]
|
|
|
|
*/
|
|
|
|
function getDateTimeTranslations(localeData) {
|
|
|
|
const dayNames = localeData.main(`dates/calendars/gregorian/days`);
|
|
|
|
const monthNames = localeData.main(`dates/calendars/gregorian/months`);
|
|
|
|
const erasNames = localeData.main(`dates/calendars/gregorian/eras`);
|
|
|
|
const dayPeriods = getDayPeriodsAmPm(localeData);
|
|
|
|
|
|
|
|
const dayPeriodsFormat = removeDuplicates([
|
2019-11-02 11:33:23 -04:00
|
|
|
objectValues(dayPeriods.format.narrow), objectValues(dayPeriods.format.abbreviated),
|
2017-07-20 04:54:07 -04:00
|
|
|
objectValues(dayPeriods.format.wide)
|
|
|
|
]);
|
|
|
|
|
|
|
|
const dayPeriodsStandalone = removeDuplicates([
|
|
|
|
objectValues(dayPeriods['stand-alone'].narrow),
|
|
|
|
objectValues(dayPeriods['stand-alone'].abbreviated),
|
|
|
|
objectValues(dayPeriods['stand-alone'].wide)
|
|
|
|
]);
|
|
|
|
|
|
|
|
const daysFormat = removeDuplicates([
|
2019-11-02 11:33:23 -04:00
|
|
|
objectValues(dayNames.format.narrow), objectValues(dayNames.format.abbreviated),
|
|
|
|
objectValues(dayNames.format.wide), objectValues(dayNames.format.short)
|
2017-07-20 04:54:07 -04:00
|
|
|
]);
|
|
|
|
|
|
|
|
const daysStandalone = removeDuplicates([
|
2019-11-02 11:33:23 -04:00
|
|
|
objectValues(dayNames['stand-alone'].narrow), objectValues(dayNames['stand-alone'].abbreviated),
|
|
|
|
objectValues(dayNames['stand-alone'].wide), objectValues(dayNames['stand-alone'].short)
|
2017-07-20 04:54:07 -04:00
|
|
|
]);
|
|
|
|
|
|
|
|
const monthsFormat = removeDuplicates([
|
2019-11-02 11:33:23 -04:00
|
|
|
objectValues(monthNames.format.narrow), objectValues(monthNames.format.abbreviated),
|
2017-07-20 04:54:07 -04:00
|
|
|
objectValues(monthNames.format.wide)
|
|
|
|
]);
|
|
|
|
|
|
|
|
const monthsStandalone = removeDuplicates([
|
|
|
|
objectValues(monthNames['stand-alone'].narrow),
|
|
|
|
objectValues(monthNames['stand-alone'].abbreviated),
|
|
|
|
objectValues(monthNames['stand-alone'].wide)
|
|
|
|
]);
|
|
|
|
|
|
|
|
const eras = removeDuplicates([
|
|
|
|
[erasNames.eraNarrow['0'], erasNames.eraNarrow['1']],
|
|
|
|
[erasNames.eraAbbr['0'], erasNames.eraAbbr['1']],
|
|
|
|
[erasNames.eraNames['0'], erasNames.eraNames['1']]
|
|
|
|
]);
|
|
|
|
|
|
|
|
const dateTimeTranslations = [
|
|
|
|
...removeDuplicates([dayPeriodsFormat, dayPeriodsStandalone]),
|
|
|
|
...removeDuplicates([daysFormat, daysStandalone]),
|
2019-11-02 11:33:23 -04:00
|
|
|
...removeDuplicates([monthsFormat, monthsStandalone]), eras
|
2017-07-20 04:54:07 -04:00
|
|
|
];
|
|
|
|
|
|
|
|
return dateTimeTranslations;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns date, time and dateTime formats for a locale
|
|
|
|
* @returns [dateFormats, timeFormats, dateTimeFormats]
|
|
|
|
* each format: [ short, medium, long, full ]
|
|
|
|
*/
|
|
|
|
function getDateTimeFormats(localeData) {
|
|
|
|
function getFormats(data) {
|
|
|
|
return removeDuplicates([
|
2019-11-02 11:33:23 -04:00
|
|
|
data.short._value || data.short, data.medium._value || data.medium,
|
|
|
|
data.long._value || data.long, data.full._value || data.full
|
2017-09-22 13:51:03 -04:00
|
|
|
]);
|
2017-07-20 04:54:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
const dateFormats = localeData.main('dates/calendars/gregorian/dateFormats');
|
|
|
|
const timeFormats = localeData.main('dates/calendars/gregorian/timeFormats');
|
|
|
|
const dateTimeFormats = localeData.main('dates/calendars/gregorian/dateTimeFormats');
|
|
|
|
|
2019-11-02 11:33:23 -04:00
|
|
|
return [getFormats(dateFormats), getFormats(timeFormats), getFormats(dateTimeFormats)];
|
2017-07-20 04:54:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns day period rules for a locale
|
|
|
|
* @returns string[]
|
|
|
|
*/
|
|
|
|
function getDayPeriodRules(localeData) {
|
2019-11-02 11:33:23 -04:00
|
|
|
const dayPeriodRules =
|
|
|
|
localeData.get(`supplemental/dayPeriodRuleSet/${localeData.attributes.language}`);
|
2017-07-20 04:54:07 -04:00
|
|
|
const rules = {};
|
|
|
|
if (dayPeriodRules) {
|
|
|
|
Object.keys(dayPeriodRules).forEach(key => {
|
|
|
|
if (dayPeriodRules[key]._at) {
|
|
|
|
rules[key] = dayPeriodRules[key]._at;
|
|
|
|
} else {
|
2017-09-22 13:51:03 -04:00
|
|
|
rules[key] = [dayPeriodRules[key]._from, dayPeriodRules[key]._before];
|
2017-07-20 04:54:07 -04:00
|
|
|
}
|
2017-09-22 13:51:03 -04:00
|
|
|
});
|
2017-07-20 04:54:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return rules;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the first day of the week, based on US week days
|
|
|
|
* @returns number
|
|
|
|
*/
|
|
|
|
function getFirstDayOfWeek(localeData) {
|
|
|
|
return WEEK_DAYS.indexOf(localeData.supplemental.weekData.firstDay());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns week-end range for a locale, based on US week days
|
|
|
|
* @returns [number, number]
|
|
|
|
*/
|
|
|
|
function getWeekendRange(localeData) {
|
|
|
|
const startDay =
|
2019-11-02 11:33:23 -04:00
|
|
|
localeData.get(`supplemental/weekData/weekendStart/${localeData.attributes.territory}`) ||
|
|
|
|
localeData.get('supplemental/weekData/weekendStart/001');
|
2017-07-20 04:54:07 -04:00
|
|
|
const endDay =
|
2019-11-02 11:33:23 -04:00
|
|
|
localeData.get(`supplemental/weekData/weekendEnd/${localeData.attributes.territory}`) ||
|
|
|
|
localeData.get('supplemental/weekData/weekendEnd/001');
|
2017-07-20 04:54:07 -04:00
|
|
|
return [WEEK_DAYS.indexOf(startDay), WEEK_DAYS.indexOf(endDay)];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns dateTime data for a locale
|
|
|
|
* @returns [ firstDayOfWeek, weekendRange, formats ]
|
|
|
|
*/
|
|
|
|
function getDateTimeSettings(localeData) {
|
2019-11-02 11:33:23 -04:00
|
|
|
return [
|
|
|
|
getFirstDayOfWeek(localeData), getWeekendRange(localeData), ...getDateTimeFormats(localeData)
|
|
|
|
];
|
2017-07-20 04:54:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the number symbols and formats for a locale
|
|
|
|
* @returns [ symbols, formats ]
|
2019-11-02 11:33:23 -04:00
|
|
|
* symbols: [ decimal, group, list, percentSign, plusSign, minusSign, exponential,
|
|
|
|
* superscriptingExponent, perMille, infinity, nan, timeSeparator, currencyDecimal?, currencyGroup?
|
|
|
|
* ]
|
2017-07-20 04:54:07 -04:00
|
|
|
* formats: [ currency, decimal, percent, scientific ]
|
|
|
|
*/
|
|
|
|
function getNumberSettings(localeData) {
|
|
|
|
const decimalFormat = localeData.main('numbers/decimalFormats-numberSystem-latn/standard');
|
|
|
|
const percentFormat = localeData.main('numbers/percentFormats-numberSystem-latn/standard');
|
|
|
|
const scientificFormat = localeData.main('numbers/scientificFormats-numberSystem-latn/standard');
|
|
|
|
const currencyFormat = localeData.main('numbers/currencyFormats-numberSystem-latn/standard');
|
|
|
|
const symbols = localeData.main('numbers/symbols-numberSystem-latn');
|
|
|
|
const symbolValues = [
|
|
|
|
symbols.decimal,
|
|
|
|
symbols.group,
|
|
|
|
symbols.list,
|
|
|
|
symbols.percentSign,
|
|
|
|
symbols.plusSign,
|
|
|
|
symbols.minusSign,
|
|
|
|
symbols.exponential,
|
|
|
|
symbols.superscriptingExponent,
|
|
|
|
symbols.perMille,
|
|
|
|
symbols.infinity,
|
|
|
|
symbols.nan,
|
|
|
|
symbols.timeSeparator,
|
|
|
|
];
|
|
|
|
|
2018-06-25 10:49:46 -04:00
|
|
|
if (symbols.currencyDecimal || symbols.currencyGroup) {
|
2017-07-20 04:54:07 -04:00
|
|
|
symbolValues.push(symbols.currencyDecimal);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (symbols.currencyGroup) {
|
|
|
|
symbolValues.push(symbols.currencyGroup);
|
|
|
|
}
|
|
|
|
|
2019-11-02 11:33:23 -04:00
|
|
|
return [symbolValues, [decimalFormat, percentFormat, currencyFormat, scientificFormat]];
|
2017-07-20 04:54:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-12-14 05:39:41 -05:00
|
|
|
* Returns the currency code, symbol and name for a locale
|
|
|
|
* @returns [ code, symbol, name ]
|
2017-07-20 04:54:07 -04:00
|
|
|
*/
|
|
|
|
function getCurrencySettings(locale, localeData) {
|
|
|
|
const currencyInfo = localeData.main(`numbers/currencies`);
|
|
|
|
let currentCurrency = '';
|
|
|
|
|
|
|
|
// find the currency currently used in this country
|
|
|
|
const currencies =
|
2019-11-02 11:33:23 -04:00
|
|
|
localeData.get(`supplemental/currencyData/region/${localeData.attributes.territory}`) ||
|
|
|
|
localeData.get(
|
|
|
|
`supplemental/currencyData/region/${localeData.attributes.language.toUpperCase()}`);
|
2017-07-20 04:54:07 -04:00
|
|
|
|
|
|
|
if (currencies) {
|
|
|
|
currencies.some(currency => {
|
|
|
|
const keys = Object.keys(currency);
|
|
|
|
return keys.some(key => {
|
|
|
|
if (currency[key]._from && !currency[key]._to) {
|
|
|
|
return currentCurrency = key;
|
|
|
|
}
|
2017-09-22 13:51:03 -04:00
|
|
|
});
|
2017-07-20 04:54:07 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
if (!currentCurrency) {
|
|
|
|
throw new Error(`Unable to find currency for locale "${locale}"`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-14 05:39:41 -05:00
|
|
|
let currencySettings = [undefined, undefined, undefined];
|
2017-07-20 04:54:07 -04:00
|
|
|
|
|
|
|
if (currentCurrency) {
|
2019-12-14 05:39:41 -05:00
|
|
|
currencySettings = [
|
|
|
|
currentCurrency, currencyInfo[currentCurrency].symbol,
|
|
|
|
currencyInfo[currentCurrency].displayName
|
|
|
|
];
|
2017-07-20 04:54:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return currencySettings;
|
|
|
|
}
|
|
|
|
|
2019-11-22 08:07:30 -05:00
|
|
|
/**
|
|
|
|
* Returns the writing direction for a locale
|
|
|
|
* @returns 'rtl' | 'ltr'
|
|
|
|
*/
|
|
|
|
function getDirectionality(localeData) {
|
|
|
|
const rtl = localeData.get('scriptMetadata/{script}/rtl');
|
|
|
|
return rtl === 'YES' ? 'rtl' : 'ltr';
|
|
|
|
}
|
|
|
|
|
2017-07-20 04:54:07 -04:00
|
|
|
/**
|
|
|
|
* Transforms a string into a regexp
|
|
|
|
*/
|
|
|
|
function toRegExp(s) {
|
|
|
|
return new RegExp(s.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'), 'g');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the plural function for a locale
|
|
|
|
* todo(ocombe): replace "cldr" extractPluralRuleFunction with our own extraction using "CldrJS"
|
|
|
|
* because the 2 libs can become out of sync if they use different versions of the cldr database
|
|
|
|
*/
|
2020-05-12 14:10:01 -04:00
|
|
|
function getPluralFunction(locale, withTypes = true) {
|
2017-07-20 04:54:07 -04:00
|
|
|
let fn = cldr.extractPluralRuleFunction(locale).toString();
|
2017-08-23 20:10:27 -04:00
|
|
|
|
2017-07-20 04:54:07 -04:00
|
|
|
if (fn === EMPTY_RULE) {
|
|
|
|
fn = DEFAULT_RULE;
|
|
|
|
}
|
|
|
|
|
2020-05-12 14:10:01 -04:00
|
|
|
const numberType = withTypes ? ': number' : '';
|
2019-10-31 13:29:41 -04:00
|
|
|
fn = fn.replace(/function anonymous\(n[^}]+{/g, `function plural(n${numberType})${numberType} {`)
|
2020-05-12 14:10:01 -04:00
|
|
|
.replace(toRegExp('var'), 'let')
|
2019-11-02 11:33:23 -04:00
|
|
|
.replace(toRegExp('if(typeof n==="string")n=parseInt(n,10);'), '')
|
|
|
|
.replace(toRegExp('\n}'), ';\n}');
|
2017-08-23 20:10:27 -04:00
|
|
|
|
|
|
|
// The replacement values must match the `Plural` enum from common.
|
|
|
|
// We do not use the enum directly to avoid depending on that package.
|
2019-11-02 11:33:23 -04:00
|
|
|
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');
|
2017-07-20 04:54:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return an array of values from an object
|
|
|
|
*/
|
|
|
|
function objectValues(obj) {
|
|
|
|
return Object.keys(obj).map(key => obj[key]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
* is undefined, we can take the previous defined value instead, because it means that it has
|
|
|
|
* been deduplicated.
|
|
|
|
* e.g.: [x, y, undefined, z, undefined, undefined]
|
|
|
|
* The first undefined is equivalent to y, the second and third are equivalent to z
|
|
|
|
* Note that the first value in an array is always defined.
|
|
|
|
*
|
|
|
|
* Also since we need to know which data is assumed similar, it is important that we store those
|
|
|
|
* similar data in arrays to mark the delimitation between values that have different meanings
|
|
|
|
* (e.g. months and days).
|
|
|
|
*
|
2018-04-09 08:15:00 -04:00
|
|
|
* For further size improvements, "undefined" values will be replaced by a constant in the arrays
|
2017-07-20 04:54:07 -04:00
|
|
|
* as the last step of the file generation (in generateLocale and generateLocaleExtra).
|
2018-04-09 08:15:00 -04:00
|
|
|
* e.g.: [x, y, undefined, z, undefined, undefined] will be [x, y, u, z, u, u]
|
2017-07-20 04:54:07 -04:00
|
|
|
*/
|
|
|
|
function removeDuplicates(data) {
|
|
|
|
const dedup = [data[0]];
|
2019-11-02 11:33:23 -04:00
|
|
|
for (let i = 1; i < data.length; i++) {
|
2017-07-20 04:54:07 -04:00
|
|
|
if (stringify(data[i]) !== stringify(data[i - 1])) {
|
|
|
|
dedup.push(data[i]);
|
|
|
|
} else {
|
|
|
|
dedup.push(undefined);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dedup;
|
|
|
|
}
|
|
|
|
|
2019-10-31 13:29:41 -04:00
|
|
|
/**
|
|
|
|
* In Angular the locale is referenced by a "normalized" form.
|
|
|
|
*/
|
|
|
|
function normalizeLocale(locale) {
|
|
|
|
return locale.toLowerCase().replace(/_/g, '-');
|
|
|
|
}
|
|
|
|
|
2017-07-20 04:54:07 -04:00
|
|
|
module.exports.I18N_FOLDER = I18N_FOLDER;
|
|
|
|
module.exports.I18N_DATA_FOLDER = I18N_DATA_FOLDER;
|
2017-08-21 13:11:07 -04:00
|
|
|
module.exports.RELATIVE_I18N_DATA_FOLDER = RELATIVE_I18N_DATA_FOLDER;
|
|
|
|
module.exports.HEADER = HEADER;
|