Bug 1400006 - Extend language negotiation in LocaleService to support looking for the best likelySubtag for the locale with region stripped. r?jfkthame draft
authorZibi Braniecki <zbraniecki@mozilla.com>
Thu, 14 Sep 2017 15:21:33 -0700
changeset 665103 c687b6eb4f53a382f352809f61d6875f1f64261d
parent 664635 9517eea4a1a5955618fc79d039f9f0282b7185ca
child 731648 ff4d4e756c40ed19be1f718f471f7e155739de2a
push id79922
push userbmo:gandalf@aviary.pl
push dateThu, 14 Sep 2017 22:21:46 +0000
reviewersjfkthame
bugs1400006
milestone57.0a1
Bug 1400006 - Extend language negotiation in LocaleService to support looking for the best likelySubtag for the locale with region stripped. r?jfkthame MozReview-Commit-ID: BR1WrgXSf6a
intl/locale/LocaleService.cpp
intl/locale/LocaleService.h
intl/locale/tests/unit/test_localeService_negotiateLanguages.js
--- a/intl/locale/LocaleService.cpp
+++ b/intl/locale/LocaleService.cpp
@@ -368,17 +368,24 @@ LocaleService::OnLocalesChanged()
  *               ^^
  *               |-- ICU likelySubtags expands it to 'en-Latn-US'
  *
  * 4) Attempt to look up for a different variant of the same locale.
  *    Example: ['ja-JP-win'] * ['ja-JP-mac'] = ['ja-JP-mac']
  *               ^^^^^^^^^
  *               |----------- replace variant with range: 'ja-JP-*'
  *
- * 5) Attempt to look up for a different region of the same locale.
+ * 5) Attempt to look up for a maximized version of the requested locale,
+ *    stripped of the region code.
+ *    Example: ['en-CA'] * ['en-ZA', 'en-US'] = ['en-US', 'en-ZA']
+ *               ^^^^^
+ *               |----------- look for likelySubtag of 'en': 'en-Latn-US'
+ *
+ *
+ * 6) Attempt to look up for a different region of the same locale.
  *    Example: ['en-GB'] * ['en-AU'] = ['en-AU']
  *               ^^^^^
  *               |----- replace region with range: 'en-*'
  *
  * It uses one of the strategies described in LocaleService.h.
  */
 void
 LocaleService::FilterMatches(const nsTArray<nsCString>& aRequested,
@@ -454,17 +461,25 @@ LocaleService::FilterMatches(const nsTAr
     }
 
     // 4) Try to match against a variant as a range
     requestedLocale.SetVariantRange();
     if (findRangeMatches(requestedLocale)) {
       HANDLE_STRATEGY;
     }
 
-    // 5) Try to match against a region as a range
+    // 5) Try to match against the likely subtag without region
+    if (requestedLocale.AddLikelySubtagsWithoutRegion()) {
+      if (findRangeMatches(requestedLocale)) {
+        HANDLE_STRATEGY;
+      }
+    }
+
+
+    // 6) Try to match against a region as a range
     requestedLocale.SetRegionRange();
     if (findRangeMatches(requestedLocale)) {
       HANDLE_STRATEGY;
     }
   }
 }
 
 bool
