Commit 40d9fca8 authored by Frank Tang's avatar Frank Tang Committed by Commit Bot

[Intl] Add "numberingSystem" for Intl.RelativeTimeFormat

Create an Intl::GetNumberingSystem function shared by several
Intl object which throw exception internally.

Bug: v8:9190
Change-Id: Ibe658e61bc8d0e5c061b26fe8527e69d086ea185
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1590442Reviewed-by: 's avatarFrank Tang <ftang@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran <gsathya@chromium.org>
Commit-Queue: Frank Tang <ftang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61419}
parent bf9e3e4d
......@@ -1495,7 +1495,9 @@ bool Intl::IsValidCalendar(const icu::Locale& locale,
return IsValidExtension<icu::Calendar>(locale, "calendar", value);
}
bool Intl::IsValidNumberingSystem(const std::string& value) {
namespace {
bool IsValidNumberingSystem(const std::string& value) {
std::set<std::string> invalid_values = {"native", "traditio", "finance"};
if (invalid_values.find(value) != invalid_values.end()) return false;
UErrorCode status = U_ZERO_ERROR;
......@@ -1504,8 +1506,6 @@ bool Intl::IsValidNumberingSystem(const std::string& value) {
return U_SUCCESS(status) && numbering_system.get() != nullptr;
}
namespace {
std::map<std::string, std::string> LookupAndValidateUnicodeExtensions(
icu::Locale* icu_locale, const std::set<std::string>& relevant_keys) {
std::map<std::string, std::string> extensions;
......@@ -1567,7 +1567,7 @@ std::map<std::string, std::string> LookupAndValidateUnicodeExtensions(
std::set<std::string> valid_values = {"upper", "lower", "false"};
is_valid_value = valid_values.find(bcp47_value) != valid_values.end();
} else if (strcmp("nu", bcp47_key) == 0) {
is_valid_value = Intl::IsValidNumberingSystem(bcp47_value);
is_valid_value = IsValidNumberingSystem(bcp47_value);
}
if (is_valid_value) {
extensions.insert(
......@@ -1879,6 +1879,29 @@ Maybe<Intl::MatcherOption> Intl::GetLocaleMatcher(Isolate* isolate,
Intl::MatcherOption::kLookup);
}
Maybe<bool> Intl::GetNumberingSystem(Isolate* isolate,
Handle<JSReceiver> options,
const char* method,
std::unique_ptr<char[]>* result) {
const std::vector<const char*> empty_values = {};
Maybe<bool> maybe = Intl::GetStringOption(isolate, options, "numberingSystem",
empty_values, method, result);
MAYBE_RETURN(maybe, Nothing<bool>());
if (maybe.FromJust() && *result != nullptr) {
if (!IsValidNumberingSystem(result->get())) {
THROW_NEW_ERROR_RETURN_VALUE(
isolate,
NewRangeError(
MessageTemplate::kInvalid,
isolate->factory()->numberingSystem_string(),
isolate->factory()->NewStringFromAsciiChecked(result->get())),
Nothing<bool>());
}
return Just(true);
}
return Just(false);
}
Intl::HourCycle Intl::ToHourCycle(const std::string& hc) {
if (hc == "h11") return Intl::HourCycle::kH11;
if (hc == "h12") return Intl::HourCycle::kH12;
......
......@@ -249,13 +249,15 @@ class Intl {
V8_WARN_UNUSED_RESULT static Maybe<MatcherOption> GetLocaleMatcher(
Isolate* isolate, Handle<JSReceiver> options, const char* method);
// Shared function to read the "numberingSystem" option.
V8_WARN_UNUSED_RESULT static Maybe<bool> GetNumberingSystem(
Isolate* isolate, Handle<JSReceiver> options, const char* method,
std::unique_ptr<char[]>* result);
// Check the calendar is valid or not for that locale.
static bool IsValidCalendar(const icu::Locale& locale,
const std::string& value);
// Check the numberingSystem is valid or not.
static bool IsValidNumberingSystem(const std::string& value);
struct ResolvedLocale {
std::string locale;
icu::Locale icu_locale;
......
......@@ -1184,7 +1184,7 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
std::unique_ptr<char[]> numbering_system_str = nullptr;
if (FLAG_harmony_intl_add_calendar_numbering_system) {
const std::vector<const char*> empty_values = {};
// 6. Let numberingSystem be ? GetOption(options, "calendar",
// 6. Let calendar be ? GetOption(options, "calendar",
// "string", undefined, undefined).
Maybe<bool> maybe_calendar =
Intl::GetStringOption(isolate, options, "calendar", empty_values,
......@@ -1196,8 +1196,7 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
THROW_NEW_ERROR(
isolate,
NewRangeError(
MessageTemplate::kInvalid,
factory->NewStringFromStaticChars("calendar"),
MessageTemplate::kInvalid, factory->calendar_string(),
factory->NewStringFromAsciiChecked(calendar_str.get())),
JSDateTimeFormat);
}
......@@ -1205,21 +1204,9 @@ MaybeHandle<JSDateTimeFormat> JSDateTimeFormat::Initialize(
// 8. Let numberingSystem be ? GetOption(options, "numberingSystem",
// "string", undefined, undefined).
Maybe<bool> maybe_numberingSystem =
Intl::GetStringOption(isolate, options, "numberingSystem", empty_values,
"Intl.NumberFormat", &numbering_system_str);
Maybe<bool> maybe_numberingSystem = Intl::GetNumberingSystem(
isolate, options, "Intl.NumberFormat", &numbering_system_str);
MAYBE_RETURN(maybe_numberingSystem, MaybeHandle<JSDateTimeFormat>());
if (maybe_numberingSystem.FromJust() && numbering_system_str != nullptr) {
if (!Intl::IsValidNumberingSystem(numbering_system_str.get())) {
THROW_NEW_ERROR(
isolate,
NewRangeError(
MessageTemplate::kInvalid,
factory->NewStringFromStaticChars("numberingSystem"),
factory->NewStringFromAsciiChecked(numbering_system_str.get())),
JSDateTimeFormat);
}
}
}
Maybe<Intl::MatcherOption> maybe_locale_matcher =
......
......@@ -254,24 +254,15 @@ MaybeHandle<JSNumberFormat> JSNumberFormat::Initialize(
std::unique_ptr<char[]> numbering_system_str = nullptr;
if (FLAG_harmony_intl_add_calendar_numbering_system) {
// 7. Let numberingSystem be ? GetOption(options, "numberingSystem",
// "string", undefined, undefined).
const std::vector<const char*> empty_values = {};
Maybe<bool> maybe_numberingSystem =
Intl::GetStringOption(isolate, options, "numberingSystem", empty_values,
"Intl.NumberFormat", &numbering_system_str);
// 7. Let _numberingSystem_ be ? GetOption(_options_, `"numberingSystem"`,
// `"string"`, *undefined*, *undefined*).
Maybe<bool> maybe_numberingSystem = Intl::GetNumberingSystem(
isolate, options, "Intl.RelativeTimeFormat", &numbering_system_str);
// 8. If _numberingSystem_ is not *undefined*, then
// a. If _numberingSystem_ does not match the
// `(3*8alphanum) *("-" (3*8alphanum))` sequence, throw a *RangeError*
// exception.
MAYBE_RETURN(maybe_numberingSystem, MaybeHandle<JSNumberFormat>());
if (maybe_numberingSystem.FromJust() && numbering_system_str != nullptr) {
if (!Intl::IsValidNumberingSystem(numbering_system_str.get())) {
THROW_NEW_ERROR(
isolate,
NewRangeError(
MessageTemplate::kInvalid,
factory->NewStringFromStaticChars("numberingSystem"),
factory->NewStringFromAsciiChecked(numbering_system_str.get())),
JSNumberFormat);
}
}
}
// 7. Let localeData be %NumberFormat%.[[LocaleData]].
......
......@@ -88,8 +88,21 @@ MaybeHandle<JSRelativeTimeFormat> JSRelativeTimeFormat::Initialize(
MAYBE_RETURN(maybe_locale_matcher, MaybeHandle<JSRelativeTimeFormat>());
Intl::MatcherOption matcher = maybe_locale_matcher.FromJust();
// 7. Let localeData be %RelativeTimeFormat%.[[LocaleData]].
// 8. Let r be
// 7. Let _numberingSystem_ be ? GetOption(_options_, `"numberingSystem"`,
// `"string"`, *undefined*, *undefined*).
std::unique_ptr<char[]> numbering_system_str = nullptr;
Maybe<bool> maybe_numberingSystem = Intl::GetNumberingSystem(
isolate, options, "Intl.RelativeTimeFormat", &numbering_system_str);
// 8. If _numberingSystem_ is not *undefined*, then
// a. If _numberingSystem_ does not match the
// `(3*8alphanum) *("-" (3*8alphanum))` sequence, throw a *RangeError*
// exception.
MAYBE_RETURN(maybe_numberingSystem, MaybeHandle<JSRelativeTimeFormat>());
// 9. Set _opt_.[[nu]] to _numberingSystem_.
// 10. Let localeData be %RelativeTimeFormat%.[[LocaleData]].
// 11. Let r be
// ResolveLocale(%RelativeTimeFormat%.[[AvailableLocales]],
// requestedLocales, opt,
// %RelativeTimeFormat%.[[RelevantExtensionKeys]], localeData).
......@@ -97,14 +110,24 @@ MaybeHandle<JSRelativeTimeFormat> JSRelativeTimeFormat::Initialize(
Intl::ResolveLocale(isolate, JSRelativeTimeFormat::GetAvailableLocales(),
requested_locales, matcher, {"nu"});
// 9. Let locale be r.[[Locale]].
// 10. Set relativeTimeFormat.[[Locale]] to locale.
// 11. Let dataLocale be r.[[DataLocale]].
Handle<String> locale_str =
isolate->factory()->NewStringFromAsciiChecked(r.locale.c_str());
// 12. Let locale be r.[[Locale]].
// 13. Set relativeTimeFormat.[[Locale]] to locale.
// 14. Let dataLocale be r.[[DataLocale]].
icu::Locale icu_locale = r.icu_locale;
UErrorCode status = U_ZERO_ERROR;
if (numbering_system_str != nullptr) {
icu_locale.setUnicodeKeywordValue("nu", numbering_system_str.get(), status);
CHECK(U_SUCCESS(status));
}
Maybe<std::string> maybe_locale_str = Intl::ToLanguageTag(icu_locale);
MAYBE_RETURN(maybe_locale_str, MaybeHandle<JSRelativeTimeFormat>());
Handle<String> locale_str = isolate->factory()->NewStringFromAsciiChecked(
maybe_locale_str.FromJust().c_str());
relative_time_format_holder->set_locale(*locale_str);
// 12. Let s be ? GetOption(options, "style", "string",
// 15. Let s be ? GetOption(options, "style", "string",
// «"long", "short", "narrow"», "long").
Maybe<Style> maybe_style = Intl::GetStringOption<Style>(
isolate, options, "style", "Intl.RelativeTimeFormat",
......@@ -113,10 +136,10 @@ MaybeHandle<JSRelativeTimeFormat> JSRelativeTimeFormat::Initialize(
MAYBE_RETURN(maybe_style, MaybeHandle<JSRelativeTimeFormat>());
Style style_enum = maybe_style.FromJust();
// 13. Set relativeTimeFormat.[[Style]] to s.
// 16. Set relativeTimeFormat.[[Style]] to s.
relative_time_format_holder->set_style(style_enum);
// 14. Let numeric be ? GetOption(options, "numeric", "string",
// 17. Let numeric be ? GetOption(options, "numeric", "string",
// «"always", "auto"», "always").
Maybe<Numeric> maybe_numeric = Intl::GetStringOption<Numeric>(
isolate, options, "numeric", "Intl.RelativeTimeFormat",
......@@ -124,12 +147,9 @@ MaybeHandle<JSRelativeTimeFormat> JSRelativeTimeFormat::Initialize(
MAYBE_RETURN(maybe_numeric, MaybeHandle<JSRelativeTimeFormat>());
Numeric numeric_enum = maybe_numeric.FromJust();
// 15. Set relativeTimeFormat.[[Numeric]] to numeric.
// 18. Set relativeTimeFormat.[[Numeric]] to numeric.
relative_time_format_holder->set_numeric(numeric_enum);
icu::Locale icu_locale = r.icu_locale;
UErrorCode status = U_ZERO_ERROR;
// 19. Let relativeTimeFormat.[[NumberFormat]] be
// ? Construct(%NumberFormat%, « nfLocale, nfOptions »).
icu::NumberFormat* number_format =
......
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Based on https://www.ecma-international.org/ecma-402/#table-numbering-system-digits
let testCases = [
["arab", "in ١٢٣ days"], // U+0660 to U+0669
["arabext", "in ۱۲۳ days"], // U+06F0 to U+06F9
["bali", "in ᭑᭒᭓ days"], // U+1B50 to U+1B59
["beng", "in ১২৩ days"], // U+09E6 to U+09EF
["deva", "in १२३ days"], // U+0966 to U+096F
["fullwide", "in 123 days"], // U+FF10 to U+FF19
["gujr", "in ૧૨૩ days"], // U+0AE6 to U+0AEF
["guru", "in ੧੨੩ days"], // U+0A66 to U+0A6F
// U+3007, U+4E00, U+4E8C, U+4E09, U+56DB, U+4E94, U+516D, U+4E03, U+516B, U+4E5D
["hanidec", "in 一二三 days"],
["khmr", "in ១២៣ days"], // U+17E0 to U+17E9
["knda", "in ೧೨೩ days"], // U+0CE6 to U+0CEF
["laoo", "in ໑໒໓ days"], // U+0ED0 to U+0ED9
["latn", "in 123 days"], // U+0030 to U+0039
["limb", "in ᥇᥈᥉ days"], // U+1946 to U+194F
["mlym", "in ൧൨൩ days"], // U+0D66 to U+0D6F
["mong", "in ᠑᠒᠓ days"], // U+1810 to U+1819
["mymr", "in ၁၂၃ days"], // U+1040 to U+1049
["orya", "in ୧୨୩ days"], // U+0B66 to U+0B6F
["tamldec", "in ௧௨௩ days"], // U+0BE6 to U+0BEF
["telu", "in ౧౨౩ days"], // U+0C66 to U+0C6F
["thai", "in ๑๒๓ days"], // U+0E50 to U+0E59
["tibt", "in ༡༢༣ days"], // U+0F20 to U+0F29
];
for ([numberingSystem, expected] of testCases) {
let byLocale = new Intl.RelativeTimeFormat("en-u-nu-" + numberingSystem);
let byOptions = new Intl.RelativeTimeFormat("en", { numberingSystem });
// Check the numberingSystem in the resolvedOptions matched.
assertEquals(numberingSystem,
byOptions.resolvedOptions().numberingSystem, numberingSystem);
assertEquals(byLocale.resolvedOptions().numberingSystem,
byOptions.resolvedOptions().numberingSystem, numberingSystem);
// Check the formatted result are the same as if creating by using -u-nu- in
// locale.
assertEquals(byLocale.format(123, "day"), byOptions.format(123, "day"), numberingSystem);
assertEquals(expected, byLocale.format(123, "day"), numberingSystem);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment