From 88a560dd843d6e9a050fa9322ca1d2c33f7a6628 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Tue, 5 Jul 2022 19:50:24 -0400 Subject: [PATCH] LibJS: Implement Intl.Locale.prototype.textInfo property --- .../LibJS/Runtime/CommonPropertyNames.h | 2 ++ .../Libraries/LibJS/Runtime/Intl/Locale.cpp | 19 ++++++++++++++ .../Libraries/LibJS/Runtime/Intl/Locale.h | 1 + .../LibJS/Runtime/Intl/LocalePrototype.cpp | 21 +++++++++++++++ .../LibJS/Runtime/Intl/LocalePrototype.h | 1 + .../Intl/Locale/Locale.prototype.textInfo.js | 26 +++++++++++++++++++ 6 files changed, 70 insertions(+) create mode 100644 Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.textInfo.js diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index 6375ca2b43..01c6d2f560 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -139,6 +139,7 @@ namespace JS { P(deleteProperty) \ P(deref) \ P(description) \ + P(direction) \ P(disambiguation) \ P(done) \ P(dotAll) \ @@ -469,6 +470,7 @@ namespace JS { P(tan) \ P(tanh) \ P(test) \ + P(textInfo) \ P(then) \ P(time) \ P(timeEnd) \ diff --git a/Userland/Libraries/LibJS/Runtime/Intl/Locale.cpp b/Userland/Libraries/LibJS/Runtime/Intl/Locale.cpp index 42ce61f154..c4429f2bce 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/Locale.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/Locale.cpp @@ -167,4 +167,23 @@ Array* time_zones_of_locale(GlobalObject& global_object, StringView region) }); } +// 1.1.7 CharacterDirectionOfLocale ( loc ), https://tc39.es/proposal-intl-locale-info/#sec-character-direction-of-locale +StringView character_direction_of_locale(Locale const& locale_object) +{ + // 1. Let locale be loc.[[Locale]]. + auto const& locale = locale_object.locale(); + + // 2. Assert: locale matches the unicode_locale_id production. + VERIFY(Unicode::parse_unicode_locale_id(locale).has_value()); + + // 3. If the default general ordering of characters (characterOrder) within a line in locale is right-to-left, return "rtl". + // NOTE: LibUnicode handles both LTR and RTL character orders in this call, not just RTL. We then fallback to LTR + // below if LibUnicode doesn't conclusively know the character order for this locale. + if (auto character_order = Unicode::character_order_for_locale(locale); character_order.has_value()) + return Unicode::character_order_to_string(*character_order); + + // 4. Return "ltr". + return "ltr"sv; +} + } diff --git a/Userland/Libraries/LibJS/Runtime/Intl/Locale.h b/Userland/Libraries/LibJS/Runtime/Intl/Locale.h index 88f148fa0f..33c6b80321 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/Locale.h +++ b/Userland/Libraries/LibJS/Runtime/Intl/Locale.h @@ -79,5 +79,6 @@ Array* collations_of_locale(GlobalObject& global_object, Locale const& locale); Array* hour_cycles_of_locale(GlobalObject& global_object, Locale const& locale); Array* numbering_systems_of_locale(GlobalObject& global_object, Locale const& locale); Array* time_zones_of_locale(GlobalObject& global_object, StringView region); +StringView character_direction_of_locale(Locale const& locale); } diff --git a/Userland/Libraries/LibJS/Runtime/Intl/LocalePrototype.cpp b/Userland/Libraries/LibJS/Runtime/Intl/LocalePrototype.cpp index 6631e761e4..02a5c25efa 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/LocalePrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/LocalePrototype.cpp @@ -48,6 +48,7 @@ void LocalePrototype::initialize(GlobalObject& global_object) define_native_accessor(vm.names.script, script, {}, Attribute::Configurable); define_native_accessor(vm.names.region, region, {}, Attribute::Configurable); define_native_accessor(vm.names.timeZones, time_zones, {}, Attribute::Configurable); + define_native_accessor(vm.names.textInfo, text_info, {}, Attribute::Configurable); } // 14.3.3 Intl.Locale.prototype.maximize ( ), https://tc39.es/ecma402/#sec-Intl.Locale.prototype.maximize @@ -242,4 +243,24 @@ JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::time_zones) return time_zones_of_locale(global_object, locale->language_id.region.value()); } +// 1.4.21 get Intl.Locale.prototype.textInfo, https://tc39.es/proposal-intl-locale-info/#sec-Intl.Locale.prototype.textInfo +JS_DEFINE_NATIVE_FUNCTION(LocalePrototype::text_info) +{ + // 1. Let loc be the this value. + // 2. Perform ? RequireInternalSlot(loc, [[InitializedLocale]]). + auto* locale_object = TRY(typed_this_object(global_object)); + + // 3. Let info be ! ObjectCreate(%Object.prototype%). + auto* info = Object::create(global_object, global_object.object_prototype()); + + // 4. Let dir be ! CharacterDirectionOfLocale(loc). + auto direction = character_direction_of_locale(*locale_object); + + // 5. Perform ! CreateDataPropertyOrThrow(info, "direction", dir). + MUST(info->create_data_property_or_throw(vm.names.direction, js_string(vm, direction))); + + // 6. Return info. + return info; +} + } diff --git a/Userland/Libraries/LibJS/Runtime/Intl/LocalePrototype.h b/Userland/Libraries/LibJS/Runtime/Intl/LocalePrototype.h index 20256b760d..781d22f42f 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/LocalePrototype.h +++ b/Userland/Libraries/LibJS/Runtime/Intl/LocalePrototype.h @@ -39,6 +39,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(script); JS_DECLARE_NATIVE_FUNCTION(region); JS_DECLARE_NATIVE_FUNCTION(time_zones); + JS_DECLARE_NATIVE_FUNCTION(text_info); }; } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.textInfo.js b/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.textInfo.js new file mode 100644 index 0000000000..9cacb623e7 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/Intl/Locale/Locale.prototype.textInfo.js @@ -0,0 +1,26 @@ +describe("errors", () => { + test("called on non-Locale object", () => { + expect(() => { + Intl.Locale.prototype.textInfo; + }).toThrowWithMessage(TypeError, "Not an object of type Intl.Locale"); + }); +}); + +describe("normal behavior", () => { + test("basic functionality", () => { + const textInfo = new Intl.Locale("en").textInfo; + + expect(textInfo).toBeDefined(); + expect(Object.getPrototypeOf(textInfo)).toBe(Object.prototype); + + expect(textInfo.direction).toBeDefined(); + expect(Object.getPrototypeOf(textInfo.direction)).toBe(String.prototype); + + expect(textInfo.direction).toBe("ltr"); + expect(new Intl.Locale("ar").textInfo.direction).toBe("rtl"); + }); + + test("fallback to ltr", () => { + expect(new Intl.Locale("xx").textInfo.direction).toBe("ltr"); + }); +});