mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 02:47:34 +00:00
LibJS: Implement ECMA-402 String.prototype.toLocale{Lower,Upper}Case
This commit is contained in:
parent
14086c69e7
commit
207319ecf1
5 changed files with 91 additions and 7 deletions
|
@ -233,7 +233,7 @@ Vector<String> canonicalize_locale_list(GlobalObject& global_object, Value local
|
|||
}
|
||||
|
||||
// 9.2.2 BestAvailableLocale ( availableLocales, locale ), https://tc39.es/ecma402/#sec-bestavailablelocale
|
||||
static Optional<String> best_available_locale(StringView const& locale)
|
||||
Optional<String> best_available_locale(StringView const& locale)
|
||||
{
|
||||
// 1. Let candidate be locale.
|
||||
StringView candidate = locale;
|
||||
|
|
|
@ -29,6 +29,7 @@ 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);
|
||||
Optional<String> best_available_locale(StringView const& locale);
|
||||
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);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <LibJS/Runtime/Array.h>
|
||||
#include <LibJS/Runtime/Error.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Intl/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/PrimitiveString.h>
|
||||
#include <LibJS/Runtime/RegExpObject.h>
|
||||
#include <LibJS/Runtime/StringIterator.h>
|
||||
|
@ -22,6 +23,7 @@
|
|||
#include <LibJS/Runtime/Utf16String.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
#include <LibUnicode/CharacterTypes.h>
|
||||
#include <LibUnicode/Locale.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace JS {
|
||||
|
@ -372,27 +374,72 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::index_of)
|
|||
return index.has_value() ? Value(*index) : Value(-1);
|
||||
}
|
||||
|
||||
// 22.1.3.24 String.prototype.toLocaleLowerCase ( [ reserved1 [ , reserved2 ] ] ), https://tc39.es/ecma262/#sec-string.prototype.tolocalelowercase
|
||||
// NOTE: This is the minimum toLocaleLowerCase implementation for engines without ECMA-402.
|
||||
static Optional<String> resolve_best_locale(GlobalObject& global_object, Value locales)
|
||||
{
|
||||
// For details on these steps, see https://tc39.es/ecma402/#sup-string.prototype.tolocalelowercase
|
||||
auto& vm = global_object.vm();
|
||||
|
||||
// 3. Let requestedLocales be ? CanonicalizeLocaleList(locales).
|
||||
auto requested_locales = Intl::canonicalize_locale_list(global_object, locales);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
Optional<Unicode::LocaleID> requested_locale;
|
||||
|
||||
// 4. If requestedLocales is not an empty List, then
|
||||
if (!requested_locales.is_empty()) {
|
||||
// a. Let requestedLocale be requestedLocales[0].
|
||||
requested_locale = Unicode::parse_unicode_locale_id(requested_locales[0]);
|
||||
}
|
||||
// 5. Else,
|
||||
else {
|
||||
// a. Let requestedLocale be DefaultLocale().
|
||||
requested_locale = Unicode::parse_unicode_locale_id(Unicode::default_locale());
|
||||
}
|
||||
VERIFY(requested_locale.has_value());
|
||||
|
||||
// 6. Let noExtensionsLocale be the String value that is requestedLocale with any Unicode locale extension sequences (6.2.1) removed.
|
||||
requested_locale->remove_extension_type<Unicode::LocaleExtension>();
|
||||
auto no_extensions_locale = requested_locale->to_string();
|
||||
|
||||
// 7. Let availableLocales be a List with language tags that includes the languages for which the Unicode Character Database contains language sensitive case mappings. Implementations may add additional language tags if they support case mapping for additional locales.
|
||||
// 8. Let locale be BestAvailableLocale(availableLocales, noExtensionsLocale).
|
||||
auto locale = Intl::best_available_locale(no_extensions_locale);
|
||||
|
||||
// 9. If locale is undefined, let locale be "und".
|
||||
if (!locale.has_value())
|
||||
locale = "und"sv;
|
||||
|
||||
return locale;
|
||||
}
|
||||
|
||||
// 18.1.2 String.prototype.toLocaleLowerCase ( [ locales ] ), https://tc39.es/ecma402/#sup-string.prototype.tolocalelowercase
|
||||
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_locale_lowercase)
|
||||
{
|
||||
auto string = ak_string_from(vm, global_object);
|
||||
if (!string.has_value())
|
||||
return {};
|
||||
|
||||
auto lowercase = Unicode::to_unicode_lowercase_full(*string);
|
||||
auto locale = resolve_best_locale(global_object, vm.argument(0));
|
||||
if (!locale.has_value())
|
||||
return {};
|
||||
|
||||
auto lowercase = Unicode::to_unicode_lowercase_full(*string, *locale);
|
||||
return js_string(vm, move(lowercase));
|
||||
}
|
||||
|
||||
// 22.1.3.25 String.prototype.toLocaleUpperCase ( [ reserved1 [ , reserved2 ] ] ), https://tc39.es/ecma262/#sec-string.prototype.tolocaleuppercase
|
||||
// NOTE: This is the minimum toLocaleUpperCase implementation for engines without ECMA-402.
|
||||
// 18.1.3 String.prototype.toLocaleUpperCase ( [ locales ] ), https://tc39.es/ecma402/#sup-string.prototype.tolocaleuppercase
|
||||
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_locale_uppercase)
|
||||
{
|
||||
auto string = ak_string_from(vm, global_object);
|
||||
if (!string.has_value())
|
||||
return {};
|
||||
|
||||
auto uppercase = Unicode::to_unicode_uppercase_full(*string);
|
||||
auto locale = resolve_best_locale(global_object, vm.argument(0));
|
||||
if (!locale.has_value())
|
||||
return {};
|
||||
|
||||
auto uppercase = Unicode::to_unicode_uppercase_full(*string, *locale);
|
||||
return js_string(vm, move(uppercase));
|
||||
}
|
||||
|
||||
|
|
|
@ -25,4 +25,25 @@ test("special case folding", () => {
|
|||
expect("\u1FB7".toLocaleLowerCase()).toBe("\u1FB7");
|
||||
expect("\u1FC7".toLocaleLowerCase()).toBe("\u1FC7");
|
||||
expect("\u1FF7".toLocaleLowerCase()).toBe("\u1FF7");
|
||||
|
||||
expect("I".toLocaleLowerCase()).toBe("i");
|
||||
expect("I".toLocaleLowerCase("az")).toBe("\u0131");
|
||||
expect("I".toLocaleLowerCase("tr")).toBe("\u0131");
|
||||
|
||||
expect("\u0130".toLocaleLowerCase()).toBe("\u0069\u0307");
|
||||
expect("\u0130".toLocaleLowerCase("az")).toBe("i");
|
||||
expect("\u0130".toLocaleLowerCase("tr")).toBe("i");
|
||||
|
||||
expect("I\u0307".toLocaleLowerCase()).toBe("i\u0307");
|
||||
expect("I\u0307".toLocaleLowerCase("az")).toBe("i");
|
||||
expect("I\u0307".toLocaleLowerCase("tr")).toBe("i");
|
||||
|
||||
expect("\u012e".toLocaleLowerCase()).toBe("\u012f");
|
||||
expect("\u012e".toLocaleLowerCase("lt")).toBe("\u012f");
|
||||
|
||||
expect("\u012e\u0300".toLocaleLowerCase()).toBe("\u012f\u0300");
|
||||
expect("\u012e\u0300".toLocaleLowerCase("lt")).toBe("\u012f\u0307\u0300");
|
||||
|
||||
expect("\u012e\u0300".toLocaleLowerCase(["en", "lt"])).toBe("\u012f\u0300");
|
||||
expect("\u012e\u0300".toLocaleLowerCase(["lt", "en"])).toBe("\u012f\u0307\u0300");
|
||||
});
|
||||
|
|
|
@ -27,4 +27,19 @@ test("special case folding", () => {
|
|||
expect("\u1FB7".toLocaleUpperCase()).toBe("\u0391\u0342\u0399");
|
||||
expect("\u1FC7".toLocaleUpperCase()).toBe("\u0397\u0342\u0399");
|
||||
expect("\u1FF7".toLocaleUpperCase()).toBe("\u03A9\u0342\u0399");
|
||||
|
||||
expect("i".toLocaleUpperCase()).toBe("I");
|
||||
expect("i".toLocaleUpperCase("lt")).toBe("I");
|
||||
|
||||
expect("i\u0307".toLocaleUpperCase()).toBe("I\u0307");
|
||||
expect("i\u0307".toLocaleUpperCase("lt")).toBe("I");
|
||||
|
||||
expect("j".toLocaleUpperCase()).toBe("J");
|
||||
expect("j".toLocaleUpperCase("lt")).toBe("J");
|
||||
|
||||
expect("j\u0307".toLocaleUpperCase()).toBe("J\u0307");
|
||||
expect("j\u0307".toLocaleUpperCase("lt")).toBe("J");
|
||||
|
||||
expect("j\u0307".toLocaleUpperCase(["en", "lt"])).toBe("J\u0307");
|
||||
expect("j\u0307".toLocaleUpperCase(["lt", "en"])).toBe("J");
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue