1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 13:27:35 +00:00

LibJS: Handle Unicode locale extensions in LookupMatcher AO

This commit is contained in:
Timothy Flynn 2021-08-30 08:51:52 -04:00 committed by Linus Groh
parent def8b44c40
commit 6f5fb87d3b
2 changed files with 44 additions and 11 deletions

View file

@ -251,16 +251,27 @@ static Optional<String> best_available_locale(StringView const& locale)
} }
} }
struct MatcherResult {
String locale;
Vector<Unicode::Extension> extensions {};
};
// 9.2.3 LookupMatcher ( availableLocales, requestedLocales ), https://tc39.es/ecma402/#sec-lookupmatcher // 9.2.3 LookupMatcher ( availableLocales, requestedLocales ), https://tc39.es/ecma402/#sec-lookupmatcher
static LocaleResult lookup_matcher(Vector<String> const& requested_locales) static MatcherResult lookup_matcher(Vector<String> const& requested_locales)
{ {
// 1. Let result be a new Record. // 1. Let result be a new Record.
LocaleResult result {}; MatcherResult result {};
// 2. For each element locale of requestedLocales, do // 2. For each element locale of requestedLocales, do
for (auto const& locale : requested_locales) { for (auto const& locale : requested_locales) {
auto locale_id = Unicode::parse_unicode_locale_id(locale);
VERIFY(locale_id.has_value());
auto extensions = move(locale_id->extensions);
locale_id->private_use_extensions.clear();
// a. Let noExtensionsLocale be the String value that is locale with any Unicode locale extension sequences removed. // a. Let noExtensionsLocale be the String value that is locale with any Unicode locale extension sequences removed.
auto const& no_extensions_locale = locale; // FIXME: Handle extensions. auto no_extensions_locale = JS::Intl::canonicalize_unicode_locale_id(*locale_id);
// b. Let availableLocale be BestAvailableLocale(availableLocales, noExtensionsLocale). // b. Let availableLocale be BestAvailableLocale(availableLocales, noExtensionsLocale).
auto available_locale = best_available_locale(no_extensions_locale); auto available_locale = best_available_locale(no_extensions_locale);
@ -270,10 +281,12 @@ static LocaleResult lookup_matcher(Vector<String> const& requested_locales)
// i. Set result.[[locale]] to availableLocale. // i. Set result.[[locale]] to availableLocale.
result.locale = available_locale.release_value(); result.locale = available_locale.release_value();
// FIXME: Handle extensions.
// ii. If locale and noExtensionsLocale are not the same String value, then // ii. If locale and noExtensionsLocale are not the same String value, then
// 1. Let extension be the String value consisting of the substring of the Unicode locale extension sequence within locale. if (locale != no_extensions_locale) {
// 2. Set result.[[extension]] to extension. // 1. Let extension be the String value consisting of the substring of the Unicode locale extension sequence within locale.
// 2. Set result.[[extension]] to extension.
result.extensions = move(extensions);
}
// iii. Return result. // iii. Return result.
return result; return result;
@ -289,7 +302,7 @@ static LocaleResult lookup_matcher(Vector<String> const& requested_locales)
} }
// 9.2.4 BestFitMatcher ( availableLocales, requestedLocales ), https://tc39.es/ecma402/#sec-bestfitmatcher // 9.2.4 BestFitMatcher ( availableLocales, requestedLocales ), https://tc39.es/ecma402/#sec-bestfitmatcher
static LocaleResult best_fit_matcher(Vector<String> const& requested_locales) static MatcherResult best_fit_matcher(Vector<String> const& requested_locales)
{ {
// The algorithm is implementation dependent, but should produce results that a typical user of the requested locales would // The algorithm is implementation dependent, but should produce results that a typical user of the requested locales would
// perceive as at least as good as those produced by the LookupMatcher abstract operation. // perceive as at least as good as those produced by the LookupMatcher abstract operation.
@ -301,23 +314,30 @@ LocaleResult resolve_locale(Vector<String> const& requested_locales, LocaleOptio
{ {
// 1. Let matcher be options.[[localeMatcher]]. // 1. Let matcher be options.[[localeMatcher]].
auto const& matcher = options.locale_matcher; auto const& matcher = options.locale_matcher;
LocaleResult result; MatcherResult matcher_result;
// 2. If matcher is "lookup", then // 2. If matcher is "lookup", then
if (matcher.is_string() && (matcher.as_string().string() == "lookup"sv)) { if (matcher.is_string() && (matcher.as_string().string() == "lookup"sv)) {
// a. Let r be LookupMatcher(availableLocales, requestedLocales). // a. Let r be LookupMatcher(availableLocales, requestedLocales).
result = lookup_matcher(requested_locales); matcher_result = lookup_matcher(requested_locales);
} }
// 3. Else, // 3. Else,
else { else {
// a. Let r be BestFitMatcher(availableLocales, requestedLocales). // a. Let r be BestFitMatcher(availableLocales, requestedLocales).
result = best_fit_matcher(requested_locales); matcher_result = best_fit_matcher(requested_locales);
} }
// 4. Let foundLocale be r.[[locale]]. // 4. Let foundLocale be r.[[locale]].
auto found_locale = move(matcher_result.locale);
// 5. Let result be a new Record. // 5. Let result be a new Record.
LocaleResult result {};
// 6. Set result.[[dataLocale]] to foundLocale. // 6. Set result.[[dataLocale]] to foundLocale.
// FIXME: Handle extensions. // FIXME: Currently, the only caller to this method has an empty [[RelevantExtensionKeys]] internal slot,
// so this block isn't testable. When a caller has a non-empty slot, implement the below steps.
//
// 7. If r has an [[extension]] field, then // 7. If r has an [[extension]] field, then
// a. Let components be ! UnicodeExtensionComponents(r.[[extension]]). // a. Let components be ! UnicodeExtensionComponents(r.[[extension]]).
// b. Let keywords be components.[[Keywords]]. // b. Let keywords be components.[[Keywords]].
@ -357,7 +377,9 @@ LocaleResult resolve_locale(Vector<String> const& requested_locales, LocaleOptio
// k. Append supportedExtensionAddition to supportedExtension. // k. Append supportedExtensionAddition to supportedExtension.
// 10. If the number of elements in supportedExtension is greater than 2, then // 10. If the number of elements in supportedExtension is greater than 2, then
// a. Let foundLocale be InsertUnicodeExtensionAndCanonicalize(foundLocale, supportedExtension). // a. Let foundLocale be InsertUnicodeExtensionAndCanonicalize(foundLocale, supportedExtension).
// 11. Set result.[[locale]] to foundLocale. // 11. Set result.[[locale]] to foundLocale.
result.locale = move(found_locale);
// 12. Return result. // 12. Return result.
return result; return result;

View file

@ -28,4 +28,15 @@ describe("correct behavior", () => {
fallback: "code", fallback: "code",
}); });
}); });
test("locales with extensions", () => {
const en = new Intl.DisplayNames("en-t-en", { type: "language" });
expect(en.resolvedOptions().locale).toBe("en");
const es419 = new Intl.DisplayNames("es-419-u-1k-aaa", { type: "language" });
expect(es419.resolvedOptions().locale).toBe("es-419");
const zhHant = new Intl.DisplayNames(["zh-Hant-x-aaa"], { type: "language" });
expect(zhHant.resolvedOptions().locale).toBe("zh-Hant");
});
}); });