Revert "build: convert CLDR locale extraction from Gulp to Bazel tool (#42230)" (#42583)

This reverts commit 1eaeb23c75.

PR Close #42583
This commit is contained in:
Alex Rickabaugh 2021-06-16 09:24:56 -07:00
parent 877cde8897
commit ec6dc78f1d
31 changed files with 1294 additions and 1278 deletions

3
.gitignore vendored
View File

@ -50,5 +50,8 @@ baseline.json
# Ignore .history for the xyz.local-history VSCode extension
.history
# CLDR data
tools/gulp-tasks/cldr/cldr-data/
# Husky
.husky/_

View File

@ -62,18 +62,3 @@ sass_repositories()
load("@io_bazel_skydoc//skylark:skylark.bzl", "skydoc_repositories")
skydoc_repositories()
load("//packages/common/locales/generate-locales-tool:cldr-data.bzl", "cldr_data_repository")
cldr_data_repository(
name = "cldr_data",
# Since we use the Github archives for CLDR 37, we need to specify a path
# to the available locales. This wouldn't be needed with CLDR 39 as that
# comes with an official JSON archive not containing a version suffix.
available_locales_path = "cldr-core-37.0.0/availableLocales.json",
urls = {
"https://github.com/unicode-cldr/cldr-core/archive/37.0.0.zip": "32b5c49c3874aa342b90412c207b42e7aefb2435295891fb714c34ce58b3c706",
"https://github.com/unicode-cldr/cldr-dates-full/archive/37.0.0.zip": "e1c410dd8ad7d75df4a5393efaf5d28f0d56c0fa126c5d66e171a3f21a988a1e",
"https://github.com/unicode-cldr/cldr-numbers-full/archive/37.0.0.zip": "a921b90cf7f436e63fbdd55880f96e39a203acd9e174b0ceafa20a02c242a12e",
},
)

View File

@ -20,3 +20,6 @@ function loadTask(fileName, taskName) {
gulp.task('source-map-test', loadTask('source-map-test'));
gulp.task('changelog:zonejs', loadTask('changelog-zonejs'));
gulp.task('cldr:extract', loadTask('cldr', 'extract'));
gulp.task('cldr:download', loadTask('cldr', 'download'));
gulp.task('cldr:gen-closure-locale', loadTask('cldr', 'closure'));

View File

@ -164,7 +164,6 @@
"@bazel/buildifier": "^4.0.1",
"@bazel/ibazel": "^0.15.8",
"@octokit/graphql": "^4.6.1",
"@types/cldrjs": "^0.4.22",
"@types/cli-progress": "^3.4.2",
"@types/conventional-commits-parser": "^3.0.1",
"@types/ejs": "^3.0.6",
@ -174,7 +173,8 @@
"browserstacktunnel-wrapper": "^2.0.4",
"check-side-effects": "0.0.23",
"clang-format": "^1.4.0",
"cldr": "5.7.0",
"cldr": "7.0.0",
"cldr-data-downloader": "^0.3.5",
"cldrjs": "0.5.5",
"cli-progress": "^3.7.0",
"conventional-changelog": "^3.1.24",
@ -217,5 +217,6 @@
"@babel/template": "7.8.6",
"@babel/traverse": "7.8.6",
"@babel/types": "7.8.6"
}
},
"cldr-data-coverage": "full"
}

View File

@ -1,17 +0,0 @@
load("//tools:defaults.bzl", "ts_library")
package(default_visibility = ["//visibility:public"])
ts_library(
name = "generate-locales-tool",
srcs = glob(["*.ts"]),
deps = [
"@npm//@bazel/runfiles",
"@npm//@types/cldrjs",
"@npm//@types/glob",
"@npm//@types/node",
"@npm//cldr",
"@npm//cldrjs",
"@npm//glob",
],
)

View File

@ -1,35 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
*/
/**
* To create smaller locale files, we remove duplicated data.
* To make this work we store the data in arrays, where `undefined` indicates that the
* value is a duplicate of the previous value in the array.
* e.g. consider an array like: [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).
*
* For further size improvements, "undefined" values will be replaced by a constant in the arrays
* as the last step of the file generation (in generateLocale and generateLocaleExtra).
* e.g.: [x, y, undefined, z, undefined, undefined] will be [x, y, u, z, u, u]
*/
export function removeDuplicates(data: unknown[]) {
const dedup = [data[0]];
for (let i = 1; i < data.length; i++) {
if (JSON.stringify(data[i]) !== JSON.stringify(data[i - 1])) {
dedup.push(data[i]);
} else {
dedup.push(undefined);
}
}
return dedup;
}

View File

@ -1,33 +0,0 @@
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
load("//tools:defaults.bzl", "ts_library")
package(default_visibility = ["//visibility:public"])
BIN_ENTRYPOINTS = [
"get-base-currencies-file",
"get-base-locale-file",
"get-closure-locale-file",
"write-locale-files-to-dist",
]
ts_library(
name = "bin",
srcs = glob(["*.ts"]),
deps = [
"//packages/common/locales/generate-locales-tool",
"@npm//@types/node",
],
)
[nodejs_binary(
name = entrypoint,
data = [
":bin",
"@cldr_data//:all_json",
],
entry_point = ":%s.ts" % entrypoint,
# We need to patch the NodeJS module resolution as this binary runs as
# part of a genrule where the linker does not work as expected.
# See: https://github.com/bazelbuild/rules_nodejs/issues/2600.
templated_args = ["--bazel_patch_module_resolver"],
) for entrypoint in BIN_ENTRYPOINTS]

View File

@ -1,17 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
*/
/**
* Base locale used as foundation for other locales. For example: A base locale allows
* generation of a file containing all currencies with their corresponding symbols. If we
* generate other locales, they can override currency symbols which are different in the base
* locale. This means that we do not need re-generate all currencies w/ symbols multiple times,
* and allows us to reduce the locale data payload as the base locale is always included.
* */
export const BASE_LOCALE = 'en';

View File

@ -1,23 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
*/
import {CldrData} from '../cldr-data';
import {generateBaseCurrenciesFile} from '../locale-base-currencies';
import {BASE_LOCALE} from './base-locale';
/** Generates the base currencies file and prints it to the stdout. */
function main() {
const cldrData = new CldrData();
const baseLocaleData = cldrData.getLocaleData(BASE_LOCALE)!;
process.stdout.write(generateBaseCurrenciesFile(baseLocaleData));
}
if (require.main === module) {
main();
}

View File

@ -1,25 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
*/
import {CldrData} from '../cldr-data';
import {generateBaseCurrencies} from '../locale-base-currencies';
import {generateLocale} from '../locale-file';
import {BASE_LOCALE} from './base-locale';
/** Generates the base locale file and prints it to the stdout. */
function main() {
const cldrData = new CldrData();
const baseLocaleData = cldrData.getLocaleData(BASE_LOCALE)!;
const baseCurrencies = generateBaseCurrencies(baseLocaleData);
process.stdout.write(generateLocale(BASE_LOCALE, baseLocaleData, baseCurrencies));
}
if (require.main === module) {
main();
}

View File

@ -1,25 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
*/
import {CldrData} from '../cldr-data';
import {generateClosureLocaleFile} from '../closure-locale-file';
import {generateBaseCurrencies} from '../locale-base-currencies';
import {BASE_LOCALE} from './base-locale';
/** Generates the Google3 closure-locale file and prints it to the stdout. */
function main() {
const cldrData = new CldrData();
const baseLocaleData = cldrData.getLocaleData(BASE_LOCALE)!;
const baseCurrencies = generateBaseCurrencies(baseLocaleData);
process.stdout.write(generateClosureLocaleFile(cldrData, baseCurrencies));
}
if (require.main === module) {
main();
}

View File

@ -1,71 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
*/
import {writeFileSync} from 'fs';
import {join} from 'path';
import {CldrData} from '../cldr-data';
import {generateBaseCurrencies} from '../locale-base-currencies';
import {generateLocaleExtra} from '../locale-extra-file';
import {generateLocale} from '../locale-file';
import {generateLocaleGlobalFile} from '../locale-global-file';
import {BASE_LOCALE} from './base-locale';
/**
* Generates locale files for each available CLDR locale and writes it to the
* specified directory.
*/
function main(outputDir: string) {
const cldrData = new CldrData();
const baseLocaleData = cldrData.getLocaleData(BASE_LOCALE)!;
const baseCurrencies = generateBaseCurrencies(baseLocaleData);
const extraLocaleDir = join(outputDir, 'extra');
const globalLocaleDir = join(outputDir, 'global');
console.info(`Writing locales to: ${outputDir}`);
// Generate locale files for all locales we have data for.
cldrData.availableLocales.forEach((locale: string) => {
const localeData = cldrData.getLocaleData(locale);
// If `cldrjs` is unable to resolve a `bundle` for the current locale, then there is no data
// for this locale, and it should not be generated. This can happen as with older versions of
// CLDR where `availableLocales.json` specifies locales for which no data is available
// (even within the `full` tier packages). See:
// http://cldr.unicode.org/development/development-process/design-proposals/json-packaging.
// TODO(devversion): Remove if we update to CLDR v39 where this seems fixed. Note that this
// worked before in the Gulp tooling without such a check because the `cldr-data-downloader`
// overwrote the `availableLocales` to only capture locales with data.
if (localeData && !(localeData.attributes as any).bundle) {
console.info(`Skipping generation of ${locale} as there is no data.`);
return;
}
const localeFile = generateLocale(locale, localeData, baseCurrencies);
const localeExtraFile = generateLocaleExtra(locale, localeData);
const localeGlobalFile = generateLocaleGlobalFile(locale, localeData, baseCurrencies);
writeFileSync(join(outputDir, `${locale}.ts`), localeFile);
writeFileSync(join(extraLocaleDir, `${locale}.ts`), localeExtraFile);
writeFileSync(join(globalLocaleDir, `${locale}.js`), localeGlobalFile);
});
}
if (require.main === module) {
// The first argument is expected to be a path resolving to a directory
// where all locales should be generated into.
const outputDir = process.argv[2];
if (outputDir === undefined) {
throw Error('No output directory specified.');
}
main(outputDir);
}

