ported from UXP: Issue #1969 - Implement Intl.RelativeTimeFormat. (86f540b6)

This commit is contained in:
2022-07-28 09:37:14 +08:00
parent dc9261360f
commit 406e64ef73
10 changed files with 653 additions and 12 deletions
+2
View File
@@ -87,12 +87,14 @@ included_inclnames_to_ignore = set([
'unicode/ucol.h', # ICU
'unicode/udat.h', # ICU
'unicode/udatpg.h', # ICU
'unicode/udisplaycontext.h',# ICU
'unicode/uenum.h', # ICU
'unicode/uniset.h', # ICU
'unicode/unorm2.h', # ICU
'unicode/unum.h', # ICU
'unicode/unumsys.h', # ICU
'unicode/upluralrules.h', # ICU
'unicode/ureldatefmt.h', # ICU
'unicode/ustring.h', # ICU
'unicode/utypes.h', # ICU
'vtune/VTuneWrapper.h' # VTune
@@ -0,0 +1,45 @@
Workaround for https://unicode-org.atlassian.net/browse/ICU-20253
https://bugzilla.mozilla.org/show_bug.cgi?id=1504656
diff --git a/intl/icu/source/i18n/reldatefmt.cpp b/intl/icu/source/i18n/reldatefmt.cpp
--- a/intl/icu/source/i18n/reldatefmt.cpp
+++ b/intl/icu/source/i18n/reldatefmt.cpp
@@ -157,24 +157,30 @@ const UnicodeString& RelativeDateTimeCac
}
// Use fallback cache for SimpleFormatter relativeUnits.
const SimpleFormatter* RelativeDateTimeCacheData::getRelativeDateTimeUnitFormatter(
int32_t fStyle,
URelativeDateTimeUnit unit,
int32_t pastFutureIndex,
int32_t pluralUnit) const {
- int32_t style = fStyle;
- do {
- if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != nullptr) {
- return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit];
+ while (true) {
+ int32_t style = fStyle;
+ do {
+ if (relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit] != nullptr) {
+ return relativeUnitsFormatters[style][unit][pastFutureIndex][pluralUnit];
+ }
+ style = fallBackCache[style];
+ } while (style != -1);
+
+ if (pluralUnit == StandardPlural::OTHER) {
+ return nullptr; // No formatter found.
}
- style = fallBackCache[style];
- } while (style != -1);
- return nullptr; // No formatter found.
+ pluralUnit = StandardPlural::OTHER;
+ }
}
static UBool getStringWithFallback(
const UResourceBundle *resource,
const char *key,
UnicodeString &result,
UErrorCode &status) {
int32_t len = 0;
+3 -2
View File
@@ -7,11 +7,11 @@ set -e
# Update to an ICU release:
# Usage: update-icu.sh <URL of ICU GIT> <release tag name>
# E.g., for ICU 63.2: update-icu.sh https://github.com/unicode-org/icu.git release-63-2
# E.g., for ICU 62.2: update-icu.sh https://github.com/unicode-org/icu.git release-62-2
#
# Update to an ICU maintenance branch:
# Usage: update-icu.sh <URL of ICU GIT> <maintenance name>
# E.g., for ICU 63.2: update-icu.sh https://github.com/unicode-org/icu.git maint/maint-63
# E.g., for ICU 62.2: update-icu.sh https://github.com/unicode-org/icu.git maint/maint-62
if [ $# -lt 2 ]; then
echo "Usage: update-icu.sh <URL of ICU GIT> <release tag name>"
@@ -86,6 +86,7 @@ for patch in \
suppress-warnings.diff \
bug-1172609-timezone-recreateDefault.diff \
bug-1198952-workaround-make-3.82-bug.diff \
bug-1504656-relativetimeformat-plural-other-fallback.diff \
; do
echo "Applying local patch $patch"
patch -d ${icu_dir}/../../ -p1 --no-backup-if-mismatch < ${icu_dir}/../icu-patches/$patch
+1 -1
View File
@@ -852,7 +852,7 @@ struct JSClass {
// application.
#define JSCLASS_GLOBAL_APPLICATION_SLOTS 5
#define JSCLASS_GLOBAL_SLOT_COUNT \
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 45)
(JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 46)
#define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
#define JSCLASS_GLOBAL_FLAGS \
+305
View File
@@ -12,6 +12,7 @@
#include "builtin/Intl.h"
#include "mozilla/Casting.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/PodOperations.h"
#include "mozilla/Range.h"
@@ -31,10 +32,12 @@
#include "unicode/ucol.h"
#include "unicode/udat.h"
#include "unicode/udatpg.h"
#include "unicode/udisplaycontext.h"
#include "unicode/uenum.h"
#include "unicode/unum.h"
#include "unicode/unumsys.h"
#include "unicode/upluralrules.h"
#include "unicode/ureldatefmt.h"
#include "unicode/ustring.h"
#include "vm/DateTime.h"
#include "vm/GlobalObject.h"
@@ -3192,6 +3195,303 @@ js::intl_GetPluralCategories(JSContext* cx, unsigned argc, Value* vp)
return true;
}
/**************** RelativeTimeFormat *****************/
static void relativeTimeFormat_finalize(FreeOp* fop, JSObject* obj);
static const uint32_t URELATIVE_TIME_FORMAT_SLOT = 0;
static const uint32_t RELATIVE_TIME_FORMAT_SLOTS_COUNT = 1;
static const ClassOps RelativeTimeFormatClassOps = {
nullptr, /* addProperty */
nullptr, /* delProperty */
nullptr, /* getProperty */
nullptr, /* enumerate */
nullptr, /* newEnumerate */
nullptr, /* resolve */
nullptr, /* mayResolve */
relativeTimeFormat_finalize
};
static const Class RelativeTimeFormatClass = {
js_Object_str,
JSCLASS_HAS_RESERVED_SLOTS(RELATIVE_TIME_FORMAT_SLOTS_COUNT) |
JSCLASS_FOREGROUND_FINALIZE,
&RelativeTimeFormatClassOps
};
#if JS_HAS_TOSOURCE
static bool
relativeTimeFormat_toSource(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setString(cx->names().RelativeTimeFormat);
return true;
}
#endif
static const JSFunctionSpec relativeTimeFormat_static_methods[] = {
JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_RelativeTimeFormat_supportedLocalesOf", 1, 0),
JS_FS_END
};
static const JSFunctionSpec relativeTimeFormat_methods[] = {
JS_SELF_HOSTED_FN("resolvedOptions", "Intl_RelativeTimeFormat_resolvedOptions", 0, 0),
JS_SELF_HOSTED_FN("format", "Intl_RelativeTimeFormat_format", 2, 0),
#if JS_HAS_TOSOURCE
JS_FN(js_toSource_str, relativeTimeFormat_toSource, 0, 0),
#endif
JS_FS_END
};
static const JSPropertySpec relativeTimeFormat_properties[] = {
JS_STRING_SYM_PS(toStringTag, "Intl.RelativeTimeFormat", JSPROP_READONLY),
JS_PS_END};
/**
* RelativeTimeFormat constructor.
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.1
*/
static bool
RelativeTimeFormat(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// Step 1.
if (!ThrowIfNotConstructing(cx, args, "Intl.RelativeTimeFormat"))
return false;
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
RootedObject proto(cx);
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
return false;
if (!proto) {
proto = GlobalObject::getOrCreateRelativeTimeFormatPrototype(cx, cx->global());
if (!proto)
return false;
}
RootedObject relativeTimeFormat(cx);
relativeTimeFormat = NewObjectWithGivenProto(cx, &RelativeTimeFormatClass, proto);
if (!relativeTimeFormat)
return false;
relativeTimeFormat->as<NativeObject>().setReservedSlot(URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
RootedValue locales(cx, args.get(0));
RootedValue options(cx, args.get(1));
// Step 3.
if (!IntlInitialize(cx, relativeTimeFormat, cx->names().InitializeRelativeTimeFormat, locales, options))
return false;
args.rval().setObject(*relativeTimeFormat);
return true;
}
static void
relativeTimeFormat_finalize(FreeOp* fop, JSObject* obj)
{
MOZ_ASSERT(fop->onMainThread());
// This is-undefined check shouldn't be necessary, but for internal
// brokenness in object allocation code. For the moment, hack around it by
// explicitly guarding against the possibility of the reserved slot not
// containing a private. See bug 949220.
const Value& slot = obj->as<NativeObject>().getReservedSlot(URELATIVE_TIME_FORMAT_SLOT);
if (!slot.isUndefined()) {
if (URelativeDateTimeFormatter* rtf = static_cast<URelativeDateTimeFormatter*>(slot.toPrivate()))
ureldatefmt_close(rtf);
}
}
static JSObject*
CreateRelativeTimeFormatPrototype(JSContext* cx, HandleObject Intl, Handle<GlobalObject*> global)
{
RootedFunction ctor(cx);
ctor = global->createConstructor(cx, &RelativeTimeFormat, cx->names().RelativeTimeFormat, 0);
if (!ctor)
return nullptr;
RootedNativeObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &RelativeTimeFormatClass));
if (!proto)
return nullptr;
proto->setReservedSlot(URELATIVE_TIME_FORMAT_SLOT, PrivateValue(nullptr));
if (!LinkConstructorAndPrototype(cx, ctor, proto))
return nullptr;
if (!JS_DefineFunctions(cx, ctor, relativeTimeFormat_static_methods))
return nullptr;
if (!JS_DefineFunctions(cx, proto, relativeTimeFormat_methods))
return nullptr;
if (!JS_DefineProperties(cx, proto, relativeTimeFormat_properties))
return nullptr;
RootedValue options(cx);
if (!CreateDefaultOptions(cx, &options))
return nullptr;
if (!IntlInitialize(cx, proto, cx->names().InitializeRelativeTimeFormat, UndefinedHandleValue,
options))
{
return nullptr;
}
RootedValue ctorValue(cx, ObjectValue(*ctor));
if (!DefineProperty(cx, Intl, cx->names().RelativeTimeFormat, ctorValue, nullptr, nullptr, 0)) {
return nullptr;
}
return proto;
}
bool
js::intl_RelativeTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 0);
RootedValue result(cx);
// We're going to use ULocale availableLocales as per ICU recommendation:
// https://ssl.icu-project.org/trac/ticket/12756
if (!intl_availableLocales(cx, uloc_countAvailable, uloc_getAvailable, &result))
return false;
args.rval().set(result);
return true;
}
enum class RelativeTimeNumeric
{
/**
* Only strings with numeric components like `1 day ago`.
*/
Always,
/**
* Natural-language strings like `yesterday` when possible,
* otherwise strings with numeric components as in `7 months ago`.
*/
Auto,
};
bool
js::intl_FormatRelativeTime(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 3);
RootedObject relativeTimeFormat(cx, &args[0].toObject());
RootedObject internals(cx, GetInternals(cx, relativeTimeFormat));
if (!internals)
return false;
RootedValue value(cx);
if (!GetProperty(cx, internals, internals, cx->names().locale, &value))
return false;
JSAutoByteString locale(cx, value.toString());
if (!locale)
return false;
if (!GetProperty(cx, internals, internals, cx->names().style, &value))
return false;
RootedLinearString style(cx, value.toString()->ensureLinear(cx));
if (!style)
return false;
double t = args[1].toNumber();
UDateRelativeDateTimeFormatterStyle relDateTimeStyle;
if (StringEqualsAscii(style, "short")) {
relDateTimeStyle = UDAT_STYLE_SHORT;
} else if (StringEqualsAscii(style, "narrow")) {
relDateTimeStyle = UDAT_STYLE_NARROW;
} else {
MOZ_ASSERT(StringEqualsAscii(style, "long"));
relDateTimeStyle = UDAT_STYLE_LONG;
}
URelativeDateTimeUnit relDateTimeUnit;
{
JSLinearString* unit = args[2].toString()->ensureLinear(cx);
if (!unit) {
return false;
}
if (StringEqualsAscii(unit, "second") || StringEqualsAscii(unit, "seconds")) {
relDateTimeUnit = UDAT_REL_UNIT_SECOND;
} else if (StringEqualsAscii(unit, "minute") || StringEqualsAscii(unit, "minutes")) {
relDateTimeUnit = UDAT_REL_UNIT_MINUTE;
} else if (StringEqualsAscii(unit, "hour") || StringEqualsAscii(unit, "hours")) {
relDateTimeUnit = UDAT_REL_UNIT_HOUR;
} else if (StringEqualsAscii(unit, "day") || StringEqualsAscii(unit, "days")) {
relDateTimeUnit = UDAT_REL_UNIT_DAY;
} else if (StringEqualsAscii(unit, "week") || StringEqualsAscii(unit, "weeks")) {
relDateTimeUnit = UDAT_REL_UNIT_WEEK;
} else if (StringEqualsAscii(unit, "month") || StringEqualsAscii(unit, "months")) {
relDateTimeUnit = UDAT_REL_UNIT_MONTH;
} else if (StringEqualsAscii(unit, "quarter") || StringEqualsAscii(unit, "quarters")) {
relDateTimeUnit = UDAT_REL_UNIT_QUARTER;
} else {
MOZ_ASSERT(StringEqualsAscii(unit, "year") || StringEqualsAscii(unit, "years"));
relDateTimeUnit = UDAT_REL_UNIT_YEAR;
}
}
if (!GetProperty(cx, internals, internals, cx->names().numeric, &value))
return false;
RootedLinearString numeric(cx, value.toString()->ensureLinear(cx));
if (!numeric)
return false;
RelativeTimeNumeric relDateTimeNumeric;
if (StringEqualsAscii(numeric, "auto")) {
relDateTimeNumeric = RelativeTimeNumeric::Auto;
} else {
MOZ_ASSERT(StringEqualsAscii(numeric, "always"));
relDateTimeNumeric = RelativeTimeNumeric::Always;
}
Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
return false;
UErrorCode status = U_ZERO_ERROR;
URelativeDateTimeFormatter* rtf =
ureldatefmt_open(icuLocale(locale.ptr()), nullptr, relDateTimeStyle,
UDISPCTX_CAPITALIZATION_FOR_STANDALONE, &status);
if (U_FAILURE(status)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
return false;
}
int32_t size;
if (relDateTimeNumeric == RelativeTimeNumeric::Auto) {
size = ureldatefmt_format(rtf, t, relDateTimeUnit, Char16ToUChar(chars.begin()),
INITIAL_CHAR_BUFFER_SIZE, &status);
} else {
MOZ_ASSERT(relDateTimeNumeric == RelativeTimeNumeric::Always);
size = ureldatefmt_formatNumeric(rtf, t, relDateTimeUnit, Char16ToUChar(chars.begin()),
INITIAL_CHAR_BUFFER_SIZE, &status);
}
ScopedICUObject<URelativeDateTimeFormatter, ureldatefmt_close> closeRelativeTimeFormat(rtf);
JSString* str = NewStringCopyN<CanGC>(cx, chars.begin(), size);
if (!str)
return false;
args.rval().setString(str);
return true;
}
bool
js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp)
{
@@ -3682,6 +3982,10 @@ GlobalObject::initIntlObject(JSContext* cx, Handle<GlobalObject*> global)
RootedObject numberFormatProto(cx, CreateNumberFormatPrototype(cx, intl, global));
if (!numberFormatProto)
return false;
RootedObject relativeTimeFmtProto(cx, CreateRelativeTimeFormatPrototype(cx, intl, global));
if (!relativeTimeFmtProto) {
return false;
}
// The |Intl| object is fully set up now, so define the global property.
RootedValue intlValue(cx, ObjectValue(*intl));
@@ -3703,6 +4007,7 @@ GlobalObject::initIntlObject(JSContext* cx, Handle<GlobalObject*> global)
global->setReservedSlot(COLLATOR_PROTO, ObjectValue(*collatorProto));
global->setReservedSlot(DATE_TIME_FORMAT_PROTO, ObjectValue(*dateTimeFormatProto));
global->setReservedSlot(NUMBER_FORMAT_PROTO, ObjectValue(*numberFormatProto));
global->setReservedSlot(RELATIVE_TIME_FORMAT_PROTO, ObjectValue(*relativeTimeFmtProto));
// Also cache |Intl| to implement spec language that conditions behavior
// based on values being equal to "the standard built-in |Intl| object".
+25
View File
@@ -399,6 +399,31 @@ intl_SelectPluralRule(JSContext* cx, unsigned argc, Value* vp);
extern MOZ_MUST_USE bool
intl_GetPluralCategories(JSContext* cx, unsigned argc, Value* vp);
/******************** RelativeTimeFormat ********************/
/**
* Returns an object indicating the supported locales for relative time format
* by having a true-valued property for each such locale with the
* canonicalized language tag as the property name. The object has no
* prototype.
*
* Usage: availableLocales = intl_RelativeTimeFormat_availableLocales()
*/
extern MOZ_MUST_USE bool
intl_RelativeTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp);
/**
* Returns a relative time as a string formatted according to the effective
* locale and the formatting options of the given RelativeTimeFormat.
*
* t should be a number representing a number to be formatted.
* unit should be "second", "minute", "hour", "day", "week", "month", "quarter", or "year".
*
* Usage: formatted = intl_FormatRelativeTime(relativeTimeFormat, t, unit)
*/
extern MOZ_MUST_USE bool
intl_FormatRelativeTime(JSContext* cx, unsigned argc, Value* vp);
/**
* Returns a plain object with calendar information for a single valid locale
* (callers must perform this validation). The object will have these
+260 -9
View File
@@ -23,6 +23,7 @@
intl_FormatDateTime: false,
intl_SelectPluralRule: false,
intl_GetPluralCategories: false,
intl_FormatRelativeTime: false,
intl_GetCalendarInfo: false,
*/
@@ -1279,7 +1280,8 @@ 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" || type === "PluralRules",
type === "NumberFormat" || type === "PluralRules" ||
type === "RelativeTimeFormat",
"bad type");
assert(IsObject(lazyData), "non-object lazy data");
@@ -1331,7 +1333,8 @@ function isInitializedIntlObject(obj) {
assert(callFunction(std_Object_hasOwnProperty, internals, "type"), "missing type");
var type = internals.type;
assert(type === "partial" || type === "Collator" ||
type === "DateTimeFormat" || type === "NumberFormat" || type === "PluralRules",
type === "DateTimeFormat" || type === "NumberFormat" ||
type === "PluralRules" || type === "RelativeTimeFormat",
"unexpected type");
assert(callFunction(std_Object_hasOwnProperty, internals, "lazyData"), "missing lazyData");
assert(callFunction(std_Object_hasOwnProperty, internals, "internalProps"), "missing internalProps");
@@ -1385,14 +1388,24 @@ function getInternals(obj)
var internalProps;
var type = internals.type;
if (type === "Collator")
internalProps = resolveCollatorInternals(lazyData)
else if (type === "DateTimeFormat")
internalProps = resolveDateTimeFormatInternals(lazyData)
else if (type === "PluralRules")
internalProps = resolvePluralRulesInternals(lazyData)
else
switch (type) {
case "Collator":
internalProps = resolveCollatorInternals(lazyData);
break;
case "DateTimeFormat":
internalProps = resolveDateTimeFormatInternals(lazyData);
break;
case "PluralRules":
internalProps = resolvePluralRulesInternals(lazyData);
break;
case "RelativeTimeFormat":
internalProps = resolveRelativeTimeFormatInternals(lazyData);
break;
default: // type === "NumberFormat"
internalProps = resolveNumberFormatInternals(lazyData);
break;
}
setInternalProperties(internals, internalProps);
return internalProps;
}
@@ -3206,6 +3219,244 @@ function Intl_PluralRules_resolvedOptions() {
}
/********** Intl.RelativeTimeFormat **********/
/**
* RelativeTimeFormat internal properties.
*
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.3.3.
*/
var relativeTimeFormatInternalProperties = {
localeData: relativeTimeFormatLocaleData,
_availableLocales: null,
availableLocales: function() // eslint-disable-line object-shorthand
{
var locales = this._availableLocales;
if (locales)
return locales;
locales = intl_RelativeTimeFormat_availableLocales();
addSpecialMissingLanguageTags(locales);
return (this._availableLocales = locales);
},
relevantExtensionKeys: [],
};
function relativeTimeFormatLocaleData() {
// RelativeTimeFormat doesn't support any extension keys.
return {};
}
/**
* Compute an internal properties object from |lazyRelativeTimeFormatData|.
*/
function resolveRelativeTimeFormatInternals(lazyRelativeTimeFormatData) {
assert(IsObject(lazyRelativeTimeFormatData), "lazy data not an object?");
var internalProps = std_Object_create(null);
var RelativeTimeFormat = relativeTimeFormatInternalProperties;
// Steps 7-8.
const r = ResolveLocale(callFunction(RelativeTimeFormat.availableLocales, RelativeTimeFormat),
lazyRelativeTimeFormatData.requestedLocales,
lazyRelativeTimeFormatData.opt,
RelativeTimeFormat.relevantExtensionKeys,
RelativeTimeFormat.localeData);
// Step 9-10.
internalProps.locale = r.locale;
// Step 11.
assert(r.locale === r.dataLocale,
"resolved locale matches the resolved data-locale when no extension-keys are present");
// Step 13.
internalProps.style = lazyRelativeTimeFormatData.style;
// Step 15.
internalProps.numeric = lazyRelativeTimeFormatData.numeric;
// Steps 16-20 (Not relevant in our implementation).
return internalProps;
}
/**
* Returns an object containing the RelativeTimeFormat internal properties of |obj|,
* or throws a TypeError if |obj| isn't RelativeTimeFormat-initialized.
*/
function getRelativeTimeFormatInternals(obj, methodName) {
var internals = getIntlObjectInternals(obj, "RelativeTimeFormat", methodName);
assert(internals.type === "RelativeTimeFormat", "bad type escaped getIntlObjectInternals");
var internalProps = maybeInternalProperties(internals);
if (internalProps)
return internalProps;
internalProps = resolveRelativeTimeFormatInternals(internals.lazyData);
setInternalProperties(internals, internalProps);
return internalProps;
}
/**
* Initializes an object as a RelativeTimeFormat.
*
* 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 RelativeTimeFormat.
* This later work occurs in |resolveRelativeTimeFormatInternals|; steps not noted
* here occur there.
*
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.1.1.
*/
function InitializeRelativeTimeFormat(relativeTimeFormat, locales, options) {
assert(IsObject(relativeTimeFormat), "InitializeRelativeTimeFormat");
if (isInitializedIntlObject(relativeTimeFormat))
ThrowTypeError(JSMSG_INTL_OBJECT_REINITED);
let internals = initializeIntlObject(relativeTimeFormat);
// Lazy RelativeTimeFormat data has the following structure:
//
// {
// requestedLocales: List of locales,
// style: "long" / "short" / "narrow",
// numeric: "always" / "auto",
//
// opt: // opt object computed in InitializeRelativeTimeFormat
// {
// localeMatcher: "lookup" / "best fit",
// }
// }
//
// Note that lazy data is only installed as a final step of initialization,
// so every RelativeTimeFormat lazy data object has *all* these properties, never a
// subset of them.
const lazyRelativeTimeFormatData = std_Object_create(null);
// Step 1.
let requestedLocales = CanonicalizeLocaleList(locales);
lazyRelativeTimeFormatData.requestedLocales = requestedLocales;
// Steps 2-3.
if (options === undefined)
options = std_Object_create(null);
else
options = ToObject(options);
// Step 4.
let opt = new Record();
// Steps 5-6.
let matcher = GetOption(options, "localeMatcher", "string", ["lookup", "best fit"], "best fit");
opt.localeMatcher = matcher;
lazyRelativeTimeFormatData.opt = opt;
// Steps 12-13.
const style = GetOption(options, "style", "string", ["long", "short", "narrow"], "long");
lazyRelativeTimeFormatData.style = style;
// Steps 14-15.
const numeric = GetOption(options, "numeric", "string", ["always", "auto"], "always");
lazyRelativeTimeFormatData.numeric = numeric;
setLazyData(internals, "RelativeTimeFormat", lazyRelativeTimeFormatData)
}
/**
* 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, RelativeTimeFormat, 1.3.2.
*/
function Intl_RelativeTimeFormat_supportedLocalesOf(locales /*, options*/) {
var options = arguments.length > 1 ? arguments[1] : undefined;
// Step 1.
var availableLocales = callFunction(relativeTimeFormatInternalProperties.availableLocales,
relativeTimeFormatInternalProperties);
// Step 2.
let requestedLocales = CanonicalizeLocaleList(locales);
// Step 3.
return SupportedLocales(availableLocales, requestedLocales, options);
}
/**
* Returns a String value representing the written form of a relative date
* formatted according to the effective locale and the formatting options
* of this RelativeTimeFormat object.
*
* Spec: ECMAScript 402 API, RelativeTImeFormat, 1.4.3.
*/
function Intl_RelativeTimeFormat_format(value, unit) {
// Step 1.
let relativeTimeFormat = this;
// Step 2.
let internals = getRelativeTimeFormatInternals(relativeTimeFormat, "format");
// Step 3.
let t = ToNumber(value);
// Step 4.
let u = ToString(unit);
// PartitionRelativeTimePattern, step 4.
if (!Number_isFinite(t)) {
ThrowRangeError(JSMSG_DATE_NOT_FINITE, "RelativeTimeFormat");
}
// PartitionRelativeTimePattern, step 5.
switch (u) {
case "second":
case "seconds":
case "minute":
case "minutes":
case "hour":
case "hours":
case "day":
case "days":
case "week":
case "weeks":
case "month":
case "months":
case "quarter":
case "quarters":
case "year":
case "years":
break;
default:
ThrowRangeError(JSMSG_INVALID_OPTION_VALUE, "unit", u);
}
// Step 5.
return intl_FormatRelativeTime(relativeTimeFormat, t, u, internals.numeric);
}
/**
* Returns the resolved options for a PluralRules object.
*
* Spec: ECMAScript 402 API, RelativeTimeFormat, 1.4.4.
*/
function Intl_RelativeTimeFormat_resolvedOptions() {
var internals = getRelativeTimeFormatInternals(this, "resolvedOptions");
// Steps 4-5.
var result = {
locale: internals.locale,
style: internals.style,
numeric: internals.numeric,
};
// Step 6.
return result;
}
function Intl_getCanonicalLocales(locales) {
let codes = CanonicalizeLocaleList(locales);
let result = [];
+4
View File
@@ -182,6 +182,7 @@
macro(InitializeDateTimeFormat, InitializeDateTimeFormat, "InitializeDateTimeFormat") \
macro(InitializeNumberFormat, InitializeNumberFormat, "InitializeNumberFormat") \
macro(InitializePluralRules, InitializePluralRules, "InitializePluralRules") \
macro(InitializeRelativeTimeFormat, InitializeRelativeTimeFormat, "InitializeRelativeTimeFormat") \
macro(innermost, innermost, "innermost") \
macro(inNursery, inNursery, "inNursery") \
macro(input, input, "input") \
@@ -296,6 +297,8 @@
macro(Reify, Reify, "Reify") \
macro(reject, reject, "reject") \
macro(rejected, rejected, "rejected") \
macro(RelativeTimeFormat, RelativeTimeFormat, "RelativeTimeFormat") \
macro(RelativeTimeFormatFormat, RelativeTimeFormatFormat, "Intl_RelativeTimeFormat_Format") \
macro(RequireObjectCoercible, RequireObjectCoercible, "RequireObjectCoercible") \
macro(resolve, resolve, "resolve") \
macro(resumeGenerator, resumeGenerator, "resumeGenerator") \
@@ -363,6 +366,7 @@
macro(unescape, unescape, "unescape") \
macro(uneval, uneval, "uneval") \
macro(unicode, unicode, "unicode") \
macro(unit, unit, "unit") \
macro(uninitialized, uninitialized, "uninitialized") \
macro(unsized, unsized, "unsized") \
macro(unwatch, unwatch, "unwatch") \
+6
View File
@@ -110,6 +110,7 @@ class GlobalObject : public NativeObject
NUMBER_FORMAT_PROTO,
DATE_TIME_FORMAT_PROTO,
PLURAL_RULES_PROTO,
RELATIVE_TIME_FORMAT_PROTO,
MODULE_PROTO,
IMPORT_ENTRY_PROTO,
EXPORT_ENTRY_PROTO,
@@ -506,6 +507,11 @@ class GlobalObject : public NativeObject
return getOrCreateObject(cx, global, PLURAL_RULES_PROTO, initIntlObject);
}
static JSObject*
getOrCreateRelativeTimeFormatPrototype(JSContext* cx, Handle<GlobalObject*> global) {
return getOrCreateObject(cx, global, RELATIVE_TIME_FORMAT_PROTO, initIntlObject);
}
static bool ensureModulePrototypesCreated(JSContext *cx, Handle<GlobalObject*> global);
JSObject* maybeGetModulePrototype() {
+2
View File
@@ -2697,6 +2697,8 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("intl_PluralRules_availableLocales", intl_PluralRules_availableLocales, 0,0),
JS_FN("intl_GetPluralCategories", intl_GetPluralCategories, 2, 0),
JS_FN("intl_SelectPluralRule", intl_SelectPluralRule, 2,0),
JS_FN("intl_RelativeTimeFormat_availableLocales", intl_RelativeTimeFormat_availableLocales, 0,0),
JS_FN("intl_FormatRelativeTime", intl_FormatRelativeTime, 3,0),
JS_INLINABLE_FN("IsRegExpObject",
intrinsic_IsInstanceOfBuiltin<RegExpObject>, 1,0,