• 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 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 &region,
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 &region)
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