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:
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/