ソースを参照

LibJS: Differentiate between undefined and null locale keys

We were previously treating undefined and null as the same (an empty
Optional). However, there are edge cases in ECMA-402 where we must treat
them differently. Namely, the hour cycle (hc) keyword. An undefined hc
value has no effect on the resolved locale, whereas a null hc value can
actively override any hc specified in the locale string. For example:

    new Intl.DateTimeFormat("en-u-hc-h11", { hour12: false });

In that object, the hour12 option does not match the u-hc-h11 value. So
the spec dictates we remove the hc value by setting it to null.
Timothy Flynn 1 年間 前
コミット
4598a505b1

+ 39 - 7
Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp

@@ -21,6 +21,17 @@
 
 
 namespace JS::Intl {
 namespace JS::Intl {
 
 
+Optional<LocaleKey> locale_key_from_value(Value value)
+{
+    if (value.is_undefined())
+        return OptionalNone {};
+    if (value.is_null())
+        return Empty {};
+    if (value.is_string())
+        return value.as_string().utf8_string();
+    VERIFY_NOT_REACHED();
+}
+
 // 6.2.2 IsStructurallyValidLanguageTag ( locale ), https://tc39.es/ecma402/#sec-isstructurallyvalidlanguagetag
 // 6.2.2 IsStructurallyValidLanguageTag ( locale ), https://tc39.es/ecma402/#sec-isstructurallyvalidlanguagetag
 bool is_structurally_valid_language_tag(StringView locale)
 bool is_structurally_valid_language_tag(StringView locale)
 {
 {
@@ -366,9 +377,30 @@ static auto& find_key_in_value(T& value, StringView key)
     VERIFY_NOT_REACHED();
     VERIFY_NOT_REACHED();
 }
 }
 
 
+static Vector<LocaleKey> available_keyword_values(StringView locale, StringView key)
+{
+    auto key_locale_data = ::Locale::available_keyword_values(locale, key);
+
+    Vector<LocaleKey> result;
+    result.ensure_capacity(key_locale_data.size());
+
+    for (auto& keyword : key_locale_data)
+        result.unchecked_append(move(keyword));
+
+    if (key == "hc"sv) {
+        // https://tc39.es/ecma402/#sec-intl.datetimeformat-internal-slots
+        // [[LocaleData]].[[<locale>]].[[hc]] must be « null, "h11", "h12", "h23", "h24" ».
+        result.prepend(Empty {});
+    }
+
+    return result;
+}
+
 // 9.2.7 ResolveLocale ( availableLocales, requestedLocales, options, relevantExtensionKeys, localeData ), https://tc39.es/ecma402/#sec-resolvelocale
 // 9.2.7 ResolveLocale ( availableLocales, requestedLocales, options, relevantExtensionKeys, localeData ), https://tc39.es/ecma402/#sec-resolvelocale
 LocaleResult resolve_locale(Vector<String> const& requested_locales, LocaleOptions const& options, ReadonlySpan<StringView> relevant_extension_keys)
 LocaleResult resolve_locale(Vector<String> const& requested_locales, LocaleOptions const& options, ReadonlySpan<StringView> relevant_extension_keys)
 {
 {
+    static auto true_string = "true"_string;
+
     // 1. Let matcher be options.[[localeMatcher]].
     // 1. Let matcher be options.[[localeMatcher]].
     auto const& matcher = options.locale_matcher;
     auto const& matcher = options.locale_matcher;
     MatcherResult matcher_result;
     MatcherResult matcher_result;
@@ -416,7 +448,7 @@ LocaleResult resolve_locale(Vector<String> const& requested_locales, LocaleOptio
         // b. Assert: Type(foundLocaleData) is Record.
         // b. Assert: Type(foundLocaleData) is Record.
         // c. Let keyLocaleData be foundLocaleData.[[<key>]].
         // c. Let keyLocaleData be foundLocaleData.[[<key>]].
         // d. Assert: Type(keyLocaleData) is List.
         // d. Assert: Type(keyLocaleData) is List.
-        auto key_locale_data = ::Locale::available_keyword_values(found_locale, key);
+        auto key_locale_data = available_keyword_values(found_locale, key);
 
 
         // e. Let value be keyLocaleData[0].
         // e. Let value be keyLocaleData[0].
         // f. Assert: Type(value) is either String or Null.
         // f. Assert: Type(value) is either String or Null.
@@ -447,9 +479,9 @@ LocaleResult resolve_locale(Vector<String> const& requested_locales, LocaleOptio
                 }
                 }
             }
             }
             // 4. Else if keyLocaleData contains "true", then
             // 4. Else if keyLocaleData contains "true", then
-            else if (key_locale_data.contains_slow("true"sv)) {
+            else if (key_locale_data.contains_slow(true_string)) {
                 // a. Let value be "true".
                 // a. Let value be "true".
-                value = "true"_string;
+                value = true_string;
 
 
                 // b. Let supportedExtensionAddition be the string-concatenation of "-" and key.
                 // b. Let supportedExtensionAddition be the string-concatenation of "-" and key.
                 supported_extension_addition = ::Locale::Keyword { MUST(String::from_utf8(key)), {} };
                 supported_extension_addition = ::Locale::Keyword { MUST(String::from_utf8(key)), {} };
@@ -464,15 +496,15 @@ LocaleResult resolve_locale(Vector<String> const& requested_locales, LocaleOptio
         auto options_value = find_key_in_value(options, key);
         auto options_value = find_key_in_value(options, key);
 
 
         // iii. If Type(optionsValue) is String, then
         // iii. If Type(optionsValue) is String, then
-        if (options_value.has_value()) {
+        if (auto* options_string = options_value.has_value() ? options_value->get_pointer<String>() : nullptr) {
             // 1. Let optionsValue be the string optionsValue after performing the algorithm steps to transform Unicode extension values to canonical syntax per Unicode Technical Standard #35 LDML § 3.2.1 Canonical Unicode Locale Identifiers, treating key as ukey and optionsValue as uvalue productions.
             // 1. Let optionsValue be the string optionsValue after performing the algorithm steps to transform Unicode extension values to canonical syntax per Unicode Technical Standard #35 LDML § 3.2.1 Canonical Unicode Locale Identifiers, treating key as ukey and optionsValue as uvalue productions.
             // 2. Let optionsValue be the string optionsValue after performing the algorithm steps to replace Unicode extension values with their canonical form per Unicode Technical Standard #35 LDML § 3.2.1 Canonical Unicode Locale Identifiers, treating key as ukey and optionsValue as uvalue productions.
             // 2. Let optionsValue be the string optionsValue after performing the algorithm steps to replace Unicode extension values with their canonical form per Unicode Technical Standard #35 LDML § 3.2.1 Canonical Unicode Locale Identifiers, treating key as ukey and optionsValue as uvalue productions.
-            ::Locale::canonicalize_unicode_extension_values(key, *options_value);
+            ::Locale::canonicalize_unicode_extension_values(key, *options_string);
 
 
             // 3. If optionsValue is the empty String, then
             // 3. If optionsValue is the empty String, then
-            if (options_value->is_empty()) {
+            if (options_string->is_empty()) {
                 // a. Let optionsValue be "true".
                 // a. Let optionsValue be "true".
-                options_value = "true"_string;
+                *options_string = true_string;
             }
             }
         }
         }
 
 

+ 15 - 12
Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.h

@@ -21,25 +21,28 @@
 
 
 namespace JS::Intl {
 namespace JS::Intl {
 
 
+using LocaleKey = Variant<Empty, String>;
+Optional<LocaleKey> locale_key_from_value(Value);
+
 struct LocaleOptions {
 struct LocaleOptions {
     Value locale_matcher;
     Value locale_matcher;
-    Optional<String> ca; // [[Calendar]]
-    Optional<String> co; // [[Collation]]
-    Optional<String> hc; // [[HourCycle]]
-    Optional<String> kf; // [[CaseFirst]]
-    Optional<String> kn; // [[Numeric]]
-    Optional<String> nu; // [[NumberingSystem]]
+    Optional<LocaleKey> ca; // [[Calendar]]
+    Optional<LocaleKey> co; // [[Collation]]
+    Optional<LocaleKey> hc; // [[HourCycle]]
+    Optional<LocaleKey> kf; // [[CaseFirst]]
+    Optional<LocaleKey> kn; // [[Numeric]]
+    Optional<LocaleKey> nu; // [[NumberingSystem]]
 };
 };
 
 
 struct LocaleResult {
 struct LocaleResult {
     String locale;
     String locale;
     String data_locale;
     String data_locale;
-    Optional<String> ca; // [[Calendar]]
-    Optional<String> co; // [[Collation]]
-    Optional<String> hc; // [[HourCycle]]
-    Optional<String> kf; // [[CaseFirst]]
-    Optional<String> kn; // [[Numeric]]
-    Optional<String> nu; // [[NumberingSystem]]
+    LocaleKey ca; // [[Calendar]]
+    LocaleKey co; // [[Collation]]
+    LocaleKey hc; // [[HourCycle]]
+    LocaleKey kf; // [[CaseFirst]]
+    LocaleKey kn; // [[Numeric]]
+    LocaleKey nu; // [[NumberingSystem]]
 };
 };
 
 
 struct PatternPartition {
 struct PatternPartition {

+ 20 - 11
Userland/Libraries/LibJS/Runtime/Intl/CollatorConstructor.cpp

@@ -62,16 +62,19 @@ static ThrowCompletionOr<NonnullGCPtr<Collator>> initialize_collator(VM& vm, Col
     auto numeric = TRY(get_option(vm, *options, vm.names.numeric, OptionType::Boolean, {}, Empty {}));
     auto numeric = TRY(get_option(vm, *options, vm.names.numeric, OptionType::Boolean, {}, Empty {}));
 
 
     // 14. If numeric is not undefined, then
     // 14. If numeric is not undefined, then
-    //     a. Let numeric be ! ToString(numeric).
+    if (!numeric.is_undefined()) {
+        // a. Let numeric be ! ToString(numeric).
+        numeric = PrimitiveString::create(vm, MUST(numeric.to_string(vm)));
+    }
+
     // 15. Set opt.[[kn]] to numeric.
     // 15. Set opt.[[kn]] to numeric.
-    if (!numeric.is_undefined())
-        opt.kn = MUST(numeric.to_string(vm));
+    opt.kn = locale_key_from_value(numeric);
 
 
     // 16. Let caseFirst be ? GetOption(options, "caseFirst", string, « "upper", "lower", "false" », undefined).
     // 16. Let caseFirst be ? GetOption(options, "caseFirst", string, « "upper", "lower", "false" », undefined).
-    // 17. Set opt.[[kf]] to caseFirst.
     auto case_first = TRY(get_option(vm, *options, vm.names.caseFirst, OptionType::String, { "upper"sv, "lower"sv, "false"sv }, Empty {}));
     auto case_first = TRY(get_option(vm, *options, vm.names.caseFirst, OptionType::String, { "upper"sv, "lower"sv, "false"sv }, Empty {}));
-    if (!case_first.is_undefined())
-        opt.kf = case_first.as_string().utf8_string();
+
+    // 17. Set opt.[[kf]] to caseFirst.
+    opt.kf = locale_key_from_value(case_first);
 
 
     // 18. Let relevantExtensionKeys be %Collator%.[[RelevantExtensionKeys]].
     // 18. Let relevantExtensionKeys be %Collator%.[[RelevantExtensionKeys]].
     auto relevant_extension_keys = Collator::relevant_extension_keys();
     auto relevant_extension_keys = Collator::relevant_extension_keys();
@@ -83,20 +86,26 @@ static ThrowCompletionOr<NonnullGCPtr<Collator>> initialize_collator(VM& vm, Col
     collator.set_locale(move(result.locale));
     collator.set_locale(move(result.locale));
 
 
     // 21. Let collation be r.[[co]].
     // 21. Let collation be r.[[co]].
+    auto& collation_value = result.co;
+
     // 22. If collation is null, let collation be "default".
     // 22. If collation is null, let collation be "default".
+    if (collation_value.has<Empty>())
+        collation_value = "default"_string;
+
     // 23. Set collator.[[Collation]] to collation.
     // 23. Set collator.[[Collation]] to collation.
-    collator.set_collation(result.co.has_value() ? result.co.release_value() : "default"_string);
+    collator.set_collation(move(collation_value.get<String>()));
 
 
     // 24. If relevantExtensionKeys contains "kn", then
     // 24. If relevantExtensionKeys contains "kn", then
-    if (relevant_extension_keys.span().contains_slow("kn"sv) && result.kn.has_value()) {
+    if (relevant_extension_keys.span().contains_slow("kn"sv)) {
         // a. Set collator.[[Numeric]] to SameValue(r.[[kn]], "true").
         // a. Set collator.[[Numeric]] to SameValue(r.[[kn]], "true").
-        collator.set_numeric(same_value(PrimitiveString::create(vm, result.kn.release_value()), PrimitiveString::create(vm, "true"_string)));
+        collator.set_numeric(result.kn == "true"_string);
     }
     }
 
 
     // 25. If relevantExtensionKeys contains "kf", then
     // 25. If relevantExtensionKeys contains "kf", then
-    if (relevant_extension_keys.span().contains_slow("kf"sv) && result.kf.has_value()) {
+    if (relevant_extension_keys.span().contains_slow("kf"sv)) {
         // a. Set collator.[[CaseFirst]] to r.[[kf]].
         // a. Set collator.[[CaseFirst]] to r.[[kf]].
-        collator.set_case_first(result.kf.release_value());
+        if (auto* resolved_case_first = result.kf.get_pointer<String>())
+            collator.set_case_first(*resolved_case_first);
     }
     }
 
 
     // 26. Let sensitivity be ? GetOption(options, "sensitivity", string, « "base", "accent", "case", "variant" », undefined).
     // 26. Let sensitivity be ? GetOption(options, "sensitivity", string, « "base", "accent", "case", "variant" », undefined).

+ 13 - 14
Userland/Libraries/LibJS/Runtime/Intl/DateTimeFormatConstructor.cpp

@@ -110,11 +110,11 @@ ThrowCompletionOr<NonnullGCPtr<DateTimeFormat>> create_date_time_format(VM& vm,
         // a. If calendar cannot be matched by the type Unicode locale nonterminal, throw a RangeError exception.
         // a. If calendar cannot be matched by the type Unicode locale nonterminal, throw a RangeError exception.
         if (!::Locale::is_type_identifier(calendar.as_string().utf8_string_view()))
         if (!::Locale::is_type_identifier(calendar.as_string().utf8_string_view()))
             return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, calendar, "calendar"sv);
             return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, calendar, "calendar"sv);
-
-        // 9. Set opt.[[ca]] to calendar.
-        opt.ca = calendar.as_string().utf8_string();
     }
     }
 
 
+    // 9. Set opt.[[ca]] to calendar.
+    opt.ca = locale_key_from_value(calendar);
+
     // 10. Let numberingSystem be ? GetOption(options, "numberingSystem", string, empty, undefined).
     // 10. Let numberingSystem be ? GetOption(options, "numberingSystem", string, empty, undefined).
     auto numbering_system = TRY(get_option(vm, *options, vm.names.numberingSystem, OptionType::String, {}, Empty {}));
     auto numbering_system = TRY(get_option(vm, *options, vm.names.numberingSystem, OptionType::String, {}, Empty {}));
 
 
@@ -123,11 +123,11 @@ ThrowCompletionOr<NonnullGCPtr<DateTimeFormat>> create_date_time_format(VM& vm,
         // a. If numberingSystem cannot be matched by the type Unicode locale nonterminal, throw a RangeError exception.
         // a. If numberingSystem cannot be matched by the type Unicode locale nonterminal, throw a RangeError exception.
         if (!::Locale::is_type_identifier(numbering_system.as_string().utf8_string_view()))
         if (!::Locale::is_type_identifier(numbering_system.as_string().utf8_string_view()))
             return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, numbering_system, "numberingSystem"sv);
             return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, numbering_system, "numberingSystem"sv);
-
-        // 12. Set opt.[[nu]] to numberingSystem.
-        opt.nu = numbering_system.as_string().utf8_string();
     }
     }
 
 
+    // 12. Set opt.[[nu]] to numberingSystem.
+    opt.nu = locale_key_from_value(numbering_system);
+
     // 13. Let hour12 be ? GetOption(options, "hour12", boolean, empty, undefined).
     // 13. Let hour12 be ? GetOption(options, "hour12", boolean, empty, undefined).
     auto hour12 = TRY(get_option(vm, *options, vm.names.hour12, OptionType::Boolean, {}, Empty {}));
     auto hour12 = TRY(get_option(vm, *options, vm.names.hour12, OptionType::Boolean, {}, Empty {}));
 
 
@@ -141,8 +141,7 @@ ThrowCompletionOr<NonnullGCPtr<DateTimeFormat>> create_date_time_format(VM& vm,
     }
     }
 
 
     // 16. Set opt.[[hc]] to hourCycle.
     // 16. Set opt.[[hc]] to hourCycle.
-    if (!hour_cycle.is_nullish())
-        opt.hc = hour_cycle.as_string().utf8_string();
+    opt.hc = locale_key_from_value(hour_cycle);
 
 
     // 17. Let localeData be %DateTimeFormat%.[[LocaleData]].
     // 17. Let localeData be %DateTimeFormat%.[[LocaleData]].
     // 18. Let r be ResolveLocale(%DateTimeFormat%.[[AvailableLocales]], requestedLocales, opt, %DateTimeFormat%.[[RelevantExtensionKeys]], localeData).
     // 18. Let r be ResolveLocale(%DateTimeFormat%.[[AvailableLocales]], requestedLocales, opt, %DateTimeFormat%.[[RelevantExtensionKeys]], localeData).
@@ -153,12 +152,12 @@ ThrowCompletionOr<NonnullGCPtr<DateTimeFormat>> create_date_time_format(VM& vm,
 
 
     // 20. Let resolvedCalendar be r.[[ca]].
     // 20. Let resolvedCalendar be r.[[ca]].
     // 21. Set dateTimeFormat.[[Calendar]] to resolvedCalendar.
     // 21. Set dateTimeFormat.[[Calendar]] to resolvedCalendar.
-    if (result.ca.has_value())
-        date_time_format->set_calendar(result.ca.release_value());
+    if (auto* resolved_calendar = result.ca.get_pointer<String>())
+        date_time_format->set_calendar(move(*resolved_calendar));
 
 
     // 22. Set dateTimeFormat.[[NumberingSystem]] to r.[[nu]].
     // 22. Set dateTimeFormat.[[NumberingSystem]] to r.[[nu]].
-    if (result.nu.has_value())
-        date_time_format->set_numbering_system(result.nu.release_value());
+    if (auto* resolved_numbering_system = result.nu.get_pointer<String>())
+        date_time_format->set_numbering_system(move(*resolved_numbering_system));
 
 
     // 23. Let dataLocale be r.[[dataLocale]].
     // 23. Let dataLocale be r.[[dataLocale]].
     auto data_locale = move(result.data_locale);
     auto data_locale = move(result.data_locale);
@@ -184,8 +183,8 @@ ThrowCompletionOr<NonnullGCPtr<DateTimeFormat>> create_date_time_format(VM& vm,
         VERIFY(hour12.is_undefined());
         VERIFY(hour12.is_undefined());
 
 
         // b. Let hc be r.[[hc]].
         // b. Let hc be r.[[hc]].
-        if (result.hc.has_value())
-            hour_cycle_value = ::Locale::hour_cycle_from_string(*result.hc);
+        if (auto* resolved_hour_cycle = result.hc.get_pointer<String>())
+            hour_cycle_value = ::Locale::hour_cycle_from_string(*resolved_hour_cycle);
 
 
         // c. If hc is null, set hc to dataLocaleData.[[hourCycle]].
         // c. If hc is null, set hc to dataLocaleData.[[hourCycle]].
         if (!hour_cycle_value.has_value())
         if (!hour_cycle_value.has_value())

+ 3 - 4
Userland/Libraries/LibJS/Runtime/Intl/DurationFormatConstructor.cpp

@@ -77,8 +77,7 @@ ThrowCompletionOr<NonnullGCPtr<Object>> DurationFormatConstructor::construct(Fun
     // 8. Let opt be the Record { [[localeMatcher]]: matcher, [[nu]]: numberingSystem }.
     // 8. Let opt be the Record { [[localeMatcher]]: matcher, [[nu]]: numberingSystem }.
     LocaleOptions opt {};
     LocaleOptions opt {};
     opt.locale_matcher = matcher;
     opt.locale_matcher = matcher;
-    if (!numbering_system.is_undefined())
-        opt.nu = numbering_system.as_string().utf8_string();
+    opt.nu = locale_key_from_value(numbering_system);
 
 
     // 9. Let r be ResolveLocale(%DurationFormat%.[[AvailableLocales]], requestedLocales, opt, %DurationFormat%.[[RelevantExtensionKeys]], %DurationFormat%.[[LocaleData]]).
     // 9. Let r be ResolveLocale(%DurationFormat%.[[AvailableLocales]], requestedLocales, opt, %DurationFormat%.[[RelevantExtensionKeys]], %DurationFormat%.[[LocaleData]]).
     auto result = resolve_locale(requested_locales, opt, DurationFormat::relevant_extension_keys());
     auto result = resolve_locale(requested_locales, opt, DurationFormat::relevant_extension_keys());
@@ -110,8 +109,8 @@ ThrowCompletionOr<NonnullGCPtr<Object>> DurationFormatConstructor::construct(Fun
     duration_format->set_minutes_seconds_separator(move(digital_format.minutes_seconds_separator));
     duration_format->set_minutes_seconds_separator(move(digital_format.minutes_seconds_separator));
 
 
     // 22. Set durationFormat.[[NumberingSystem]] to r.[[nu]].
     // 22. Set durationFormat.[[NumberingSystem]] to r.[[nu]].
-    if (result.nu.has_value())
-        duration_format->set_numbering_system(result.nu.release_value());
+    if (auto* resolved_numbering_system = result.nu.get_pointer<String>())
+        duration_format->set_numbering_system(move(*resolved_numbering_system));
 
 
     // 23. Let style be ? GetOption(options, "style", string, « "long", "short", "narrow", "digital" », "short").
     // 23. Let style be ? GetOption(options, "style", string, « "long", "short", "narrow", "digital" », "short").
     auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "long"sv, "short"sv, "narrow"sv, "digital"sv }, "short"sv));
     auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "long"sv, "short"sv, "narrow"sv, "digital"sv }, "short"sv));

+ 5 - 5
Userland/Libraries/LibJS/Runtime/Intl/NumberFormatConstructor.cpp

@@ -106,11 +106,11 @@ ThrowCompletionOr<NonnullGCPtr<NumberFormat>> initialize_number_format(VM& vm, N
         // a. If numberingSystem does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.
         // a. If numberingSystem does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.
         if (!::Locale::is_type_identifier(numbering_system.as_string().utf8_string_view()))
         if (!::Locale::is_type_identifier(numbering_system.as_string().utf8_string_view()))
             return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, numbering_system, "numberingSystem"sv);
             return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, numbering_system, "numberingSystem"sv);
-
-        // 8. Set opt.[[nu]] to numberingSystem.
-        opt.nu = numbering_system.as_string().utf8_string();
     }
     }
 
 
