2 * Copyright (C) 2014 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
17 package org.lineageos.eleven.locale;
19 import android.text.TextUtils;
21 import androidx.annotation.VisibleForTesting;
23 import java.util.Locale;
25 public class LocaleSet {
26 private static final String CHINESE_LANGUAGE = Locale.CHINESE.getLanguage().toLowerCase();
27 private static final String JAPANESE_LANGUAGE = Locale.JAPANESE.getLanguage().toLowerCase();
28 private static final String KOREAN_LANGUAGE = Locale.KOREAN.getLanguage().toLowerCase();
30 private static class LocaleWrapper {
31 private final Locale mLocale;
32 private final String mLanguage;
33 private final boolean mLocaleIsCJK;
35 private static boolean isLanguageCJK(String language) {
36 return CHINESE_LANGUAGE.equals(language) ||
37 JAPANESE_LANGUAGE.equals(language) ||
38 KOREAN_LANGUAGE.equals(language);
41 public LocaleWrapper(Locale locale) {
43 if (mLocale != null) {
44 mLanguage = mLocale.getLanguage().toLowerCase();
45 mLocaleIsCJK = isLanguageCJK(mLanguage);
52 public boolean hasLocale() {
53 return mLocale != null;
56 public Locale getLocale() {
60 public boolean isLocale(Locale locale) {
61 return mLocale == null ? (locale == null) : mLocale.equals(locale);
64 public boolean isLocaleCJK() {
68 public boolean isLanguage(String language) {
69 return mLanguage == null ? (language == null)
70 : mLanguage.equalsIgnoreCase(language);
73 public String toString() {
74 return mLocale != null ? mLocale.toLanguageTag() : "(null)";
78 public static LocaleSet getDefault() {
79 return new LocaleSet(Locale.getDefault());
82 public LocaleSet(Locale locale) {
87 * Returns locale set for a given set of IETF BCP-47 tags separated by ';'.
88 * BCP-47 tags are what is used by ICU 52's toLanguageTag/forLanguageTag
89 * methods to represent individual Locales: "en-US" for Locale.US,
90 * "zh-CN" for Locale.CHINA, etc. So eg "en-US;zh-CN" specifies the locale
91 * set LocaleSet(Locale.US, Locale.CHINA).
93 * @param localeString One or more BCP-47 tags separated by ';'.
94 * @return LocaleSet for specified locale string, or default set if null
97 public static LocaleSet getLocaleSet(String localeString) {
98 // Locale.toString() generates strings like "en_US" and "zh_CN_#Hans".
99 // Locale.toLanguageTag() generates strings like "en-US" and "zh-Hans-CN".
100 // We can only parse language tags.
101 if (localeString != null && localeString.indexOf('_') == -1) {
102 final String[] locales = localeString.split(";");
103 final Locale primaryLocale = Locale.forLanguageTag(locales[0]);
104 // ICU tags undefined/unparseable locales "und"
105 if (primaryLocale != null &&
106 !TextUtils.equals(primaryLocale.toLanguageTag(), "und")) {
107 if (locales.length > 1 && locales[1] != null) {
108 final Locale secondaryLocale = Locale.forLanguageTag(locales[1]);
109 if (secondaryLocale != null &&
110 !TextUtils.equals(secondaryLocale.toLanguageTag(), "und")) {
111 return new LocaleSet(primaryLocale, secondaryLocale);
114 return new LocaleSet(primaryLocale);
120 private final LocaleWrapper mPrimaryLocale;
121 private final LocaleWrapper mSecondaryLocale;
123 public LocaleSet(Locale primaryLocale, Locale secondaryLocale) {
124 mPrimaryLocale = new LocaleWrapper(primaryLocale);
125 mSecondaryLocale = new LocaleWrapper(
126 mPrimaryLocale.equals(secondaryLocale) ? null : secondaryLocale);
129 public LocaleSet normalize() {
130 final Locale primaryLocale = getPrimaryLocale();
131 if (primaryLocale == null) {
134 Locale secondaryLocale = getSecondaryLocale();
135 // disallow both locales with same language (redundant and/or conflicting)
136 // disallow both locales CJK (conflicting rules)
137 if (secondaryLocale == null ||
138 isPrimaryLanguage(secondaryLocale.getLanguage()) ||
139 (isPrimaryLocaleCJK() && isSecondaryLocaleCJK())) {
140 return new LocaleSet(primaryLocale);
142 // unnecessary to specify English as secondary locale (redundant)
143 if (isSecondaryLanguage(Locale.ENGLISH.getLanguage())) {
144 return new LocaleSet(primaryLocale);
149 public boolean hasSecondaryLocale() {
150 return mSecondaryLocale.hasLocale();
153 public Locale getPrimaryLocale() {
154 return mPrimaryLocale.getLocale();
157 public Locale getSecondaryLocale() {
158 return mSecondaryLocale.getLocale();
161 public boolean isPrimaryLocale(Locale locale) {
162 return mPrimaryLocale.isLocale(locale);
165 public boolean isSecondaryLocale(Locale locale) {
166 return mSecondaryLocale.isLocale(locale);
169 private static final String SCRIPT_SIMPLIFIED_CHINESE = "Hans";
170 private static final String SCRIPT_TRADITIONAL_CHINESE = "Hant";
173 public static boolean isLocaleSimplifiedChinese(Locale locale) {
174 // language must match
175 if (locale == null || !TextUtils.equals(locale.getLanguage(), CHINESE_LANGUAGE)) {
178 // script is optional but if present must match
179 if (!TextUtils.isEmpty(locale.getScript())) {
180 return locale.getScript().equals(SCRIPT_SIMPLIFIED_CHINESE);
182 // if no script, must match known country
183 return locale.equals(Locale.SIMPLIFIED_CHINESE);
186 public boolean isPrimaryLocaleSimplifiedChinese() {
187 return isLocaleSimplifiedChinese(getPrimaryLocale());
190 public boolean isSecondaryLocaleSimplifiedChinese() {
191 return isLocaleSimplifiedChinese(getSecondaryLocale());
195 public static boolean isLocaleTraditionalChinese(Locale locale) {
196 // language must match
197 if (locale == null || !TextUtils.equals(locale.getLanguage(), CHINESE_LANGUAGE)) {
200 // script is optional but if present must match
201 if (!TextUtils.isEmpty(locale.getScript())) {
202 return locale.getScript().equals(SCRIPT_TRADITIONAL_CHINESE);
204 // if no script, must match known country
205 return locale.equals(Locale.TRADITIONAL_CHINESE);
208 public boolean isPrimaryLocaleTraditionalChinese() {
209 return isLocaleTraditionalChinese(getPrimaryLocale());
212 public boolean isSecondaryLocaleTraditionalChinese() {
213 return isLocaleTraditionalChinese(getSecondaryLocale());
216 public boolean isPrimaryLocaleCJK() {
217 return mPrimaryLocale.isLocaleCJK();
220 public boolean isSecondaryLocaleCJK() {
221 return mSecondaryLocale.isLocaleCJK();
224 public boolean isPrimaryLanguage(String language) {
225 return mPrimaryLocale.isLanguage(language);
228 public boolean isSecondaryLanguage(String language) {
229 return mSecondaryLocale.isLanguage(language);
233 public boolean equals(Object object) {
234 if (object == this) {
237 if (object instanceof LocaleSet) {
238 final LocaleSet other = (LocaleSet) object;
239 return other.isPrimaryLocale(mPrimaryLocale.getLocale())
240 && other.isSecondaryLocale(mSecondaryLocale.getLocale());
246 public final String toString() {
247 StringBuilder builder = new StringBuilder();
248 builder.append(mPrimaryLocale.toString());
249 if (hasSecondaryLocale()) {
251 builder.append(mSecondaryLocale.toString());
253 return builder.toString();