Bug 1332207 - Introduce mozilla::intl::LocaleService. r=jfkthame
authorZibi Braniecki <gandalf@mozilla.com>
Wed, 25 Jan 2017 15:58:14 -0800
changeset 469947 70d1ad7e8c89afda82029abe3291fcd8a2652305
parent 469946 d541ec6dbe9ccdb631423589085cba9694e544e8
child 469948 92eff00a7ed2fc84e6e3b343c46158b92b457a38
push id43887
push userbmo:gps@mozilla.com
push dateFri, 03 Feb 2017 00:34:08 +0000
reviewersjfkthame
bugs1332207
milestone54.0a1
Bug 1332207 - Introduce mozilla::intl::LocaleService. r=jfkthame MozReview-Commit-ID: AV1bvCt6tmP
chrome/nsChromeRegistryChrome.cpp
intl/locale/LocaleService.cpp
intl/locale/LocaleService.h
intl/locale/moz.build
intl/locale/tests/gtest/TestLocaleService.cpp
intl/locale/tests/gtest/moz.build
--- a/chrome/nsChromeRegistryChrome.cpp
+++ b/chrome/nsChromeRegistryChrome.cpp
@@ -21,16 +21,17 @@
 #include "nsEnumeratorUtils.h"
 #include "nsNetUtil.h"
 #include "nsStringEnumerator.h"
 #include "nsTextFormatter.h"
 #include "nsXPCOMCIDInternal.h"
 
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Unused.h"
+#include "mozilla/intl/LocaleService.h"
 
 #include "nsICommandLine.h"
 #include "nsILocaleService.h"
 #include "nsIObserverService.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "mozilla/Preferences.h"
 #include "nsIResProtocolHandler.h"
