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