Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Intl.PluralRules #4940

Merged
merged 3 commits into from
May 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions lib/Runtime/ByteCode/ByteCodeCacheReleaseFileVersion.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
// NOTE: If there is a merge conflict the correct fix is to make a new GUID.
// This file was generated with core\tools\update_bytecode_version.ps1

// {752B5975-74CF-499D-8438-F7028EB3CAC4}
// 15f08b09-6802-4d9a-80b2-86ce974c1fec
const GUID byteCodeCacheReleaseFileVersion =
{ 0x752B5975, 0x74CF, 0x499D, { 0x84, 0x38, 0xF7, 0x02, 0x8E, 0xB3, 0xCA, 0xC4 } };
{ 0x15f08b09, 0x6802, 0x4d9a, { 0x80, 0xb2, 0x86, 0xce, 0x97, 0x4c, 0x1f, 0xec } };
205 changes: 184 additions & 21 deletions lib/Runtime/Library/InJavascript/Intl.js
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,40 @@

const BestFitSupportedLocales = LookupSupportedLocales;

/**
* Applies digit options used for number formatting onto the given intlObj
*
* This function is used by both NumberFormat and PluralRules, despite being defined
* as a NumberFormat abstract operation
*
* ECMA 402: #sec-setnfdigitoptions
*
* @param {Object} intlObj The state object of either a NumberFormat or PluralRules on which to set the resolved number options
* @param {Object} options The option object to pull min/max sigfigs, fraction digits, and integer digits
* @param {Number} mnfdDefault The default minimumFractionDigits
* @param {Number} mxfdDefault The default maximumFractionDigits
*/
const SetNumberFormatDigitOptions = function (intlObj, options, mnfdDefault, mxfdDefault) {
const mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1);
const mnfd = GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault);
const mxfdActualDefault = _.max(mnfd, mxfdDefault);
const mxfd = GetNumberOption(options, "maximumFractionDigits", mnfd, 20, mxfdActualDefault);
intlObj.minimumIntegerDigits = mnid;
intlObj.minimumFractionDigits = mnfd;
intlObj.maximumFractionDigits = mxfd;

let mnsd = options.minimumSignificantDigits;
let mxsd = options.maximumSignificantDigits;
if (mnsd !== undefined || mxsd !== undefined) {
// don't read options.minimumSignificantDigits below in order to pass
// test262/test/intl402/NumberFormat/significant-digits-options-get-sequence.js
mnsd = GetNumberOption({ minimumSignificantDigits: mnsd }, "minimumSignificantDigits", 1, 21, 1);
mxsd = GetNumberOption({ maximumSignificantDigits: mxsd }, "maximumSignificantDigits", mnsd, 21, 21);
intlObj.minimumSignificantDigits = mnsd;
intlObj.maximumSignificantDigits = mxsd;
}
};

/**
* Returns the subset of requestedLocales that has a matching locale, according to
* options.localeMatcher and isAvailableLocale.
Expand Down Expand Up @@ -1115,27 +1149,6 @@
return;
}

const SetNumberFormatDigitOptions = function (nf, options, mnfdDefault, mxfdDefault) {
const mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1);
const mnfd = GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault);
const mxfdActualDefault = _.max(mnfd, mxfdDefault);
const mxfd = GetNumberOption(options, "maximumFractionDigits", mnfd, 20, mxfdActualDefault);
nf.minimumIntegerDigits = mnid;
nf.minimumFractionDigits = mnfd;
nf.maximumFractionDigits = mxfd;

let mnsd = options.minimumSignificantDigits;
let mxsd = options.maximumSignificantDigits;
if (mnsd !== undefined || mxsd !== undefined) {
// don't read options.minimumSignificantDigits below in order to pass
// test262/test/intl402/NumberFormat/significant-digits-options-get-sequence.js
mnsd = GetNumberOption({ minimumSignificantDigits: mnsd }, "minimumSignificantDigits", 1, 21, 1);
mxsd = GetNumberOption({ maximumSignificantDigits: mxsd }, "maximumSignificantDigits", mnsd, 21, 21);
nf.minimumSignificantDigits = mnsd;
nf.maximumSignificantDigits = mxsd;
}
};

const InitializeNumberFormat = function (nf, locales, options) {
const requestedLocales = CanonicalizeLocaleList(locales);
options = options === undefined ? _.create() : Internal.ToObject(options);
Expand Down Expand Up @@ -1928,11 +1941,161 @@
return DateTimeFormat;
})();

const PluralRules = (function() {
if (InitType !== "Intl") {
return;
}

/**
* Initializes the given pluralRules object
*
* ECMA 402: #sec-initializepluralrules
*
* @param {Object} pluralRules
* @param {String|String[]} locales
* @param {Object} options
*/
const InitializePluralRules = function (pluralRules, locales, options) {
const requestedLocales = CanonicalizeLocaleList(locales);
options = options === undefined ? _.create() : Internal.ToObject(options);
const opt = _.create();
opt.matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
pluralRules.type = GetOption(options, "type", "string", ["cardinal", "ordinal"], "cardinal");

SetNumberFormatDigitOptions(pluralRules, options, 0, 3);

// %PluralRules%.[[RelevantExtensionKeys]] = [] (#sec-intl.pluralrules-internal-slots)
const r = ResolveLocale(platform.isPRLocaleAvailable, requestedLocales, opt, []);

pluralRules.locale = r.locale;
pluralRules.pluralCategories = platform.pluralRulesKeywords(pluralRules);

pluralRules.initializedPluralRules = true;

return pluralRules;
};