@@ -381,16 +382,17 @@ nsresult nsChromeRegistryChrome::UpdateS
   if (prefs) {
     rv = SelectLocaleFromPref(prefs);
     if (NS_SUCCEEDED(rv)) {
       nsCOMPtr<nsIObserverService> obsSvc =
         mozilla::services::GetObserverService();
       NS_ASSERTION(obsSvc, "Couldn't get observer service.");
       obsSvc->NotifyObservers((nsIChromeRegistry*) this,
                               "selected-locale-has-changed", nullptr);
+      mozilla::intl::LocaleService::GetInstance()->Refresh();
     }
   }
 
   return rv;
 }
 
 static void
 SerializeURI(nsIURI* aURI,
new file mode 100644
--- /dev/null
+++ b/intl/locale/LocaleService.cpp
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#include "LocaleService.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Services.h"
+#include "nsIObserverService.h"
+#include "nsIToolkitChromeRegistry.h"
+
+using namespace mozilla::intl;
+
+/**
+ * This function performs the actual language negotiation for the API.
+ *
+ * Currently it collects the locale ID used by nsChromeRegistry and
+ * adds hardcoded "en-US" locale as a fallback.
+ */
+static void
+ReadAppLocales(nsTArray<nsCString>& aRetVal)
+{
+  nsAutoCString uaLangTag;
+  nsCOMPtr<nsIToolkitChromeRegistry> cr =
+    mozilla::services::GetToolkitChromeRegistryService();
+  if (cr) {
+    cr->GetSelectedLocale(NS_LITERAL_CSTRING("global"), true, uaLangTag);
+  }
+  if (!uaLangTag.IsEmpty()) {
+    aRetVal.AppendElement(uaLangTag);
+  }
+
+  if (!uaLangTag.EqualsLiteral("en-US")) {
+    aRetVal.AppendElement(NS_LITERAL_CSTRING("en-US"));
+  }
+}
+
+mozilla::StaticAutoPtr<LocaleService> LocaleService::sInstance;
+
+LocaleService* LocaleService::GetInstance()
+{
+  if (!sInstance) {
+    sInstance = new LocaleService();
+    ClearOnShutdown(&sInstance);
+  }
+  return sInstance;
+}
+
+void
+LocaleService::GetAppLocales(nsTArray<nsCString>& aRetVal)
+{
+  if (mAppLocales.IsEmpty()) {
+    ReadAppLocales(mAppLocales);
+  }
+  aRetVal = mAppLocales;
+}
+
+void
+LocaleService::GetAppLocale(nsACString& aRetVal)
+{
+  if (mAppLocales.IsEmpty()) {
+    ReadAppLocales(mAppLocales);
+  }
+  aRetVal = mAppLocales[0];
+}
+
+void
+LocaleService::Refresh()
+{
+  nsTArray<nsCString> newLocales;
+  ReadAppLocales(newLocales);
+
+  if (mAppLocales != newLocales) {
+    mAppLocales = Move(newLocales);
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    if (obs) {
+      obs->NotifyObservers(nullptr, "intl:app-locales-changed", nullptr);
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/intl/locale/LocaleService.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_intl_LocaleService_h__
+#define mozilla_intl_LocaleService_h__
+
+#include "mozilla/StaticPtr.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace intl {
+
+
+/**
+ * LocaleService is a manager of language negotiation in Gecko.
+ *
+ * It's intended to be the core place for collecting available and
+ * requested languages and negotiating them to produce a fallback
+ * chain of locales for the application.
+ */
+class LocaleService
+{
+public:
+  static LocaleService* GetInstance();
+
+  /**
+   * Returns a list of locales that the application should be localized to.
+   *
+   * The result is a sorted list of valid locale IDs and it should be
+   * used for all APIs that accept list of locales, like ECMA402 and L10n APIs.
+   *
+   * This API always returns at least one locale.
+   *
+   * Example: ["en-US", "de", "pl", "sr-Cyrl", "zh-Hans-HK"]
+   *
+   * Usage:
+   * nsTArray<nsCString> appLocales;
+   * LocaleService::GetInstance()->GetAppLocales(appLocales);
+   */
+  void GetAppLocales(nsTArray<nsCString>& aRetVal);
+
+  /**
+   * Returns the best locale that the application should be localized to.
+   *
+   * The result is a valid locale IDs and it should be
+   * used for all APIs that do not handle language negotiation.
+   *
+   * Where possible, GetAppLocales should be preferred over this API and
+   * all callsites should handle some form of "best effort" language
+   * negotiation to respect user preferences in case the use case does
+   * not have data for the first locale in the list.
+   *
+   * Example: "zh-Hans-HK"
+   *
+   * Usage:
+   * nsAutoCString appLocale;
+   * LocaleService::GetInstance()->GetAppLocale(appLocale);
+   */
+  void GetAppLocale(nsACString& aRetVal);
+
+  /**
+   * Triggers a refresh of the language negotiation process.
+   *
+   * If the result differs from the previous list, it will additionally
+   * trigger a global event "intl:app-locales-changed".
+   */
+  void Refresh();
+
+protected:
+  nsTArray<nsCString> mAppLocales;
+
+private:
+  static StaticAutoPtr<LocaleService> sInstance;
+};
+
+} // intl
+} // namespace mozilla
+
+#endif /* mozilla_intl_LocaleService_h__ */
--- a/intl/locale/moz.build
+++ b/intl/locale/moz.build
@@ -30,17 +30,22 @@ EXPORTS += [
     'nsCollationCID.h',
     'nsILanguageAtomService.h',
     'nsIPlatformCharset.h',
     'nsPosixLocale.h',
     'nsUConvPropertySearch.h',
     'nsWin32Locale.h',
 ]
 
+EXPORTS.mozilla.intl += [
+    'LocaleService.h'
+]
+
 UNIFIED_SOURCES += [
+    'LocaleService.cpp',
     'nsCollation.cpp',
     'nsLanguageAtomService.cpp',
     'nsLocale.cpp',
     'nsLocaleService.cpp',
     'nsScriptableDateFormat.cpp',
     'nsUConvPropertySearch.cpp',
 ]
 
@@ -69,8 +74,11 @@ RESOURCE_FILES += [
 ]
 
 GENERATED_FILES += [
     'langGroups.properties.h',
 ]
 langgroups = GENERATED_FILES['langGroups.properties.h']
 langgroups.script = 'props2arrays.py'
 langgroups.inputs = ['langGroups.properties']
+
+if CONFIG['ENABLE_TESTS']:
+    DIRS += ['tests/gtest']
new file mode 100644
--- /dev/null
+++ b/intl/locale/tests/gtest/TestLocaleService.cpp
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "gtest/gtest.h"
+#include "LocaleService.h"
+#include "mozilla/Services.h"
+#include "nsIToolkitChromeRegistry.h"
+
+using namespace mozilla::intl;
+
+
+TEST(Intl_Locale_LocaleService, GetAppLocales) {
+  nsTArray<nsCString> appLocales;
+  LocaleService::GetInstance()->GetAppLocales(appLocales);
+
+  ASSERT_FALSE(appLocales.IsEmpty());
+}
+
+TEST(Intl_Locale_LocaleService, GetAppLocales_firstMatchesChromeReg) {
+  nsTArray<nsCString> appLocales;
+  LocaleService::GetInstance()->GetAppLocales(appLocales);
+
+  nsAutoCString uaLangTag;
+  nsCOMPtr<nsIToolkitChromeRegistry> cr =
+    mozilla::services::GetToolkitChromeRegistryService();
+  if (cr) {
+    cr->GetSelectedLocale(NS_LITERAL_CSTRING("global"), true, uaLangTag);
+  }
+
+  ASSERT_TRUE(appLocales[0].Equals(uaLangTag));
+}
+
+TEST(Intl_Locale_LocaleService, GetAppLocales_lastIsEnUS) {
+  nsTArray<nsCString> appLocales;
+  LocaleService::GetInstance()->GetAppLocales(appLocales);
+
+  int32_t len = appLocales.Length();
+  ASSERT_TRUE(appLocales[len - 1].EqualsLiteral("en-US"));
+}
+
+TEST(Intl_Locale_LocaleService, GetAppLocale) {
+  nsTArray<nsCString> appLocales;
+  LocaleService::GetInstance()->GetAppLocales(appLocales);
+
+  nsAutoCString locale;
+  LocaleService::GetInstance()->GetAppLocale(locale);
+
+  ASSERT_TRUE(appLocales[0] == locale);
+}
new file mode 100644
--- /dev/null
+++ b/intl/locale/tests/gtest/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+UNIFIED_SOURCES += [
+    'TestLocaleService.cpp',
+]
+
+LOCAL_INCLUDES += [
+    '/intl/locale',
+]
+
+FINAL_LIBRARY = 'xul-gtest'