Files
Paul Gschwendtner b280b97d5a refactor(common): include locale extra data in closure locale file (#46167)
The recent update to CLDR 41 highlighted (internally) that some time
formats have changed to rely on locale-specific day periods. In
particular the `zh_TW` (or canonical: `zh_Hant`) has changed some
time formats/patterns from the universal `AM/PM` symbols (`a`) to `B`.

The `b`/`B` symbols rely on locale-specific day period rules. This data
is only extracted from CLDR into the so-called extra locale data that
Angular provides.

To fix this, and to be able to reverse the internal workaround that
doesn't allow us to use the actual CLDR 41 data here, we always provide
the locale extra data when conditonally loading CLDR data based on
`goog.LOCALE`.

This will result in additional payload cost that locale-specific JS
bundles have to pay, but the only alternative would be to break
Angular's `b`/`B` symbol support in favor of falling back to `AM/PM`
rules (similar to how Closure Libray itself does it).

For our external users (something I want to note here), they will be
able to load the extra data easily (like it was possible before).
Angular has some good erroring if the extra data is required. Most of
our users will always use the global locale files anyway (the Angular
CLI always uses them). These come with the extra data by default.

Resources:

- https://github.com/unicode-org/cldr/commit/0d538327d1ea2a31d1614d8989007b97a3ee5021
- https://github.com/unicode-org/cldr/blob/0d538327d1ea2a31d1614d8989007b97a3ee5021/common/main/zh_Hant.xml
- https://github.com/angular/angular/blob/6a4353cb40227330be2a5685adca485a8b8e8a0b/packages/common/src/i18n/format_date.ts#L609-L639
- https://cldr.unicode.org/translation/date-time/date-time-symbols
- https://cs.github.com/angular/angular-cli/blob/4be7cdce8283a177b92594bb7b34b4718d0a39c3/packages/angular_devkit/build_angular/src/utils/i18n-options.ts?q=%22locales%2Fglobal%22#L23

PR Close #46167
2022-06-07 10:14:14 -07:00

83 lines
3.3 KiB
TypeScript

/**
* @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 ${generateLocaleExtraDataArrayCode(locale, localeData)};
`;
}
/**
* Generates the "extra" locale data array (in JS-code as a string) for the given locale.
*
* The array follows the data and indices as specified in the `ExtraLocaleDataIndex`
* enum from `packages/core/src/i18n/locale_data_api.ts`.
*
* Extra data currently consists of day period names and rules. The non-extra locale
* data by default only contains the universal `AM/PM` day period names.
*
* NOTE: Instances of `undefined` in the array have been replaced with the `u` identifier.
* This identifier is used to shorten the generated code of unprocessed locale files.
*/
export function generateLocaleExtraDataArrayCode(locale: string, localeData: CldrLocaleData) {
const dayPeriods = getDayPeriodsNoAmPm(localeData);
const dayPeriodRules = getDayPeriodRules(localeData);
// The JSON data for some locales may include `dayPeriods` for which no rule is defined in
// `dayPeriodRules`. Ignore `dayPeriods` keys that lack a corresponding rule.
//
// As of CLDR v41, `hi-Latn` is the only locale that is affected by this issue and it is currently
// not clear whether it is a bug on intended behavior. This is being tracked in
// https://unicode-org.atlassian.net/browse/CLDR-15563.
//
// TODO(gkalpak): If this turns out to be a bug and is fixed in CLDR, restore the previous logic
// of expecting the exact same keys in `dayPeriods` and `dayPeriodRules`.
const dayPeriodKeys =
Object.keys(dayPeriods.format.narrow).filter(key => dayPeriodRules.hasOwnProperty(key));
let dayPeriodsSupplemental: any[] = [];
if (dayPeriodKeys.length) {
if (dayPeriodKeys.length !== Object.keys(dayPeriodRules).length) {
throw new Error(`Error: locale ${locale} has an incorrect number of day period rules`);
}
const dayPeriodsFormat = removeDuplicates([
getValuesForKeys(dayPeriodKeys, dayPeriods.format.narrow),
getValuesForKeys(dayPeriodKeys, dayPeriods.format.abbreviated),
getValuesForKeys(dayPeriodKeys, dayPeriods.format.wide),
]);
const dayPeriodsStandalone = removeDuplicates([
getValuesForKeys(dayPeriodKeys, dayPeriods['stand-alone'].narrow),
getValuesForKeys(dayPeriodKeys, dayPeriods['stand-alone'].abbreviated),
getValuesForKeys(dayPeriodKeys, dayPeriods['stand-alone'].wide),
]);
const rules = getValuesForKeys(dayPeriodKeys, dayPeriodRules);
dayPeriodsSupplemental = [...removeDuplicates([dayPeriodsFormat, dayPeriodsStandalone]), rules];
}
return stringify(dayPeriodsSupplemental).replace(/undefined/g, 'u');
}
function getValuesForKeys<T>(keys: string[], obj: Record<string, T>): T[] {
return keys.map(key => obj[key]);
}