tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit cb2cd703778be16cfb583c081d5a00c2377db0b2
parent 12bf2b1c5311094236999130012b1d1b6e3846f9
Author: Eemeli Aro <eemeli@mozilla.com>
Date:   Thu, 30 Oct 2025 10:57:45 +0000

Bug 1760013 - Add intl/locale/rust/locale_service_glue/. r=hsivonen,bolsson

This uses icu_locale for the added functions, but does not migrate existing unic_langid usage to it.

The initial data matches the current localized values.

I'll be filing a follow-up PR in https://github.com/mozilla-l10n/documentation to add a section to the "Adding a new locale to Beta and Release builds" page (https://mozilla-l10n.github.io/documentation/products/firefox_desktop/adding_release.html) to give some additional visibility for needing to review these behaviours when adding a new locale.

Differential Revision: https://phabricator.services.mozilla.com/D266753

Diffstat:
MCargo.lock | 9+++++++++
Mintl/locale/LocaleService.cpp | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mintl/locale/LocaleService.h | 10++++++++++
Mintl/locale/moz.build | 5+++++
Mintl/locale/mozILocaleService.idl | 29+++++++++++++++++++++++++++++
Aintl/locale/rust/locale_service_glue/Cargo.toml | 13+++++++++++++
Aintl/locale/rust/locale_service_glue/cbindgen.toml | 18++++++++++++++++++
Aintl/locale/rust/locale_service_glue/src/lib.rs | 284+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mintl/locale/tests/gtest/TestLocaleService.cpp | 18++++++++++++++++++
Mintl/locale/tests/unit/test_localeService.js | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtoolkit/library/rust/shared/Cargo.toml | 1+
Mtoolkit/library/rust/shared/lib.rs | 2++
Mtools/@types/generated/lib.gecko.xpcom.d.ts | 5+++++
Mtools/lint/clippy.yml | 1+
14 files changed, 569 insertions(+), 0 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -2612,6 +2612,7 @@ dependencies = [ "l10nregistry", "l10nregistry-ffi", "libz-rs-sys", + "locale_service_glue", "localization-ffi", "log", "mapped_hyph", @@ -3885,6 +3886,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] +name = "locale_service_glue" +version = "0.1.0" +dependencies = [ + "icu_locale", + "nsstring", +] + +[[package]] name = "localization-ffi" version = "0.1.0" dependencies = [ diff --git a/intl/locale/LocaleService.cpp b/intl/locale/LocaleService.cpp @@ -14,6 +14,7 @@ #include "mozilla/intl/AppDateTimeFormat.h" #include "mozilla/intl/Locale.h" #include "mozilla/intl/OSPreferences.h" +#include "mozilla/intl/locale_service_glue_generated.h" #include "nsContentUtils.h" #include "nsDirectoryService.h" #include "nsDirectoryServiceDefs.h" @@ -31,6 +32,9 @@ #define INTL_SYSTEM_LOCALES_CHANGED "intl:system-locales-changed" +#define ACCEPT_LANGUAGES_PREF "intl.accept_languages" +#define FONT_LANGUAGE_GROUP_PREF "font.language.group" + #define PSEUDO_LOCALE_PREF "intl.l10n.pseudo" #define REQUESTED_LOCALES_PREF "intl.locale.requested" #define WEB_EXPOSED_LOCALES_PREF "intl.locale.privacy.web_exposed" @@ -694,3 +698,78 @@ LocaleService::GetPackagedLocales(nsTArray<nsCString>& aRetVal) { aRetVal = mPackagedLocales.Clone(); return NS_OK; } + +NS_IMETHODIMP +LocaleService::GetEllipsis(nsAString& aRetVal) { + if (mAppLocales.IsEmpty()) { + NegotiateAppLocales(mAppLocales); + } + ffi::locale_service_ellipsis(&mAppLocales[0], &aRetVal); + return NS_OK; +} + +bool LocaleService::AlwaysAppendAccesskeys() { + if (mAppLocales.IsEmpty()) { + NegotiateAppLocales(mAppLocales); + } + return ffi::locale_service_always_append_accesskeys(&mAppLocales[0]); +} + +bool LocaleService::InsertSeparatorBeforeAccesskeys() { + if (mAppLocales.IsEmpty()) { + NegotiateAppLocales(mAppLocales); + } + return ffi::locale_service_insert_separator_before_accesskeys( + &mAppLocales[0]); +} + +NS_IMETHODIMP +LocaleService::GetAlwaysAppendAccesskeys(bool* aRetVal) { + (*aRetVal) = AlwaysAppendAccesskeys(); + return NS_OK; +} + +NS_IMETHODIMP +LocaleService::GetInsertSeparatorBeforeAccesskeys(bool* aRetVal) { + (*aRetVal) = InsertSeparatorBeforeAccesskeys(); + return NS_OK; +} + +NS_IMETHODIMP +LocaleService::GetAcceptLanguages(nsACString& aRetVal) { + // if there is a user (or locked) value, use it + if (Preferences::HasUserValue(ACCEPT_LANGUAGES_PREF) || + Preferences::IsLocked(ACCEPT_LANGUAGES_PREF)) { + nsresult rv = Preferences::GetCString(ACCEPT_LANGUAGES_PREF, aRetVal); + if (NS_SUCCEEDED(rv)) { + return NS_OK; + } + } + + // if we need to fetch the default value, do that instead + if (mAppLocales.IsEmpty()) { + NegotiateAppLocales(mAppLocales); + } + ffi::locale_service_default_accept_languages(&mAppLocales[0], &aRetVal); + return NS_OK; +} + +NS_IMETHODIMP +LocaleService::GetFontLanguageGroup(nsACString& aRetVal) { + // if there is a user (or locked) value, use it + if (Preferences::HasUserValue(FONT_LANGUAGE_GROUP_PREF) || + Preferences::IsLocked(FONT_LANGUAGE_GROUP_PREF)) { + nsresult rv = Preferences::GetCString(FONT_LANGUAGE_GROUP_PREF, aRetVal); + if (NS_SUCCEEDED(rv)) { + return NS_OK; + } + } + + // if we need to fetch the default value, do that instead + if (mAppLocales.IsEmpty()) { + NegotiateAppLocales(mAppLocales); + } + ffi::locale_service_default_font_language_group(&mAppLocales[0], &aRetVal); + + return NS_OK; +} diff --git a/intl/locale/LocaleService.h b/intl/locale/LocaleService.h @@ -168,6 +168,16 @@ class LocaleService final : public mozILocaleService, */ bool IsAppLocaleRTL(); + /** + * If true, accesskeys should always be appended for the current app locale. + */ + bool AlwaysAppendAccesskeys(); + + /** + * If true, accesskeys should always be separated from the label. + */ + bool InsertSeparatorBeforeAccesskeys(); + static bool LanguagesMatch(const nsACString& aRequested, const nsACString& aAvailable); diff --git a/intl/locale/moz.build b/intl/locale/moz.build @@ -78,6 +78,10 @@ if CONFIG["COMPILE_ENVIRONMENT"]: inputs=["/intl/locale/rust/fluent-langneg-ffi"], ) CbindgenHeader( + "locale_service_glue_generated.h", + inputs=["/intl/locale/rust/locale_service_glue"], + ) + CbindgenHeader( "oxilangtag_ffi_generated.h", inputs=["/intl/locale/rust/oxilangtag-ffi"] ) CbindgenHeader( @@ -86,6 +90,7 @@ if CONFIG["COMPILE_ENVIRONMENT"]: EXPORTS.mozilla.intl += [ "!fluent_langneg_ffi_generated.h", + "!locale_service_glue_generated.h", "!oxilangtag_ffi_generated.h", "!unic_langid_ffi_generated.h", ] diff --git a/intl/locale/mozILocaleService.idl b/intl/locale/mozILocaleService.idl @@ -174,4 +174,33 @@ interface mozILocaleService : nsISupports * Example: ["en-US", "de", "pl", "sr-Cyrl", "zh-Hans-HK"] */ readonly attribute Array<ACString> packagedLocales; + + /** + * The unicode ellipsis char "…", or "...", depending on the current app locale. + */ + readonly attribute AString ellipsis; + + /** + * If true, accesskeys should always be appended for the current app locale. + */ + readonly attribute boolean alwaysAppendAccesskeys; + + /** + * If true, accesskeys should always be separated from the label. + */ + readonly attribute boolean insertSeparatorBeforeAccesskeys; + + /** + * A comma-separated list of valid BCP 47 language tags. + */ + readonly attribute ACString acceptLanguages; + + /** + * The initial setting of the language drop-down menu + * in the Fonts and Colors > Advanced preference panel. + * + * Takes one of the values of the menuitems in the "selectLangs" menulist in + * https://searchfox.org/firefox-main/source/browser/components/preferences/dialogs/fonts.xhtml + */ + readonly attribute ACString fontLanguageGroup; }; diff --git a/intl/locale/rust/locale_service_glue/Cargo.toml b/intl/locale/rust/locale_service_glue/Cargo.toml @@ -0,0 +1,13 @@ +[package] +edition = "2021" +name = "locale_service_glue" +version = "0.1.0" +license = "MPL-2.0" +authors = ["Eemeli Aro <eemeli@mozilla.com>"] + +[dependencies] +nsstring = { path = "../../../../xpcom/rust/nsstring" } + +[dependencies.icu_locale] +version = "~2.0.0" +default-features = false diff --git a/intl/locale/rust/locale_service_glue/cbindgen.toml b/intl/locale/rust/locale_service_glue/cbindgen.toml @@ -0,0 +1,18 @@ +header = """/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */""" +autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. See RunCbindgen.py */ +#ifndef mozilla_intl_locale_MozLocaleBindings_h +#error "Don't include this file directly, instead include MozLocaleBindings.h" +#endif +""" +include_version = true +braces = "SameLine" +line_length = 100 +tab_width = 2 +language = "C++" +namespaces = ["mozilla", "intl", "ffi"] + +[parse] +parse_deps = true +include = ["icu_locale"] diff --git a/intl/locale/rust/locale_service_glue/src/lib.rs b/intl/locale/rust/locale_service_glue/src/lib.rs @@ -0,0 +1,284 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use icu_locale::{ + langid, + subtags::{region, variant, Script}, + LanguageIdentifier, LocaleExpander, ParseError, +}; +use nsstring::{nsACString, nsAString}; + +pub fn langid_for_mozilla(name: &nsACString) -> Result<LanguageIdentifier, ParseError> { + if name.eq_ignore_ascii_case(b"ja-jp-mac") { + Ok(langid!("ja-JP-macos")) + } else { + // Cut out any `.FOO` like `en-US.POSIX`. + let mut name: &[u8] = name.as_ref(); + if let Some(ptr) = name.iter().position(|b| b == &b'.') { + name = &name[..ptr]; + } + LanguageIdentifier::try_from_utf8(name) + } +} + +/// The unicode ellipsis char "…", or "...", depending on the locale. +#[no_mangle] +pub extern "C" fn locale_service_ellipsis(name: &nsACString, out: &mut nsAString) { + match langid_for_mozilla(name) { + Ok(langid) if langid.language.as_str() == "ja" => out.assign_str("..."), + _ => out.assign_str("…"), + } +} + +/// If true, accesskeys should always be appended for the locale. +#[no_mangle] +pub extern "C" fn locale_service_always_append_accesskeys(name: &nsACString) -> bool { + match langid_for_mozilla(name) { + Ok(langid) => match langid.language.as_str() { + "as" | "ja" => true, + "zh" if langid.region == Some(region!("TW")) => true, + _ => false, + }, + Err(_) => false, + } +} + +/// If false, accesskeys should not be separated from the label. +#[no_mangle] +pub extern "C" fn locale_service_insert_separator_before_accesskeys(name: &nsACString) -> bool { + match langid_for_mozilla(name) { + Ok(langid) => match langid.language.as_str() { + "ja" | "ko" => false, + "zh" if langid.region == Some(region!("CN")) => false, + _ => true, + }, + Err(_) => true, + } +} + +/// A comma-separated list of valid BCP 47 language tags. +/// +/// The default value is either +/// +/// $lang, en-US, en +/// +/// or +/// +/// $lang-$region, $lang, en-US, en +/// +/// if the current locale includes a region subtag. +/// +/// If customizing this, begin with the language tag of your locale. +/// Next, include language tags for other languages that you expect most users of your locale to be able to speak, +/// so that their browsing experience degrades gracefully if content is not available in their primary language. +/// +/// By default, "en-US, en" is appended to the end of the list, providing locales of last resort. +/// If you know that users of your locale would prefer a different variety of English, +/// or if they are not likely to understand English at all, +/// you may opt to include a different English language tag, +/// or to exclude English altogether. +#[no_mangle] +pub extern "C" fn locale_service_default_accept_languages(name: &nsACString, out: &mut nsACString) { + let mut add_en_us = true; + let lang; + let fmt; + let langs = match langid_for_mozilla(name) { + Ok(langid) => { + lang = langid.language; + match lang.as_str() { + "ace" => "ace, id", + "ach" => "ach, en-GB", + "af" => "af, en-ZA, en-GB", + "ak" => "ak, ak-GH", + "an" => "an, es-ES, es, ca", + "ast" => "ast, es-ES, es", + "az" => "az-AZ, az", + "bo" => "bo-CN, bo-IN, bo", + "br" => "br, fr-FR, fr", + "brx" => "brx, as", + "bs" => "bs-BA, bs", + "ca" if langid.variants.get(0) == Some(&variant!("valencia")) => "ca-valencia, ca", + "cak" => "cak, kaq, es", + "crh" => "tr-TR, tr", + "cs" => "cs, sk", + "csb" => "csb, csb-PL, pl", + "cy" => "cy-GB, cy", + "dsb" => "dsb, hsb, de", + "el" => "el-GR, el", + "en" => { + add_en_us = false; + match langid.region { + Some(region) => match region.as_str() { + "CA" => "en-CA, en-US, en", + "GB" => "en-GB, en", + "ZA" => "en-ZA, en-GB, en-US, en", + _ => "en-US, en", + }, + _ => "en-US, en", + } + } + "et" => "et, et-EE", + "fa" => "fa-IR, fa", + "ff" => "ff, fr-FR, fr, en-GB", + "fi" => "fi-FI, fi", + "fr" => "fr, fr-FR", + "frp" => "frp, fr-FR, fr", + "fur" => "fur-IT, fur, it-IT, it", + "fy" => "fy-NL, fy, nl", + "ga" => "ga-IE, ga, en-IE, en-GB", + "gd" => "gd-GB, gd, en-GB", + "gl" => "gl-ES, gl", + "gn" => "gn, es", + "gv" => "gv, en-GB", + "he" => "he, he-IL", + "hr" => "hr, hr-HR", + "hsb" => "hsb, dsb, de", + "hto" => "es-MX, es-ES, es, es-AR, es-CL", + "hu" => "hu-HU, hu", + "hye" => "hye, hy", + "ilo" => "ilo-PH, ilo", + "it" => "it-IT, it", + "ixl" => "ixl, es-MX, es", + "ja" => "ja", // Also catches ja-JP-mac + "ka" => "ka-GE, ka", + "kab" => "kab-DZ, kab, fr-FR, fr", + "kk" => "kk, ru, ru-RU", + "kn" => "kn-IN, kn", + "ko" => "ko-KR, ko", + "lb" => "lb, de-DE, de", + "lg" => "lg, en-GB", + "lij" => "lij, it", + "lt" => "lt, ru, pl", + "ltg" => "ltg, lv", + "mai" => "mai, hi-IN, en", + "meh" => "meh, es-MX, es", + "mix" => "mix, es-MX, es", + "mk" => "mk-MK, mk", + "ml" => "ml-IN, ml", + "mr" => "mr-IN, mr", + "my" => { + add_en_us = false; + "my, en-GB, en" + } + "nb" => "nb-NO, nb, no-NO, no, nn-NO, nn", + "nn" => "nn-NO, nn, no-NO, no, nb-NO, nb", + "nr" => "nr-ZA, nr, en-ZA, en-GB", + "nso" => "nso-ZA, nso, en-ZA, en-GB", + "oc" => "oc, ca, fr, es, it", + "pa" => "pa, pa-IN", + "ppl" => "ppl, es-MX, es", + "rm" => "rm, rm-CH, de-CH, de", + "ro" => { + add_en_us = false; + "ro-RO, ro-GB, en" + } + "ru" => "ru-RU, ru", + "sah" => "sah, ru-RU, ru", + "sc" => "sc, it-IT, it", + "scn" => "scn, it-IT, it", + "sco" => { + add_en_us = false; + "sco, en-GB, en" + } + "si" => "si-LK, si", + "sk" => "sk, cs", + "sl" => { + add_en_us = false; + "sl, en-GB, en" + } + "son" => "son, son-ML, fr", + "sq" => "sq, sq-AL", + "sr" => "sr-RS, sr", + "st" => "st-ZA, st, en-ZA, en-GB", + "szl" => { + add_en_us = false; + "szl, pl-PL, pl, en, de" + } + "ta" => "ta-IN, ta", + "te" => "te-IN, te", + "tl" => "tl-PH, tl", + "tr" => "tr-TR, tr", + "trs" => "trs, es-MX, es", + "ts" => "ts-ZA, ts, en-ZA, en-GB", + "uk" => "uk-UA, uk", + "ur" => "ur-PK, ur", + "uz" => "uz, ru", + "ve" => "ve-ZA, ve, en-ZA, en-GB", + "vi" => "vi-VN, vi", + "xcl" => "xcl, hy", + "xh" => "xh-ZA, xh", + "zam" => "zam, es-MX, es", + "zh" if langid.region == Some(region!("CN")) => "zh-CN, zh, zh-TW, zh-HK", + _ => { + if langid.region.is_some() { + let region = langid.region.unwrap(); + fmt = format!("{lang}-{region}, {lang}"); + fmt.as_str() + } else { + lang.as_str() + } + } + } + } + Err(_) => { + add_en_us = false; + "en-US, en" + } + }; + if add_en_us { + out.assign(format!("{langs}, en-US, en").as_str()) + } else { + out.assign(langs) + } +} + +/// The initial setting of the language drop-down menu +/// in the Fonts and Colors > Advanced preference panel. +/// +/// Takes one of the values of the menuitems in the "selectLangs" menulist in +/// https://searchfox.org/firefox-main/source/browser/components/preferences/dialogs/fonts.xhtml +#[no_mangle] +pub extern "C" fn locale_service_default_font_language_group( + name: &nsACString, + out: &mut nsACString, +) { + const X_WESTERN: &'static str = "x-western"; + let font_group = match langid_for_mozilla(name) { + Ok(mut langid) => match langid.language.as_str() { + "az" | "mai" | "sat" | "vi" => "x-unicode", + // These should really have their scripts correctly detected below. + "bo" => "x-tibt", + "ckb" | "skr" => "ar", + "hye" | "xcl" => "x-armn", + _ => { + LocaleExpander::new_common().maximize(&mut langid); + match langid.script.as_ref().map(Script::as_str) { + Some("Arab") => "ar", + Some("Armn") => "x-armn", + Some("Beng") => "x-beng", + Some("Cyrl") => "x-cyrillic", + Some("Deva") => "x-devanagari", + Some("Grek") => "el", + Some("Gujr") => "x-gujr", + Some("Guru") => "x-guru", + Some("Hans") => "zh-CN", + Some("Hant") => "zh-TW", + Some("Hebr") => "he", + Some("Jpan") => "ja", + Some("Knda") => "x-knda", + Some("Kore") => "ko", + Some("Laoo") | Some("Thai") => "th", + Some("Mlym") => "x-mlym", + Some("Sinh") => "x-sinh", + Some("Telu") => "x-telu", + Some("Tibt") => "x-tibt", + // "Geor" | "Khmr" | "Latn" | "Mymr" | "Taml" => X_WESTERN, + _ => X_WESTERN, + } + } + }, + Err(_) => X_WESTERN, + }; + out.assign(font_group) +} diff --git a/intl/locale/tests/gtest/TestLocaleService.cpp b/intl/locale/tests/gtest/TestLocaleService.cpp @@ -72,6 +72,14 @@ TEST(Intl_Locale_LocaleService, GetAppLocaleAsLangTag) ASSERT_TRUE(appLocales[0] == locale); } +TEST(Intl_Locale_LocaleService, GetEllipsis) +{ + nsAutoString ellipsis; + LocaleService::GetInstance()->GetEllipsis(ellipsis); + + ASSERT_TRUE(ellipsis == u"…"); +} + TEST(Intl_Locale_LocaleService, GetRegionalPrefsLocales) { nsTArray<nsCString> rpLocales; @@ -154,6 +162,16 @@ TEST(Intl_Locale_LocaleService, IsAppLocaleRTL) mozilla::Preferences::ClearUser("intl.l10n.pseudo"); } +TEST(Intl_Locale_LocaleService, AlwaysAppendAccesskeys) +{ + ASSERT_FALSE(LocaleService::GetInstance()->AlwaysAppendAccesskeys()); +} + +TEST(Intl_Locale_LocaleService, InsertSeparatorBeforeAccesskeys) +{ + ASSERT_TRUE(LocaleService::GetInstance()->InsertSeparatorBeforeAccesskeys()); +} + TEST(Intl_Locale_LocaleService, TryCreateComponent) { { diff --git a/intl/locale/tests/unit/test_localeService.js b/intl/locale/tests/unit/test_localeService.js @@ -243,6 +243,101 @@ add_test(function test_handle_ja_JP_mac() { run_next_test(); }); +add_test(function test_locale_service_glue() { + // On Android, intl.accept_languages is set with a user pref value, + // so we clear that in order to test the default value handling. + let origAcceptLanguages = null; + if (Services.prefs.prefHasUserValue("intl.accept_languages")) { + origAcceptLanguages = Services.prefs.getCharPref("intl.accept_languages"); + Services.prefs.clearUserPref("intl.accept_languages"); + } + const origAvLocales = localeService.availableLocales; + + localeService.availableLocales = ["en-US"]; + localeService.requestedLocales = ["en-US"]; + Assert.equal(localeService.acceptLanguages, "en-US, en"); + Assert.equal(localeService.ellipsis, "…"); + Assert.equal(localeService.alwaysAppendAccesskeys, false); + Assert.equal(localeService.insertSeparatorBeforeAccesskeys, true); + + localeService.availableLocales = ["ca-valencia", "en-US"]; + localeService.requestedLocales = ["ca-valencia"]; + Assert.equal(localeService.acceptLanguages, "ca-valencia, ca, en-US, en"); + + localeService.availableLocales = ["hi-IN", "en-US"]; + localeService.requestedLocales = ["hi-IN"]; + Assert.equal(localeService.acceptLanguages, "hi-IN, hi, en-US, en"); + + localeService.availableLocales = ["ja-JP-mac", "en-US"]; + localeService.requestedLocales = ["ja-JP-mac"]; + Assert.equal(localeService.acceptLanguages, "ja, en-US, en"); + Assert.equal(localeService.ellipsis, "..."); + Assert.equal(localeService.alwaysAppendAccesskeys, true); + Assert.equal(localeService.insertSeparatorBeforeAccesskeys, false); + + localeService.availableLocales = ["zh-CN", "en-US"]; + localeService.requestedLocales = ["zh-CN"]; + Assert.equal( + localeService.acceptLanguages, + "zh-CN, zh, zh-TW, zh-HK, en-US, en" + ); + Assert.equal(localeService.ellipsis, "…"); + Assert.equal(localeService.alwaysAppendAccesskeys, false); + Assert.equal(localeService.insertSeparatorBeforeAccesskeys, false); + + localeService.availableLocales = ["zh-TW", "en-US"]; + localeService.requestedLocales = ["zh-TW"]; + Assert.equal(localeService.acceptLanguages, "zh-TW, zh, en-US, en"); + Assert.equal(localeService.ellipsis, "…"); + Assert.equal(localeService.alwaysAppendAccesskeys, true); + Assert.equal(localeService.insertSeparatorBeforeAccesskeys, true); + + if (origAcceptLanguages) { + Services.prefs.setCharPref("intl.accept_languages", origAcceptLanguages); + } + localeService.availableLocales = origAvLocales; + + run_next_test(); +}); + +add_test(function test_font_langauge_group() { + const origAvLocales = localeService.availableLocales; + + for (const [locales, expGroup] of [ + [["ar", "ckb", "fa", "ks", "skr"], "ar"], + [["az", "mai", "sat", "vi"], "x-unicode"], + [["be", "bg", "kk", "mk", "ru", "sah", "sr", "tg", "uk"], "x-cyrillic"], + [["bn"], "x-beng"], + [["bo"], "x-tibt"], + [["brx", "hi", "mr", "ne"], "x-devanagari"], + [["el"], "el"], + [["gu-IN"], "x-gujr"], + [["he"], "he"], + [["hy", "hye", "xcl"], "x-armn"], + [["ja"], "ja"], + [["kn"], "x-knda"], + [["ko"], "ko"], + [["lo", "th"], "th"], + [["ml"], "x-mlym"], + [["si"], "x-sinh"], + [["son"], "x-western"], + [["te"], "x-telu"], + [["th"], "th"], + [["zh-CN"], "zh-CN"], + [["zh-TW"], "zh-TW"], + ]) { + for (const locale of locales) { + localeService.availableLocales = [locale, "en-US"]; + localeService.requestedLocales = [locale]; + Assert.equal(localeService.fontLanguageGroup, expGroup, locale); + } + } + + localeService.availableLocales = origAvLocales; + + run_next_test(); +}); + registerCleanupFunction(() => { Services.prefs.clearUserPref(PREF_REQUESTED_LOCALES); }); diff --git a/toolkit/library/rust/shared/Cargo.toml b/toolkit/library/rust/shared/Cargo.toml @@ -28,6 +28,7 @@ cubeb-sys = { version = "0.30.1", optional = true, features=["gecko-in-tree"] } audioipc2-client = { git = "https://github.com/mozilla/audioipc", rev = "82fe7fa7e3aaa35468137239a0e4c2f867457214", optional = true } audioipc2-server = { git = "https://github.com/mozilla/audioipc", rev = "82fe7fa7e3aaa35468137239a0e4c2f867457214", optional = true } encoding_glue = { path = "../../../../intl/encoding_glue" } +locale_service_glue = { path = "../../../../intl/locale/rust/locale_service_glue" } authrs_bridge = { path = "../../../../dom/webauthn/authrs_bridge" } gkrust_utils = { path = "../../../../xpcom/rust/gkrust_utils" } gecko_logger = { path = "../../../../xpcom/rust/gecko_logger" } diff --git a/toolkit/library/rust/shared/lib.rs b/toolkit/library/rust/shared/lib.rs @@ -67,6 +67,8 @@ extern crate aa_stroke; extern crate qcms; extern crate wpf_gpu_raster; +extern crate locale_service_glue; + extern crate unic_langid; extern crate unic_langid_ffi; diff --git a/tools/@types/generated/lib.gecko.xpcom.d.ts b/tools/@types/generated/lib.gecko.xpcom.d.ts @@ -5379,6 +5379,11 @@ interface mozILocaleService extends nsISupports { availableLocales: string[]; readonly isAppLocaleRTL: boolean; readonly packagedLocales: string[]; + readonly ellipsis: string; + readonly alwaysAppendAccesskeys: boolean; + readonly insertSeparatorBeforeAccesskeys: boolean; + readonly acceptLanguages: string; + readonly fontLanguageGroup: string; } // https://searchfox.org/mozilla-central/source/intl/locale/mozIOSPreferences.idl diff --git a/tools/lint/clippy.yml b/tools/lint/clippy.yml @@ -88,6 +88,7 @@ clippy: - xpcom/rust/gtest/nsstring/ - security/manager/ssl/cert_storage/ - intl/locale/rust/fluent-langneg-ffi/ + - intl/locale/rust/locale_service_glue/ - intl/locale/rust/unic-langid-ffi/ - toolkit/components/places/bookmark_sync/ - xpcom/rust/nserror/