OSDN Git Service

Migrate to AndroidX
[android-x86/packages-apps-Eleven.git] / src / org / lineageos / eleven / locale / LocaleSet.java
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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
15  */
16
17 package org.lineageos.eleven.locale;
18
19 import android.text.TextUtils;
20
21 import androidx.annotation.VisibleForTesting;
22
23 import java.util.Locale;
24
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();
29
30     private static class LocaleWrapper {
31         private final Locale mLocale;
32         private final String mLanguage;
33         private final boolean mLocaleIsCJK;
34
35         private static boolean isLanguageCJK(String language) {
36             return CHINESE_LANGUAGE.equals(language) ||
37                     JAPANESE_LANGUAGE.equals(language) ||
38                     KOREAN_LANGUAGE.equals(language);
39         }
40
41         public LocaleWrapper(Locale locale) {
42             mLocale = locale;
43             if (mLocale != null) {
44                 mLanguage = mLocale.getLanguage().toLowerCase();
45                 mLocaleIsCJK = isLanguageCJK(mLanguage);
46             } else {
47                 mLanguage = null;
48                 mLocaleIsCJK = false;
49             }
50         }
51
52         public boolean hasLocale() {
53             return mLocale != null;
54         }
55
56         public Locale getLocale() {
57             return mLocale;
58         }
59
60         public boolean isLocale(Locale locale) {
61             return mLocale == null ? (locale == null) : mLocale.equals(locale);
62         }
63
64         public boolean isLocaleCJK() {
65             return mLocaleIsCJK;
66         }
67
68         public boolean isLanguage(String language) {
69             return mLanguage == null ? (language == null)
70                     : mLanguage.equalsIgnoreCase(language);
71         }
72
73         public String toString() {
74             return mLocale != null ? mLocale.toLanguageTag() : "(null)";
75         }
76     }
77
78     public static LocaleSet getDefault() {
79         return new LocaleSet(Locale.getDefault());
80     }
81
82     public LocaleSet(Locale locale) {
83         this(locale, null);
84     }
85
86     /**
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).
92      *
93      * @param localeString One or more BCP-47 tags separated by ';'.
94      * @return LocaleSet for specified locale string, or default set if null
95      * or unable to parse.
96      */
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);
112                     }
113                 }
114                 return new LocaleSet(primaryLocale);
115             }
116         }
117         return getDefault();
118     }
119
120     private final LocaleWrapper mPrimaryLocale;
121     private final LocaleWrapper mSecondaryLocale;
122
123     public LocaleSet(Locale primaryLocale, Locale secondaryLocale) {
124         mPrimaryLocale = new LocaleWrapper(primaryLocale);
125         mSecondaryLocale = new LocaleWrapper(
126                 mPrimaryLocale.equals(secondaryLocale) ? null : secondaryLocale);
127     }
128
129     public LocaleSet normalize() {
130         final Locale primaryLocale = getPrimaryLocale();
131         if (primaryLocale == null) {
132             return getDefault();
133         }
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);
141         }
142         // unnecessary to specify English as secondary locale (redundant)
143         if (isSecondaryLanguage(Locale.ENGLISH.getLanguage())) {
144             return new LocaleSet(primaryLocale);
145         }
146         return this;
147     }
148
149     public boolean hasSecondaryLocale() {
150         return mSecondaryLocale.hasLocale();
151     }
152
153     public Locale getPrimaryLocale() {
154         return mPrimaryLocale.getLocale();
155     }
156
157     public Locale getSecondaryLocale() {
158         return mSecondaryLocale.getLocale();
159     }
160
161     public boolean isPrimaryLocale(Locale locale) {
162         return mPrimaryLocale.isLocale(locale);
163     }
164
165     public boolean isSecondaryLocale(Locale locale) {
166         return mSecondaryLocale.isLocale(locale);
167     }
168
169     private static final String SCRIPT_SIMPLIFIED_CHINESE = "Hans";
170     private static final String SCRIPT_TRADITIONAL_CHINESE = "Hant";
171
172     @VisibleForTesting
173     public static boolean isLocaleSimplifiedChinese(Locale locale) {
174         // language must match
175         if (locale == null || !TextUtils.equals(locale.getLanguage(), CHINESE_LANGUAGE)) {
176             return false;
177         }
178         // script is optional but if present must match
179         if (!TextUtils.isEmpty(locale.getScript())) {
180             return locale.getScript().equals(SCRIPT_SIMPLIFIED_CHINESE);
181         }
182         // if no script, must match known country
183         return locale.equals(Locale.SIMPLIFIED_CHINESE);
184     }
185
186     public boolean isPrimaryLocaleSimplifiedChinese() {
187         return isLocaleSimplifiedChinese(getPrimaryLocale());
188     }
189
190     public boolean isSecondaryLocaleSimplifiedChinese() {
191         return isLocaleSimplifiedChinese(getSecondaryLocale());
192     }
193
194     @VisibleForTesting
195     public static boolean isLocaleTraditionalChinese(Locale locale) {
196         // language must match
197         if (locale == null || !TextUtils.equals(locale.getLanguage(), CHINESE_LANGUAGE)) {
198             return false;
199         }
200         // script is optional but if present must match
201         if (!TextUtils.isEmpty(locale.getScript())) {
202             return locale.getScript().equals(SCRIPT_TRADITIONAL_CHINESE);
203         }
204         // if no script, must match known country
205         return locale.equals(Locale.TRADITIONAL_CHINESE);
206     }
207
208     public boolean isPrimaryLocaleTraditionalChinese() {
209         return isLocaleTraditionalChinese(getPrimaryLocale());
210     }
211
212     public boolean isSecondaryLocaleTraditionalChinese() {
213         return isLocaleTraditionalChinese(getSecondaryLocale());
214     }
215
216     public boolean isPrimaryLocaleCJK() {
217         return mPrimaryLocale.isLocaleCJK();
218     }
219
220     public boolean isSecondaryLocaleCJK() {
221         return mSecondaryLocale.isLocaleCJK();
222     }
223
224     public boolean isPrimaryLanguage(String language) {
225         return mPrimaryLocale.isLanguage(language);
226     }
227
228     public boolean isSecondaryLanguage(String language) {
229         return mSecondaryLocale.isLanguage(language);
230     }
231
232     @Override
233     public boolean equals(Object object) {
234         if (object == this) {
235             return true;
236         }
237         if (object instanceof LocaleSet) {
238             final LocaleSet other = (LocaleSet) object;
239             return other.isPrimaryLocale(mPrimaryLocale.getLocale())
240                     && other.isSecondaryLocale(mSecondaryLocale.getLocale());
241         }
242         return false;
243     }
244
245     @Override
246     public final String toString() {
247         StringBuilder builder = new StringBuilder();
248         builder.append(mPrimaryLocale.toString());
249         if (hasSecondaryLocale()) {
250             builder.append(";");
251             builder.append(mSecondaryLocale.toString());
252         }
253         return builder.toString();
254     }
255 }