Line data Source code
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 :
13 : #include "mozILocaleService.h"
14 :
15 : namespace mozilla {
16 : namespace intl {
17 :
18 : /**
19 : * LocaleService is a manager of language negotiation in Gecko.
20 : *
21 : * It's intended to be the core place for collecting available and
22 : * requested languages and negotiating them to produce a fallback
23 : * chain of locales for the application.
24 : *
25 : * Client / Server
26 : *
27 : * LocaleService may operate in one of two modes:
28 : *
29 : * server
30 : * in the server mode, LocaleService is collecting and negotiating
31 : * languages. It also subscribes to relevant observers.
32 : * There should be at most one server per application instance.
33 : *
34 : * client
35 : * in the client mode, LocaleService is not responsible for collecting
36 : * or reacting to any system changes. It still distributes information
37 : * about locales, but internally, it gets information from the server instance
38 : * instead of collecting it on its own.
39 : * This prevents any data desynchronization and minimizes the cost
40 : * of running the service.
41 : *
42 : * In both modes, all get* methods should work the same way and all
43 : * static methods are available.
44 : *
45 : * In the server mode, other components may inform LocaleService about their
46 : * status either via calls to set* methods or via observer events.
47 : * In the client mode, only the process communication should provide data
48 : * to the LocaleService.
49 : *
50 : * At the moment desktop apps use the parent process in the server mode, and
51 : * content processes in the client mode.
52 : *
53 : * Locale / Language
54 : *
55 : * The terms `Locale ID` and `Language ID` are used slightly differently
56 : * by different organizations. Mozilla uses the term `Language ID` to describe
57 : * a string that contains information about the language itself, script,
58 : * region and variant. For example "en-Latn-US-mac" is a correct Language ID.
59 : *
60 : * Locale ID contains a Language ID plus a number of extension tags that
61 : * contain information that go beyond language inforamation such as
62 : * preferred currency, date/time formatting etc.
63 : *
64 : * An example of a Locale ID is `en-Latn-US-x-hc-h12-ca-gregory`
65 : *
66 : * At the moment we do not support full extension tag system, but we
67 : * try to be specific when naming APIs, so the service is for locales,
68 : * but we negotiate between languages etc.
69 : */
70 : class LocaleService : public mozILocaleService,
71 : public nsIObserver
72 : {
73 : public:
74 : NS_DECL_ISUPPORTS
75 : NS_DECL_NSIOBSERVER
76 : NS_DECL_MOZILOCALESERVICE
77 :
78 : /**
79 : * List of available language negotiation strategies.
80 : *
81 : * See the mozILocaleService.idl for detailed description of the
82 : * strategies.
83 : */
84 : enum class LangNegStrategy {
85 : Filtering,
86 : Matching,
87 : Lookup
88 : };
89 :
90 : explicit LocaleService(bool aIsServer);
91 :
92 : /**
93 : * Create (if necessary) and return a raw pointer to the singleton instance.
94 : * Use this accessor in C++ code that just wants to call a method on the
95 : * instance, but does not need to hold a reference, as in
96 : * nsAutoCString str;
97 : * LocaleService::GetInstance()->GetAppLocaleAsLangTag(str);
98 : */
99 : static LocaleService* GetInstance();
100 :
101 : /**
102 : * Return an addRef'd pointer to the singleton instance. This is used by the
103 : * XPCOM constructor that exists to support usage from JS.
104 : */
105 1 : static already_AddRefed<LocaleService> GetInstanceAddRefed()
106 : {
107 1 : return RefPtr<LocaleService>(GetInstance()).forget();
108 : }
109 :
110 : /**
111 : * Returns a list of locales that the application should be localized to.
112 : *
113 : * The result is a ordered list of valid locale IDs and it should be
114 : * used for all APIs that accept list of locales, like ECMA402 and L10n APIs.
115 : *
116 : * This API always returns at least one locale.
117 : *
118 : * Example: ["en-US", "de", "pl", "sr-Cyrl", "zh-Hans-HK"]
119 : *
120 : * Usage:
121 : * nsTArray<nsCString> appLocales;
122 : * LocaleService::GetInstance()->GetAppLocalesAsLangTags(appLocales);
123 : *
124 : * (See mozILocaleService.idl for a JS-callable version of this.)
125 : */
126 : void GetAppLocalesAsLangTags(nsTArray<nsCString>& aRetVal);
127 : void GetAppLocalesAsBCP47(nsTArray<nsCString>& aRetVal);
128 :
129 :
130 : /**
131 : * Returns a list of locales to use for any regional specific operations
132 : * like date formatting, calendars, unit formatting etc.
133 : *
134 : * The result is a ordered list of valid locale IDs and it should be
135 : * used for all APIs that accept list of locales, like ECMA402 and L10n APIs.
136 : *
137 : * This API always returns at least one locale.
138 : *
139 : * Example: ["en-US", "de", "pl", "sr-Cyrl", "zh-Hans-HK"]
140 : *
141 : * Usage:
142 : * nsTArray<nsCString> rgLocales;
143 : * LocaleService::GetInstance()->GetRegionalPrefsLocales(rgLocales);
144 : *
145 : * (See mozILocaleService.idl for a JS-callable version of this.)
146 : */
147 : void GetRegionalPrefsLocales(nsTArray<nsCString>& aRetVal);
148 :
149 : /**
150 : * This method should only be called in the client mode.
151 : *
152 : * It replaces all the language negotiation and is supposed to be called
153 : * in order to bring the client LocaleService in sync with the server
154 : * LocaleService.
155 : *
156 : * Currently, it's called by the IPC code.
157 : */
158 : void AssignAppLocales(const nsTArray<nsCString>& aAppLocales);
159 : void AssignRequestedLocales(const nsTArray<nsCString>& aRequestedLocales);
160 :
161 : /**
162 : * Returns a list of locales that the user requested the app to be
163 : * localized to.
164 : *
165 : * The result is a sorted list of valid locale IDs and it should be
166 : * used as a requestedLocales input list for languages negotiation.
167 : *
168 : * Example: ["en-US", "de", "pl", "sr-Cyrl", "zh-Hans-HK"]
169 : *
170 : * Usage:
171 : * nsTArray<nsCString> reqLocales;
172 : * LocaleService::GetInstance()->GetRequestedLocales(reqLocales);
173 : *
174 : * Returns a boolean indicating if the attempt to retrieve prefs
175 : * was successful.
176 : *
177 : * (See mozILocaleService.idl for a JS-callable version of this.)
178 : */
179 : bool GetRequestedLocales(nsTArray<nsCString>& aRetVal);
180 :
181 : /**
182 : * Returns a list of available locales that can be used to
183 : * localize the app.
184 : *
185 : * The result is an unsorted list of valid locale IDs and it should be
186 : * used as a availableLocales input list for languages negotiation.
187 : *
188 : * Example: ["de", "en-US", "pl", "sr-Cyrl", "zh-Hans-HK"]
189 : *
190 : * Usage:
191 : * nsTArray<nsCString> availLocales;
192 : * LocaleService::GetInstance()->GetAvailableLocales(availLocales);
193 : *
194 : * Returns a boolean indicating if the attempt to retrieve at least
195 : * one locale was successful.
196 : *
197 : * (See mozILocaleService.idl for a JS-callable version of this.)
198 : */
199 : bool GetAvailableLocales(nsTArray<nsCString>& aRetVal);
200 :
201 : /**
202 : * Those three functions allow to trigger cache invalidation on one of the
203 : * three cached values.
204 : *
205 : * In most cases, the functions will be called by the observer in
206 : * LocaleService itself, but in a couple special cases, we have the
207 : * other component call this manually instead of sending a global event.
208 : *
209 : * If the result differs from the previous list, it will additionally
210 : * trigger a corresponding event
211 : *
212 : * This code should be called only in the server mode..
213 : */
214 : void OnAvailableLocalesChanged();
215 : void OnRequestedLocalesChanged();
216 : void OnLocalesChanged();
217 :
218 : /**
219 : * Negotiates the best locales out of an ordered list of requested locales and
220 : * a list of available locales.
221 : *
222 : * Internally it uses the following naming scheme:
223 : *
224 : * Requested - locales requested by the user
225 : * Available - locales for which the data is available
226 : * Supported - locales negotiated by the algorithm
227 : *
228 : * Additionally, if defaultLocale is provided, it adds it to the end of the
229 : * result list as a "last resort" locale.
230 : *
231 : * Strategy is one of the three strategies described at the top of this file.
232 : *
233 : * The result list is ordered according to the order of the requested locales.
234 : *
235 : * (See mozILocaleService.idl for a JS-callable version of this.)
236 : */
237 : bool NegotiateLanguages(const nsTArray<nsCString>& aRequested,
238 : const nsTArray<nsCString>& aAvailable,
239 : const nsACString& aDefaultLocale,
240 : LangNegStrategy aLangNegStrategy,
241 : nsTArray<nsCString>& aRetVal);
242 :
243 : /**
244 : * Returns whether the current app locale is RTL.
245 : */
246 : bool IsAppLocaleRTL();
247 :
248 : static bool LanguagesMatch(const nsCString& aRequested,
249 : const nsCString& aAvailable);
250 :
251 : bool IsServer();
252 :
253 : private:
254 : /**
255 : * Locale object, a BCP47-style tag decomposed into subtags for
256 : * matching purposes.
257 : *
258 : * If constructed with aRange = true, any missing subtags will be
259 : * set to "*".
260 : */
261 7 : class Locale
262 : {
263 : public:
264 : Locale(const nsCString& aLocale, bool aRange);
265 :
266 : bool Matches(const Locale& aLocale) const;
267 : bool LanguageMatches(const Locale& aLocale) const;
268 :
269 : void SetVariantRange();
270 : void SetRegionRange();
271 :
272 : bool AddLikelySubtags(); // returns false if nothing changed
273 :
274 2 : const nsCString& AsString() const {
275 2 : return mLocaleStr;
276 : }
277 :
278 2 : bool operator== (const Locale& aOther) {
279 2 : const auto& cmp = nsCaseInsensitiveCStringComparator();
280 4 : return mLanguage.Equals(aOther.mLanguage, cmp) &&
281 2 : mScript.Equals(aOther.mScript, cmp) &&
282 2 : mRegion.Equals(aOther.mRegion, cmp) &&
283 2 : mVariant.Equals(aOther.mVariant, cmp);
284 : }
285 :
286 : private:
287 : const nsCString& mLocaleStr;
288 : nsCString mLanguage;
289 : nsCString mScript;
290 : nsCString mRegion;
291 : nsCString mVariant;
292 : };
293 :
294 : void FilterMatches(const nsTArray<nsCString>& aRequested,
295 : const nsTArray<nsCString>& aAvailable,
296 : LangNegStrategy aStrategy,
297 : nsTArray<nsCString>& aRetVal);
298 :
299 : void NegotiateAppLocales(nsTArray<nsCString>& aRetVal);
300 :
301 : virtual ~LocaleService();
302 :
303 : nsTArray<nsCString> mAppLocales;
304 : nsTArray<nsCString> mRequestedLocales;
305 : nsTArray<nsCString> mAvailableLocales;
306 : const bool mIsServer;
307 :
308 : static StaticRefPtr<LocaleService> sInstance;
309 : };
310 : } // intl
311 : } // namespace mozilla
312 :
313 : #endif /* mozilla_intl_LocaleService_h__ */
|