1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #ifndef mozilla_intl_LocaleService_h__ 7 #define mozilla_intl_LocaleService_h__ 8 9 #include "nsIObserver.h" 10 #include "nsString.h" 11 #include "nsTArray.h" 12 #include "nsWeakReference.h" 13 #include "MozLocaleBindings.h" 14 #include "mozilla/intl/ICU4CGlue.h" 15 #include "mozILocaleService.h" 16 17 namespace mozilla { 18 namespace intl { 19 20 /** 21 * LocaleService is a manager of language negotiation in Gecko. 22 * 23 * It's intended to be the core place for collecting available and 24 * requested languages and negotiating them to produce a fallback 25 * chain of locales for the application. 26 * 27 * Client / Server 28 * 29 * LocaleService may operate in one of two modes: 30 * 31 * server 32 * in the server mode, LocaleService is collecting and negotiating 33 * languages. It also subscribes to relevant observers. 34 * There should be at most one server per application instance. 35 * 36 * client 37 * in the client mode, LocaleService is not responsible for collecting 38 * or reacting to any system changes. It still distributes information 39 * about locales, but internally, it gets information from the server 40 * instance instead of collecting it on its own. This prevents any data 41 * desynchronization and minimizes the cost of running the service. 42 * 43 * In both modes, all get* methods should work the same way and all 44 * static methods are available. 45 * 46 * In the server mode, other components may inform LocaleService about their 47 * status either via calls to set* methods or via observer events. 48 * In the client mode, only the process communication should provide data 49 * to the LocaleService. 50 * 51 * At the moment desktop apps use the parent process in the server mode, and 52 * content processes in the client mode. 53 * 54 * Locale / Language 55 * 56 * The terms `Locale ID` and `Language ID` are used slightly differently 57 * by different organizations. Mozilla uses the term `Language ID` to describe 58 * a string that contains information about the language itself, script, 59 * region and variant. For example "en-Latn-US-mac" is a correct Language ID. 60 * 61 * Locale ID contains a Language ID plus a number of extension tags that 62 * contain information that go beyond language inforamation such as 63 * preferred currency, date/time formatting etc. 64 * 65 * An example of a Locale ID is `en-Latn-US-x-hc-h12-ca-gregory` 66 * 67 * At the moment we do not support full extension tag system, but we 68 * try to be specific when naming APIs, so the service is for locales, 69 * but we negotiate between languages etc. 70 */ 71 class LocaleService final : public mozILocaleService, 72 public nsIObserver, 73 public nsSupportsWeakReference { 74 public: 75 NS_DECL_ISUPPORTS 76 NS_DECL_NSIOBSERVER 77 NS_DECL_MOZILOCALESERVICE 78 79 /** 80 * List of available language negotiation strategies. 81 * 82 * See the mozILocaleService.idl for detailed description of the 83 * strategies. 84 */ 85 static const int32_t kLangNegStrategyFiltering = 0; 86 static const int32_t kLangNegStrategyMatching = 1; 87 static const int32_t kLangNegStrategyLookup = 2; 88 89 explicit LocaleService(bool aIsServer); 90 91 /** 92 * Create (if necessary) and return a raw pointer to the singleton instance. 93 * Use this accessor in C++ code that just wants to call a method on the 94 * instance, but does not need to hold a reference, as in 95 * nsAutoCString str; 96 * LocaleService::GetInstance()->GetAppLocaleAsLangTag(str); 97 */ 98 static LocaleService* GetInstance(); 99 100 /** 101 * Return an addRef'd pointer to the singleton instance. This is used by the 102 * XPCOM constructor that exists to support usage from JS. 103 */ GetInstanceAddRefed()104 static already_AddRefed<LocaleService> GetInstanceAddRefed() { 105 return RefPtr<LocaleService>(GetInstance()).forget(); 106 } 107 108 /** 109 * Canonicalize a Unicode Language Identifier string. 110 * 111 * The operation is: 112 * * Normalizing casing (`eN-Us-Windows` -> `en-US-windows`) 113 * * Switching `_` to `-` (`en_US` -> `en-US`) 114 * * Rejecting invalid identifiers (`e21-X` sets aLocale to `und` and 115 * returns false) 116 * * Normalizing Mozilla's `ja-JP-mac` to `ja-JP-macos` 117 * * Cutting off POSIX dot postfix (`en-US.utf8` -> `en-US`) 118 * 119 * This operation should be used on any external input before 120 * it gets used in internal operations. 121 */ CanonicalizeLanguageId(nsACString & aLocale)122 static bool CanonicalizeLanguageId(nsACString& aLocale) { 123 return ffi::unic_langid_canonicalize(&aLocale); 124 } 125 /** 126 * This method should only be called in the client mode. 127 * 128 * It replaces all the language negotiation and is supposed to be called 129 * in order to bring the client LocaleService in sync with the server 130 * LocaleService. 131 * 132 * Currently, it's called by the IPC code. 133 */ 134 void AssignAppLocales(const nsTArray<nsCString>& aAppLocales); 135 void AssignRequestedLocales(const nsTArray<nsCString>& aRequestedLocales); 136 137 /** 138 * Those two functions allow to trigger cache invalidation on one of the 139 * three cached values. 140 * 141 * In most cases, the functions will be called by the observer in 142 * LocaleService itself, but in a couple special cases, we have the 143 * other component call this manually instead of sending a global event. 144 * 145 * If the result differs from the previous list, it will additionally 146 * trigger a corresponding event 147 * 148 * This code should be called only in the server mode.. 149 */ 150 void RequestedLocalesChanged(); 151 void LocalesChanged(); 152 153 /** 154 * This function keeps the pref setting updated. 155 */ 156 void WebExposedLocalesChanged(); 157 158 /** 159 * Returns whether the locale is RTL. 160 */ 161 static bool IsLocaleRTL(const nsACString& aLocale); 162 163 /** 164 * Returns whether the current app locale is RTL. 165 * 166 * This method respects this override: 167 * - `intl.l10n.pseudo` 168 */ 169 bool IsAppLocaleRTL(); 170 171 static bool LanguagesMatch(const nsACString& aRequested, 172 const nsACString& aAvailable); 173 174 bool IsServer(); 175 176 /** 177 * Create a component from intl/components with the current app's locale. This 178 * is a convenience method for efficient string management with the app 179 * locale. 180 */ 181 template <typename T, typename... Args> TryCreateComponent(Args...args)182 static Result<UniquePtr<T>, ICUError> TryCreateComponent(Args... args) { 183 // 32 is somewhat arbitrary for the length, but it should fit common 184 // locales, but locales such as the following will be heap allocated: 185 // 186 // "de-u-ca-gregory-fw-mon-hc-h23-co-phonebk-ka-noignore-kb-false-kc- 187 // false-kf-false-kh-false-kk-false-kn-false-kr-space-ks-level1-kv-space-cf- 188 // standard-cu-eur-ms-metric-nu-latn-lb-strict-lw-normal-ss-none-tz-atvie-em- 189 // default-rg-atzzzz-sd-atat1-va-posix" 190 nsAutoCStringN<32> appLocale; 191 mozilla::intl::LocaleService::GetInstance()->GetAppLocaleAsBCP47(appLocale); 192 193 return T::TryCreate(appLocale.get(), args...); 194 } 195 196 /** 197 * Create a component from intl/components with a given locale, but fallback 198 * to the app locale if it doesn't work. 199 */ 200 template <typename T, typename... Args> TryCreateComponentWithLocale(const char * aLocale,Args...args)201 static Result<UniquePtr<T>, ICUError> TryCreateComponentWithLocale( 202 const char* aLocale, Args... args) { 203 auto result = T::TryCreate(aLocale, args...); 204 if (result.isOk()) { 205 return result; 206 } 207 return TryCreateComponent<T>(args...); 208 } 209 210 private: 211 void NegotiateAppLocales(nsTArray<nsCString>& aRetVal); 212 213 void InitPackagedLocales(); 214 215 void RemoveObservers(); 216 217 virtual ~LocaleService() = default; 218 219 nsAutoCStringN<16> mDefaultLocale; 220 nsTArray<nsCString> mAppLocales; 221 nsTArray<nsCString> mRequestedLocales; 222 nsTArray<nsCString> mAvailableLocales; 223 nsTArray<nsCString> mPackagedLocales; 224 nsTArray<nsCString> mWebExposedLocales; 225 const bool mIsServer; 226 227 static StaticRefPtr<LocaleService> sInstance; 228 }; 229 } // namespace intl 230 } // namespace mozilla 231 232 #endif /* mozilla_intl_LocaleService_h__ */ 233