View File

@ -1,48 +0,0 @@
def _cldr_data_repository_impl(ctx):
for url, sha256 in ctx.attr.urls.items():
ctx.report_progress("Downloading CLDR data from: %s" % url)
ctx.download_and_extract(
url = url,
sha256 = sha256,
)
ctx.report_progress("Extracting available locales from: %s" % ctx.attr.available_locales_path)
locales_json = ctx.read(ctx.attr.available_locales_path)
locales = json.decode(locales_json)["availableLocales"]["full"]
ctx.report_progress("Extracted %s locales from CLDR" % len(locales))
ctx.file("index.bzl", content = """
LOCALES=%s
""" % locales)
ctx.file("BUILD.bazel", content = """
filegroup(
name = "all_json",
srcs = glob(["**/*.json"]),
visibility = ["//visibility:public"],
)
""")
"""
Repository rule that downloads CLDR data from the specified repository and generates a
`BUILD.bazel` file that exposes all data files. Additionally, an `index.bzl` file is generated
that exposes a constant for all locales the repository contains data for. This can be used to
generate pre-declared outputs.
"""
cldr_data_repository = repository_rule(
implementation = _cldr_data_repository_impl,
attrs = {
"urls": attr.string_dict(doc = """
Dictionary of URLs that resolve to archives containing CLDR JSON data. These archives
will be downloaded and extracted at root of the repository. Each key can specify
a SHA256 checksum for hermetic builds.
""", mandatory = True),
"available_locales_path": attr.string(
doc = """
Relative path to the JSON data file describing all available locales.
This file usually resides within the `cldr-core` package
""",
default = "cldr-core/availableLocales.json",
),
},
)

View File

@ -1,140 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
*/
import {runfiles} from '@bazel/runfiles';
import {CldrStatic} from 'cldrjs';
import {sync as globSync} from 'glob';
// TypeScript doesn't allow us to import the default export without the `esModuleInterop`. We use
// the NodeJS require function instead as specifying a custom tsconfig complicates the setup
// unnecessarily.
// TODO: See if we can improve this by having better types for `cldrjs`.
const cldrjs: typeof import('cldrjs') = require('cldrjs');
/**
* Globs that match CLDR JSON data files that should be fetched. We limit these intentionally
* as loading unused data results in significant slow-down of the generation
* (noticeable in local development if locale data is re-generated).
*/
const CLDR_DATA_GLOBS = [
'cldr-core-37.0.0/scriptMetadata.json',
'cldr-core-37.0.0/supplemental/**/*.json',
'cldr-dates-full-37.0.0/main/**/*.json',
'cldr-numbers-full-37.0.0/main/**/*.json',
];
/** Path to the CLDR available locales file. */
const CLDR_AVAILABLE_LOCALES_PATH = 'cldr-core-37.0.0/availableLocales.json';
/**
* Instance providing access to a locale's CLDR data. This type extends the `cldrjs`
* instance type with the missing `bundle` attribute property.
*/
export type CldrLocaleData = CldrStatic&{
attributes: {
/**
* Resolved bundle name for the locale.
* More details: http://www.unicode.org/reports/tr35/#Bundle_vs_Item_Lookup
*/
bundle: string;
}
};
/**
* Class that provides access to the CLDR data downloaded as part of
* the `@cldr_data` Bazel repository.
*/
export class CldrData {
/** Path to the CLDR data Bazel repository. i.e. `@cldr_data//`. */
readonly cldrDataDir = runfiles.resolve('cldr_data');
/** List of all available locales CLDR provides data for. */
readonly availableLocales: readonly CldrLocaleData[];
constructor() {
this._loadAndPopulateCldrData();
this.availableLocales = this._getAvailableLocales();
}
/** Gets the CLDR data for the specified locale. */
getLocaleData(localeName: string): CldrLocaleData|null {
// Cast to `CldrLocaleData` because the default `cldrjs` types from `DefinitelyTyped`
// are outdated and do not capture the `bundle` attribute. See:
// https://github.com/rxaviers/cldrjs#instantiate-a-locale-and-get-it-normalized.
const localeData = new cldrjs(localeName) as CldrLocaleData;
// In case a locale has been requested for which no data is available, we return
// `null` immediately instead of returning an empty `CldrStatic` instance.
if (localeData.attributes.bundle === null) {
return null;
}
return localeData;
}
/** Gets a list of all locales CLDR provides data for. */
private _getAvailableLocales(): CldrLocaleData[] {
const allLocales =
require(`${this.cldrDataDir}/${CLDR_AVAILABLE_LOCALES_PATH}`).availableLocales.full;
const localesWithData: CldrLocaleData[] = [];
for (const localeName of allLocales) {
const localeData = this.getLocaleData(localeName);
// If `cldrjs` is unable to resolve a `bundle` for the current locale, then there is no data
// for this locale, and it should not be generated. This can happen as with older versions of
// CLDR where `availableLocales.json` specifies locales for which no data is available
// (even within the `full` tier packages). See:
// http://cldr.unicode.org/development/development-process/design-proposals/json-packaging.
// TODO(devversion): Remove if we update to CLDR v39 where this seems fixed. Note that this
// worked before in the Gulp tooling without such a check because the `cldr-data-downloader`
// overwrote the `availableLocales` to only capture locales with data.
if (localeData !== null) {
localesWithData.push(localeData);
}
}
return localesWithData;
}
/** Loads the CLDR data and populates the `cldrjs` library with it. */
private _loadAndPopulateCldrData() {
const localeData = this._readCldrDataFromRepository();
if (localeData.length === 0) {
throw Error('No CLDR data could be found.');
}
// Populate the `cldrjs` library with the locale data. Note that we need this type cast
// to satisfy the first `cldrjs.load` parameter which cannot be undefined.
cldrjs.load(...localeData as [object, ...object[]]);
}
/**
* Reads the CLDR JSON data from the Bazel repository.
* @returns a list of read JSON objects representing the CLDR data.
*/
private _readCldrDataFromRepository(): object[] {
const jsonFiles =
CLDR_DATA_GLOBS.map(pattern => globSync(pattern, {cwd: this.cldrDataDir, absolute: true}))
.reduce((acc, dataFiles) => [...acc, ...dataFiles], []);
// Read the JSON for all determined CLDR json files.
return jsonFiles.map(filePath => {
const parsed = require(filePath);
// Guards against cases where non-CLDR data files are accidentally picked up
// by the glob above and would throw-off the bundle lookup in `cldrjs`.
if (parsed.main !== undefined && typeof parsed.main !== 'object') {
throw Error('Unexpected CLDR json file with "main" field which is not an object.');
}
return parsed;
});
}
}

View File

@ -1,153 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
*/
import {CldrData} from './cldr-data';
import {fileHeader} from './file-header';
import {BaseCurrencies} from './locale-base-currencies';
import {generateLocale} from './locale-file';
/**
* List of locales used by Closure. These locales will be incorporated in the generated
* closure locale file. See:
* https://github.com/google/closure-library/blob/master/closure/goog/i18n/datetimepatterns.js#L2450
*/
const GOOG_LOCALES = [
'af', 'am', 'ar', 'ar-DZ', 'az', 'be', 'bg', 'bn', 'br', 'bs',
'ca', 'chr', 'cs', 'cy', 'da', 'de', 'de-AT', 'de-CH', 'el', 'en-AU',
'en-CA', 'en-GB', 'en-IE', 'en-IN', 'en-SG', 'en-ZA', 'es', 'es-419', 'es-MX', 'es-US',
'et', 'eu', 'fa', 'fi', 'fr', 'fr-CA', 'ga', 'gl', 'gsw', 'gu',
'haw', 'hi', 'hr', 'hu', 'hy', 'in', 'is', 'it', 'iw', 'ja',
'ka', 'kk', 'km', 'kn', 'ko', 'ky', 'ln', 'lo', 'lt', 'lv',
'mk', 'ml', 'mn', 'mo', 'mr', 'ms', 'mt', 'my', 'ne', 'nl',
'no', 'or', 'pa', 'pl', 'pt', 'pt-PT', 'ro', 'ru', 'sh', 'si',
'sk', 'sl', 'sq', 'sr', 'sv', 'sw', 'ta', 'te', 'th', 'tl',
'tr', 'uk', 'ur', 'uz', 'vi', 'zh', 'zh-CN', 'zh-HK', 'zh-TW', 'zu'
];
export function generateClosureLocaleFile(
cldrData: CldrData, baseCurrencies: BaseCurrencies): string {
// locale id aliases to support deprecated locale ids used by closure
// it maps deprecated ids --> new ids
// manually extracted from ./cldr-data/supplemental/aliases.json
// TODO: Consider extracting directly from the CLDR data instead.
const aliases = {
'in': 'id',
'iw': 'he',
'mo': 'ro-MD',
'no': 'nb',
'nb': 'no-NO',
'sh': 'sr-Latn',
'tl': 'fil',
'pt': 'pt-BR',
'zh-CN': 'zh-Hans-CN',
'zh-Hans-CN': 'zh-Hans',
'zh-HK': 'zh-Hant-HK',
'zh-TW': 'zh-Hant-TW',
'zh-Hant-TW': 'zh-Hant',
};
return generateAllLocalesFile(cldrData, GOOG_LOCALES, aliases, baseCurrencies);
}
/**
* Generate a file that contains all locale to import for closure.
* Tree shaking will only keep the data for the `goog.LOCALE` locale.
*/
function generateAllLocalesFile(
cldrData: CldrData, locales: string[], aliases: {[name: string]: string},
baseCurrencies: BaseCurrencies) {
const existingLocalesAliases: {[locale: string]: Set<string>} = {};
const existingLocalesData: {[locale: string]: string} = {};
// for each locale, get the data and the list of equivalent locales
locales.forEach(locale => {
const eqLocales = new Set<string>();
eqLocales.add(locale);
if (locale.match(/-/)) {
eqLocales.add(locale.replace(/-/g, '_'));
}
// check for aliases
const alias = aliases[locale];
if (alias) {
eqLocales.add(alias);
if (alias.match(/-/)) {
eqLocales.add(alias.replace(/-/g, '_'));
}
// to avoid duplicated "case" we regroup all locales in the same "case"
// the simplest way to do that is to have alias aliases
// e.g. 'no' --> 'nb', 'nb' --> 'no-NO'
// which means that we'll have 'no', 'nb' and 'no-NO' in the same "case"
const aliasKeys = Object.keys(aliases);
for (let i = 0; i < aliasKeys.length; i++) {
const aliasValue = aliases[alias];
if (aliasKeys.indexOf(alias) !== -1 && !eqLocales.has(aliasValue)) {
eqLocales.add(aliasValue);
if (aliasValue.match(/-/)) {
eqLocales.add(aliasValue.replace(/-/g, '_'));
}
}
}
}
const localeNameForData = aliases[locale] ?? locale;
const localeData = cldrData.getLocaleData(localeNameForData);
const localeName = formatLocale(locale);
existingLocalesData[locale] =
generateLocale(localeNameForData, localeData, baseCurrencies)
.replace(`${fileHeader}\n`, '')
.replace('export default ', `export const locale_${localeName} = `)
.replace('function plural', `function plural_${localeName}`)
.replace(/,\s+plural/, `, plural_${localeName}`)
.replace(/\s*const u = undefined;\s*/, '');
existingLocalesAliases[locale] = eqLocales;
});
function generateCases(locale: string) {
let str = '';
let locales: string[] = [];
const eqLocales = existingLocalesAliases[locale];
eqLocales.forEach(l => {
str += `case '${l}':\n`;
locales.push(`'${l}'`);
});
let localesStr = '[' + locales.join(',') + ']';
str += ` l = locale_${formatLocale(locale)};
locales = ${localesStr};
break;\n`;
return str;
}
return `${fileHeader}
import {registerLocaleData} from '../src/i18n/locale_data';
const u = undefined;
${locales.map(locale => `${existingLocalesData[locale]}`).join('\n')}
let l: any;
let locales: string[] = [];
switch (goog.LOCALE) {
${locales.map(locale => generateCases(locale)).join('')}}
if(l) {
locales.forEach(locale => registerLocaleData(l, locale));
}
`;
}
function formatLocale(locale: string): string {
return locale.replace(/-/g, '_');
}

