Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

@formatjs/intl-localematcher

Package Overview
Dependencies
Maintainers
3
Versions
60
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@formatjs/intl-localematcher - npm Package Compare versions

Comparing version
0.7.4
to
0.7.5
+4
-4
abstract/BestAvailableLocale.d.ts
/**
* https://tc39.es/ecma402/#sec-bestavailablelocale
* @param availableLocales
* @param locale
*/
* https://tc39.es/ecma402/#sec-bestavailablelocale
* @param availableLocales
* @param locale
*/
export declare function BestAvailableLocale(availableLocales: readonly string[], locale: string): string | undefined;
// Cache for Set conversions to avoid repeated array->Set conversions
var availableLocalesSetCache = new WeakMap();
const availableLocalesSetCache = new WeakMap();
/**
* https://tc39.es/ecma402/#sec-bestavailablelocale
* @param availableLocales
* @param locale
*/
* https://tc39.es/ecma402/#sec-bestavailablelocale
* @param availableLocales
* @param locale
*/
export function BestAvailableLocale(availableLocales, locale) {
// Fast path: use Set for O(1) lookups instead of O(n) indexOf
var availableSet = availableLocalesSetCache.get(availableLocales);
if (!availableSet) {
availableSet = new Set(availableLocales);
availableLocalesSetCache.set(availableLocales, availableSet);
}
var candidate = locale;
while (true) {
if (availableSet.has(candidate)) {
return candidate;
}
var pos = candidate.lastIndexOf('-');
if (!~pos) {
return undefined;
}
if (pos >= 2 && candidate[pos - 2] === '-') {
pos -= 2;
}
candidate = candidate.slice(0, pos);
}
// Fast path: use Set for O(1) lookups instead of O(n) indexOf
let availableSet = availableLocalesSetCache.get(availableLocales);
if (!availableSet) {
availableSet = new Set(availableLocales);
availableLocalesSetCache.set(availableLocales, availableSet);
}
let candidate = locale;
while (true) {
if (availableSet.has(candidate)) {
return candidate;
}
let pos = candidate.lastIndexOf("-");
if (!~pos) {
return undefined;
}
if (pos >= 2 && candidate[pos - 2] === "-") {
pos -= 2;
}
candidate = candidate.slice(0, pos);
}
}

@@ -1,8 +0,8 @@

import { LookupMatcherResult } from './types.js';
import type { LookupMatcherResult } from "./types.js";
/**
* https://tc39.es/ecma402/#sec-bestfitmatcher
* @param availableLocales
* @param requestedLocales
* @param getDefaultLocale
*/
* https://tc39.es/ecma402/#sec-bestfitmatcher
* @param availableLocales
* @param requestedLocales
* @param getDefaultLocale
*/
export declare function BestFitMatcher(availableLocales: readonly string[], requestedLocales: readonly string[], getDefaultLocale: () => string): LookupMatcherResult;

@@ -1,31 +0,30 @@

import { UNICODE_EXTENSION_SEQUENCE_REGEX, findBestMatch } from './utils.js';
import { UNICODE_EXTENSION_SEQUENCE_REGEX, findBestMatch } from "./utils.js";
/**
* https://tc39.es/ecma402/#sec-bestfitmatcher
* @param availableLocales
* @param requestedLocales
* @param getDefaultLocale
*/
* https://tc39.es/ecma402/#sec-bestfitmatcher
* @param availableLocales
* @param requestedLocales
* @param getDefaultLocale
*/
export function BestFitMatcher(availableLocales, requestedLocales, getDefaultLocale) {
var foundLocale;
var extension;
var noExtensionLocales = [];
var noExtensionLocaleMap = requestedLocales.reduce(function (all, l) {
var noExtensionLocale = l.replace(UNICODE_EXTENSION_SEQUENCE_REGEX, '');
noExtensionLocales.push(noExtensionLocale);
all[noExtensionLocale] = l;
return all;
}, {});
var result = findBestMatch(noExtensionLocales, availableLocales);
if (result.matchedSupportedLocale && result.matchedDesiredLocale) {
foundLocale = result.matchedSupportedLocale;
extension =
noExtensionLocaleMap[result.matchedDesiredLocale].slice(result.matchedDesiredLocale.length) || undefined;
}
if (!foundLocale) {
return { locale: getDefaultLocale() };
}
return {
locale: foundLocale,
extension: extension,
};
let foundLocale;
let extension;
const noExtensionLocales = [];
const noExtensionLocaleMap = requestedLocales.reduce((all, l) => {
const noExtensionLocale = l.replace(UNICODE_EXTENSION_SEQUENCE_REGEX, "");
noExtensionLocales.push(noExtensionLocale);
all[noExtensionLocale] = l;
return all;
}, {});
const result = findBestMatch(noExtensionLocales, availableLocales);
if (result.matchedSupportedLocale && result.matchedDesiredLocale) {
foundLocale = result.matchedSupportedLocale;
extension = noExtensionLocaleMap[result.matchedDesiredLocale].slice(result.matchedDesiredLocale.length) || undefined;
}
if (!foundLocale) {
return { locale: getDefaultLocale() };
}
return {
locale: foundLocale,
extension
};
}
/**
* http://ecma-international.org/ecma-402/7.0/index.html#sec-canonicalizelocalelist
* @param locales
*/
* http://ecma-international.org/ecma-402/7.0/index.html#sec-canonicalizelocalelist
* @param locales
*/
export declare function CanonicalizeLocaleList(locales?: string | readonly string[]): string[];
/**
* http://ecma-international.org/ecma-402/7.0/index.html#sec-canonicalizelocalelist
* @param locales
*/
* http://ecma-international.org/ecma-402/7.0/index.html#sec-canonicalizelocalelist
* @param locales
*/
export function CanonicalizeLocaleList(locales) {
return Intl.getCanonicalLocales(locales);
return Intl.getCanonicalLocales(locales);
}
export function CanonicalizeUnicodeLocaleId(locale) {
return Intl.getCanonicalLocales(locale)[0];
return Intl.getCanonicalLocales(locale)[0];
}

@@ -1,8 +0,8 @@

import { invariant } from './utils.js';
import { invariant } from "./utils.js";
export function CanonicalizeUValue(ukey, uvalue) {
// TODO: Implement algorithm for CanonicalizeUValue per https://tc39.es/ecma402/#sec-canonicalizeuvalue
var lowerValue = uvalue.toLowerCase();
invariant(ukey !== undefined, "ukey must be defined");
var canonicalized = lowerValue;
return canonicalized;
// TODO: Implement algorithm for CanonicalizeUValue per https://tc39.es/ecma402/#sec-canonicalizeuvalue
let lowerValue = uvalue.toLowerCase();
invariant(ukey !== undefined, `ukey must be defined`);
let canonicalized = lowerValue;
return canonicalized;
}

@@ -1,2 +0,2 @@

import { Keyword } from './types.js';
import type { Keyword } from "./types.js";
export declare function InsertUnicodeExtensionAndCanonicalize(locale: string, attributes: string[], keywords: Array<Keyword>): string;

@@ -1,32 +0,29 @@

import { CanonicalizeUnicodeLocaleId } from './CanonicalizeUnicodeLocaleId.js';
import { invariant } from './utils.js';
import { CanonicalizeUnicodeLocaleId } from "./CanonicalizeUnicodeLocaleId.js";
import { invariant } from "./utils.js";
export function InsertUnicodeExtensionAndCanonicalize(locale, attributes, keywords) {
invariant(locale.indexOf('-u-') === -1, 'Expected locale to not have a Unicode locale extension');
var extension = '-u';
for (var _i = 0, attributes_1 = attributes; _i < attributes_1.length; _i++) {
var attr = attributes_1[_i];
extension += "-".concat(attr);
}
for (var _a = 0, keywords_1 = keywords; _a < keywords_1.length; _a++) {
var kw = keywords_1[_a];
var key = kw.key, value = kw.value;
extension += "-".concat(key);
if (value !== '') {
extension += "-".concat(value);
}
}
if (extension === '-u') {
return CanonicalizeUnicodeLocaleId(locale);
}
var privateIndex = locale.indexOf('-x-');
var newLocale;
if (privateIndex === -1) {
newLocale = locale + extension;
}
else {
var preExtension = locale.slice(0, privateIndex);
var postExtension = locale.slice(privateIndex);
newLocale = preExtension + extension + postExtension;
}
return CanonicalizeUnicodeLocaleId(newLocale);
invariant(locale.indexOf("-u-") === -1, "Expected locale to not have a Unicode locale extension");
let extension = "-u";
for (const attr of attributes) {
extension += `-${attr}`;
}
for (const kw of keywords) {
const { key, value } = kw;
extension += `-${key}`;
if (value !== "") {
extension += `-${value}`;
}
}
if (extension === "-u") {
return CanonicalizeUnicodeLocaleId(locale);
}
let privateIndex = locale.indexOf("-x-");
let newLocale;
if (privateIndex === -1) {
newLocale = locale + extension;
} else {
let preExtension = locale.slice(0, privateIndex);
let postExtension = locale.slice(privateIndex);
newLocale = preExtension + extension + postExtension;
}
return CanonicalizeUnicodeLocaleId(newLocale);
}

@@ -1,8 +0,8 @@

import { LookupMatcherResult } from './types.js';
import type { LookupMatcherResult } from "./types.js";
/**
* https://tc39.es/ecma402/#sec-lookupmatcher
* @param availableLocales
* @param requestedLocales
* @param getDefaultLocale
*/
* https://tc39.es/ecma402/#sec-lookupmatcher
* @param availableLocales
* @param requestedLocales
* @param getDefaultLocale
*/
export declare function LookupMatcher(availableLocales: readonly string[], requestedLocales: readonly string[], getDefaultLocale: () => string): LookupMatcherResult;

@@ -1,25 +0,24 @@

import { BestAvailableLocale } from './BestAvailableLocale.js';
import { UNICODE_EXTENSION_SEQUENCE_REGEX } from './utils.js';
import { BestAvailableLocale } from "./BestAvailableLocale.js";
import { UNICODE_EXTENSION_SEQUENCE_REGEX } from "./utils.js";
/**
* https://tc39.es/ecma402/#sec-lookupmatcher
* @param availableLocales
* @param requestedLocales
* @param getDefaultLocale
*/
* https://tc39.es/ecma402/#sec-lookupmatcher
* @param availableLocales
* @param requestedLocales
* @param getDefaultLocale
*/
export function LookupMatcher(availableLocales, requestedLocales, getDefaultLocale) {
var result = { locale: '' };
for (var _i = 0, requestedLocales_1 = requestedLocales; _i < requestedLocales_1.length; _i++) {
var locale = requestedLocales_1[_i];
var noExtensionLocale = locale.replace(UNICODE_EXTENSION_SEQUENCE_REGEX, '');
var availableLocale = BestAvailableLocale(availableLocales, noExtensionLocale);
if (availableLocale) {
result.locale = availableLocale;
if (locale !== noExtensionLocale) {
result.extension = locale.slice(noExtensionLocale.length, locale.length);
}
return result;
}
}
result.locale = getDefaultLocale();
return result;
const result = { locale: "" };
for (const locale of requestedLocales) {
const noExtensionLocale = locale.replace(UNICODE_EXTENSION_SEQUENCE_REGEX, "");
const availableLocale = BestAvailableLocale(availableLocales, noExtensionLocale);
if (availableLocale) {
result.locale = availableLocale;
if (locale !== noExtensionLocale) {
result.extension = locale.slice(noExtensionLocale.length, locale.length);
}
return result;
}
}
result.locale = getDefaultLocale();
return result;
}
/**
* https://tc39.es/ecma402/#sec-lookupsupportedlocales
* @param availableLocales
* @param requestedLocales
*/
* https://tc39.es/ecma402/#sec-lookupsupportedlocales
* @param availableLocales
* @param requestedLocales
*/
export declare function LookupSupportedLocales(availableLocales: string[], requestedLocales: string[]): string[];

