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 "i18n_hilog.h"
17 #include "locale_config.h"
18 #include "system_locale_manager.h"
19 #include "unicode/calendar.h"
20 #include "unicode/timezone.h"
21 #include "utils.h"
22 #include "i18n_timezone.h"
23 
24 namespace OHOS {
25 namespace Global {
26 namespace I18n {
27 const char* SystemLocaleManager::SIM_COUNTRY_CODE_KEY = "telephony.sim.countryCode0";
28 
SystemLocaleManager()29 SystemLocaleManager::SystemLocaleManager()
30 {
31     tabooUtils = std::make_unique<TabooUtils>();
32 }
33 
~SystemLocaleManager()34 SystemLocaleManager::~SystemLocaleManager()
35 {
36 }
37 
38 /**
39  * Language arrays are sorted according to the following steps:
40  * 1. Remove blocked languages.
41  * 2. Compute language locale displayName; If options.isUseLocalName is true, compute language local displayName.
42  *    replace display name with taboo data.
43  * 3. Judge whether language is suggested with system region and sim card region.
44  * 4. Sort the languages use locale displayName, local displyName and suggestion infomation.
45  */
GetLanguageInfoArray(const std::vector<std::string> & languages,const SortOptions & options,I18nErrorCode & status)46 std::vector<LocaleItem> SystemLocaleManager::GetLanguageInfoArray(const std::vector<std::string> &languages,
47     const SortOptions &options, I18nErrorCode &status)
48 {
49     std::vector<LocaleItem> localeItemList;
50     status = I18nErrorCode::SUCCESS;
51     if (!CheckSystemPermission()) {
52         status = I18nErrorCode::NOT_SYSTEM_APP;
53         return localeItemList;
54     }
55     std::unordered_set<std::string> blockedLanguages = LocaleConfig::GetBlockedLanguages();
56     for (auto it = languages.begin(); it != languages.end(); ++it) {
57         if (blockedLanguages.find(*it) != blockedLanguages.end()) {
58             continue;
59         }
60         std::string languageDisplayName = LocaleConfig::GetDisplayLanguage(*it, options.localeTag, true);
61         languageDisplayName = tabooUtils->ReplaceLanguageName(*it, options.localeTag, languageDisplayName);
62         std::string languageNativeName;
63         if (options.isUseLocalName) {
64             languageNativeName = LocaleConfig::GetDisplayLanguage(*it, *it, true);
65             languageNativeName = tabooUtils->ReplaceLanguageName(*it, *it, languageNativeName);
66         }
67         bool isSuggestedWithSystemRegion = LocaleConfig::IsSuggested(*it, LocaleConfig::GetSystemRegion());
68         std::string simRegion = ReadSystemParameter(SIM_COUNTRY_CODE_KEY, CONFIG_LEN);
69         bool isSuggestedWithSimRegion = false;
70         if (simRegion.length() > 0) {
71             isSuggestedWithSimRegion = LocaleConfig::IsSuggested(*it, simRegion);
72         }
73         SuggestionType suggestionType = SuggestionType::SUGGESTION_TYPE_NONE;
74         if (isSuggestedWithSimRegion) {
75             suggestionType = SuggestionType::SUGGESTION_TYPE_SIM;
76         } else if (isSuggestedWithSystemRegion) {
77             suggestionType = SuggestionType::SUGGESTION_TYPE_RELATED;
78         }
79         LocaleItem item { *it, suggestionType, languageDisplayName, languageNativeName };
80         localeItemList.push_back(item);
81     }
82     SortLocaleItemList(localeItemList, options);
83     return localeItemList;
84 }
85 
86 /**
87  * Region arrays are sorted according to the following steps:
88  * 1. Remove blocked regions and blocked regions under system Language.
89  * 2. Compute region locale displayName; replace display name with taboo data.
90  * 3. Judge whether region is suggested with system language.
91  * 4. Sort the regions use locale displayName and suggestion infomation.
92  */
GetCountryInfoArray(const std::vector<std::string> & countries,const SortOptions & options,I18nErrorCode & status)93 std::vector<LocaleItem> SystemLocaleManager::GetCountryInfoArray(const std::vector<std::string> &countries,
94     const SortOptions &options, I18nErrorCode &status)
95 {
96     std::vector<LocaleItem> localeItemList;
97     status = I18nErrorCode::SUCCESS;
98     if (!CheckSystemPermission()) {
99         status = I18nErrorCode::NOT_SYSTEM_APP;
100         return localeItemList;
101     }
102     std::unordered_set<std::string> blockedRegions = LocaleConfig::GetBlockedRegions();
103     std::unordered_set<std::string> blockedLanguageRegions = LocaleConfig::GetLanguageBlockedRegions();
104     std::string pseudoProcessedRegion = PseudoLocalizationProcessor("");
105     for (auto it = countries.begin(); it != countries.end(); ++it) {
106         if (blockedRegions.find(*it) != blockedRegions.end() || blockedLanguageRegions.find(*it) !=
107             blockedLanguageRegions.end()) {
108             continue;
109         }
110         std::string regionDisplayName = LocaleConfig::GetDisplayRegion(*it, options.localeTag, true);
111         regionDisplayName = tabooUtils->ReplaceCountryName(*it, options.localeTag, regionDisplayName);
112         bool isSuggestedRegion = LocaleConfig::IsSuggested(LocaleConfig::GetSystemLanguage(), *it);
113         SuggestionType suggestionType = SuggestionType::SUGGESTION_TYPE_NONE;
114         if (isSuggestedRegion) {
115             suggestionType = SuggestionType::SUGGESTION_TYPE_RELATED;
116         }
117         LocaleItem item { *it, suggestionType, regionDisplayName, pseudoProcessedRegion };
118         localeItemList.push_back(item);
119     }
120     SortLocaleItemList(localeItemList, options);
121     return localeItemList;
122 }
123 
SortLocaleItemList(std::vector<LocaleItem> & localeItemList,const SortOptions & options)124 void SystemLocaleManager::SortLocaleItemList(std::vector<LocaleItem> &localeItemList, const SortOptions &options)
125 {
126     std::vector<std::string> collatorLocaleTags { options.localeTag };
127     std::map<std::string, std::string> collatorOptions {};
128     Collator *collator = new (std::nothrow) Collator(collatorLocaleTags, collatorOptions);
129     if (collator == nullptr) {
130         return;
131     }
132     auto compareFunc = [collator, options](LocaleItem item1, LocaleItem item2) {
133         if (options.isSuggestedFirst) {
134             if (item1.suggestionType < item2.suggestionType) {
135                 return false;
136             } else if (item1.suggestionType > item2.suggestionType) {
137                 return true;
138             }
139         }
140         CompareResult result = CompareResult::INVALID;
141         if (item1.localName.length() != 0) {
142             result = collator->Compare(item1.localName, item2.localName);
143             if (result == CompareResult::SMALLER) {
144                 return true;
145             }
146             if (result == CompareResult::INVALID) {
147                 HILOG_ERROR_I18N("SystemLocaleManager: invalid compare result for local name.");
148             }
149             return false;
150         }
151         result = collator->Compare(item1.displayName, item2.displayName);
152         if (result == CompareResult::SMALLER) {
153             return true;
154         }
155         if (result == CompareResult::INVALID) {
156             HILOG_ERROR_I18N("SystemLocaleManager: invalid compare result for display name.");
157         }
158         return false;
159     };
160     std::sort(localeItemList.begin(), localeItemList.end(), compareFunc);
161     delete collator;
162 }
163 
GetTimezoneCityInfoArray(I18nErrorCode & status)164 std::vector<TimeZoneCityItem> SystemLocaleManager::GetTimezoneCityInfoArray(I18nErrorCode& status)
165 {
166     std::vector<TimeZoneCityItem> result;
167     status = I18nErrorCode::SUCCESS;
168     if (!CheckSystemPermission()) {
169         status = I18nErrorCode::NOT_SYSTEM_APP;
170         return result;
171     }
172     result = GetTimezoneCityInfoArray();
173     SortTimezoneCityItemList(LocaleConfig::GetSystemLocale(), result);
174     return result;
175 }
176 
GetTimezoneCityInfoArray()177 std::vector<TimeZoneCityItem> SystemLocaleManager::GetTimezoneCityInfoArray()
178 {
179     std::vector<TimeZoneCityItem> result;
180     std::set<std::string> zoneCityIds = I18nTimeZone::GetAvailableZoneCityIDs();
181     std::string locale = LocaleConfig::GetSystemLocale();
182     std::string localeBaseName = I18nTimeZone::GetLocaleBaseName(locale);
183     std::map<std::string, std::string> displayNameMap = I18nTimeZone::FindCityDisplayNameMap(localeBaseName);
184     std::map<std::string, icu::TimeZone*> tzMap;
185     bool ifEnforce = GetPseudoLocalizationEnforce();
186     for (auto it = zoneCityIds.begin(); it != zoneCityIds.end(); ++it) {
187         std::string cityId = *it, cityDisplayName = "";
188         if (displayNameMap.find(cityId) != displayNameMap.end()) {
189             cityDisplayName = displayNameMap.find(cityId)->second;
190         }
191         int32_t rawOffset = 0, dstOffset = 0;
192         bool local = false;
193         UErrorCode status = U_ZERO_ERROR;
194         UDate date = icu::Calendar::getNow();
195         std::string timezoneId = I18nTimeZone::GetTimezoneIdByCityId(cityId);
196         if (timezoneId.length() == 0) {
197             continue;
198         }
199         if (tzMap.find(timezoneId) != tzMap.end()) {
200             icu::TimeZone *icuTimeZone = tzMap.find(timezoneId)->second;
201             icuTimeZone->getOffset(date, (UBool)local, rawOffset, dstOffset, status);
202         } else {
203             icu::UnicodeString unicodeString = icu::UnicodeString::fromUTF8(timezoneId);
204             icu::TimeZone *icuTimeZone = icu::TimeZone::createTimeZone(unicodeString);
205             if (icuTimeZone == nullptr) {
206                 continue;
207             }
208             icuTimeZone->getOffset(date, (UBool)local, rawOffset, dstOffset, status);
209             tzMap.insert({timezoneId, icuTimeZone});
210         }
211         struct TimeZoneCityItem tzCityItem = {
212             timezoneId, cityId, PseudoLocalizationProcessor(cityDisplayName, ifEnforce), dstOffset + rawOffset,
213             PseudoLocalizationProcessor("", ifEnforce), rawOffset
214         };
215         result.push_back(tzCityItem);
216     }
217     for (auto it = tzMap.begin(); it != tzMap.end(); ++it) {
218         delete it->second;
219         it->second = nullptr;
220     }
221     return result;
222 }
223 
SortTimezoneCityItemList(const std::string & locale,std::vector<TimeZoneCityItem> & timezoneCityItemList)224 void SystemLocaleManager::SortTimezoneCityItemList(const std::string &locale,
225                                                    std::vector<TimeZoneCityItem> &timezoneCityItemList)
226 {
227     std::vector<std::string> collatorLocaleTags { locale };
228     std::map<std::string, std::string> collatorOptions {};
229     Collator *collator = new (std::nothrow) Collator(collatorLocaleTags, collatorOptions);
230     if (collator == nullptr) {
231         return;
232     }
233     auto sortFunc = [collator](TimeZoneCityItem item1, TimeZoneCityItem item2) {
234         CompareResult result = CompareResult::INVALID;
235         result = collator->Compare(item1.cityDisplayName, item2.cityDisplayName);
236         if (result == CompareResult::SMALLER) {
237             return true;
238         }
239         if (result == CompareResult::INVALID) {
240             HILOG_ERROR_I18N("SystemLocaleManager: invalid compare result for city display name.");
241         }
242         return false;
243     };
244     std::sort(timezoneCityItemList.begin(), timezoneCityItemList.end(), sortFunc);
245     delete collator;
246 }
247 } // I18n
248 } // Global
249 } // OHOS