mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-03 14:28:39 +09:00
Add pluralrules to JS Intl
This commit is contained in:
parent
49676c9254
commit
ea1e157289
3 changed files with 542 additions and 134 deletions
|
|
@ -9,7 +9,8 @@
|
|||
JSMSG_INVALID_OPTION_VALUE: false, JSMSG_INVALID_DIGITS_VALUE: false,
|
||||
JSMSG_INTL_OBJECT_REINITED: false, JSMSG_INVALID_CURRENCY_CODE: false,
|
||||
JSMSG_UNDEFINED_CURRENCY: false, JSMSG_INVALID_TIME_ZONE: false,
|
||||
JSMSG_DATE_NOT_FINITE: false,
|
||||
JSMSG_DATE_NOT_FINITE: false, JSMSG_INVALID_KEYS_TYPE: false,
|
||||
JSMSG_INVALID_KEY: false,
|
||||
intl_Collator_availableLocales: false,
|
||||
intl_availableCollations: false,
|
||||
intl_CompareStrings: false,
|
||||
|
|
@ -20,6 +21,9 @@
|
|||
intl_availableCalendars: false,
|
||||
intl_patternForSkeleton: false,
|
||||
intl_FormatDateTime: false,
|
||||
intl_SelectPluralRule: false,
|
||||
intl_GetPluralCategories: false,
|
||||
intl_GetCalendarInfo: false,
|
||||
*/
|
||||
|
||||
/*
|
||||
|
|
@ -432,7 +436,7 @@ function CanonicalizeLanguageTag(locale) {
|
|||
subtags[i] = subtag;
|
||||
i++;
|
||||
}
|
||||
var normal = callFunction(std_Array_join, callFunction(std_Array_slice, subtags, 0, i), "-");
|
||||
var normal = ArrayJoinRange(subtags, "-", 0, i);
|
||||
|
||||
// Extension sequences are sorted by their singleton characters.
|
||||
// "u-ca-chinese-t-zh-latn" -> "t-zh-latn-u-ca-chinese"
|
||||
|
|
@ -442,7 +446,7 @@ function CanonicalizeLanguageTag(locale) {
|
|||
i++;
|
||||
while (i < subtags.length && subtags[i].length > 1)
|
||||
i++;
|
||||
var extension = callFunction(std_Array_join, callFunction(std_Array_slice, subtags, extensionStart, i), "-");
|
||||
var extension = ArrayJoinRange(subtags, "-", extensionStart, i);
|
||||
callFunction(std_Array_push, extensions, extension);
|
||||
}
|
||||
callFunction(std_Array_sort, extensions);
|
||||
|
|
@ -450,7 +454,7 @@ function CanonicalizeLanguageTag(locale) {
|
|||
// Private use sequences are left as is. "x-private"
|
||||
var privateUse = "";
|
||||
if (i < subtags.length)
|
||||
privateUse = callFunction(std_Array_join, callFunction(std_Array_slice, subtags, i), "-");
|
||||
privateUse = ArrayJoinRange(subtags, "-", i);
|
||||
|
||||
// Put everything back together.
|
||||
var canonical = normal;
|
||||
|
|
@ -467,6 +471,24 @@ function CanonicalizeLanguageTag(locale) {
|
|||
return canonical;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins the array elements in the given range with the supplied separator.
|
||||
*/
|
||||
function ArrayJoinRange(array, separator, from, to = array.length) {
|
||||
assert(typeof separator === "string", "|separator| is a string value");
|
||||
assert(typeof from === "number", "|from| is a number value");
|
||||
assert(typeof to === "number", "|to| is a number value");
|
||||
assert(0 <= from && from <= to && to <= array.length, "|from| and |to| form a valid range");
|
||||
|
||||
if (from === to)
|
||||
return "";
|
||||
|
||||
var result = array[from];
|
||||
for (var i = from + 1; i < to; i++) {
|
||||
result += separator + array[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function localeContainsNoUnicodeExtensions(locale) {
|
||||
// No "-u-", no possible Unicode extension.
|
||||
|
|
@ -838,6 +860,7 @@ function BestAvailableLocaleIgnoringDefault(availableLocales, locale) {
|
|||
return BestAvailableLocaleHelper(availableLocales, locale, false);
|
||||
}
|
||||
|
||||
var noRelevantExtensionKeys = [];
|
||||
|
||||
/**
|
||||
* Compares a BCP 47 language priority list against the set of locales in
|
||||
|
|
@ -1165,9 +1188,10 @@ function GetOption(options, property, type, values, fallback) {
|
|||
* Spec: ECMAScript Internationalization API Specification, 9.2.10.
|
||||
*/
|
||||
function GetNumberOption(options, property, minimum, maximum, fallback) {
|
||||
assert(typeof minimum === "number", "GetNumberOption");
|
||||
assert(typeof maximum === "number", "GetNumberOption");
|
||||
assert(fallback === undefined || (fallback >= minimum && fallback <= maximum), "GetNumberOption");
|
||||
assert(typeof minimum === "number" && (minimum | 0) === minimum, "GetNumberOption");
|
||||
assert(typeof maximum === "number" && (maximum | 0) === maximum, "GetNumberOption");
|
||||
assert(typeof fallback === "number" && (fallback | 0) === fallback, "GetNumberOption");
|
||||
assert(minimum <= fallback && fallback <= maximum, "GetNumberOption");
|
||||
|
||||
// Step 1.
|
||||
var value = options[property];
|
||||
|
|
@ -1177,7 +1201,10 @@ function GetNumberOption(options, property, minimum, maximum, fallback) {
|
|||
value = ToNumber(value);
|
||||
if (Number_isNaN(value) || value < minimum || value > maximum)
|
||||
ThrowRangeError(JSMSG_INVALID_DIGITS_VALUE, value);
|
||||
return std_Math_floor(value);
|
||||
|
||||
// Apply bitwise-or to convert -0 to +0 per ES2017, 5.2 and to ensure
|
||||
// the result is an int32 value.
|
||||
return std_Math_floor(value) | 0;
|
||||
}
|
||||
|
||||
// Step 3.
|
||||
|
|
@ -1251,7 +1278,9 @@ function initializeIntlObject(obj) {
|
|||
function setLazyData(internals, type, lazyData)
|
||||
{
|
||||
assert(internals.type === "partial", "can't set lazy data for anything but a newborn");
|
||||
assert(type === "Collator" || type === "DateTimeFormat" || type == "NumberFormat", "bad type");
|
||||
assert(type === "Collator" || type === "DateTimeFormat" ||
|
||||
type == "NumberFormat" || type === "PluralRules",
|
||||
"bad type");
|
||||
assert(IsObject(lazyData), "non-object lazy data");
|
||||
|
||||
// Set in reverse order so that the .type change is a barrier.
|
||||
|
|
@ -1301,7 +1330,9 @@ function isInitializedIntlObject(obj) {
|
|||
if (IsObject(internals)) {
|
||||
assert(callFunction(std_Object_hasOwnProperty, internals, "type"), "missing type");
|
||||
var type = internals.type;
|
||||
assert(type === "partial" || type === "Collator" || type === "DateTimeFormat" || type === "NumberFormat", "unexpected type");
|
||||
assert(type === "partial" || type === "Collator" ||
|
||||
type === "DateTimeFormat" || type === "NumberFormat" || type === "PluralRules",
|
||||
"unexpected type");
|
||||
assert(callFunction(std_Object_hasOwnProperty, internals, "lazyData"), "missing lazyData");
|
||||
assert(callFunction(std_Object_hasOwnProperty, internals, "internalProps"), "missing internalProps");
|
||||
} else {
|
||||
|
|
@ -1358,6 +1389,8 @@ function getInternals(obj)
|
|||
internalProps = resolveCollatorInternals(lazyData)
|
||||
else if (type === "DateTimeFormat")
|
||||
internalProps = resolveDateTimeFormatInternals(lazyData)
|
||||
else if (type === "PluralRules")
|
||||
internalProps = resolvePluralRulesInternals(lazyData)
|
||||
else
|
||||
internalProps = resolveNumberFormatInternals(lazyData);
|
||||
setInternalProperties(internals, internalProps);
|
||||
|
|
@ -1689,6 +1722,7 @@ function Intl_Collator_compare_get() {
|
|||
// Step 2.
|
||||
return internals.boundCompare;
|
||||
}
|
||||
_SetCanonicalName(Intl_Collator_compare_get, "get compare");
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -1757,45 +1791,37 @@ function resolveNumberFormatInternals(lazyNumberFormatData) {
|
|||
// Step 6.
|
||||
var opt = lazyNumberFormatData.opt;
|
||||
|
||||
// Compute effective locale.
|
||||
// Step 9.
|
||||
var NumberFormat = numberFormatInternalProperties;
|
||||
|
||||
// Step 10.
|
||||
// Step 9.
|
||||
var localeData = NumberFormat.localeData;
|
||||
|
||||
// Step 11.
|
||||
// Step 10.
|
||||
var r = ResolveLocale(callFunction(NumberFormat.availableLocales, NumberFormat),
|
||||
lazyNumberFormatData.requestedLocales,
|
||||
lazyNumberFormatData.opt,
|
||||
NumberFormat.relevantExtensionKeys,
|
||||
localeData);
|
||||
|
||||
// Steps 12-13. (Step 14 is not relevant to our implementation.)
|
||||
// Steps 11-12. (Step 13 is not relevant to our implementation.)
|
||||
internalProps.locale = r.locale;
|
||||
internalProps.numberingSystem = r.nu;
|
||||
|
||||
// Compute formatting options.
|
||||
// Step 16.
|
||||
var s = lazyNumberFormatData.style;
|
||||
internalProps.style = s;
|
||||
// Step 15.
|
||||
var style = lazyNumberFormatData.style;
|
||||
internalProps.style = style;
|
||||
|
||||
// Steps 20, 22.
|
||||
if (s === "currency") {
|
||||
// Steps 19, 21.
|
||||
if (style === "currency") {
|
||||
internalProps.currency = lazyNumberFormatData.currency;
|
||||
internalProps.currencyDisplay = lazyNumberFormatData.currencyDisplay;
|
||||
}
|
||||
|
||||
// Step 24.
|
||||
internalProps.minimumIntegerDigits = lazyNumberFormatData.minimumIntegerDigits;
|
||||
|
||||
// Steps 27.
|
||||
internalProps.minimumFractionDigits = lazyNumberFormatData.minimumFractionDigits;
|
||||
|
||||
// Step 30.
|
||||
internalProps.maximumFractionDigits = lazyNumberFormatData.maximumFractionDigits;
|
||||
|
||||
// Step 33.
|
||||
if ("minimumSignificantDigits" in lazyNumberFormatData) {
|
||||
// Note: Intl.NumberFormat.prototype.resolvedOptions() exposes the
|
||||
// actual presence (versus undefined-ness) of these properties.
|
||||
|
|
@ -1804,10 +1830,10 @@ function resolveNumberFormatInternals(lazyNumberFormatData) {
|
|||
internalProps.maximumSignificantDigits = lazyNumberFormatData.maximumSignificantDigits;
|
||||
}
|
||||
|
||||
// Step 35.
|
||||
// Step 27.
|
||||
internalProps.useGrouping = lazyNumberFormatData.useGrouping;
|
||||
|
||||
// Step 42.
|
||||
// Step 34.
|
||||
internalProps.boundFormat = undefined;
|
||||
|
||||
// The caller is responsible for associating |internalProps| with the right
|
||||
|
|
@ -1835,6 +1861,44 @@ function getNumberFormatInternals(obj, methodName) {
|
|||
return internalProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies digit options used for number formatting onto the intl object.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 11.1.1.
|
||||
*/
|
||||
function SetNumberFormatDigitOptions(lazyData, options, mnfdDefault, mxfdDefault) {
|
||||
// We skip Step 1 because we set the properties on a lazyData object.
|
||||
|
||||
// Step 2-3.
|
||||
assert(IsObject(options), "SetNumberFormatDigitOptions");
|
||||
assert(typeof mnfdDefault === "number", "SetNumberFormatDigitOptions");
|
||||
assert(typeof mxfdDefault === "number", "SetNumberFormatDigitOptions");
|
||||
assert(mnfdDefault <= mxfdDefault, "SetNumberFormatDigitOptions");
|
||||
|
||||
// Steps 4-6.
|
||||
const mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1);
|
||||
const mnfd = GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault);
|
||||
const mxfdActualDefault = std_Math_max(mnfd, mxfdDefault);
|
||||
const mxfd = GetNumberOption(options, "maximumFractionDigits", mnfd, 20, mxfdActualDefault);
|
||||
|
||||
// Steps 7-8.
|
||||
let mnsd = options.minimumSignificantDigits;
|
||||
let mxsd = options.maximumSignificantDigits;
|
||||
|
||||
// Steps 9-11.
|
||||
lazyData.minimumIntegerDigits = mnid;
|
||||
lazyData.minimumFractionDigits = mnfd;
|
||||
lazyData.maximumFractionDigits = mxfd;
|
||||
|
||||
// Step 12.
|
||||
if (mnsd !== undefined || mxsd !== undefined) {
|
||||
mnsd = GetNumberOption(options, "minimumSignificantDigits", 1, 21, 1);
|
||||
mxsd = GetNumberOption(options, "maximumSignificantDigits", mnsd, 21, 21);
|
||||
lazyData.minimumSignificantDigits = mnsd;
|
||||
lazyData.maximumSignificantDigits = mxsd;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes an object as a NumberFormat.
|
||||
|
|
@ -1884,7 +1948,7 @@ function InitializeNumberFormat(numberFormat, locales, options) {
|
|||
// }
|
||||
//
|
||||
// Note that lazy data is only installed as a final step of initialization,
|
||||
// so every Collator lazy data object has *all* these properties, never a
|
||||
// so every NumberFormat lazy data object has *all* these properties, never a
|
||||
// subset of them.
|
||||
var lazyNumberFormatData = std_Object_create(null);
|
||||
|
||||
|
|
@ -1914,67 +1978,46 @@ function InitializeNumberFormat(numberFormat, locales, options) {
|
|||
opt.localeMatcher = matcher;
|
||||
|
||||
// Compute formatting options.
|
||||
// Step 15.
|
||||
var s = GetOption(options, "style", "string", ["decimal", "percent", "currency"], "decimal");
|
||||
lazyNumberFormatData.style = s;
|
||||
// Step 14.
|
||||
var style = GetOption(options, "style", "string", ["decimal", "percent", "currency"], "decimal");
|
||||
lazyNumberFormatData.style = style;
|
||||
|
||||
// Steps 17-20.
|
||||
// Steps 16-19.
|
||||
var c = GetOption(options, "currency", "string", undefined, undefined);
|
||||
if (c !== undefined && !IsWellFormedCurrencyCode(c))
|
||||
ThrowRangeError(JSMSG_INVALID_CURRENCY_CODE, c);
|
||||
var cDigits;
|
||||
if (s === "currency") {
|
||||
if (style === "currency") {
|
||||
if (c === undefined)
|
||||
ThrowTypeError(JSMSG_UNDEFINED_CURRENCY);
|
||||
|
||||
// Steps 20.a-c.
|
||||
// Steps 19.a-c.
|
||||
c = toASCIIUpperCase(c);
|
||||
lazyNumberFormatData.currency = c;
|
||||
cDigits = CurrencyDigits(c);
|
||||
}
|
||||
|
||||
// Step 21.
|
||||
// Step 20.
|
||||
var cd = GetOption(options, "currencyDisplay", "string", ["code", "symbol", "name"], "symbol");
|
||||
if (s === "currency")
|
||||
if (style === "currency")
|
||||
lazyNumberFormatData.currencyDisplay = cd;
|
||||
|
||||
// Step 23.
|
||||
var mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1);
|
||||
lazyNumberFormatData.minimumIntegerDigits = mnid;
|
||||
|
||||
// Steps 25-26.
|
||||
var mnfdDefault = (s === "currency") ? cDigits : 0;
|
||||
var mnfd = GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault);
|
||||
lazyNumberFormatData.minimumFractionDigits = mnfd;
|
||||
|
||||
// Steps 28-29.
|
||||
var mxfdDefault;
|
||||
if (s === "currency")
|
||||
mxfdDefault = std_Math_max(mnfd, cDigits);
|
||||
else if (s === "percent")
|
||||
mxfdDefault = std_Math_max(mnfd, 0);
|
||||
else
|
||||
mxfdDefault = std_Math_max(mnfd, 3);
|
||||
var mxfd = GetNumberOption(options, "maximumFractionDigits", mnfd, 20, mxfdDefault);
|
||||
lazyNumberFormatData.maximumFractionDigits = mxfd;
|
||||
|
||||
// Steps 31-32.
|
||||
var mnsd = options.minimumSignificantDigits;
|
||||
var mxsd = options.maximumSignificantDigits;
|
||||
|
||||
// Step 33.
|
||||
if (mnsd !== undefined || mxsd !== undefined) {
|
||||
mnsd = GetNumberOption(options, "minimumSignificantDigits", 1, 21, 1);
|
||||
mxsd = GetNumberOption(options, "maximumSignificantDigits", mnsd, 21, 21);
|
||||
lazyNumberFormatData.minimumSignificantDigits = mnsd;
|
||||
lazyNumberFormatData.maximumSignificantDigits = mxsd;
|
||||
// Steps 22-25.
|
||||
var mnfdDefault, mxfdDefault;
|
||||
if (style === "currency") {
|
||||
mnfdDefault = cDigits;
|
||||
mxfdDefault = cDigits;
|
||||
} else {
|
||||
mnfdDefault = 0;
|
||||
mxfdDefault = style === "percent" ? 0 : 3;
|
||||
}
|
||||
SetNumberFormatDigitOptions(lazyNumberFormatData, options, mnfdDefault, mxfdDefault);
|
||||
|
||||
// Step 34.
|
||||
// Steps 26.
|
||||
var g = GetOption(options, "useGrouping", "boolean", undefined, true);
|
||||
lazyNumberFormatData.useGrouping = g;
|
||||
|
||||
// Step 43.
|
||||
// Steps 35-36.
|
||||
//
|
||||
// We've done everything that must be done now: mark the lazy data as fully
|
||||
// computed and install it.
|
||||
|
|
@ -1982,43 +2025,6 @@ function InitializeNumberFormat(numberFormat, locales, options) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mapping from currency codes to the number of decimal digits used for them.
|
||||
* Default is 2 digits.
|
||||
*
|
||||
* Spec: ISO 4217 Currency and Funds Code List.
|
||||
* http://www.currency-iso.org/en/home/tables/table-a1.html
|
||||
*/
|
||||
var currencyDigits = {
|
||||
BHD: 3,
|
||||
BIF: 0,
|
||||
BYR: 0,
|
||||
CLF: 4,
|
||||
CLP: 0,
|
||||
DJF: 0,
|
||||
GNF: 0,
|
||||
IQD: 3,
|
||||
ISK: 0,
|
||||
JOD: 3,
|
||||
JPY: 0,
|
||||
KMF: 0,
|
||||
KRW: 0,
|
||||
KWD: 3,
|
||||
LYD: 3,
|
||||
OMR: 3,
|
||||
PYG: 0,
|
||||
RWF: 0,
|
||||
TND: 3,
|
||||
UGX: 0,
|
||||
UYI: 0,
|
||||
VND: 0,
|
||||
VUV: 0,
|
||||
XAF: 0,
|
||||
XOF: 0,
|
||||
XPF: 0
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of decimal digits to be used for the given currency.
|
||||
*
|
||||
|
|
@ -2100,7 +2106,7 @@ function numberFormatFormatToBind(value) {
|
|||
|
||||
// Step 1.a.ii-iii.
|
||||
var x = ToNumber(value);
|
||||
return intl_FormatNumber(this, x);
|
||||
return intl_FormatNumber(this, x, /* formatToParts = */ false);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2127,6 +2133,22 @@ function Intl_NumberFormat_format_get() {
|
|||
// Step 2.
|
||||
return internals.boundFormat;
|
||||
}
|
||||
_SetCanonicalName(Intl_NumberFormat_format_get, "get format");
|
||||
|
||||
|
||||
function Intl_NumberFormat_formatToParts(value) {
|
||||
// Step 1.
|
||||
var nf = this;
|
||||
|
||||
// Steps 2-3.
|
||||
getNumberFormatInternals(nf, "formatToParts");
|
||||
|
||||
// Step 4.
|
||||
var x = ToNumber(value);
|
||||
|
||||
// Step 5.
|
||||
return intl_FormatNumber(nf, x, /* formatToParts = */ true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -2268,26 +2290,6 @@ function getDateTimeFormatInternals(obj, methodName) {
|
|||
return internalProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Components of date and time formats and their values.
|
||||
*
|
||||
* Spec: ECMAScript Internationalization API Specification, 12.1.1.
|
||||
*/
|
||||
var dateTimeComponentValues = {
|
||||
weekday: ["narrow", "short", "long"],
|
||||
era: ["narrow", "short", "long"],
|
||||
year: ["2-digit", "numeric"],
|
||||
month: ["2-digit", "numeric", "narrow", "short", "long"],
|
||||
day: ["2-digit", "numeric"],
|
||||
hour: ["2-digit", "numeric"],
|
||||
minute: ["2-digit", "numeric"],
|
||||
second: ["2-digit", "numeric"],
|
||||
timeZoneName: ["short", "long"]
|
||||
};
|
||||
|
||||
|
||||
var dateTimeComponents = std_Object_getOwnPropertyNames(dateTimeComponentValues);
|
||||
|
||||
|
||||
/**
|
||||
* Initializes an object as a DateTimeFormat.
|
||||
|
|
@ -2377,12 +2379,19 @@ function InitializeDateTimeFormat(dateTimeFormat, locales, options) {
|
|||
lazyDateTimeFormatData.formatOpt = formatOpt;
|
||||
|
||||
// Step 19.
|
||||
var i, prop;
|
||||
for (i = 0; i < dateTimeComponents.length; i++) {
|
||||
prop = dateTimeComponents[i];
|
||||
var value = GetOption(options, prop, "string", dateTimeComponentValues[prop], undefined);
|
||||
formatOpt[prop] = value;
|
||||
}
|
||||
// 12.1, Table 4: Components of date and time formats.
|
||||
formatOpt.weekday = GetOption(options, "weekday", "string", ["narrow", "short", "long"],
|
||||
undefined);
|
||||
formatOpt.era = GetOption(options, "era", "string", ["narrow", "short", "long"], undefined);
|
||||
formatOpt.year = GetOption(options, "year", "string", ["2-digit", "numeric"], undefined);
|
||||
formatOpt.month = GetOption(options, "month", "string",
|
||||
["2-digit", "numeric", "narrow", "short", "long"], undefined);
|
||||
formatOpt.day = GetOption(options, "day", "string", ["2-digit", "numeric"], undefined);
|
||||
formatOpt.hour = GetOption(options, "hour", "string", ["2-digit", "numeric"], undefined);
|
||||
formatOpt.minute = GetOption(options, "minute", "string", ["2-digit", "numeric"], undefined);
|
||||
formatOpt.second = GetOption(options, "second", "string", ["2-digit", "numeric"], undefined);
|
||||
formatOpt.timeZoneName = GetOption(options, "timeZoneName", "string", ["short", "long"],
|
||||
undefined);
|
||||
|
||||
// Steps 20-21 provided by ICU - see comment after this function.
|
||||
|
||||
|
|
@ -2650,7 +2659,6 @@ function ToDateTimeOptions(options, required, defaults) {
|
|||
return options;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares the date and time components requested by options with the available
|
||||
* date and time formats in formats, and selects the best match according
|
||||
|
|
@ -2836,6 +2844,7 @@ function Intl_DateTimeFormat_format_get() {
|
|||
// Step 2.
|
||||
return internals.boundFormat;
|
||||
}
|
||||
_SetCanonicalName(Intl_DateTimeFormat_format_get, "get format");
|
||||
|
||||
|
||||
function Intl_DateTimeFormat_formatToParts() {
|
||||
|
|
@ -2971,6 +2980,232 @@ function resolveICUPattern(pattern, result) {
|
|||
}
|
||||
}
|
||||
|
||||
/********** Intl.PluralRules **********/
|
||||
|
||||
/**
|
||||
* PluralRules internal properties.
|
||||
*
|
||||
* Spec: ECMAScript 402 API, PluralRules, 1.3.3.
|
||||
*/
|
||||
var pluralRulesInternalProperties = {
|
||||
_availableLocales: null,
|
||||
availableLocales: function()
|
||||
{
|
||||
var locales = this._availableLocales;
|
||||
if (locales)
|
||||
return locales;
|
||||
|
||||
locales = intl_PluralRules_availableLocales();
|
||||
addSpecialMissingLanguageTags(locales);
|
||||
return (this._availableLocales = locales);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute an internal properties object from |lazyPluralRulesData|.
|
||||
*/
|
||||
function resolvePluralRulesInternals(lazyPluralRulesData) {
|
||||
assert(IsObject(lazyPluralRulesData), "lazy data not an object?");
|
||||
|
||||
var internalProps = std_Object_create(null);
|
||||
|
||||
var requestedLocales = lazyPluralRulesData.requestedLocales;
|
||||
|
||||
var PluralRules = pluralRulesInternalProperties;
|
||||
|
||||
// Step 13.
|
||||
const r = ResolveLocale(callFunction(PluralRules.availableLocales, PluralRules),
|
||||
lazyPluralRulesData.requestedLocales,
|
||||
lazyPluralRulesData.opt,
|
||||
noRelevantExtensionKeys, undefined);
|
||||
|
||||
// Step 14.
|
||||
internalProps.locale = r.locale;
|
||||
internalProps.type = lazyPluralRulesData.type;
|
||||
|
||||
internalProps.pluralCategories = intl_GetPluralCategories(
|
||||
internalProps.locale,
|
||||
internalProps.type);
|
||||
|
||||
internalProps.minimumIntegerDigits = lazyPluralRulesData.minimumIntegerDigits;
|
||||
internalProps.minimumFractionDigits = lazyPluralRulesData.minimumFractionDigits;
|
||||
internalProps.maximumFractionDigits = lazyPluralRulesData.maximumFractionDigits;
|
||||
|
||||
if ("minimumSignificantDigits" in lazyPluralRulesData) {
|
||||
assert("maximumSignificantDigits" in lazyPluralRulesData, "min/max sig digits mismatch");
|
||||
internalProps.minimumSignificantDigits = lazyPluralRulesData.minimumSignificantDigits;
|
||||
internalProps.maximumSignificantDigits = lazyPluralRulesData.maximumSignificantDigits;
|
||||
}
|
||||
|
||||
return internalProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object containing the PluralRules internal properties of |obj|,
|
||||
* or throws a TypeError if |obj| isn't PluralRules-initialized.
|
||||
*/
|
||||
function getPluralRulesInternals(obj, methodName) {
|
||||
var internals = getIntlObjectInternals(obj, "PluralRules", methodName);
|
||||
assert(internals.type === "PluralRules", "bad type escaped getIntlObjectInternals");
|
||||
|
||||
var internalProps = maybeInternalProperties(internals);
|
||||
if (internalProps)
|
||||
return internalProps;
|
||||
|
||||
internalProps = resolvePluralRulesInternals(internals.lazyData);
|
||||
setInternalProperties(internals, internalProps);
|
||||
return internalProps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes an object as a PluralRules.
|
||||
*
|
||||
* This method is complicated a moderate bit by its implementing initialization
|
||||
* as a *lazy* concept. Everything that must happen now, does -- but we defer
|
||||
* all the work we can until the object is actually used as a PluralRules.
|
||||
* This later work occurs in |resolvePluralRulesInternals|; steps not noted
|
||||
* here occur there.
|
||||
*
|
||||
* Spec: ECMAScript 402 API, PluralRules, 1.1.1.
|
||||
*/
|
||||
function InitializePluralRules(pluralRules, locales, options) {
|
||||
assert(IsObject(pluralRules), "InitializePluralRules");
|
||||
|
||||
// Step 1.
|
||||
if (isInitializedIntlObject(pluralRules))
|
||||
ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
|
||||
|
||||
let internals = initializeIntlObject(pluralRules);
|
||||
|
||||
// Lazy PluralRules data has the following structure:
|
||||
//
|
||||
// {
|
||||
// requestedLocales: List of locales,
|
||||
// type: "cardinal" / "ordinal",
|
||||
//
|
||||
// opt: // opt object computer in InitializePluralRules
|
||||
// {
|
||||
// localeMatcher: "lookup" / "best fit",
|
||||
// }
|
||||
//
|
||||
// minimumIntegerDigits: integer ∈ [1, 21],
|
||||
// minimumFractionDigits: integer ∈ [0, 20],
|
||||
// maximumFractionDigits: integer ∈ [0, 20],
|
||||
//
|
||||
// // optional
|
||||
// minimumSignificantDigits: integer ∈ [1, 21],
|
||||
// maximumSignificantDigits: integer ∈ [1, 21],
|
||||
// }
|
||||
//
|
||||
// Note that lazy data is only installed as a final step of initialization,
|
||||
// so every PluralRules lazy data object has *all* these properties, never a
|
||||
// subset of them.
|
||||
const lazyPluralRulesData = std_Object_create(null);
|
||||
|
||||
// Step 3.
|
||||
let requestedLocales = CanonicalizeLocaleList(locales);
|
||||
lazyPluralRulesData.requestedLocales = requestedLocales;
|
||||
|
||||
// Steps 4-5.
|
||||
if (options === undefined)
|
||||
options = {};
|
||||
else
|
||||
options = ToObject(options);
|
||||
|
||||
// Step 6.
|
||||
const type = GetOption(options, "type", "string", ["cardinal", "ordinal"], "cardinal");
|
||||
lazyPluralRulesData.type = type;
|
||||
|
||||
// Step 8.
|
||||
let opt = new Record();
|
||||
lazyPluralRulesData.opt = opt;
|
||||
|
||||
// Steps 9-10.
|
||||
let matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
|
||||
opt.localeMatcher = matcher;
|
||||
|
||||
// Steps 11-12.
|
||||
SetNumberFormatDigitOptions(lazyPluralRulesData, options, 0, 3);
|
||||
|
||||
setLazyData(internals, "PluralRules", lazyPluralRulesData)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subset of the given locale list for which this locale list has a
|
||||
* matching (possibly fallback) locale. Locales appear in the same order in the
|
||||
* returned list as in the input list.
|
||||
*
|
||||
* Spec: ECMAScript 402 API, PluralRules, 1.3.2.
|
||||
*/
|
||||
function Intl_PluralRules_supportedLocalesOf(locales /*, options*/) {
|
||||
var options = arguments.length > 1 ? arguments[1] : undefined;
|
||||
|
||||
// Step 1.
|
||||
var availableLocales = callFunction(pluralRulesInternalProperties.availableLocales,
|
||||
pluralRulesInternalProperties);
|
||||
// Step 2.
|
||||
let requestedLocales = CanonicalizeLocaleList(locales);
|
||||
|
||||
// Step 3.
|
||||
return SupportedLocales(availableLocales, requestedLocales, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String value representing the plural category matching
|
||||
* the number passed as value according to the
|
||||
* effective locale and the formatting options of this PluralRules.
|
||||
*
|
||||
* Spec: ECMAScript 402 API, PluralRules, 1.4.3.
|
||||
*/
|
||||
function Intl_PluralRules_select(value) {
|
||||
// Step 1.
|
||||
let pluralRules = this;
|
||||
// Step 2.
|
||||
let internals = getPluralRulesInternals(pluralRules, "select");
|
||||
|
||||
// Steps 3-4.
|
||||
let n = ToNumber(value);
|
||||
|
||||
// Step 5.
|
||||
return intl_SelectPluralRule(pluralRules, n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resolved options for a PluralRules object.
|
||||
*
|
||||
* Spec: ECMAScript 402 API, PluralRules, 1.4.4.
|
||||
*/
|
||||
function Intl_PluralRules_resolvedOptions() {
|
||||
var internals = getPluralRulesInternals(this, "resolvedOptions");
|
||||
|
||||
var internalsPluralCategories = internals.pluralCategories;
|
||||
var pluralCategories = [];
|
||||
for (var i = 0; i < internalsPluralCategories.length; i++)
|
||||
_DefineDataProperty(pluralCategories, i, internalsPluralCategories[i]);
|
||||
|
||||
var result = {
|
||||
locale: internals.locale,
|
||||
type: internals.type,
|
||||
pluralCategories,
|
||||
minimumIntegerDigits: internals.minimumIntegerDigits,
|
||||
minimumFractionDigits: internals.minimumFractionDigits,
|
||||
maximumFractionDigits: internals.maximumFractionDigits,
|
||||
};
|
||||
|
||||
var optionalProperties = [
|
||||
"minimumSignificantDigits",
|
||||
"maximumSignificantDigits"
|
||||
];
|
||||
|
||||
for (var i = 0; i < optionalProperties.length; i++) {
|
||||
var p = optionalProperties[i];
|
||||
if (callFunction(std_Object_hasOwnProperty, internals, p))
|
||||
_DefineDataProperty(result, p, internals[p]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function Intl_getCanonicalLocales(locales) {
|
||||
let codes = CanonicalizeLocaleList(locales);
|
||||
let result = [];
|
||||
|
|
@ -3006,3 +3241,126 @@ function Intl_getCalendarInfo(locales) {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is a custom method designed after Intl API, but currently
|
||||
* not part of the spec or spec proposal.
|
||||
* We want to use it internally to retrieve translated values from CLDR in
|
||||
* order to ensure they're aligned with what Intl API returns.
|
||||
*
|
||||
* This API may one day be a foundation for an ECMA402 API spec proposal.
|
||||
*
|
||||
* The function takes two arguments - locales which is a list of locale strings
|
||||
* and options which is an object with two optional properties:
|
||||
*
|
||||
* keys:
|
||||
* an Array of string values that are paths to individual terms
|
||||
*
|
||||
* style:
|
||||
* a String with a value "long", "short" or "narrow"
|
||||
*
|
||||
* It returns an object with properties:
|
||||
*
|
||||
* locale:
|
||||
* a negotiated locale string
|
||||
*
|
||||
* style:
|
||||
* negotiated style
|
||||
*
|
||||
* values:
|
||||
* A key-value pair list of requested keys and corresponding
|
||||
* translated values
|
||||
*
|
||||
*/
|
||||
function Intl_getDisplayNames(locales, options) {
|
||||
// 1. Let requestLocales be ? CanonicalizeLocaleList(locales).
|
||||
const requestedLocales = CanonicalizeLocaleList(locales);
|
||||
|
||||
// 2. If options is undefined, then
|
||||
if (options === undefined)
|
||||
// a. Let options be ObjectCreate(%ObjectPrototype%).
|
||||
options = {};
|
||||
// 3. Else,
|
||||
else
|
||||
// a. Let options be ? ToObject(options).
|
||||
options = ToObject(options);
|
||||
|
||||
const DateTimeFormat = dateTimeFormatInternalProperties;
|
||||
|
||||
// 4. Let localeData be %DateTimeFormat%.[[localeData]].
|
||||
const localeData = DateTimeFormat.localeData;
|
||||
|
||||
// 5. Let opt be a new Record.
|
||||
const localeOpt = new Record();
|
||||
// 6. Set localeOpt.[[localeMatcher]] to "best fit".
|
||||
localeOpt.localeMatcher = "best fit";
|
||||
|
||||
// 7. Let r be ResolveLocale(%DateTimeFormat%.[[availableLocales]], requestedLocales, localeOpt,
|
||||
// %DateTimeFormat%.[[relevantExtensionKeys]], localeData).
|
||||
const r = ResolveLocale(callFunction(DateTimeFormat.availableLocales, DateTimeFormat),
|
||||
requestedLocales,
|
||||
localeOpt,
|
||||
DateTimeFormat.relevantExtensionKeys,
|
||||
localeData);
|
||||
|
||||
// 8. Let style be ? GetOption(options, "style", "string", « "long", "short", "narrow" », "long").
|
||||
const style = GetOption(options, "style", "string", ["long", "short", "narrow"], "long");
|
||||
// 9. Let keys be ? Get(options, "keys").
|
||||
let keys = options.keys;
|
||||
|
||||
// 10. If keys is undefined,
|
||||
if (keys === undefined) {
|
||||
// a. Let keys be ArrayCreate(0).
|
||||
keys = [];
|
||||
} else if (!IsObject(keys)) {
|
||||
// 11. Else,
|
||||
// a. If Type(keys) is not Object, throw a TypeError exception.
|
||||
ThrowTypeError(JSMSG_INVALID_KEYS_TYPE);
|
||||
}
|
||||
|
||||
// 12. Let processedKeys be ArrayCreate(0).
|
||||
// (This really should be a List, but we use an Array here in order that
|
||||
// |intl_ComputeDisplayNames| may infallibly access the list's length via
|
||||
// |ArrayObject::length|.)
|
||||
let processedKeys = [];
|
||||
// 13. Let len be ? ToLength(? Get(keys, "length")).
|
||||
let len = ToLength(keys.length);
|
||||
// 14. Let i be 0.
|
||||
// 15. Repeat, while i < len
|
||||
for (let i = 0; i < len; i++) {
|
||||
// a. Let processedKey be ? ToString(? Get(keys, i)).
|
||||
// b. Perform ? CreateDataPropertyOrThrow(processedKeys, i, processedKey).
|
||||
callFunction(std_Array_push, processedKeys, ToString(keys[i]));
|
||||
}
|
||||
|
||||
// 16. Let names be ? ComputeDisplayNames(r.[[locale]], style, processedKeys).
|
||||
const names = intl_ComputeDisplayNames(r.locale, style, processedKeys);
|
||||
|
||||
// 17. Let values be ObjectCreate(%ObjectPrototype%).
|
||||
const values = {};
|
||||
|
||||
// 18. Set i to 0.
|
||||
// 19. Repeat, while i < len
|
||||
for (let i = 0; i < len; i++) {
|
||||
// a. Let key be ? Get(processedKeys, i).
|
||||
const key = processedKeys[i];
|
||||
// b. Let name be ? Get(names, i).
|
||||
const name = names[i];
|
||||
// c. Assert: Type(name) is string.
|
||||
assert(typeof name === "string", "unexpected non-string value");
|
||||
// d. Assert: the length of name is greater than zero.
|
||||
assert(name.length > 0, "empty string value");
|
||||
// e. Perform ? DefinePropertyOrThrow(values, key, name).
|
||||
_DefineDataProperty(values, key, name);
|
||||
}
|
||||
|
||||
// 20. Let options be ObjectCreate(%ObjectPrototype%).
|
||||
// 21. Perform ! DefinePropertyOrThrow(result, "locale", r.[[locale]]).
|
||||
// 22. Perform ! DefinePropertyOrThrow(result, "style", style).
|
||||
// 23. Perform ! DefinePropertyOrThrow(result, "values", values).
|
||||
const result = { locale: r.locale, style, values };
|
||||
|
||||
// 24. Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.hasOwnProperty("addIntlExtras"))
|
||||
|
||||
// Tests the PluralRules.resolvedOptions function for overriden Array[Symbol.species].
|
||||
|
||||
addIntlExtras(Intl);
|
||||
|
||||
var pl = new Intl.PluralRules("de");
|
||||
|
||||
Object.defineProperty(Array, Symbol.species, {
|
||||
value: function() {
|
||||
return new Proxy(["?"], {
|
||||
get(t, pk, r) {
|
||||
return Reflect.get(t, pk, r);
|
||||
},
|
||||
defineProperty(t, pk) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var pluralCategories = pl.resolvedOptions().pluralCategories;
|
||||
|
||||
assertEqArray(pluralCategories, ["one", "other"]);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0);
|
||||
23
js/src/tests/Intl/getCanonicalLocales-overridden-species.js
Normal file
23
js/src/tests/Intl/getCanonicalLocales-overridden-species.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
|
||||
|
||||
// Tests the getCanonicalLocales function for overriden Array[Symbol.species].
|
||||
|
||||
Object.defineProperty(Array, Symbol.species, {
|
||||
value: function() {
|
||||
return new Proxy(["?"], {
|
||||
get(t, pk, r) {
|
||||
return Reflect.get(t, pk, r);
|
||||
},
|
||||
defineProperty(t, pk) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var arr = Intl.getCanonicalLocales("de-x-private");
|
||||
|
||||
assertEqArray(arr, ["de-x-private"]);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0);
|
||||
Loading…
Add table
Add a link
Reference in a new issue