View File

@ -1,75 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
*/
import {CldrLocaleData} from './cldr-data';
/**
* Returns data for the chosen day periods
*/
export function getDayPeriods(localeData: CldrLocaleData, dayPeriodsList: string[]): {
format: {narrow: string[], abbreviated: string[], wide: string[]},
'stand-alone': {narrow: string[], abbreviated: string[], wide: string[]}
} {
const dayPeriods = localeData.main(`dates/calendars/gregorian/dayPeriods`);
const result: any = {};
// cleaning up unused keys
Object.keys(dayPeriods).forEach(key1 => { // format / stand-alone
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 as any;
}
/**
* Returns day period rules for a locale
* @returns string[]
*/
export function getDayPeriodRules(localeData: CldrLocaleData): {[key: string]: []} {
const dayPeriodRules =
localeData.get(`supplemental/dayPeriodRuleSet/${localeData.attributes.language}`);
const rules: any = {};
if (dayPeriodRules) {
Object.keys(dayPeriodRules).forEach(key => {
if (dayPeriodRules[key]._at) {
rules[key] = dayPeriodRules[key]._at;
} else {
rules[key] = [dayPeriodRules[key]._from, dayPeriodRules[key]._before];
}
});
}
return rules;
}
/**
* Returns the basic day periods (am/pm)
*/
export function getDayPeriodsAmPm(localeData: CldrLocaleData) {
return getDayPeriods(localeData, ['am', 'pm']);
}
/**
* Returns the extra day periods (without am/pm)
*/
export function getDayPeriodsNoAmPm(localeData: CldrLocaleData) {
return getDayPeriods(localeData, [
'noon', 'midnight', 'morning1', 'morning2', 'afternoon1', 'afternoon2', 'evening1', 'evening2',
'night1', 'night2'
]);
}

View File

@ -1,17 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
*/
export const fileHeader = `/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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.`;

View File

@ -1,74 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
*/
import {CldrLocaleData} from './cldr-data';
import {fileHeader} from './file-header';
import {stringify} from './object-stringify';
export type BaseCurrencySymbols = [
string
]|[string | undefined, string]|[string, undefined, number]|[string | undefined, string, number];
export type BaseCurrencies = {
[code: string]: BaseCurrencySymbols|undefined;
};
/**
* Generate a file that contains the list of currencies, their symbols and digits.
*/
export function generateBaseCurrenciesFile(baseLocaleData: CldrLocaleData) {
const baseCurrencies = generateBaseCurrencies(baseLocaleData);
return `${fileHeader}
export type CurrenciesSymbols = [string] | [string | undefined, string];
/** @internal */
export const CURRENCIES_EN: {[code: string]: CurrenciesSymbols | [string | undefined, string | undefined, number]} = ${
stringify(baseCurrencies)};
`;
}
/**
* Generate a list of currencies to be used as a base for other currencies
* e.g.: {'ARS': [, '$'], 'AUD': ['A$', '$'], ...}
*/
export function generateBaseCurrencies(localeData: CldrLocaleData) {
const currenciesData = localeData.main('numbers/currencies');
const fractions = localeData.get(`supplemental/currencyData/fractions`);
const currencies: BaseCurrencies = {};
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 {
symbolsArray = [undefined, symbolNarrow];
}
}
if (fractions[key] && fractions[key]['_digits']) {
const digits = parseInt(fractions[key]['_digits'], 10);
if (symbolsArray.length === 2) {
symbolsArray.push(digits);
} else if (symbolsArray.length === 1) {
symbolsArray = [...symbolsArray, undefined, digits];
} else {
symbolsArray = [undefined, undefined, digits];
}
}
if (symbolsArray.length > 0) {
currencies[key] = symbolsArray as BaseCurrencySymbols;
}
});
return currencies;
}

View File

@ -1,87 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
*/
import {CldrLocaleData} from './cldr-data';
import {BaseCurrencies} from './locale-base-currencies';
/**
* To minimize the file even more, we only output the differences compared to the base currency
*/
export function generateLocaleCurrencies(
localeData: CldrLocaleData, baseCurrencies: BaseCurrencies) {
const currenciesData = localeData.main('numbers/currencies');
const currencies: any = {};
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 {
symbolsArray = [undefined, symbolNarrow];
}
}
const baseCurrencySymbols = baseCurrencies[code] || [];
// Jf locale data is equal to the one in the base currencies, skip this currency to
// avoid unnecessary locale data that could be inferred from the base currency.
if (baseCurrencySymbols && baseCurrencySymbols[0] === symbolsArray[0] &&
baseCurrencySymbols[1] === symbolsArray[1]) {
return;
}
currencies[code] = symbolsArray;
});
return currencies;
}
/**
* Returns the currency code, symbol and name for a locale
*/
export function getCurrencySettings(localeName: string, localeData: CldrLocaleData) {
const currencyInfo = localeData.main(`numbers/currencies`);
let currentCurrency = '';
// find the currency currently used in this country
const currencies: any[] =
localeData.get(`supplemental/currencyData/region/${localeData.attributes.territory}`) ||
localeData.get(
`supplemental/currencyData/region/${localeData.attributes.language.toUpperCase()}`);
if (currencies) {
currencies.some(currency => {
const keys = Object.keys(currency);
return keys.some(key => {
if (currency[key]._from && !currency[key]._to) {
return currentCurrency = key;
}
});
});
if (!currentCurrency) {
throw new Error(`Unable to find currency for locale "${localeName}"`);
}
}
let currencySettings = [undefined, undefined, undefined];
if (currentCurrency) {
currencySettings = [
currentCurrency, currencyInfo[currentCurrency].symbol,
currencyInfo[currentCurrency].displayName
];
}
return currencySettings;
}

View File

@ -1,57 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
*/
import {removeDuplicates} from './array-deduplication';
import {CldrLocaleData} from './cldr-data';
import {getDayPeriodRules, getDayPeriodsNoAmPm} from './day-periods';
import {fileHeader} from './file-header';
import {stringify} from './object-stringify';
/**
* Generate the contents for the extra data file
*/
export function generateLocaleExtra(locale: string, localeData: CldrLocaleData) {
return `${fileHeader}
const u = undefined;
export default ${generateDayPeriodsSupplementalString(locale, localeData)};
`;
}
/**
* Collect up the day period rules, and extended day period data.
*/
export function generateDayPeriodsSupplementalString(locale: string, localeData: CldrLocaleData) {
const dayPeriods = getDayPeriodsNoAmPm(localeData);
const dayPeriodRules = getDayPeriodRules(localeData);
let dayPeriodsSupplemental: any[] = [];
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([
Object.values(dayPeriods.format.narrow), Object.values(dayPeriods.format.abbreviated),
Object.values(dayPeriods.format.wide)
]);
const dayPeriodsStandalone = removeDuplicates([
Object.values(dayPeriods['stand-alone'].narrow),
Object.values(dayPeriods['stand-alone'].abbreviated),
Object.values(dayPeriods['stand-alone'].wide)
]);
const rules = keys.map(key => dayPeriodRules[key]);
dayPeriodsSupplemental = [...removeDuplicates([dayPeriodsFormat, dayPeriodsStandalone]), rules];
}
return stringify(dayPeriodsSupplemental).replace(/undefined/g, 'u');
}

View File

