1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <cctype>
17 #include "i18n_hilog.h"
18 #include "locale_config.h"
19 #include "system_locale_manager.h"
20 #include "unicode/calendar.h"
21 #include "unicode/timezone.h"
22 #include "utils.h"
23 #include "i18n_timezone.h"
24 #include "unicode/localebuilder.h"
25 #include "unicode/locid.h"
26
27 namespace OHOS {
28 namespace Global {
29 namespace I18n {
30 const char* SystemLocaleManager::SIM_COUNTRY_CODE_KEY = "telephony.sim.countryCode0";
31 const std::unordered_set<std::string> SystemLocaleManager::supportLocales {
32 "ar-Arab-EG",
33 "az-Cyrl-AZ",
34 "bo-Tibt-CN",
35 "jv-Latn-ID",
36 "sr-Latn-RS",
37 "be-Cyrl-BY",
38 "bg-Cyrl-BG",
39 "bn-Beng-BD",
40 "bs-Cyrl-BA",
41 "ca-Latn-ES",
42 "cs-Latn-CZ",
43 "da-Latn-DK",
44 "de-Latn-DE",
45 "de-Latn-LI",
46 "de-Latn-CH",
47 "de-Latn-AT",
48 "el-Grek-GR",
49 "en-Latn-AU",
50 "en-Latn-CA",
51 "en-Latn-GB",
52 "en-Latn-IN",
53 "en-Latn-NZ",
54 "en-Latn-SG",
55 "en-Latn-US",
56 "en-Qaag-US",
57 "es-Latn-ES",
58 "es-Latn-US",
59 "et-Latn-EE",
60 "eu-Latn-ES",
61 "fa-Arab-IR",
62 "fa-Arab-AF",
63 "fi-Latn-FI",
64 "fr-Latn-FR",
65 "fr-Latn-CA",
66 "fr-Latn-BE",
67 "fr-Latn-CH",
68 "gl-Latn-ES",
69 "gu-Gujr-IN",
70 "hi-Deva-IN",
71 "hr-Latn-HR",
72 "hu-Latn-HU",
73 "in-Latn-ID",
74 "id-Latn-ID",
75 "it-Latn-IT",
76 "it-Latn-CH",
77 "fil-Latn-PH",
78 "iw-Hebr-IL",
79 "he-Hebr-IL",
80 "ja-Jpan-JP",
81 "ka-Geor-GE",
82 "kk-Cyrl-KZ",
83 "km-Khmr-KH",
84 "kn-Knda-IN",
85 "ko-Kore-KR",
86 "lo-Laoo-LA",
87 "lt-Latn-LT",
88 "lv-Latn-LV",
89 "mai-Deva-IN",
90 "mk-Cyrl-MK",
91 "mi-Latn-NZ",
92 "ml-Mlym-IN",
93 "mn-Cyrl-MN",
94 "mr-Deva-IN",
95 "ms-Latn-MY",
96 "my-Mymr-MM",
97 "my-Qaag-MM",
98 "nb-Latn-NO",
99 "ne-Deva-NP",
100 "nl-Latn-NL",
101 "nl-Latn-BE",
102 "or-Orya-IN",
103 "pa-Arab-PK",
104 "pa-Guru-IN",
105 "pl-Latn-PL",
106 "pt-Latn-BR",
107 "pt-Latn-PT",
108 "ro-Latn-RO",
109 "ru-Cyrl-RU",
110 "si-Sinh-LK",
111 "sk-Latn-SK",
112 "sl-Latn-SI",
113 "sv-Latn-SE",
114 "sw-Latn-KE",
115 "sw-Latn-TZ",
116 "ta-Taml-IN",
117 "te-Telu-IN",
118 "th-Thai-TH",
119 "tr-Latn-TR",
120 "ug-Arab-CN",
121 "uk-Cyrl-UA",
122 "ur-Arab-PK",
123 "uz-Cyrl-UZ",
124 "uz-Latn-UZ",
125 "vi-Latn-VN",
126 "zh-Hans-CN",
127 "zh-Hant-HK",
128 "zh-Hant-TW",
129 };
130
SystemLocaleManager()131 SystemLocaleManager::SystemLocaleManager()
132 {
133 tabooUtils = std::make_unique<TabooUtils>();
134 }
135
~SystemLocaleManager()136 SystemLocaleManager::~SystemLocaleManager()
137 {
138 }
139
140 /**
141 * Language arrays are sorted according to the following steps:
142 * 1. Remove blocked languages.
143 * 2. Compute language locale displayName; If options.isUseLocalName is true, compute language local displayName.
144 * replace display name with taboo data.
145 * 3. Judge whether language is suggested with system region and sim card region.
146 * 4. Sort the languages use locale displayName, local displyName and suggestion infomation.
147 */
GetLanguageInfoArray(const std::vector<std::string> & languages,const SortOptions & options,I18nErrorCode & status)148 std::vector<LocaleItem> SystemLocaleManager::GetLanguageInfoArray(const std::vector<std::string> &languages,
149 const SortOptions &options, I18nErrorCode &status)
150 {
151 std::vector<LocaleItem> localeItemList;
152 status = I18nErrorCode::SUCCESS;
153 if (!CheckSystemPermission()) {
154 status = I18nErrorCode::NOT_SYSTEM_APP;
155 return localeItemList;
156 }
157 std::unordered_set<std::string> blockedLanguages = LocaleConfig::GetBlockedLanguages();
158 std::unordered_set<std::string> simRegions = GetCountryCodeFromSimCard();
159 for (auto it = languages.begin(); it != languages.end(); ++it) {
160 if (blockedLanguages.find(*it) != blockedLanguages.end()) {
161 continue;
162 }
163 std::string languageDisplayName = LocaleConfig::GetDisplayLanguage(*it, options.localeTag, true);
164 languageDisplayName = tabooUtils->ReplaceLanguageName(*it, options.localeTag, languageDisplayName);
165 std::string languageNativeName;
166 if (options.isUseLocalName) {
167 languageNativeName = LocaleConfig::GetDisplayLanguage(*it, *it, true);
168 languageNativeName = tabooUtils->ReplaceLanguageName(*it, *it, languageNativeName);
169 }
170 bool isSuggestedWithSystemRegion = LocaleConfig::IsSuggested(*it, LocaleConfig::GetSystemRegion());
171 SuggestionType suggestionType = SuggestionType::SUGGESTION_TYPE_NONE;
172 if (isSuggestedWithSystemRegion) {
173 suggestionType = SuggestionType::SUGGESTION_TYPE_RELATED;
174 } else if (IsLanguageSimRegionSuggest(simRegions, *it)) {
175 suggestionType = SuggestionType::SUGGESTION_TYPE_SIM;
176 }
177 LocaleItem item { *it, suggestionType, languageDisplayName, languageNativeName };
178 localeItemList.push_back(item);
179 }
180 SortLocaleItemList(localeItemList, options);
181 return localeItemList;
182 }
183
184 /**
185 * Region arrays are sorted according to the following steps:
186 * 1. Remove blocked regions and blocked regions under system Language.
187 * 2. Compute region locale displayName; replace display name with taboo data.
188 * 3. Judge whether region is suggested with system language.
189 * 4. Sort the regions use locale displayName and suggestion infomation.
190 */
GetCountryInfoArray(const std::vector<std::string> & countries,const SortOptions & options,I18nErrorCode & status)191 std::vector<LocaleItem> SystemLocaleManager::GetCountryInfoArray(const std::vector<std::string> &countries,
192 const SortOptions &options, I18nErrorCode &status)
193 {
194 std::vector<LocaleItem> localeItemList;
195 status = I18nErrorCode::SUCCESS;
196 if (!CheckSystemPermission()) {
197 status = I18nErrorCode::NOT_SYSTEM_APP;
198 return localeItemList;
199 }
200 std::string systemLanguage = LocaleConfig::GetSystemLanguage();
201 std::unordered_set<std::string> blockedRegions = LocaleConfig::GetBlockedRegions();
202 std::unordered_set<std::string> blockedLanguageRegions = LocaleConfig::GetLanguageBlockedRegions();
203 std::unordered_set<std::string> simRegions = GetCountryCodeFromSimCard();
204 for (auto it = countries.begin(); it != countries.end(); ++it) {
205 if (blockedRegions.find(*it) != blockedRegions.end() || blockedLanguageRegions.find(*it) !=
206 blockedLanguageRegions.end()) {
207 continue;
208 }
209 bool isSuggestedRegion = IsSuggestRegion(systemLanguage, *it);
210 SuggestionType suggestionType = SuggestionType::SUGGESTION_TYPE_NONE;
211 if (isSuggestedRegion) {
212 suggestionType = SuggestionType::SUGGESTION_TYPE_RELATED;
213 } else if (simRegions.find(*it) != simRegions.end()) {
214 suggestionType = SuggestionType::SUGGESTION_TYPE_SIM;
215 }
216 LocaleItem item = GetLocaleItem(*it, suggestionType, options.localeTag);
217 localeItemList.push_back(item);
218 }
219 SortLocaleItemList(localeItemList, options);
220 return localeItemList;
221 }
222
GetLocaleItem(const std::string & region,SuggestionType & suggestionType,const std::string & localeTag)223 LocaleItem SystemLocaleManager::GetLocaleItem(const std::string ®ion,
224 SuggestionType &suggestionType, const std::string &localeTag)
225 {
226 std::string pseudoProcessedRegion = PseudoLocalizationProcessor("");
227 std::string regionName = LocaleConfig::GetDisplayRegion(region, localeTag, true);
228 if (this->tabooUtils != nullptr) {
229 regionName = this->tabooUtils->ReplaceCountryName(region, localeTag, regionName);
230 }
231 LocaleItem item { region, suggestionType, regionName, pseudoProcessedRegion };
232 return item;
233 }
234
SortLocaleItemList(std::vector<LocaleItem> & localeItemList,const SortOptions & options)235 void SystemLocaleManager::SortLocaleItemList(std::vector<LocaleItem> &localeItemList, const SortOptions &options)
236 {
237 std::vector<std::string> collatorLocaleTags { options.localeTag };
238 std::map<std::string, std::string> collatorOptions {};
239 Collator *collator = new (std::nothrow) Collator(collatorLocaleTags, collatorOptions);
240 if (collator == nullptr) {
241 return;
242 }
243 auto compareFunc = [collator, options](LocaleItem item1, LocaleItem item2) {
244 if (options.isSuggestedFirst) {
245 if (item1.suggestionType < item2.suggestionType) {
246 return false;
247 } else if (item1.suggestionType > item2.suggestionType) {
248 return true;
249 }
250 }
251 CompareResult result = CompareResult::INVALID;
252 if (item1.localName.length() != 0) {
253 result = collator->Compare(item1.localName, item2.localName);
254 if (result == CompareResult::SMALLER) {
255 return true;
256 }
257 if (result == CompareResult::INVALID) {
258 HILOG_ERROR_I18N("SystemLocaleManager: invalid compare result for local name.");
259 }
260 return false;
261 }
262 result = collator->Compare(item1.displayName, item2.displayName);
263 if (result == CompareResult::SMALLER) {
264 return true;
265 }
266 if (result == CompareResult::INVALID) {
267 HILOG_ERROR_I18N("SystemLocaleManager: invalid compare result for display name.");
268 }
269 return false;
270 };
271 std::sort(localeItemList.begin(), localeItemList.end(), compareFunc);
272 delete collator;
273 }
274
IsLanguageSimRegionSuggest(const std::unordered_set<std::string> & simRegions,const std::string & language)275 bool SystemLocaleManager::IsLanguageSimRegionSuggest(const std::unordered_set<std::string> &simRegions,
276 const std::string &language)
277 {
278 for (auto iter = simRegions.begin(); iter != simRegions.end(); ++iter) {
279 if (LocaleConfig::IsSuggested(language, *iter)) {
280 return true;
281 }
282 }
283 return false;
284 }
285
IsSuggestRegion(const std::string & language,const std::string & region)286 bool SystemLocaleManager::IsSuggestRegion(const std::string &language, const std::string ®ion)
287 {
288 UErrorCode status = U_ZERO_ERROR;
289 icu::Locale origin = icu::Locale::forLanguageTag(language, status);
290 if (U_FAILURE(status)) {
291 HILOG_ERROR_I18N("SystemLocaleManager: create locale for %{public}s failed.", language.c_str());
292 return false;
293 }
294 origin.addLikelySubtags(status);
295 if (U_FAILURE(status)) {
296 HILOG_ERROR_I18N("SystemLocaleManager: addLikelySubtags for %{public}s failed.", language.c_str());
297 return false;
298 }
299 icu::LocaleBuilder builder = icu::LocaleBuilder().setLanguage(origin.getLanguage()).
300 setScript(origin.getScript()).setRegion(region);
301 icu::Locale temp = builder.setExtension('u', "").build(status);
302 if (U_FAILURE(status)) {
303 HILOG_ERROR_I18N("SystemLocaleManager: LocaleBuilder for %{public}s, %{public}s failed.",
304 language.c_str(), region.c_str());
305 return false;
306 }
307 std::string fullLanguage = temp.toLanguageTag<std::string>(status);
308 if (U_FAILURE(status)) {
309 HILOG_ERROR_I18N("SystemLocaleManager: toLanguageTag for %{public}s failed.", language.c_str());
310 return false;
311 }
312 return supportLocales.find(fullLanguage) != supportLocales.end();
313 }
314
GetTimezoneCityInfoArray(I18nErrorCode & status)315 std::vector<TimeZoneCityItem> SystemLocaleManager::GetTimezoneCityInfoArray(I18nErrorCode& status)
316 {
317 std::vector<TimeZoneCityItem> result;
318 status = I18nErrorCode::SUCCESS;
319 if (!CheckSystemPermission()) {
320 status = I18nErrorCode::NOT_SYSTEM_APP;
321 return result;
322 }
323 result = GetTimezoneCityInfoArray();
324 SortTimezoneCityItemList(LocaleConfig::GetSystemLocale(), result);
325 return result;
326 }
327
GetTimezoneCityInfoArray()328 std::vector<TimeZoneCityItem> SystemLocaleManager::GetTimezoneCityInfoArray()
329 {
330 std::vector<TimeZoneCityItem> result;
331 std::set<std::string> zoneCityIds = I18nTimeZone::GetAvailableZoneCityIDs();
332 std::string locale = LocaleConfig::GetSystemLocale();
333 std::string localeBaseName = I18nTimeZone::GetLocaleBaseName(locale);
334 std::map<std::string, std::string> displayNameMap = I18nTimeZone::FindCityDisplayNameMap(localeBaseName);
335 std::map<std::string, icu::TimeZone*> tzMap;
336 bool ifEnforce = GetPseudoLocalizationEnforce();
337 for (auto it = zoneCityIds.begin(); it != zoneCityIds.end(); ++it) {
338 std::string cityId = *it, cityDisplayName = "";
339 if (displayNameMap.find(cityId) != displayNameMap.end()) {
340 cityDisplayName = displayNameMap.find(cityId)->second;
341 }
342 int32_t rawOffset = 0, dstOffset = 0;
343 bool local = false;
344 UErrorCode status = U_ZERO_ERROR;
345 UDate date = icu::Calendar::getNow();
346 std::string timezoneId = I18nTimeZone::GetTimezoneIdByCityId(cityId);
347 if (timezoneId.length() == 0) {
348 continue;
349 }
350 if (tzMap.find(timezoneId) != tzMap.end()) {
351 icu::TimeZone *icuTimeZone = tzMap.find(timezoneId)->second;
352 icuTimeZone->getOffset(date, (UBool)local, rawOffset, dstOffset, status);
353 } else {
354 icu::UnicodeString unicodeString = icu::UnicodeString::fromUTF8(timezoneId);
355 icu::TimeZone *icuTimeZone = icu::TimeZone::createTimeZone(unicodeString);
356 if (icuTimeZone == nullptr) {
357 continue;
358 }
359 icuTimeZone->getOffset(date, (UBool)local, rawOffset, dstOffset, status);
360 tzMap.insert({timezoneId, icuTimeZone});
361 }
362 struct TimeZoneCityItem tzCityItem = {
363 timezoneId, cityId, PseudoLocalizationProcessor(cityDisplayName, ifEnforce), dstOffset + rawOffset,
364 PseudoLocalizationProcessor("", ifEnforce), rawOffset
365 };
366 result.push_back(tzCityItem);
367 }
368 for (auto it = tzMap.begin(); it != tzMap.end(); ++it) {
369 delete it->second;
370 it->second = nullptr;
371 }
372 return result;
373 }
374
SortTimezoneCityItemList(const std::string & locale,std::vector<TimeZoneCityItem> & timezoneCityItemList)375 void SystemLocaleManager::SortTimezoneCityItemList(const std::string &locale,
376 std::vector<TimeZoneCityItem> &timezoneCityItemList)
377 {
378 std::vector<std::string> collatorLocaleTags { locale };
379 std::map<std::string, std::string> collatorOptions {};
380 Collator *collator = new (std::nothrow) Collator(collatorLocaleTags, collatorOptions);
381 if (collator == nullptr) {
382 return;
383 }
384 auto sortFunc = [collator](TimeZoneCityItem item1, TimeZoneCityItem item2) {
385 CompareResult result = CompareResult::INVALID;
386 result = collator->Compare(item1.cityDisplayName, item2.cityDisplayName);
387 if (result == CompareResult::SMALLER) {
388 return true;
389 }
390 if (result == CompareResult::INVALID) {
391 HILOG_ERROR_I18N("SystemLocaleManager: invalid compare result for city display name.");
392 }
393 return false;
394 };
395 std::sort(timezoneCityItemList.begin(), timezoneCityItemList.end(), sortFunc);
396 delete collator;
397 }
398
GetCountryCodeFromSimCard()399 std::unordered_set<std::string> SystemLocaleManager::GetCountryCodeFromSimCard()
400 {
401 std::unordered_set<std::string> resultSet;
402 std::vector<size_t> soltIds = {0, 1};
403 for (size_t i = 0; i < soltIds.size(); i++) {
404 GetCountryCodeFromSimCardInner(resultSet, soltIds[i]);
405 }
406 return resultSet;
407 }
408
GetCountryCodeFromSimCardInner(std::unordered_set<std::string> & resultSet,size_t soltId)409 void SystemLocaleManager::GetCountryCodeFromSimCardInner(std::unordered_set<std::string> &resultSet,
410 size_t soltId)
411 {
412 }
413 } // I18n
414 } // Global
415 } // OHOS