LibJS: Allow specifying keyword values not directly defined for a locale

For example, consider the locales "en-u-nu-fullwide" or "en-u-nu-arab".
The CLDR only declares the "latn" numbering system for the "en" locale,
thus ResolveLocale would change the locale to "en-u-nu-latn". This patch
allows using non-latn numbering systems digits.
This commit is contained in:
Timothy Flynn 2022-07-14 13:55:13 -04:00 committed by Andreas Kling
parent b24b9c0a65
commit aafcdc4c72
Notes: sideshowbarker 2024-07-17 08:55:16 +09:00
6 changed files with 42 additions and 12 deletions

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 * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -425,13 +425,15 @@ LocaleResult resolve_locale(Vector<String> const& requested_locales, LocaleOptio
// b. Assert: Type(foundLocaleData) is Record. // b. Assert: Type(foundLocaleData) is Record.
// c. Let keyLocaleData be foundLocaleData.[[<key>]]. // c. Let keyLocaleData be foundLocaleData.[[<key>]].
// d. Assert: Type(keyLocaleData) is List. // d. Assert: Type(keyLocaleData) is List.
auto key_locale_data = Unicode::get_keywords_for_locale(found_locale, key); auto key_locale_data = Unicode::get_available_keyword_values(key);
// e. Let value be keyLocaleData[0]. // e. Let value be keyLocaleData[0].
// f. Assert: Type(value) is either String or Null. // f. Assert: Type(value) is either String or Null.
// NOTE: ECMA-402 assumes keyLocaleData is sorted by locale preference. Our list is sorted
// alphabetically, so we get the locale's preferred value from LibUnicode.
Optional<String> value; Optional<String> value;
if (!key_locale_data.is_empty()) if (auto preference = Unicode::get_preferred_keyword_value_for_locale(found_locale, key); preference.has_value())
value = key_locale_data[0]; value = *preference;
// g. Let supportedExtensionAddition be "". // g. Let supportedExtensionAddition be "".
Optional<Unicode::Keyword> supported_extension_addition {}; Optional<Unicode::Keyword> supported_extension_addition {};

View file

@ -64,12 +64,12 @@ describe("correct behavior", () => {
}); });
test("numberingSystem option limited to known 'nu' values", () => { test("numberingSystem option limited to known 'nu' values", () => {
["latn", "arab"].forEach(numberingSystem => { ["latn", "foo"].forEach(numberingSystem => {
const en = Intl.DateTimeFormat("en", { numberingSystem: numberingSystem }); const en = Intl.DateTimeFormat("en", { numberingSystem: numberingSystem });
expect(en.resolvedOptions().numberingSystem).toBe("latn"); expect(en.resolvedOptions().numberingSystem).toBe("latn");
}); });
["latn", "arab"].forEach(numberingSystem => { ["latn", "foo"].forEach(numberingSystem => {
const en = Intl.DateTimeFormat(`en-u-nu-${numberingSystem}`); const en = Intl.DateTimeFormat(`en-u-nu-${numberingSystem}`);
expect(en.resolvedOptions().numberingSystem).toBe("latn"); expect(en.resolvedOptions().numberingSystem).toBe("latn");
}); });

View file

@ -33,12 +33,12 @@ describe("correct behavior", () => {
}); });
test("numberingSystem option limited to known 'nu' values", () => { test("numberingSystem option limited to known 'nu' values", () => {
["latn", "arab"].forEach(numberingSystem => { ["latn", "foo"].forEach(numberingSystem => {
const en = new Intl.DurationFormat("en", { numberingSystem: numberingSystem }); const en = new Intl.DurationFormat("en", { numberingSystem: numberingSystem });
expect(en.resolvedOptions().numberingSystem).toBe("latn"); expect(en.resolvedOptions().numberingSystem).toBe("latn");
}); });
["latn", "arab"].forEach(numberingSystem => { ["latn", "foo"].forEach(numberingSystem => {
const en = new Intl.DurationFormat(`en-u-nu-${numberingSystem}`); const en = new Intl.DurationFormat(`en-u-nu-${numberingSystem}`);
expect(en.resolvedOptions().numberingSystem).toBe("latn"); expect(en.resolvedOptions().numberingSystem).toBe("latn");
}); });

View file

@ -207,6 +207,21 @@ describe("style=decimal", () => {
expect(en.format(12000000)).toBe("12 million"); expect(en.format(12000000)).toBe("12 million");
expect(en.format(12900000)).toBe("13 million"); expect(en.format(12900000)).toBe("13 million");
const enFullwide = new Intl.NumberFormat("en", {
notation: "compact",
compactDisplay: "long",
numberingSystem: "fullwide",
});
expect(enFullwide.format(1)).toBe("");
expect(enFullwide.format(1200)).toBe(". thousand");
expect(enFullwide.format(1290)).toBe(". thousand");
expect(enFullwide.format(12000)).toBe(" thousand");
expect(enFullwide.format(12900)).toBe(" thousand");
expect(enFullwide.format(1200000)).toBe(". million");
expect(enFullwide.format(1290000)).toBe(". million");
expect(enFullwide.format(12000000)).toBe(" million");
expect(enFullwide.format(12900000)).toBe(" million");
const ar = new Intl.NumberFormat("ar", { notation: "compact", compactDisplay: "long" }); const ar = new Intl.NumberFormat("ar", { notation: "compact", compactDisplay: "long" });
expect(ar.format(1)).toBe("\u0661"); expect(ar.format(1)).toBe("\u0661");
expect(ar.format(1200)).toBe("\u0661\u066b\u0662 ألف"); expect(ar.format(1200)).toBe("\u0661\u066b\u0662 ألف");
@ -579,6 +594,19 @@ describe("style=percent", () => {
expect(en.format(0.123)).toBe("12%"); expect(en.format(0.123)).toBe("12%");
expect(en.format(0.1234)).toBe("12%"); expect(en.format(0.1234)).toBe("12%");
const enFullwide = new Intl.NumberFormat("en", {
style: "percent",
notation: "compact",
numberingSystem: "fullwide",
});
expect(enFullwide.format(0.01)).toBe("%");
expect(enFullwide.format(0.012)).toBe(".%");
expect(enFullwide.format(0.0123)).toBe(".%");
expect(enFullwide.format(0.0129)).toBe(".%");
expect(enFullwide.format(0.12)).toBe("%");
expect(enFullwide.format(0.123)).toBe("%");
expect(enFullwide.format(0.1234)).toBe("%");
const ar = new Intl.NumberFormat("ar", { style: "percent", notation: "compact" }); const ar = new Intl.NumberFormat("ar", { style: "percent", notation: "compact" });
expect(ar.format(0.01)).toBe("\u0661\u066a\u061c"); expect(ar.format(0.01)).toBe("\u0661\u066a\u061c");
expect(ar.format(0.012)).toBe("\u0661\u066b\u0662\u066a\u061c"); expect(ar.format(0.012)).toBe("\u0661\u066b\u0662\u066a\u061c");

View file

@ -36,12 +36,12 @@ describe("correct behavior", () => {
}); });
test("numberingSystem option limited to known 'nu' values", () => { test("numberingSystem option limited to known 'nu' values", () => {
["latn", "arab"].forEach(numberingSystem => { ["latn", "foo"].forEach(numberingSystem => {
const en = Intl.NumberFormat("en", { numberingSystem: numberingSystem }); const en = Intl.NumberFormat("en", { numberingSystem: numberingSystem });
expect(en.resolvedOptions().numberingSystem).toBe("latn"); expect(en.resolvedOptions().numberingSystem).toBe("latn");
}); });
["latn", "arab"].forEach(numberingSystem => { ["latn", "foo"].forEach(numberingSystem => {
const en = Intl.NumberFormat(`en-u-nu-${numberingSystem}`); const en = Intl.NumberFormat(`en-u-nu-${numberingSystem}`);
expect(en.resolvedOptions().numberingSystem).toBe("latn"); expect(en.resolvedOptions().numberingSystem).toBe("latn");
}); });

View file

@ -36,12 +36,12 @@ describe("correct behavior", () => {
}); });
test("numberingSystem option limited to known 'nu' values", () => { test("numberingSystem option limited to known 'nu' values", () => {
["latn", "arab"].forEach(numberingSystem => { ["latn", "foo"].forEach(numberingSystem => {
const en = new Intl.RelativeTimeFormat("en", { numberingSystem: numberingSystem }); const en = new Intl.RelativeTimeFormat("en", { numberingSystem: numberingSystem });
expect(en.resolvedOptions().numberingSystem).toBe("latn"); expect(en.resolvedOptions().numberingSystem).toBe("latn");
}); });
["latn", "arab"].forEach(numberingSystem => { ["latn", "foo"].forEach(numberingSystem => {
const en = new Intl.RelativeTimeFormat(`en-u-nu-${numberingSystem}`); const en = new Intl.RelativeTimeFormat(`en-u-nu-${numberingSystem}`);
expect(en.resolvedOptions().numberingSystem).toBe("latn"); expect(en.resolvedOptions().numberingSystem).toBe("latn");
}); });