288 lines
11 KiB
Java
288 lines
11 KiB
Java
![]() |
// Copyright 2014 The Chromium Authors
|
||
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
// found in the LICENSE file.
|
||
|
|
||
|
package org.chromium.base;
|
||
|
|
||
|
import android.content.Context;
|
||
|
import android.content.res.Configuration;
|
||
|
import android.os.Build;
|
||
|
import android.os.LocaleList;
|
||
|
import android.text.TextUtils;
|
||
|
|
||
|
import androidx.annotation.RequiresApi;
|
||
|
import androidx.annotation.VisibleForTesting;
|
||
|
|
||
|
import org.jni_zero.CalledByNative;
|
||
|
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.Locale;
|
||
|
|
||
|
/** This class provides the locale related methods. */
|
||
|
public class LocaleUtils {
|
||
|
/** Guards this class from being instantiated. */
|
||
|
private LocaleUtils() {}
|
||
|
|
||
|
/**
|
||
|
* Java keeps deprecated language codes for Hebrew, Yiddish and Indonesian but Chromium uses
|
||
|
* updated ones. Similarly, Android uses "tl" while Chromium uses "fil" for Tagalog/Filipino.
|
||
|
* The Translate settings use "gom", but Chrome uses "kok". Apply a mapping here. See
|
||
|
* http://developer.android.com/reference/java/util/Locale.html
|
||
|
* @return a updated language code for Chromium with given language string.
|
||
|
*/
|
||
|
public static String getUpdatedLanguageForChromium(String language) {
|
||
|
// IMPORTANT: If adding a new Chrome UI language, update the mapping found in:
|
||
|
// build/android/gyp/util/resource_utils.py (Languages that are accept languages, but not
|
||
|
// Chrome Android UI languages do not need to be kept in sync).
|
||
|
switch (language) {
|
||
|
case "gom":
|
||
|
return "kok"; // Konkani
|
||
|
case "in":
|
||
|
return "id"; // Indonesian
|
||
|
case "iw":
|
||
|
return "he"; // Hebrew
|
||
|
case "ji":
|
||
|
return "yi"; // Yiddish
|
||
|
case "jw":
|
||
|
return "jv"; // Javanese
|
||
|
case "tl":
|
||
|
return "fil"; // Filipino
|
||
|
default:
|
||
|
return language;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return a locale with updated language codes for Chromium, with translated modern language
|
||
|
* codes used by Chromium.
|
||
|
*/
|
||
|
@VisibleForTesting
|
||
|
public static Locale getUpdatedLocaleForChromium(Locale locale) {
|
||
|
String language = locale.getLanguage();
|
||
|
String languageForChrome = getUpdatedLanguageForChromium(language);
|
||
|
if (languageForChrome.equals(language)) {
|
||
|
return locale;
|
||
|
}
|
||
|
return new Locale.Builder().setLocale(locale).setLanguage(languageForChrome).build();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Android uses "tl" while Chromium uses "fil" for Tagalog/Filipino.
|
||
|
* So apply a mapping here.
|
||
|
* See http://developer.android.com/reference/java/util/Locale.html
|
||
|
* @return a updated language code for Android with given language string.
|
||
|
*/
|
||
|
public static String getUpdatedLanguageForAndroid(String language) {
|
||
|
// IMPORTANT: Keep in sync with the mapping found in:
|
||
|
// build/android/gyp/util/resource_utils.py
|
||
|
switch (language) {
|
||
|
case "und":
|
||
|
return ""; // Undefined
|
||
|
case "fil":
|
||
|
return "tl"; // Filipino
|
||
|
default:
|
||
|
return language;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return a locale with updated language codes for Android, from translated modern language
|
||
|
* codes used by Chromium.
|
||
|
*/
|
||
|
@VisibleForTesting
|
||
|
public static Locale getUpdatedLocaleForAndroid(Locale locale) {
|
||
|
String language = locale.getLanguage();
|
||
|
String languageForAndroid = getUpdatedLanguageForAndroid(language);
|
||
|
if (languageForAndroid.equals(language)) {
|
||
|
return locale;
|
||
|
}
|
||
|
return new Locale.Builder().setLocale(locale).setLanguage(languageForAndroid).build();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* This function creates a Locale object from xx-XX style string where xx is language code
|
||
|
* and XX is a country code.
|
||
|
* @return the locale that best represents the language tag.
|
||
|
*/
|
||
|
public static Locale forLanguageTag(String languageTag) {
|
||
|
Locale locale = Locale.forLanguageTag(languageTag);
|
||
|
return getUpdatedLocaleForAndroid(locale);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Converts Locale object to the BCP 47 compliant string format.
|
||
|
* This works for API level lower than 24.
|
||
|
*
|
||
|
* Note that for Android M or before, we cannot use Locale.getLanguage() and
|
||
|
* Locale.toLanguageTag() for this purpose. Since Locale.getLanguage() returns deprecated
|
||
|
* language code even if the Locale object is constructed with updated language code. As for
|
||
|
* Locale.toLanguageTag(), it does a special conversion from deprecated language code to updated
|
||
|
* one, but it is only usable for Android N or after.
|
||
|
* @return a well-formed IETF BCP 47 language tag with language and country code that
|
||
|
* represents this locale.
|
||
|
*/
|
||
|
public static String toLanguageTag(Locale locale) {
|
||
|
String language = getUpdatedLanguageForChromium(locale.getLanguage());
|
||
|
String country = locale.getCountry();
|
||
|
if (language.equals("no") && country.equals("NO") && locale.getVariant().equals("NY")) {
|
||
|
return "nn-NO";
|
||
|
}
|
||
|
return country.isEmpty() ? language : language + "-" + country;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Converts LocaleList object to the comma separated BCP 47 compliant string format.
|
||
|
*
|
||
|
* @return a well-formed IETF BCP 47 language tag with language and country code that
|
||
|
* represents this locale list.
|
||
|
*/
|
||
|
@RequiresApi(Build.VERSION_CODES.N)
|
||
|
public static String toLanguageTags(LocaleList localeList) {
|
||
|
ArrayList<String> newLocaleList = new ArrayList<>();
|
||
|
for (int i = 0; i < localeList.size(); i++) {
|
||
|
Locale locale = getUpdatedLocaleForChromium(localeList.get(i));
|
||
|
newLocaleList.add(toLanguageTag(locale));
|
||
|
}
|
||
|
return TextUtils.join(",", newLocaleList);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Extracts the base language from a BCP 47 language tag.
|
||
|
* @param languageTag language tag of the form xx-XX or xx.
|
||
|
* @return the xx part of the language tag.
|
||
|
*/
|
||
|
public static String toBaseLanguage(String languageTag) {
|
||
|
int pos = languageTag.indexOf('-');
|
||
|
if (pos < 0) {
|
||
|
return languageTag;
|
||
|
}
|
||
|
return languageTag.substring(0, pos);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param first A BCP 47 formated language tag.
|
||
|
* @param second A BCP 47 formated language tag.
|
||
|
* @return True if the base language (e.g. "en" for "en-AU") is the same for each tag.
|
||
|
*/
|
||
|
public static boolean isBaseLanguageEqual(String first, String second) {
|
||
|
return TextUtils.equals(toBaseLanguage(first), toBaseLanguage(second));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return a language tag string that represents the default locale.
|
||
|
* The language tag is well-formed IETF BCP 47 language tag with language and country
|
||
|
* code.
|
||
|
*/
|
||
|
@CalledByNative
|
||
|
public static String getDefaultLocaleString() {
|
||
|
return toLanguageTag(Locale.getDefault());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return a comma separated language tags string that represents a default locale or locales.
|
||
|
* Each language tag is well-formed IETF BCP 47 language tag with language and country
|
||
|
* code.
|
||
|
*/
|
||
|
@CalledByNative
|
||
|
public static String getDefaultLocaleListString() {
|
||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||
|
return toLanguageTags(LocaleList.getDefault());
|
||
|
}
|
||
|
return getDefaultLocaleString();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return The default country code set during install.
|
||
|
*/
|
||
|
@CalledByNative
|
||
|
private static String getDefaultCountryCode() {
|
||
|
CommandLine commandLine = CommandLine.getInstance();
|
||
|
return commandLine.hasSwitch(BaseSwitches.DEFAULT_COUNTRY_CODE_AT_INSTALL)
|
||
|
? commandLine.getSwitchValue(BaseSwitches.DEFAULT_COUNTRY_CODE_AT_INSTALL)
|
||
|
: Locale.getDefault().getCountry();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the language tag of the first language in Configuration.
|
||
|
* @param config Configuration to get language for.
|
||
|
* @return The BCP 47 tag representation of the configuration's first locale.
|
||
|
* Configuration.locale is deprecated on N+. However, read only is equivalent to
|
||
|
* Configuration.getLocales()[0]. Change when minSdkVersion >= 24.
|
||
|
*/
|
||
|
@SuppressWarnings("deprecation")
|
||
|
public static String getConfigurationLanguage(Configuration config) {
|
||
|
Locale locale = config.locale;
|
||
|
return (locale != null) ? locale.toLanguageTag() : "";
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return the language tag of the first language in the configuration
|
||
|
* @param context Context to get language for.
|
||
|
* @return The BCP 47 tag representation of the context's first locale.
|
||
|
*/
|
||
|
public static String getContextLanguage(Context context) {
|
||
|
return getConfigurationLanguage(context.getResources().getConfiguration());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Prepend languageTag to the default locales on config.
|
||
|
* @param base The Context to use for the base configuration.
|
||
|
* @param config The Configuration to update.
|
||
|
* @param languageTag The language to prepend to default locales.
|
||
|
*/
|
||
|
public static void updateConfig(Context base, Configuration config, String languageTag) {
|
||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||
|
ApisN.setConfigLocales(base, config, languageTag);
|
||
|
} else {
|
||
|
config.setLocale(Locale.forLanguageTag(languageTag));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Updates the default Locale/LocaleList to those of config.
|
||
|
* @param config
|
||
|
*/
|
||
|
public static void setDefaultLocalesFromConfiguration(Configuration config) {
|
||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||
|
ApisN.setLocaleList(config);
|
||
|
} else {
|
||
|
Locale.setDefault(config.locale);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Helper class for N only code that is not validated on pre-N devices. */
|
||
|
@RequiresApi(Build.VERSION_CODES.N)
|
||
|
@VisibleForTesting
|
||
|
static class ApisN {
|
||
|
static void setConfigLocales(Context base, Configuration config, String language) {
|
||
|
LocaleList updatedLocales =
|
||
|
prependToLocaleList(
|
||
|
language, base.getResources().getConfiguration().getLocales());
|
||
|
config.setLocales(updatedLocales);
|
||
|
}
|
||
|
|
||
|
static void setLocaleList(Configuration config) {
|
||
|
LocaleList.setDefault(config.getLocales());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a new LocaleList with languageTag added to the front.
|
||
|
* If languageTag is already in the list the existing tag is moved to the front.
|
||
|
* @param languageTag String of language tag to prepend
|
||
|
* @param localeList LocaleList to prepend to.
|
||
|
* @return LocaleList
|
||
|
*/
|
||
|
static LocaleList prependToLocaleList(String languageTag, LocaleList localeList) {
|
||
|
String languageList = localeList.toLanguageTags();
|
||
|
|
||
|
// Remove the first instance of languageTag with associated comma if present.
|
||
|
// Pattern example: "(^|,)en-US$|en-US,"
|
||
|
String pattern = String.format("(^|,)%1$s$|%1$s,", languageTag);
|
||
|
languageList = languageList.replaceFirst(pattern, "");
|
||
|
|
||
|
return LocaleList.forLanguageTags(
|
||
|
String.format("%1$s,%2$s", languageTag, languageList));
|
||
|
}
|
||
|
}
|
||
|
}
|