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 TEL_CORE_SERVICE_EXISTS
18 #include "core_service_client.h"
19 #endif
20 #include <cctype>
21 #include "hilog/log.h"
22 #include "ipc_skeleton.h"
23 #include "libxml/parser.h"
24 #include "locale_info.h"
25 #include "localebuilder.h"
26 #include "locid.h"
27 #include "ohos/init_data.h"
28 #include "parameter.h"
29 #include "securec.h"
30 #include "string_ex.h"
31 #include "ucase.h"
32 #include "ulocimp.h"
33 #include "unistr.h"
34 #include "ureslocs.h"
35 #include "ustring.h"
36 #include "ustr_imp.h"
37 #include "locale_config.h"
38
39 namespace OHOS {
40 namespace Global {
41 namespace I18n {
42 using namespace std;
43 using namespace OHOS::HiviewDFX;
44
45 static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, 0xD001E00, "LocaleConfig" };
46 const char *LocaleConfig::LANGUAGE_KEY = "persist.global.language";
47 const char *LocaleConfig::LOCALE_KEY = "persist.global.locale";
48 const char *LocaleConfig::HOUR_KEY = "persist.global.is24Hour";
49 const char *LocaleConfig::DEFAULT_LOCALE_KEY = "const.global.locale";
50 const char *LocaleConfig::DEFAULT_LANGUAGE_KEY = "const.global.language";
51 const char *LocaleConfig::DEFAULT_REGION_KEY = "const.global.region";
52 const char *LocaleConfig::SUPPORTED_LOCALES_NAME = "supported_locales";
53 const char *LocaleConfig::SUPPORTED_REGIONS_NAME = "supported_regions";
54 const char *LocaleConfig::WHITE_LANGUAGES_NAME = "white_languages";
55 const char *LocaleConfig::FORBIDDEN_LANGUAGES_NAME = "forbidden_languages";
56 const char *LocaleConfig::FORBIDDEN_REGIONS_NAME = "forbidden_regions";
57 const char *LocaleConfig::FORBIDDEN_LANGUAGES_PATH = "/system/usr/ohos_locale_config/forbidden_languages.xml";
58 const char *LocaleConfig::FORBIDDEN_REGIONS_PATH = "/system/usr/ohos_locale_config/forbidden_regions.xml";
59 const char *LocaleConfig::SUPPORTED_LOCALES_PATH = "/system/usr/ohos_locale_config/supported_locales.xml";
60 const char *LocaleConfig::SUPPORTED_REGIONS_PATH = "/system/usr/ohos_locale_config/supported_regions.xml";
61 const char *LocaleConfig::WHITE_LANGUAGES_PATH = "/system/usr/ohos_locale_config/white_languages.xml";
62 const char *LocaleConfig::SUPPORT_LOCALES_PATH = "/etc/ohos_lang_config/supported_locales.xml";
63 const char *LocaleConfig::DEFAULT_LOCALE = "en-Latn";
64 const char *LocaleConfig::supportLocalesTag = "supported_locales";
65 const char *LocaleConfig::LANG_PATH = "/etc/ohos_lang_config/";
66 const char *LocaleConfig::rootTag = "languages";
67 const char *LocaleConfig::secondRootTag = "lang";
68 unordered_set<string> LocaleConfig::supportedLocales;
69 unordered_set<string> LocaleConfig::supportedRegions;
70 unordered_set<string> LocaleConfig::whiteLanguages;
71 unordered_map<string, string> LocaleConfig::dialectMap {
72 { "es-Latn-419", "es-Latn-419" },
73 { "es-Latn-BO", "es-Latn-419" },
74 { "es-Latn-BR", "es-Latn-419" },
75 { "es-Latn-BZ", "es-Latn-419" },
76 { "es-Latn-CL", "es-Latn-419" },
77 { "es-Latn-CO", "es-Latn-419" },
78 { "es-Latn-CR", "es-Latn-419" },
79 { "es-Latn-CU", "es-Latn-419" },
80 { "es-Latn-DO", "es-Latn-419" },
81 { "es-Latn-EC", "es-Latn-419" },
82 { "es-Latn-GT", "es-Latn-419" },
83 { "es-Latn-HN", "es-Latn-419" },
84 { "es-Latn-MX", "es-Latn-419" },
85 { "es-Latn-NI", "es-Latn-419" },
86 { "es-Latn-PA", "es-Latn-419" },
87 { "es-Latn-PE", "es-Latn-419" },
88 { "es-Latn-PR", "es-Latn-419" },
89 { "es-Latn-PY", "es-Latn-419" },
90 { "es-Latn-SV", "es-Latn-419" },
91 { "es-Latn-US", "es-Latn-419" },
92 { "es-Latn-UY", "es-Latn-419" },
93 { "es-Latn-VE", "es-Latn-419" },
94 { "pt-Latn-PT", "pt-Latn-PT" },
95 { "en-Latn-US", "en-Latn-US" }
96 };
97
98 std::map<std::string, std::string> LocaleConfig::supportedDialectLocales;
99 std::map<string, string> LocaleConfig::locale2DisplayName {};
100 std::string LocaleConfig::currentDialectLocale = "";
101
102 set<std::string> LocaleConfig::validCaTag {
103 "buddhist",
104 "chinese",
105 "coptic",
106 "dangi",
107 "ethioaa",
108 "ethiopic",
109 "gregory",
110 "hebrew",
111 "indian",
112 "islamic",
113 "islamic-umalqura",
114 "islamic-tbla",
115 "islamic-civil",
116 "islamic-rgsa",
117 "iso8601",
118 "japanese",
119 "persian",
120 "roc",
121 "islamicc",
122 };
123 set<std::string> LocaleConfig::validCoTag {
124 "big5han",
125 "compat",
126 "dict",
127 "direct",
128 "ducet",
129 "eor",
130 "gb2312",
131 "phonebk",
132 "phonetic",
133 "pinyin",
134 "reformed",
135 "searchjl",
136 "stroke",
137 "trad",
138 "unihan",
139 "zhuyin",
140 };
141 set<std::string> LocaleConfig::validKnTag {
142 "true",
143 "false",
144 };
145 set<std::string> LocaleConfig::validKfTag {
146 "upper",
147 "lower",
148 "false",
149 };
150 set<std::string> LocaleConfig::validNuTag {
151 "adlm", "ahom", "arab", "arabext", "bali", "beng",
152 "bhks", "brah", "cakm", "cham", "deva", "diak",
153 "fullwide", "gong", "gonm", "gujr", "guru", "hanidec",
154 "hmng", "hmnp", "java", "kali", "khmr", "knda",
155 "lana", "lanatham", "laoo", "latn", "lepc", "limb",
156 "mathbold", "mathdbl", "mathmono", "mathsanb", "mathsans", "mlym",
157 "modi", "mong", "mroo", "mtei", "mymr", "mymrshan",
158 "mymrtlng", "newa", "nkoo", "olck", "orya", "osma",
159 "rohg", "saur", "segment", "shrd", "sind", "sinh",
160 "sora", "sund", "takr", "talu", "tamldec", "telu",
161 "thai", "tibt", "tirh", "vaii", "wara", "wcho",
162 };
163 set<std::string> LocaleConfig::validHcTag {
164 "h12",
165 "h23",
166 "h11",
167 "h24",
168 };
169 set<std::string> LocaleConfig::dialectLang {
170 "zh",
171 "ro",
172 "fa",
173 };
174
175 static unordered_map<string, string> g_languageMap = {
176 { "zh-Hans", "zh-Hans" },
177 { "zh-Hant", "zh-Hant" },
178 { "my-Qaag", "my-Qaag" },
179 { "es-Latn-419", "es-419" },
180 { "es-Latn-US", "es-419" },
181 { "az-Latn", "az-Latn" },
182 { "bs-Latn", "bs-Latn" },
183 { "en-Qaag", "en-Qaag" },
184 { "uz-Latn", "uz-Latn" },
185 { "sr-Latn", "sr-Latn" },
186 { "jv-Latn", "jv-Latn" },
187 { "pt-Latn-BR", "pt-BR" },
188 { "pa-Guru", "pa-Guru" },
189 { "mai-Deva", "mai-Deva" }
190 };
191
Adjust(const string & origin)192 string Adjust(const string &origin)
193 {
194 for (auto iter = g_languageMap.begin(); iter != g_languageMap.end(); ++iter) {
195 string key = iter->first;
196 if (!origin.compare(0, key.length(), key)) {
197 return iter->second;
198 }
199 }
200 return origin;
201 }
202
GetDialectName(const char * localeName,char * name,size_t nameCapacity,UErrorCode & status)203 int32_t GetDialectName(const char *localeName, char *name, size_t nameCapacity, UErrorCode &status)
204 {
205 icu::Locale locale = icu::Locale::forLanguageTag(localeName, status);
206 if (status != U_ZERO_ERROR) {
207 return 0;
208 }
209 const char *lang = locale.getLanguage();
210 const char *script = locale.getScript();
211 const char *country = locale.getCountry();
212 bool hasScript = (script != nullptr) && strlen(script) > 0;
213 bool hasCountry = (country != nullptr) && strlen(country) > 0;
214 string temp = lang;
215 if (hasScript && hasCountry) {
216 temp.append("_");
217 temp.append(script);
218 temp.append("_");
219 temp.append(country);
220 } else if (hasScript) {
221 temp.append("_");
222 temp.append(script);
223 } else if (hasCountry) {
224 temp.append("_");
225 temp.append(country);
226 }
227 if (strcpy_s(name, nameCapacity, temp.data()) != EOK) {
228 return 0;
229 }
230 return temp.size();
231 }
232
GetDisplayName(const char * locale,const char * displayLocale,UChar * dest,int32_t destCapacity,UErrorCode & status)233 int32_t GetDisplayName(const char *locale, const char *displayLocale, UChar *dest, int32_t destCapacity,
234 UErrorCode &status)
235 {
236 if (status != U_ZERO_ERROR) {
237 return 0;
238 }
239 if ((destCapacity < 0) || (destCapacity > 0 && !dest)) {
240 status = U_ILLEGAL_ARGUMENT_ERROR;
241 return 0;
242 }
243 char localeBuffer[ULOC_FULLNAME_CAPACITY];
244 int32_t length = GetDialectName(locale, localeBuffer, sizeof(localeBuffer), status);
245 if (status != U_ZERO_ERROR || !length) {
246 status = U_ILLEGAL_ARGUMENT_ERROR;
247 return 0;
248 }
249 const UChar *str = uloc_getTableStringWithFallback(U_ICUDATA_LANG, displayLocale, "Languages",
250 nullptr, localeBuffer, &length, &status);
251 if (status <= U_ZERO_ERROR) {
252 int32_t len = (length < destCapacity) ? length : destCapacity;
253 if ((len > 0) && (str != nullptr)) {
254 u_memcpy(dest, str, len);
255 }
256 } else {
257 status = U_USING_DEFAULT_WARNING;
258 return 0;
259 }
260 return u_terminateUChars(dest, destCapacity, length, &status);
261 }
262
GetDisplayLanguageImpl(const char * locale,const char * displayLocale,icu::UnicodeString & result)263 void GetDisplayLanguageImpl(const char *locale, const char *displayLocale, icu::UnicodeString &result)
264 {
265 UChar *buffer = result.getBuffer(50); // size 50 is enough to hold language name
266 if (!buffer) {
267 result.truncate(0);
268 return;
269 }
270 UErrorCode status = U_ZERO_ERROR;
271 int32_t length = GetDisplayName(locale, displayLocale, buffer, result.getCapacity(), status);
272 result.releaseBuffer(U_SUCCESS(status) ? length : 0);
273 }
274
GetDisplayLanguageInner(const string & language,const string & displayLocale,bool sentenceCase)275 string GetDisplayLanguageInner(const string &language, const string &displayLocale, bool sentenceCase)
276 {
277 icu::UnicodeString unistr;
278 // 0 is the start position of language, 2 is the length of zh and fa
279 if (!language.compare(0, 2, "zh") || !language.compare(0, 2, "fa")) {
280 UErrorCode error = U_ZERO_ERROR;
281 icu::Locale disLocale = icu::Locale::forLanguageTag(displayLocale, error);
282 if (error != U_ZERO_ERROR) {
283 return language;
284 }
285 const char *name = disLocale.getName();
286 if (!name) {
287 return language;
288 }
289 GetDisplayLanguageImpl(language.c_str(), name, unistr);
290 } else {
291 UErrorCode status = U_ZERO_ERROR;
292 icu::Locale displayLoc = icu::Locale::forLanguageTag(displayLocale, status);
293 if (status != U_ZERO_ERROR) {
294 return "";
295 }
296 icu::Locale locale = icu::Locale::forLanguageTag(language, status);
297 if (status != U_ZERO_ERROR) {
298 return "";
299 }
300 locale.getDisplayName(displayLoc, unistr);
301 }
302 if (sentenceCase) {
303 UChar32 ch = ucase_toupper(unistr.char32At(0));
304 unistr.replace(0, 1, ch);
305 }
306 string out;
307 unistr.toUTF8String(out);
308 return out;
309 }
310
311 bool LocaleConfig::listsInitialized = LocaleConfig::InitializeLists();
312
GetSystemLanguage()313 string LocaleConfig::GetSystemLanguage()
314 {
315 char value[CONFIG_LEN];
316 int code = GetParameter(LANGUAGE_KEY, "", value, CONFIG_LEN);
317 if (code > 0) {
318 return value;
319 }
320 code = GetParameter(DEFAULT_LANGUAGE_KEY, "", value, CONFIG_LEN);
321 if (code > 0) {
322 return value;
323 }
324 return "";
325 }
326
GetSystemRegion()327 string LocaleConfig::GetSystemRegion()
328 {
329 string locale = GetSystemLocale();
330 char value[CONFIG_LEN];
331 int code = GetParameter(LOCALE_KEY, "", value, CONFIG_LEN);
332 if (code > 0) {
333 string tag(value, code);
334 UErrorCode status = U_ZERO_ERROR;
335 icu::Locale origin = icu::Locale::forLanguageTag(tag, status);
336 if (status == U_ZERO_ERROR) {
337 const char *country = origin.getCountry();
338 if (country != nullptr) {
339 return country;
340 }
341 }
342 }
343 code = GetParameter(DEFAULT_REGION_KEY, "", value, CONFIG_LEN);
344 if (code > 0) {
345 return value;
346 }
347 return "";
348 }
349
GetSystemLocale()350 string LocaleConfig::GetSystemLocale()
351 {
352 char value[CONFIG_LEN];
353 int code = GetParameter(LOCALE_KEY, "", value, CONFIG_LEN);
354 if (code > 0) {
355 return value;
356 }
357 code = GetParameter(DEFAULT_LOCALE_KEY, "", value, CONFIG_LEN);
358 if (code > 0) {
359 return value;
360 }
361 return "";
362 }
363
CheckPermission()364 bool LocaleConfig::CheckPermission()
365 {
366 Security::AccessToken::AccessTokenID callerToken = IPCSkeleton::GetCallingTokenID();
367 int result = Security::AccessToken::PermissionState::PERMISSION_GRANTED;
368 if (Security::AccessToken::AccessTokenKit::GetTokenTypeFlag(callerToken)
369 == Security::AccessToken::ATokenTypeEnum::TOKEN_NATIVE) {
370 result = Security::AccessToken::AccessTokenKit::VerifyNativeToken(callerToken,
371 "ohos.permission.UPDATE_CONFIGURATION");
372 } else if (Security::AccessToken::AccessTokenKit::GetTokenTypeFlag(callerToken)
373 == Security::AccessToken::ATokenTypeEnum::TOKEN_HAP) {
374 result = Security::AccessToken::AccessTokenKit::VerifyAccessToken(callerToken,
375 "ohos.permission.UPDATE_CONFIGURATION");
376 } else {
377 HiLog::Info(LABEL, "Invlid tokenID");
378 return false;
379 }
380 if (result != Security::AccessToken::PermissionState::PERMISSION_GRANTED) {
381 HiLog::Info(LABEL, "Verify permission failed");
382 return false;
383 }
384 return true;
385 }
386
SetSystemLanguage(const string & language)387 bool LocaleConfig::SetSystemLanguage(const string &language)
388 {
389 if (!CheckPermission()) {
390 return false;
391 }
392 if (!IsValidTag(language)) {
393 return false;
394 }
395 return SetParameter(LANGUAGE_KEY, language.data()) == 0;
396 }
397
SetSystemRegion(const string & region)398 bool LocaleConfig::SetSystemRegion(const string ®ion)
399 {
400 if (!CheckPermission()) {
401 return false;
402 }
403 if (!IsValidRegion(region)) {
404 return false;
405 }
406 char value[CONFIG_LEN];
407 int code = GetParameter(LOCALE_KEY, "", value, CONFIG_LEN);
408 string newLocale;
409 if (code > 0) {
410 string tag(value, code);
411 newLocale = GetRegionChangeLocale(tag, region);
412 if (newLocale == "") {
413 return false;
414 }
415 } else {
416 icu::Locale temp("", region.c_str());
417 UErrorCode status = U_ZERO_ERROR;
418 temp.addLikelySubtags(status);
419 if (status != U_ZERO_ERROR) {
420 return false;
421 }
422 newLocale = temp.toLanguageTag<string>(status);
423 if (status != U_ZERO_ERROR) {
424 return false;
425 }
426 }
427 return SetParameter(LOCALE_KEY, newLocale.data()) == 0;
428 }
429
SetSystemLocale(const string & locale)430 bool LocaleConfig::SetSystemLocale(const string &locale)
431 {
432 if (!CheckPermission()) {
433 return false;
434 }
435 if (!IsValidTag(locale)) {
436 return false;
437 }
438 return SetParameter(LOCALE_KEY, locale.data()) == 0;
439 }
440
IsValidLanguage(const string & language)441 bool LocaleConfig::IsValidLanguage(const string &language)
442 {
443 string::size_type size = language.size();
444 if ((size != LANGUAGE_LEN) && (size != LANGUAGE_LEN + 1)) {
445 return false;
446 }
447 for (size_t i = 0; i < size; ++i) {
448 if ((language[i] > 'z') || (language[i] < 'a')) {
449 return false;
450 }
451 }
452 return true;
453 }
454
IsValidScript(const string & script)455 bool LocaleConfig::IsValidScript(const string &script)
456 {
457 string::size_type size = script.size();
458 if (size != LocaleInfo::SCRIPT_LEN) {
459 return false;
460 }
461 char first = script[0];
462 if ((first < 'A') || (first > 'Z')) {
463 return false;
464 }
465 for (string::size_type i = 1; i < LocaleInfo::SCRIPT_LEN; ++i) {
466 if ((script[i] > 'z') || (script[i] < 'a')) {
467 return false;
468 }
469 }
470 return true;
471 }
472
IsValidRegion(const string & region)473 bool LocaleConfig::IsValidRegion(const string ®ion)
474 {
475 string::size_type size = region.size();
476 if (size != LocaleInfo::REGION_LEN) {
477 return false;
478 }
479 for (size_t i = 0; i < LocaleInfo::REGION_LEN; ++i) {
480 if ((region[i] > 'Z') || (region[i] < 'A')) {
481 return false;
482 }
483 }
484 return true;
485 }
486
IsValidTag(const string & tag)487 bool LocaleConfig::IsValidTag(const string &tag)
488 {
489 if (!tag.size()) {
490 return false;
491 }
492 vector<string> splits;
493 Split(tag, "-", splits);
494 if (!IsValidLanguage(splits[0])) {
495 return false;
496 }
497 return true;
498 }
499
Split(const string & src,const string & sep,vector<string> & dest)500 void LocaleConfig::Split(const string &src, const string &sep, vector<string> &dest)
501 {
502 string::size_type begin = 0;
503 string::size_type end = src.find(sep);
504 while (end != string::npos) {
505 dest.push_back(src.substr(begin, end - begin));
506 begin = end + sep.size();
507 end = src.find(sep, begin);
508 }
509 if (begin != src.size()) {
510 dest.push_back(src.substr(begin));
511 }
512 }
513
514 // language in white languages should have script.
GetSystemLanguages(vector<string> & ret)515 void LocaleConfig::GetSystemLanguages(vector<string> &ret)
516 {
517 for (auto item : whiteLanguages) {
518 ret.push_back(item);
519 }
520 }
521
GetSupportedLocales()522 const unordered_set<string>& LocaleConfig::GetSupportedLocales()
523 {
524 return supportedLocales;
525 }
526
GetSupportedRegions()527 const unordered_set<string>& LocaleConfig::GetSupportedRegions()
528 {
529 return supportedRegions;
530 }
531
GetSystemCountries(vector<string> & ret)532 void LocaleConfig::GetSystemCountries(vector<string> &ret)
533 {
534 for (auto item : supportedRegions) {
535 ret.push_back(item);
536 }
537 }
538
IsSuggested(const string & language)539 bool LocaleConfig::IsSuggested(const string &language)
540 {
541 unordered_set<string> relatedLocales;
542 vector<string> simCountries;
543 GetCountriesFromSim(simCountries);
544 GetRelatedLocales(relatedLocales, simCountries);
545 for (auto iter = relatedLocales.begin(); iter != relatedLocales.end();) {
546 if (whiteLanguages.find(*iter) == whiteLanguages.end()) {
547 iter = relatedLocales.erase(iter);
548 } else {
549 ++iter;
550 }
551 }
552 string mainLanguage = GetMainLanguage(language);
553 return relatedLocales.find(mainLanguage) != relatedLocales.end();
554 }
555
IsSuggested(const std::string & language,const std::string & region)556 bool LocaleConfig::IsSuggested(const std::string &language, const std::string ®ion)
557 {
558 unordered_set<string> relatedLocales;
559 vector<string> countries { region };
560 GetRelatedLocales(relatedLocales, countries);
561 for (auto iter = relatedLocales.begin(); iter != relatedLocales.end();) {
562 if (whiteLanguages.find(*iter) == whiteLanguages.end()) {
563 iter = relatedLocales.erase(iter);
564 } else {
565 ++iter;
566 }
567 }
568 string mainLanguage = GetMainLanguage(language);
569 return relatedLocales.find(mainLanguage) != relatedLocales.end();
570 }
571
GetRelatedLocales(unordered_set<string> & relatedLocales,vector<string> countries)572 void LocaleConfig::GetRelatedLocales(unordered_set<string> &relatedLocales, vector<string> countries)
573 {
574 // remove unsupported countries
575 const unordered_set<string> ®ions = GetSupportedRegions();
576 for (auto iter = countries.begin(); iter != countries.end();) {
577 if (regions.find(*iter) == regions.end()) {
578 iter = countries.erase(iter);
579 } else {
580 ++iter;
581 }
582 }
583 const unordered_set<string> &locales = GetSupportedLocales();
584 for (string locale : locales) {
585 bool find = false;
586 for (string country : countries) {
587 if (locale.find(country) != string::npos) {
588 find = true;
589 break;
590 }
591 }
592 if (!find) {
593 continue;
594 }
595 string mainLanguage = GetMainLanguage(locale);
596 if (mainLanguage != "") {
597 relatedLocales.insert(mainLanguage);
598 }
599 }
600 }
601
GetCountriesFromSim(vector<string> & simCountries)602 void LocaleConfig::GetCountriesFromSim(vector<string> &simCountries)
603 {
604 simCountries.push_back(GetSystemRegion());
605 #ifdef TEL_CORE_SERVICE_EXISTS
606 simCountries.push_back(Str16ToStr8(
607 DelayedRefSingleton<Telephony::CoreServiceClient>::GetInstance().GetISOCountryCodeForSim(0)));
608 #endif
609 }
610
GetListFromFile(const char * path,const char * resourceName,unordered_set<string> & ret)611 void LocaleConfig::GetListFromFile(const char *path, const char *resourceName, unordered_set<string> &ret)
612 {
613 xmlKeepBlanksDefault(0);
614 if (!path) {
615 return;
616 }
617 xmlDocPtr doc = xmlParseFile(path);
618 if (!doc) {
619 return;
620 }
621 xmlNodePtr cur = xmlDocGetRootElement(doc);
622 if (!cur || xmlStrcmp(cur->name, reinterpret_cast<const xmlChar *>(resourceName))) {
623 xmlFreeDoc(doc);
624 return;
625 }
626 cur = cur->xmlChildrenNode;
627 xmlChar *content = nullptr;
628 while (cur != nullptr) {
629 content = xmlNodeGetContent(cur);
630 if (content != nullptr) {
631 ret.insert(reinterpret_cast<const char*>(content));
632 xmlFree(content);
633 cur = cur->next;
634 } else {
635 break;
636 }
637 }
638 xmlFreeDoc(doc);
639 }
640
Expunge(unordered_set<string> & src,const unordered_set<string> & another)641 void LocaleConfig::Expunge(unordered_set<string> &src, const unordered_set<string> &another)
642 {
643 for (auto iter = src.begin(); iter != src.end();) {
644 if (another.find(*iter) != another.end()) {
645 iter = src.erase(iter);
646 } else {
647 ++iter;
648 }
649 }
650 }
651
InitializeLists()652 bool LocaleConfig::InitializeLists()
653 {
654 SetHwIcuDirectory();
655 GetListFromFile(SUPPORTED_REGIONS_PATH, SUPPORTED_REGIONS_NAME, supportedRegions);
656 unordered_set<string> forbiddenRegions;
657 GetListFromFile(FORBIDDEN_REGIONS_PATH, FORBIDDEN_REGIONS_NAME, forbiddenRegions);
658 Expunge(supportedRegions, forbiddenRegions);
659 GetListFromFile(WHITE_LANGUAGES_PATH, WHITE_LANGUAGES_NAME, whiteLanguages);
660 unordered_set<string> forbiddenLanguages;
661 GetListFromFile(FORBIDDEN_LANGUAGES_PATH, FORBIDDEN_LANGUAGES_NAME, forbiddenLanguages);
662 Expunge(whiteLanguages, forbiddenLanguages);
663 GetListFromFile(SUPPORTED_LOCALES_PATH, SUPPORTED_LOCALES_NAME, supportedLocales);
664 return true;
665 }
666
GetRegionChangeLocale(const string & languageTag,const string & region)667 string LocaleConfig::GetRegionChangeLocale(const string &languageTag, const string ®ion)
668 {
669 UErrorCode status = U_ZERO_ERROR;
670 const icu::Locale origin = icu::Locale::forLanguageTag(languageTag, status);
671 if (status != U_ZERO_ERROR) {
672 return "";
673 }
674 icu::LocaleBuilder builder = icu::LocaleBuilder().setLanguage(origin.getLanguage()).
675 setScript(origin.getScript()).setRegion(region);
676 icu::Locale temp = builder.setExtension('u', "").build(status);
677 if (status != U_ZERO_ERROR) {
678 return "";
679 }
680 string ret = temp.toLanguageTag<string>(status);
681 return (status != U_ZERO_ERROR) ? "" : ret;
682 }
683
GetMainLanguage(const string & language)684 string LocaleConfig::GetMainLanguage(const string &language)
685 {
686 UErrorCode status = U_ZERO_ERROR;
687 icu::Locale origin = icu::Locale::forLanguageTag(language, status);
688 if (status != U_ZERO_ERROR) {
689 return "";
690 }
691 origin.addLikelySubtags(status);
692 if (status != U_ZERO_ERROR) {
693 return "";
694 }
695 icu::LocaleBuilder builder = icu::LocaleBuilder().setLanguage(origin.getLanguage()).
696 setScript(origin.getScript()).setRegion(origin.getCountry());
697 icu::Locale temp = builder.setExtension('u', "").build(status);
698 string fullLanguage = temp.toLanguageTag<string>(status);
699 if (status != U_ZERO_ERROR) {
700 return "";
701 }
702 if (dialectMap.find(fullLanguage) != dialectMap.end()) {
703 return dialectMap[fullLanguage];
704 }
705 builder.setRegion("");
706 temp = builder.build(status);
707 fullLanguage = temp.toLanguageTag<string>(status);
708 if (status != U_ZERO_ERROR) {
709 return "";
710 }
711 return fullLanguage;
712 }
713
GetDisplayLanguage(const string & language,const string & displayLocale,bool sentenceCase)714 string LocaleConfig::GetDisplayLanguage(const string &language, const string &displayLocale, bool sentenceCase)
715 {
716 string adjust = Adjust(language);
717 if (adjust == language) {
718 UErrorCode status = U_ZERO_ERROR;
719 icu::Locale displayLoc = icu::Locale::forLanguageTag(displayLocale, status);
720 if (status != U_ZERO_ERROR) {
721 return "";
722 }
723 icu::Locale locale = icu::Locale::forLanguageTag(language, status);
724 if (status != U_ZERO_ERROR) {
725 return "";
726 }
727 icu::UnicodeString unistr;
728 std::string lang(locale.getLanguage());
729 std::string result;
730 if (dialectLang.find(lang) != dialectLang.end()) {
731 result = GetDsiplayLanguageWithDialect(language, displayLocale);
732 } else {
733 locale.getDisplayLanguage(displayLoc, unistr);
734 unistr.toUTF8String(result);
735 }
736 if (sentenceCase) {
737 char ch = static_cast<char>(toupper(result[0]));
738 return result.replace(0, 1, 1, ch);
739 }
740 return result;
741 }
742 return GetDisplayLanguageInner(adjust, displayLocale, sentenceCase);
743 }
744
ComputeLocale(const std::string & displayLocale)745 std::string LocaleConfig::ComputeLocale(const std::string &displayLocale)
746 {
747 if (supportedDialectLocales.size() == 0) {
748 xmlKeepBlanksDefault(0);
749 xmlDocPtr doc = xmlParseFile(SUPPORT_LOCALES_PATH);
750 if (!doc) {
751 return DEFAULT_LOCALE;
752 }
753 xmlNodePtr cur = xmlDocGetRootElement(doc);
754 if (!cur || xmlStrcmp(cur->name, reinterpret_cast<const xmlChar *>(supportLocalesTag))) {
755 xmlFreeDoc(doc);
756 HiLog::Info(LABEL, "can not parse language supported locale file");
757 return DEFAULT_LOCALE;
758 }
759 cur = cur->xmlChildrenNode;
760 while (cur != nullptr) {
761 xmlChar *content = xmlNodeGetContent(cur);
762 if (content == nullptr) {
763 HiLog::Info(LABEL, "get xml node content failed");
764 break;
765 }
766 std::map<std::string, std::string> localeInfoConfigs = {};
767 LocaleInfo localeinfo(reinterpret_cast<const char*>(content), localeInfoConfigs);
768 std::string language = localeinfo.GetLanguage();
769 std::string script = localeinfo.GetScript();
770 std::string languageAndScript = (script.length() == 0) ? language : language + "-" + script;
771 LocaleInfo newLocaleInfo(languageAndScript, localeInfoConfigs);
772 std::string maximizeLocale = newLocaleInfo.Maximize();
773 supportedDialectLocales.insert(
774 std::make_pair<std::string, std::string>(maximizeLocale.c_str(),
775 reinterpret_cast<const char*>(content)));
776 xmlFree(content);
777 cur = cur->next;
778 }
779 }
780 std::map<std::string, std::string> configs = {};
781 LocaleInfo localeinfo(displayLocale, configs);
782 std::string language = localeinfo.GetLanguage();
783 std::string script = localeinfo.GetScript();
784 std::string languageAndScript = (script.length() == 0) ? language : language + "-" + script;
785 LocaleInfo newLocaleInfo(languageAndScript, configs);
786 std::string maximizeLocale = newLocaleInfo.Maximize();
787 if (supportedDialectLocales.find(maximizeLocale) != supportedDialectLocales.end()) {
788 return supportedDialectLocales.at(maximizeLocale);
789 }
790 return DEFAULT_LOCALE;
791 }
792
ReadLangData(const char * langDataPath)793 void LocaleConfig::ReadLangData(const char *langDataPath)
794 {
795 xmlKeepBlanksDefault(0);
796 if (langDataPath == nullptr) {
797 return;
798 }
799 xmlDocPtr doc = xmlParseFile(langDataPath);
800 if (!doc) {
801 HiLog::Info(LABEL, "can not open language data file");
802 return;
803 }
804 xmlNodePtr cur = xmlDocGetRootElement(doc);
805 if (!cur || xmlStrcmp(cur->name, reinterpret_cast<const xmlChar *>(rootTag))) {
806 xmlFreeDoc(doc);
807 HiLog::Info(LABEL, "parse language data file failed");
808 return;
809 }
810 cur = cur->xmlChildrenNode;
811 while (cur != nullptr && !xmlStrcmp(cur->name, reinterpret_cast<const xmlChar *>(secondRootTag))) {
812 xmlChar *langContents[ELEMENT_NUM] = { 0 }; // 2 represent langid, displayname;
813 xmlNodePtr langValue = cur->xmlChildrenNode;
814 for (size_t i = 0; i < ELEMENT_NUM; i++) {
815 if (langValue != nullptr) {
816 langContents[i] = xmlNodeGetContent(langValue);
817 langValue = langValue->next;
818 } else {
819 break;
820 }
821 }
822 // 0 represents langid index, 1 represents displayname index
823 locale2DisplayName.insert(
824 std::make_pair<std::string, std::string>(reinterpret_cast<const char *>(langContents[0]),
825 reinterpret_cast<const char *>(langContents[1])));
826 for (size_t i = 0; i < ELEMENT_NUM; i++) {
827 if (langContents[i] != nullptr) {
828 xmlFree(langContents[i]);
829 }
830 }
831 cur = cur->next;
832 }
833 xmlFreeDoc(doc);
834 }
835
GetDsiplayLanguageWithDialect(const std::string & localeStr,const std::string & displayLocale)836 string LocaleConfig::GetDsiplayLanguageWithDialect(const std::string &localeStr, const std::string &displayLocale)
837 {
838 std::string finalLocale = ComputeLocale(displayLocale);
839 if (finalLocale.compare(currentDialectLocale) != 0) {
840 std::string xmlPath = LANG_PATH + finalLocale + ".xml";
841 locale2DisplayName.clear();
842 ReadLangData(xmlPath.c_str());
843 currentDialectLocale = finalLocale;
844 }
845 if (locale2DisplayName.find(localeStr) != locale2DisplayName.end()) {
846 return locale2DisplayName.at(localeStr);
847 }
848 std::map<std::string, std::string> configs = {};
849 LocaleInfo locale(localeStr, configs);
850 std::string language = locale.GetLanguage();
851 std::string scripts = locale.GetScript();
852 std::string region = locale.GetRegion();
853 if (scripts.length() != 0) {
854 std::string languageAndScripts = language + "-" + scripts;
855 if (locale2DisplayName.find(languageAndScripts) != locale2DisplayName.end()) {
856 return locale2DisplayName.at(languageAndScripts);
857 }
858 }
859 if (region.length() != 0) {
860 std::string languageAndRegion = language + "-" + region;
861 if (locale2DisplayName.find(languageAndRegion) != locale2DisplayName.end()) {
862 return locale2DisplayName.at(languageAndRegion);
863 }
864 }
865 if (locale2DisplayName.find(language) != locale2DisplayName.end()) {
866 return locale2DisplayName.at(language);
867 }
868 return "";
869 }
870
GetDisplayRegion(const string & region,const string & displayLocale,bool sentenceCase)871 string LocaleConfig::GetDisplayRegion(const string ®ion, const string &displayLocale, bool sentenceCase)
872 {
873 UErrorCode status = U_ZERO_ERROR;
874 icu::Locale originLocale;
875 if (IsValidRegion(region)) {
876 icu::LocaleBuilder builder = icu::LocaleBuilder().setRegion(region);
877 originLocale = builder.build(status);
878 } else {
879 originLocale = icu::Locale::forLanguageTag(region, status);
880 }
881 std::string country(originLocale.getCountry());
882 if (country.length() == 0) {
883 return "";
884 }
885 if (status != U_ZERO_ERROR) {
886 return "";
887 }
888 icu::Locale locale = icu::Locale::forLanguageTag(displayLocale, status);
889 if (status != U_ZERO_ERROR) {
890 return "";
891 }
892 icu::UnicodeString displayRegion;
893 originLocale.getDisplayCountry(locale, displayRegion);
894 if (sentenceCase) {
895 UChar32 ch = ucase_toupper(displayRegion.char32At(0));
896 displayRegion.replace(0, 1, ch);
897 }
898 string temp;
899 displayRegion.toUTF8String(temp);
900 return temp;
901 }
902
IsRTL(const string & locale)903 bool LocaleConfig::IsRTL(const string &locale)
904 {
905 icu::Locale curLocale(locale.c_str());
906 return curLocale.isRightToLeft();
907 }
908
ContainTag(std::string & localeTag,std::string & defaultLocaleTag,const std::string & extensionTag)909 std::string ContainTag(std::string &localeTag, std::string &defaultLocaleTag,
910 const std::string &extensionTag)
911 {
912 std::string tag = localeTag;
913 std::size_t found = tag.find(extensionTag);
914 if (found == std::string::npos) {
915 tag = defaultLocaleTag;
916 found = tag.find(extensionTag);
917 }
918 if (found == std::string::npos) {
919 return "";
920 }
921
922 std::size_t start = found + 4; // 4 is the tag length
923 std::size_t end = tag.find("-", start);
924
925 return tag.substr(start, end - start);
926 }
927
parseExtension(const std::string & extension,std::map<std::string,std::string> & map)928 void parseExtension(const std::string &extension, std::map<std::string, std::string> &map)
929 {
930 std::string pattern = "-..-";
931 std::regex express(pattern);
932
933 std::regex_token_iterator<std::string::const_iterator> begin1(extension.cbegin(), extension.cend(), express);
934 std::regex_token_iterator<std::string::const_iterator> begin2(extension.cbegin(), extension.cend(), express, -1);
935 begin2++;
936 for (; begin1 != std::sregex_token_iterator() && begin2 != std::sregex_token_iterator(); begin1++, begin2++) {
937 map.insert(std::pair<std::string, std::string>(begin1->str(), begin2->str()));
938 }
939 }
940
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)941 void setExtension(std::string &extension, const std::string &tag, const std::set<string> &validValue,
942 const std::map<std::string, std::string> &extensionMap,
943 const std::map<std::string, std::string> &defaultExtensionMap)
944 {
945 std::string value = "";
946 auto it = extensionMap.find(tag);
947 if (it != extensionMap.end()) {
948 value = it->second;
949 if (validValue.find(value) == validValue.end()) {
950 return;
951 } else {
952 extension += tag;
953 extension += value;
954 }
955 } else {
956 it = defaultExtensionMap.find(tag);
957 if (it != defaultExtensionMap.end()) {
958 value = it->second;
959 if (validValue.find(value) == validValue.end()) {
960 return;
961 } else {
962 extension += tag;
963 extension += value;
964 }
965 }
966 }
967 }
968
setOtherExtension(std::string & extension,std::map<std::string,std::string> & extensionMap,std::map<std::string,std::string> & defaultExtensionMap)969 void setOtherExtension(std::string &extension, std::map<std::string, std::string> &extensionMap,
970 std::map<std::string, std::string> &defaultExtensionMap)
971 {
972 std::set<std::string> tags;
973 tags.insert("-ca-");
974 tags.insert("-co-");
975 tags.insert("-kn-");
976 tags.insert("-kf-");
977 tags.insert("-nu-");
978 tags.insert("-hc-");
979
980 for (auto it = tags.begin(); it != tags.end(); it++) {
981 extensionMap.erase(*it);
982 defaultExtensionMap.erase(*it);
983 }
984
985 for (auto it = defaultExtensionMap.begin(); it != defaultExtensionMap.end(); it++) {
986 extensionMap.insert(std::pair<std::string, std::string>(it->first, it->second));
987 }
988
989 for (auto it = extensionMap.begin(); it != extensionMap.end(); it++) {
990 extension += it->first;
991 extension += it->second;
992 }
993 }
994
GetValidLocale(const std::string & localeTag)995 std::string LocaleConfig::GetValidLocale(const std::string &localeTag)
996 {
997 std::string baseLocale = "";
998 std::string extension = "";
999 std::size_t found = localeTag.find("-u-");
1000 baseLocale = localeTag.substr(0, found);
1001 if (found != std::string::npos) {
1002 extension = localeTag.substr(found);
1003 }
1004 std::map<std::string, std::string> extensionMap;
1005 if (extension != "") {
1006 parseExtension(extension, extensionMap);
1007 }
1008
1009 std::string systemLocaleTag = GetSystemLocale();
1010 std::string defaultExtension = "";
1011 found = systemLocaleTag.find("-u-");
1012 if (found != std::string::npos) {
1013 defaultExtension = systemLocaleTag.substr(found);
1014 }
1015 std::map<std::string, std::string> defaultExtensionMap;
1016 if (defaultExtension != "") {
1017 parseExtension(defaultExtension, defaultExtensionMap);
1018 }
1019
1020 std::string ext = "";
1021 setExtension(ext, "-ca-", validCaTag, extensionMap, defaultExtensionMap);
1022 setExtension(ext, "-co-", validCoTag, extensionMap, defaultExtensionMap);
1023 setExtension(ext, "-kn-", validKnTag, extensionMap, defaultExtensionMap);
1024 setExtension(ext, "-kf-", validKfTag, extensionMap, defaultExtensionMap);
1025 setExtension(ext, "-nu-", validNuTag, extensionMap, defaultExtensionMap);
1026 setExtension(ext, "-hc-", validHcTag, extensionMap, defaultExtensionMap);
1027
1028 std::string otherExt = "";
1029 setOtherExtension(otherExt, extensionMap, defaultExtensionMap);
1030 if (ext != "" || otherExt != "") {
1031 return baseLocale + "-u" + ext + otherExt;
1032 } else {
1033 return baseLocale;
1034 }
1035 }
1036
Is24HourClock()1037 bool LocaleConfig::Is24HourClock()
1038 {
1039 char value[CONFIG_LEN];
1040 int code = GetParameter(HOUR_KEY, "", value, CONFIG_LEN);
1041 if (code <= 0) {
1042 return false;
1043 }
1044 if (!strcmp(value, "true")) {
1045 return true;
1046 }
1047 return false;
1048 }
1049
Set24HourClock(bool option)1050 bool LocaleConfig::Set24HourClock(bool option)
1051 {
1052 if (!CheckPermission()) {
1053 return false;
1054 }
1055 std::string optionStr = "";
1056 if (option) {
1057 optionStr = "true";
1058 } else {
1059 optionStr = "false";
1060 }
1061 return SetParameter(HOUR_KEY, optionStr.data()) == 0;
1062 }
1063 } // namespace I18n
1064 } // namespace Global
1065 } // namespace OHOS
1066