From c7dbe2778197dae6c75332df0e48899032824cfa Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Thu, 13 Jan 2022 14:05:40 -0500 Subject: [PATCH] LibJS: Handle the [[LanguageDisplay]] tag when localizing languages --- .../Runtime/Intl/DisplayNamesPrototype.cpp | 14 ++++- .../DisplayNames/DisplayNames.prototype.of.js | 61 ++++++++++++++++--- 2 files changed, 63 insertions(+), 12 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.cpp b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.cpp index 2778b7c3f8..f10fcc8771 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/DisplayNamesPrototype.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -51,11 +52,18 @@ JS_DEFINE_NATIVE_FUNCTION(DisplayNamesPrototype::of) // 5. Let fields be displayNames.[[Fields]]. // 6. If fields has a field [[]], return fields.[[]]. Optional result; + Optional formatted_result; switch (display_names->type()) { case DisplayNames::Type::Language: - // FIXME: Handle the [[LanguageDisplay]] internal slot once we know where that data comes from. - result = Unicode::get_locale_language_mapping(display_names->locale(), code.as_string().string()); + if (display_names->language_display() == DisplayNames::LanguageDisplay::Dialect) { + result = Unicode::get_locale_language_mapping(display_names->locale(), code.as_string().string()); + if (result.has_value()) + break; + } + + if (auto locale = is_structurally_valid_language_tag(code.as_string().string()); locale.has_value()) + formatted_result = Unicode::format_locale_for_display(display_names->locale(), locale.release_value()); break; case DisplayNames::Type::Region: result = Unicode::get_locale_territory_mapping(display_names->locale(), code.as_string().string()); @@ -102,6 +110,8 @@ JS_DEFINE_NATIVE_FUNCTION(DisplayNamesPrototype::of) if (result.has_value()) return js_string(vm, result.release_value()); + if (formatted_result.has_value()) + return js_string(vm, formatted_result.release_value()); // 7. If displayNames.[[Fallback]] is "code", return code. if (display_names->fallback() == DisplayNames::Fallback::Code) diff --git a/Userland/Libraries/LibJS/Tests/builtins/Intl/DisplayNames/DisplayNames.prototype.of.js b/Userland/Libraries/LibJS/Tests/builtins/Intl/DisplayNames/DisplayNames.prototype.of.js index a6a1f9726e..4b90da3e1e 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Intl/DisplayNames/DisplayNames.prototype.of.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Intl/DisplayNames/DisplayNames.prototype.of.js @@ -41,19 +41,60 @@ describe("correct behavior", () => { expect(Intl.DisplayNames.prototype.of).toHaveLength(1); }); - test("option type language", () => { - const en = new Intl.DisplayNames("en", { type: "language" }); - expect(en.of("en")).toBe("English"); + test("option type language, display dialect", () => { + // prettier-ignore + const data = [ + { locale: "en", en: "English", es419: "inglés", zhHant: "英文" }, + { locale: "en-US", en: "American English", es419: "inglés estadounidense", zhHant: "英文(美國)" }, + { locale: "en-GB", en: "British English", es419: "inglés británico", zhHant: "英文(英國)" }, + { locale: "sr", en: "Serbian", es419: "serbio", zhHant: "塞爾維亞文" }, + { locale: "sr-Cyrl", en: "Serbian (Cyrillic)", es419: "serbio (cirílico)", zhHant: "塞爾維亞文(斯拉夫文)" }, + { locale: "sr-Cyrl-BA", en: "Serbian (Cyrillic, Bosnia & Herzegovina)", es419: "serbio (cirílico, Bosnia-Herzegovina)", zhHant: "塞爾維亞文(斯拉夫文,波士尼亞與赫塞哥維納)" }, + ]; - const es419 = new Intl.DisplayNames("es-419", { type: "language" }); - expect(es419.of("en")).toBe("inglés"); + const en = new Intl.DisplayNames("en", { type: "language", languageDisplay: "dialect" }); + const es419 = new Intl.DisplayNames("es-419", { + type: "language", + languageDisplay: "dialect", + }); + const zhHant = new Intl.DisplayNames("zh-Hant", { + type: "language", + languageDisplay: "dialect", + }); - const zhHant = new Intl.DisplayNames(["zh-Hant"], { type: "language" }); - expect(zhHant.of("en")).toBe("英文"); + data.forEach(d => { + expect(en.of(d.locale)).toBe(d.en); + expect(es419.of(d.locale)).toBe(d.es419); + expect(zhHant.of(d.locale)).toBe(d.zhHant); + }); + }); - expect(en.of("zz")).toBe("zz"); - expect(es419.of("zz")).toBe("zz"); - expect(zhHant.of("zz")).toBe("zz"); + test("option type language, display standard", () => { + // prettier-ignore + const data = [ + { locale: "en", en: "English", es419: "inglés", zhHant: "英文" }, + { locale: "en-US", en: "English (United States)", es419: "inglés (Estados Unidos)", zhHant: "英文(美國)" }, + { locale: "en-GB", en: "English (United Kingdom)", es419: "inglés (Reino Unido)", zhHant: "英文(英國)" }, + { locale: "sr", en: "Serbian", es419: "serbio", zhHant: "塞爾維亞文" }, + { locale: "sr-Cyrl", en: "Serbian (Cyrillic)", es419: "serbio (cirílico)", zhHant: "塞爾維亞文(斯拉夫文)" }, + { locale: "sr-Cyrl-BA", en: "Serbian (Cyrillic, Bosnia & Herzegovina)", es419: "serbio (cirílico, Bosnia-Herzegovina)", zhHant: "塞爾維亞文(斯拉夫文,波士尼亞與赫塞哥維納)" }, + ]; + + const en = new Intl.DisplayNames("en", { type: "language", languageDisplay: "standard" }); + const es419 = new Intl.DisplayNames("es-419", { + type: "language", + languageDisplay: "standard", + }); + const zhHant = new Intl.DisplayNames("zh-Hant", { + type: "language", + languageDisplay: "standard", + }); + + data.forEach(d => { + expect(en.of(d.locale)).toBe(d.en); + expect(es419.of(d.locale)).toBe(d.es419); + expect(zhHant.of(d.locale)).toBe(d.zhHant); + }); }); test("option type region", () => {