+    // 8. Set opt.[[nu]] to numberingSystem.
+    opt.nu = locale_key_from_value(numbering_system);
+
     // 9. Let localeData be %NumberFormat%.[[LocaleData]].
     // 9. Let localeData be %NumberFormat%.[[LocaleData]].
     // 10. Let r be ResolveLocale(%NumberFormat%.[[AvailableLocales]], requestedLocales, opt, %NumberFormat%.[[RelevantExtensionKeys]], localeData).
     // 10. Let r be ResolveLocale(%NumberFormat%.[[AvailableLocales]], requestedLocales, opt, %NumberFormat%.[[RelevantExtensionKeys]], localeData).
     auto result = resolve_locale(requested_locales, opt, NumberFormat::relevant_extension_keys());
     auto result = resolve_locale(requested_locales, opt, NumberFormat::relevant_extension_keys());
@@ -122,8 +122,8 @@ ThrowCompletionOr<NonnullGCPtr<NumberFormat>> initialize_number_format(VM& vm, N
     number_format.set_data_locale(move(result.data_locale));
     number_format.set_data_locale(move(result.data_locale));
 
 
     // 13. Set numberFormat.[[NumberingSystem]] to r.[[nu]].
     // 13. Set numberFormat.[[NumberingSystem]] to r.[[nu]].
-    if (result.nu.has_value())
-        number_format.set_numbering_system(result.nu.release_value());
+    if (auto* resolved_numbering_system = result.nu.get_pointer<String>())
+        number_format.set_numbering_system(move(*resolved_numbering_system));
 
 
     // 14. Perform ? SetNumberFormatUnitOptions(numberFormat, options).
     // 14. Perform ? SetNumberFormatUnitOptions(numberFormat, options).
     TRY(set_number_format_unit_options(vm, number_format, *options));
     TRY(set_number_format_unit_options(vm, number_format, *options));

+ 5 - 5
Userland/Libraries/LibJS/Runtime/Intl/RelativeTimeFormatConstructor.cpp

@@ -77,11 +77,11 @@ ThrowCompletionOr<NonnullGCPtr<Object>> RelativeTimeFormatConstructor::construct
         // a. If numberingSystem cannot be matched by the type Unicode locale nonterminal, throw a RangeError exception.
         // a. If numberingSystem cannot be matched by the type Unicode locale nonterminal, throw a RangeError exception.
         if (!::Locale::is_type_identifier(numbering_system.as_string().utf8_string_view()))
         if (!::Locale::is_type_identifier(numbering_system.as_string().utf8_string_view()))
             return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, numbering_system, "numberingSystem"sv);
             return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, numbering_system, "numberingSystem"sv);
