UNPKG

@formatjs/intl-displaynames

Version:

Polyfill for: https://tc39.es/proposal-intl-displaynames

211 lines (210 loc) 10.3 kB
import { __assign } from "tslib"; import { CanonicalizeLocaleList, GetOption, GetOptionsObject, IsWellFormedCurrencyCode, SupportedLocales, ToString, getInternalSlot, getMultiInternalSlots, invariant, setInternalSlot, } from '@formatjs/ecma402-abstract'; import { CanonicalCodeForDisplayNames } from './abstract/CanonicalCodeForDisplayNames'; import { IsValidDateTimeFieldCode } from './abstract/IsValidDateTimeFieldCode'; import { ResolveLocale } from '@formatjs/intl-localematcher'; var DisplayNames = /** @class */ (function () { function DisplayNames(locales, options) { var _newTarget = this.constructor; if (_newTarget === undefined) { throw TypeError("Constructor Intl.DisplayNames requires 'new'"); } var requestedLocales = CanonicalizeLocaleList(locales); options = GetOptionsObject(options); var opt = Object.create(null); var localeData = DisplayNames.localeData; var matcher = GetOption(options, 'localeMatcher', 'string', ['lookup', 'best fit'], 'best fit'); opt.localeMatcher = matcher; var r = ResolveLocale(Array.from(DisplayNames.availableLocales), requestedLocales, opt, [], // there is no relevantExtensionKeys DisplayNames.localeData, DisplayNames.getDefaultLocale); var style = GetOption(options, 'style', 'string', ['narrow', 'short', 'long'], 'long'); setSlot(this, 'style', style); var type = GetOption(options, 'type', 'string', ['language', 'region', 'script', 'currency', 'calendar', 'dateTimeField'], undefined); if (type === undefined) { throw TypeError("Intl.DisplayNames constructor requires \"type\" option"); } setSlot(this, 'type', type); var fallback = GetOption(options, 'fallback', 'string', ['code', 'none'], 'code'); setSlot(this, 'fallback', fallback); setSlot(this, 'locale', r.locale); var dataLocale = r.dataLocale; var dataLocaleData = localeData[dataLocale]; invariant(!!dataLocaleData, "Missing locale data for ".concat(dataLocale)); setSlot(this, 'localeData', dataLocaleData); invariant(dataLocaleData !== undefined, "locale data for ".concat(r.locale, " does not exist.")); var types = dataLocaleData.types; invariant(typeof types === 'object' && types != null, 'invalid types data'); var typeFields = types[type]; invariant(typeof typeFields === 'object' && typeFields != null, 'invalid typeFields data'); var languageDisplay = GetOption(options, 'languageDisplay', 'string', ['dialect', 'standard'], 'dialect'); if (type === 'language') { setSlot(this, 'languageDisplay', languageDisplay); // Using types[type] instead of typeFields because TypeScript cannot infer the correct type var typeFields_1 = types[type][languageDisplay]; invariant(typeof typeFields_1 === 'object' && typeFields_1 != null, 'invalid language typeFields data'); } // Using types[type] instead of typeFields because TypeScript cannot infer the correct type var styleFields = type === 'language' ? types[type][languageDisplay][style] : types[type][style]; invariant(typeof styleFields === 'object' && styleFields != null, 'invalid styleFields data'); setSlot(this, 'fields', styleFields); } DisplayNames.supportedLocalesOf = function (locales, options) { return SupportedLocales(DisplayNames.availableLocales, CanonicalizeLocaleList(locales), options); }; DisplayNames.__addLocaleData = function () { var data = []; for (var _i = 0; _i < arguments.length; _i++) { data[_i] = arguments[_i]; } for (var _a = 0, data_1 = data; _a < data_1.length; _a++) { var _b = data_1[_a], d = _b.data, locale = _b.locale; var minimizedLocale = new Intl.Locale(locale) .minimize() .toString(); DisplayNames.localeData[locale] = DisplayNames.localeData[minimizedLocale] = d; DisplayNames.availableLocales.add(minimizedLocale); DisplayNames.availableLocales.add(locale); if (!DisplayNames.__defaultLocale) { DisplayNames.__defaultLocale = minimizedLocale; } } }; DisplayNames.prototype.of = function (code) { checkReceiver(this, 'of'); var type = getSlot(this, 'type'); var codeAsString = ToString(code); if (!isValidCodeForDisplayNames(type, codeAsString)) { throw RangeError('invalid code for Intl.DisplayNames.prototype.of'); } var _a = getMultiInternalSlots(__INTERNAL_SLOT_MAP__, this, 'localeData', 'style', 'fallback'), localeData = _a.localeData, style = _a.style, fallback = _a.fallback; // Canonicalize the case. var canonicalCode = CanonicalCodeForDisplayNames(type, codeAsString); var name; if (type === 'language') { var languageDisplay = getSlot(this, 'languageDisplay'); name = getNameForTypeLanguage(languageDisplay, localeData, style, canonicalCode, fallback); } else { // All the other types var typesData = localeData.types[type]; name = typesData[style][canonicalCode] || typesData.long[canonicalCode]; } if (name !== undefined) { return name; } if (fallback === 'code') { return codeAsString; } }; DisplayNames.prototype.resolvedOptions = function () { checkReceiver(this, 'resolvedOptions'); return __assign({}, getMultiInternalSlots(__INTERNAL_SLOT_MAP__, this, 'locale', 'style', 'type', 'fallback', 'languageDisplay')); }; DisplayNames.getDefaultLocale = function () { return DisplayNames.__defaultLocale; }; DisplayNames.localeData = {}; DisplayNames.availableLocales = new Set(); DisplayNames.__defaultLocale = ''; DisplayNames.polyfilled = true; return DisplayNames; }()); export { DisplayNames }; // https://tc39.es/proposal-intl-displaynames/#sec-isvalidcodefordisplaynames function isValidCodeForDisplayNames(type, code) { switch (type) { case 'language': // subset of unicode_language_id // languageCode ["-" scriptCode] ["-" regionCode] *("-" variant) // where: // - languageCode is either a two letters ISO 639-1 language code or a three letters ISO 639-2 language code. // - scriptCode is should be an ISO-15924 four letters script code // - regionCode is either an ISO-3166 two letters region code, or a three digits UN M49 Geographic Regions. return /^[a-z]{2,3}(-[a-z]{4})?(-([a-z]{2}|\d{3}))?(-([a-z\d]{5,8}|\d[a-z\d]{3}))*$/i.test(code); case 'region': // unicode_region_subtag return /^([a-z]{2}|\d{3})$/i.test(code); case 'script': // unicode_script_subtag return /^[a-z]{4}$/i.test(code); case 'currency': return IsWellFormedCurrencyCode(code); case 'calendar': // unicode locale identifier type return /^[a-z0-9]{3,8}([-_][a-z0-9]{3,8})*$/i.test(code); case 'dateTimeField': return IsValidDateTimeFieldCode(code); } } try { // IE11 does not have Symbol if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { Object.defineProperty(DisplayNames.prototype, Symbol.toStringTag, { value: 'Intl.DisplayNames', configurable: true, enumerable: false, writable: false, }); } Object.defineProperty(DisplayNames, 'length', { value: 2, writable: false, enumerable: false, configurable: true, }); } catch (e) { // Make test 262 compliant } var __INTERNAL_SLOT_MAP__ = new WeakMap(); function getSlot(instance, key) { return getInternalSlot(__INTERNAL_SLOT_MAP__, instance, key); } function setSlot(instance, key, value) { setInternalSlot(__INTERNAL_SLOT_MAP__, instance, key, value); } function checkReceiver(receiver, methodName) { if (!(receiver instanceof DisplayNames)) { throw TypeError("Method Intl.DisplayNames.prototype.".concat(methodName, " called on incompatible receiver")); } } function getNameForTypeLanguage(languageDisplay, localeData, style, canonicalCode, fallback) { // First, try to get the name using the canonicalCode var typesData = localeData.types.language[languageDisplay]; var name = typesData[style][canonicalCode] || typesData.long[canonicalCode]; if (name === undefined) { // If no name has been found using the canonicalCode, // check if the latter contains a region sub tag var regionMatch = /-([a-z]{2}|\d{3})\b/i.exec(canonicalCode); if (regionMatch) { // Extract the language and region sub tags var languageSubTag = canonicalCode.substring(0, regionMatch.index) + canonicalCode.substring(regionMatch.index + regionMatch[0].length); var regionSubTag = regionMatch[1]; // Let's try again using languageSubTag this time var name_1 = typesData[style][languageSubTag] || typesData.long[languageSubTag]; // If a name has been found and a region sub tag exists, // compose them together or use the code fallback if (name_1 !== undefined && regionSubTag) { // Retrieve region display names var regionsData = localeData.types.region; var regionDisplayName = regionsData[style][regionSubTag] || regionsData.long[regionSubTag]; if (regionDisplayName || fallback === 'code') { // Interpolate into locale-specific pattern. var pattern = localeData.patterns.locale; return pattern .replace('{0}', name_1) .replace('{1}', regionDisplayName || regionSubTag); } } else { return name_1; } } } else { return name; } }