Bug 1450656 - Canonicalize ja-JP-mac to ja-JP-macos and use BCP47 version in Fluent. r?jfkthame draft
authorZibi Braniecki <zbraniecki@mozilla.com>
Tue, 10 Apr 2018 14:19:05 -0700
changeset 781811 7df01e12e41e90352e0ca8f0430ce03c102c29d9
parent 781810 2243b83d2c5ca7a13b592e0fed4639657a2882f1
push id106417
push userbmo:gandalf@aviary.pl
push dateFri, 13 Apr 2018 17:46:59 +0000
reviewersjfkthame
bugs1450656
milestone61.0a1
Bug 1450656 - Canonicalize ja-JP-mac to ja-JP-macos and use BCP47 version in Fluent. r?jfkthame MozReview-Commit-ID: 2UbD2T8yyGm
intl/l10n/L10nRegistry.jsm
intl/l10n/Localization.jsm
intl/locale/LocaleService.cpp
intl/locale/MozLocale.cpp
intl/locale/tests/unit/test_localeService.js
--- a/intl/l10n/L10nRegistry.jsm
+++ b/intl/l10n/L10nRegistry.jsm
@@ -320,16 +320,21 @@ class FileSource {
     //
     // If the `indexed` property is set to `true` it will be treated as the
     // resource not being available. Otherwise, the resource may be
     // available and we do not have any information about it yet.
     this.cache = {};
   }
 
   getPath(locale, path) {
+    // This is a special case for the only not BCP47-conformant locale
+    // code we have resources for.
+    if (locale === "ja-JP-macos") {
+      locale = "ja-JP-mac";
+    }
     return (this.prePath + path).replace(/\{locale\}/g, locale);
   }
 
   hasFile(locale, path) {
     if (!this.locales.includes(locale)) {
       return false;
     }
 
--- a/intl/l10n/Localization.jsm
+++ b/intl/l10n/Localization.jsm
@@ -96,17 +96,17 @@ class CachedIterable {
  * available in L10nRegistry, with locales requested by the user to
  * generate the iterator over MessageContexts.
  *
  * In the future, we may want to allow certain modules to override this
  * with a different negotitation strategy to allow for the module to
  * be localized into a different language - for example DevTools.
  */
 function defaultGenerateMessages(resourceIds) {
-  const appLocales = Services.locale.getAppLocalesAsLangTags();
+  const appLocales = Services.locale.getAppLocalesAsBCP47();
   return L10nRegistry.generateContexts(appLocales, resourceIds);
 }
 
 /**
  * The `Localization` class is a central high-level API for vanilla
  * JavaScript use of Fluent.
  * It combines language negotiation, MessageContext and I/O to
  * provide a scriptable API to format translations.
--- a/intl/locale/LocaleService.cpp
+++ b/intl/locale/LocaleService.cpp
@@ -39,27 +39,35 @@ NS_IMPL_ISUPPORTS(LocaleService, mozILoc
                   nsISupportsWeakReference)
 
 mozilla::StaticRefPtr<LocaleService> LocaleService::sInstance;
 
 /**
  * This function transforms a canonical Mozilla Language Tag, into it's
  * BCP47 compilant form.
  *
- * Example: "ja-JP-mac" -> "ja-JP-x-lvariant-mac"
+ * Example: "ja-JP-mac" -> "ja-JP-macos"
  *
  * The BCP47 form should be used for all calls to ICU/Intl APIs.
  * The canonical form is used for all internal operations.
  */
 static bool
 SanitizeForBCP47(nsACString& aLocale, bool strict)
 {
   // Currently, the only locale code we use that's not BCP47-conformant is
-  // "ja-JP-mac" on OS X, but let's try to be more general than just
-  // hard-coding that here.
+  // "ja-JP-mac" on OS X, and ICU canonicalizes it into a mouthfull
+  // "ja-JP-x-lvariant-mac", so instead we're hardcoding a conversion
+  // of it to "ja-JP-macos".
+  if (aLocale.LowerCaseEqualsASCII("ja-jp-mac")) {
+    aLocale.AssignLiteral("ja-JP-macos");
+    return true;
+  }
+
+  // The rest of this function will use ICU canonicalization for any other
+  // tag that may come this way.
   const int32_t LANG_TAG_CAPACITY = 128;
   char langTag[LANG_TAG_CAPACITY];
   nsAutoCString locale(aLocale);
   locale.Trim(" ");
   UErrorCode err = U_ZERO_ERROR;
   // This is a fail-safe method that will set langTag to "und" if it cannot
   // match any part of the input locale code.
   int32_t len = uloc_toLanguageTag(locale.get(), langTag, LANG_TAG_CAPACITY,
@@ -75,22 +83,17 @@ SanitizeForBCP47(nsACString& aLocale, bo
  * language tags and returns them to the caller.
  */
 static void
 SplitLocaleListStringIntoArray(nsACString& str, nsTArray<nsCString>& aRetVal)
 {
   if (str.Length() > 0) {
     for (const nsACString& part : str.Split(',')) {
       nsAutoCString locale(part);
-      if (locale.EqualsLiteral("ja-JP-mac")) {
-        // This is a hack required to handle the special Mozilla `ja-JP-mac` locale.
-        if (!aRetVal.Contains(locale)) {
-          aRetVal.AppendElement(locale);
-        }
-      } else if (SanitizeForBCP47(locale, true)) {
+      if (SanitizeForBCP47(locale, true)) {
         if (!aRetVal.Contains(locale)) {
           aRetVal.AppendElement(locale);
         }
       }
     }
   }
 }
 
@@ -209,30 +212,33 @@ LocaleService::~LocaleService()
 }
 
 void
 LocaleService::GetAppLocalesAsLangTags(nsTArray<nsCString>& aRetVal)
 {
   if (mAppLocales.IsEmpty()) {
     NegotiateAppLocales(mAppLocales);
   }
-  aRetVal = mAppLocales;
+  for (uint32_t i = 0; i < mAppLocales.Length(); i++) {
+    nsAutoCString locale(mAppLocales[i]);
+    if (locale.LowerCaseEqualsASCII("ja-jp-macos")) {
+      aRetVal.AppendElement("ja-JP-mac");
+    } else {
+      aRetVal.AppendElement(locale);
+    }
+  }
 }
 
 void
 LocaleService::GetAppLocalesAsBCP47(nsTArray<nsCString>& aRetVal)
 {
   if (mAppLocales.IsEmpty()) {
     NegotiateAppLocales(mAppLocales);
   }
-  for (uint32_t i = 0; i < mAppLocales.Length(); i++) {
-    nsAutoCString locale(mAppLocales[i]);
-    SanitizeForBCP47(locale, false);
-    aRetVal.AppendElement(locale);
-  }
+  aRetVal = mAppLocales;
 }
 
 void
 LocaleService::GetRegionalPrefsLocales(nsTArray<nsCString>& aRetVal)
 {
   bool useOSLocales = Preferences::GetBool("intl.regional_prefs.use_os_locales", false);
 
   // If the user specified that they want to use OS Regional Preferences locales,
@@ -685,25 +691,27 @@ LocaleService::GetPackagedLocales(nsTArr
  */
 
 NS_IMETHODIMP
 LocaleService::GetDefaultLocale(nsACString& aRetVal)
 {
   // We don't allow this to change during a session (it's set at build/package
   // time), so we cache the result the first time we're called.
   if (mDefaultLocale.IsEmpty()) {
+    nsAutoCString locale;
     // Try to get the package locale from update.locale in omnijar. If the
     // update.locale file is not found, item.len will remain 0 and we'll
     // just use our hard-coded default below.
-    GetGREFileContents("update.locale", &mDefaultLocale);
-    mDefaultLocale.Trim(" \t\n\r");
+    GetGREFileContents("update.locale", &locale);
+    locale.Trim(" \t\n\r");
     // This should never be empty.
-    MOZ_ASSERT(!mDefaultLocale.IsEmpty());
-    MOZ_ASSERT(mDefaultLocale.EqualsLiteral("ja-JP-mac")
-        || SanitizeForBCP47(mDefaultLocale, true));
+    MOZ_ASSERT(!locale.IsEmpty());
+    if (SanitizeForBCP47(locale, true)) {
+      mDefaultLocale.Assign(locale);
+    }
 
     // Hard-coded fallback to allow us to survive even if update.locale was
     // missing/broken in some way.
     if (mDefaultLocale.IsEmpty()) {
       GetLastFallbackLocale(mDefaultLocale);
     }
   }
 
@@ -716,57 +724,54 @@ LocaleService::GetLastFallbackLocale(nsA
 {
   aRetVal.AssignLiteral("en-US");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 LocaleService::GetAppLocalesAsLangTags(uint32_t* aCount, char*** aOutArray)
 {
-  if (mAppLocales.IsEmpty()) {
-    NegotiateAppLocales(mAppLocales);
-  }
-
-  *aCount = mAppLocales.Length();
-  *aOutArray = CreateOutArray(mAppLocales);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-LocaleService::GetAppLocalesAsBCP47(uint32_t* aCount, char*** aOutArray)
-{
   AutoTArray<nsCString, 32> locales;
-  GetAppLocalesAsBCP47(locales);
+  GetAppLocalesAsLangTags(locales);
 
   *aCount = locales.Length();
   *aOutArray = CreateOutArray(locales);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-LocaleService::GetAppLocaleAsLangTag(nsACString& aRetVal)
+LocaleService::GetAppLocalesAsBCP47(uint32_t* aCount, char*** aOutArray)
 {
   if (mAppLocales.IsEmpty()) {
     NegotiateAppLocales(mAppLocales);
   }
-  aRetVal = mAppLocales[0];
+  *aCount = mAppLocales.Length();
+  *aOutArray = CreateOutArray(mAppLocales);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LocaleService::GetAppLocaleAsLangTag(nsACString& aRetVal)
+{
+  AutoTArray<nsCString, 32> locales;
+  GetAppLocalesAsLangTags(locales);
+
+  aRetVal = locales[0];
   return NS_OK;
 }
 
 NS_IMETHODIMP
 LocaleService::GetAppLocaleAsBCP47(nsACString& aRetVal)
 {
   if (mAppLocales.IsEmpty()) {
     NegotiateAppLocales(mAppLocales);
   }
   aRetVal = mAppLocales[0];
-
-  SanitizeForBCP47(aRetVal, false);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 LocaleService::GetRegionalPrefsLocales(uint32_t* aCount, char*** aOutArray)
 {
   AutoTArray<nsCString,10> rgLocales;
 
@@ -897,18 +902,17 @@ LocaleService::GetRequestedLocale(nsACSt
 NS_IMETHODIMP
 LocaleService::SetRequestedLocales(const char** aRequested,
                                    uint32_t aRequestedCount)
 {
   nsAutoCString str;
 
   for (uint32_t i = 0; i < aRequestedCount; i++) {
     nsAutoCString locale(aRequested[i]);
-    if (!locale.EqualsLiteral("ja-JP-mac") &&
-        !SanitizeForBCP47(locale, true)) {
+    if (!SanitizeForBCP47(locale, true)) {
       NS_ERROR("Invalid language tag provided to SetRequestedLocales!");
       return NS_ERROR_INVALID_ARG;
     }
 
     if (i > 0) {
       str.AppendLiteral(",");
     }
     str.Append(locale);
@@ -944,18 +948,17 @@ LocaleService::GetIsAppLocaleRTL(bool* a
 NS_IMETHODIMP
 LocaleService::SetAvailableLocales(const char** aAvailable,
                                    uint32_t aAvailableCount)
 {
   nsTArray<nsCString> newLocales;
 
   for (uint32_t i = 0; i < aAvailableCount; i++) {
     nsAutoCString locale(aAvailable[i]);
-    if (!locale.EqualsLiteral("ja-JP-mac") &&
-        !SanitizeForBCP47(locale, true)) {
+    if (!SanitizeForBCP47(locale, true)) {
       NS_ERROR("Invalid language tag provided to SetAvailableLocales!");
       return NS_ERROR_INVALID_ARG;
     }
     newLocales.AppendElement(locale);
   }
 
   if (newLocales != mAvailableLocales) {
     mAvailableLocales = Move(newLocales);
--- a/intl/locale/MozLocale.cpp
+++ b/intl/locale/MozLocale.cpp
@@ -78,21 +78,17 @@ Locale::Locale(const nsACString& aLocale
       mScript = subTag;
       ToLowerCase(mScript);
       mScript.Replace(0, 1, ToUpperCase(mScript[0]));
       position = 3;
     } else if (position <= 3 && slen == 2) {
       mRegion = subTag;
       ToUpperCase(mRegion);
       position = 4;
-    } else if (position <= 4 && slen >= 3 && slen <= 8) {
-      // we're quirky here because we allow for variant to be 3 char long.
-      // BCP47 requires variants to be 5-8 char long at lest.
-      //
-      // We do this to support the `ja-JP-mac` quirk that we have.
+    } else if (position <= 4 && slen >= 5 && slen <= 8) {
       nsAutoCString lcSubTag(subTag);
       ToLowerCase(lcSubTag);
       mVariants.InsertElementSorted(lcSubTag);
       position = 4;
     }
   }
 }
 
--- a/intl/locale/tests/unit/test_localeService.js
+++ b/intl/locale/tests/unit/test_localeService.js
@@ -184,23 +184,28 @@ add_test(function test_getRequestedLocal
 add_test(function test_handle_ja_JP_mac() {
   const bkpAvLocales = localeService.getAvailableLocales();
 
   localeService.setAvailableLocales(["ja-JP-mac", "en-US"]);
 
   localeService.setRequestedLocales(['ja-JP-mac']);
 
   let reqLocales = localeService.getRequestedLocales();
-  Assert.ok(reqLocales[0] === 'ja-JP-mac');
+  Assert.equal(reqLocales[0], 'ja-JP-macos');
 
   let avLocales = localeService.getAvailableLocales();
-  Assert.ok(avLocales[0] === 'ja-JP-mac');
+  Assert.equal(avLocales[0], 'ja-JP-macos');
+
+  let appLocales = localeService.getAppLocalesAsBCP47();
+  Assert.equal(appLocales[0], 'ja-JP-macos');
 
-  let appLocales = localeService.getAppLocalesAsLangTags();
-  Assert.ok(appLocales[0] === 'ja-JP-mac');
+  let appLocalesAsLT = localeService.getAppLocalesAsLangTags();
+  Assert.equal(appLocalesAsLT[0], 'ja-JP-mac');
+
+  Assert.equal(localeService.getAppLocaleAsLangTag(), "ja-JP-mac");
 
   localeService.setAvailableLocales(bkpAvLocales);
 
   run_next_test();
 });
 
 
 registerCleanupFunction(() => {