Bug 1491394 - Migrate mozILocaleService to use Array<> interface. r=jfkthame

Differential Revision: https://phabricator.services.mozilla.com/D5924

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Zibi Braniecki 2018-09-21 15:30:24 +00:00
parent b4e5b102e0
commit 8fdbb6d26b
6 changed files with 201 additions and 526 deletions

View File

@ -97,7 +97,7 @@ SplitLocaleListStringIntoArray(nsACString& str, nsTArray<nsCString>& aRetVal)
}
}
static bool
static void
ReadRequestedLocales(nsTArray<nsCString>& aRetVal)
{
nsAutoCString str;
@ -116,13 +116,16 @@ ReadRequestedLocales(nsTArray<nsCString>& aRetVal)
} else {
SplitLocaleListStringIntoArray(str, aRetVal);
}
} else {
}
// This will happen when either the pref is not set,
// or parsing of the pref didn't produce any usable
// result.
if (aRetVal.IsEmpty()) {
nsAutoCString defaultLocale;
LocaleService::GetInstance()->GetDefaultLocale(defaultLocale);
aRetVal.AppendElement(defaultLocale);
}
return true;
}
LocaleService::LocaleService(bool aIsServer)
@ -148,7 +151,7 @@ LocaleService::NegotiateAppLocales(nsTArray<nsCString>& aRetVal)
GetRequestedLocales(requestedLocales);
NegotiateLanguages(requestedLocales, availableLocales, defaultLocale,
LangNegStrategy::Filtering, aRetVal);
kLangNegStrategyFiltering, aRetVal);
}
nsAutoCString lastFallbackLocale;
@ -202,72 +205,6 @@ LocaleService::~LocaleService()
}
}
void
LocaleService::GetAppLocalesAsLangTags(nsTArray<nsCString>& aRetVal)
{
if (mAppLocales.IsEmpty()) {
NegotiateAppLocales(mAppLocales);
}
for (uint32_t i = 0; i < mAppLocales.Length(); i++) {
nsAutoCString locale(mAppLocales[i]);
if (locale.LowerCaseEqualsASCII("ja-jp-macos")) {
aRetVal.AppendElement("ja-JP-mac");
} else {
aRetVal.AppendElement(locale);
}
}
}
void
LocaleService::GetAppLocalesAsBCP47(nsTArray<nsCString>& aRetVal)
{
if (mAppLocales.IsEmpty()) {
NegotiateAppLocales(mAppLocales);
}
aRetVal = mAppLocales;
}
void
LocaleService::GetRegionalPrefsLocales(nsTArray<nsCString>& aRetVal)
{
bool useOSLocales = Preferences::GetBool("intl.regional_prefs.use_os_locales", false);
// If the user specified that they want to use OS Regional Preferences locales,
// try to retrieve them and use.
if (useOSLocales) {
if (OSPreferences::GetInstance()->GetRegionalPrefsLocales(aRetVal)) {
return;
}
// If we fail to retrieve them, return the app locales.
GetAppLocalesAsBCP47(aRetVal);
return;
}
// Otherwise, fetch OS Regional Preferences locales and compare the first one
// to the app locale. If the language subtag matches, we can safely use
// the OS Regional Preferences locale.
//
// This facilitates scenarios such as Firefox in "en-US" and User sets
// regional prefs to "en-GB".
nsAutoCString appLocale;
AutoTArray<nsCString, 10> regionalPrefsLocales;
LocaleService::GetInstance()->GetAppLocaleAsBCP47(appLocale);
if (!OSPreferences::GetInstance()->GetRegionalPrefsLocales(regionalPrefsLocales)) {
GetAppLocalesAsBCP47(aRetVal);
return;
}
if (LocaleService::LanguagesMatch(appLocale, regionalPrefsLocales[0])) {
aRetVal = regionalPrefsLocales;
return;
}
// Otherwise use the app locales.
GetAppLocalesAsBCP47(aRetVal);
}
void
LocaleService::AssignAppLocales(const nsTArray<nsCString>& aAppLocales)
{
@ -292,36 +229,6 @@ LocaleService::AssignRequestedLocales(const nsTArray<nsCString>& aRequestedLocal
}
}
bool
LocaleService::GetRequestedLocales(nsTArray<nsCString>& aRetVal)
{
if (mRequestedLocales.IsEmpty()) {
ReadRequestedLocales(mRequestedLocales);
}
aRetVal = mRequestedLocales;
return true;
}
bool
LocaleService::GetAvailableLocales(nsTArray<nsCString>& aRetVal)
{
MOZ_ASSERT(mIsServer, "This should only be called in the server mode.");
if (!mIsServer) {
return false;
}
if (mAvailableLocales.IsEmpty()) {
// If there are no available locales set, it means that L10nRegistry
// did not register its locale pool yet. The best course of action
// is to use packaged locales until that happens.
GetPackagedLocales(mAvailableLocales);
}
aRetVal = mAvailableLocales;
return true;
}
void
LocaleService::RequestedLocalesChanged()
{
@ -368,11 +275,11 @@ LocaleService::LocalesChanged()
// according to the desired negotiation strategy.
#define HANDLE_STRATEGY \
switch (aStrategy) { \
case LangNegStrategy::Lookup: \
case kLangNegStrategyLookup: \
return; \
case LangNegStrategy::Matching: \
case kLangNegStrategyMatching: \
continue; \
case LangNegStrategy::Filtering: \
case kLangNegStrategyFiltering: \
break; \
}
@ -420,7 +327,7 @@ LocaleService::LocalesChanged()
void
LocaleService::FilterMatches(const nsTArray<nsCString>& aRequested,
const nsTArray<nsCString>& aAvailable,
LangNegStrategy aStrategy,
int32_t aStrategy,
nsTArray<nsCString>& aRetVal)
{
// Local copy of the list of available locales, in Locale form for flexible
@ -434,7 +341,6 @@ LocaleService::FilterMatches(const nsTArray<nsCString>& aRequested,
for (auto& requested : aRequested) {
if (requested.IsEmpty()) {
MOZ_ASSERT(!requested.IsEmpty(), "Locale string cannot be empty.");
continue;
}
@ -466,7 +372,7 @@ LocaleService::FilterMatches(const nsTArray<nsCString>& aRequested,
aRetVal.AppendElement(aAvailable[match - availLocales.begin()]);
match->Invalidate();
foundMatch = true;
if (aStrategy != LangNegStrategy::Filtering) {
if (aStrategy != kLangNegStrategyFiltering) {
return true; // we only want the first match
}
}
@ -507,43 +413,6 @@ LocaleService::FilterMatches(const nsTArray<nsCString>& aRequested,
}
}
void
LocaleService::NegotiateLanguages(const nsTArray<nsCString>& aRequested,
const nsTArray<nsCString>& aAvailable,
const nsACString& aDefaultLocale,
LangNegStrategy aStrategy,
nsTArray<nsCString>& aRetVal)
{
MOZ_ASSERT(aDefaultLocale.IsEmpty() || Locale(aDefaultLocale).IsWellFormed(),
"If specified, default locale must be a well-formed BCP47 language tag.");
if (aStrategy == LangNegStrategy::Lookup && aDefaultLocale.IsEmpty()) {
NS_WARNING("Default locale should be specified when using lookup strategy.");
}
FilterMatches(aRequested, aAvailable, aStrategy, aRetVal);
if (aStrategy == LangNegStrategy::Lookup) {
// If the strategy is Lookup and Filtering returned no matches, use
// the default locale.
if (aRetVal.Length() == 0) {
// If the default locale is empty, we already issued a warning, so
// now we will just pick up the LocaleService's defaultLocale.
if (aDefaultLocale.IsEmpty()) {
nsAutoCString defaultLocale;
GetDefaultLocale(defaultLocale);
aRetVal.AppendElement(defaultLocale);
} else {
aRetVal.AppendElement(aDefaultLocale);
}
}
} else if (!aDefaultLocale.IsEmpty() && !aRetVal.Contains(aDefaultLocale)) {
// If it's not a Lookup strategy, add the default locale only if it's
// set and it's not in the results already.
aRetVal.AppendElement(aDefaultLocale);
}
}
bool
LocaleService::IsAppLocaleRTL()
{
@ -593,17 +462,6 @@ LocaleService::IsServer()
return mIsServer;
}
static char**
CreateOutArray(const nsTArray<nsCString>& aArray)
{
uint32_t n = aArray.Length();
char** result = static_cast<char**>(moz_xmalloc(n * sizeof(char*)));
for (uint32_t i = 0; i < n; i++) {
result[i] = moz_xstrdup(aArray[i].get());
}
return result;
}
static bool
GetGREFileContents(const char* aFilePath, nsCString* aOutString)
{
@ -673,15 +531,6 @@ LocaleService::InitPackagedLocales()
}
}
void
LocaleService::GetPackagedLocales(nsTArray<nsCString>& aRetVal)
{
if (mPackagedLocales.IsEmpty()) {
InitPackagedLocales();
}
aRetVal = mPackagedLocales;
}
/**
* mozILocaleService methods
*/
@ -723,25 +572,30 @@ LocaleService::GetLastFallbackLocale(nsACString& aRetVal)
}
NS_IMETHODIMP
LocaleService::GetAppLocalesAsLangTags(uint32_t* aCount, char*** aOutArray)
{
AutoTArray<nsCString, 32> locales;
GetAppLocalesAsLangTags(locales);
*aCount = locales.Length();
*aOutArray = CreateOutArray(locales);
return NS_OK;
}
NS_IMETHODIMP
LocaleService::GetAppLocalesAsBCP47(uint32_t* aCount, char*** aOutArray)
LocaleService::GetAppLocalesAsLangTags(nsTArray<nsCString>& aRetVal)
{
if (mAppLocales.IsEmpty()) {
NegotiateAppLocales(mAppLocales);
}
*aCount = mAppLocales.Length();
*aOutArray = CreateOutArray(mAppLocales);
for (uint32_t i = 0; i < mAppLocales.Length(); i++) {
nsAutoCString locale(mAppLocales[i]);
if (locale.LowerCaseEqualsASCII("ja-jp-macos")) {
aRetVal.AppendElement("ja-JP-mac");
} else {
aRetVal.AppendElement(locale);
}
}
return NS_OK;
}
NS_IMETHODIMP
LocaleService::GetAppLocalesAsBCP47(nsTArray<nsCString>& aRetVal)
{
if (mAppLocales.IsEmpty()) {
NegotiateAppLocales(mAppLocales);
}
aRetVal = mAppLocales;
return NS_OK;
}
@ -767,137 +621,116 @@ LocaleService::GetAppLocaleAsBCP47(nsACString& aRetVal)
}
NS_IMETHODIMP
LocaleService::GetRegionalPrefsLocales(uint32_t* aCount, char*** aOutArray)
LocaleService::GetRegionalPrefsLocales(nsTArray<nsCString>& aRetVal)
{
AutoTArray<nsCString,10> rgLocales;
bool useOSLocales = Preferences::GetBool("intl.regional_prefs.use_os_locales", false);
GetRegionalPrefsLocales(rgLocales);
// If the user specified that they want to use OS Regional Preferences locales,
// try to retrieve them and use.
if (useOSLocales) {
if (OSPreferences::GetInstance()->GetRegionalPrefsLocales(aRetVal)) {
return NS_OK;
}
*aCount = rgLocales.Length();
*aOutArray = static_cast<char**>(moz_xmalloc(*aCount * sizeof(char*)));
for (uint32_t i = 0; i < *aCount; i++) {
(*aOutArray)[i] = moz_xstrdup(rgLocales[i].get());
// If we fail to retrieve them, return the app locales.
GetAppLocalesAsBCP47(aRetVal);
return NS_OK;
}
// Otherwise, fetch OS Regional Preferences locales and compare the first one
// to the app locale. If the language subtag matches, we can safely use
// the OS Regional Preferences locale.
//
// This facilitates scenarios such as Firefox in "en-US" and User sets
// regional prefs to "en-GB".
nsAutoCString appLocale;
AutoTArray<nsCString, 10> regionalPrefsLocales;
LocaleService::GetInstance()->GetAppLocaleAsBCP47(appLocale);
if (!OSPreferences::GetInstance()->GetRegionalPrefsLocales(regionalPrefsLocales)) {
GetAppLocalesAsBCP47(aRetVal);
return NS_OK;
}
if (LocaleService::LanguagesMatch(appLocale, regionalPrefsLocales[0])) {
aRetVal = regionalPrefsLocales;
return NS_OK;
}
// Otherwise use the app locales.
GetAppLocalesAsBCP47(aRetVal);
return NS_OK;
}
static LocaleService::LangNegStrategy
ToLangNegStrategy(int32_t aStrategy)
{
switch (aStrategy) {
case 1:
return LocaleService::LangNegStrategy::Matching;
case 2:
return LocaleService::LangNegStrategy::Lookup;
default:
return LocaleService::LangNegStrategy::Filtering;
}
}
NS_IMETHODIMP
LocaleService::NegotiateLanguages(const char** aRequested,
const char** aAvailable,
const char* aDefaultLocale,
LocaleService::NegotiateLanguages(const nsTArray<nsCString>& aRequested,
const nsTArray<nsCString>& aAvailable,
const nsACString& aDefaultLocale,
int32_t aStrategy,
uint32_t aRequestedCount,
uint32_t aAvailableCount,
uint32_t* aCount, char*** aRetVal)
nsTArray<nsCString>& aRetVal)
{
if (aStrategy < 0 || aStrategy > 2) {
return NS_ERROR_INVALID_ARG;
}
// Check that the given string contains only ASCII characters valid in tags
// (i.e. alphanumerics, plus '-' and '_'), and is non-empty.
auto validTagChars = [](const char* s) {
if (!s || !*s) {
return false;
}
while (*s) {
if (isalnum((unsigned char)*s) || *s == '-' || *s == '_' || *s == '*') {
s++;
MOZ_ASSERT(aDefaultLocale.IsEmpty() || Locale(aDefaultLocale).IsWellFormed(),
"If specified, default locale must be a well-formed BCP47 language tag.");
if (aStrategy == kLangNegStrategyLookup && aDefaultLocale.IsEmpty()) {
NS_WARNING("Default locale should be specified when using lookup strategy.");
}
FilterMatches(aRequested, aAvailable, aStrategy, aRetVal);
if (aStrategy == kLangNegStrategyLookup) {
// If the strategy is Lookup and Filtering returned no matches, use
// the default locale.
if (aRetVal.Length() == 0) {
// If the default locale is empty, we already issued a warning, so
// now we will just pick up the LocaleService's defaultLocale.
if (aDefaultLocale.IsEmpty()) {
nsAutoCString defaultLocale;
GetDefaultLocale(defaultLocale);
aRetVal.AppendElement(defaultLocale);
} else {
return false;
aRetVal.AppendElement(aDefaultLocale);
}
}
return true;
};
AutoTArray<nsCString, 100> requestedLocales;
for (uint32_t i = 0; i < aRequestedCount; i++) {
if (!validTagChars(aRequested[i])) {
continue;
}
requestedLocales.AppendElement(aRequested[i]);
} else if (!aDefaultLocale.IsEmpty() && !aRetVal.Contains(aDefaultLocale)) {
// If it's not a Lookup strategy, add the default locale only if it's
// set and it's not in the results already.
aRetVal.AppendElement(aDefaultLocale);
}
AutoTArray<nsCString, 100> availableLocales;
for (uint32_t i = 0; i < aAvailableCount; i++) {
if (!validTagChars(aAvailable[i])) {
continue;
}
availableLocales.AppendElement(aAvailable[i]);
}
nsAutoCString defaultLocale(aDefaultLocale);
LangNegStrategy strategy = ToLangNegStrategy(aStrategy);
AutoTArray<nsCString, 100> supportedLocales;
NegotiateLanguages(requestedLocales, availableLocales,
defaultLocale, strategy, supportedLocales);
*aRetVal =
static_cast<char**>(moz_xmalloc(sizeof(char*) * supportedLocales.Length()));
*aCount = 0;
for (const auto& supported : supportedLocales) {
(*aRetVal)[(*aCount)++] = moz_xstrdup(supported.get());
}
return NS_OK;
}
NS_IMETHODIMP
LocaleService::GetRequestedLocales(uint32_t* aCount, char*** aOutArray)
LocaleService::GetRequestedLocales(nsTArray<nsCString>& aRetVal)
{
AutoTArray<nsCString, 16> requestedLocales;
bool res = GetRequestedLocales(requestedLocales);
if (!res) {
NS_ERROR("Couldn't retrieve selected locales from prefs!");
return NS_ERROR_FAILURE;
if (mRequestedLocales.IsEmpty()) {
ReadRequestedLocales(mRequestedLocales);
}
*aCount = requestedLocales.Length();
*aOutArray = CreateOutArray(requestedLocales);
aRetVal = mRequestedLocales;
return NS_OK;
}
NS_IMETHODIMP
LocaleService::GetRequestedLocale(nsACString& aRetVal)
{
AutoTArray<nsCString, 16> requestedLocales;
bool res = GetRequestedLocales(requestedLocales);
if (!res) {
NS_ERROR("Couldn't retrieve selected locales from prefs!");
return NS_ERROR_FAILURE;
if (mRequestedLocales.IsEmpty()) {
ReadRequestedLocales(mRequestedLocales);
}
if (requestedLocales.Length() > 0) {
aRetVal = requestedLocales[0];
if (mRequestedLocales.Length() > 0) {
aRetVal = mRequestedLocales[0];
}
return NS_OK;
}
NS_IMETHODIMP
LocaleService::SetRequestedLocales(const char** aRequested,
uint32_t aRequestedCount)
LocaleService::SetRequestedLocales(const nsTArray<nsCString>& aRequested)
{
MOZ_ASSERT(mIsServer, "This should only be called in the server mode.");
if (!mIsServer) {
@ -906,14 +739,14 @@ LocaleService::SetRequestedLocales(const char** aRequested,
nsAutoCString str;
for (uint32_t i = 0; i < aRequestedCount; i++) {
nsAutoCString locale(aRequested[i]);
for (auto& req : aRequested) {
nsAutoCString locale(req);
if (!SanitizeForBCP47(locale, true)) {
NS_ERROR("Invalid language tag provided to SetRequestedLocales!");
return NS_ERROR_INVALID_ARG;
}
if (i > 0) {
if (!str.IsEmpty()) {
str.AppendLiteral(",");
}
str.Append(locale);
@ -924,18 +757,21 @@ LocaleService::SetRequestedLocales(const char** aRequested,
}
NS_IMETHODIMP
LocaleService::GetAvailableLocales(uint32_t* aCount, char*** aOutArray)
LocaleService::GetAvailableLocales(nsTArray<nsCString>& aRetVal)
{
AutoTArray<nsCString, 100> availableLocales;
bool res = GetAvailableLocales(availableLocales);
if (!res) {
NS_ERROR("Couldn't retrieve available locales!");
return NS_ERROR_FAILURE;
MOZ_ASSERT(mIsServer, "This should only be called in the server mode.");
if (!mIsServer) {
return NS_ERROR_UNEXPECTED;
}
*aCount = availableLocales.Length();
*aOutArray = CreateOutArray(availableLocales);
if (mAvailableLocales.IsEmpty()) {
// If there are no available locales set, it means that L10nRegistry
// did not register its locale pool yet. The best course of action
// is to use packaged locales until that happens.
GetPackagedLocales(mAvailableLocales);
}
aRetVal = mAvailableLocales;
return NS_OK;
}
@ -947,8 +783,7 @@ LocaleService::GetIsAppLocaleRTL(bool* aRetVal)
}
NS_IMETHODIMP
LocaleService::SetAvailableLocales(const char** aAvailable,
uint32_t aAvailableCount)
LocaleService::SetAvailableLocales(const nsTArray<nsCString>& aAvailable)
{
MOZ_ASSERT(mIsServer, "This should only be called in the server mode.");
if (!mIsServer) {
@ -957,8 +792,8 @@ LocaleService::SetAvailableLocales(const char** aAvailable,
nsTArray<nsCString> newLocales;
for (uint32_t i = 0; i < aAvailableCount; i++) {
nsAutoCString locale(aAvailable[i]);
for (auto& avail : aAvailable) {
nsAutoCString locale(avail);
if (!SanitizeForBCP47(locale, true)) {
NS_ERROR("Invalid language tag provided to SetAvailableLocales!");
return NS_ERROR_INVALID_ARG;
@ -975,14 +810,11 @@ LocaleService::SetAvailableLocales(const char** aAvailable,
}
NS_IMETHODIMP
LocaleService::GetPackagedLocales(uint32_t* aCount, char*** aOutArray)
LocaleService::GetPackagedLocales(nsTArray<nsCString>& aRetVal)
{
if (mPackagedLocales.IsEmpty()) {
InitPackagedLocales();
}
*aCount = mPackagedLocales.Length();
*aOutArray = CreateOutArray(mPackagedLocales);
aRetVal = mPackagedLocales;
return NS_OK;
}

View File

@ -83,11 +83,9 @@ public:
* See the mozILocaleService.idl for detailed description of the
* strategies.
*/
enum class LangNegStrategy {
Filtering,
Matching,
Lookup
};
static const int32_t kLangNegStrategyFiltering = 0;
static const int32_t kLangNegStrategyMatching = 1;
static const int32_t kLangNegStrategyLookup = 2;
explicit LocaleService(bool aIsServer);
@ -109,45 +107,6 @@ public:
return RefPtr<LocaleService>(GetInstance()).forget();
}
/**
* Returns a list of locales that the application should be localized to.
*
* The result is a ordered 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()->GetAppLocalesAsLangTags(appLocales);
*
* (See mozILocaleService.idl for a JS-callable version of this.)
*/
void GetAppLocalesAsLangTags(nsTArray<nsCString>& aRetVal);
void GetAppLocalesAsBCP47(nsTArray<nsCString>& aRetVal);
/**
* Returns a list of locales to use for any regional specific operations
* like date formatting, calendars, unit formatting etc.
*
* The result is a ordered 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> rgLocales;
* LocaleService::GetInstance()->GetRegionalPrefsLocales(rgLocales);
*
* (See mozILocaleService.idl for a JS-callable version of this.)
*/
void GetRegionalPrefsLocales(nsTArray<nsCString>& aRetVal);
/**
* This method should only be called in the client mode.
*
@ -160,53 +119,6 @@ public:
void AssignAppLocales(const nsTArray<nsCString>& aAppLocales);
void AssignRequestedLocales(const nsTArray<nsCString>& aRequestedLocales);
/**
* Returns a list of locales that the user requested the app to be
* localized to.
*
* The result is a sorted list of valid locale IDs and it should be
* used as a requestedLocales input list for languages negotiation.
*
* Example: ["en-US", "de", "pl", "sr-Cyrl", "zh-Hans-HK"]
*
* Usage:
* nsTArray<nsCString> reqLocales;
* LocaleService::GetInstance()->GetRequestedLocales(reqLocales);
*
* Returns a boolean indicating if the attempt to retrieve prefs
* was successful.
*
* (See mozILocaleService.idl for a JS-callable version of this.)
*/
bool GetRequestedLocales(nsTArray<nsCString>& aRetVal);
/**
* Returns a list of available locales that can be used to
* localize the app.
*
* The result is an unsorted list of valid locale IDs and it should be
* used as a availableLocales input list for languages negotiation.
*
* Example: ["de", "en-US", "pl", "sr-Cyrl", "zh-Hans-HK"]
*
* Usage:
* nsTArray<nsCString> availLocales;
* LocaleService::GetInstance()->GetAvailableLocales(availLocales);
*
* Returns a boolean indicating if the attempt to retrieve at least
* one locale was successful.
*
* (See mozILocaleService.idl for a JS-callable version of this.)
*/
bool GetAvailableLocales(nsTArray<nsCString>& aRetVal);
/**
* Returns a list of locales packaged into the app bundle.
*
* (See mozILocaleService.idl for a JS-callable version of this.)
*/
void GetPackagedLocales(nsTArray<nsCString>& aRetVal);
/**
* Those two functions allow to trigger cache invalidation on one of the
* three cached values.
@ -223,32 +135,6 @@ public:
void RequestedLocalesChanged();
void LocalesChanged();
/**
* Negotiates the best locales out of an ordered list of requested locales and
* a list of available locales.
*
* Internally it uses the following naming scheme:
*
* Requested - locales requested by the user
* Available - locales for which the data is available
* Supported - locales negotiated by the algorithm
*
* Additionally, if defaultLocale is provided, it adds it to the end of the
* result list as a "last resort" locale.
*
* Strategy is one of the three strategies described at the top of this file.
*
* The result list is canonicalized and ordered according to the order
* of the requested locales.
*
* (See mozILocaleService.idl for a JS-callable version of this.)
*/
void NegotiateLanguages(const nsTArray<nsCString>& aRequested,
const nsTArray<nsCString>& aAvailable,
const nsACString& aDefaultLocale,
LangNegStrategy aLangNegStrategy,
nsTArray<nsCString>& aRetVal);
/**
* Returns whether the current app locale is RTL.
*/
@ -262,7 +148,7 @@ public:
private:
void FilterMatches(const nsTArray<nsCString>& aRequested,
const nsTArray<nsCString>& aAvailable,
LangNegStrategy aStrategy,
int32_t aStrategy,
nsTArray<nsCString>& aRetVal);
void NegotiateAppLocales(nsTArray<nsCString>& aRetVal);

View File

@ -80,13 +80,9 @@ interface mozILocaleService : nsISupports
* use the BCP47 form.
*
* Example: ["en-US", "de", "pl", "sr-Cyrl", "zh-Hans-HK"]
*
* (See LocaleService.h for a more C++-friendly version of this.)
*/
void getAppLocalesAsLangTags([optional] out unsigned long aCount,
[retval, array, size_is(aCount)] out string aLocales);
void getAppLocalesAsBCP47([optional] out unsigned long aCount,
[retval, array, size_is(aCount)] out string aLocales);
readonly attribute Array<ACString> appLocalesAsLangTags;
readonly attribute Array<ACString> appLocalesAsBCP47;
/**
* Returns a list of locales to use for any regional specific operations
@ -98,11 +94,8 @@ interface mozILocaleService : nsISupports
* This API always returns at least one locale.
*
* Example: ["en-US", "de", "pl", "sr-Cyrl", "zh-Hans-HK"]
*
* (See LocaleService.h for a more C++-friendly version of this.)
*/
void getRegionalPrefsLocales([optional] out unsigned long aCount,
[retval, array, size_is(aCount)] out string aOutArray);
readonly attribute Array<ACString> regionalPrefsLocales;
/**
* Negotiates the best locales out of a ordered list of requested locales and
@ -124,14 +117,10 @@ interface mozILocaleService : nsISupports
*
* (See LocaleService.h for a more C++-friendly version of this.)
*/
void negotiateLanguages([array, size_is(aRequestedCount)] in string aRequested,
[array, size_is(aAvailableCount)] in string aAvailable,
[optional] in string aDefaultLocale,
[optional] in long langNegStrategy,
[optional] in unsigned long aRequestedCount,
[optional] in unsigned long aAvailableCount,
[optional] out unsigned long aCount,
[retval, array, size_is(aCount)] out string aLocales);
Array<ACString> negotiateLanguages(in Array<ACString> aRequested,
in Array<ACString> aAvailable,
[optional] in ACString aDefaultLocale,
[optional] in long langNegStrategy);
/**
* Returns the best locale that the application should be localized to.
@ -144,15 +133,15 @@ interface mozILocaleService : nsISupports
* When retrieving the locales for Intl API or ICU locale settings,
* use the BCP47 form.
*
* Where possible, getAppLocales*() should be preferred over this API and
* Where possible, appLocales* 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"
*/
ACString getAppLocaleAsLangTag();
ACString getAppLocaleAsBCP47();
readonly attribute ACString appLocaleAsLangTag;
readonly attribute ACString appLocaleAsBCP47;
/**
* Returns a list of locales that the user requested the app to be
@ -163,32 +152,12 @@ interface mozILocaleService : nsISupports
*
* Example: ["en-US", "de", "pl", "sr-Cyrl", "zh-Hans-HK"]
*/
void getRequestedLocales([optional] out unsigned long aCount,
[retval, array, size_is(aCount)] out string aLocales);
attribute Array<ACString> requestedLocales;
/**
* Returns the top-requested locale from the user, or an empty string if none is set.
*/
ACString getRequestedLocale();
/**
* Sets a list of locales that the user requested the app to be
* localized to.
*
* The argument is an ordered list of locale IDs which should be
* used as a requestedLocales input list for language negotiation.
*
* The current implementation is limited to handle at most one
* locale passed to the API. In the future we'll transition to support
* whole fallback chain.
*
* If an empty list is passed, the list of requested locales will
* be picked from the operating system.
*
* Example: ["de"]
*/
void setRequestedLocales([array, size_is(aRequestedCount)] in string aRequested,
[optional] in unsigned long aRequestedCount);
readonly attribute ACString requestedLocale;
/**
* Returns a list of locales that the app can be localized to.
@ -198,37 +167,17 @@ interface mozILocaleService : nsISupports
*
* Example: ["en-US", "de", "pl", "sr-Cyrl", "zh-Hans-HK"]
*/
void getAvailableLocales([optional] out unsigned long aCount,
[retval, array, size_is(aCount)] out string aLocales);
attribute Array<ACString> availableLocales;
/**
* Returns whether the current app locale is RTL.
*/
readonly attribute boolean isAppLocaleRTL;
/**
* Sets a list of locales the application has resources to be localized into.
*
* The primary use of this function is to let L10nRegistry communicate all
* locale updates.
*
* The secondary use case is for testing purposes in scenarios in which the
* actual resources don't have to be available.
* It is recommended for tests to create a mock FileSource and register it in
* the L10nRegistry rather than using this call, in order to emulate full
* resource availability cycle.
*
*/
void setAvailableLocales([array, size_is(aAvailableCount)] in string aAvailable,
[optional] in unsigned long aAvailableCount);
/**
* Returns a list of locales packaged into the app bundle.
*
* Example: ["en-US", "de", "pl", "sr-Cyrl", "zh-Hans-HK"]
*
* (See LocaleService.h for a more C++-friendly version of this.)
*/
void getPackagedLocales([optional] out unsigned long aCount,
[retval, array, size_is(aCount)] out string aOutArray);
readonly attribute Array<ACString> packagedLocales;
};

View File

@ -15,8 +15,8 @@ TEST(Intl_Locale_LocaleService, Negotiate) {
nsTArray<nsCString> availableLocales;
nsTArray<nsCString> supportedLocales;
nsAutoCString defaultLocale("en-US");
LocaleService::LangNegStrategy strategy =
LocaleService::LangNegStrategy::Filtering;
int32_t strategy =
LocaleService::kLangNegStrategyFiltering;
requestedLocales.AppendElement(NS_LITERAL_CSTRING("sr"));
@ -36,8 +36,8 @@ TEST(Intl_Locale_LocaleService, UseLSDefaultLocale) {
nsTArray<nsCString> availableLocales;
nsTArray<nsCString> supportedLocales;
nsAutoCString defaultLocale("");
LocaleService::LangNegStrategy strategy =
LocaleService::LangNegStrategy::Lookup;
int32_t strategy =
LocaleService::kLangNegStrategyLookup;
requestedLocales.AppendElement(NS_LITERAL_CSTRING("sr"));

View File

@ -30,11 +30,11 @@ add_test(function test_lastFallbackLocale() {
run_next_test();
});
add_test(function test_getAppLocalesAsLangTags() {
const appLocale = localeService.getAppLocaleAsLangTag();
add_test(function test_appLocalesAsLangTags() {
const appLocale = localeService.appLocaleAsLangTag;
Assert.ok(appLocale != "", "appLocale is non-empty");
const appLocales = localeService.getAppLocalesAsLangTags();
const appLocales = localeService.appLocalesAsLangTags;
Assert.ok(Array.isArray(appLocales), "appLocales returns an array");
Assert.ok(appLocale == appLocales[0], "appLocale matches first entry in appLocales");
@ -48,8 +48,8 @@ add_test(function test_getAppLocalesAsLangTags() {
const PREF_REQUESTED_LOCALES = "intl.locale.requested";
const REQ_LOC_CHANGE_EVENT = "intl:requested-locales-changed";
add_test(function test_getRequestedLocales() {
const requestedLocales = localeService.getRequestedLocales();
add_test(function test_requestedLocales() {
const requestedLocales = localeService.requestedLocales;
Assert.ok(Array.isArray(requestedLocales), "requestedLocales returns an array");
run_next_test();
@ -61,9 +61,9 @@ add_test(function test_getRequestedLocales() {
* pref for matchOS is set to true.
*
* Then, we test that when the matchOS is set to true, we will retrieve
* OS locale from getRequestedLocales.
* OS locale from requestedLocales.
*/
add_test(function test_getRequestedLocales_matchOS() {
add_test(function test_requestedLocales_matchOS() {
do_test_pending();
Services.prefs.setCharPref(PREF_REQUESTED_LOCALES, "ar-IR");
@ -72,7 +72,7 @@ add_test(function test_getRequestedLocales_matchOS() {
observe: function (aSubject, aTopic, aData) {
switch (aTopic) {
case REQ_LOC_CHANGE_EVENT:
const reqLocs = localeService.getRequestedLocales();
const reqLocs = localeService.requestedLocales;
Assert.ok(reqLocs[0] === osPrefs.systemLocale);
Services.obs.removeObserver(observer, REQ_LOC_CHANGE_EVENT);
do_test_finished();
@ -91,7 +91,7 @@ add_test(function test_getRequestedLocales_matchOS() {
* event for requested locales change, it will be fired when the
* pref for browser UI locale changes.
*/
add_test(function test_getRequestedLocales_onChange() {
add_test(function test_requestedLocales_onChange() {
do_test_pending();
Services.prefs.setCharPref(PREF_REQUESTED_LOCALES, "ar-IR");
@ -100,7 +100,7 @@ add_test(function test_getRequestedLocales_onChange() {
observe: function (aSubject, aTopic, aData) {
switch (aTopic) {
case REQ_LOC_CHANGE_EVENT:
const reqLocs = localeService.getRequestedLocales();
const reqLocs = localeService.requestedLocales;
Assert.ok(reqLocs[0] === "sr-RU");
Services.obs.removeObserver(observer, REQ_LOC_CHANGE_EVENT);
do_test_finished();
@ -114,10 +114,10 @@ add_test(function test_getRequestedLocales_onChange() {
run_next_test();
});
add_test(function test_getRequestedLocale() {
add_test(function test_requestedLocale() {
Services.prefs.setCharPref(PREF_REQUESTED_LOCALES, "tlh");
let requestedLocale = localeService.getRequestedLocale();
let requestedLocale = localeService.requestedLocale;
Assert.ok(requestedLocale === "tlh", "requestedLocale returns the right value");
Services.prefs.clearUserPref(PREF_REQUESTED_LOCALES);
@ -125,10 +125,10 @@ add_test(function test_getRequestedLocale() {
run_next_test();
});
add_test(function test_setRequestedLocales() {
localeService.setRequestedLocales(['de-AT', 'de-DE', 'de-CH']);
add_test(function test_requestedLocales() {
localeService.requestedLocales = ['de-AT', 'de-DE', 'de-CH'];
let locales = localeService.getRequestedLocales();
let locales = localeService.requestedLocales;
Assert.ok(locales[0] === 'de-AT');
Assert.ok(locales[1] === 'de-DE');
Assert.ok(locales[2] === 'de-CH');
@ -142,22 +142,22 @@ add_test(function test_isAppLocaleRTL() {
run_next_test();
});
add_test(function test_getPackagedLocales() {
const locales = localeService.getPackagedLocales();
add_test(function test_packagedLocales() {
const locales = localeService.packagedLocales;
Assert.ok(locales.length !== 0, "Packaged locales are empty");
run_next_test();
});
add_test(function test_setAvailableLocales() {
const avLocales = localeService.getAvailableLocales();
localeService.setAvailableLocales(["und", "ar-IR"]);
add_test(function test_availableLocales() {
const avLocales = localeService.availableLocales;
localeService.availableLocales = ["und", "ar-IR"];
let locales = localeService.getAvailableLocales();
let locales = localeService.availableLocales;
Assert.ok(locales.length == 2);
Assert.ok(locales[0] === 'und');
Assert.ok(locales[1] === 'ar-IR');
localeService.setAvailableLocales(avLocales);
localeService.availableLocales = avLocales;
run_next_test();
});
@ -165,10 +165,10 @@ add_test(function test_setAvailableLocales() {
/**
* This test verifies that all values coming from the pref are sanitized.
*/
add_test(function test_getRequestedLocales_sanitize() {
add_test(function test_requestedLocales_sanitize() {
Services.prefs.setCharPref(PREF_REQUESTED_LOCALES, "de,2,#$@#,pl,ąó,!a2,DE-at,,;");
let locales = localeService.getRequestedLocales();
let locales = localeService.requestedLocales;
Assert.equal(locales[0], "de");
Assert.equal(locales[1], "pl");
Assert.equal(locales[2], "de-AT");
@ -181,27 +181,27 @@ add_test(function test_getRequestedLocales_sanitize() {
});
add_test(function test_handle_ja_JP_mac() {
const bkpAvLocales = localeService.getAvailableLocales();
const bkpAvLocales = localeService.availableLocales;
localeService.setAvailableLocales(["ja-JP-mac", "en-US"]);
localeService.availableLocales = ["ja-JP-mac", "en-US"];
localeService.setRequestedLocales(['ja-JP-mac']);
localeService.requestedLocales = ['ja-JP-mac'];
let reqLocales = localeService.getRequestedLocales();
let reqLocales = localeService.requestedLocales;
Assert.equal(reqLocales[0], 'ja-JP-macos');
let avLocales = localeService.getAvailableLocales();
let avLocales = localeService.availableLocales;
Assert.equal(avLocales[0], 'ja-JP-macos');
let appLocales = localeService.getAppLocalesAsBCP47();
let appLocales = localeService.appLocalesAsBCP47;
Assert.equal(appLocales[0], 'ja-JP-macos');
let appLocalesAsLT = localeService.getAppLocalesAsLangTags();
let appLocalesAsLT = localeService.appLocalesAsLangTags;
Assert.equal(appLocalesAsLT[0], 'ja-JP-mac');
Assert.equal(localeService.getAppLocaleAsLangTag(), "ja-JP-mac");
Assert.equal(localeService.appLocaleAsLangTag, "ja-JP-mac");
localeService.setAvailableLocales(bkpAvLocales);
localeService.availableLocales = bkpAvLocales;
run_next_test();
});

View File

@ -82,23 +82,9 @@ const data = {
[["ja-JP-mac", "de-DE"], ["ja-JP-mac", "de-DE"], ["ja-JP-mac", "de-DE"]],
],
"should not crash on invalid input": [
[null, ["fr-FR"], []],
[[null], [], []],
[[undefined], [], []],
[[undefined], [null], []],
[[undefined], [undefined], []],
[[null], [null], null, null, []],
[undefined, ["fr-FR"], []],
[2, ["fr-FR"], []],
["fr-FR", ["fr-FR"], []],
[["fą-FŻ"], ["ór_Fń"], []],
[["fr-FR"], null, []],
[["fr-FR"], undefined, []],
[["fr-FR"], 2, []],
[["fr-FR"], "fr-FR", []],
[["2"], ["ąóżł"], []],
[[[]], ["fr-FR"], []],
[[[]], [[2]], []],
[[[""]], ["fr-FR"], []],
],
},
"matching": {
@ -143,4 +129,26 @@ function run_test()
}
}
}
// Verify that we error out when requested or available is not an array.
for ([req, avail] in [
[null, ["fr-FR"]],
[undefined, ["fr-FR"]],
[2, ["fr-FR"]],
["fr-FR", ["fr-FR"]],
[["fr-FR"], null],
[["fr-FR"], undefined],
[["fr-FR"], 2],
[["fr-FR"], "fr-FR"],
[[null], []],
[[undefined], []],
[[undefined], [null]],
[[undefined], [undefined]],
[[null], [null]],
[[[]], [[2]]],
]) {
Assert.throws(() => {
nl(req, avail);
}, err => err.result == Cr.NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY);
}
}