Bug 1287677 - Add mozIntl.getDisplayNames API. r=Waldo

MozReview-Commit-ID: GYVlvSv3Yd9

--HG--
extra : rebase_source : 75f24ca61ab87529f72bcba3aab2157bc29bed98
This commit is contained in:
Zibi Braniecki 2016-11-28 12:06:20 -08:00
Родитель e65067f730
Коммит 6fa9d29045
10 изменённых файлов: 843 добавлений и 1 удалений

Просмотреть файл

@ -104,6 +104,18 @@ Char16ToUChar(char16_t* chars)
MOZ_CRASH("Char16ToUChar: Intl API disabled");
}
inline char16_t*
UCharToChar16(UChar* chars)
{
MOZ_CRASH("UCharToChar16: Intl API disabled");
}
inline const char16_t*
UCharToChar16(const UChar* chars)
{
MOZ_CRASH("UCharToChar16: Intl API disabled");
}
struct UEnumeration;
int32_t
@ -352,6 +364,27 @@ enum UCalendarDateFields {
UCAL_DAY_OF_MONTH = UCAL_DATE
};
enum UCalendarMonths {
UCAL_JANUARY,
UCAL_FEBRUARY,
UCAL_MARCH,
UCAL_APRIL,
UCAL_MAY,
UCAL_JUNE,
UCAL_JULY,
UCAL_AUGUST,
UCAL_SEPTEMBER,
UCAL_OCTOBER,
UCAL_NOVEMBER,
UCAL_DECEMBER,
UCAL_UNDECIMBER
};
enum UCalendarAMPMs {
UCAL_AM,
UCAL_PM
};
UCalendar*
ucal_open(const UChar* zoneID, int32_t len, const char* locale,
UCalendarType type, UErrorCode* status)
@ -416,6 +449,13 @@ ucal_getDefaultTimeZone(UChar* result, int32_t resultCapacity, UErrorCode* statu
MOZ_CRASH("ucal_getDefaultTimeZone: Intl API disabled");
}
enum UDateTimePatternField {
UDATPG_YEAR_FIELD,
UDATPG_MONTH_FIELD,
UDATPG_WEEK_OF_YEAR_FIELD,
UDATPG_DAY_FIELD,
};
typedef void* UDateTimePatternGenerator;
UDateTimePatternGenerator*
@ -432,6 +472,14 @@ udatpg_getBestPattern(UDateTimePatternGenerator* dtpg, const UChar* skeleton,
MOZ_CRASH("udatpg_getBestPattern: Intl API disabled");
}
static const UChar *
udatpg_getAppendItemName(const UDateTimePatternGenerator *dtpg,
UDateTimePatternField field,
int32_t *pLength)
{
MOZ_CRASH("udatpg_getAppendItemName: Intl API disabled");
}
void
udatpg_close(UDateTimePatternGenerator* dtpg)
{
@ -484,10 +532,46 @@ enum UDateFormatField {
};
enum UDateFormatStyle {
UDAT_FULL,
UDAT_LONG,
UDAT_MEDIUM,
UDAT_SHORT,
UDAT_DEFAULT = UDAT_MEDIUM,
UDAT_PATTERN = -2,
UDAT_IGNORE = UDAT_PATTERN
};
enum UDateFormatSymbolType {
UDAT_ERAS,
UDAT_MONTHS,
UDAT_SHORT_MONTHS,
UDAT_WEEKDAYS,
UDAT_SHORT_WEEKDAYS,
UDAT_AM_PMS,
UDAT_LOCALIZED_CHARS,
UDAT_ERA_NAMES,
UDAT_NARROW_MONTHS,
UDAT_NARROW_WEEKDAYS,
UDAT_STANDALONE_MONTHS,
UDAT_STANDALONE_SHORT_MONTHS,
UDAT_STANDALONE_NARROW_MONTHS,
UDAT_STANDALONE_WEEKDAYS,
UDAT_STANDALONE_SHORT_WEEKDAYS,
UDAT_STANDALONE_NARROW_WEEKDAYS,
UDAT_QUARTERS,
UDAT_SHORT_QUARTERS,
UDAT_STANDALONE_QUARTERS,
UDAT_STANDALONE_SHORT_QUARTERS,
UDAT_SHORTER_WEEKDAYS,
UDAT_STANDALONE_SHORTER_WEEKDAYS,
UDAT_CYCLIC_YEARS_WIDE,
UDAT_CYCLIC_YEARS_ABBREVIATED,
UDAT_CYCLIC_YEARS_NARROW,
UDAT_ZODIAC_NAMES_WIDE,
UDAT_ZODIAC_NAMES_ABBREVIATED,
UDAT_ZODIAC_NAMES_NARROW
};
int32_t
udat_countAvailable()
{
@ -561,6 +645,13 @@ udat_close(UDateFormat* format)
} // anonymous namespace
static int32_t
udat_getSymbols(const UDateFormat *fmt, UDateFormatSymbolType type, int32_t symbolIndex,
UChar *result, int32_t resultLength, UErrorCode *status)
{
MOZ_CRASH("udat_getSymbols: Intl API disabled");
}
#endif
@ -2922,6 +3013,296 @@ js::intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp)
return true;
}
template<size_t N>
inline bool
MatchPart(const char** pattern, const char (&part)[N])
{
if (strncmp(*pattern, part, N - 1))
return false;
*pattern += N - 1;
return true;
}
bool
js::intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 3);
// 1. Assert: locale is a string.
MOZ_ASSERT(args[0].isString());
// 2. Assert: style is a string.
MOZ_ASSERT(args[1].isString());
// 3. Assert: keys is an Array.
MOZ_ASSERT(args[2].isObject());
JSAutoByteString locale(cx, args[0].toString());
if (!locale)
return false;
JSAutoByteString style(cx, args[1].toString());
if (!style)
return false;
RootedArrayObject keys(cx, &args[2].toObject().as<ArrayObject>());
if (!keys)
return false;
// 4. Let result be ArrayCreate(0).
RootedArrayObject result(cx, NewDenseUnallocatedArray(cx, keys->length()));
if (!result)
return false;
UErrorCode status = U_ZERO_ERROR;
UDateFormat* fmt =
udat_open(UDAT_DEFAULT, UDAT_DEFAULT, icuLocale(locale.ptr()),
nullptr, 0, nullptr, 0, &status);
if (U_FAILURE(status)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
return false;
}
ScopedICUObject<UDateFormat, udat_close> datToClose(fmt);
// UDateTimePatternGenerator will be needed for translations of date and
// time fields like "month", "week", "day" etc.
UDateTimePatternGenerator* dtpg = udatpg_open(icuLocale(locale.ptr()), &status);
if (U_FAILURE(status)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
return false;
}
ScopedICUObject<UDateTimePatternGenerator, udatpg_close> datPgToClose(dtpg);
RootedValue keyValue(cx);
RootedString keyValStr(cx);
RootedValue wordVal(cx);
Vector<char16_t, INITIAL_CHAR_BUFFER_SIZE> chars(cx);
if (!chars.resize(INITIAL_CHAR_BUFFER_SIZE))
return false;
// 5. For each element of keys,
for (uint32_t i = 0; i < keys->length(); i++) {
/**
* We iterate over keys array looking for paths that we have code
* branches for.
*
* For any unknown path branch, the wordVal will keep NullValue and
* we'll throw at the end.
*/
if (!GetElement(cx, keys, keys, i, &keyValue))
return false;
JSAutoByteString pattern;
keyValStr = keyValue.toString();
if (!pattern.encodeUtf8(cx, keyValStr))
return false;
wordVal.setNull();
// 5.a. Perform an implementation dependent algorithm to map a key to a
// corresponding display name.
const char* pat = pattern.ptr();
if (!MatchPart(&pat, "dates")) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
if (!MatchPart(&pat, "/")) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
if (MatchPart(&pat, "fields")) {
if (!MatchPart(&pat, "/")) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
UDateTimePatternField fieldType;
if (MatchPart(&pat, "year")) {
fieldType = UDATPG_YEAR_FIELD;
} else if (MatchPart(&pat, "month")) {
fieldType = UDATPG_MONTH_FIELD;
} else if (MatchPart(&pat, "week")) {
fieldType = UDATPG_WEEK_OF_YEAR_FIELD;
} else if (MatchPart(&pat, "day")) {
fieldType = UDATPG_DAY_FIELD;
} else {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
// This part must be the final part with no trailing data.
if (*pat != '\0') {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
int32_t resultSize;
const UChar* value = udatpg_getAppendItemName(dtpg, fieldType, &resultSize);
if (U_FAILURE(status)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
return false;
}
JSString* word = NewStringCopyN<CanGC>(cx, UCharToChar16(value), resultSize);
if (!word)
return false;
wordVal.setString(word);
} else if (MatchPart(&pat, "gregorian")) {
if (!MatchPart(&pat, "/")) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
UDateFormatSymbolType symbolType;
int32_t index;
if (MatchPart(&pat, "months")) {
if (!MatchPart(&pat, "/")) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
if (equal(style, "narrow")) {
symbolType = UDAT_STANDALONE_NARROW_MONTHS;
} else if (equal(style, "short")) {
symbolType = UDAT_STANDALONE_SHORT_MONTHS;
} else {
MOZ_ASSERT(equal(style, "long"));
symbolType = UDAT_STANDALONE_MONTHS;
}
if (MatchPart(&pat, "january")) {
index = UCAL_JANUARY;
} else if (MatchPart(&pat, "february")) {
index = UCAL_FEBRUARY;
} else if (MatchPart(&pat, "march")) {
index = UCAL_MARCH;
} else if (MatchPart(&pat, "april")) {
index = UCAL_APRIL;
} else if (MatchPart(&pat, "may")) {
index = UCAL_MAY;
} else if (MatchPart(&pat, "june")) {
index = UCAL_JUNE;
} else if (MatchPart(&pat, "july")) {
index = UCAL_JULY;
} else if (MatchPart(&pat, "august")) {
index = UCAL_AUGUST;
} else if (MatchPart(&pat, "september")) {
index = UCAL_SEPTEMBER;
} else if (MatchPart(&pat, "october")) {
index = UCAL_OCTOBER;
} else if (MatchPart(&pat, "november")) {
index = UCAL_NOVEMBER;
} else if (MatchPart(&pat, "december")) {
index = UCAL_DECEMBER;
} else {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
} else if (MatchPart(&pat, "weekdays")) {
if (!MatchPart(&pat, "/")) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
if (equal(style, "narrow")) {
symbolType = UDAT_STANDALONE_NARROW_WEEKDAYS;
} else if (equal(style, "short")) {
symbolType = UDAT_STANDALONE_SHORT_WEEKDAYS;
} else {
MOZ_ASSERT(equal(style, "long"));
symbolType = UDAT_STANDALONE_WEEKDAYS;
}
if (MatchPart(&pat, "monday")) {
index = UCAL_MONDAY;
} else if (MatchPart(&pat, "tuesday")) {
index = UCAL_TUESDAY;
} else if (MatchPart(&pat, "wednesday")) {
index = UCAL_WEDNESDAY;
} else if (MatchPart(&pat, "thursday")) {
index = UCAL_THURSDAY;
} else if (MatchPart(&pat, "friday")) {
index = UCAL_FRIDAY;
} else if (MatchPart(&pat, "saturday")) {
index = UCAL_SATURDAY;
} else if (MatchPart(&pat, "sunday")) {
index = UCAL_SUNDAY;
} else {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
} else if (MatchPart(&pat, "dayperiods")) {
if (!MatchPart(&pat, "/")) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
symbolType = UDAT_AM_PMS;
if (MatchPart(&pat, "am")) {
index = UCAL_AM;
} else if (MatchPart(&pat, "pm")) {
index = UCAL_PM;
} else {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
} else {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
// This part must be the final part with no trailing data.
if (*pat != '\0') {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
int32_t resultSize =
udat_getSymbols(fmt, symbolType, index, Char16ToUChar(chars.begin()),
INITIAL_CHAR_BUFFER_SIZE, &status);
if (status == U_BUFFER_OVERFLOW_ERROR) {
if (!chars.resize(resultSize))
return false;
status = U_ZERO_ERROR;
udat_getSymbols(fmt, symbolType, index, Char16ToUChar(chars.begin()),
resultSize, &status);
}
if (U_FAILURE(status)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
return false;
}
JSString* word = NewStringCopyN<CanGC>(cx, chars.begin(), resultSize);
if (!word)
return false;
wordVal.setString(word);
} else {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_INVALID_KEY, pattern.ptr());
return false;
}
MOZ_ASSERT(wordVal.isString());
// 5.b. Append the result string to result.
if (!DefineElement(cx, result, i, wordVal))
return false;
}
// 6. Return result.
args.rval().setObject(*result);
return true;
}
/******************** Intl ********************/
const Class js::IntlClass = {

Просмотреть файл

@ -389,6 +389,48 @@ intl_FormatDateTime(JSContext* cx, unsigned argc, Value* vp);
extern MOZ_MUST_USE bool
intl_GetCalendarInfo(JSContext* cx, unsigned argc, Value* vp);
/**
* Returns an Array with CLDR-based fields display names.
* The function takes three arguments:
*
* locale
* BCP47 compliant locale string
* style
* A string with values: long or short or narrow
* keys
* An array or path-like strings that identify keys to be returned
* At the moment the following types of keys are supported:
*
* 'dates/fields/{year|month|week|day}'
* 'dates/gregorian/months/{january|...|december}'
* 'dates/gregorian/weekdays/{sunday|...|saturday}'
* 'dates/gregorian/dayperiods/{am|pm}'
*
* Example:
*
* let info = intl_ComputeDisplayNames(
* 'en-US',
* 'long',
* [
* 'dates/fields/year',
* 'dates/gregorian/months/january',
* 'dates/gregorian/weekdays/monday',
* 'dates/gregorian/dayperiods/am',
* ]
* );
*
* Returned value:
*
* [
* 'year',
* 'January',
* 'Monday',
* 'AM'
* ]
*/
extern MOZ_MUST_USE bool
intl_ComputeDisplayNames(JSContext* cx, unsigned argc, Value* vp);
#if ENABLE_INTL_API
/**
* Cast char16_t* strings to UChar* strings used by ICU.
@ -404,6 +446,18 @@ Char16ToUChar(char16_t* chars)
{
return reinterpret_cast<UChar*>(chars);
}
inline char16_t*
UCharToChar16(UChar* chars)
{
return reinterpret_cast<char16_t*>(chars);
}
inline const char16_t*
UCharToChar16(const UChar* chars)
{
return reinterpret_cast<const char16_t*>(chars);
}
#endif // ENABLE_INTL_API
} // namespace js

Просмотреть файл

@ -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,
@ -3004,3 +3005,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;
}

Просмотреть файл

@ -477,6 +477,8 @@ MSG_DEF(JSMSG_INTL_OBJECT_NOT_INITED, 3, JSEXN_TYPEERR, "Intl.{0}.prototype.{1}
MSG_DEF(JSMSG_INTL_OBJECT_REINITED, 0, JSEXN_TYPEERR, "can't initialize object twice as an object of an Intl constructor")
MSG_DEF(JSMSG_INVALID_CURRENCY_CODE, 1, JSEXN_RANGEERR, "invalid currency code in NumberFormat(): {0}")
MSG_DEF(JSMSG_INVALID_DIGITS_VALUE, 1, JSEXN_RANGEERR, "invalid digits value: {0}")
MSG_DEF(JSMSG_INVALID_KEYS_TYPE, 0, JSEXN_TYPEERR, "calendar info keys must be an object or undefined")
MSG_DEF(JSMSG_INVALID_KEY, 1, JSEXN_RANGEERR, "invalid key: {0}")
MSG_DEF(JSMSG_INVALID_LANGUAGE_TAG, 1, JSEXN_RANGEERR, "invalid language tag: {0}")
MSG_DEF(JSMSG_INVALID_LOCALES_ELEMENT, 0, JSEXN_TYPEERR, "invalid element in locales argument")
MSG_DEF(JSMSG_INVALID_LOCALE_MATCHER, 1, JSEXN_RANGEERR, "invalid locale matcher in supportedLocalesOf(): {0}")

Просмотреть файл

@ -907,6 +907,7 @@ AddIntlExtras(JSContext* cx, unsigned argc, Value* vp)
static const JSFunctionSpec funcs[] = {
JS_SELF_HOSTED_FN("getCalendarInfo", "Intl_getCalendarInfo", 1, 0),
JS_SELF_HOSTED_FN("getDisplayNames", "Intl_getDisplayNames", 2, 0),
JS_FS_END
};

Просмотреть файл

@ -0,0 +1,238 @@
// |reftest| skip-if(!this.hasOwnProperty('Intl')||!this.hasOwnProperty('addIntlExtras'))
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Tests the getCalendarInfo function with a diverse set of arguments.
/*
* Test if getDisplayNames return value matches expected values.
*/
function checkDisplayNames(names, expected)
{
assertEq(Object.getPrototypeOf(names), Object.prototype);
assertEq(names.locale, expected.locale);
assertEq(names.style, expected.style);
const nameValues = names.values;
const expectedValues = expected.values;
const nameValuesKeys = Object.getOwnPropertyNames(nameValues).sort();
const expectedValuesKeys = Object.getOwnPropertyNames(expectedValues).sort();
assertEqArray(nameValuesKeys, expectedValuesKeys);
for (let key of expectedValuesKeys)
assertEq(nameValues[key], expectedValues[key]);
}
addIntlExtras(Intl);
let gDN = Intl.getDisplayNames;
assertEq(gDN.length, 2);
checkDisplayNames(gDN('en-US', {
}), {
locale: 'en-US',
style: 'long',
values: {}
});
checkDisplayNames(gDN('en-US', {
keys: [
'dates/gregorian/weekdays/wednesday'
],
style: 'narrow'
}), {
locale: 'en-US',
style: 'narrow',
values: {
'dates/gregorian/weekdays/wednesday': 'W'
}
});
checkDisplayNames(gDN('en-US', {
keys: [
'dates/fields/year',
'dates/fields/month',
'dates/fields/week',
'dates/fields/day',
'dates/gregorian/months/january',
'dates/gregorian/months/february',
'dates/gregorian/months/march',
'dates/gregorian/weekdays/tuesday'
]
}), {
locale: 'en-US',
style: 'long',
values: {
'dates/fields/year': 'year',
'dates/fields/month': 'month',
'dates/fields/week': 'week',
'dates/fields/day': 'day',
'dates/gregorian/months/january': 'January',
'dates/gregorian/months/february': 'February',
'dates/gregorian/months/march': 'March',
'dates/gregorian/weekdays/tuesday': 'Tuesday',
}
});
checkDisplayNames(gDN('fr', {
keys: [
'dates/fields/year',
'dates/fields/day',
'dates/gregorian/months/october',
'dates/gregorian/weekdays/saturday',
'dates/gregorian/dayperiods/pm'
]
}), {
locale: 'fr',
style: 'long',
values: {
'dates/fields/year': 'année',
'dates/fields/day': 'jour',
'dates/gregorian/months/october': 'octobre',
'dates/gregorian/weekdays/saturday': 'samedi',
'dates/gregorian/dayperiods/pm': 'PM'
}
});
checkDisplayNames(gDN('it', {
style: 'short',
keys: [
'dates/gregorian/weekdays/thursday',
'dates/gregorian/months/august',
'dates/gregorian/dayperiods/am',
'dates/fields/month',
]
}), {
locale: 'it',
style: 'short',
values: {
'dates/gregorian/weekdays/thursday': 'gio',
'dates/gregorian/months/august': 'ago',
'dates/gregorian/dayperiods/am': 'AM',
'dates/fields/month': 'mese'
}
});
checkDisplayNames(gDN('ar', {
style: 'long',
keys: [
'dates/gregorian/weekdays/thursday',
'dates/gregorian/months/august',
'dates/gregorian/dayperiods/am',
'dates/fields/month',
]
}), {
locale: 'ar',
style: 'long',
values: {
'dates/gregorian/weekdays/thursday': 'الخميس',
'dates/gregorian/months/august': 'أغسطس',
'dates/gregorian/dayperiods/am': 'ص',
'dates/fields/month': 'الشهر'
}
});
/* Invalid input */
assertThrowsInstanceOf(() => {
gDN('en-US', {
style: '',
keys: [
'dates/gregorian/weekdays/thursday',
]
});
}, RangeError);
assertThrowsInstanceOf(() => {
gDN('en-US', {
style: 'bogus',
keys: [
'dates/gregorian/weekdays/thursday',
]
});
}, RangeError);
assertThrowsInstanceOf(() => {
gDN('foo-X', {
keys: [
'dates/gregorian/weekdays/thursday',
]
});
}, RangeError);
const typeErrorKeys = [
null,
'string',
Symbol.iterator,
15,
1,
3.7,
NaN,
Infinity
];
for (let keys of typeErrorKeys) {
assertThrowsInstanceOf(() => {
gDN('en-US', {
keys
});
}, TypeError);
}
const rangeErrorKeys = [
[''],
['foo'],
['dates/foo'],
['/dates/foo'],
['dates/foo/foo'],
['dates/fields'],
['dates/fields/'],
['dates/fields/foo'],
['dates/fields/foo/month'],
['/dates/foo/faa/bar/baz'],
['dates///bar/baz'],
['dates/gregorian'],
['dates/gregorian/'],
['dates/gregorian/foo'],
['dates/gregorian/months'],
['dates/gregorian/months/foo'],
['dates/gregorian/weekdays'],
['dates/gregorian/weekdays/foo'],
['dates/gregorian/dayperiods'],
['dates/gregorian/dayperiods/foo'],
['dates/gregorian/months/الشهر'],
[3],
[null],
['d', 'a', 't', 'e', 's'],
['datesEXTRA'],
['dates/fieldsEXTRA'],
['dates/gregorianEXTRA'],
['dates/gregorian/monthsEXTRA'],
['dates/gregorian/weekdaysEXTRA'],
['dates/fields/dayperiods/amEXTRA'],
['dates/gregori\u1161n/months/january'],
["dates/fields/year/"],
["dates/fields/month/"],
["dates/fields/week/"],
["dates/fields/day/"],
["dates/gregorian/months/january/"],
["dates/gregorian/weekdays/saturday/"],
["dates/gregorian/dayperiods/am/"],
["dates/fields/months/january/"],
];
for (let keys of rangeErrorKeys) {
assertThrowsInstanceOf(() => {
gDN('en-US', {
keys
});
}, RangeError);
}
if (typeof reportCompare === 'function')
reportCompare(0, 0);

Просмотреть файл

@ -2477,6 +2477,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("intl_FormatDateTime", intl_FormatDateTime, 2,0),
JS_FN("intl_FormatNumber", intl_FormatNumber, 2,0),
JS_FN("intl_GetCalendarInfo", intl_GetCalendarInfo, 1,0),
JS_FN("intl_ComputeDisplayNames", intl_ComputeDisplayNames, 3,0),
JS_FN("intl_IsValidTimeZoneName", intl_IsValidTimeZoneName, 1,0),
JS_FN("intl_NumberFormat", intl_NumberFormat, 2,0),
JS_FN("intl_NumberFormat_availableLocales", intl_NumberFormat_availableLocales, 0,0),

Просмотреть файл

@ -44,6 +44,32 @@ MozIntl::AddGetCalendarInfo(JS::Handle<JS::Value> val, JSContext* cx)
return NS_OK;
}
NS_IMETHODIMP
MozIntl::AddGetDisplayNames(JS::Handle<JS::Value> val, JSContext* cx)
{
if (!val.isObject()) {
return NS_ERROR_INVALID_ARG;
}
JS::Rooted<JSObject*> realIntlObj(cx, js::CheckedUnwrap(&val.toObject()));
if (!realIntlObj) {
return NS_ERROR_INVALID_ARG;
}
JSAutoCompartment ac(cx, realIntlObj);
static const JSFunctionSpec funcs[] = {
JS_SELF_HOSTED_FN("getDisplayNames", "Intl_getDisplayNames", 2, 0),
JS_FS_END
};
if (!JS_DefineFunctions(cx, realIntlObj, funcs)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_GENERIC_FACTORY_CONSTRUCTOR(MozIntl)
NS_DEFINE_NAMED_CID(MOZ_MOZINTL_CID);

Просмотреть файл

@ -9,4 +9,5 @@
interface mozIMozIntl : nsISupports
{
[implicit_jscontext] void addGetCalendarInfo(in jsval intlObject);
[implicit_jscontext] void addGetDisplayNames(in jsval intlObject);
};

Просмотреть файл

@ -7,6 +7,7 @@ function run_test() {
test_this_global(mozIntl);
test_cross_global(mozIntl);
test_methods_presence(mozIntl);
ok(true);
}
@ -30,3 +31,16 @@ function test_cross_global(mozIntl) {
equal(waivedX.getCalendarInfo() instanceof Object, false);
equal(waivedX.getCalendarInfo() instanceof global.Object, true);
}
function test_methods_presence(mozIntl) {
equal(mozIntl.addGetCalendarInfo instanceof Function, true);
equal(mozIntl.addGetDisplayNames instanceof Function, true);
let x = {};
mozIntl.addGetCalendarInfo(x);
equal(x.getCalendarInfo instanceof Function, true);
mozIntl.addGetDisplayNames(x);
equal(x.getDisplayNames instanceof Function, true);
}