@@ -1,19 +0,18 @@

import { BestAvailableLocale } from './BestAvailableLocale.js';
import { UNICODE_EXTENSION_SEQUENCE_REGEX } from './utils.js';
import { BestAvailableLocale } from "./BestAvailableLocale.js";
import { UNICODE_EXTENSION_SEQUENCE_REGEX } from "./utils.js";
/**
* https://tc39.es/ecma402/#sec-lookupsupportedlocales
* @param availableLocales
* @param requestedLocales
*/
* https://tc39.es/ecma402/#sec-lookupsupportedlocales
* @param availableLocales
* @param requestedLocales
*/
export function LookupSupportedLocales(availableLocales, requestedLocales) {
var subset = [];
for (var _i = 0, requestedLocales_1 = requestedLocales; _i < requestedLocales_1.length; _i++) {
var locale = requestedLocales_1[_i];
var noExtensionLocale = locale.replace(UNICODE_EXTENSION_SEQUENCE_REGEX, '');
var availableLocale = BestAvailableLocale(availableLocales, noExtensionLocale);
if (availableLocale) {
subset.push(availableLocale);
}
}
return subset;
const subset = [];
for (const locale of requestedLocales) {
const noExtensionLocale = locale.replace(UNICODE_EXTENSION_SEQUENCE_REGEX, "");
const availableLocale = BestAvailableLocale(availableLocales, noExtensionLocale);
if (availableLocale) {
subset.push(availableLocale);
}
}
return subset;
}

@@ -0,1 +1,2 @@