-
-        // 10. Set opt.[[nu]] to numberingSystem.
-        opt.nu = numbering_system.as_string().utf8_string();
     }
     }
 
 
+    // 10. Set opt.[[nu]] to numberingSystem.
+    opt.nu = locale_key_from_value(numbering_system);
+
     // 11. Let r be ResolveLocale(%Intl.RelativeTimeFormat%.[[AvailableLocales]], requestedLocales, opt, %Intl.RelativeTimeFormat%.[[RelevantExtensionKeys]], %Intl.RelativeTimeFormat%.[[LocaleData]]).
     // 11. Let r be ResolveLocale(%Intl.RelativeTimeFormat%.[[AvailableLocales]], requestedLocales, opt, %Intl.RelativeTimeFormat%.[[RelevantExtensionKeys]], %Intl.RelativeTimeFormat%.[[LocaleData]]).
     auto result = resolve_locale(requested_locales, opt, RelativeTimeFormat::relevant_extension_keys());
     auto result = resolve_locale(requested_locales, opt, RelativeTimeFormat::relevant_extension_keys());
 
 
@@ -95,8 +95,8 @@ ThrowCompletionOr<NonnullGCPtr<Object>> RelativeTimeFormatConstructor::construct
     relative_time_format->set_data_locale(move(result.data_locale));
     relative_time_format->set_data_locale(move(result.data_locale));
 
 
     // 15. Set relativeTimeFormat.[[NumberingSystem]] to r.[[nu]].
     // 15. Set relativeTimeFormat.[[NumberingSystem]] to r.[[nu]].
-    if (result.nu.has_value())
-        relative_time_format->set_numbering_system(result.nu.release_value());
+    if (auto* resolved_numbering_system = result.nu.get_pointer<String>())
+        relative_time_format->set_numbering_system(move(*resolved_numbering_system));
 
 
     // 16. Let style be ? GetOption(options, "style", string, « "long", "short", "narrow" », "long").
     // 16. Let style be ? GetOption(options, "style", string, « "long", "short", "narrow" », "long").
     auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "long"sv, "short"sv, "narrow"sv }, "long"sv));
     auto style = TRY(get_option(vm, *options, vm.names.style, OptionType::String, { "long"sv, "short"sv, "narrow"sv }, "long"sv));