Bug 955805 - Normalize locale codes appropriately for each subsystem. r=mfinkle, a=lsblakk
authorRichard Newman <rnewman@mozilla.com>
Tue, 31 Dec 2013 12:35:14 -0800
changeset 175763 3f0b920677460be8b4432729bda84691e793df92
parent 175762 e3ed25347ea3c65651b49d6b2b4eb5c2dba4c926
child 175764 7044a11396975d6ba1220603e6b9efbfb8f260c3
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmfinkle, lsblakk
bugs955805
milestone28.0a2
Bug 955805 - Normalize locale codes appropriately for each subsystem. r=mfinkle, a=lsblakk
mobile/android/base/LocaleManager.java
mobile/android/chrome/content/browser.js
--- a/mobile/android/base/LocaleManager.java
+++ b/mobile/android/base/LocaleManager.java
@@ -118,32 +118,65 @@ public class LocaleManager {
             final String langCode = localeCode.substring(0, index);
             final String countryCode = localeCode.substring(index + 1);
             return new Locale(langCode, countryCode);
         } else {
             return new Locale(localeCode);
         }
     }
 
+    /**
+     * Gecko uses locale codes like "es-ES", whereas a Java {@link Locale}
+     * stringifies as "es_ES".
+     *
+     * This method approximates the Java 7 method <code>Locale#toLanguageTag()</code>.
+     *
+     * @return a locale string suitable for passing to Gecko.
+     */
+    public static String getLanguageTag(final Locale locale) {
+        // If this were Java 7:
+        // return locale.toLanguageTag();
+
+        String language = locale.getLanguage();  // Can, but should never be, an empty string.
+        // Modernize certain language codes.
+        if (language.equals("iw")) {
+            language = "he";
+        } else if (language.equals("in")) {
+            language = "id";
+        } else if (language.equals("ji")) {
+            language = "yi";
+        }
+
+        String country = locale.getCountry();    // Can be an empty string.
+        if (country.equals("")) {
+            return language;
+        }
+        return language + "-" + country;
+    }
+
     public static Locale getCurrentLocale() {
         if (currentLocale != null) {
             return currentLocale;
         }
 
         final String current = getPersistedLocale();
         if (current == null) {
             return null;
         }
         return currentLocale = parseLocaleCode(current);
     }
 
     /**
-     * Returns the persisted locale if it differed from the current.
+     * Updates the Java locale and the Android configuration.
+     *
+     * Returns the persisted locale if it differed.
+     *
+     * Does not notify Gecko.
      */
-    public static String updateLocale(String localeCode) {
+    private static String updateLocale(String localeCode) {
         // Fast path.
         final Locale defaultLocale = Locale.getDefault();
         if (defaultLocale.toString().equals(localeCode)) {
             return null;
         }
 
         final Locale locale = parseLocaleCode(localeCode);
 
@@ -156,21 +189,23 @@ public class LocaleManager {
         currentLocale = locale;
 
         // Update resources.
         Resources res = getContext().getResources();
         Configuration config = res.getConfiguration();
         config.locale = locale;
         res.updateConfiguration(config, res.getDisplayMetrics());
 
+        return locale.toString();
+    }
+
+    public static void notifyGeckoOfLocaleChange(Locale locale) {
         // Tell Gecko.
-        GeckoEvent ev = GeckoEvent.createBroadcastEvent("Locale:Changed", locale.toString());
+        GeckoEvent ev = GeckoEvent.createBroadcastEvent("Locale:Changed", getLanguageTag(locale));
         GeckoAppShell.sendEventToGecko(ev);
-
-        return locale.toString();
     }
 
     private static String getPrefName() {
         return getContext().getPackageName() + ".locale";
     }
 
     public static String getPersistedLocale() {
         final SharedPreferences settings = getSharedPreferences();
@@ -193,24 +228,36 @@ public class LocaleManager {
 
     public static String getAndApplyPersistedLocale() {
         final long t1 = android.os.SystemClock.uptimeMillis();
         final String localeCode = getPersistedLocale();
         if (localeCode == null) {
             return null;
         }
 
+        // Note that we don't tell Gecko about this. We notify Gecko when the
+        // locale is set, not when we update Java.
         updateLocale(localeCode);
+
         final long t2 = android.os.SystemClock.uptimeMillis();
         Log.i(LOG_TAG, "Locale read and update took: " + (t2 - t1) + "ms.");
         return localeCode;
     }
 
     /**
-     * Returns the set locale if it changed. Always persists.
+     * Returns the set locale if it changed.
+     *
+     * Always persists and notifies Gecko.
      */
     public static String setSelectedLocale(String localeCode) {
         final String resultant = updateLocale(localeCode);
+
+        // We always persist and notify Gecko, even if nothing seemed to
+        // change. This might happen if you're picking a locale that's the same
+        // as the current OS locale. The OS locale might change next time we
+        // launch, and we need the Gecko pref and persisted locale to have been
+        // set by the time that happens.
         persistLocale(localeCode);
+        notifyGeckoOfLocaleChange(getCurrentLocale());
         return resultant;
     }
 }
 
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -1500,16 +1500,20 @@ var BrowserApp = {
         this.selectedTab.updateViewportSize(gScreenWidth);
         break;
 
       case "nsPref:changed":
         this.notifyPrefObservers(aData);
         break;
 
       case "Locale:Changed":
+        // The value provided to Locale:Changed should be a BCP47 language tag
+        // understood by Gecko -- for example, "es-ES" or "de".
+        console.log("Locale:Changed: " + aData);
+
         // TODO: do we need to be more nuanced here -- e.g., checking for the
         // OS locale -- or should it always be false on Fennec?
         Services.prefs.setBoolPref("intl.locale.matchOS", false);
         Services.prefs.setCharPref("general.useragent.locale", aData);
         break;
 
       default:
         dump('BrowserApp.observe: unexpected topic "' + aTopic + '"\n');