summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp72
-rw-r--r--Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.h3
-rw-r--r--Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp23
-rw-r--r--Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.h2
-rw-r--r--Userland/Libraries/LibJS/Tests/builtins/Intl/DisplayNames/DisplayNames.supportedLocalesOf.js43
6 files changed, 144 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h
index f0604cf635..2fbf591ce0 100644
--- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h
+++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h
@@ -403,6 +403,7 @@ namespace JS {
P(substring) \
P(subtract) \
P(sup) \
+ P(supportedLocalesOf) \
P(tan) \
P(tanh) \
P(test) \
diff --git a/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp
index 36283eb16c..39f8b51229 100644
--- a/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.cpp
@@ -7,6 +7,7 @@
#include <AK/AllOf.h>
#include <AK/AnyOf.h>
#include <AK/CharacterTypes.h>
+#include <AK/Function.h>
#include <AK/QuickSort.h>
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/Array.h>
@@ -402,6 +403,77 @@ LocaleResult resolve_locale(Vector<String> const& requested_locales, LocaleOptio
return result;
}
+// 9.2.8 LookupSupportedLocales ( availableLocales, requestedLocales ), https://tc39.es/ecma402/#sec-lookupsupportedlocales
+Vector<String> lookup_supported_locales(Vector<String> const& requested_locales)
+{
+ // 1. Let subset be a new empty List.
+ Vector<String> subset;
+
+ // 2. For each element locale of requestedLocales, do
+ for (auto const& locale : requested_locales) {
+ auto locale_id = Unicode::parse_unicode_locale_id(locale);
+ VERIFY(locale_id.has_value());
+
+ // a. Let noExtensionsLocale be the String value that is locale with any Unicode locale extension sequences removed.
+ locale_id->remove_extension_type<Unicode::LocaleExtension>();
+ auto no_extensions_locale = locale_id->to_string();
+
+ // b. Let availableLocale be BestAvailableLocale(availableLocales, noExtensionsLocale).
+ auto available_locale = best_available_locale(no_extensions_locale);
+
+ // c. If availableLocale is not undefined, append locale to the end of subset.
+ if (available_locale.has_value())
+ subset.append(locale);
+ }
+
+ // 3. Return subset.
+ return subset;
+}
+
+// 9.2.9 BestFitSupportedLocales ( availableLocales, requestedLocales ), https://tc39.es/ecma402/#sec-bestfitsupportedlocales
+Vector<String> best_fit_supported_locales(Vector<String> const& requested_locales)
+{
+ // The BestFitSupportedLocales abstract operation returns the subset of the provided BCP 47
+ // language priority list requestedLocales for which availableLocales has a matching locale
+ // when using the Best Fit Matcher algorithm. Locales appear in the same order in the returned
+ // list as in requestedLocales. The steps taken are implementation dependent.
+
+ // :yakbrain:
+ return lookup_supported_locales(requested_locales);
+}
+
+// 9.2.10 SupportedLocales ( availableLocales, requestedLocales, options ), https://tc39.es/ecma402/#sec-supportedlocales
+Array* supported_locales(GlobalObject& global_object, Vector<String> const& requested_locales, Value options)
+{
+ auto& vm = global_object.vm();
+
+ // 1. Set options to ? CoerceOptionsToObject(options).
+ auto* options_object = coerce_options_to_object(global_object, options);
+ if (vm.exception())
+ return {};
+
+ // 2. Let matcher be ? GetOption(options, "localeMatcher", "string", ยซ "lookup", "best fit" ยป, "best fit").
+ auto matcher = get_option(global_object, options_object, vm.names.localeMatcher, Value::Type::String, { "lookup"sv, "best fit"sv }, "best fit"sv);
+ if (vm.exception())
+ return {};
+
+ Vector<String> supported_locales;
+
+ // 3. If matcher is "best fit", then
+ if (matcher.as_string().string() == "best fit"sv) {
+ // a. Let supportedLocales be BestFitSupportedLocales(availableLocales, requestedLocales).
+ supported_locales = best_fit_supported_locales(requested_locales);
+ }
+ // 4. Else,
+ else {
+ // a. Let supportedLocales be LookupSupportedLocales(availableLocales, requestedLocales).
+ supported_locales = lookup_supported_locales(requested_locales);
+ }
+
+ // 5. Return CreateArrayFromList(supportedLocales).
+ return Array::create_from<String>(global_object, supported_locales, [&vm](auto& locale) { return js_string(vm, locale); });
+}
+
// 9.2.12 CoerceOptionsToObject ( options ), https://tc39.es/ecma402/#sec-coerceoptionstoobject
Object* coerce_options_to_object(GlobalObject& global_object, Value options)
{
diff --git a/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.h b/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.h
index be5c4c9e7d..03a4853fad 100644
--- a/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.h
+++ b/Userland/Libraries/LibJS/Runtime/Intl/AbstractOperations.h
@@ -29,6 +29,9 @@ struct LocaleResult {
Optional<Unicode::LocaleID> is_structurally_valid_language_tag(StringView locale);
String canonicalize_unicode_locale_id(Unicode::LocaleID& locale);
Vector<String> canonicalize_locale_list(GlobalObject&, Value locales);
+Vector<String> best_fit_supported_locales(Vector<String> const& requested_locales);
+Vector<String> lookup_supported_locales(Vector<String> const& requested_locales);
+Array* supported_locales(GlobalObject&, Vector<String> const& requested_locales, Value options);
Object* coerce_options_to_object(GlobalObject& global_object, Value options);
Value get_option(GlobalObject& global_object, Value options, PropertyName const& property, Value::Type type, Vector<StringView> const& values, Fallback fallback);
String insert_unicode_extension_and_canonicalize(Unicode::LocaleID locale_id, Unicode::LocaleExtension extension);
diff --git a/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp
index d8990d8ee4..51099e859c 100644
--- a/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp
+++ b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.cpp
@@ -5,6 +5,7 @@
*/
#include <LibJS/Runtime/AbstractOperations.h>
+#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Intl/AbstractOperations.h>
#include <LibJS/Runtime/Intl/DisplayNames.h>
@@ -28,6 +29,10 @@ void DisplayNamesConstructor::initialize(GlobalObject& global_object)
// 12.3.1 Intl.DisplayNames.prototype, https://tc39.es/ecma402/#sec-Intl.DisplayNames.prototype
define_direct_property(vm.names.prototype, global_object.intl_display_names_prototype(), 0);
+
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+ define_native_function(vm.names.supportedLocalesOf, supported_locales_of, 1, attr);
+
define_direct_property(vm.names.length, Value(2), Attribute::Configurable);
}
@@ -125,4 +130,22 @@ Value DisplayNamesConstructor::construct(FunctionObject& new_target)
return display_names;
}
+// 12.3.2 Intl.DisplayNames.supportedLocalesOf ( locales [ , options ] ), https://tc39.es/ecma402/#sec-Intl.DisplayNames.supportedLocalesOf
+JS_DEFINE_NATIVE_FUNCTION(DisplayNamesConstructor::supported_locales_of)
+{
+ auto locales = vm.argument(0);
+ auto options = vm.argument(1);
+
+ // 1. Let availableLocales be %DisplayNames%.[[AvailableLocales]].
+ // No-op, availability of each requested locale is checked via Unicode::is_locale_available()
+
+ // 2. Let requestedLocales be ? CanonicalizeLocaleList(locales).
+ auto requested_locales = canonicalize_locale_list(global_object, locales);
+ if (vm.exception())
+ return {};
+
+ // 3. Return ? SupportedLocales(availableLocales, requestedLocales, options).
+ return supported_locales(global_object, requested_locales, options);
+}
+
}
diff --git a/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.h b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.h
index 5ea50241c6..3e7afc7143 100644
--- a/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.h
+++ b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesConstructor.h
@@ -23,6 +23,8 @@ public:
private:
virtual bool has_constructor() const override { return true; }
+
+ JS_DECLARE_NATIVE_FUNCTION(supported_locales_of);
};
}
diff --git a/Userland/Libraries/LibJS/Tests/builtins/Intl/DisplayNames/DisplayNames.supportedLocalesOf.js b/Userland/Libraries/LibJS/Tests/builtins/Intl/DisplayNames/DisplayNames.supportedLocalesOf.js
new file mode 100644
index 0000000000..a5d6ade92e
--- /dev/null
+++ b/Userland/Libraries/LibJS/Tests/builtins/Intl/DisplayNames/DisplayNames.supportedLocalesOf.js
@@ -0,0 +1,43 @@
+describe("correct behavior", () => {
+ test("length is 1", () => {
+ expect(Intl.DisplayNames.supportedLocalesOf).toHaveLength(1);
+ });
+
+ test("basic functionality", () => {
+ // prettier-ignore
+ const values = [
+ [[], []],
+ [undefined, []],
+ ["en", ["en"]],
+ [new Intl.Locale("en"), ["en"]],
+ [["en"], ["en"]],
+ [["en", "en-gb", "en-us"], ["en", "en-GB", "en-US"]],
+ [["en", "de", "fr"], ["en", "de", "fr"]],
+ [["en-foobar"], ["en-foobar"]],
+ [["en-foobar-u-abc"], ["en-foobar-u-abc"]],
+ [["aa", "zz"], []],
+ [["en", "aa", "zz"], ["en"]],
+ ];
+ for (const [input, expected] of values) {
+ expect(Intl.DisplayNames.supportedLocalesOf(input)).toEqual(expected);
+ // "best fit" (implementation defined) just uses the same implementation as "lookup" at the moment
+ expect(
+ Intl.DisplayNames.supportedLocalesOf(input, { localeMatcher: "best fit" })
+ ).toEqual(Intl.DisplayNames.supportedLocalesOf(input, { localeMatcher: "lookup" }));
+ }
+ });
+});
+
+describe("errors", () => {
+ test("invalid value for localeMatcher option", () => {
+ expect(() => {
+ Intl.DisplayNames.supportedLocalesOf([], { localeMatcher: "foo" });
+ }).toThrowWithMessage(RangeError, "foo is not a valid value for option localeMatcher");
+ });
+
+ test("invalid language tag", () => {
+ expect(() => {
+ Intl.DisplayNames.supportedLocalesOf(["aaaaaaaaa"]);
+ }).toThrowWithMessage(RangeError, "aaaaaaaaa is not a structurally valid language tag");
+ });
+});