@ -1,225 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
*/
import {removeDuplicates} from './array-deduplication';
import {CldrLocaleData} from './cldr-data';
import {getDayPeriodsAmPm} from './day-periods';
import {fileHeader} from './file-header';
import {BaseCurrencies} from './locale-base-currencies';
import {generateLocaleCurrencies, getCurrencySettings} from './locale-currencies';
import {stringify} from './object-stringify';
import {getPluralFunction} from './plural-function';
const WEEK_DAYS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
/** Generate contents for the basic locale data file */
export function generateLocale(
locale: string, localeData: CldrLocaleData, baseCurrencies: BaseCurrencies) {
return `${fileHeader}
const u = undefined;
${getPluralFunction(locale)}
export default ${generateBasicLocaleString(locale, localeData, baseCurrencies)};
`;
}
/**
* Collect up the basic locale data [ localeId, dateTime, number, currency, directionality,
* pluralCase ].
*/
export function generateBasicLocaleString(
locale: string, localeData: CldrLocaleData, baseCurrencies: BaseCurrencies) {
let data = stringify([
locale,
...getDateTimeTranslations(localeData),
...getDateTimeSettings(localeData),
...getNumberSettings(localeData),
...getCurrencySettings(locale, localeData),
generateLocaleCurrencies(localeData, baseCurrencies),
getDirectionality(localeData),
])
// We remove "undefined" added by spreading arrays when there is no value
.replace(/undefined/g, 'u');
// adding plural function after, because we don't want it as a string. The function named `plural`
// is expected to be available in the file. See `generateLocale` above.
data = data.replace(/\]$/, ', plural]');
return data;
}
/**
* Returns the writing direction for a locale
* @returns 'rtl' | 'ltr'
*/
function getDirectionality(localeData: CldrLocaleData): 'rtl'|'ltr' {
const rtl = localeData.get('scriptMetadata/{script}/rtl');
return rtl === 'YES' ? 'rtl' : 'ltr';
}
/**
* Returns dateTime data for a locale
* @returns [ firstDayOfWeek, weekendRange, formats ]
*/
function getDateTimeSettings(localeData: CldrLocaleData) {
return [
getFirstDayOfWeek(localeData), getWeekendRange(localeData), ...getDateTimeFormats(localeData)
];
}
/**
* Returns the number symbols and formats for a locale
* @returns [ symbols, formats ]
* symbols: [ decimal, group, list, percentSign, plusSign, minusSign, exponential,
* superscriptingExponent, perMille, infinity, nan, timeSeparator, currencyDecimal?, currencyGroup?
* ]
* formats: [ currency, decimal, percent, scientific ]
*/
function getNumberSettings(localeData: CldrLocaleData) {
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,
];
if (symbols.currencyDecimal || symbols.currencyGroup) {
symbolValues.push(symbols.currencyDecimal);
}
if (symbols.currencyGroup) {
symbolValues.push(symbols.currencyGroup);
}
return [symbolValues, [decimalFormat, percentFormat, currencyFormat, scientificFormat]];
}
/**
* Returns week-end range for a locale, based on US week days
* @returns [number, number]
*/
function getWeekendRange(localeData: CldrLocaleData) {
const startDay =
localeData.get(`supplemental/weekData/weekendStart/${localeData.attributes.territory}`) ||
localeData.get('supplemental/weekData/weekendStart/001');
const endDay =
localeData.get(`supplemental/weekData/weekendEnd/${localeData.attributes.territory}`) ||
localeData.get('supplemental/weekData/weekendEnd/001');
return [WEEK_DAYS.indexOf(startDay), WEEK_DAYS.indexOf(endDay)];
}
/**
* Returns date-related translations for a locale
* @returns [ dayPeriodsFormat, dayPeriodsStandalone, daysFormat, dayStandalone, monthsFormat,
* monthsStandalone, eras ]
* each value: [ narrow, abbreviated, wide, short? ]
*/
function getDateTimeTranslations(localeData: CldrLocaleData) {
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([
Object.values(dayPeriods.format.narrow), Object.values(dayPeriods.format.abbreviated),
Object.values(dayPeriods.format.wide)
]);
const dayPeriodsStandalone = removeDuplicates([
Object.values(dayPeriods['stand-alone'].narrow),
Object.values(dayPeriods['stand-alone'].abbreviated),
Object.values(dayPeriods['stand-alone'].wide)
]);
const daysFormat = removeDuplicates([
Object.values(dayNames.format.narrow), Object.values(dayNames.format.abbreviated),
Object.values(dayNames.format.wide), Object.values(dayNames.format.short)
]);
const daysStandalone = removeDuplicates([
Object.values(dayNames['stand-alone'].narrow),
Object.values(dayNames['stand-alone'].abbreviated), Object.values(dayNames['stand-alone'].wide),
Object.values(dayNames['stand-alone'].short)
]);
const monthsFormat = removeDuplicates([
Object.values(monthNames.format.narrow), Object.values(monthNames.format.abbreviated),
Object.values(monthNames.format.wide)
]);
const monthsStandalone = removeDuplicates([
Object.values(monthNames['stand-alone'].narrow),
Object.values(monthNames['stand-alone'].abbreviated),
Object.values(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]),
...removeDuplicates([monthsFormat, monthsStandalone]), eras
];
return dateTimeTranslations;
}
/**
* Returns date, time and dateTime formats for a locale
* @returns [dateFormats, timeFormats, dateTimeFormats]
* each format: [ short, medium, long, full ]
*/
function getDateTimeFormats(localeData: CldrLocaleData) {
function getFormats(data: any) {
return removeDuplicates([
data.short._value || data.short, data.medium._value || data.medium,
data.long._value || data.long, data.full._value || data.full
]);
}
const dateFormats = localeData.main('dates/calendars/gregorian/dateFormats');
const timeFormats = localeData.main('dates/calendars/gregorian/timeFormats');
const dateTimeFormats = localeData.main('dates/calendars/gregorian/dateTimeFormats');
return [getFormats(dateFormats), getFormats(timeFormats), getFormats(dateTimeFormats)];
}
/**
* Returns the first day of the week, based on US week days
* @returns number
*/
function getFirstDayOfWeek(localeData: CldrLocaleData) {
// The `cldrjs` package does not provide proper types for `supplemental`. The
// types are part of the package but embedded incorrectly and not usable.
return WEEK_DAYS.indexOf((localeData as any).supplemental.weekData.firstDay());
}

View File

@ -1,42 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
*/
import {CldrLocaleData} from './cldr-data';
import {fileHeader} from './file-header';
import {BaseCurrencies} from './locale-base-currencies';
import {generateDayPeriodsSupplementalString} from './locale-extra-file';
import {generateBasicLocaleString} from './locale-file';
import {getPluralFunction} from './plural-function';
/**
* Generated the contents for the global locale file
*/
export function generateLocaleGlobalFile(
locale: string, localeData: CldrLocaleData, baseCurrencies: BaseCurrencies) {
const basicLocaleData = generateBasicLocaleString(locale, localeData, baseCurrencies);
const extraLocaleData = generateDayPeriodsSupplementalString(locale, localeData);
const data = basicLocaleData.replace(/\]$/, `, ${extraLocaleData}]`);
return `${fileHeader}
(function(global) {
global.ng = global.ng || {};
global.ng.common = global.ng.common || {};
global.ng.common.locales = global.ng.common.locales || {};
const u = undefined;
${getPluralFunction(locale, false)}
global.ng.common.locales['${normalizeLocale(locale)}'] = ${data};
})(typeof globalThis !== 'undefined' && globalThis || typeof global !== 'undefined' && global || typeof window !== 'undefined' && window);
`;
}
/**
* In Angular the locale is referenced by a "normalized" form.
*/
function normalizeLocale(locale: string): string {
return locale.toLowerCase().replace(/_/g, '-');
}

View File

@ -1,30 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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 UNDEFINED_PLACEHOLDER = `ɵɵUNDEFINED_PLACEHOLDER_JSON`;
const UNDEFINED_PLACEHOLDER_REGEX = new RegExp(`["']${UNDEFINED_PLACEHOLDER}["']`, 'g');
/**
* Stringifies the given object while preserving `undefined` values which would usually
* be transformed into `null` with JSON5. We want to preserve `undefined` because in generated
* JavaScript, the `undefined` values are separate from `null`, and `undefined` can be minified
* more efficiently. For example in arrays: `[, , someValue]`.
*
* Note that we do not use `JSON5` or similar formats where properties are not explicitly
* wrapped in quotes. Quotes are necessary so that Closure compiler does not accidentally
* rename properties. See an example where the currency symbols will be incorrect:
* https://closure-compiler.appspot.com/home#code%3D%252F%252F%2520%253D%253DClosureCompiler%253D%253D%250A%252F%252F%2520%2540output_file_name%2520default.js%250A%252F%252F%2520%2540compilation_level%2520ADVANCED_OPTIMIZATIONS%250A%252F%252F%2520%253D%253D%252FClosureCompiler%253D%253D%250A%250Aconst%2520base_currencies%2520%253D%2520%257B%250A%2520%2520ABC%253A%2520'd'%252C%250A%2509USD%253A%2520'x'%252C%250A%257D%253B%250A%250Aconst%2520current_locale_currencies%2520%253D%2520%257B%257D%250A%250Afunction%2520getCurrencySymbol(l)%2520%257B%250A%2520%2520return%2520current_locale_currencies%255Bl%255D%2520%257C%257C%2520base_currencies%255Bl%255D%2520%257C%257C%2520l%250A%257D%250A%250Aconsole.log(getCurrencySymbol('de'))%253B
*/
export function stringify(value: any) {
const result =
JSON.stringify(value, ((_, value) => value === undefined ? UNDEFINED_PLACEHOLDER : value));
UNDEFINED_PLACEHOLDER_REGEX.lastIndex = 0;
return result.replace(UNDEFINED_PLACEHOLDER_REGEX, 'undefined');
}

View File

