Bug 1332207 - Introduce mozilla::intl::LocaleService. r=jfkthame
authorZibi Braniecki <gandalf@mozilla.com>
Wed, 25 Jan 2017 15:58:14 -0800
changeset 381297 70d1ad7e8c89afda82029abe3291fcd8a2652305
parent 381296 d541ec6dbe9ccdb631423589085cba9694e544e8
child 381298 92eff00a7ed2fc84e6e3b343c46158b92b457a38
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1332207
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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'