// This file is generated from regions-gen.ts
export declare const regions: Record<string, string[]>;
// This file is generated from regions-gen.ts
export var regions = {
'001': [
'001',
'001-status-grouping',
'002',
'005',
'009',
'011',
'013',
'014',
'015',
'017',
'018',
'019',
'021',
'029',
'030',
'034',
'035',
'039',
'053',
'054',
'057',
'061',
'142',
'143',
'145',
'150',
'151',
'154',
'155',
'AC',
'AD',
'AE',
'AF',
'AG',
'AI',
'AL',
'AM',
'AO',
'AQ',
'AR',
'AS',
'AT',
'AU',
'AW',
'AX',
'AZ',
'BA',
'BB',
'BD',
'BE',
'BF',
'BG',
'BH',
'BI',
'BJ',
'BL',
'BM',
'BN',
'BO',
'BQ',
'BR',
'BS',
'BT',
'BV',
'BW',
'BY',
'BZ',
'CA',
'CC',
'CD',
'CF',
'CG',
'CH',
'CI',
'CK',
'CL',
'CM',
'CN',
'CO',
'CP',
'CQ',
'CR',
'CU',
'CV',
'CW',
'CX',
'CY',
'CZ',
'DE',
'DG',
'DJ',
'DK',
'DM',
'DO',
'DZ',
'EA',
'EC',
'EE',
'EG',
'EH',
'ER',
'ES',
'ET',
'EU',
'EZ',
'FI',
'FJ',
'FK',
'FM',
'FO',
'FR',
'GA',
'GB',
'GD',
'GE',
'GF',
'GG',
'GH',
'GI',
'GL',
'GM',
'GN',
'GP',
'GQ',
'GR',
'GS',
'GT',
'GU',
'GW',
'GY',
'HK',
'HM',
'HN',
'HR',
'HT',
'HU',
'IC',
'ID',
'IE',
'IL',
'IM',
'IN',
'IO',
'IQ',
'IR',
'IS',
'IT',
'JE',
'JM',
'JO',
'JP',
'KE',
'KG',
'KH',
'KI',
'KM',
'KN',
'KP',
'KR',
'KW',
'KY',
'KZ',
'LA',
'LB',
'LC',
'LI',
'LK',
'LR',
'LS',
'LT',
'LU',
'LV',
'LY',
'MA',
'MC',
'MD',
'ME',
'MF',
'MG',
'MH',
'MK',
'ML',
'MM',
'MN',
'MO',
'MP',
'MQ',
'MR',
'MS',
'MT',
'MU',
'MV',
'MW',
'MX',
'MY',
'MZ',
'NA',
'NC',
'NE',
'NF',
'NG',
'NI',
'NL',
'NO',
'NP',
'NR',
'NU',
'NZ',
'OM',
'PA',
'PE',
'PF',
'PG',
'PH',
'PK',
'PL',
'PM',
'PN',
'PR',
'PS',
'PT',
'PW',
'PY',
'QA',
'QO',
'RE',
'RO',
'RS',
'RU',
'RW',
'SA',
'SB',
'SC',
'SD',
'SE',
'SG',
'SH',
'SI',
'SJ',
'SK',
'SL',
'SM',
'SN',
'SO',
'SR',
'SS',
'ST',
'SV',
'SX',
'SY',
'SZ',
'TA',
'TC',
'TD',
'TF',
'TG',
'TH',
'TJ',
'TK',
'TL',
'TM',
'TN',
'TO',
'TR',
'TT',
'TV',
'TW',
'TZ',
'UA',
'UG',
'UM',
'UN',
'US',
'UY',
'UZ',
'VA',
'VC',
'VE',
'VG',
'VI',
'VN',
'VU',
'WF',
'WS',
'XK',
'YE',
'YT',
'ZA',
'ZM',
'ZW',
],
'002': [
'002',
'002-status-grouping',
'011',
'014',
'015',
'017',
'018',
'202',
'AO',
'BF',
'BI',
'BJ',
'BW',
'CD',
'CF',
'CG',
'CI',
'CM',
'CV',
'DJ',
'DZ',
'EA',
'EG',
'EH',
'ER',
'ET',
'GA',
'GH',
'GM',
'GN',
'GQ',
'GW',
'IC',
'IO',
'KE',
'KM',
'LR',
'LS',
'LY',
'MA',
'MG',
'ML',
'MR',
'MU',
'MW',
'MZ',
'NA',
'NE',
'NG',
'RE',
'RW',
'SC',
'SD',
'SH',
'SL',
'SN',
'SO',
'SS',
'ST',
'SZ',
'TD',
'TF',
'TG',
'TN',
'TZ',
'UG',
'YT',
'ZA',
'ZM',
'ZW',
],
'003': [
'003',
'013',
'021',
'029',
'AG',
'AI',
'AW',
'BB',
'BL',
'BM',
'BQ',
'BS',
'BZ',
'CA',
'CR',
'CU',
'CW',
'DM',
'DO',
'GD',
'GL',
'GP',
'GT',
'HN',
'HT',
'JM',
'KN',
'KY',
'LC',
'MF',
'MQ',
'MS',
'MX',
'NI',
'PA',
'PM',
'PR',
'SV',
'SX',
'TC',
'TT',
'US',
'VC',
'VG',
'VI',
],
'005': [
'005',
'AR',
'BO',
'BR',
'BV',
'CL',
'CO',
'EC',
'FK',
'GF',
'GS',
'GY',
'PE',
'PY',
'SR',
'UY',
'VE',
],
'009': [
'009',
'053',
'054',
'057',
'061',
'AC',
'AQ',
'AS',
'AU',
'CC',
'CK',
'CP',
'CX',
'DG',
'FJ',
'FM',
'GU',
'HM',
'KI',
'MH',
'MP',
'NC',
'NF',
'NR',
'NU',
'NZ',
'PF',
'PG',
'PN',
'PW',
'QO',
'SB',
'TA',
'TK',
'TO',
'TV',
'UM',
'VU',
'WF',
'WS',
],
'011': [
'011',
'BF',
'BJ',
'CI',
'CV',
'GH',
'GM',
'GN',
'GW',
'LR',
'ML',
'MR',
'NE',
'NG',
'SH',
'SL',
'SN',
'TG',
],
'013': ['013', 'BZ', 'CR', 'GT', 'HN', 'MX', 'NI', 'PA', 'SV'],
'014': [
'014',
'BI',
'DJ',
'ER',
'ET',
'IO',
'KE',
'KM',
'MG',
'MU',
'MW',
'MZ',
'RE',
'RW',
'SC',
'SO',
'SS',
'TF',
'TZ',
'UG',
'YT',
'ZM',
'ZW',
],
'015': ['015', 'DZ', 'EA', 'EG', 'EH', 'IC', 'LY', 'MA', 'SD', 'TN'],
'017': ['017', 'AO', 'CD', 'CF', 'CG', 'CM', 'GA', 'GQ', 'ST', 'TD'],
'018': ['018', 'BW', 'LS', 'NA', 'SZ', 'ZA'],
'019': [
'003',
'005',
'013',
'019',
'019-status-grouping',
'021',
'029',
'419',
'AG',
'AI',
'AR',
'AW',
'BB',
'BL',
'BM',
'BO',
'BQ',
'BR',
'BS',
'BV',
'BZ',
'CA',
'CL',
'CO',
'CR',
'CU',
'CW',
'DM',
'DO',
'EC',
'FK',
'GD',
'GF',
'GL',
'GP',
'GS',
'GT',
'GY',
'HN',
'HT',
'JM',
'KN',
'KY',
'LC',
'MF',
'MQ',
'MS',
'MX',
'NI',
'PA',
'PE',
'PM',
'PR',
'PY',
'SR',
'SV',
'SX',
'TC',
'TT',
'US',
'UY',
'VC',
'VE',
'VG',
'VI',
],
'021': ['021', 'BM', 'CA', 'GL', 'PM', 'US'],
'029': [
'029',
'AG',
'AI',
'AW',
'BB',
'BL',
'BQ',
'BS',
'CU',
'CW',
'DM',
'DO',
'GD',
'GP',
'HT',
'JM',
'KN',
'KY',
'LC',
'MF',
'MQ',
'MS',
'PR',
'SX',
'TC',
'TT',
'VC',
'VG',
'VI',
],
'030': ['030', 'CN', 'HK', 'JP', 'KP', 'KR', 'MN', 'MO', 'TW'],
'034': ['034', 'AF', 'BD', 'BT', 'IN', 'IR', 'LK', 'MV', 'NP', 'PK'],
'035': [
'035',
'BN',
'ID',
'KH',
'LA',
'MM',
'MY',
'PH',
'SG',
'TH',
'TL',
'VN',
],
'039': [
'039',
'AD',
'AL',
'BA',
'ES',
'GI',
'GR',
'HR',
'IT',
'ME',
'MK',
'MT',
'PT',
'RS',
'SI',
'SM',
'VA',
'XK',
],
'053': ['053', 'AU', 'CC', 'CX', 'HM', 'NF', 'NZ'],
'054': ['054', 'FJ', 'NC', 'PG', 'SB', 'VU'],
'057': ['057', 'FM', 'GU', 'KI', 'MH', 'MP', 'NR', 'PW', 'UM'],
'061': ['061', 'AS', 'CK', 'NU', 'PF', 'PN', 'TK', 'TO', 'TV', 'WF', 'WS'],
'142': [
'030',
'034',
'035',
'142',
'143',
'145',
'AE',
'AF',
'AM',
'AZ',
'BD',
'BH',
'BN',
'BT',
'CN',
'CY',
'GE',
'HK',
'ID',
'IL',
'IN',
'IQ',
'IR',
'JO',
'JP',
'KG',
'KH',
'KP',
'KR',
'KW',
'KZ',
'LA',
'LB',
'LK',
'MM',
'MN',
'MO',
'MV',
'MY',
'NP',
'OM',
'PH',
'PK',
'PS',
'QA',
'SA',
'SG',
'SY',
'TH',
'TJ',
'TL',
'TM',
'TR',
'TW',
'UZ',
'VN',
'YE',
],
'143': ['143', 'KG', 'KZ', 'TJ', 'TM', 'UZ'],
'145': [
'145',
'AE',
'AM',
'AZ',
'BH',
'CY',
'GE',
'IL',
'IQ',
'JO',
'KW',
'LB',
'OM',
'PS',
'QA',
'SA',
'SY',
'TR',
'YE',
],
'150': [
'039',
'150',
'151',
'154',
'155',
'AD',
'AL',
'AT',
'AX',
'BA',
'BE',
'BG',
'BY',
'CH',
'CQ',
'CZ',
'DE',
'DK',
'EE',
'ES',
'FI',
'FO',
'FR',
'GB',
'GG',
'GI',
'GR',
'HR',
'HU',
'IE',
'IM',
'IS',
'IT',
'JE',
'LI',
'LT',
'LU',
'LV',
'MC',
'MD',
'ME',
'MK',
'MT',
'NL',
'NO',
'PL',
'PT',
'RO',
'RS',
'RU',
'SE',
'SI',
'SJ',
'SK',
'SM',
'UA',
'VA',
'XK',
],
'151': ['151', 'BG', 'BY', 'CZ', 'HU', 'MD', 'PL', 'RO', 'RU', 'SK', 'UA'],
'154': [
'154',
'AX',
'CQ',
'DK',
'EE',
'FI',
'FO',
'GB',
'GG',
'IE',
'IM',
'IS',
'JE',
'LT',
'LV',
'NO',
'SE',
'SJ',
],
'155': ['155', 'AT', 'BE', 'CH', 'DE', 'FR', 'LI', 'LU', 'MC', 'NL'],
'202': [
'011',
'014',
'017',
'018',
'202',
'AO',
'BF',
'BI',
'BJ',
'BW',
'CD',
'CF',
'CG',
'CI',
'CM',
'CV',
'DJ',
'ER',
'ET',
'GA',
'GH',
'GM',
'GN',
'GQ',
'GW',
'IO',
'KE',
'KM',
'LR',
'LS',
'MG',
'ML',
'MR',
'MU',
'MW',
'MZ',
'NA',
'NE',
'NG',
'RE',
'RW',
'SC',
'SH',
'SL',
'SN',
'SO',
'SS',
'ST',
'SZ',
'TD',
'TF',
'TG',
'TZ',
'UG',
'YT',
'ZA',
'ZM',
'ZW',
],
'419': [
'005',
'013',
'029',
'419',
'AG',
'AI',
'AR',
'AW',
'BB',
'BL',
'BO',
'BQ',
'BR',
'BS',
'BV',
'BZ',
'CL',
'CO',
'CR',
'CU',
'CW',
'DM',
'DO',
'EC',
'FK',
'GD',
'GF',
'GP',
'GS',
'GT',
'GY',
'HN',
'HT',
'JM',
'KN',
'KY',
'LC',
'MF',
'MQ',
'MS',
'MX',
'NI',
'PA',
'PE',
'PR',
'PY',
'SR',
'SV',
'SX',
'TC',
'TT',
'UY',
'VC',
'VE',
'VG',
'VI',
],
EU: [
'AT',
'BE',
'BG',
'CY',
'CZ',
'DE',
'DK',
'EE',
'ES',
'EU',
'FI',
'FR',
'GR',
'HR',
'HU',
'IE',
'IT',
'LT',
'LU',
'LV',
'MT',
'NL',
'PL',
'PT',
'RO',
'SE',
'SI',
'SK',
],
EZ: [
'AT',
'BE',
'CY',
'DE',
'EE',
'ES',
'EZ',
'FI',
'FR',
'GR',
'IE',
'IT',
'LT',
'LU',
'LV',
'MT',
'NL',
'PT',
'SI',
'SK',
],
QO: ['AC', 'AQ', 'CP', 'DG', 'QO', 'TA'],
UN: [
'AD',
'AE',
'AF',
'AG',
'AL',
'AM',
'AO',
'AR',
'AT',
'AU',
'AZ',
'BA',
'BB',
'BD',
'BE',
'BF',
'BG',
'BH',
'BI',
'BJ',
'BN',
'BO',
'BR',
'BS',
'BT',
'BW',
'BY',
'BZ',
'CA',
'CD',
'CF',
'CG',
'CH',
'CI',
'CL',
'CM',
'CN',
'CO',
'CR',
'CU',
'CV',
'CY',
'CZ',
'DE',
'DJ',
'DK',
'DM',
'DO',
'DZ',
'EC',
'EE',
'EG',
'ER',
'ES',
'ET',
'FI',
'FJ',
'FM',
'FR',
'GA',
'GB',
'GD',
'GE',
'GH',
'GM',
'GN',
'GQ',
'GR',
'GT',
'GW',
'GY',
'HN',
'HR',
'HT',
'HU',
'ID',
'IE',
'IL',
'IN',
'IQ',
'IR',
'IS',
'IT',
'JM',
'JO',
'JP',
'KE',
'KG',
'KH',
'KI',
'KM',
'KN',
'KP',
'KR',
'KW',
'KZ',
'LA',
'LB',
'LC',
'LI',
'LK',
'LR',
'LS',
'LT',
'LU',
'LV',
'LY',
'MA',
'MC',
'MD',
'ME',
'MG',
'MH',
'MK',
'ML',
'MM',
'MN',
'MR',
'MT',
'MU',
'MV',
'MW',
'MX',
'MY',
'MZ',
'NA',
'NE',
'NG',
'NI',
'NL',
'NO',
'NP',
'NR',
'NZ',
'OM',
'PA',
'PE',
'PG',
'PH',
'PK',
'PL',
'PT',
'PW',
'PY',
'QA',
'RO',
'RS',
'RU',
'RW',
'SA',
'SB',
'SC',
'SD',
'SE',
'SG',
'SI',
'SK',
'SL',
'SM',
'SN',
'SO',
'SR',
'SS',
'ST',
'SV',
'SY',
'SZ',
'TD',
'TG',
'TH',
'TJ',
'TL',
'TM',
'TN',
'TO',
'TR',
'TT',
'TV',
'TZ',
'UA',
'UG',
'UN',
'US',
'UY',
'UZ',
'VC',
'VE',
'VN',
'VU',
'WS',
'YE',
'ZA',
'ZM',
'ZW',
],
export const regions = {
"001": [
"001",
"001-status-grouping",
"002",
"005",
"009",
"011",
"013",
"014",
"015",
"017",
"018",
"019",
"021",
"029",
"030",
"034",
"035",
"039",
"053",
"054",
"057",
"061",
"142",
"143",
"145",
"150",
"151",
"154",
"155",
"AC",
"AD",
"AE",
"AF",
"AG",
"AI",
"AL",
"AM",
"AO",
"AQ",
"AR",
"AS",
"AT",
"AU",
"AW",
"AX",
"AZ",
"BA",
"BB",
"BD",
"BE",
"BF",
"BG",
"BH",
"BI",
"BJ",
"BL",
"BM",
"BN",
"BO",
"BQ",
"BR",
"BS",
"BT",
"BV",
"BW",
"BY",
"BZ",
"CA",
"CC",
"CD",
"CF",
"CG",
"CH",
"CI",
"CK",
"CL",
"CM",
"CN",
"CO",
"CP",
"CQ",
"CR",
"CU",
"CV",
"CW",
"CX",
"CY",
"CZ",
"DE",
"DG",
"DJ",
"DK",
"DM",
"DO",
"DZ",
"EA",
"EC",
"EE",
"EG",
"EH",
"ER",
"ES",
"ET",
"EU",
"EZ",
"FI",
"FJ",
"FK",
"FM",
"FO",
"FR",
"GA",
"GB",
"GD",
"GE",
"GF",
"GG",
"GH",
"GI",
"GL",
"GM",
"GN",
"GP",
"GQ",
"GR",
"GS",
"GT",
"GU",
"GW",
"GY",
"HK",
"HM",
"HN",
"HR",
"HT",
"HU",
"IC",
"ID",
"IE",
"IL",
"IM",
"IN",
"IO",
"IQ",
"IR",
"IS",
"IT",
"JE",
"JM",
"JO",
"JP",
"KE",
"KG",
"KH",
"KI",
"KM",
"KN",
"KP",
"KR",
"KW",
"KY",
"KZ",
"LA",
"LB",
"LC",
"LI",
"LK",
"LR",
"LS",
"LT",
"LU",
"LV",
"LY",
"MA",
"MC",
"MD",
"ME",
"MF",
"MG",
"MH",
"MK",
"ML",
"MM",
"MN",
"MO",
"MP",
"MQ",
"MR",
"MS",
"MT",
"MU",
"MV",
"MW",
"MX",
"MY",
"MZ",
"NA",
"NC",
"NE",
"NF",
"NG",
"NI",
"NL",
"NO",
"NP",
"NR",
"NU",
"NZ",
"OM",
"PA",
"PE",
"PF",
"PG",
"PH",
"PK",
"PL",
"PM",
"PN",
"PR",
"PS",
"PT",
"PW",
"PY",
"QA",
"QO",
"RE",
"RO",
"RS",
"RU",
"RW",
"SA",
"SB",
"SC",
"SD",
"SE",
"SG",
"SH",
"SI",
"SJ",
"SK",
"SL",
"SM",
"SN",
"SO",
"SR",
"SS",
"ST",
"SV",
"SX",
"SY",
"SZ",
"TA",
"TC",
"TD",
"TF",
"TG",
"TH",
"TJ",
"TK",
"TL",
"TM",
"TN",
"TO",
"TR",
"TT",
"TV",
"TW",
"TZ",
"UA",
"UG",
"UM",
"UN",
"US",
"UY",
"UZ",
"VA",
"VC",
"VE",
"VG",
"VI",
"VN",
"VU",
"WF",
"WS",
"XK",
"YE",
"YT",
"ZA",
"ZM",
"ZW"
],
"002": [
"002",
"002-status-grouping",
"011",
"014",
"015",
"017",
"018",
"202",
"AO",
"BF",
"BI",
"BJ",
"BW",
"CD",
"CF",
"CG",
"CI",
"CM",
"CV",
"DJ",
"DZ",
"EA",
"EG",
"EH",
"ER",
"ET",
"GA",
"GH",
"GM",
"GN",
"GQ",
"GW",
"IC",
"IO",
"KE",
"KM",
"LR",
"LS",
"LY",
"MA",
"MG",
"ML",
"MR",
"MU",
"MW",
"MZ",
"NA",
"NE",
"NG",
"RE",
"RW",
"SC",
"SD",
"SH",
"SL",
"SN",
"SO",
"SS",
"ST",
"SZ",
"TD",
"TF",
"TG",
"TN",
"TZ",
"UG",
"YT",
"ZA",
"ZM",
"ZW"
],
"003": [
"003",
"013",
"021",
"029",
"AG",
"AI",
"AW",
"BB",
"BL",
"BM",
"BQ",
"BS",
"BZ",
"CA",
"CR",
"CU",
"CW",
"DM",
"DO",
"GD",
"GL",
"GP",
"GT",
"HN",
"HT",
"JM",
"KN",
"KY",
"LC",
"MF",
"MQ",
"MS",
"MX",
"NI",
"PA",
"PM",
"PR",
"SV",
"SX",
"TC",
"TT",
"US",
"VC",
"VG",
"VI"
],
"005": [
"005",
"AR",
"BO",
"BR",
"BV",
"CL",
"CO",
"EC",
"FK",
"GF",
"GS",
"GY",
"PE",
"PY",
"SR",
"UY",
"VE"
],
"009": [
"009",
"053",
"054",
"057",
"061",
"AC",
"AQ",
"AS",
"AU",
"CC",
"CK",
"CP",
"CX",
"DG",
"FJ",
"FM",
"GU",
"HM",
"KI",
"MH",
"MP",
"NC",
"NF",
"NR",
"NU",
"NZ",
"PF",
"PG",
"PN",
"PW",
"QO",
"SB",
"TA",
"TK",
"TO",
"TV",
"UM",
"VU",
"WF",
"WS"
],
"011": [
"011",
"BF",
"BJ",
"CI",
"CV",
"GH",
"GM",
"GN",
"GW",
"LR",
"ML",
"MR",
"NE",
"NG",
"SH",
"SL",
"SN",
"TG"
],
"013": [
"013",
"BZ",
"CR",
"GT",
"HN",
"MX",
"NI",
"PA",
"SV"
],
"014": [
"014",
"BI",
"DJ",
"ER",
"ET",
"IO",
"KE",
"KM",
"MG",
"MU",
"MW",
"MZ",
"RE",
"RW",
"SC",
"SO",
"SS",
"TF",
"TZ",
"UG",
"YT",
"ZM",
"ZW"
],
"015": [
"015",
"DZ",
"EA",
"EG",
"EH",
"IC",
"LY",
"MA",
"SD",
"TN"
],
"017": [
"017",
"AO",
"CD",
"CF",
"CG",
"CM",
"GA",
"GQ",
"ST",
"TD"
],
"018": [
"018",
"BW",
"LS",
"NA",
"SZ",
"ZA"
],
"019": [
"003",
"005",
"013",
"019",
"019-status-grouping",
"021",
"029",
"419",
"AG",
"AI",
"AR",
"AW",
"BB",
"BL",
"BM",
"BO",
"BQ",
"BR",
"BS",
"BV",
"BZ",
"CA",
"CL",
"CO",
"CR",
"CU",
"CW",
"DM",
"DO",
"EC",
"FK",
"GD",
"GF",
"GL",
"GP",
"GS",
"GT",
"GY",
"HN",
"HT",
"JM",
"KN",
"KY",
"LC",
"MF",
"MQ",
"MS",
"MX",
"NI",
"PA",
"PE",
"PM",
"PR",
"PY",
"SR",
"SV",
"SX",
"TC",
"TT",
"US",
"UY",
"VC",
"VE",
"VG",
"VI"
],
"021": [
"021",
"BM",
"CA",
"GL",
"PM",
"US"
],
"029": [
"029",
"AG",
"AI",
"AW",
"BB",
"BL",
"BQ",
"BS",
"CU",
"CW",
"DM",
"DO",
"GD",
"GP",
"HT",
"JM",
"KN",
"KY",
"LC",
"MF",
"MQ",
"MS",
"PR",
"SX",
"TC",
"TT",
"VC",
"VG",
"VI"
],
"030": [
"030",
"CN",
"HK",
"JP",
"KP",
"KR",
"MN",
"MO",
"TW"
],
"034": [
"034",
"AF",
"BD",
"BT",
"IN",
"IR",
"LK",
"MV",
"NP",
"PK"
],
"035": [
"035",
"BN",
"ID",
"KH",
"LA",
"MM",
"MY",
"PH",
"SG",
"TH",
"TL",
"VN"
],
"039": [
"039",
"AD",
"AL",
"BA",
"ES",
"GI",
"GR",
"HR",
"IT",
"ME",
"MK",
"MT",
"PT",
"RS",
"SI",
"SM",
"VA",
"XK"
],
"053": [
"053",
"AU",
"CC",
"CX",
"HM",
"NF",
"NZ"
],
"054": [
"054",
"FJ",
"NC",
"PG",
"SB",
"VU"
],
"057": [
"057",
"FM",
"GU",
"KI",
"MH",
"MP",
"NR",
"PW",
"UM"
],
"061": [
"061",
"AS",
"CK",
"NU",
"PF",
"PN",
"TK",
"TO",
"TV",
"WF",
"WS"
],
"142": [
"030",
"034",
"035",
"142",
"143",
"145",
"AE",
"AF",
"AM",
"AZ",
"BD",
"BH",
"BN",
"BT",
"CN",
"CY",
"GE",
"HK",
"ID",
"IL",
"IN",
"IQ",
"IR",
"JO",
"JP",
"KG",
"KH",
"KP",
"KR",
"KW",
"KZ",
"LA",
"LB",
"LK",
"MM",
"MN",
"MO",
"MV",
"MY",
"NP",
"OM",
"PH",
"PK",
"PS",
"QA",
"SA",
"SG",
"SY",
"TH",
"TJ",
"TL",
"TM",
"TR",
"TW",
"UZ",
"VN",
"YE"
],
"143": [
"143",
"KG",
"KZ",
"TJ",
"TM",
"UZ"
],
"145": [
"145",
"AE",
"AM",
"AZ",
"BH",
"CY",
"GE",
"IL",
"IQ",
"JO",
"KW",
"LB",
"OM",
"PS",
"QA",
"SA",
"SY",
"TR",
"YE"
],
"150": [
"039",
"150",
"151",
"154",
"155",
"AD",
"AL",
"AT",
"AX",
"BA",
"BE",
"BG",
"BY",
"CH",
"CQ",
"CZ",
"DE",
"DK",
"EE",
"ES",
"FI",
"FO",
"FR",
"GB",
"GG",
"GI",
"GR",
"HR",
"HU",
"IE",
"IM",
"IS",
"IT",
"JE",
"LI",
"LT",
"LU",
"LV",
"MC",
"MD",
"ME",
"MK",
"MT",
"NL",
"NO",
"PL",
"PT",
"RO",
"RS",
"RU",
"SE",
"SI",
"SJ",
"SK",
"SM",
"UA",
"VA",
"XK"
],
"151": [
"151",
"BG",
"BY",
"CZ",
"HU",
"MD",
"PL",
"RO",
"RU",
"SK",
"UA"
],
"154": [
"154",
"AX",
"CQ",
"DK",
"EE",
"FI",
"FO",
"GB",
"GG",
"IE",
"IM",
"IS",
"JE",
"LT",
"LV",
"NO",
"SE",
"SJ"
],
"155": [
"155",
"AT",
"BE",
"CH",
"DE",
"FR",
"LI",
"LU",
"MC",
"NL"
],
"202": [
"011",
"014",
"017",
"018",
"202",
"AO",
"BF",
"BI",
"BJ",
"BW",
"CD",
"CF",
"CG",
"CI",
"CM",
"CV",
"DJ",
"ER",
"ET",
"GA",
"GH",
"GM",
"GN",
"GQ",
"GW",
"IO",
"KE",
"KM",
"LR",
"LS",
"MG",
"ML",
"MR",
"MU",
"MW",
"MZ",
"NA",
"NE",
"NG",
"RE",
"RW",
"SC",
"SH",
"SL",
"SN",
"SO",
"SS",
"ST",
"SZ",
"TD",
"TF",
"TG",
"TZ",
"UG",
"YT",
"ZA",
"ZM",
"ZW"
],
"419": [
"005",
"013",
"029",
"419",
"AG",
"AI",
"AR",
"AW",
"BB",
"BL",
"BO",
"BQ",
"BR",
"BS",
"BV",
"BZ",
"CL",
"CO",
"CR",
"CU",
"CW",
"DM",
"DO",
"EC",
"FK",
"GD",
"GF",
"GP",
"GS",
"GT",
"GY",
"HN",
"HT",
"JM",
"KN",
"KY",
"LC",
"MF",
"MQ",
"MS",
"MX",
"NI",
"PA",
"PE",
"PR",
"PY",
"SR",
"SV",
"SX",
"TC",
"TT",
"UY",
"VC",
"VE",
"VG",
"VI"
],
EU: [
"AT",
"BE",
"BG",
"CY",
"CZ",
"DE",
"DK",
"EE",
"ES",
"EU",
"FI",
"FR",
"GR",
"HR",
"HU",
"IE",
"IT",
"LT",
"LU",
"LV",
"MT",
"NL",
"PL",
"PT",
"RO",
"SE",
"SI",
"SK"
],
EZ: [
"AT",
"BE",
"CY",
"DE",
"EE",
"ES",
"EZ",
"FI",
"FR",
"GR",
"IE",
"IT",
"LT",
"LU",
"LV",
"MT",
"NL",
"PT",
"SI",
"SK"
],
QO: [
"AC",
"AQ",
"CP",
"DG",
"QO",
"TA"
],
UN: [
"AD",
"AE",
"AF",
"AG",
"AL",
"AM",
"AO",
"AR",
"AT",
"AU",
"AZ",
"BA",
"BB",
"BD",
"BE",
"BF",
"BG",
"BH",
"BI",
"BJ",
"BN",
"BO",
"BR",
"BS",
"BT",
"BW",
"BY",
"BZ",
"CA",
"CD",
"CF",
"CG",
"CH",
"CI",
"CL",
"CM",
"CN",
"CO",
"CR",
"CU",
"CV",
"CY",
"CZ",
"DE",
"DJ",
"DK",
"DM",
"DO",
"DZ",
"EC",
"EE",
"EG",
"ER",
"ES",
"ET",
"FI",
"FJ",
"FM",
"FR",
"GA",
"GB",
"GD",
"GE",
"GH",
"GM",
"GN",
"GQ",
"GR",
"GT",
"GW",
"GY",
"HN",
"HR",
"HT",
"HU",
"ID",
"IE",
"IL",
"IN",
"IQ",
"IR",
"IS",
"IT",
"JM",
"JO",
"JP",
"KE",
"KG",
"KH",
"KI",
"KM",
"KN",
"KP",
"KR",
"KW",
"KZ",
"LA",
"LB",
"LC",
"LI",
"LK",
"LR",
"LS",
"LT",
"LU",
"LV",
"LY",
"MA",
"MC",
"MD",
"ME",
"MG",
"MH",
"MK",
"ML",
"MM",
"MN",
"MR",
"MT",
"MU",
"MV",
"MW",
"MX",
"MY",
"MZ",
"NA",
"NE",
"NG",
"NI",
"NL",
"NO",
"NP",
"NR",
"NZ",
"OM",
"PA",
"PE",
"PG",
"PH",
"PK",
"PL",
"PT",
"PW",
"PY",
"QA",
"RO",
"RS",
"RU",
"RW",
"SA",
"SB",
"SC",
"SD",
"SE",
"SG",
"SI",
"SK",
"SL",
"SM",
"SN",
"SO",
"SR",
"SS",
"ST",
"SV",
"SY",
"SZ",
"TD",
"TG",
"TH",
"TJ",
"TL",
"TM",
"TN",
"TO",
"TR",
"TT",
"TV",
"TZ",
"UA",
"UG",
"UN",
"US",
"UY",
"UZ",
"VC",
"VE",
"VN",
"VU",
"WS",
"YE",
"ZA",
"ZM",
"ZW"
]
};
export interface ResolveLocaleResult {
locale: string;
dataLocale: string;
[k: string]: any;
locale: string;
dataLocale: string;
[k: string]: any;
}
/**
* https://tc39.es/ecma402/#sec-resolvelocale
*/
export declare function ResolveLocale<K extends string, D extends {
[k in K]: any;
}>(availableLocales: Set<string> | readonly string[], requestedLocales: readonly string[], options: {
localeMatcher: string;
[k: string]: string;
* https://tc39.es/ecma402/#sec-resolvelocale
*/
export declare function ResolveLocale<
K extends string,
D extends { [k in K] : any }
>(availableLocales: Set<string> | readonly string[], requestedLocales: readonly string[], options: {
localeMatcher: string;
[k: string]: string;
}, relevantExtensionKeys: K[], localeData: Record<string, D | undefined>, getDefaultLocale: () => string): ResolveLocaleResult;

@@ -1,100 +0,95 @@

import { BestFitMatcher } from './BestFitMatcher.js';
import { CanonicalizeUValue } from './CanonicalizeUValue.js';
import { InsertUnicodeExtensionAndCanonicalize } from './InsertUnicodeExtensionAndCanonicalize.js';
import { LookupMatcher } from './LookupMatcher.js';
import { UnicodeExtensionComponents } from './UnicodeExtensionComponents.js';
import { invariant } from './utils.js';
import { BestFitMatcher } from "./BestFitMatcher.js";
import { CanonicalizeUValue } from "./CanonicalizeUValue.js";
import { InsertUnicodeExtensionAndCanonicalize } from "./InsertUnicodeExtensionAndCanonicalize.js";
import { LookupMatcher } from "./LookupMatcher.js";
import { UnicodeExtensionComponents } from "./UnicodeExtensionComponents.js";
import { invariant } from "./utils.js";
/**
* https://tc39.es/ecma402/#sec-resolvelocale
*/
* https://tc39.es/ecma402/#sec-resolvelocale
*/
export function ResolveLocale(availableLocales, requestedLocales, options, relevantExtensionKeys, localeData, getDefaultLocale) {
var _a;
var matcher = options.localeMatcher;
var r;
if (matcher === 'lookup') {
r = LookupMatcher(Array.from(availableLocales), requestedLocales, getDefaultLocale);
}
else {
r = BestFitMatcher(Array.from(availableLocales), requestedLocales, getDefaultLocale);
}
if (r == null) {
r = {
locale: getDefaultLocale(),
extension: '',
};
}
var foundLocale = r.locale;
var foundLocaleData = localeData[foundLocale];
// TODO: We can't really guarantee that the locale data is available
// invariant(
// foundLocaleData !== undefined,
// `Missing locale data for ${foundLocale}`
// )
var result = { locale: 'en', dataLocale: foundLocale };
var components;
var keywords;
if (r.extension) {
components = UnicodeExtensionComponents(r.extension);
keywords = components.keywords;
}
else {
keywords = [];
}
var supportedKeywords = [];
var _loop_1 = function (key) {
// TODO: Shouldn't default to empty array, see TODO above
var keyLocaleData = (_a = foundLocaleData === null || foundLocaleData === void 0 ? void 0 : foundLocaleData[key]) !== null && _a !== void 0 ? _a : [];
invariant(Array.isArray(keyLocaleData), "keyLocaleData for ".concat(key, " must be an array"));
var value = keyLocaleData[0];
invariant(value === undefined || typeof value === 'string', "value must be a string or undefined");
var supportedKeyword = void 0;
var entry = keywords.find(function (k) { return k.key === key; });
if (entry) {
var requestedValue = entry.value;
if (requestedValue !== '') {
if (keyLocaleData.indexOf(requestedValue) > -1) {
value = requestedValue;
supportedKeyword = {
key: key,
value: value,
};
}
}
else if (keyLocaleData.indexOf('true') > -1) {
value = 'true';
supportedKeyword = {
key: key,
value: value,
};
}
}
var optionsValue = options[key];
invariant(optionsValue == null || typeof optionsValue === 'string', "optionsValue must be a string or undefined");
if (typeof optionsValue === 'string') {
var ukey = key.toLowerCase();
optionsValue = CanonicalizeUValue(ukey, optionsValue);
if (optionsValue === '') {
optionsValue = 'true';
}
}
if (optionsValue !== value && keyLocaleData.indexOf(optionsValue) > -1) {
value = optionsValue;
supportedKeyword = undefined;
}
if (supportedKeyword) {
supportedKeywords.push(supportedKeyword);
}
result[key] = value;
};
for (var _i = 0, relevantExtensionKeys_1 = relevantExtensionKeys; _i < relevantExtensionKeys_1.length; _i++) {
var key = relevantExtensionKeys_1[_i];
_loop_1(key);
}
var supportedAttributes = [];
if (supportedKeywords.length > 0) {
supportedAttributes = [];
foundLocale = InsertUnicodeExtensionAndCanonicalize(foundLocale, supportedAttributes, supportedKeywords);
}
result.locale = foundLocale;
return result;
const matcher = options.localeMatcher;
let r;
if (matcher === "lookup") {
r = LookupMatcher(Array.from(availableLocales), requestedLocales, getDefaultLocale);
} else {
r = BestFitMatcher(Array.from(availableLocales), requestedLocales, getDefaultLocale);
}
if (r == null) {
r = {
locale: getDefaultLocale(),
extension: ""
};
}
let foundLocale = r.locale;
let foundLocaleData = localeData[foundLocale];
// TODO: We can't really guarantee that the locale data is available
// invariant(
// foundLocaleData !== undefined,
// `Missing locale data for ${foundLocale}`
// )
const result = {
locale: "en",
dataLocale: foundLocale
};
let components;
let keywords;
if (r.extension) {
components = UnicodeExtensionComponents(r.extension);
keywords = components.keywords;
} else {
keywords = [];
}
let supportedKeywords = [];
for (const key of relevantExtensionKeys) {
// TODO: Shouldn't default to empty array, see TODO above
let keyLocaleData = foundLocaleData?.[key] ?? [];
invariant(Array.isArray(keyLocaleData), `keyLocaleData for ${key} must be an array`);
let value = keyLocaleData[0];
invariant(value === undefined || typeof value === "string", `value must be a string or undefined`);
let supportedKeyword;
let entry = keywords.find((k) => k.key === key);
if (entry) {
let requestedValue = entry.value;
if (requestedValue !== "") {
if (keyLocaleData.indexOf(requestedValue) > -1) {
value = requestedValue;
supportedKeyword = {
key,
value
};
}
} else if (keyLocaleData.indexOf("true") > -1) {
value = "true";
supportedKeyword = {
key,
value
};
}
}
let optionsValue = options[key];
invariant(optionsValue == null || typeof optionsValue === "string", `optionsValue must be a string or undefined`);
if (typeof optionsValue === "string") {
let ukey = key.toLowerCase();
optionsValue = CanonicalizeUValue(ukey, optionsValue);
if (optionsValue === "") {
optionsValue = "true";
}
}
if (optionsValue !== value && keyLocaleData.indexOf(optionsValue) > -1) {
value = optionsValue;
supportedKeyword = undefined;
}
if (supportedKeyword) {
supportedKeywords.push(supportedKeyword);
}
result[key] = value;
}
let supportedAttributes = [];
if (supportedKeywords.length > 0) {
supportedAttributes = [];
foundLocale = InsertUnicodeExtensionAndCanonicalize(foundLocale, supportedAttributes, supportedKeywords);
}
result.locale = foundLocale;
return result;
}
export interface LookupMatcherResult {
locale: string;
extension?: string;
nu?: string;
locale: string;
extension?: string;
nu?: string;
}
export interface Keyword {
key: string;
value: string;
key: string;
value: string;
}

@@ -1,5 +0,5 @@

import { Keyword } from './types.js';
import type { Keyword } from "./types.js";
export declare function UnicodeExtensionComponents(extension: string): {
attributes: string[];
keywords: Array<Keyword>;
attributes: string[];
keywords: Array<Keyword>;
};

@@ -1,42 +0,44 @@

import { invariant } from './utils.js';
import { invariant } from "./utils.js";
export function UnicodeExtensionComponents(extension) {
invariant(extension === extension.toLowerCase(), 'Expected extension to be lowercase');
invariant(extension.slice(0, 3) === '-u-', 'Expected extension to be a Unicode locale extension');
var attributes = [];
var keywords = [];
var keyword;
var size = extension.length;
var k = 3;
while (k < size) {
var e = extension.indexOf('-', k);
var len = void 0;
if (e === -1) {
len = size - k;
}
else {
len = e - k;
}
var subtag = extension.slice(k, k + len);
invariant(len >= 2, 'Expected a subtag to have at least 2 characters');
if (keyword === undefined && len != 2) {
if (attributes.indexOf(subtag) === -1) {
attributes.push(subtag);
}
}
else if (len === 2) {
keyword = { key: subtag, value: '' };
if (keywords.find(function (k) { return k.key === (keyword === null || keyword === void 0 ? void 0 : keyword.key); }) === undefined) {
keywords.push(keyword);
}
}
else if ((keyword === null || keyword === void 0 ? void 0 : keyword.value) === '') {
keyword.value = subtag;
}
else {
invariant(keyword !== undefined, 'Expected keyword to be defined');
keyword.value += '-' + subtag;
}
k += len + 1;
}
return { attributes: attributes, keywords: keywords };
invariant(extension === extension.toLowerCase(), "Expected extension to be lowercase");
invariant(extension.slice(0, 3) === "-u-", "Expected extension to be a Unicode locale extension");
const attributes = [];
const keywords = [];
let keyword;
let size = extension.length;
let k = 3;
while (k < size) {
let e = extension.indexOf("-", k);
let len;
if (e === -1) {
len = size - k;
} else {
len = e - k;
}
let subtag = extension.slice(k, k + len);
invariant(len >= 2, "Expected a subtag to have at least 2 characters");
if (keyword === undefined && len != 2) {
if (attributes.indexOf(subtag) === -1) {
attributes.push(subtag);
}
} else if (len === 2) {
keyword = {
key: subtag,
value: ""
};
if (keywords.find((k) => k.key === keyword?.key) === undefined) {
keywords.push(keyword);
}
} else if (keyword?.value === "") {
keyword.value = subtag;
} else {
invariant(keyword !== undefined, "Expected keyword to be defined");
keyword.value += "-" + subtag;
}
k += len + 1;
}
return {
attributes,
keywords
};
}
/**
* https://tc39.es/ecma402/#sec-unicodeextensionvalue
* @param extension
* @param key
*/
* https://tc39.es/ecma402/#sec-unicodeextensionvalue
* @param extension
* @param key
*/
export declare function UnicodeExtensionValue(extension: string, key: string): string | undefined;

@@ -1,46 +0,43 @@

import { invariant } from './utils.js';
import { invariant } from "./utils.js";
/**
* https://tc39.es/ecma402/#sec-unicodeextensionvalue
* @param extension
* @param key
*/
* https://tc39.es/ecma402/#sec-unicodeextensionvalue
* @param extension
* @param key
*/
export function UnicodeExtensionValue(extension, key) {
invariant(key.length === 2, 'key must have 2 elements');
var size = extension.length;
var searchValue = "-".concat(key, "-");
var pos = extension.indexOf(searchValue);
if (pos !== -1) {
var start = pos + 4;
var end = start;
var k = start;
var done = false;
while (!done) {
var e = extension.indexOf('-', k);
var len = void 0;
if (e === -1) {
len = size - k;
}
else {
len = e - k;
}
if (len === 2) {
done = true;
}
else if (e === -1) {
end = size;
done = true;
}
else {
end = e;
k = e + 1;
}
}
return extension.slice(start, end);
}
searchValue = "-".concat(key);
pos = extension.indexOf(searchValue);
if (pos !== -1 && pos + 3 === size) {
return '';
}
return undefined;
invariant(key.length === 2, "key must have 2 elements");
const size = extension.length;
let searchValue = `-${key}-`;
let pos = extension.indexOf(searchValue);
if (pos !== -1) {
const start = pos + 4;
let end = start;
let k = start;
let done = false;
while (!done) {
const e = extension.indexOf("-", k);
let len;
if (e === -1) {
len = size - k;
} else {
len = e - k;
}
if (len === 2) {
done = true;
} else if (e === -1) {
end = size;
done = true;
} else {
end = e;
k = e + 1;
}
}
return extension.slice(start, end);
}
searchValue = `-${key}`;
pos = extension.indexOf(searchValue);
if (pos !== -1 && pos + 3 === size) {
return "";
}
return undefined;
}
export declare const UNICODE_EXTENSION_SEQUENCE_REGEX: RegExp;
/**
* Asserts that a condition is true, throwing an error if it is not.
* Used for runtime validation and type narrowing.
*
* @param condition - The condition to check
* @param message - Error message if condition is false
* @param Err - Error constructor to use (defaults to Error)
* @throws {Error} When condition is false
*
* @example
* ```ts
* invariant(locale !== undefined, 'Locale must be defined')
* // locale is now narrowed to non-undefined type
* ```
*/
* Asserts that a condition is true, throwing an error if it is not.
* Used for runtime validation and type narrowing.
*
* @param condition - The condition to check
* @param message - Error message if condition is false
* @param Err - Error constructor to use (defaults to Error)
* @throws {Error} When condition is false
*
* @example
* ```ts
* invariant(locale !== undefined, 'Locale must be defined')
* // locale is now narrowed to non-undefined type
* ```
*/
export declare function invariant(condition: boolean, message: string, Err?: any): asserts condition;
/**
* Calculates the matching distance between two locales using the CLDR Enhanced Language Matching algorithm.
* This function is memoized for performance, as distance calculations are expensive.
*
* The distance represents how "far apart" two locales are, with 0 being identical (after maximization).
* Distances are calculated based on Language-Script-Region (LSR) differences using CLDR data.
*
* @param desired - The desired locale (e.g., "en-US")
* @param supported - The supported locale to compare against (e.g., "en-GB")
* @returns The calculated distance between the locales
*
* @example
* ```ts
* findMatchingDistance('en-US', 'en-US') // 0 - identical
* findMatchingDistance('en-US', 'en-GB') // 40 - same language/script, different region
* findMatchingDistance('es-CO', 'es-419') // 39 - regional variant
* findMatchingDistance('en', 'fr') // 840 - completely different languages
* ```
*
* @see https://unicode.org/reports/tr35/#EnhancedLanguageMatching
*/
* Calculates the matching distance between two locales using the CLDR Enhanced Language Matching algorithm.
* This function is memoized for performance, as distance calculations are expensive.
*
* The distance represents how "far apart" two locales are, with 0 being identical (after maximization).
* Distances are calculated based on Language-Script-Region (LSR) differences using CLDR data.
*
* @param desired - The desired locale (e.g., "en-US")
* @param supported - The supported locale to compare against (e.g., "en-GB")
* @returns The calculated distance between the locales
*
* @example
* ```ts
* findMatchingDistance('en-US', 'en-US') // 0 - identical
* findMatchingDistance('en-US', 'en-GB') // 40 - same language/script, different region
* findMatchingDistance('es-CO', 'es-419') // 39 - regional variant
* findMatchingDistance('en', 'fr') // 840 - completely different languages
* ```
*
* @see https://unicode.org/reports/tr35/#EnhancedLanguageMatching
*/
export declare const findMatchingDistance: (desired: string, supported: string) => number;
interface LocaleMatchingResult {
distances: Record<string, Record<string, number>>;
matchedSupportedLocale?: string;
matchedDesiredLocale?: string;
distances: Record<string, Record<string, number>>;
matchedSupportedLocale?: string;
matchedDesiredLocale?: string;
}
export declare function findBestMatch(requestedLocales: readonly string[], supportedLocales: readonly string[], threshold?: number): LocaleMatchingResult;
export {};

@@ -1,379 +0,349 @@

import { __spreadArray } from "tslib";
import { memoize } from '@formatjs/fast-memoize';
import { data as jsonData } from './languageMatching.js';
import { regions } from './regions.generated.js';
export var UNICODE_EXTENSION_SEQUENCE_REGEX = /-u(?:-[0-9a-z]{2,8})+/gi;
import { memoize } from "@formatjs/fast-memoize";
import { data as jsonData } from "./languageMatching.js";
import { regions } from "./regions.generated.js";
export const UNICODE_EXTENSION_SEQUENCE_REGEX = /-u(?:-[0-9a-z]{2,8})+/gi;
/**
* Asserts that a condition is true, throwing an error if it is not.
* Used for runtime validation and type narrowing.
*
* @param condition - The condition to check
* @param message - Error message if condition is false
* @param Err - Error constructor to use (defaults to Error)
* @throws {Error} When condition is false
*
* @example
* ```ts
* invariant(locale !== undefined, 'Locale must be defined')
* // locale is now narrowed to non-undefined type
* ```
*/
export function invariant(condition, message, Err) {
if (Err === void 0) { Err = Error; }
if (!condition) {
throw new Err(message);
}
* Asserts that a condition is true, throwing an error if it is not.
* Used for runtime validation and type narrowing.
*
* @param condition - The condition to check
* @param message - Error message if condition is false
* @param Err - Error constructor to use (defaults to Error)
* @throws {Error} When condition is false
*
* @example
* ```ts
* invariant(locale !== undefined, 'Locale must be defined')
* // locale is now narrowed to non-undefined type
* ```
*/
export function invariant(condition, message, Err = Error) {
if (!condition) {
throw new Err(message);
}
}
// This is effectively 2 languages in 2 different regions in the same cluster
var DEFAULT_MATCHING_THRESHOLD = 838;
var PROCESSED_DATA;
const DEFAULT_MATCHING_THRESHOLD = 838;
let PROCESSED_DATA;
function processData() {
var _a, _b;
if (!PROCESSED_DATA) {
var paradigmLocales = (_b = (_a = jsonData.supplemental.languageMatching['written-new'][0]) === null || _a === void 0 ? void 0 : _a.paradigmLocales) === null || _b === void 0 ? void 0 : _b._locales.split(' ');
var matchVariables = jsonData.supplemental.languageMatching['written-new'].slice(1, 5);
var data = jsonData.supplemental.languageMatching['written-new'].slice(5);
var matches = data.map(function (d) {
var key = Object.keys(d)[0];
var value = d[key];
return {
supported: key,
desired: value._desired,
distance: +value._distance,
oneway: value.oneway === 'true' ? true : false,
};
}, {});
PROCESSED_DATA = {
matches: matches,
matchVariables: matchVariables.reduce(function (all, d) {
var key = Object.keys(d)[0];
var value = d[key];
all[key.slice(1)] = value._value.split('+');
return all;
}, {}),
paradigmLocales: __spreadArray(__spreadArray([], paradigmLocales, true), paradigmLocales.map(function (l) {
return new Intl.Locale(l.replace(/_/g, '-')).maximize().toString();
}), true),
};
}
return PROCESSED_DATA;
if (!PROCESSED_DATA) {
const paradigmLocales = jsonData.supplemental.languageMatching["written-new"][0]?.paradigmLocales?._locales.split(" ");
const matchVariables = jsonData.supplemental.languageMatching["written-new"].slice(1, 5);
const data = jsonData.supplemental.languageMatching["written-new"].slice(5);
const matches = data.map((d) => {
const key = Object.keys(d)[0];
const value = d[key];
return {
supported: key,
desired: value._desired,
distance: +value._distance,
oneway: value.oneway === "true" ? true : false
};
}, {});
PROCESSED_DATA = {
matches,
matchVariables: matchVariables.reduce((all, d) => {
const key = Object.keys(d)[0];
const value = d[key];
all[key.slice(1)] = value._value.split("+");
return all;
}, {}),
paradigmLocales: [...paradigmLocales, ...paradigmLocales.map((l) => new Intl.Locale(l.replace(/_/g, "-")).maximize().toString())]
};
}
return PROCESSED_DATA;
}
function isMatched(locale, languageMatchInfoLocale, matchVariables) {
var _a = languageMatchInfoLocale.split('-'), language = _a[0], script = _a[1], region = _a[2];
var matches = true;
if (region && region[0] === '$') {
var shouldInclude = region[1] !== '!';
var matchRegions = shouldInclude
? matchVariables[region.slice(1)]
: matchVariables[region.slice(2)];
var expandedMatchedRegions = matchRegions
.map(function (r) { return regions[r] || [r]; })
.reduce(function (all, list) { return __spreadArray(__spreadArray([], all, true), list, true); }, []);
matches && (matches = !(expandedMatchedRegions.indexOf(locale.region || '') > -1 !=
shouldInclude));
}
else {
matches && (matches = locale.region
? region === '*' || region === locale.region
: true);
}
matches && (matches = locale.script ? script === '*' || script === locale.script : true);
matches && (matches = locale.language
? language === '*' || language === locale.language
: true);
return matches;
const [language, script, region] = languageMatchInfoLocale.split("-");
let matches = true;
if (region && region[0] === "$") {
const shouldInclude = region[1] !== "!";
const matchRegions = shouldInclude ? matchVariables[region.slice(1)] : matchVariables[region.slice(2)];
const expandedMatchedRegions = matchRegions.map((r) => regions[r] || [r]).reduce((all, list) => [...all, ...list], []);
matches &&= !(expandedMatchedRegions.indexOf(locale.region || "") > -1 != shouldInclude);
} else {
matches &&= locale.region ? region === "*" || region === locale.region : true;
}
matches &&= locale.script ? script === "*" || script === locale.script : true;
matches &&= locale.language ? language === "*" || language === locale.language : true;
return matches;
}
function serializeLSR(lsr) {
return [lsr.language, lsr.script, lsr.region].filter(Boolean).join('-');
return [
lsr.language,
lsr.script,
lsr.region
].filter(Boolean).join("-");
}
function findMatchingDistanceForLSR(desired, supported, data) {
for (var _i = 0, _a = data.matches; _i < _a.length; _i++) {
var d = _a[_i];
var matches = isMatched(desired, d.desired, data.matchVariables) &&
isMatched(supported, d.supported, data.matchVariables);
if (!d.oneway && !matches) {
matches =
isMatched(desired, d.supported, data.matchVariables) &&
isMatched(supported, d.desired, data.matchVariables);
}
if (matches) {
var distance = d.distance * 10;
if (data.paradigmLocales.indexOf(serializeLSR(desired)) > -1 !=
data.paradigmLocales.indexOf(serializeLSR(supported)) > -1) {
return distance - 1;
}
return distance;
}
}
throw new Error('No matching distance found');
for (const d of data.matches) {
let matches = isMatched(desired, d.desired, data.matchVariables) && isMatched(supported, d.supported, data.matchVariables);
if (!d.oneway && !matches) {
matches = isMatched(desired, d.supported, data.matchVariables) && isMatched(supported, d.desired, data.matchVariables);
}
if (matches) {
const distance = d.distance * 10;
if (data.paradigmLocales.indexOf(serializeLSR(desired)) > -1 != data.paradigmLocales.indexOf(serializeLSR(supported)) > -1) {
return distance - 1;
}
return distance;
}
}
throw new Error("No matching distance found");
}
function findMatchingDistanceImpl(desired, supported) {
var desiredLocale = new Intl.Locale(desired).maximize();
var supportedLocale = new Intl.Locale(supported).maximize();
var desiredLSR = {
language: desiredLocale.language,
script: desiredLocale.script || '',
region: desiredLocale.region || '',
};
var supportedLSR = {
language: supportedLocale.language,
script: supportedLocale.script || '',
region: supportedLocale.region || '',
};
var matchingDistance = 0;
var data = processData();
if (desiredLSR.language !== supportedLSR.language) {
matchingDistance += findMatchingDistanceForLSR({
language: desiredLocale.language,
script: '',
region: '',
}, {
language: supportedLocale.language,
script: '',
region: '',
}, data);
}
if (desiredLSR.script !== supportedLSR.script) {
matchingDistance += findMatchingDistanceForLSR({
language: desiredLocale.language,
script: desiredLSR.script,
region: '',
}, {
language: supportedLocale.language,
script: supportedLSR.script,
region: '',
}, data);
}
if (desiredLSR.region !== supportedLSR.region) {
matchingDistance += findMatchingDistanceForLSR(desiredLSR, supportedLSR, data);
}
return matchingDistance;
const desiredLocale = new Intl.Locale(desired).maximize();
const supportedLocale = new Intl.Locale(supported).maximize();
const desiredLSR = {
language: desiredLocale.language,
script: desiredLocale.script || "",
region: desiredLocale.region || ""
};
const supportedLSR = {
language: supportedLocale.language,
script: supportedLocale.script || "",
region: supportedLocale.region || ""
};
let matchingDistance = 0;
const data = processData();
if (desiredLSR.language !== supportedLSR.language) {
matchingDistance += findMatchingDistanceForLSR({
language: desiredLocale.language,
script: "",
region: ""
}, {
language: supportedLocale.language,
script: "",
region: ""
}, data);
}
if (desiredLSR.script !== supportedLSR.script) {
matchingDistance += findMatchingDistanceForLSR({
language: desiredLocale.language,
script: desiredLSR.script,
region: ""
}, {
language: supportedLocale.language,
script: supportedLSR.script,
region: ""
}, data);
}
if (desiredLSR.region !== supportedLSR.region) {
matchingDistance += findMatchingDistanceForLSR(desiredLSR, supportedLSR, data);
}
return matchingDistance;
}
/**
* Calculates the matching distance between two locales using the CLDR Enhanced Language Matching algorithm.
* This function is memoized for performance, as distance calculations are expensive.
*
* The distance represents how "far apart" two locales are, with 0 being identical (after maximization).
* Distances are calculated based on Language-Script-Region (LSR) differences using CLDR data.
*
* @param desired - The desired locale (e.g., "en-US")
* @param supported - The supported locale to compare against (e.g., "en-GB")
* @returns The calculated distance between the locales
*
* @example
* ```ts
* findMatchingDistance('en-US', 'en-US') // 0 - identical
* findMatchingDistance('en-US', 'en-GB') // 40 - same language/script, different region
* findMatchingDistance('es-CO', 'es-419') // 39 - regional variant
* findMatchingDistance('en', 'fr') // 840 - completely different languages
* ```
*
* @see https://unicode.org/reports/tr35/#EnhancedLanguageMatching
*/
export var findMatchingDistance = memoize(findMatchingDistanceImpl, {
serializer: function (args) { return "".concat(args[0], "|").concat(args[1]); },
});
* Calculates the matching distance between two locales using the CLDR Enhanced Language Matching algorithm.
* This function is memoized for performance, as distance calculations are expensive.
*
* The distance represents how "far apart" two locales are, with 0 being identical (after maximization).
* Distances are calculated based on Language-Script-Region (LSR) differences using CLDR data.
*
* @param desired - The desired locale (e.g., "en-US")
* @param supported - The supported locale to compare against (e.g., "en-GB")
* @returns The calculated distance between the locales
*
* @example
* ```ts
* findMatchingDistance('en-US', 'en-US') // 0 - identical
* findMatchingDistance('en-US', 'en-GB') // 40 - same language/script, different region
* findMatchingDistance('es-CO', 'es-419') // 39 - regional variant
* findMatchingDistance('en', 'fr') // 840 - completely different languages
* ```
*
* @see https://unicode.org/reports/tr35/#EnhancedLanguageMatching
*/
export const findMatchingDistance = memoize(findMatchingDistanceImpl, { serializer: (args) => `${args[0]}|${args[1]}` });
/**
* Generates fallback candidates by progressively removing subtags
* e.g., "en-US" -> ["en-US", "en"]
* "zh-Hans-CN" -> ["zh-Hans-CN", "zh-Hans", "zh"]
*/
* Generates fallback candidates by progressively removing subtags
* e.g., "en-US" -> ["en-US", "en"]
* "zh-Hans-CN" -> ["zh-Hans-CN", "zh-Hans", "zh"]
*/
function getFallbackCandidates(locale) {
var candidates = [];
var current = locale;
while (current) {
candidates.push(current);
var lastDash = current.lastIndexOf('-');
if (lastDash === -1)
break;
current = current.substring(0, lastDash);
}
return candidates;
const candidates = [];
let current = locale;
while (current) {
candidates.push(current);
const lastDash = current.lastIndexOf("-");
if (lastDash === -1) break;
current = current.substring(0, lastDash);
}
return candidates;
}
/**
* Finds the best locale match using a three-tier optimization hierarchy.
*
* ## Three-Tier Matching Algorithm:
*
* **Tier 1 - Fast Path** (O(n)): Exact string matching via Set lookup
* - Example: 'en' matches 'en' exactly → distance 0
* - Solves #4936: 48x faster than baseline (12ms vs 610ms with 700+ locales)
*
* **Tier 2 - Fallback Path** (O(k×n)): Maximization + progressive subtag removal
* - Maximizes requested locale, then removes subtags right-to-left
* - Example: "zh-TW" → "zh-Hant-TW" → ["zh-Hant-TW", "zh-Hant", "zh"]
* - Distance: 0 for maximized match, 10 per removed subtag + position penalty
* - 40-50x faster than full UTS #35, handles 99% of real-world cases correctly
*
* **Tier 3 - Slow Path** (O(n×m), memoized): Full UTS #35 CLDR matching
* - Calculates Language-Script-Region distances using CLDR data
* - Handles complex cases like cross-script matching (sr-Cyrl ↔ sr-Latn)
* - Only used when Tiers 1 & 2 find no match
* - Still 6x faster than baseline due to memoization
*
* ## Performance Impact of Maximization:
*
* While Tier 2 now calls `Intl.Locale().maximize()` once per requested locale,
* this is still much faster than Tier 3's full distance calculation:
* - Tier 1: ~12ms (exact match, no maximization)
* - Tier 2: ~13-15ms (maximization + fallback)
* - Tier 3: ~100ms+ (full UTS #35 with all supported locales)
*
* @param requestedLocales - Locale identifiers in preference order
* @param supportedLocales - Available locale identifiers
* @param threshold - Maximum distance (default: 838, from CLDR)
* @returns Matching result with distances
*
* @example
* ```ts
* // Tier 1: Exact match
* findBestMatch(['en'], ['en', 'fr'])
* // → { matchedSupportedLocale: 'en', distances: { en: { en: 0 } } }
*
* // Tier 2: Fallback with maximization
* findBestMatch(['zh-TW'], ['zh-Hant'])
* // → zh-TW maximizes to zh-Hant-TW, falls back to zh-Hant (distance 0)
*
* findBestMatch(['en-US'], ['en'])
* // → en-US maximizes to en-Latn-US, falls back to en (distance 10)
*
* // Tier 3: Full calculation
* findBestMatch(['en-XZ'], ['ja', 'ko'])
* // → No fallback match, uses UTS #35 to find closest match
* ```
*
* @see https://unicode.org/reports/tr35/#EnhancedLanguageMatching
* @see https://github.com/formatjs/formatjs/issues/4936
*/
* Finds the best locale match using a three-tier optimization hierarchy.
*
* ## Three-Tier Matching Algorithm:
*
* **Tier 1 - Fast Path** (O(n)): Exact string matching via Set lookup
* - Example: 'en' matches 'en' exactly → distance 0
* - Solves #4936: 48x faster than baseline (12ms vs 610ms with 700+ locales)
*
* **Tier 2 - Fallback Path** (O(k×n)): Maximization + progressive subtag removal
* - Maximizes requested locale, then removes subtags right-to-left
* - Example: "zh-TW" → "zh-Hant-TW" → ["zh-Hant-TW", "zh-Hant", "zh"]
* - Distance: 0 for maximized match, 10 per removed subtag + position penalty
* - 40-50x faster than full UTS #35, handles 99% of real-world cases correctly
*
* **Tier 3 - Slow Path** (O(n×m), memoized): Full UTS #35 CLDR matching
* - Calculates Language-Script-Region distances using CLDR data
* - Handles complex cases like cross-script matching (sr-Cyrl ↔ sr-Latn)
* - Only used when Tiers 1 & 2 find no match
* - Still 6x faster than baseline due to memoization
*
* ## Performance Impact of Maximization:
*
* While Tier 2 now calls `Intl.Locale().maximize()` once per requested locale,
* this is still much faster than Tier 3's full distance calculation:
* - Tier 1: ~12ms (exact match, no maximization)
* - Tier 2: ~13-15ms (maximization + fallback)
* - Tier 3: ~100ms+ (full UTS #35 with all supported locales)
*
* @param requestedLocales - Locale identifiers in preference order
* @param supportedLocales - Available locale identifiers
* @param threshold - Maximum distance (default: 838, from CLDR)
* @returns Matching result with distances
*
* @example
* ```ts
* // Tier 1: Exact match
* findBestMatch(['en'], ['en', 'fr'])
* // → { matchedSupportedLocale: 'en', distances: { en: { en: 0 } } }
*
* // Tier 2: Fallback with maximization
* findBestMatch(['zh-TW'], ['zh-Hant'])
* // → zh-TW maximizes to zh-Hant-TW, falls back to zh-Hant (distance 0)
*
* findBestMatch(['en-US'], ['en'])
* // → en-US maximizes to en-Latn-US, falls back to en (distance 10)
*
* // Tier 3: Full calculation
* findBestMatch(['en-XZ'], ['ja', 'ko'])
* // → No fallback match, uses UTS #35 to find closest match
* ```
*
* @see https://unicode.org/reports/tr35/#EnhancedLanguageMatching
* @see https://github.com/formatjs/formatjs/issues/4936
*/
// WeakMap to cache canonicalized supported locales arrays
var canonicalizedSupportedCache = new WeakMap();
export function findBestMatch(requestedLocales, supportedLocales, threshold) {
var _a;
if (threshold === void 0) { threshold = DEFAULT_MATCHING_THRESHOLD; }
var lowestDistance = Infinity;
var result = {
matchedDesiredLocale: '',
distances: {},
};
// Get or compute canonicalized supported locales (one by one to preserve indices)
var canonicalizedSupportedLocales = canonicalizedSupportedCache.get(supportedLocales);
if (!canonicalizedSupportedLocales) {
canonicalizedSupportedLocales = supportedLocales.map(function (locale) {
try {
var canonical = Intl.getCanonicalLocales([locale]);
return canonical[0] || locale;
}
catch (_a) {
return locale;
}
});
canonicalizedSupportedCache.set(supportedLocales, canonicalizedSupportedLocales);
}
var supportedSet = new Set(canonicalizedSupportedLocales);
// === TIER 1: FAST PATH - Exact Match ===
// Check for exact matches in ALL requested locales
// This is the fastest path and handles the majority of real-world cases
for (var i = 0; i < requestedLocales.length; i++) {
var desired = requestedLocales[i];
if (supportedSet.has(desired)) {
var distance = 0 + i * 40;
result.distances[desired] = (_a = {}, _a[desired] = distance, _a);
if (distance < lowestDistance) {
lowestDistance = distance;
result.matchedDesiredLocale = desired;
result.matchedSupportedLocale = desired;
}
// Only return immediately if this is the first requested locale (distance=0)
// Otherwise, continue checking for potentially better matches
if (i === 0) {
return result;
}
}
}
// If we found an exact match in Tier 1 (but not for first locale), check Tier 2
// to see if there's a better fallback match with lower distance
// If no exact match found, Tier 2 will find fallback matches
// === TIER 2: FALLBACK PATH - Maximization + Progressive Subtag Removal ===
// Try maximization-based matching before resorting to expensive Tier 3
// This handles cases like zh-TW → zh-Hant efficiently
for (var i = 0; i < requestedLocales.length; i++) {
var desired = requestedLocales[i];
// Maximize then fallback (for linguistic accuracy like zh-TW → zh-Hant)
try {
var maximized = new Intl.Locale(desired).maximize().toString();
if (maximized !== desired) {
var maximizedCandidates = getFallbackCandidates(maximized);
for (var j = 0; j < maximizedCandidates.length; j++) {
var candidate = maximizedCandidates[j];
if (candidate === desired)
continue; // Already checked in Tier 1
if (supportedSet.has(candidate)) {
// Check if candidate also maximizes to the same form
// e.g., zh-TW → zh-Hant-TW and zh-Hant → zh-Hant-TW (distance 0)
// but es-co → es-Latn-CO and es → es-Latn-ES (distance 10)
var distance = void 0;
try {
var candidateMaximized = new Intl.Locale(candidate)
.maximize()
.toString();
distance =
candidateMaximized === maximized ? 0 + i * 40 : j * 10 + i * 40;
}
catch (_b) {
distance = j * 10 + i * 40;
}
if (!result.distances[desired]) {
result.distances[desired] = {};
}
result.distances[desired][candidate] = distance;
if (distance < lowestDistance) {
lowestDistance = distance;
result.matchedDesiredLocale = desired;
result.matchedSupportedLocale = candidate;
}
break; // Stop after finding first maximized match
}
}
}
}
catch (_c) {
// Locale maximization failed, continue to Tier 3
}
}
// If Tier 2 found a perfect maximized match (distance 0), return immediately (fast path)
if (result.matchedSupportedLocale && lowestDistance === 0) {
return result;
}
// === TIER 3: SLOW PATH - Full UTS #35 Distance Calculation ===
// Always run Tier 3 for full CLDR accuracy
// Tier 3 may find better matches than Tier 2's fallback approach
// findMatchingDistance is memoized, so repeated calculations are cached
requestedLocales.forEach(function (desired, i) {
if (!result.distances[desired]) {
result.distances[desired] = {};
}
canonicalizedSupportedLocales.forEach(function (canonicalLocale, supportedIndex) {
var originalSupported = supportedLocales[supportedIndex];
// findMatchingDistance is memoized via fast-memoize
// Use the canonical locale for distance calculation
var distance = findMatchingDistance(desired, canonicalLocale);
// Add some weight to the distance based on the order of the supported locales
// Add penalty for the order of the requested locales, which currently is 0 since ECMA-402
// doesn't really have room for weighted locales like `en; q=0.1`
var finalDistance = distance + 0 + i * 40;
// Store and return the original locale, not the canonical one
// Tier 3 overwrites Tier 2 distances (Tier 3 is more accurate)
result.distances[desired][originalSupported] = finalDistance;
if (finalDistance < lowestDistance) {
lowestDistance = finalDistance;
result.matchedDesiredLocale = desired;
result.matchedSupportedLocale = originalSupported;
}
});
});
if (lowestDistance >= threshold) {
result.matchedDesiredLocale = undefined;
result.matchedSupportedLocale = undefined;
}
return result;
const canonicalizedSupportedCache = new WeakMap();
export function findBestMatch(requestedLocales, supportedLocales, threshold = DEFAULT_MATCHING_THRESHOLD) {
let lowestDistance = Infinity;
let result = {
matchedDesiredLocale: "",
distances: {}
};
// Get or compute canonicalized supported locales (one by one to preserve indices)
let canonicalizedSupportedLocales = canonicalizedSupportedCache.get(supportedLocales);
if (!canonicalizedSupportedLocales) {
canonicalizedSupportedLocales = supportedLocales.map((locale) => {
try {
const canonical = Intl.getCanonicalLocales([locale]);
return canonical[0] || locale;
} catch {
return locale;
}
});
canonicalizedSupportedCache.set(supportedLocales, canonicalizedSupportedLocales);
}
const supportedSet = new Set(canonicalizedSupportedLocales);
// === TIER 1: FAST PATH - Exact Match ===
// Check for exact matches in ALL requested locales
// This is the fastest path and handles the majority of real-world cases
for (let i = 0; i < requestedLocales.length; i++) {
const desired = requestedLocales[i];
if (supportedSet.has(desired)) {
const distance = 0 + i * 40;
result.distances[desired] = { [desired]: distance };
if (distance < lowestDistance) {
lowestDistance = distance;
result.matchedDesiredLocale = desired;
result.matchedSupportedLocale = desired;
}
// Only return immediately if this is the first requested locale (distance=0)
// Otherwise, continue checking for potentially better matches
if (i === 0) {
return result;
}
}
}
// If we found an exact match in Tier 1 (but not for first locale), check Tier 2
// to see if there's a better fallback match with lower distance
// If no exact match found, Tier 2 will find fallback matches
// === TIER 2: FALLBACK PATH - Maximization + Progressive Subtag Removal ===
// Try maximization-based matching before resorting to expensive Tier 3
// This handles cases like zh-TW → zh-Hant efficiently
for (let i = 0; i < requestedLocales.length; i++) {
const desired = requestedLocales[i];
// Maximize then fallback (for linguistic accuracy like zh-TW → zh-Hant)
try {
const maximized = new Intl.Locale(desired).maximize().toString();
if (maximized !== desired) {
const maximizedCandidates = getFallbackCandidates(maximized);
for (let j = 0; j < maximizedCandidates.length; j++) {
const candidate = maximizedCandidates[j];
if (candidate === desired) continue;
if (supportedSet.has(candidate)) {
// Check if candidate also maximizes to the same form
// e.g., zh-TW → zh-Hant-TW and zh-Hant → zh-Hant-TW (distance 0)
// but es-co → es-Latn-CO and es → es-Latn-ES (distance 10)
let distance;
try {
const candidateMaximized = new Intl.Locale(candidate).maximize().toString();
distance = candidateMaximized === maximized ? 0 + i * 40 : j * 10 + i * 40;
} catch {
distance = j * 10 + i * 40;
}
if (!result.distances[desired]) {
result.distances[desired] = {};
}
result.distances[desired][candidate] = distance;
if (distance < lowestDistance) {
lowestDistance = distance;
result.matchedDesiredLocale = desired;
result.matchedSupportedLocale = candidate;
}
break;
}
}
}
} catch {}
}
// If Tier 2 found a perfect maximized match (distance 0), return immediately (fast path)
if (result.matchedSupportedLocale && lowestDistance === 0) {
return result;
}
// === TIER 3: SLOW PATH - Full UTS #35 Distance Calculation ===
// Always run Tier 3 for full CLDR accuracy
// Tier 3 may find better matches than Tier 2's fallback approach
// findMatchingDistance is memoized, so repeated calculations are cached
requestedLocales.forEach((desired, i) => {
if (!result.distances[desired]) {
result.distances[desired] = {};
}
canonicalizedSupportedLocales.forEach((canonicalLocale, supportedIndex) => {
const originalSupported = supportedLocales[supportedIndex];
// findMatchingDistance is memoized via fast-memoize
// Use the canonical locale for distance calculation
const distance = findMatchingDistance(desired, canonicalLocale);
// Add some weight to the distance based on the order of the supported locales
// Add penalty for the order of the requested locales, which currently is 0 since ECMA-402
// doesn't really have room for weighted locales like `en; q=0.1`
const finalDistance = distance + 0 + i * 40;
// Store and return the original locale, not the canonical one
// Tier 3 overwrites Tier 2 distances (Tier 3 is more accurate)
result.distances[desired][originalSupported] = finalDistance;
if (finalDistance < lowestDistance) {
lowestDistance = finalDistance;
result.matchedDesiredLocale = desired;
result.matchedSupportedLocale = originalSupported;
}
});
});
if (lowestDistance >= threshold) {
result.matchedDesiredLocale = undefined;
result.matchedSupportedLocale = undefined;
}
return result;
}
export interface Opts {
algorithm: 'lookup' | 'best fit';
algorithm: "lookup" | "best fit";
}
export declare function match(requestedLocales: readonly string[], availableLocales: readonly string[], defaultLocale: string, opts?: Opts): string;
export { LookupSupportedLocales } from './abstract/LookupSupportedLocales.js';
export { ResolveLocale } from './abstract/ResolveLocale.js';
export { LookupSupportedLocales } from "./abstract/LookupSupportedLocales.js";
export { ResolveLocale } from "./abstract/ResolveLocale.js";

@@ -1,9 +0,7 @@

import { CanonicalizeLocaleList } from './abstract/CanonicalizeLocaleList.js';
import { ResolveLocale } from './abstract/ResolveLocale.js';
import { CanonicalizeLocaleList } from "./abstract/CanonicalizeLocaleList.js";
import { ResolveLocale } from "./abstract/ResolveLocale.js";
export function match(requestedLocales, availableLocales, defaultLocale, opts) {
return ResolveLocale(availableLocales, CanonicalizeLocaleList(requestedLocales), {
localeMatcher: (opts === null || opts === void 0 ? void 0 : opts.algorithm) || 'best fit',
}, [], {}, function () { return defaultLocale; }).locale;
return ResolveLocale(availableLocales, CanonicalizeLocaleList(requestedLocales), { localeMatcher: opts?.algorithm || "best fit" }, [], {}, () => defaultLocale).locale;
}
export { LookupSupportedLocales } from './abstract/LookupSupportedLocales.js';
export { ResolveLocale } from './abstract/ResolveLocale.js';
export { LookupSupportedLocales } from "./abstract/LookupSupportedLocales.js";
export { ResolveLocale } from "./abstract/ResolveLocale.js";
{
"name": "@formatjs/intl-localematcher",
"description": "Intl.LocaleMatcher ponyfill",
"version": "0.7.4",
"version": "0.7.5",
"license": "MIT",

@@ -15,3 +15,3 @@ "author": "Long Ho <holevietlong@gmail.com>",

"tslib": "^2.8.0",
"@formatjs/fast-memoize": "3.0.2"
"@formatjs/fast-memoize": "3.0.3"
},

@@ -18,0 +18,0 @@ "bugs": "https://github.com/formatjs/formatjs/issues",

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display