@ -1,36 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
*/
// There are no types available for `cldr`.
const cldr = require('cldr');
/**
* 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
*/
export function getPluralFunction(locale: string, withTypes = true) {
let fn = cldr.extractPluralRuleFunction(locale).toString();
const numberType = withTypes ? ': number' : '';
fn =
fn.replace(/function anonymous\(n[^}]+{/g, `function plural(n${numberType})${numberType} {`)
// Since our generated plural functions only take numbers, we can eliminate some of
// the logic generated by the `cldr` package (to reduce payload size).
.replace(/var/g, /let/)
.replace(/if\s+\(typeof\s+n\s+===\s+["']string["']\)\s+n\s+=\s+parseInt\(n,\s+10\);/, '');
// 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(/["']zero["']/, '0')
.replace(/["']one["']/, '1')
.replace(/["']two["']/, '2')
.replace(/["']few["']/, '3')
.replace(/["']many["']/, '4')
.replace(/["']other["']/, '5');
}

43
tools/gulp-tasks/cldr.js Normal file
View File

@ -0,0 +1,43 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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 path = require('path');
const fs = require('fs');
module.exports = {
extract: gulp => done => {
if (!fs.existsSync(path.join(__dirname, 'cldr/cldr-data'))) {
throw new Error(`You must run "gulp cldr:download" before you can extract the data`);
}
const extract = require('./cldr/extract');
return extract(gulp, done);
},
download: gulp => done => {
const cldrDownloader = require('cldr-data-downloader');
const cldrDataFolder = path.join(__dirname, 'cldr/cldr-data');
if (fs.existsSync(cldrDataFolder)) {
fs.rmdirSync(cldrDataFolder, {recursive: true});
} else {
fs.mkdirSync(cldrDataFolder);
}
cldrDownloader(path.join(__dirname, 'cldr/cldr-urls.json'), cldrDataFolder, {}, done);
},
closure: gulp => done => {
const {RELATIVE_I18N_DATA_FOLDER} = require('./cldr/extract');
// tslint:disable-next-line:no-console
console.log(RELATIVE_I18N_DATA_FOLDER, fs.existsSync(RELATIVE_I18N_DATA_FOLDER));
if (!fs.existsSync(RELATIVE_I18N_DATA_FOLDER)) {
throw new Error(
`You must run "gulp cldr:extract" before you can create the closure-locale.ts file`);
}
const localeAll = require('./cldr/closure');
return localeAll(gulp, done);
},
};

View File

@ -0,0 +1,86 @@
// tslint:disable:file-header
/**
* Npm module for Unicode CLDR JSON data
*
* @license
* Copyright 2013 Rafael Xavier de Souza
* Released under the MIT license
* https://github.com/rxaviers/cldr-data-npm/blob/master/LICENSE-MIT
*/
'use strict';
const JSON_EXTENSION = /^(.*)\.json$/;
const assert = require('assert');
const _fs = require('fs');
const _path = require('path');
function argsToArray(arg) {
return [].slice.call(arg, 0);
}
function jsonFiles(dirName) {
const fileList = _fs.readdirSync(_path.join(__dirname, 'cldr-data', dirName));
return fileList.reduce(function(sum, file) {
if (JSON_EXTENSION.test(file)) {
return sum.concat(file.match(JSON_EXTENSION)[1]);
}
}, []);
}
function cldrData(path /*, ...*/) {
assert(
typeof path === 'string',
'must include path (e.g., "main/en/numbers" or "supplemental/likelySubtags")');
if (arguments.length > 1) {
return argsToArray(arguments).reduce(function(sum, path) {
sum.push(cldrData(path));
return sum;
}, []);
}
return require('./cldr-data/' + path);
}
function mainPathsFor(locales) {
return locales.reduce(function(sum, locale) {
const mainFiles = jsonFiles(_path.join('main', locale));
mainFiles.forEach(function(mainFile) {
sum.push(_path.join('main', locale, mainFile));
});
return sum;
}, []);
}
function supplementalPaths() {
const supplementalFiles = jsonFiles('supplemental');
return supplementalFiles.map(function(supplementalFile) {
return _path.join('supplemental', supplementalFile);
});
}
Object.defineProperty(cldrData, 'availableLocales', {
get: function() {
return cldrData('availableLocales').availableLocales;
}
});
cldrData.all = function() {
const paths = supplementalPaths().concat(mainPathsFor(this.availableLocales));
return cldrData.apply({}, paths);
};
cldrData.entireMainFor = function(locale /*, ...*/) {
assert(typeof locale === 'string', 'must include locale (e.g., "en")');
return cldrData.apply({}, mainPathsFor(argsToArray(arguments)));
};
cldrData.entireSupplemental = function() {
return cldrData.apply({}, supplementalPaths());
};
module.exports = cldrData;

View File

@ -0,0 +1,22 @@
{
"core": [
"https://github.com/unicode-cldr/cldr-core/archive/37.0.0.zip",
"https://github.com/unicode-cldr/cldr-segments-modern/archive/37.0.0.zip",
"https://github.com/unicode-cldr/cldr-dates-full/archive/37.0.0.zip",
"https://github.com/unicode-cldr/cldr-cal-buddhist-full/archive/37.0.0.zip",
"https://github.com/unicode-cldr/cldr-cal-chinese-full/archive/37.0.0.zip",
"https://github.com/unicode-cldr/cldr-cal-coptic-full/archive/37.0.0.zip",
"https://github.com/unicode-cldr/cldr-cal-dangi-full/archive/37.0.0.zip",
"https://github.com/unicode-cldr/cldr-cal-ethiopic-full/archive/37.0.0.zip",
"https://github.com/unicode-cldr/cldr-cal-hebrew-full/archive/37.0.0.zip",
"https://github.com/unicode-cldr/cldr-cal-indian-full/archive/37.0.0.zip",
"https://github.com/unicode-cldr/cldr-cal-islamic-full/archive/37.0.0.zip",
"https://github.com/unicode-cldr/cldr-cal-japanese-full/archive/37.0.0.zip",
"https://github.com/unicode-cldr/cldr-cal-persian-full/archive/37.0.0.zip",
"https://github.com/unicode-cldr/cldr-cal-roc-full/archive/37.0.0.zip",
"https://github.com/unicode-cldr/cldr-localenames-full/archive/37.0.0.zip",
"https://github.com/unicode-cldr/cldr-misc-full/archive/37.0.0.zip",
"https://github.com/unicode-cldr/cldr-numbers-full/archive/37.0.0.zip",
"https://github.com/unicode-cldr/cldr-units-full/archive/37.0.0.zip"
]
}

View File

@ -0,0 +1,163 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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 yargs = require('yargs').argv;
const shelljs = require('shelljs');
const {I18N_DATA_FOLDER, RELATIVE_I18N_DATA_FOLDER, HEADER} = require('./extract');
const OUTPUT_NAME = `closure-locale.ts`;
// tslint:disable:no-console
module.exports = (gulp, done) => {
// the locales used by closure that will be used to generate the closure-locale file
// extracted from:
// https://github.com/google/closure-library/blob/master/closure/goog/i18n/datetimepatterns.js#L2136
let GOOG_LOCALES = [
'af', 'am', 'ar', 'ar-DZ', 'az', 'be', 'bg', 'bn', 'br', 'bs',
'ca', 'chr', 'cs', 'cy', 'da', 'de', 'de-AT', 'de-CH', 'el', 'en-AU',
'en-CA', 'en-GB', 'en-IE', 'en-IN', 'en-SG', 'en-ZA', 'es', 'es-419', 'es-MX', 'es-US',
'et', 'eu', 'fa', 'fi', 'fr', 'fr-CA', 'ga', 'gl', 'gsw', 'gu',
'haw', 'hi', 'hr', 'hu', 'hy', 'in', 'is', 'it', 'iw', 'ja',
'ka', 'kk', 'km', 'kn', 'ko', 'ky', 'ln', 'lo', 'lt', 'lv',
'mk', 'ml', 'mn', 'mo', 'mr', 'ms', 'mt', 'my', 'ne', 'nl',
'no', 'or', 'pa', 'pl', 'pt', 'pt-PT', 'ro', 'ru', 'sh', 'si',
'sk', 'sl', 'sq', 'sr', 'sv', 'sw', 'ta', 'te', 'th', 'tl',
'tr', 'uk', 'ur', 'uz', 'vi', 'zh', 'zh-CN', 'zh-HK', 'zh-TW', 'zu'
];
// locale id aliases to support deprecated locale ids used by closure
// it maps deprecated ids --> new ids
// manually extracted from ./cldr-data/supplemental/aliases.json
const ALIASES = {
'in': 'id',
'iw': 'he',
'mo': 'ro-MD',
'no': 'nb',
'nb': 'no-NO',
'sh': 'sr-Latn',
'tl': 'fil',
'pt': 'pt-BR',
'zh-CN': 'zh-Hans-CN',
'zh-Hans-CN': 'zh-Hans',
'zh-HK': 'zh-Hant-HK',
'zh-TW': 'zh-Hant-TW',
'zh-Hant-TW': 'zh-Hant',
};
if (yargs.locales) {
GOOG_LOCALES = yargs.locales.split(',');
}
console.log(`Writing file ${I18N_DATA_FOLDER}/${OUTPUT_NAME}`);
fs.writeFileSync(
`${RELATIVE_I18N_DATA_FOLDER}/${OUTPUT_NAME}`, generateAllLocalesFile(GOOG_LOCALES, ALIASES));
console.log(`Formatting ${I18N_DATA_FOLDER}/${OUTPUT_NAME}..."`);
shelljs.exec(`yarn clang-format -i ${I18N_DATA_FOLDER}/${OUTPUT_NAME}`, {silent: true});
done();
};
/**
* Generate a file that contains all locale to import for closure.
* Tree shaking will only keep the data for the `goog.LOCALE` locale.
*/
function generateAllLocalesFile(LOCALES, ALIASES) {
const existingLocalesAliases = {};
const existingLocalesData = {};
// for each locale, get the data and the list of equivalent locales
LOCALES.forEach(locale => {
const eqLocales = new Set();
eqLocales.add(locale);
if (locale.match(/-/)) {
eqLocales.add(locale.replace(/-/g, '_'));
}
// check for aliases
const alias = ALIASES[locale];
if (alias) {
eqLocales.add(alias);
if (alias.match(/-/)) {
eqLocales.add(alias.replace(/-/g, '_'));
}
// to avoid duplicated "case" we regroup all locales in the same "case"
// the simplest way to do that is to have alias aliases
// e.g. 'no' --> 'nb', 'nb' --> 'no-NO'
// which means that we'll have 'no', 'nb' and 'no-NO' in the same "case"
const aliasKeys = Object.keys(ALIASES);
for (let i = 0; i < aliasKeys.length; i++) {
const aliasValue = ALIASES[alias];
if (aliasKeys.indexOf(alias) !== -1 && !eqLocales.has(aliasValue)) {
eqLocales.add(aliasValue);
if (aliasValue.match(/-/)) {
eqLocales.add(aliasValue.replace(/-/g, '_'));
}
}
}
}
for (let l of eqLocales) {
// find the existing content file
const path = `${RELATIVE_I18N_DATA_FOLDER}/${l}.ts`;
if (fs.existsSync(`${RELATIVE_I18N_DATA_FOLDER}/${l}.ts`)) {
const localeName = formatLocale(locale);
existingLocalesData[locale] =
fs.readFileSync(path, 'utf8')
.replace(`${HEADER}\n`, '')
.replace('export default ', `export const locale_${localeName} = `)
.replace('function plural', `function plural_${localeName}`)
.replace(/,(\n | )plural/, `, plural_${localeName}`)
.replace('const u = undefined;\n\n', '');
}
}
existingLocalesAliases[locale] = eqLocales;
});
function generateCases(locale) {
let str = '';
let locales = [];
const eqLocales = existingLocalesAliases[locale];
for (let l of eqLocales) {
str += `case '${l}':\n`;
locales.push(`'${l}'`);
}
let localesStr = '[' + locales.join(',') + ']';
str += ` l = locale_${formatLocale(locale)};
locales = ${localesStr};
break;\n`;
return str;
}
function formatLocale(locale) {
return locale.replace(/-/g, '_');
}
// clang-format off
return `${HEADER}
import {registerLocaleData} from '../src/i18n/locale_data';
const u = undefined;
${LOCALES.map(locale => `${existingLocalesData[locale]}`).join('\n')}
let l: any;
let locales: string[] = [];
switch (goog.LOCALE) {
${LOCALES.map(locale => generateCases(locale)).join('')}}
if(l) {
locales.forEach(locale => registerLocaleData(l, locale));
}
`;
// clang-format on
}

View File

@ -0,0 +1,632 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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');
const stringify = require('./util').stringify;
// used to extract plural rules
const cldr = require('cldr');
// used to extract all other cldr data
const cldrJs = require('cldrjs');
// used to call to clang-format
const shelljs = require('shelljs');
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`;
const I18N_DATA_EXTRA_FOLDER = `${I18N_DATA_FOLDER}/extra`;
const I18N_GLOBAL_FOLDER = `${I18N_DATA_FOLDER}/global`;
const RELATIVE_I18N_FOLDER = path.resolve(__dirname, `../../../${I18N_FOLDER}`);
const RELATIVE_I18N_CORE_FOLDER = path.resolve(__dirname, `../../../${I18N_CORE_FOLDER}`);
const RELATIVE_I18N_DATA_FOLDER = path.resolve(__dirname, `../../../${I18N_DATA_FOLDER}`);
const RELATIVE_I18N_DATA_EXTRA_FOLDER =
path.resolve(__dirname, `../../../${I18N_DATA_EXTRA_FOLDER}`);
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}';
const WEEK_DAYS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
const HEADER = `/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* 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
`;
// tslint:disable:no-console
module.exports = (gulp, done) => {
const cldrData = require('./cldr-data');
const LOCALES = cldrData.availableLocales;
console.log(`Loading CLDR data...`);
cldrJs.load(cldrData.all().concat(cldrData('scriptMetadata')));
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);
}
if (!fs.existsSync(RELATIVE_I18N_GLOBAL_FOLDER)) {
fs.mkdirSync(RELATIVE_I18N_GLOBAL_FOLDER);
}
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
console.log(`Writing file ${I18N_CORE_FOLDER}/locale_en.ts`);
const localeEnFile = generateLocale('en', new cldrJs('en'), baseCurrencies);
fs.writeFileSync(`${RELATIVE_I18N_CORE_FOLDER}/locale_en.ts`, localeEnFile);
LOCALES.forEach((locale, index) => {
const localeData = new cldrJs(locale);
console.log(`${index + 1}/${LOCALES.length}`);
console.log(`\t${I18N_DATA_FOLDER}/${locale}.ts`);
fs.writeFileSync(
`${RELATIVE_I18N_DATA_FOLDER}/${locale}.ts`,
locale === 'en' ? localeEnFile : generateLocale(locale, localeData, baseCurrencies));
console.log(`\t${I18N_DATA_EXTRA_FOLDER}/${locale}.ts`);
fs.writeFileSync(
`${RELATIVE_I18N_DATA_EXTRA_FOLDER}/${locale}.ts`, generateLocaleExtra(locale, localeData));
console.log(`\t${I18N_GLOBAL_FOLDER}/${locale}.js`);
fs.writeFileSync(
`${RELATIVE_I18N_GLOBAL_FOLDER}/${locale}.js`,
generateGlobalLocale(
locale, locale === 'en' ? new cldrJs('en') : localeData, baseCurrencies));
});
console.log(`${LOCALES.length} locale files generated.`);
console.log(`All i18n cldr files have been generated, formatting files..."`);
shelljs.exec(
`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});
done();
};
/**
* Generate contents for the basic locale data file
*/
function generateLocale(locale, localeData, baseCurrencies) {
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 || {};
const u = undefined;
${getPluralFunction(locale, false)}
global.ng.common.locales['${normalizeLocale(locale)}'] = ${data};
})(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) {
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');
// adding plural function after, because we don't want it as a string
data = data.replace(/\]$/, ', plural]');
return data;
}
/**
* Collect up the day period rules, and extended day period data.
*/
function generateDayPeriodsSupplementalString(locale, localeData) {
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([
objectValues(dayPeriods.format.narrow), objectValues(dayPeriods.format.abbreviated),
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];
}
return stringify(dayPeriodsSupplemental).replace(/undefined/g, 'u');
}
/**
* Generate a list of currencies to be used as a based for other currencies
* e.g.: {'ARS': [, '$'], 'AUD': ['A$', '$'], ...}
*/
function generateBaseCurrencies(localeData, addDigits) {
const currenciesData = localeData.main('numbers/currencies');
const fractions = new cldrJs('en').get(`supplemental/currencyData/fractions`);
const currencies = {};
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 {
symbolsArray = [undefined, symbolNarrow];
}
}
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) {
symbolsArray = [...symbolsArray, undefined, digits];
} else {
symbolsArray = [undefined, undefined, digits];
}
}
if (symbolsArray.length > 0) {
currencies[key] = symbolsArray;
}
});
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 {
symbolsArray = [undefined, symbolNarrow];
}
}
// 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);
return `${HEADER}
export type CurrenciesSymbols = [string] | [string | undefined, string];
/** @internal */
export const CURRENCIES_EN: {[code: string]: CurrenciesSymbols | [string | undefined, string | undefined, number]} = ${
stringify(baseCurrencies, true)};
`;
}
/**
* Returns data for the chosen day periods
* @returns {
* format: {narrow / abbreviated / wide: [...]},
* stand-alone: {narrow / abbreviated / wide: [...]}
* }
*/
function getDayPeriods(localeData, dayPeriodsList) {
const dayPeriods = localeData.main(`dates/calendars/gregorian/dayPeriods`);
const result = {};
// cleaning up unused keys
Object.keys(dayPeriods).forEach(key1 => { // format / stand-alone
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) {
return getDayPeriods(localeData, [
'noon', 'midnight', 'morning1', 'morning2', 'afternoon1', 'afternoon2', 'evening1', 'evening2',
'night1', 'night2'
]);
}
/**
* Returns date-related translations for a locale
* @returns [ dayPeriodsFormat, dayPeriodsStandalone, daysFormat, dayStandalone, monthsFormat,
* monthsStandalone, eras ]
* 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([
objectValues(dayPeriods.format.narrow), objectValues(dayPeriods.format.abbreviated),
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([
objectValues(dayNames.format.narrow), objectValues(dayNames.format.abbreviated),
objectValues(dayNames.format.wide), objectValues(dayNames.format.short)
]);
const daysStandalone = removeDuplicates([
objectValues(dayNames['stand-alone'].narrow), objectValues(dayNames['stand-alone'].abbreviated),
objectValues(dayNames['stand-alone'].wide), objectValues(dayNames['stand-alone'].short)
]);
const monthsFormat = removeDuplicates([
objectValues(monthNames.format.narrow), objectValues(monthNames.format.abbreviated),
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]),
...removeDuplicates([monthsFormat, monthsStandalone]), eras
];
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([
data.short._value || data.short, data.medium._value || data.medium,
data.long._value || data.long, data.full._value || data.full
]);
}
const dateFormats = localeData.main('dates/calendars/gregorian/dateFormats');
const timeFormats = localeData.main('dates/calendars/gregorian/timeFormats');
const dateTimeFormats = localeData.main('dates/calendars/gregorian/dateTimeFormats');
return [getFormats(dateFormats), getFormats(timeFormats), getFormats(dateTimeFormats)];
}
/**
* Returns day period rules for a locale
* @returns string[]
*/
function getDayPeriodRules(localeData) {
const dayPeriodRules =
localeData.get(`supplemental/dayPeriodRuleSet/${localeData.attributes.language}`);
const rules = {};
if (dayPeriodRules) {
Object.keys(dayPeriodRules).forEach(key => {
if (dayPeriodRules[key]._at) {
rules[key] = dayPeriodRules[key]._at;
} else {
rules[key] = [dayPeriodRules[key]._from, dayPeriodRules[key]._before];
}
});
}
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 =
localeData.get(`supplemental/weekData/weekendStart/${localeData.attributes.territory}`) ||
localeData.get('supplemental/weekData/weekendStart/001');
const endDay =
localeData.get(`supplemental/weekData/weekendEnd/${localeData.attributes.territory}`) ||
localeData.get('supplemental/weekData/weekendEnd/001');
return [WEEK_DAYS.indexOf(startDay), WEEK_DAYS.indexOf(endDay)];
}
/**
* Returns dateTime data for a locale
* @returns [ firstDayOfWeek, weekendRange, formats ]
*/
function getDateTimeSettings(localeData) {
return [
getFirstDayOfWeek(localeData), getWeekendRange(localeData), ...getDateTimeFormats(localeData)
];
}
/**
* Returns the number symbols and formats for a locale
* @returns [ symbols, formats ]
* symbols: [ decimal, group, list, percentSign, plusSign, minusSign, exponential,
* superscriptingExponent, perMille, infinity, nan, timeSeparator, currencyDecimal?, currencyGroup?
* ]
* 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,
];
if (symbols.currencyDecimal || symbols.currencyGroup) {
symbolValues.push(symbols.currencyDecimal);
}
if (symbols.currencyGroup) {
symbolValues.push(symbols.currencyGroup);
}
return [symbolValues, [decimalFormat, percentFormat, currencyFormat, scientificFormat]];
}
/**
* Returns the currency code, symbol and name for a locale
* @returns [ code, symbol, name ]
*/
function getCurrencySettings(locale, localeData) {
const currencyInfo = localeData.main(`numbers/currencies`);
let currentCurrency = '';
// find the currency currently used in this country
const currencies =
localeData.get(`supplemental/currencyData/region/${localeData.attributes.territory}`) ||
localeData.get(
`supplemental/currencyData/region/${localeData.attributes.language.toUpperCase()}`);
if (currencies) {
currencies.some(currency => {
const keys = Object.keys(currency);
return keys.some(key => {
if (currency[key]._from && !currency[key]._to) {
return currentCurrency = key;
}
});
});
if (!currentCurrency) {
throw new Error(`Unable to find currency for locale "${locale}"`);
}
}
let currencySettings = [undefined, undefined, undefined];
if (currentCurrency) {
currencySettings = [
currentCurrency, currencyInfo[currentCurrency].symbol,
currencyInfo[currentCurrency].displayName
];
}
return currencySettings;
}
/**
* 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';
}
/**
* 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
*/
function getPluralFunction(locale, withTypes = true) {
let fn = cldr.extractPluralRuleFunction(locale).toString();
if (fn === EMPTY_RULE) {
fn = DEFAULT_RULE;
}
const numberType = withTypes ? ': number' : '';
fn = fn.replace(/function anonymous\(n[^}]+{/g, `function plural(n${numberType})${numberType} {`)
.replace(toRegExp('var'), 'let')
.replace(toRegExp('if(typeof n==="string")n=parseInt(n,10);'), '')
.replace(toRegExp('\n}'), ';\n}');
// 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');
}
/**
* 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).
*
* For further size improvements, "undefined" values will be replaced by a constant in the arrays
* as the last step of the file generation (in generateLocale and generateLocaleExtra).
* e.g.: [x, y, undefined, z, undefined, undefined] will be [x, y, u, z, u, u]
*/
function removeDuplicates(data) {
const dedup = [data[0]];
for (let i = 1; i < data.length; i++) {
if (stringify(data[i]) !== stringify(data[i - 1])) {
dedup.push(data[i]);
} else {
dedup.push(undefined);
}
}
return dedup;
}
/**
* In Angular the locale is referenced by a "normalized" form.
*/
function normalizeLocale(locale) {
return locale.toLowerCase().replace(/_/g, '-');
}
module.exports.I18N_FOLDER = I18N_FOLDER;
module.exports.I18N_DATA_FOLDER = I18N_DATA_FOLDER;
module.exports.RELATIVE_I18N_DATA_FOLDER = RELATIVE_I18N_DATA_FOLDER;
module.exports.HEADER = HEADER;

View File

@ -0,0 +1,182 @@
// tslint:disable:file-header
/**
* Like JSON.stringify, but without double quotes around keys, and without null instead of undefined
* values
* Based on https://github.com/json5/json5/blob/master/lib/json5.js
* Use option "quoteKeys" to preserve quotes for keys
*/
module.exports.stringify = function(obj, quoteKeys) {
var getReplacedValueOrUndefined = function(holder, key) {
var value = holder[key];
// Replace the value with its toJSON value first, if possible
if (value && value.toJSON && typeof value.toJSON === 'function') {
value = value.toJSON();
}
return value;
};
function isWordChar(c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') ||
c === '_' || c === '$';
}
function isWordStart(c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c === '_' || c === '$';
}
function isWord(key) {
if (typeof key !== 'string') {
return false;
}
if (!isWordStart(key[0])) {
return false;
}
var i = 1, length = key.length;
while (i < length) {
if (!isWordChar(key[i])) {
return false;
}
i++;
}
return true;
}
// polyfills
function isArray(obj) {
if (Array.isArray) {
return Array.isArray(obj);
} else {
return Object.prototype.toString.call(obj) === '[object Array]';
}
}
function isDate(obj) {
return Object.prototype.toString.call(obj) === '[object Date]';
}
var objStack = [];
function checkForCircular(obj) {
for (var i = 0; i < objStack.length; i++) {
if (objStack[i] === obj) {
throw new TypeError('Converting circular structure to JSON');
}
}
}
// Copied from Crokford's implementation of JSON
// See
// https://github.com/douglascrockford/JSON-js/blob/e39db4b7e6249f04a195e7dd0840e610cc9e941e/json2.js#L195
// Begin
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
};
function escapeString(str, keepQuotes) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapable.lastIndex = 0;
return escapable.test(str) && !keepQuotes ? '"' + str.replace(escapable, function(a) {
var c = meta[a];
return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' : '"' + str + '"';
}
// End
function internalStringify(holder, key) {
var buffer, res;
// Replace the value, if necessary
var obj_part = getReplacedValueOrUndefined(holder, key);
if (obj_part && !isDate(obj_part)) {
// unbox objects
// don't unbox dates, since will turn it into number
obj_part = obj_part.valueOf();
}
switch (typeof obj_part) {
case 'boolean':
return obj_part.toString();
case 'number':
if (isNaN(obj_part) || !isFinite(obj_part)) {
return 'null';
}
return obj_part.toString();
case 'string':
return escapeString(obj_part.toString());
case 'object':
if (obj_part === null) {
return 'null';
} else if (isArray(obj_part)) {
checkForCircular(obj_part);
buffer = '[';
objStack.push(obj_part);
for (var i = 0; i < obj_part.length; i++) {
res = internalStringify(obj_part, i);
if (res === null) {
buffer += 'null';
} /* else if (typeof res === 'undefined') { // modified to support empty array values
buffer += '';
}*/
else {
buffer += res;
}
if (i < obj_part.length - 1) {
buffer += ',';
}
}
objStack.pop();
buffer += ']';
} else {
checkForCircular(obj_part);
buffer = '{';
var nonEmpty = false;
objStack.push(obj_part);
for (var prop in obj_part) {
if (obj_part.hasOwnProperty(prop)) {
var value = internalStringify(obj_part, prop);
if (typeof value !== 'undefined' && value !== null) {
nonEmpty = true;
key = isWord(prop) && !quoteKeys ? prop : escapeString(prop, quoteKeys);
buffer += key + ':' + value + ',';
}
}
}
objStack.pop();
if (nonEmpty) {
buffer = buffer.substring(0, buffer.length - 1) + '}';
} else {
buffer = '{}';
}
}
return buffer;
default:
// functions and undefined should be ignored
return undefined;
}
}
// special case...when undefined is used inside of
// a compound object/array, return null.
// but when top-level, return undefined
var topLevelHolder = {'': obj};
if (obj === undefined) {
return getReplacedValueOrUndefined(topLevelHolder, '');
}
return internalStringify(topLevelHolder, '');
};

186
yarn.lock
View File

@ -1620,11 +1620,6 @@
resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.35.tgz#3964c48372bf62d60616d8673dd77a9719ebac9b"
integrity sha512-2WeeXK7BuQo7yPI4WGOBum90SzF/f8rqlvpaXx4rjeTmNssGRDHWf7fgDUH90xMB3sUOu716fUK5d+OVx0+ncQ==
"@types/cldrjs@^0.4.22":
version "0.4.22"
resolved "https://registry.yarnpkg.com/@types/cldrjs/-/cldrjs-0.4.22.tgz#24e31cdf15a4ea806ca0a774b024150d1066fea1"
integrity sha512-YyzxXZ5s9xwPWznXnI3++X14JGnomDdDAlin7kWZvxX/MzirC9BNFcDSQ0yR8YG2M/xNMn0nXsCGkgbFVyXjGw==
"@types/cli-progress@^3.4.2":
version "3.9.1"
resolved "https://registry.yarnpkg.com/@types/cli-progress/-/cli-progress-3.9.1.tgz#285e7fbdad6e7baf072d163ae1c3b23b7b219130"
@ -2137,6 +2132,11 @@ adjust-sourcemap-loader@^4.0.0:
loader-utils "^2.0.0"
regex-parser "^2.2.11"
adm-zip@0.4.11:
version "0.4.11"
resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.11.tgz#2aa54c84c4b01a9d0fb89bb11982a51f13e3d62a"
integrity sha512-L8vcjDTCOIJk7wFvmlEUN7AsSb8T+2JrdP7KINBjzr24TJ5Mwj590sLu3BC7zNZowvJWa/JtPmD8eJCzdtDWjA==
adm-zip@^0.4.9, adm-zip@~0.4.3:
version "0.4.16"
resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz#cf4c508fdffab02c269cbc7f471a875f05570365"
@ -2210,6 +2210,16 @@ ajv@8.2.0:
require-from-string "^2.0.2"
uri-js "^4.2.2"
ajv@^5.1.0:
version "5.5.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=
dependencies:
co "^4.6.0"
fast-deep-equal "^1.0.0"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0"
ajv@^6.1.0, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
@ -2714,7 +2724,7 @@ aws-sign2@~0.7.0:
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
aws4@^1.8.0:
aws4@^1.6.0, aws4@^1.8.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
@ -3501,20 +3511,33 @@ class-utils@^0.3.5:
isobject "^3.0.0"
static-extend "^0.1.1"
cldr@5.7.0:
version "5.7.0"
resolved "https://registry.yarnpkg.com/cldr/-/cldr-5.7.0.tgz#8c466bdc2500fd293462029631011adcd55bc5ae"
integrity sha512-Pyoh0kwXJIUvbAvQoQqKIr0pKWwWfkcYCIDKWmVIxJ9HftSsg57AqyfW1EzWRcP4nJj40WX4vB/lXQ+Uw4NbNA==
cldr-data-downloader@^0.3.5:
version "0.3.5"
resolved "https://registry.yarnpkg.com/cldr-data-downloader/-/cldr-data-downloader-0.3.5.tgz#f5445cb9d222bf7fa8426c62e0ae9d7d4897b243"
integrity sha512-uyIMa1K98DAp/PE7dYpq2COIrkWn681Atjng1GgEzeJzYb1jANtugtp9wre6+voE+qzVC8jtWv6E/xZ1GTJdlw==
dependencies:
escodegen "^1.12.0"
adm-zip "0.4.11"
mkdirp "0.5.0"
nopt "3.0.x"
progress "1.1.8"
q "1.0.1"
request "~2.87.0"
request-progress "0.3.1"
cldr@7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/cldr/-/cldr-7.0.0.tgz#8e0f42acc21b5762842cf23a747120be898447fe"
integrity sha512-BmD93+RhHGkdVRO9LYL6kd7IA9ANxnpH1A+OM6FdhKVYXqRgBaDmt9P83VaQB6gMBaFvOl4IozW/g3ViLn9LeQ==
dependencies:
escodegen "^2.0.0"
esprima "^4.0.1"
memoizeasync "^1.1.0"
passerror "^1.1.1"
pegjs "^0.10.0"
seq "^0.3.5"
unicoderegexp "^0.4.1"
xmldom "^0.3.0"
xpath "^0.0.27"
xmldom "^0.6.0"
xpath "^0.0.32"
cldrjs@0.5.5:
version "0.5.5"
@ -3668,6 +3691,11 @@ cloneable-readable@^1.0.0:
process-nextick-args "^2.0.0"
readable-stream "^2.3.5"
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
@ -3768,7 +3796,7 @@ colorspace@1.1.x:
color "3.0.x"
text-hex "1.0.x"
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.5, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
@ -5235,7 +5263,7 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
escodegen@^1.12.0, escodegen@^1.8.1:
escodegen@^1.8.1:
version "1.14.3"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503"
integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==
@ -5502,7 +5530,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2:
assign-symbols "^1.0.0"
is-extendable "^1.0.1"
extend@^3.0.0, extend@^3.0.2, extend@~3.0.2:
extend@^3.0.0, extend@^3.0.2, extend@~3.0.1, extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
@ -5561,6 +5589,11 @@ fancy-log@^1.3.2:
parse-node-version "^1.0.0"
time-stamp "^1.0.0"
fast-deep-equal@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=
fast-deep-equal@^3.1.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@ -5939,7 +5972,7 @@ form-data@^3.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
form-data@~2.3.2:
form-data@~2.3.1, form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
@ -6618,6 +6651,14 @@ har-schema@^2.0.0:
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
har-validator@~5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd"
integrity sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=
dependencies:
ajv "^5.1.0"
har-schema "^2.0.0"
har-validator@~5.1.3:
version "5.1.5"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd"
@ -7861,6 +7902,11 @@ json-ptr@^2.2.0:
dependencies:
tslib "^2.2.0"
json-schema-traverse@^0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
@ -8987,6 +9033,11 @@ minimist-options@4.1.0:
is-plain-obj "^1.1.0"
kind-of "^6.0.3"
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
minimist@1.x, minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
@ -9087,6 +9138,13 @@ mkdirp-classic@^0.5.2:
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
mkdirp@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12"
integrity sha1-HXMHam35hs2TROFecfzAWkyavxI=
dependencies:
minimist "0.0.8"
"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
@ -9350,6 +9408,13 @@ nodejs-websocket@^1.7.2:
resolved "https://registry.yarnpkg.com/nodejs-websocket/-/nodejs-websocket-1.7.2.tgz#94abd1e248f57d4d1c663dec3831015c6dad98a6"
integrity sha512-PFX6ypJcCNDs7obRellR0DGTebfUhw1SXGKe2zpB+Ng1DQJhdzbzx1ob+AvJCLzy2TJF4r8cCDqMQqei1CZdPQ==
nopt@3.0.x:
version "3.0.6"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k=
dependencies:
abbrev "1"
nopt@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
@ -9510,6 +9575,11 @@ number-is-nan@^1.0.0:
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
oauth-sign@~0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
integrity sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=
oauth-sign@~0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
@ -10895,6 +10965,11 @@ process-nextick-args@~1.0.6:
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=
progress@1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=
progress@^2.0.1, progress@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
@ -11061,7 +11136,7 @@ punycode@1.3.2:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
punycode@^1.3.2:
punycode@^1.3.2, punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
@ -11096,6 +11171,11 @@ puppeteer@5.4.1:
unbzip2-stream "^1.3.3"
ws "^7.2.3"
q@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.0.1.tgz#11872aeedee89268110b10a718448ffb10112a14"
integrity sha1-EYcq7t7okmgRCxCnGESP+xARKhQ=
q@1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
@ -11123,7 +11203,7 @@ qs@^6.4.0, qs@^6.6.0:
dependencies:
side-channel "^1.0.4"
qs@~6.5.2:
qs@~6.5.1, qs@~6.5.2:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
@ -11513,6 +11593,13 @@ replace-homedir@^1.0.0:
is-absolute "^1.0.0"
remove-trailing-separator "^1.1.0"
request-progress@0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-0.3.1.tgz#0721c105d8a96ac6b2ce8b2c89ae2d5ecfcf6b3a"
integrity sha1-ByHBBdipasayzossia4tXs/Pazo=
dependencies:
throttleit "~0.0.2"
request@^2.87.0, request@^2.88.2:
version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
@ -11539,6 +11626,32 @@ request@^2.87.0, request@^2.88.2:
tunnel-agent "^0.6.0"
uuid "^3.3.2"
request@~2.87.0:
version "2.87.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e"
integrity sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.6.0"
caseless "~0.12.0"
combined-stream "~1.0.5"
extend "~3.0.1"
forever-agent "~0.6.1"
form-data "~2.3.1"
har-validator "~5.0.3"
http-signature "~1.2.0"
is-typedarray "~1.0.0"
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.17"
oauth-sign "~0.8.2"
performance-now "^2.1.0"
qs "~6.5.1"
safe-buffer "^5.1.1"
tough-cookie "~2.3.3"
tunnel-agent "^0.6.0"
uuid "^3.1.0"
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
@ -11848,7 +11961,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@ -11900,6 +12013,7 @@ sauce-connect-launcher@^1.2.4:
"sauce-connect@https://saucelabs.com/downloads/sc-4.6.2-linux.tar.gz":
version "0.0.0"
uid "7b7f35433af9c3380758e048894d7b9aecf3754e"
resolved "https://saucelabs.com/downloads/sc-4.6.2-linux.tar.gz#7b7f35433af9c3380758e048894d7b9aecf3754e"
saucelabs@^1.5.0:
@ -13009,6 +13123,11 @@ text-table@0.2.0:
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
throttleit@~0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-0.0.2.tgz#cfedf88e60c00dd9697b61fdd2a8343a9b680eaf"
integrity sha1-z+34jmDADdlpe2H90qg0OptoDq8=
through2-filter@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254"
@ -13156,6 +13275,13 @@ toidentifier@1.0.0:
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
tough-cookie@~2.3.3:
version "2.3.4"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655"
integrity sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==
dependencies:
punycode "^1.4.1"
tough-cookie@~2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
@ -13728,7 +13854,7 @@ uuid@8.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
uuid@^3.0.0, uuid@^3.3.2, uuid@^3.4.0:
uuid@^3.0.0, uuid@^3.1.0, uuid@^3.3.2, uuid@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
@ -14264,25 +14390,25 @@ xmlbuilder@~11.0.0:
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
xmldom@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.3.0.tgz#e625457f4300b5df9c2e1ecb776147ece47f3e5a"
integrity sha512-z9s6k3wxE+aZHgXYxSTpGDo7BYOUfJsIRyoZiX6HTjwpwfS2wpQBQKa2fD+ShLyPkqDYo5ud7KitmLZ2Cd6r0g==
xmldom@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.5.0.tgz#193cb96b84aa3486127ea6272c4596354cb4962e"
integrity sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA==
xmldom@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.6.0.tgz#43a96ecb8beece991cef382c08397d82d4d0c46f"
integrity sha512-iAcin401y58LckRZ0TkI4k0VSM1Qg0KGSc3i8rU+xrxe19A/BN1zHyVSJY7uoutVlaTSzYyk/v5AmkewAP7jtg==
xmlhttprequest-ssl@~1.5.4:
version "1.5.5"
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=
xpath@^0.0.27:
version "0.0.27"
resolved "https://registry.yarnpkg.com/xpath/-/xpath-0.0.27.tgz#dd3421fbdcc5646ac32c48531b4d7e9d0c2cfa92"
integrity sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==
xpath@^0.0.32:
version "0.0.32"
resolved "https://registry.yarnpkg.com/xpath/-/xpath-0.0.32.tgz#1b73d3351af736e17ec078d6da4b8175405c48af"
integrity sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==
xregexp@2.0.0:
version "2.0.0"