• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &region,
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 &region)
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