tor-browser

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

commit 8e988ae55dddbc4019bb498c39b20080183b10ac
parent d152432109fd54238ea30435a1182a45c5e88a6e
Author: Olivia Hall <ohall@mozilla.com>
Date:   Mon, 10 Nov 2025 14:44:57 +0000

Bug 1998284 - Requested locales on Android 14+ need normalized r=m_kato,geckoview-reviewers

Language tags on Android 14 and greater can have extended formatting cues.
This normalizes the tag the same way as Accept-Languages.

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

Diffstat:
Mmobile/android/geckoview/src/main/java/org/mozilla/gecko/util/LocaleUtils.java | 14+++++++++++---
Mmobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java | 20+++++++++++++++++---
Mmobile/android/geckoview/src/test/java/org/mozilla/gecko/util/LocaleUtilsTest.java | 30++++++++++++------------------
3 files changed, 40 insertions(+), 24 deletions(-)

diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/LocaleUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/LocaleUtils.java @@ -10,9 +10,17 @@ import android.text.TextUtils; import java.util.Locale; public class LocaleUtils { - // Locale.getLanguage() may return legacy language code until Java 17 - // https://developer.android.com/reference/java/util/Locale#legacy_language_codes - public static String getLanguageTagForAcceptLanguage(final Locale locale) { + /** + * Function normalizes BCP-47 language tags to use non-legacy language codes and only return + * 'language-region' style codes for use with Accept-Language and Requested Locales. + * + * <p>Locale.getLanguage() may return legacy language code until Java 17 + * https://developer.android.com/reference/java/util/Locale#legacy_language_codes + * + * @param locale The BCP-47 locale to normalize. e.g., iw-IL, zn-Hans-CN, en-US-u-mu-celsius, + * @return A normalized BCP-47 language code with only language-region, e.g., he-IL, zn-CN, en-US. + */ + public static String getLanguageRegionLocale(final Locale locale) { String language = locale.getLanguage(); if (language.equals("in")) { language = "id"; diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java @@ -1475,7 +1475,18 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { private void commitLocales() { final GeckoBundle data = new GeckoBundle(1); - data.putStringArray("requestedLocales", mRequestedLocales); + if (mRequestedLocales != null) { + // Requested locales should be in language or language-region format + final String[] normalizedRequestedLocales = + Arrays.stream(mRequestedLocales) + .map(Locale::forLanguageTag) + .map(LocaleUtils::getLanguageRegionLocale) + .toArray(String[]::new); + data.putStringArray("requestedLocales", normalizedRequestedLocales); + } else { + data.putStringArray("requestedLocales", (String[]) null); + } + data.putString("acceptLanguages", computeAcceptLanguages()); EventDispatcher.getInstance().dispatch("GeckoView:SetLocale", data); } @@ -1486,7 +1497,10 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { // Explicitly-set app prefs come first: if (mRequestedLocales != null) { for (final String locale : mRequestedLocales) { - locales.put(locale.toLowerCase(Locale.ROOT), locale); + // Requested locales should be in language or language-region format + final String normalizedLocale = + LocaleUtils.getLanguageRegionLocale(Locale.forLanguageTag(locale)); + locales.put(normalizedLocale.toLowerCase(Locale.ROOT), normalizedLocale); } } // OS prefs come second: @@ -1505,7 +1519,7 @@ public final class GeckoRuntimeSettings extends RuntimeSettings { final String[] locales = new String[localeList.size()]; for (int i = 0; i < localeList.size(); i++) { // accept-language should be language or language-region format. - locales[i] = LocaleUtils.getLanguageTagForAcceptLanguage(localeList.get(i)); + locales[i] = LocaleUtils.getLanguageRegionLocale(localeList.get(i)); } return locales; } diff --git a/mobile/android/geckoview/src/test/java/org/mozilla/gecko/util/LocaleUtilsTest.java b/mobile/android/geckoview/src/test/java/org/mozilla/gecko/util/LocaleUtilsTest.java @@ -16,32 +16,26 @@ import org.robolectric.RobolectricTestRunner; public class LocaleUtilsTest { @Test public void languageTagForAcceptLanguage() { - assertEquals( - LocaleUtils.getLanguageTagForAcceptLanguage(Locale.forLanguageTag("zn-Hans-CN")), "zn-CN"); - assertEquals( - LocaleUtils.getLanguageTagForAcceptLanguage(Locale.forLanguageTag("zn-Hant-TW")), "zn-TW"); + assertEquals(LocaleUtils.getLanguageRegionLocale(Locale.forLanguageTag("zn-Hans-CN")), "zn-CN"); + assertEquals(LocaleUtils.getLanguageRegionLocale(Locale.forLanguageTag("zn-Hant-TW")), "zn-TW"); // If builder is Java 17+, Locale.getLanguage doesn't repelace with old language code. // But we should keep this to make things understandable. - assertEquals( - LocaleUtils.getLanguageTagForAcceptLanguage(Locale.forLanguageTag("id-ID")), "id-ID"); - assertEquals( - LocaleUtils.getLanguageTagForAcceptLanguage(Locale.forLanguageTag("in-ID")), "id-ID"); + assertEquals(LocaleUtils.getLanguageRegionLocale(Locale.forLanguageTag("id-ID")), "id-ID"); + assertEquals(LocaleUtils.getLanguageRegionLocale(Locale.forLanguageTag("in-ID")), "id-ID"); - assertEquals( - LocaleUtils.getLanguageTagForAcceptLanguage(Locale.forLanguageTag("yi-US")), "yi-US"); - assertEquals( - LocaleUtils.getLanguageTagForAcceptLanguage(Locale.forLanguageTag("ji-US")), "yi-US"); + assertEquals(LocaleUtils.getLanguageRegionLocale(Locale.forLanguageTag("yi-US")), "yi-US"); + assertEquals(LocaleUtils.getLanguageRegionLocale(Locale.forLanguageTag("ji-US")), "yi-US"); - assertEquals( - LocaleUtils.getLanguageTagForAcceptLanguage(Locale.forLanguageTag("he-IL")), "he-IL"); - assertEquals( - LocaleUtils.getLanguageTagForAcceptLanguage(Locale.forLanguageTag("iw-IL")), "he-IL"); + assertEquals(LocaleUtils.getLanguageRegionLocale(Locale.forLanguageTag("he-IL")), "he-IL"); + assertEquals(LocaleUtils.getLanguageRegionLocale(Locale.forLanguageTag("iw-IL")), "he-IL"); // Android 14 may add extension (Bug 1873578) assertEquals( - LocaleUtils.getLanguageTagForAcceptLanguage( - Locale.forLanguageTag("en-US-u-fw-mon-mu-celsius")), + LocaleUtils.getLanguageRegionLocale(Locale.forLanguageTag("en-US-u-fw-mon-mu-celsius")), "en-US"); + + assertEquals( + LocaleUtils.getLanguageRegionLocale(Locale.forLanguageTag("en-US-u-mu-celsius")), "en-US"); } }