зеркало из https://github.com/mozilla/gecko-dev.git
219 строки
7.5 KiB
C++
219 строки
7.5 KiB
C++
/* -*- 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 "nsIObserver.h"
|
|
#include "nsString.h"
|
|
#include "nsTArray.h"
|
|
#include "nsWeakReference.h"
|
|
#include "MozLocaleBindings.h"
|
|
#include "mozilla/intl/ICU4CGlue.h"
|
|
#include "mozILocaleService.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.
|
|
*
|
|
* Client / Server
|
|
*
|
|
* LocaleService may operate in one of two modes:
|
|
*
|
|
* server
|
|
* in the server mode, LocaleService is collecting and negotiating
|
|
* languages. It also subscribes to relevant observers.
|
|
* There should be at most one server per application instance.
|
|
*
|
|
* client
|
|
* in the client mode, LocaleService is not responsible for collecting
|
|
* or reacting to any system changes. It still distributes information
|
|
* about locales, but internally, it gets information from the server
|
|
* instance instead of collecting it on its own. This prevents any data
|
|
* desynchronization and minimizes the cost of running the service.
|
|
*
|
|
* In both modes, all get* methods should work the same way and all
|
|
* static methods are available.
|
|
*
|
|
* In the server mode, other components may inform LocaleService about their
|
|
* status either via calls to set* methods or via observer events.
|
|
* In the client mode, only the process communication should provide data
|
|
* to the LocaleService.
|
|
*
|
|
* At the moment desktop apps use the parent process in the server mode, and
|
|
* content processes in the client mode.
|
|
*
|
|
* Locale / Language
|
|
*
|
|
* The terms `Locale ID` and `Language ID` are used slightly differently
|
|
* by different organizations. Mozilla uses the term `Language ID` to describe
|
|
* a string that contains information about the language itself, script,
|
|
* region and variant. For example "en-Latn-US-mac" is a correct Language ID.
|
|
*
|
|
* Locale ID contains a Language ID plus a number of extension tags that
|
|
* contain information that go beyond language inforamation such as
|
|
* preferred currency, date/time formatting etc.
|
|
*
|
|
* An example of a Locale ID is `en-Latn-US-x-hc-h12-ca-gregory`
|
|
*
|
|
* At the moment we do not support full extension tag system, but we
|
|
* try to be specific when naming APIs, so the service is for locales,
|
|
* but we negotiate between languages etc.
|
|
*/
|
|
class LocaleService final : public mozILocaleService,
|
|
public nsIObserver,
|
|
public nsSupportsWeakReference {
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIOBSERVER
|
|
NS_DECL_MOZILOCALESERVICE
|
|
|
|
/**
|
|
* List of available language negotiation strategies.
|
|
*
|
|
* See the mozILocaleService.idl for detailed description of the
|
|
* strategies.
|
|
*/
|
|
static const int32_t kLangNegStrategyFiltering = 0;
|
|
static const int32_t kLangNegStrategyMatching = 1;
|
|
static const int32_t kLangNegStrategyLookup = 2;
|
|
|
|
explicit LocaleService(bool aIsServer);
|
|
|
|
/**
|
|
* Create (if necessary) and return a raw pointer to the singleton instance.
|
|
* Use this accessor in C++ code that just wants to call a method on the
|
|
* instance, but does not need to hold a reference, as in
|
|
* nsAutoCString str;
|
|
* LocaleService::GetInstance()->GetAppLocaleAsLangTag(str);
|
|
*/
|
|
static LocaleService* GetInstance();
|
|
|
|
/**
|
|
* Return an addRef'd pointer to the singleton instance. This is used by the
|
|
* XPCOM constructor that exists to support usage from JS.
|
|
*/
|
|
static already_AddRefed<LocaleService> GetInstanceAddRefed() {
|
|
return RefPtr<LocaleService>(GetInstance()).forget();
|
|
}
|
|
|
|
/**
|
|
* Canonicalize a Unicode Language Identifier string.
|
|
*
|
|
* The operation is:
|
|
* * Normalizing casing (`eN-Us-Windows` -> `en-US-windows`)
|
|
* * Switching `_` to `-` (`en_US` -> `en-US`)
|
|
* * Rejecting invalid identifiers (`e21-X` sets aLocale to `und` and
|
|
* returns false)
|
|
* * Normalizing Mozilla's `ja-JP-mac` to `ja-JP-macos`
|
|
* * Cutting off POSIX dot postfix (`en-US.utf8` -> `en-US`)
|
|
*
|
|
* This operation should be used on any external input before
|
|
* it gets used in internal operations.
|
|
*/
|
|
static bool CanonicalizeLanguageId(nsACString& aLocale) {
|
|
return ffi::unic_langid_canonicalize(&aLocale);
|
|
}
|
|
/**
|
|
* This method should only be called in the client mode.
|
|
*
|
|
* It replaces all the language negotiation and is supposed to be called
|
|
* in order to bring the client LocaleService in sync with the server
|
|
* LocaleService.
|
|
*
|
|
* Currently, it's called by the IPC code.
|
|
*/
|
|
void AssignAppLocales(const nsTArray<nsCString>& aAppLocales);
|
|
void AssignRequestedLocales(const nsTArray<nsCString>& aRequestedLocales);
|
|
|
|
/**
|
|
* Those two functions allow to trigger cache invalidation on one of the
|
|
* three cached values.
|
|
*
|
|
* In most cases, the functions will be called by the observer in
|
|
* LocaleService itself, but in a couple special cases, we have the
|
|
* other component call this manually instead of sending a global event.
|
|
*
|
|
* If the result differs from the previous list, it will additionally
|
|
* trigger a corresponding event
|
|
*
|
|
* This code should be called only in the server mode..
|
|
*/
|
|
void RequestedLocalesChanged();
|
|
void LocalesChanged();
|
|
|
|
/**
|
|
* This function keeps the pref setting updated.
|
|
*/
|
|
void WebExposedLocalesChanged();
|
|
|
|
/**
|
|
* Returns whether the locale is RTL.
|
|
*/
|
|
static bool IsLocaleRTL(const nsACString& aLocale);
|
|
|
|
/**
|
|
* Returns whether the current app locale is RTL.
|
|
*
|
|
* This method respects this override:
|
|
* - `intl.l10n.pseudo`
|
|
*/
|
|
bool IsAppLocaleRTL();
|
|
|
|
static bool LanguagesMatch(const nsACString& aRequested,
|
|
const nsACString& aAvailable);
|
|
|
|
bool IsServer();
|
|
|
|
/**
|
|
* Create a component from intl/components with the current app's locale. This
|
|
* is a convenience method for efficient string management with the app
|
|
* locale.
|
|
*/
|
|
template <typename T, typename... Args>
|
|
static Result<UniquePtr<T>, ICUError> TryCreateComponent(Args... args) {
|
|
// 32 is somewhat arbitrary for the length, but it should fit common
|
|
// locales, but locales such as the following will be heap allocated:
|
|
//
|
|
// "de-u-ca-gregory-fw-mon-hc-h23-co-phonebk-ka-noignore-kb-false-kc-
|
|
// false-kf-false-kh-false-kk-false-kn-false-kr-space-ks-level1-kv-space-cf-
|
|
// standard-cu-eur-ms-metric-nu-latn-lb-strict-lw-normal-ss-none-tz-atvie-em-
|
|
// default-rg-atzzzz-sd-atat1-va-posix"
|
|
nsAutoCStringN<32> appLocale;
|
|
mozilla::intl::LocaleService::GetInstance()->GetAppLocaleAsBCP47(appLocale);
|
|
|
|
return T::TryCreate(appLocale.get(), args...);
|
|
}
|
|
|
|
private:
|
|
void NegotiateAppLocales(nsTArray<nsCString>& aRetVal);
|
|
|
|
void InitPackagedLocales();
|
|
|
|
void RemoveObservers();
|
|
|
|
virtual ~LocaleService() = default;
|
|
|
|
nsAutoCStringN<16> mDefaultLocale;
|
|
nsTArray<nsCString> mAppLocales;
|
|
nsTArray<nsCString> mRequestedLocales;
|
|
nsTArray<nsCString> mAvailableLocales;
|
|
nsTArray<nsCString> mPackagedLocales;
|
|
nsTArray<nsCString> mWebExposedLocales;
|
|
const bool mIsServer;
|
|
|
|
static StaticRefPtr<LocaleService> sInstance;
|
|
};
|
|
} // namespace intl
|
|
} // namespace mozilla
|
|
|
|
#endif /* mozilla_intl_LocaleService_h__ */
|