/**
* Returns a String value representing the plural form of n according to
* the effective locale and the options of pluralRules
*
* ECMA 402: #sec-resolveplural
*
* @param {Object} pluralRules
* @param {Number} n
*/
const ResolvePlural = function (pluralRules, n) {
if (!_.isFinite(n)) {
return "other";
}

return platform.pluralRulesSelect(pluralRules, n);
};

// params are explicitly `= undefined` to make PluralRules.length === 0
const PluralRules = function (locales = undefined, options = undefined) {
if (new.target === undefined) {
platform.raiseNeedObjectOfType("Intl.PluralRules", "PluralRules");
}

const stateObject = _.create();
platform.setHiddenObject(this, stateObject);

InitializePluralRules(stateObject, locales, options);

return this;
};
tagPublicFunction("Intl.PluralRules", PluralRules);

// ECMA 402: #sec-intl.pluralrules.prototype
_.defineProperty(PluralRules, "prototype", {
value: {},
writable: false,
enumerable: false,
configurable: false,
});

// See explanation of `getCanonicalLocales`
// ECMA 402: #sec-intl.pluralrules.supportedlocalesof
const pluralRules_supportedLocalesOf_name = "Intl.PluralRules.supportedLocalesOf";
const pluralRules_supportedLocalesOf_func = tagPublicFunction(pluralRules_supportedLocalesOf_name, function (locales, options = undefined) {
return supportedLocalesOf_unconstructable(this, pluralRules_supportedLocalesOf_name, platform.isPRLocaleAvailable, locales, options);
});
const pluralRules_supportedLocalesOf = _.bind(pluralRules_supportedLocalesOf_func, intlStaticMethodThisArg);
_.defineProperty(pluralRules_supportedLocalesOf, "name", {
value: "supportedLocalesOf",
writable: false,
enumerable: false,
configurable: true,
});
_.defineProperty(PluralRules, "supportedLocalesOf", {
value: pluralRules_supportedLocalesOf,
writable: true,
enumerable: false,
configurable: true,
});

// ECMA 402: #sec-intl.pluralrules.prototype.select
const select = function (value) {
const pr = platform.getHiddenObject(this);
if (!pr || !pr.initializedPluralRules) {
platform.raiseNeedObjectOfType("Intl.PluralRules.prototype.select", "PluralRules");
}

const n = Internal.ToNumber(value);
return ResolvePlural(pr, n);
};
tagPublicFunction("Intl.PluralRules.prototype.select", select);
_.defineProperty(PluralRules.prototype, "select", {
value: select,
enumerable: false,
configurable: true,
writable: true,
});

const resolvedOptions = function () {
const pr = platform.getHiddenObject(this);
if (!pr || !pr.initializedPluralRules) {
platform.raiseNeedObjectOfType("Intl.PluralRules.prototype.select", "PluralRules");
}

const options = {};
_.forEach([
"locale",
"type",
"minimumIntegerDigits",
"minimumFractionDigits",
"maximumFractionDigits",
"minimumSignificantDigits",
"maximumSignificantDigits"
], (prop) => {
if (pr[prop] !== undefined) {
options[prop] = pr[prop];
}
});

// https://github.com/tc39/ecma402/issues/224: create a copy of the pluralCategories array
options.pluralCategories = _.slice(pr.pluralCategories, 0);

return options;
};
tagPublicFunction("Intl.PluralRules.prototype.resolvedOptions", resolvedOptions);
_.defineProperty(PluralRules.prototype, "resolvedOptions", {
value: resolvedOptions,
enumerable: false,
configurable: true,
writable: true,
});

return PluralRules;
})();

// Initialize Intl properties only if needed
if (InitType === "Intl") {
ObjectDefineProperty(Intl, "Collator", { value: Collator, writable: true, enumerable: false, configurable: true });
ObjectDefineProperty(Intl, "NumberFormat", { value: NumberFormat, writable: true, enumerable: false, configurable: true });
ObjectDefineProperty(Intl, "DateTimeFormat", { value: DateTimeFormat, writable: true, enumerable: false, configurable: true });
ObjectDefineProperty(Intl, "PluralRules", { value: PluralRules, writable: true, enumerable: false, configurable: true });
}

}
Expand Down
Loading