1 /*
2  * Copyright (c) 2021-2022 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 #include <regex>
16 #include "accesstoken_kit.h"
17 #ifdef SUPPORT_GRAPHICS
18 #include "app_mgr_client.h"
19 #include "ability_manager_client.h"
20 #include <common_event_manager.h>
21 #include <common_event_publish_info.h>
22 #include <common_event_support.h>
23 #endif
24 #include <cctype>
25 #include "config_policy_utils.h"
26 #include "i18n_hilog.h"
27 #include "ipc_skeleton.h"
28 #include "libxml/parser.h"
29 #include "locale_info.h"
30 #include "unicode/localebuilder.h"
31 #include "unicode/locdspnm.h"
32 #include "unicode/locid.h"
33 #include "unicode/smpdtfmt.h"
34 #include "ohos/init_data.h"
35 #include "parameter.h"
36 #include "securec.h"
37 #include "string_ex.h"
38 #include "ucase.h"
39 #include "ulocimp.h"
40 #include "unicode/unistr.h"
41 #include "ureslocs.h"
42 #include "unicode/ustring.h"
43 #include "ustr_imp.h"
44 #include "utils.h"
45 #include "tokenid_kit.h"
46 #include "locale_config.h"
47 
48 namespace OHOS {
49 namespace Global {
50 namespace I18n {
51 using namespace std;
52 const char *LocaleConfig::LANGUAGE_KEY = "persist.global.language";
53 const char *LocaleConfig::LOCALE_KEY = "persist.global.locale";
54 const char *LocaleConfig::HOUR_KEY = "persist.global.is24Hour";
55 const char *LocaleConfig::UPGRADE_LOCALE_KEY = "persist.global.upgrade_locale";
56 const char *LocaleConfig::DEFAULT_LOCALE_KEY = "const.global.locale";
57 const char *LocaleConfig::DEFAULT_LANGUAGE_KEY = "const.global.language";
58 const char *LocaleConfig::DEFAULT_REGION_KEY = "const.global.region";
59 const char *LocaleConfig::SIM_COUNTRY_CODE_KEY = "telephony.sim.countryCode0";
60 const char *LocaleConfig::SUPPORTED_LOCALES_NAME = "supported_locales";
61 const char *LocaleConfig::REGIONS_LANGUAGES_PATH = "etc/xml/i18n_param_config.xml";
62 const char *LocaleConfig::REGIONS_LANGUAGES_NAME = "i18n_param_config";
63 const char *LocaleConfig::SUPPORTED_REGIONS_NAME = "supported_regions";
64 const char *LocaleConfig::WHITE_LANGUAGES_NAME = "white_languages";
65 const char *LocaleConfig::FORBIDDEN_LANGUAGES_NAME = "forbidden_languages";
66 const char *LocaleConfig::FORBIDDEN_REGIONS_NAME = "forbidden_regions";
67 const char *LocaleConfig::SUPPORTED_LOCALES_PATH = "/system/usr/ohos_locale_config/supported_locales.xml";
68 const char *LocaleConfig::SUPPORT_LOCALES_PATH = "/etc/ohos_lang_config/supported_locales.xml";
69 const char *LocaleConfig::SUPPORT_LOCALES_NAME = "supported_locales";
70 const char *LocaleConfig::DIALECT_LANGS_PATH = "/system/usr/ohos_locale_config/dialect_languages.xml";
71 const char *LocaleConfig::DIALECT_LANGS_NAME = "dialect_langs";
72 const char *LocaleConfig::OVERRIDE_SUPPORTED_REGIONS_NAME = "supported_regions";
73 const char *LocaleConfig::OVERRIDE_SUPPORTED_REGIONS_PATH =
74     "/system/usr/ohos_locale_config/region/supported_regions.xml";
75 const char *LocaleConfig::DEFAULT_LOCALE = "en-Latn";
76 const char *LocaleConfig::supportLocalesTag = "supported_locales";
77 const char *LocaleConfig::LANG_PATH = "/etc/ohos_lang_config/";
78 const char *LocaleConfig::REGION_PATH = "/system/usr/ohos_locale_config/region/";
79 const char *LocaleConfig::rootTag = "languages";
80 const char *LocaleConfig::secondRootTag = "lang";
81 const char *LocaleConfig::rootRegion = "regions";
82 const char *LocaleConfig::secondRootRegion = "region";
83 const char *LocaleConfig::NUMBER_SYSTEM_KEY = "-nu-";
84 const std::string LocaleConfig::HOUR_EVENT_DATA = "24HourChange";
85 unordered_set<string> LocaleConfig::supportedLocales;
86 unordered_set<string> LocaleConfig::supportLocales;
87 unordered_set<string> LocaleConfig::supportedRegions;
88 unordered_set<string> LocaleConfig::overrideSupportedRegions;
89 unordered_set<string> LocaleConfig::dialectLang;
90 unordered_set<string> LocaleConfig::blockedLanguages;
91 unordered_set<string> LocaleConfig::blockedRegions;
92 unordered_map<string, unordered_set<string>> LocaleConfig::blockedLanguageRegions;
93 unordered_set<string> LocaleConfig::whiteLanguages;
94 std::set<std::string> LocaleConfig::extendWhiteLanguageList;
95 unordered_map<string, string> LocaleConfig::dialectMap {
96     { "es-Latn-419", "es-Latn-419" },
97     { "es-Latn-BO", "es-Latn-419" },
98     { "es-Latn-BR", "es-Latn-419" },
99     { "es-Latn-BZ", "es-Latn-419" },
100     { "es-Latn-CL", "es-Latn-419" },
101     { "es-Latn-CO", "es-Latn-419" },
102     { "es-Latn-CR", "es-Latn-419" },
103     { "es-Latn-CU", "es-Latn-419" },
104     { "es-Latn-DO", "es-Latn-419" },
105     { "es-Latn-EC", "es-Latn-419" },
106     { "es-Latn-GT", "es-Latn-419" },
107     { "es-Latn-HN", "es-Latn-419" },
108     { "es-Latn-MX", "es-Latn-419" },
109     { "es-Latn-NI", "es-Latn-419" },
110     { "es-Latn-PA", "es-Latn-419" },
111     { "es-Latn-PE", "es-Latn-419" },
112     { "es-Latn-PR", "es-Latn-419" },
113     { "es-Latn-PY", "es-Latn-419" },
114     { "es-Latn-SV", "es-Latn-419" },
115     { "es-Latn-US", "es-Latn-419" },
116     { "es-Latn-UY", "es-Latn-419" },
117     { "es-Latn-VE", "es-Latn-419" },
118     { "pt-Latn-PT", "pt-Latn-PT" },
119     { "en-Latn-US", "en-Latn-US" }
120 };
121 
122 unordered_map<string, string> LocaleConfig::localDigitMap {
123     { "ar", "arab" },
124     { "as", "beng" },
125     { "bn", "beng" },
126     { "fa", "arabext" },
127     { "mr", "deva" },
128     { "my", "mymr" },
129     { "ne", "deva" },
130     { "ur", "latn" }
131 };
132 
133 std::map<std::string, std::string> LocaleConfig::supportedDialectLocales;
134 std::map<string, string> LocaleConfig::locale2DisplayName {};
135 std::map<string, string> LocaleConfig::region2DisplayName {};
136 std::string LocaleConfig::currentDialectLocale = "";
137 std::string LocaleConfig::currentOverrideRegion = "";
138 std::mutex LocaleConfig::dialectLocaleMutex;
139 std::mutex LocaleConfig::region2DisplayNameMutex;
140 std::mutex LocaleConfig::locale2DisplayNameMutex;
141 
142 set<std::string> LocaleConfig::validCaTag {
143     "buddhist",
144     "chinese",
145     "coptic",
146     "dangi",
147     "ethioaa",
148     "ethiopic",
149     "gregory",
150     "hebrew",
151     "indian",
152     "islamic",
153     "islamic-umalqura",
154     "islamic-tbla",
155     "islamic-civil",
156     "islamic-rgsa",
157     "iso8601",
158     "japanese",
159     "persian",
160     "roc",
161     "islamicc",
162 };
163 set<std::string> LocaleConfig::validCoTag {
164     "big5han",
165     "compat",
166     "dict",
167     "direct",
168     "ducet",
169     "eor",
170     "gb2312",
171     "phonebk",
172     "phonetic",
173     "pinyin",
174     "reformed",
175     "searchjl",
176     "stroke",
177     "trad",
178     "unihan",
179     "zhuyin",
180 };
181 set<std::string> LocaleConfig::validKnTag {
182     "true",
183     "false",
184 };
185 set<std::string> LocaleConfig::validKfTag {
186     "upper",
187     "lower",
188     "false",
189 };
190 set<std::string> LocaleConfig::validNuTag {
191     "adlm", "ahom", "arab", "arabext", "bali", "beng",
192     "bhks", "brah", "cakm", "cham", "deva", "diak",
193     "fullwide", "gong", "gonm", "gujr", "guru", "hanidec",
194     "hmng", "hmnp", "java", "kali", "khmr", "knda",
195     "lana", "lanatham", "laoo", "latn", "lepc", "limb",
196     "mathbold", "mathdbl", "mathmono", "mathsanb", "mathsans", "mlym",
197     "modi", "mong", "mroo", "mtei", "mymr", "mymrshan",
198     "mymrtlng", "newa", "nkoo", "olck", "orya", "osma",
199     "rohg", "saur", "segment", "shrd", "sind", "sinh",
200     "sora", "sund", "takr", "talu", "tamldec", "telu",
201     "thai", "tibt", "tirh", "vaii", "wara", "wcho",
202 };
203 set<std::string> LocaleConfig::validHcTag {
204     "h12",
205     "h23",
206     "h11",
207     "h24",
208 };
209 
210 static unordered_map<string, string> g_languageMap = {
211     { "zh-Hans", "zh-Hans" },
212     { "zh-Hant-HK", "zh-Hant-HK" },
213     { "zh-Hant", "zh-Hant" },
214     { "my-Qaag", "my-Qaag" },
215     { "es-Latn-419", "es-419" },
216     { "es-Latn-US", "es-419" },
217     { "az-Latn", "az-Latn" },
218     { "bs-Latn", "bs-Latn" },
219     { "en-Latn-US", "en" },
220     { "en-Qaag", "en-Qaag" },
221     { "uz-Latn", "uz-Latn" },
222     { "sr-Latn", "sr-Latn" },
223     { "jv-Latn", "jv-Latn" },
224     { "pt-Latn-BR", "pt-BR" },
225     { "pa-Guru", "pa-Guru" },
226     { "mai-Deva", "mai-Deva" }
227 };
228 
Adjust(const string & origin)229 string Adjust(const string &origin)
230 {
231     auto iter = g_languageMap.find(origin);
232     if (iter != g_languageMap.end()) {
233         return iter->second;
234     }
235     for (iter = g_languageMap.begin(); iter != g_languageMap.end(); ++iter) {
236         string key = iter->first;
237         if (!origin.compare(0, key.length(), key)) {
238             return iter->second;
239         }
240     }
241     return origin;
242 }
243 
GetDisplayLanguageInner(const string & language,const string & displayLocaleTag,bool sentenceCase)244 string GetDisplayLanguageInner(const string &language, const string &displayLocaleTag, bool sentenceCase)
245 {
246     icu::UnicodeString unistr;
247     // 0 is the start position of language, 2 is the length of zh and fa
248     if (!language.compare(0, 2, "zh") || !language.compare(0, 2, "fa") || !language.compare(0, 2, "ro")) {
249         UErrorCode status = U_ZERO_ERROR;
250         icu::Locale displayLocale = icu::Locale::forLanguageTag(displayLocaleTag.c_str(), status);
251         if (status != U_ZERO_ERROR) {
252             return "";
253         }
254         icu::LocaleDisplayNames *dspNames = icu::LocaleDisplayNames::createInstance(displayLocale,
255             UDialectHandling::ULDN_DIALECT_NAMES);
256         icu::Locale tempLocale = icu::Locale::forLanguageTag(language.c_str(), status);
257         if (status != U_ZERO_ERROR) {
258             return "";
259         }
260         if (dspNames != nullptr) {
261             dspNames->localeDisplayName(tempLocale, unistr);
262             delete dspNames;
263         }
264     } else {
265         UErrorCode status = U_ZERO_ERROR;
266         icu::Locale displayLoc = icu::Locale::forLanguageTag(displayLocaleTag, status);
267         if (status != U_ZERO_ERROR) {
268             return "";
269         }
270         icu::Locale locale = icu::Locale::forLanguageTag(language, status);
271         if (status != U_ZERO_ERROR) {
272             return "";
273         }
274         locale.getDisplayName(displayLoc, unistr);
275     }
276     if (sentenceCase) {
277         UChar32 ch = ucase_toupper(unistr.char32At(0));
278         unistr.replace(0, 1, ch);
279     }
280     string out;
281     unistr.toUTF8String(out);
282     return out;
283 }
284 
285 bool LocaleConfig::listsInitialized = LocaleConfig::InitializeLists();
286 
GetSystemLanguage()287 string LocaleConfig::GetSystemLanguage()
288 {
289     std::string systemLanguage = ReadSystemParameter(LANGUAGE_KEY, CONFIG_LEN);
290     if (systemLanguage.empty()) {
291         systemLanguage = ReadSystemParameter(DEFAULT_LANGUAGE_KEY, CONFIG_LEN);
292     }
293     return systemLanguage;
294 }
295 
GetSystemRegion()296 string LocaleConfig::GetSystemRegion()
297 {
298     std::string systemRegion = GetCountry(LOCALE_KEY);
299     if (systemRegion.empty()) {
300         systemRegion = GetCountry(DEFAULT_LOCALE_KEY);
301     }
302     if (systemRegion.empty()) {
303         return "CN";
304     }
305     return systemRegion;
306 }
307 
GetCountry(const string & parameter)308 string LocaleConfig::GetCountry(const string& parameter)
309 {
310     std::string systemRegion = ReadSystemParameter(parameter.c_str(), CONFIG_LEN);
311     if (systemRegion.empty()) {
312         HILOG_INFO_I18N("GetCountry: ReadSystemParameter %{public}s failed", parameter.c_str());
313         return systemRegion;
314     }
315     UErrorCode status = U_ZERO_ERROR;
316     icu::Locale origin = icu::Locale::forLanguageTag(systemRegion, status);
317     if (U_SUCCESS(status)) {
318         const char* country = origin.getCountry();
319         if (country != nullptr) {
320             return country;
321         }
322         HILOG_INFO_I18N("GetCountry: %{public}s get failed, country is nullptr", parameter.c_str());
323     } else {
324         HILOG_INFO_I18N("GetCountry: %{public}s get failed, U_SUCCESS is false", parameter.c_str());
325     }
326     return "";
327 }
328 
GetSystemLocale()329 string LocaleConfig::GetSystemLocale()
330 {
331     std::string systemLocale = ReadSystemParameter(LOCALE_KEY, CONFIG_LEN);
332     if (systemLocale.empty()) {
333         systemLocale = ReadSystemParameter(DEFAULT_LOCALE_KEY, CONFIG_LEN);
334     }
335     return systemLocale;
336 }
337 
IsValidLanguage(const string & language)338 bool LocaleConfig::IsValidLanguage(const string &language)
339 {
340     string::size_type size = language.size();
341     if ((size != LANGUAGE_LEN) && (size != LANGUAGE_LEN + 1)) {
342         return false;
343     }
344     for (size_t i = 0; i < size; ++i) {
345         if ((language[i] > 'z') || (language[i] < 'a')) {
346             return false;
347         }
348     }
349     return true;
350 }
351 
IsValidRegion(const string & region)352 bool LocaleConfig::IsValidRegion(const string &region)
353 {
354     string::size_type size = region.size();
355     if (size != LocaleInfo::REGION_LEN) {
356         return false;
357     }
358     for (size_t i = 0; i < LocaleInfo::REGION_LEN; ++i) {
359         if ((region[i] > 'Z') || (region[i] < 'A')) {
360             return false;
361         }
362     }
363     return true;
364 }
365 
IsValidTag(const string & tag)366 bool LocaleConfig::IsValidTag(const string &tag)
367 {
368     if (!tag.size()) {
369         return false;
370     }
371     vector<string> splits;
372     Split(tag, "-", splits);
373     if (!IsValidLanguage(splits[0])) {
374         return false;
375     }
376     return true;
377 }
378 
Split(const string & src,const string & sep,vector<string> & dest)379 void LocaleConfig::Split(const string &src, const string &sep, vector<string> &dest)
380 {
381     string::size_type begin = 0;
382     string::size_type end = src.find(sep);
383     while (end != string::npos) {
384         dest.push_back(src.substr(begin, end - begin));
385         begin = end + sep.size();
386         end = src.find(sep, begin);
387     }
388     if (begin != src.size()) {
389         dest.push_back(src.substr(begin));
390     }
391 }
392 
Split(const string & src,const string & sep,std::unordered_set<string> & dest)393 void LocaleConfig::Split(const string &src, const string &sep, std::unordered_set<string> &dest)
394 {
395     string::size_type begin = 0;
396     string::size_type end = src.find(sep);
397     while (end != string::npos) {
398         dest.insert(src.substr(begin, end - begin));
399         begin = end + sep.size();
400         end = src.find(sep, begin);
401     }
402     if (begin != src.size()) {
403         dest.insert(src.substr(begin));
404     }
405 }
406 
407 // language in white languages should have script.
GetSystemLanguages(vector<string> & ret)408 void LocaleConfig::GetSystemLanguages(vector<string> &ret)
409 {
410     std::copy(whiteLanguages.begin(), whiteLanguages.end(), std::back_inserter(ret));
411 }
412 
GetSupportedLocales()413 const unordered_set<string>& LocaleConfig::GetSupportedLocales()
414 {
415     return supportedLocales;
416 }
417 
GetSupportedRegions()418 const unordered_set<string>& LocaleConfig::GetSupportedRegions()
419 {
420     return supportedRegions;
421 }
422 
GetSystemCountries(vector<string> & ret)423 void LocaleConfig::GetSystemCountries(vector<string> &ret)
424 {
425     std::copy(supportedRegions.begin(), supportedRegions.end(), std::back_inserter(ret));
426 }
427 
IsSuggested(const string & language)428 bool LocaleConfig::IsSuggested(const string &language)
429 {
430     unordered_set<string> relatedLocales;
431     vector<string> simCountries;
432     GetCountriesFromSim(simCountries);
433     GetRelatedLocales(relatedLocales, simCountries);
434     for (auto iter = relatedLocales.begin(); iter != relatedLocales.end();) {
435         if (extendWhiteLanguageList.find(*iter) == extendWhiteLanguageList.end()) {
436             iter = relatedLocales.erase(iter);
437         } else {
438             ++iter;
439         }
440     }
441     string mainLanguage = GetMainLanguage(language);
442     return relatedLocales.find(mainLanguage) != relatedLocales.end();
443 }
444 
IsSuggested(const std::string & language,const std::string & region)445 bool LocaleConfig::IsSuggested(const std::string &language, const std::string &region)
446 {
447     unordered_set<string> relatedLocales;
448     vector<string> countries { region };
449     GetRelatedLocales(relatedLocales, countries);
450     for (auto iter = relatedLocales.begin(); iter != relatedLocales.end();) {
451         if (extendWhiteLanguageList.find(*iter) == extendWhiteLanguageList.end()) {
452             iter = relatedLocales.erase(iter);
453         } else {
454             ++iter;
455         }
456     }
457     string mainLanguage = GetMainLanguage(language);
458     return relatedLocales.find(mainLanguage) != relatedLocales.end();
459 }
460 
ExtendWhiteLanguages()461 void LocaleConfig::ExtendWhiteLanguages()
462 {
463     UErrorCode status = U_ZERO_ERROR;
464     for (auto iter = whiteLanguages.begin(); iter != whiteLanguages.end(); ++iter) {
465         extendWhiteLanguageList.insert(*iter);
466         icu::Locale locale = icu::Locale::forLanguageTag((*iter).c_str(), status);
467         locale.addLikelySubtags(status);
468         if (U_FAILURE(status)) {
469             HILOG_INFO_I18N("create Locale object for %{public}s failed.", (*iter).c_str());
470             continue;
471         }
472         const char* baseName = locale.getBaseName();
473         if (baseName != nullptr) {
474             std::string baseNameStr(baseName);
475             std::replace(baseNameStr.begin(), baseNameStr.end(), '_', '-');
476             extendWhiteLanguageList.insert(baseNameStr);
477         }
478     }
479 }
480 
GetRelatedLocales(unordered_set<string> & relatedLocales,vector<string> countries)481 void LocaleConfig::GetRelatedLocales(unordered_set<string> &relatedLocales, vector<string> countries)
482 {
483     // remove unsupported countries
484     const unordered_set<string> &regions = GetSupportedRegions();
485     for (auto iter = countries.begin(); iter != countries.end();) {
486         if (regions.find(*iter) == regions.end()) {
487             iter = countries.erase(iter);
488         } else {
489             ++iter;
490         }
491     }
492     const unordered_set<string> &locales = GetSupportedLocales();
493     for (string locale : locales) {
494         bool find = false;
495         for (string country : countries) {
496             if (locale.find(country) != string::npos) {
497                 find = true;
498                 break;
499             }
500         }
501         if (!find) {
502             continue;
503         }
504         string mainLanguage = GetMainLanguage(locale);
505         if (mainLanguage != "") {
506             relatedLocales.insert(mainLanguage);
507         }
508     }
509 }
510 
GetCountriesFromSim(vector<string> & simCountries)511 void LocaleConfig::GetCountriesFromSim(vector<string> &simCountries)
512 {
513     simCountries.push_back(GetSystemRegion());
514     char value[CONFIG_LEN];
515     int code = GetParameter(SIM_COUNTRY_CODE_KEY, "", value, CONFIG_LEN);
516     if (code > 0) {
517         simCountries.push_back(value);
518     }
519 }
520 
GetListFromFile(const char * path,const char * resourceName,unordered_set<string> & ret)521 void LocaleConfig::GetListFromFile(const char *path, const char *resourceName, unordered_set<string> &ret)
522 {
523     xmlKeepBlanksDefault(0);
524     if (!path) {
525         return;
526     }
527     xmlDocPtr doc = xmlParseFile(path);
528     if (!doc) {
529         return;
530     }
531     xmlNodePtr cur = xmlDocGetRootElement(doc);
532     if (!cur || xmlStrcmp(cur->name, reinterpret_cast<const xmlChar *>(resourceName))) {
533         xmlFreeDoc(doc);
534         return;
535     }
536     cur = cur->xmlChildrenNode;
537     xmlChar *content = nullptr;
538     while (cur != nullptr) {
539         content = xmlNodeGetContent(cur);
540         if (content != nullptr) {
541             ret.insert(reinterpret_cast<const char*>(content));
542             xmlFree(content);
543             cur = cur->next;
544         } else {
545             break;
546         }
547     }
548     xmlFreeDoc(doc);
549 }
550 
ProcessForbiddenRegions(const unordered_set<string> & forbiddenRegions)551 void LocaleConfig::ProcessForbiddenRegions(const unordered_set<string> &forbiddenRegions)
552 {
553     for (auto it = forbiddenRegions.begin(); it != forbiddenRegions.end(); ++it) {
554         size_t pos = it->rfind("-");
555         std::string language = it->substr(0, pos);
556         std::string region = it->substr(pos + 1);
557         if (language.compare("*") == 0) {
558             blockedRegions.insert(region);
559         } else {
560             if (blockedLanguageRegions.find(language) == blockedLanguageRegions.end()) {
561                 blockedLanguageRegions[language] = { region };
562             } else {
563                 blockedLanguageRegions[language].insert(region);
564             }
565         }
566     }
567 }
568 
Expunge(unordered_set<string> & src,const unordered_set<string> & another)569 void LocaleConfig::Expunge(unordered_set<string> &src, const unordered_set<string> &another)
570 {
571     for (auto iter = src.begin(); iter != src.end();) {
572         if (another.find(*iter) != another.end()) {
573             iter = src.erase(iter);
574         } else {
575             ++iter;
576         }
577     }
578 }
579 
InitializeLists()580 bool LocaleConfig::InitializeLists()
581 {
582     SetHwIcuDirectory();
583     unordered_set<string> forbiddenRegions;
584     LoadRegionsLanguages(forbiddenRegions);
585     ProcessForbiddenRegions(forbiddenRegions);
586     Expunge(supportedRegions, blockedRegions);
587     Expunge(whiteLanguages, blockedLanguages);
588     GetListFromFile(SUPPORTED_LOCALES_PATH, SUPPORTED_LOCALES_NAME, supportedLocales);
589     GetListFromFile(SUPPORT_LOCALES_PATH, SUPPORT_LOCALES_NAME, supportLocales);
590     GetListFromFile(OVERRIDE_SUPPORTED_REGIONS_PATH, OVERRIDE_SUPPORTED_REGIONS_NAME, overrideSupportedRegions);
591     GetListFromFile(DIALECT_LANGS_PATH, DIALECT_LANGS_NAME, dialectLang);
592     ExtendWhiteLanguages();
593     return true;
594 }
595 
LoadRegionsLanguages(unordered_set<std::string> & forbiddenRegions)596 void LocaleConfig::LoadRegionsLanguages(unordered_set<std::string>& forbiddenRegions)
597 {
598     char buf[MAX_PATH_LEN] = {0};
599     char* path = GetOneCfgFile(REGIONS_LANGUAGES_PATH, buf, MAX_PATH_LEN);
600     xmlKeepBlanksDefault(0);
601     if (!path) {
602         return;
603     }
604     xmlDocPtr doc = xmlParseFile(path);
605     if (!doc) {
606         return;
607     }
608     xmlNodePtr cur = xmlDocGetRootElement(doc);
609     if (!cur || xmlStrcmp(cur->name, reinterpret_cast<const xmlChar *>(REGIONS_LANGUAGES_NAME))) {
610         xmlFreeDoc(doc);
611         return;
612     }
613     cur = cur->xmlChildrenNode;
614     xmlChar *content = nullptr;
615     while (cur != nullptr) {
616         content = xmlNodeGetContent(cur);
617         if (content != nullptr && !xmlStrcmp(cur->name, reinterpret_cast<const xmlChar *>(SUPPORTED_REGIONS_NAME))) {
618             Split(reinterpret_cast<const char*>(content), ",", supportedRegions);
619         } else if (content != nullptr && !xmlStrcmp(cur->name,
620             reinterpret_cast<const xmlChar *>(WHITE_LANGUAGES_NAME))) {
621             Split(reinterpret_cast<const char*>(content), ",", whiteLanguages);
622         } else if (content != nullptr && !xmlStrcmp(cur->name,
623             reinterpret_cast<const xmlChar *>(FORBIDDEN_REGIONS_NAME))) {
624             Split(reinterpret_cast<const char*>(content), ",", forbiddenRegions);
625         } else if (content != nullptr && !xmlStrcmp(cur->name,
626             reinterpret_cast<const xmlChar *>(FORBIDDEN_LANGUAGES_NAME))) {
627             Split(reinterpret_cast<const char*>(content), ",", blockedLanguages);
628         }
629         if (content != nullptr) {
630             xmlFree(content);
631         }
632         cur = cur->next;
633     }
634     xmlFreeDoc(doc);
635 }
636 
GetMainLanguage(const string & language)637 string LocaleConfig::GetMainLanguage(const string &language)
638 {
639     UErrorCode status = U_ZERO_ERROR;
640     icu::Locale origin = icu::Locale::forLanguageTag(language, status);
641     if (status != U_ZERO_ERROR) {
642         return "";
643     }
644     origin.addLikelySubtags(status);
645     if (status != U_ZERO_ERROR) {
646         return "";
647     }
648     icu::LocaleBuilder builder = icu::LocaleBuilder().setLanguage(origin.getLanguage()).
649         setScript(origin.getScript()).setRegion(origin.getCountry());
650     icu::Locale temp = builder.setExtension('u', "").build(status);
651     if (status != U_ZERO_ERROR) {
652         return "";
653     }
654     string fullLanguage = temp.toLanguageTag<string>(status);
655     if (status != U_ZERO_ERROR) {
656         return "";
657     }
658     if (dialectMap.find(fullLanguage) != dialectMap.end()) {
659         return dialectMap[fullLanguage];
660     }
661     builder.setRegion("");
662     temp = builder.build(status);
663     if (status != U_ZERO_ERROR) {
664         return "";
665     }
666     fullLanguage = temp.toLanguageTag<string>(status);
667     if (status != U_ZERO_ERROR) {
668         return "";
669     }
670     return fullLanguage;
671 }
672 
GetDisplayLanguage(const string & language,const string & displayLocale,bool sentenceCase)673 string LocaleConfig::GetDisplayLanguage(const string &language, const string &displayLocale, bool sentenceCase)
674 {
675     std::string result;
676     string adjust = Adjust(language);
677     if (adjust == language) {
678         UErrorCode status = U_ZERO_ERROR;
679         icu::Locale displayLoc = icu::Locale::forLanguageTag(displayLocale, status);
680         if (status != U_ZERO_ERROR) {
681             return PseudoLocalizationProcessor("");
682         }
683         icu::Locale locale = icu::Locale::forLanguageTag(language, status);
684         if (status != U_ZERO_ERROR) {
685             return PseudoLocalizationProcessor("");
686         }
687         icu::UnicodeString unistr;
688         std::string lang(locale.getLanguage());
689         if (dialectLang.find(lang) != dialectLang.end()) {
690             result = GetDisplayLanguageWithDialect(language, displayLocale);
691         }
692     }
693     if (result.empty()) {
694         result = GetDisplayLanguageInner(adjust, displayLocale, sentenceCase);
695     }
696     if (sentenceCase && !result.empty()) {
697         char ch = static_cast<char>(toupper(result[0]));
698         return PseudoLocalizationProcessor(result.replace(0, 1, 1, ch));
699     }
700     return PseudoLocalizationProcessor(result);
701 }
702 
ComputeLocale(const std::string & displayLocale)703 std::string LocaleConfig::ComputeLocale(const std::string &displayLocale)
704 {
705     if (supportedDialectLocales.size() == 0) {
706         xmlKeepBlanksDefault(0);
707         xmlDocPtr doc = xmlParseFile(SUPPORT_LOCALES_PATH);
708         if (!doc) {
709             return "";
710         }
711         xmlNodePtr cur = xmlDocGetRootElement(doc);
712         if (!cur || xmlStrcmp(cur->name, reinterpret_cast<const xmlChar *>(supportLocalesTag))) {
713             xmlFreeDoc(doc);
714             HILOG_INFO_I18N("can not parse language supported locale file");
715             return "";
716         }
717         cur = cur->xmlChildrenNode;
718         while (cur != nullptr) {
719             xmlChar *content = xmlNodeGetContent(cur);
720             if (content == nullptr) {
721                 HILOG_INFO_I18N("get xml node content failed");
722                 break;
723             }
724             std::map<std::string, std::string> localeInfoConfigs = {};
725             LocaleInfo localeinfo(reinterpret_cast<const char*>(content), localeInfoConfigs);
726             std::string maximizeLocale = localeinfo.Maximize();
727             const char* key = maximizeLocale.c_str();
728             const char* value = reinterpret_cast<const char*>(content);
729             SetSupportedDialectLocales(key, value);
730             xmlFree(content);
731             cur = cur->next;
732         }
733         xmlFreeDoc(doc);
734     }
735     std::map<std::string, std::string> configs = {};
736     LocaleInfo localeinfo(displayLocale, configs);
737     std::string maximizeLocale = localeinfo.Maximize();
738     if (supportedDialectLocales.find(maximizeLocale) != supportedDialectLocales.end()) {
739         return supportedDialectLocales.at(maximizeLocale);
740     }
741     return "";
742 }
743 
SetSupportedDialectLocales(const char * key,const char * value)744 void LocaleConfig::SetSupportedDialectLocales(const char* key, const char* value)
745 {
746     std::lock_guard<std::mutex> dialectLocaleLock(dialectLocaleMutex);
747     supportedDialectLocales.insert(
748         std::make_pair<std::string, std::string>(key, value));
749 }
750 
ReadLangData(const char * langDataPath)751 void LocaleConfig::ReadLangData(const char *langDataPath)
752 {
753     xmlKeepBlanksDefault(0);
754     if (langDataPath == nullptr) {
755         return;
756     }
757     xmlDocPtr doc = xmlParseFile(langDataPath);
758     if (!doc) {
759         HILOG_INFO_I18N("can not open language data file");
760         return;
761     }
762     xmlNodePtr cur = xmlDocGetRootElement(doc);
763     if (!cur || xmlStrcmp(cur->name, reinterpret_cast<const xmlChar *>(rootTag))) {
764         xmlFreeDoc(doc);
765         HILOG_INFO_I18N("parse language data file failed");
766         return;
767     }
768     cur = cur->xmlChildrenNode;
769     while (cur != nullptr && !xmlStrcmp(cur->name, reinterpret_cast<const xmlChar *>(secondRootTag))) {
770         xmlChar *langContents[ELEMENT_NUM] = { 0 }; // 2 represent langid, displayname;
771         xmlNodePtr langValue = cur->xmlChildrenNode;
772         bool xmlNodeNull = false;
773         for (size_t i = 0; i < ELEMENT_NUM && langValue != nullptr; i++) {
774             langContents[i] = xmlNodeGetContent(langValue);
775             langValue = langValue->next;
776             if (langContents[i] == nullptr) {
777                 xmlNodeNull = true;
778             }
779         }
780         if (!xmlNodeNull) {
781             // 0 represents langid index, 1 represents displayname index
782             const char* key = reinterpret_cast<const char *>(langContents[0]);
783             const char* value = reinterpret_cast<const char *>(langContents[1]);
784             SetLocale2DisplayName(key, value);
785         }
786         for (size_t i = 0; i < ELEMENT_NUM; i++) {
787             if (langContents[i] != nullptr) {
788                 xmlFree(langContents[i]);
789             }
790         }
791         cur = cur->next;
792     }
793     xmlFreeDoc(doc);
794 }
795 
SetRegion2DisplayName(const char * key,const char * value)796 void LocaleConfig::SetRegion2DisplayName(const char* key, const char* value)
797 {
798     std::lock_guard<std::mutex> regionDisplayLock(region2DisplayNameMutex);
799     region2DisplayName.insert(
800         std::make_pair<std::string, std::string>(key, value));
801 }
802 
SetLocale2DisplayName(const char * key,const char * value)803 void LocaleConfig::SetLocale2DisplayName(const char* key, const char* value)
804 {
805     std::lock_guard<std::mutex> localeDisplayLock(locale2DisplayNameMutex);
806     locale2DisplayName.insert(
807         std::make_pair<std::string, std::string>(key, value));
808 }
809 
ReadRegionData(const char * regionDataPath)810 void LocaleConfig::ReadRegionData(const char *regionDataPath)
811 {
812     xmlKeepBlanksDefault(0);
813     if (regionDataPath == nullptr) {
814         return;
815     }
816     xmlDocPtr doc = xmlParseFile(regionDataPath);
817     if (!doc) {
818         HILOG_INFO_I18N("can not open region data file");
819         return;
820     }
821     xmlNodePtr cur = xmlDocGetRootElement(doc);
822     if (cur) {
823         HILOG_INFO_I18N("cur pointer is true");
824     }
825     if (!cur || xmlStrcmp(cur->name, reinterpret_cast<const xmlChar *>(rootRegion))) {
826         xmlFreeDoc(doc);
827         HILOG_INFO_I18N("parse region data file failed");
828         return;
829     }
830     cur = cur->xmlChildrenNode;
831     while (cur != nullptr && !xmlStrcmp(cur->name, reinterpret_cast<const xmlChar *>(secondRootRegion))) {
832         xmlChar *regionContents[ELEMENT_NUM] = { 0 };
833         xmlNodePtr regionValue = cur->xmlChildrenNode;
834         bool xmlNodeNull = false;
835         for (size_t i = 0; i < ELEMENT_NUM && regionValue != nullptr; i++) {
836             regionContents[i] = xmlNodeGetContent(regionValue);
837             regionValue = regionValue->next;
838             if (regionContents[i] == nullptr) {
839                 xmlNodeNull = true;
840             }
841         }
842         if (!xmlNodeNull) {
843             // 0 represents langid index, 1 represents displayname index
844             const char* regionKey = reinterpret_cast<const char *>(regionContents[0]);
845             const char* regionVal = reinterpret_cast<const char *>(regionContents[1]);
846             SetRegion2DisplayName(regionKey, regionVal);
847         }
848         for (size_t i = 0; i < ELEMENT_NUM; i++) {
849             if (regionContents[i] != nullptr) {
850                 xmlFree(regionContents[i]);
851             }
852         }
853         cur = cur->next;
854     }
855     xmlFreeDoc(doc);
856 }
857 
GetDisplayLanguageWithDialect(const std::string & localeStr,const std::string & displayLocale)858 string LocaleConfig::GetDisplayLanguageWithDialect(const std::string &localeStr, const std::string &displayLocale)
859 {
860     std::string finalLocale = ComputeLocale(displayLocale);
861     if (finalLocale.empty()) {
862         return "";
863     }
864     if (finalLocale.compare(currentDialectLocale) != 0) {
865         std::string xmlPath = LANG_PATH + finalLocale + ".xml";
866         locale2DisplayName.clear();
867         ReadLangData(xmlPath.c_str());
868         currentDialectLocale = finalLocale;
869     }
870     if (locale2DisplayName.find(localeStr) != locale2DisplayName.end()) {
871         return locale2DisplayName.at(localeStr);
872     }
873     std::map<std::string, std::string> configs = {};
874     LocaleInfo locale(localeStr, configs);
875     std::string language = locale.GetLanguage();
876     std::string scripts = locale.GetScript();
877     std::string region = locale.GetRegion();
878     if (scripts.length() != 0) {
879         std::string languageAndScripts = language + "-" + scripts;
880         if (locale2DisplayName.find(languageAndScripts) != locale2DisplayName.end()) {
881             return locale2DisplayName.at(languageAndScripts);
882         }
883     }
884     if (region.length() != 0) {
885         std::string languageAndRegion = language + "-" + region;
886         if (locale2DisplayName.find(languageAndRegion) != locale2DisplayName.end()) {
887             return locale2DisplayName.at(languageAndRegion);
888         }
889     }
890     if (locale2DisplayName.find(language) != locale2DisplayName.end()) {
891         return locale2DisplayName.at(language);
892     }
893     return "";
894 }
895 
GetDisplayOverrideRegion(const std::string & region,const std::string & displayLocale)896 string LocaleConfig::GetDisplayOverrideRegion(const std::string &region, const std::string &displayLocale)
897 {
898     UErrorCode status = U_ZERO_ERROR;
899     icu::Locale originLocale;
900     icu::UnicodeString displayRegion;
901     if (displayLocale.compare(currentOverrideRegion) != 0) {
902         std::string xmlPath = REGION_PATH + displayLocale + ".xml";
903         region2DisplayName.clear();
904         ReadRegionData(xmlPath.c_str());
905         currentOverrideRegion = displayLocale;
906     }
907     if (region2DisplayName.find(region) != region2DisplayName.end()) {
908         return region2DisplayName.at(region);
909     } else {
910         icu::Locale locale = icu::Locale::forLanguageTag(displayLocale, status);
911         if (status != U_ZERO_ERROR) {
912             return "";
913         }
914         if (IsValidRegion(region)) {
915             icu::LocaleBuilder builder = icu::LocaleBuilder().setRegion(region);
916             originLocale = builder.build(status);
917         } else {
918             originLocale = icu::Locale::forLanguageTag(region, status);
919         }
920         originLocale.getDisplayCountry(locale, displayRegion);
921         std::string result;
922         displayRegion.toUTF8String(result);
923         return result;
924     }
925 }
926 
GetDisplayRegion(const string & region,const string & displayLocale,bool sentenceCase)927 string LocaleConfig::GetDisplayRegion(const string &region, const string &displayLocale, bool sentenceCase)
928 {
929     UErrorCode status = U_ZERO_ERROR;
930     icu::Locale originLocale;
931     if (IsValidRegion(region)) {
932         icu::LocaleBuilder builder = icu::LocaleBuilder().setRegion(region);
933         originLocale = builder.build(status);
934     } else {
935         originLocale = icu::Locale::forLanguageTag(region, status);
936     }
937     std::string country(originLocale.getCountry());
938     if (country.length() == 0) {
939         return PseudoLocalizationProcessor("");
940     }
941     if (status != U_ZERO_ERROR) {
942         return PseudoLocalizationProcessor("");
943     }
944     icu::Locale locale = icu::Locale::forLanguageTag(displayLocale, status);
945     if (status != U_ZERO_ERROR) {
946         return PseudoLocalizationProcessor("");
947     }
948     icu::UnicodeString unistr;
949     icu::UnicodeString displayRegion;
950     std::string result;
951     if (overrideSupportedRegions.find(displayLocale) != overrideSupportedRegions.end()) {
952         result = GetDisplayOverrideRegion(region, displayLocale);
953     } else {
954         originLocale.getDisplayCountry(locale, displayRegion);
955         displayRegion.toUTF8String(result);
956     }
957     if (sentenceCase) {
958         char ch = static_cast<char>(toupper(result[0]));
959         return PseudoLocalizationProcessor(result.replace(0, 1, 1, ch));
960     }
961     return PseudoLocalizationProcessor(result);
962 }
963 
IsRTL(const string & locale)964 bool LocaleConfig::IsRTL(const string &locale)
965 {
966     icu::Locale curLocale(locale.c_str());
967     return curLocale.isRightToLeft();
968 }
969 
parseExtension(const std::string & extension,std::map<std::string,std::string> & map)970 void parseExtension(const std::string &extension, std::map<std::string, std::string> &map)
971 {
972     std::string pattern = "-..-";
973     std::regex express(pattern);
974 
975     std::regex_token_iterator<std::string::const_iterator> begin1(extension.cbegin(), extension.cend(), express);
976     std::regex_token_iterator<std::string::const_iterator> begin2(extension.cbegin(), extension.cend(), express, -1);
977     begin2++;
978     for (; begin1 != std::sregex_token_iterator() && begin2 != std::sregex_token_iterator(); begin1++, begin2++) {
979         map.insert(std::pair<std::string, std::string>(begin1->str(), begin2->str()));
980     }
981 }
982 
setExtension(std::string & extension,const std::string & tag,const std::set<string> & validValue,const std::map<std::string,std::string> & extensionMap,const std::map<std::string,std::string> & defaultExtensionMap)983 void setExtension(std::string &extension, const std::string &tag, const std::set<string> &validValue,
984     const std::map<std::string, std::string> &extensionMap,
985     const std::map<std::string, std::string> &defaultExtensionMap)
986 {
987     std::string value;
988     auto it = extensionMap.find(tag);
989     if (it != extensionMap.end()) {
990         value = it->second;
991         if (validValue.find(value) == validValue.end()) {
992             return;
993         } else {
994             extension += tag;
995             extension += value;
996         }
997     } else {
998         it = defaultExtensionMap.find(tag);
999         if (it != defaultExtensionMap.end()) {
1000             value = it->second;
1001             if (validValue.find(value) == validValue.end()) {
1002                 return;
1003             } else {
1004                 extension += tag;
1005                 extension += value;
1006             }
1007         }
1008     }
1009 }
1010 
setOtherExtension(std::string & extension,std::map<std::string,std::string> & extensionMap,std::map<std::string,std::string> & defaultExtensionMap)1011 void setOtherExtension(std::string &extension, std::map<std::string, std::string> &extensionMap,
1012     std::map<std::string, std::string> &defaultExtensionMap)
1013 {
1014     std::set<std::string> tags;
1015     tags.insert("-ca-");
1016     tags.insert("-co-");
1017     tags.insert("-kn-");
1018     tags.insert("-kf-");
1019     tags.insert("-nu-");
1020     tags.insert("-hc-");
1021 
1022     for (auto it = tags.begin(); it != tags.end(); it++) {
1023         extensionMap.erase(*it);
1024         defaultExtensionMap.erase(*it);
1025     }
1026 
1027     for (auto it = defaultExtensionMap.begin(); it != defaultExtensionMap.end(); it++) {
1028         extensionMap.insert(std::pair<std::string, std::string>(it->first, it->second));
1029     }
1030 
1031     for (auto it = extensionMap.begin(); it != extensionMap.end(); it++) {
1032         extension += it->first;
1033         extension += it->second;
1034     }
1035 }
1036 
GetValidLocale(const std::string & localeTag)1037 std::string LocaleConfig::GetValidLocale(const std::string &localeTag)
1038 {
1039     std::string baseLocale = "";
1040     std::string extension = "";
1041     std::size_t found = localeTag.find("-u-");
1042     baseLocale = localeTag.substr(0, found);
1043     if (found != std::string::npos) {
1044         extension = localeTag.substr(found);
1045     }
1046     std::map<std::string, std::string> extensionMap;
1047     if (extension != "") {
1048         parseExtension(extension, extensionMap);
1049     }
1050 
1051     std::string systemLocaleTag = GetSystemLocale();
1052     std::string defaultExtension = "";
1053     found = systemLocaleTag.find("-u-");
1054     if (found != std::string::npos) {
1055         defaultExtension = systemLocaleTag.substr(found);
1056     }
1057     std::map<std::string, std::string> defaultExtensionMap;
1058     if (defaultExtension != "") {
1059         parseExtension(defaultExtension, defaultExtensionMap);
1060     }
1061 
1062     std::string ext = "";
1063     setExtension(ext, "-ca-", validCaTag, extensionMap, defaultExtensionMap);
1064     setExtension(ext, "-co-", validCoTag, extensionMap, defaultExtensionMap);
1065     setExtension(ext, "-kn-", validKnTag, extensionMap, defaultExtensionMap);
1066     setExtension(ext, "-kf-", validKfTag, extensionMap, defaultExtensionMap);
1067     setExtension(ext, "-nu-", validNuTag, extensionMap, defaultExtensionMap);
1068     setExtension(ext, "-hc-", validHcTag, extensionMap, defaultExtensionMap);
1069 
1070     std::string otherExt = "";
1071     setOtherExtension(otherExt, extensionMap, defaultExtensionMap);
1072     if (ext != "" || otherExt != "") {
1073         return baseLocale + "-u" + ext + otherExt;
1074     } else {
1075         return baseLocale;
1076     }
1077 }
1078 
IsEmpty24HourClock()1079 bool LocaleConfig::IsEmpty24HourClock()
1080 {
1081     std::string is24Hour = ReadSystemParameter(HOUR_KEY, CONFIG_LEN);
1082     return is24Hour.empty();
1083 }
1084 
Is24HourClock()1085 bool LocaleConfig::Is24HourClock()
1086 {
1087     std::string is24Hour = ReadSystemParameter(HOUR_KEY, CONFIG_LEN);
1088     if (is24Hour.empty()) {
1089         std::string systemLocale = GetSystemLocale();
1090         return Is24HourLocale(systemLocale);
1091     }
1092     if (is24Hour.compare("true") == 0) {
1093         return true;
1094     }
1095     return false;
1096 }
1097 
GetSystemHour()1098 std::string LocaleConfig::GetSystemHour()
1099 {
1100     std::string is24HourVal = ReadSystemParameter(HOUR_KEY, CONFIG_LEN);
1101     HILOG_INFO_I18N("GetSystemHour: read from system param:%{public}s.", is24HourVal.c_str());
1102     bool is24Hour = Is24HourClock();
1103     return is24Hour ? "true" : "false";
1104 }
1105 
Is24HourLocale(const std::string & systemLocale)1106 bool LocaleConfig::Is24HourLocale(const std::string& systemLocale)
1107 {
1108     static std::unordered_map<std::string, bool> is24HourLocaleMap;
1109     if (is24HourLocaleMap.find(systemLocale) != is24HourLocaleMap.end()) {
1110         return is24HourLocaleMap[systemLocale];
1111     }
1112     UErrorCode status = U_ZERO_ERROR;
1113     icu::Locale locale = icu::Locale::forLanguageTag(icu::StringPiece(systemLocale), status);
1114     if (U_FAILURE(status)) {
1115         HILOG_INFO_I18N("Is24HourLocale: %{public}s create locale failed", systemLocale.c_str());
1116         return false;
1117     }
1118 
1119     icu::UnicodeString formatPattern;
1120     icu::DateFormat* dateFormat = icu::DateFormat::createTimeInstance(icu::DateFormat::EStyle::kLong, locale);
1121     if (dateFormat == nullptr) {
1122         HILOG_INFO_I18N("Is24HourLocale: createTimeInstance failed");
1123         return false;
1124     }
1125     icu::SimpleDateFormat* simDateFormat = static_cast<icu::SimpleDateFormat*>(dateFormat);
1126     if (simDateFormat == nullptr) {
1127         HILOG_INFO_I18N("Is24HourLocale: failed to convert dateFormat");
1128         return false;
1129     }
1130     simDateFormat->toPattern(formatPattern);
1131     delete dateFormat;
1132     std::string pattern;
1133     formatPattern.toUTF8String(pattern);
1134     bool result = HasDesignator(pattern, 'H');
1135     is24HourLocaleMap[systemLocale] = result;
1136     return result;
1137 }
1138 
HasDesignator(const std::string & pattern,const char designator)1139 bool LocaleConfig::HasDesignator(const std::string& pattern, const char designator)
1140 {
1141     if (pattern.empty()) {
1142         HILOG_INFO_I18N("HasDesignator: pattern is empty");
1143         return false;
1144     }
1145     bool insideQuote = false;
1146     for (const auto& c : pattern) {
1147         if (c == '\'') {
1148             insideQuote = !insideQuote;
1149         } else if (!insideQuote) {
1150             if (c == designator) {
1151                 return true;
1152             }
1153         }
1154     }
1155     return false;
1156 }
1157 
GetUsingLocalDigit()1158 bool LocaleConfig::GetUsingLocalDigit()
1159 {
1160     std::string locale = GetSystemLocale();
1161     LocaleInfo localeInfo(locale);
1162     std::string language = localeInfo.GetLanguage();
1163     if (localDigitMap.find(language) == localDigitMap.end()) {
1164         return false;
1165     }
1166     std::string localNumberSystem = localDigitMap.at(language);
1167     if (localNumberSystem.compare(localeInfo.GetNumberingSystem()) != 0) {
1168         return false;
1169     }
1170     return true;
1171 }
1172 
GetBlockedLanguages()1173 std::unordered_set<std::string> LocaleConfig::GetBlockedLanguages()
1174 {
1175     return blockedLanguages;
1176 }
1177 
GetBlockedRegions()1178 std::unordered_set<std::string> LocaleConfig::GetBlockedRegions()
1179 {
1180     return blockedRegions;
1181 }
1182 
GetLanguageBlockedRegions()1183 std::unordered_set<std::string> LocaleConfig::GetLanguageBlockedRegions()
1184 {
1185     std::string systemLanguage = LocaleConfig::GetSystemLanguage();
1186     if (blockedLanguageRegions.find(systemLanguage) != blockedLanguageRegions.end()) {
1187         return blockedLanguageRegions[systemLanguage];
1188     }
1189     std::unordered_set<std::string> emptyResult;
1190     return emptyResult;
1191 }
1192 
SetSystemLanguage(const std::string & languageTag)1193 I18nErrorCode LocaleConfig::SetSystemLanguage(const std::string &languageTag)
1194 {
1195     if (!IsValidTag(languageTag)) {
1196         HILOG_ERROR_I18N("LocaleConfig::SetSystemLanguage %{public}s is not valid language tag.",
1197             languageTag.c_str());
1198         return I18nErrorCode::INVALID_LANGUAGE_TAG;
1199     }
1200     // save old language, reset system language to old language if update locale failed.
1201     std::string oldLanguageTag = GetSystemLanguage();
1202     if (SetParameter(LANGUAGE_KEY, languageTag.data()) != 0) {
1203         HILOG_ERROR_I18N("LocaleConfig::SetSystemLanguage update system language failed.");
1204         return I18nErrorCode::UPDATE_SYSTEM_LANGUAGE_FAILED;
1205     }
1206     std::string newLocaleTag = UpdateLanguageOfLocale(languageTag);
1207     if (SetSystemLocale(newLocaleTag) == I18nErrorCode::SUCCESS) {
1208         return I18nErrorCode::SUCCESS;
1209     }
1210     // reset system language to old language in case that system language is inconsist with system locale's lanuage.
1211     HILOG_ERROR_I18N("LocaleConfig::SetSystemLanguage update system locale failed.");
1212     SetParameter(LANGUAGE_KEY, oldLanguageTag.data());
1213     return I18nErrorCode::UPDATE_SYSTEM_LANGUAGE_FAILED;
1214 }
1215 
SetSystemRegion(const std::string & regionTag)1216 I18nErrorCode LocaleConfig::SetSystemRegion(const std::string &regionTag)
1217 {
1218     QueryUpgradeLocale();
1219     if (!IsValidRegion(regionTag)) {
1220         HILOG_ERROR_I18N("LocaleConfig::SetSystemRegion %{public}s is not valid region tag.", regionTag.c_str());
1221         return I18nErrorCode::INVALID_REGION_TAG;
1222     }
1223     return SetSystemLocale(UpdateRegionOfLocale(regionTag));
1224 }
1225 
QueryUpgradeLocale()1226 void LocaleConfig::QueryUpgradeLocale()
1227 {
1228     std::string upgradeLocale = ReadSystemParameter(UPGRADE_LOCALE_KEY, CONFIG_LEN);
1229     if (!upgradeLocale.empty()) {
1230         HILOG_ERROR_I18N("LocaleConfig::QueryUpgradeLocale: upgrade locale is %{public}s.", upgradeLocale.c_str());
1231     }
1232 }
1233 
SetSystemLocale(const std::string & localeTag)1234 I18nErrorCode LocaleConfig::SetSystemLocale(const std::string &localeTag)
1235 {
1236     if (!IsValidTag(localeTag)) {
1237         HILOG_ERROR_I18N("LocaleConfig::SetSystemLocale %{public}s is not a valid locale tag.", localeTag.c_str());
1238         return I18nErrorCode::INVALID_LOCALE_TAG;
1239     }
1240     if (SetParameter(LOCALE_KEY, localeTag.data()) != 0) {
1241         return I18nErrorCode::UPDATE_SYSTEM_LOCALE_FAILED;
1242     }
1243 #ifdef SUPPORT_GRAPHICS
1244     UpdateConfiguration(AAFwk::GlobalConfigurationKey::SYSTEM_LANGUAGE, localeTag);
1245     return PublishCommonEvent(EventFwk::CommonEventSupport::COMMON_EVENT_LOCALE_CHANGED);
1246 #else
1247     return I18nErrorCode::SUCCESS;
1248 #endif
1249 }
1250 
IsValid24HourClockValue(const std::string & tag)1251 bool LocaleConfig::IsValid24HourClockValue(const std::string &tag)
1252 {
1253     if (tag.compare("true") == 0 || tag.compare("false") == 0 || tag.length() == 0) {
1254         return true;
1255     }
1256     return false;
1257 }
1258 
Set24HourClock(const std::string & option)1259 I18nErrorCode LocaleConfig::Set24HourClock(const std::string &option)
1260 {
1261     if (!IsValid24HourClockValue(option)) {
1262         HILOG_ERROR_I18N("LocaleConfig::Set24HourClock invalid 24 Hour clock tag: %{public}s", option.c_str());
1263         return I18nErrorCode::INVALID_24_HOUR_CLOCK_TAG;
1264     }
1265     if (SetParameter(HOUR_KEY, option.data()) != 0) {
1266         HILOG_ERROR_I18N("LocaleConfig::Set24HourClock update 24 hour clock failed with option=%{public}s",
1267             option.c_str());
1268         return I18nErrorCode::UPDATE_24_HOUR_CLOCK_FAILED;
1269     }
1270 #ifdef SUPPORT_GRAPHICS
1271     UpdateConfiguration(AAFwk::GlobalConfigurationKey::SYSTEM_HOUR, option);
1272     return PublishCommonEvent(EventFwk::CommonEventSupport::COMMON_EVENT_TIME_CHANGED);
1273 #else
1274     return I18nErrorCode::SUCCESS;
1275 #endif
1276 }
1277 
SetUsingLocalDigit(bool flag)1278 I18nErrorCode LocaleConfig::SetUsingLocalDigit(bool flag)
1279 {
1280     // check whether current language support local digit.
1281     std::string localeTag = GetSystemLocale();
1282     std::string languageTag = localeTag.substr(0, 2); // obtain 2 length language code.
1283     auto it = localDigitMap.find(languageTag);
1284     if (it == localDigitMap.end()) {
1285         HILOG_ERROR_I18N("LocaleConfig::SetUsingLocalDigit current system doesn't support set local digit");
1286         return I18nErrorCode::UPDATE_LOCAL_DIGIT_FAILED;
1287     }
1288     // update system locale.
1289     return SetSystemLocale(UpdateNumberSystemOfLocale(it->second, flag));
1290 }
1291 
UpdateNumberSystemOfLocale(const std::string & localDigitTag,bool flag)1292 std::string LocaleConfig::UpdateNumberSystemOfLocale(const std::string &localDigitTag, bool flag)
1293 {
1294     if (flag) {
1295         // add local digit tag to number system param of locale
1296         return AddLocalDigitToLocale(localDigitTag);
1297     }
1298     // remove local digit tag to number system param of locale
1299     return RemoveLocalDigitFromLocale(localDigitTag);
1300 }
1301 
AddLocalDigitToLocale(const std::string & localDigitTag)1302 std::string LocaleConfig::AddLocalDigitToLocale(const std::string &localDigitTag)
1303 {
1304     std::string localeTag = GetSystemLocale();
1305     // Case: no extend param, add '-u-' and number system tag.
1306     if (localeTag.find("-u-") == std::string::npos) {
1307         localeTag += "-u" + std::string(NUMBER_SYSTEM_KEY) + localDigitTag;
1308         return localeTag;
1309     }
1310     // Case: has extend param but doesn't hava number system param, add number system tag.
1311     if (localeTag.find(NUMBER_SYSTEM_KEY) == std::string::npos) {
1312         localeTag += std::string(NUMBER_SYSTEM_KEY) + localDigitTag;
1313         return localeTag;
1314     }
1315     // Case: has number system param, replace local digit tag to localDigitTag.
1316     LocaleInfo localeInfo(localeTag);
1317     std::string oldNumberSystem = localeInfo.GetNumberingSystem();
1318     localeTag.replace(localeTag.find(oldNumberSystem), oldNumberSystem.length(), localDigitTag);
1319     return localeTag;
1320 }
1321 
RemoveLocalDigitFromLocale(const std::string & localDigitTag)1322 std::string LocaleConfig::RemoveLocalDigitFromLocale(const std::string &localDigitTag)
1323 {
1324     // remove number system tag from locale
1325     std::string localeTag = GetSystemLocale();
1326     std::string numberSystemTag = NUMBER_SYSTEM_KEY + localDigitTag;
1327     size_t pos = localeTag.find(numberSystemTag);
1328     if (pos != std::string::npos) {
1329         localeTag.replace(pos, numberSystemTag.length(), "");
1330     }
1331     // remove "-u" if localeTag ends with "-u"
1332     size_t uLength = 2;
1333     if (localeTag.find("-u") == (localeTag.length() - uLength)) {
1334         localeTag.resize(localeTag.length() - uLength);
1335     }
1336     return localeTag;
1337 }
1338 
1339 #ifdef SUPPORT_GRAPHICS
UpdateConfiguration(const char * key,const std::string & value)1340 void LocaleConfig::UpdateConfiguration(const char *key, const std::string &value)
1341 {
1342     AppExecFwk::Configuration configuration;
1343     configuration.AddItem(key, value);
1344     auto appMgrClient = std::make_unique<AppExecFwk::AppMgrClient>();
1345     appMgrClient->UpdateConfiguration(configuration);
1346     HILOG_INFO_I18N("LocaleConfig::UpdateLanguageConfiguration update configuration finished.");
1347 }
1348 
PublishCommonEvent(const std::string & eventType)1349 I18nErrorCode LocaleConfig::PublishCommonEvent(const std::string &eventType)
1350 {
1351     OHOS::AAFwk::Want localeChangeWant;
1352     localeChangeWant.SetAction(eventType);
1353     OHOS::EventFwk::CommonEventData event(localeChangeWant);
1354     if (EventFwk::CommonEventSupport::COMMON_EVENT_TIME_CHANGED.compare(eventType) == 0) {
1355         event.SetData(HOUR_EVENT_DATA);
1356     }
1357     if (!OHOS::EventFwk::CommonEventManager::PublishCommonEvent(event)) {
1358         HILOG_ERROR_I18N("LocaleConfig::PublishCommonEvent Failed to Publish event %{public}s",
1359             localeChangeWant.GetAction().c_str());
1360         return I18nErrorCode::PUBLISH_COMMON_EVENT_FAILED;
1361     }
1362     HILOG_INFO_I18N("LocaleConfig::PublishCommonEvent publish event finished.");
1363     return I18nErrorCode::SUCCESS;
1364 }
1365 #endif
1366 
UpdateLanguageOfLocale(const std::string & languageTag)1367 std::string LocaleConfig::UpdateLanguageOfLocale(const std::string &languageTag)
1368 {
1369     // Compute language and script part from languageTag.
1370     UErrorCode status = U_ZERO_ERROR;
1371     icu::Locale languageLocale = icu::Locale::forLanguageTag(languageTag.c_str(), status);
1372     if (U_FAILURE(status)) {
1373         HILOG_ERROR_I18N("LocaleConfig::UpdateLanguageOfLocale init icu Locale for language %{public}s failed.",
1374             languageTag.c_str());
1375         return "";
1376     }
1377     std::string langTag = languageLocale.getLanguage();
1378     std::string scriptTag = languageLocale.getScript();
1379     // Compute region and extend param part from current system locale.
1380     std::string systemLocaleTag = GetSystemLocale();
1381     icu::Locale systemLocale = icu::Locale::forLanguageTag(systemLocaleTag.c_str(), status);
1382     if (U_FAILURE(status)) {
1383         HILOG_ERROR_I18N("LocaleConfig::UpdateSystemLocale init icu Locale for locale %{public}s failed.",
1384             systemLocaleTag.c_str());
1385         return "";
1386     }
1387     std::string regionTag = systemLocale.getCountry();
1388     std::string extendParamTag;
1389     size_t pos = systemLocaleTag.find("-u-");
1390     if (pos != std::string::npos) {
1391         extendParamTag = systemLocaleTag.substr(pos);
1392     }
1393     // Combine above elements.
1394     return CreateLocale(langTag, scriptTag, regionTag, extendParamTag);
1395 }
1396 
CreateLocale(const std::string & languageTag,const std::string & scriptTag,const std::string & regionTag,const std::string & extendParamTag)1397 std::string LocaleConfig::CreateLocale(const std::string &languageTag, const std::string &scriptTag,
1398     const std::string &regionTag, const std::string &extendParamTag)
1399 {
1400     // combine language, script, region and extend param with '-'
1401     std::string localeTag = languageTag;
1402     std::string splitor = "-";
1403     if (scriptTag.length() > 0) {
1404         localeTag += splitor + scriptTag;
1405     }
1406     if (regionTag.length() > 0) {
1407         localeTag += splitor + regionTag;
1408     }
1409     if (extendParamTag.length() > 0) {
1410         localeTag += extendParamTag;
1411     }
1412     return localeTag;
1413 }
1414 
UpdateRegionOfLocale(const std::string & regionTag)1415 std::string LocaleConfig::UpdateRegionOfLocale(const std::string &regionTag)
1416 {
1417     std::string localeTag = GetSystemLocale();
1418     // if current system locale is null, contruct a locale from region tag.
1419     if (localeTag.length() == 0) {
1420         return CreateLocaleFromRegion(regionTag);
1421     }
1422     // combine locale with origin locale's language and script with regionTag.
1423     UErrorCode status = U_ZERO_ERROR;
1424     const icu::Locale origin = icu::Locale::forLanguageTag(localeTag, status);
1425     if (U_FAILURE(status)) {
1426         HILOG_ERROR_I18N("LocaleConfig::UpdateRegionOfLocale init origin locale failed.");
1427         return "";
1428     }
1429     icu::LocaleBuilder builder = icu::LocaleBuilder().setLanguage(origin.getLanguage()).
1430         setScript(origin.getScript()).setRegion(regionTag);
1431     icu::Locale temp = builder.setExtension('u', "").build(status);
1432     string ret = temp.toLanguageTag<string>(status);
1433     if (U_FAILURE(status)) {
1434         HILOG_ERROR_I18N("LocaleConfig::UpdateRegionOfLocale obtain new locale's tag failed.");
1435         return "";
1436     }
1437     return ret;
1438 }
1439 
CreateLocaleFromRegion(const std::string & regionTag)1440 std::string LocaleConfig::CreateLocaleFromRegion(const std::string &regionTag)
1441 {
1442     // fill locale with icu
1443     icu::Locale locale("", regionTag.c_str());
1444     UErrorCode status = U_ZERO_ERROR;
1445     locale.addLikelySubtags(status);
1446     if (U_FAILURE(status)) {
1447         HILOG_ERROR_I18N("LocaleConfig::CreateLocaleFromRegion init new locale failed.");
1448         return "";
1449     }
1450     std::string localeTag = locale.toLanguageTag<string>(status);
1451     if (U_FAILURE(status)) {
1452         HILOG_ERROR_I18N("LocaleConfig::CreateLocaleFromRegion obtain new locale's tag failed.");
1453         return "";
1454     }
1455     return localeTag;
1456 }
1457 } // namespace I18n
1458 } // namespace Global
1459 } // namespace OHOS
1460