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;
20 import com.google.common.annotations.VisibleForTesting;
21 import java.util.Locale;
23 public class LocaleSet {
24 private static final String CHINESE_LANGUAGE = Locale.CHINESE.getLanguage().toLowerCase();
25 private static final String JAPANESE_LANGUAGE = Locale.JAPANESE.getLanguage().toLowerCase();
26 private static final String KOREAN_LANGUAGE = Locale.KOREAN.getLanguage().toLowerCase();
28 private static class LocaleWrapper {
29 private final Locale mLocale;
30 private final String mLanguage;
31 private final boolean mLocaleIsCJK;
33 private static boolean isLanguageCJK(String language) {
34 return CHINESE_LANGUAGE.equals(language) ||
35 JAPANESE_LANGUAGE.equals(language) ||
36 KOREAN_LANGUAGE.equals(language);
39 public LocaleWrapper(Locale locale) {
41 if (mLocale != null) {
42 mLanguage = mLocale.getLanguage().toLowerCase();
43 mLocaleIsCJK = isLanguageCJK(mLanguage);
50 public boolean hasLocale() {
51 return mLocale != null;
54 public Locale getLocale() {
58 public boolean isLocale(Locale locale) {
59 return mLocale == null ? (locale == null) : mLocale.equals(locale);
62 public boolean isLocaleCJK() {
66 public boolean isLanguage(String language) {
67 return mLanguage == null ? (language == null)
68 : mLanguage.equalsIgnoreCase(language);
71 public String toString() {
72 return mLocale != null ? mLocale.toLanguageTag() : "(null)";
76 public static LocaleSet getDefault() {
77 return new LocaleSet(Locale.getDefault());
80 public LocaleSet(Locale locale) {
85 * Returns locale set for a given set of IETF BCP-47 tags separated by ';'.
86 * BCP-47 tags are what is used by ICU 52's toLanguageTag/forLanguageTag
87 * methods to represent individual Locales: "en-US" for Locale.US,
88 * "zh-CN" for Locale.CHINA, etc. So eg "en-US;zh-CN" specifies the locale
89 * set LocaleSet(Locale.US, Locale.CHINA).
91 * @param localeString One or more BCP-47 tags separated by ';'.
92 * @return LocaleSet for specified locale string, or default set if null
95 public static LocaleSet getLocaleSet(String localeString) {
96 // Locale.toString() generates strings like "en_US" and "zh_CN_#Hans".
97 // Locale.toLanguageTag() generates strings like "en-US" and "zh-Hans-CN".
98 // We can only parse language tags.
99 if (localeString != null && localeString.indexOf('_') == -1) {
100 final String[] locales = localeString.split(";");
101 final Locale primaryLocale = Locale.forLanguageTag(locales[0]);
102 // ICU tags undefined/unparseable locales "und"
103 if (primaryLocale != null &&
104 !TextUtils.equals(primaryLocale.toLanguageTag(), "und")) {
105 if (locales.length > 1 && locales[1] != null) {
106 final Locale secondaryLocale = Locale.forLanguageTag(locales[1]);
107 if (secondaryLocale != null &&
108 !TextUtils.equals(secondaryLocale.toLanguageTag(), "und")) {
109 return new LocaleSet(primaryLocale, secondaryLocale);
112 return new LocaleSet(primaryLocale);
118 private final LocaleWrapper mPrimaryLocale;
119 private final LocaleWrapper mSecondaryLocale;
121 public LocaleSet(Locale primaryLocale, Locale secondaryLocale) {
122 mPrimaryLocale = new LocaleWrapper(primaryLocale);
123 mSecondaryLocale = new LocaleWrapper(
124 mPrimaryLocale.equals(secondaryLocale) ? null : secondaryLocale);
127 public LocaleSet normalize() {
128 final Locale primaryLocale = getPrimaryLocale();
129 if (primaryLocale == null) {
132 Locale secondaryLocale = getSecondaryLocale();
133 // disallow both locales with same language (redundant and/or conflicting)
134 // disallow both locales CJK (conflicting rules)
135 if (secondaryLocale == null ||
136 isPrimaryLanguage(secondaryLocale.getLanguage()) ||
137 (isPrimaryLocaleCJK() && isSecondaryLocaleCJK())) {
138 return new LocaleSet(primaryLocale);
140 // unnecessary to specify English as secondary locale (redundant)
141 if (isSecondaryLanguage(Locale.ENGLISH.getLanguage())) {
142 return new LocaleSet(primaryLocale);
147 public boolean hasSecondaryLocale() {
148 return mSecondaryLocale.hasLocale();
151 public Locale getPrimaryLocale() {
152 return mPrimaryLocale.getLocale();
155 public Locale getSecondaryLocale() {
156 return mSecondaryLocale.getLocale();
159 public boolean isPrimaryLocale(Locale locale) {
160 return mPrimaryLocale.isLocale(locale);
163 public boolean isSecondaryLocale(Locale locale) {
164 return mSecondaryLocale.isLocale(locale);
167 private static final String SCRIPT_SIMPLIFIED_CHINESE = "Hans";
168 private static final String SCRIPT_TRADITIONAL_CHINESE = "Hant";
171 public static boolean isLocaleSimplifiedChinese(Locale locale) {
172 // language must match
173 if (locale == null || !TextUtils.equals(locale.getLanguage(), CHINESE_LANGUAGE)) {
176 // script is optional but if present must match
177 if (!TextUtils.isEmpty(locale.getScript())) {
178 return locale.getScript().equals(SCRIPT_SIMPLIFIED_CHINESE);
180 // if no script, must match known country
181 return locale.equals(Locale.SIMPLIFIED_CHINESE);
184 public boolean isPrimaryLocaleSimplifiedChinese() {
185 return isLocaleSimplifiedChinese(getPrimaryLocale());
188 public boolean isSecondaryLocaleSimplifiedChinese() {
189 return isLocaleSimplifiedChinese(getSecondaryLocale());
193 public static boolean isLocaleTraditionalChinese(Locale locale) {
194 // language must match
195 if (locale == null || !TextUtils.equals(locale.getLanguage(), CHINESE_LANGUAGE)) {
198 // script is optional but if present must match
199 if (!TextUtils.isEmpty(locale.getScript())) {
200 return locale.getScript().equals(SCRIPT_TRADITIONAL_CHINESE);
202 // if no script, must match known country
203 return locale.equals(Locale.TRADITIONAL_CHINESE);
206 public boolean isPrimaryLocaleTraditionalChinese() {
207 return isLocaleTraditionalChinese(getPrimaryLocale());
210 public boolean isSecondaryLocaleTraditionalChinese() {
211 return isLocaleTraditionalChinese(getSecondaryLocale());
214 public boolean isPrimaryLocaleCJK() {
215 return mPrimaryLocale.isLocaleCJK();
218 public boolean isSecondaryLocaleCJK() {
219 return mSecondaryLocale.isLocaleCJK();
222 public boolean isPrimaryLanguage(String language) {
223 return mPrimaryLocale.isLanguage(language);
226 public boolean isSecondaryLanguage(String language) {
227 return mSecondaryLocale.isLanguage(language);
231 public boolean equals(Object object) {
232 if (object == this) {
235 if (object instanceof LocaleSet) {
236 final LocaleSet other = (LocaleSet) object;
237 return other.isPrimaryLocale(mPrimaryLocale.getLocale())
238 && other.isSecondaryLocale(mSecondaryLocale.getLocale());
244 public final String toString() {
245 StringBuilder builder = new StringBuilder();
246 builder.append(mPrimaryLocale.toString());
247 if (hasSecondaryLocale()) {
249 builder.append(mSecondaryLocale.toString());
251 return builder.toString();