@@ -817,38 +832,63 @@ void
 LocaleService::Locale::SetRegionRange()
 {
   mRegion.AssignLiteral("*");
 }
 
 bool
 LocaleService::Locale::AddLikelySubtags()
 {
+  return AddLikelySubtagsForLocale(mLocaleStr);
+}
+
+bool
+LocaleService::Locale::AddLikelySubtagsWithoutRegion()
+{
+  nsAutoCString locale(mLanguage);
+
+  if (!mScript.IsEmpty()) {
+    locale.Append("-");
+    locale.Append(mScript);
+  }
+
+  // We don't add variant here because likelySubtag doesn't care about it.
+
+  return AddLikelySubtagsForLocale(locale);
+}
+
+bool
+LocaleService::Locale::AddLikelySubtagsForLocale(const nsACString& aLocale)
+{
 #ifdef ENABLE_INTL_API
   const int32_t kLocaleMax = 160;
   char maxLocale[kLocaleMax];
+  nsAutoCString locale(aLocale);
 
   UErrorCode status = U_ZERO_ERROR;
-  uloc_addLikelySubtags(mLocaleStr.get(), maxLocale, kLocaleMax, &status);
+  uloc_addLikelySubtags(locale.get(), maxLocale, kLocaleMax, &status);
 
   if (U_FAILURE(status)) {
     return false;
   }
 
   nsDependentCString maxLocStr(maxLocale);
   Locale loc = Locale(maxLocStr, false);
 
   if (loc == *this) {
     return false;
   }
 
   mLanguage = loc.mLanguage;
   mScript = loc.mScript;
   mRegion = loc.mRegion;
-  mVariant = loc.mVariant;
+
+  // We don't update variant from likelySubtag since it's not going to
+  // provide it and we want to preserve the range
+
   return true;
 #else
   return false;
 #endif
 }
 
 NS_IMETHODIMP
 LocaleService::GetRequestedLocales(uint32_t* aCount, char*** aOutArray)
--- a/intl/locale/LocaleService.h
+++ b/intl/locale/LocaleService.h
@@ -266,17 +266,19 @@ private:
     Locale(const nsCString& aLocale, bool aRange);
 
     bool Matches(const Locale& aLocale) const;
     bool LanguageMatches(const Locale& aLocale) const;
 
     void SetVariantRange();
     void SetRegionRange();
 
-    bool AddLikelySubtags(); // returns false if nothing changed
+    // returns false if nothing changed
+    bool AddLikelySubtags();
+    bool AddLikelySubtagsWithoutRegion();
 
     const nsCString& AsString() const {
       return mLocaleStr;
     }
 
     bool operator== (const Locale& aOther) {
       const auto& cmp = nsCaseInsensitiveCStringComparator();
       return mLanguage.Equals(aOther.mLanguage, cmp) &&
@@ -286,16 +288,18 @@ private:
     }
 
   private:
     const nsCString& mLocaleStr;
     nsCString mLanguage;
     nsCString mScript;
     nsCString mRegion;
     nsCString mVariant;
+
+    bool AddLikelySubtagsForLocale(const nsACString& aLocale);
   };
 
   void FilterMatches(const nsTArray<nsCString>& aRequested,
                      const nsTArray<nsCString>& aAvailable,
                      LangNegStrategy aStrategy,
                      nsTArray<nsCString>& aRetVal);
 
   void NegotiateAppLocales(nsTArray<nsCString>& aRetVal);
--- a/intl/locale/tests/unit/test_localeService_negotiateLanguages.js
+++ b/intl/locale/tests/unit/test_localeService_negotiateLanguages.js
@@ -30,16 +30,19 @@ const data = {
       [["fr"], ["fr-CA", "fr-FR"], ["fr-FR", "fr-CA"]],
       [["az-IR"], ["az-Latn", "az-Arab"], ["az-Arab"]],
       [["sr-RU"], ["sr-Cyrl", "sr-Latn"], ["sr-Latn"]],
       [["sr"], ["sr-Latn", "sr-Cyrl"], ["sr-Cyrl"]],
       [["zh-GB"], ["zh-Hans", "zh-Hant"], ["zh-Hant"]],
       [["sr", "ru"], ["sr-Latn", "ru"], ["ru"]],
       [["sr-RU"], ["sr-Latn-RO", "sr-Cyrl"], ["sr-Latn-RO"]],
     ],
+    "should match likelySubtag region over other regions": [
+      [["en-CA"], ["en-ZA", "en-GB", "en-US"], ["en-US", "en-ZA", "en-GB"]],
+    ],
     "should match on a requested locale as a range": [
       [["en-*-US"], ["en-US"], ["en-US"]],
       [["en-Latn-US-*"], ["en-Latn-US"], ["en-Latn-US"]],
       [["en-*-US-*"], ["en-US"], ["en-US"]],
     ],
     "should match cross-region": [
       [["en"], ["en-US"], ["en-US"]],
       [["en-US"], ["en-GB"], ["en-GB"]],