1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-14 09:34:59 +00:00

LibJS: Implement Intl.Locale.prototype.calendars property

This commit is contained in:
Timothy Flynn 2022-07-05 12:10:24 -04:00 committed by Linus Groh
parent 028a6b90b1
commit e9bc35d805
6 changed files with 90 additions and 4 deletions

View file

@ -83,6 +83,7 @@ namespace JS {
P(byteOffset) \
P(calendar) \
P(calendarName) \
P(calendars) \
P(call) \
P(callee) \
P(caller) \

View file

@ -1,9 +1,10 @@
/*
* Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2021-2022, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Intl/Locale.h>
#include <LibUnicode/Locale.h>
@ -50,4 +51,40 @@ Locale::Locale(Unicode::LocaleID const& locale_id, Object& prototype)
}
}
// 1.1.1 CreateArrayFromListOrRestricted ( list , restricted )
static Array* create_array_from_list_or_restricted(GlobalObject& global_object, Vector<StringView> list, Optional<String> restricted)
{
auto& vm = global_object.vm();
// 1. If restricted is not undefined, then
if (restricted.has_value()) {
// a. Set list to « restricted ».
list = { *restricted };
}
// 2. Return ! CreateArrayFromList( list ).
return Array::create_from<StringView>(global_object, list, [&vm](auto value) {
return js_string(vm, value);
});
}
// 1.1.2 CalendarsOfLocale ( loc ), https://tc39.es/proposal-intl-locale-info/#sec-calendars-of-locale
Array* calendars_of_locale(GlobalObject& global_object, Locale const& locale_object)
{
// 1. Let restricted be loc.[[Calendar]].
Optional<String> restricted = locale_object.has_calendar() ? locale_object.calendar() : Optional<String> {};
// 2. Let locale be loc.[[Locale]].
auto const& locale = locale_object.locale();
// 3. Assert: locale matches the unicode_locale_id production.
VERIFY(Unicode::parse_unicode_locale_id(locale).has_value());
// 4. Let list be a List of 1 or more unique canonical calendar identifiers, which must be lower case String values conforming to the type sequence from UTS 35 Unicode Locale Identifier, section 3.2, sorted in descending preference of those in common use for date and time formatting in locale.
auto list = Unicode::get_keywords_for_locale(locale, "ca"sv);
// 5. Return ! CreateArrayFromListOrRestricted( list, restricted ).
return create_array_from_list_or_restricted(global_object, move(list), move(restricted));
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2021-2022, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -10,6 +10,7 @@
#include <AK/Optional.h>
#include <AK/String.h>
#include <AK/Vector.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Value.h>
#include <LibUnicode/Forward.h>
@ -73,4 +74,6 @@ private:
bool m_numeric { false }; // [[Numeric]]
};
Array* calendars_of_locale(GlobalObject& global_object, Locale const& locale);
}

View file

@ -1,10 +1,11 @@
/*
* Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2021-2022, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Intl/Locale.h>
#include <LibJS/Runtime/Intl/LocalePrototype.h>
@ -34,6 +35,7 @@ void LocalePrototype::initialize(GlobalObject& global_object)
define_native_accessor(vm.names.baseName, base_name, {}, Attribute::Configurable);
define_native_accessor(vm.names.calendar, calendar, {}, Attribute::Configurable);
define_native_accessor(vm.names.calendars, calendars, {}, Attribute::Configurable);
define_native_accessor(vm.names.caseFirst, case_first, {}, Attribute::Configurable);
define_native_accessor(vm.names.collation, collation, {}, Attribute::Configurable);
define_native_accessor(vm.names.hourCycle, hour_cycle, {}, Attribute::Configurable);
@ -199,4 +201,17 @@ JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::region)
return js_string(vm, *locale->language_id.region);
}
#define JS_ENUMERATE_LOCALE_INFO_PROPERTIES \
__JS_ENUMERATE(calendars)
// 1.4.16 get Intl.Locale.prototype.calendars, https://tc39.es/proposal-intl-locale-info/#sec-Intl.Locale.prototype.calendars
#define __JS_ENUMERATE(keyword) \
JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::keyword) \
{ \
auto* locale_object = TRY(typed_this_object(global_object)); \
return keyword##_of_locale(global_object, *locale_object); \
}
JS_ENUMERATE_LOCALE_INFO_PROPERTIES
#undef __JS_ENUMERATE
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2021-2022, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -26,6 +26,7 @@ private:
JS_DECLARE_NATIVE_FUNCTION(base_name);
JS_DECLARE_NATIVE_FUNCTION(calendar);
JS_DECLARE_NATIVE_FUNCTION(calendars);
JS_DECLARE_NATIVE_FUNCTION(case_first);
JS_DECLARE_NATIVE_FUNCTION(collation);
JS_DECLARE_NATIVE_FUNCTION(hour_cycle);

View file

@ -0,0 +1,29 @@
describe("errors", () => {
test("called on non-Locale object", () => {
expect(() => {
Intl.Locale.prototype.calendars;
}).toThrowWithMessage(TypeError, "Not an object of type Intl.Locale");
});
});
describe("normal behavior", () => {
test("basic functionality", () => {
expect(Array.isArray(new Intl.Locale("en").calendars)).toBeTrue();
expect(new Intl.Locale("en").calendars).toEqual(["gregory"]);
expect(Array.isArray(new Intl.Locale("ar").calendars)).toBeTrue();
expect(new Intl.Locale("ar").calendars).toEqual(["gregory"]);
});
test("extension keyword overrides default data", () => {
expect(new Intl.Locale("en-u-ca-islamicc").calendars).toEqual(["islamic-civil"]);
expect(new Intl.Locale("en", { calendar: "dangi" }).calendars).toEqual(["dangi"]);
expect(new Intl.Locale("ar-u-ca-ethiopic-amete-alem").calendars).toEqual(["ethioaa"]);
expect(new Intl.Locale("ar", { calendar: "hebrew" }).calendars).toEqual(["hebrew"]);
// Invalid calendars also take precedence.
expect(new Intl.Locale("en-u-ca-ladybird").calendars).toEqual(["ladybird"]);
expect(new Intl.Locale("en", { calendar: "ladybird" }).calendars).toEqual(["ladybird"]);
});
});