19596a2c1Sopenharmony_ci/*
29596a2c1Sopenharmony_ci * Copyright (c) 2023 Huawei Device Co., Ltd.
39596a2c1Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
49596a2c1Sopenharmony_ci * you may not use this file except in compliance with the License.
59596a2c1Sopenharmony_ci * You may obtain a copy of the License at
69596a2c1Sopenharmony_ci *
79596a2c1Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
89596a2c1Sopenharmony_ci *
99596a2c1Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
109596a2c1Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
119596a2c1Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
129596a2c1Sopenharmony_ci * See the License for the specific language governing permissions and
139596a2c1Sopenharmony_ci * limitations under the License.
149596a2c1Sopenharmony_ci */
159596a2c1Sopenharmony_ci
169596a2c1Sopenharmony_ci#include "i18n_hilog.h"
179596a2c1Sopenharmony_ci#include "locale_config.h"
189596a2c1Sopenharmony_ci#include "system_locale_manager.h"
199596a2c1Sopenharmony_ci#include "unicode/calendar.h"
209596a2c1Sopenharmony_ci#include "unicode/timezone.h"
219596a2c1Sopenharmony_ci#include "utils.h"
229596a2c1Sopenharmony_ci#include "i18n_timezone.h"
239596a2c1Sopenharmony_ci
249596a2c1Sopenharmony_cinamespace OHOS {
259596a2c1Sopenharmony_cinamespace Global {
269596a2c1Sopenharmony_cinamespace I18n {
279596a2c1Sopenharmony_ciconst char* SystemLocaleManager::SIM_COUNTRY_CODE_KEY = "telephony.sim.countryCode0";
289596a2c1Sopenharmony_ci
299596a2c1Sopenharmony_ciSystemLocaleManager::SystemLocaleManager()
309596a2c1Sopenharmony_ci{
319596a2c1Sopenharmony_ci    tabooUtils = std::make_unique<TabooUtils>();
329596a2c1Sopenharmony_ci}
339596a2c1Sopenharmony_ci
349596a2c1Sopenharmony_ciSystemLocaleManager::~SystemLocaleManager()
359596a2c1Sopenharmony_ci{
369596a2c1Sopenharmony_ci}
379596a2c1Sopenharmony_ci
389596a2c1Sopenharmony_ci/**
399596a2c1Sopenharmony_ci * Language arrays are sorted according to the following steps:
409596a2c1Sopenharmony_ci * 1. Remove blocked languages.
419596a2c1Sopenharmony_ci * 2. Compute language locale displayName; If options.isUseLocalName is true, compute language local displayName.
429596a2c1Sopenharmony_ci *    replace display name with taboo data.
439596a2c1Sopenharmony_ci * 3. Judge whether language is suggested with system region and sim card region.
449596a2c1Sopenharmony_ci * 4. Sort the languages use locale displayName, local displyName and suggestion infomation.
459596a2c1Sopenharmony_ci */
469596a2c1Sopenharmony_cistd::vector<LocaleItem> SystemLocaleManager::GetLanguageInfoArray(const std::vector<std::string> &languages,
479596a2c1Sopenharmony_ci    const SortOptions &options, I18nErrorCode &status)
489596a2c1Sopenharmony_ci{
499596a2c1Sopenharmony_ci    std::vector<LocaleItem> localeItemList;
509596a2c1Sopenharmony_ci    status = I18nErrorCode::SUCCESS;
519596a2c1Sopenharmony_ci    if (!CheckSystemPermission()) {
529596a2c1Sopenharmony_ci        status = I18nErrorCode::NOT_SYSTEM_APP;
539596a2c1Sopenharmony_ci        return localeItemList;
549596a2c1Sopenharmony_ci    }
559596a2c1Sopenharmony_ci    std::unordered_set<std::string> blockedLanguages = LocaleConfig::GetBlockedLanguages();
569596a2c1Sopenharmony_ci    for (auto it = languages.begin(); it != languages.end(); ++it) {
579596a2c1Sopenharmony_ci        if (blockedLanguages.find(*it) != blockedLanguages.end()) {
589596a2c1Sopenharmony_ci            continue;
599596a2c1Sopenharmony_ci        }
609596a2c1Sopenharmony_ci        std::string languageDisplayName = LocaleConfig::GetDisplayLanguage(*it, options.localeTag, true);
619596a2c1Sopenharmony_ci        languageDisplayName = tabooUtils->ReplaceLanguageName(*it, options.localeTag, languageDisplayName);
629596a2c1Sopenharmony_ci        std::string languageNativeName;
639596a2c1Sopenharmony_ci        if (options.isUseLocalName) {
649596a2c1Sopenharmony_ci            languageNativeName = LocaleConfig::GetDisplayLanguage(*it, *it, true);
659596a2c1Sopenharmony_ci            languageNativeName = tabooUtils->ReplaceLanguageName(*it, *it, languageNativeName);
669596a2c1Sopenharmony_ci        }
679596a2c1Sopenharmony_ci        bool isSuggestedWithSystemRegion = LocaleConfig::IsSuggested(*it, LocaleConfig::GetSystemRegion());
689596a2c1Sopenharmony_ci        std::string simRegion = ReadSystemParameter(SIM_COUNTRY_CODE_KEY, CONFIG_LEN);
699596a2c1Sopenharmony_ci        bool isSuggestedWithSimRegion = false;
709596a2c1Sopenharmony_ci        if (simRegion.length() > 0) {
719596a2c1Sopenharmony_ci            isSuggestedWithSimRegion = LocaleConfig::IsSuggested(*it, simRegion);
729596a2c1Sopenharmony_ci        }
739596a2c1Sopenharmony_ci        SuggestionType suggestionType = SuggestionType::SUGGESTION_TYPE_NONE;
749596a2c1Sopenharmony_ci        if (isSuggestedWithSimRegion) {
759596a2c1Sopenharmony_ci            suggestionType = SuggestionType::SUGGESTION_TYPE_SIM;
769596a2c1Sopenharmony_ci        } else if (isSuggestedWithSystemRegion) {
779596a2c1Sopenharmony_ci            suggestionType = SuggestionType::SUGGESTION_TYPE_RELATED;
789596a2c1Sopenharmony_ci        }
799596a2c1Sopenharmony_ci        LocaleItem item { *it, suggestionType, languageDisplayName, languageNativeName };
809596a2c1Sopenharmony_ci        localeItemList.push_back(item);
819596a2c1Sopenharmony_ci    }
829596a2c1Sopenharmony_ci    SortLocaleItemList(localeItemList, options);
839596a2c1Sopenharmony_ci    return localeItemList;
849596a2c1Sopenharmony_ci}
859596a2c1Sopenharmony_ci
869596a2c1Sopenharmony_ci/**
879596a2c1Sopenharmony_ci * Region arrays are sorted according to the following steps:
889596a2c1Sopenharmony_ci * 1. Remove blocked regions and blocked regions under system Language.
899596a2c1Sopenharmony_ci * 2. Compute region locale displayName; replace display name with taboo data.
909596a2c1Sopenharmony_ci * 3. Judge whether region is suggested with system language.
919596a2c1Sopenharmony_ci * 4. Sort the regions use locale displayName and suggestion infomation.
929596a2c1Sopenharmony_ci */
939596a2c1Sopenharmony_cistd::vector<LocaleItem> SystemLocaleManager::GetCountryInfoArray(const std::vector<std::string> &countries,
949596a2c1Sopenharmony_ci    const SortOptions &options, I18nErrorCode &status)
959596a2c1Sopenharmony_ci{
969596a2c1Sopenharmony_ci    std::vector<LocaleItem> localeItemList;
979596a2c1Sopenharmony_ci    status = I18nErrorCode::SUCCESS;
989596a2c1Sopenharmony_ci    if (!CheckSystemPermission()) {
999596a2c1Sopenharmony_ci        status = I18nErrorCode::NOT_SYSTEM_APP;
1009596a2c1Sopenharmony_ci        return localeItemList;
1019596a2c1Sopenharmony_ci    }
1029596a2c1Sopenharmony_ci    std::unordered_set<std::string> blockedRegions = LocaleConfig::GetBlockedRegions();
1039596a2c1Sopenharmony_ci    std::unordered_set<std::string> blockedLanguageRegions = LocaleConfig::GetLanguageBlockedRegions();
1049596a2c1Sopenharmony_ci    std::string pseudoProcessedRegion = PseudoLocalizationProcessor("");
1059596a2c1Sopenharmony_ci    for (auto it = countries.begin(); it != countries.end(); ++it) {
1069596a2c1Sopenharmony_ci        if (blockedRegions.find(*it) != blockedRegions.end() || blockedLanguageRegions.find(*it) !=
1079596a2c1Sopenharmony_ci            blockedLanguageRegions.end()) {
1089596a2c1Sopenharmony_ci            continue;
1099596a2c1Sopenharmony_ci        }
1109596a2c1Sopenharmony_ci        std::string regionDisplayName = LocaleConfig::GetDisplayRegion(*it, options.localeTag, true);
1119596a2c1Sopenharmony_ci        regionDisplayName = tabooUtils->ReplaceCountryName(*it, options.localeTag, regionDisplayName);
1129596a2c1Sopenharmony_ci        bool isSuggestedRegion = LocaleConfig::IsSuggested(LocaleConfig::GetSystemLanguage(), *it);
1139596a2c1Sopenharmony_ci        SuggestionType suggestionType = SuggestionType::SUGGESTION_TYPE_NONE;
1149596a2c1Sopenharmony_ci        if (isSuggestedRegion) {
1159596a2c1Sopenharmony_ci            suggestionType = SuggestionType::SUGGESTION_TYPE_RELATED;
1169596a2c1Sopenharmony_ci        }
1179596a2c1Sopenharmony_ci        LocaleItem item { *it, suggestionType, regionDisplayName, pseudoProcessedRegion };
1189596a2c1Sopenharmony_ci        localeItemList.push_back(item);
1199596a2c1Sopenharmony_ci    }
1209596a2c1Sopenharmony_ci    SortLocaleItemList(localeItemList, options);
1219596a2c1Sopenharmony_ci    return localeItemList;
1229596a2c1Sopenharmony_ci}
1239596a2c1Sopenharmony_ci
1249596a2c1Sopenharmony_civoid SystemLocaleManager::SortLocaleItemList(std::vector<LocaleItem> &localeItemList, const SortOptions &options)
1259596a2c1Sopenharmony_ci{
1269596a2c1Sopenharmony_ci    std::vector<std::string> collatorLocaleTags { options.localeTag };
1279596a2c1Sopenharmony_ci    std::map<std::string, std::string> collatorOptions {};
1289596a2c1Sopenharmony_ci    Collator *collator = new (std::nothrow) Collator(collatorLocaleTags, collatorOptions);
1299596a2c1Sopenharmony_ci    if (collator == nullptr) {
1309596a2c1Sopenharmony_ci        return;
1319596a2c1Sopenharmony_ci    }
1329596a2c1Sopenharmony_ci    auto compareFunc = [collator, options](LocaleItem item1, LocaleItem item2) {
1339596a2c1Sopenharmony_ci        if (options.isSuggestedFirst) {
1349596a2c1Sopenharmony_ci            if (item1.suggestionType < item2.suggestionType) {
1359596a2c1Sopenharmony_ci                return false;
1369596a2c1Sopenharmony_ci            } else if (item1.suggestionType > item2.suggestionType) {
1379596a2c1Sopenharmony_ci                return true;
1389596a2c1Sopenharmony_ci            }
1399596a2c1Sopenharmony_ci        }
1409596a2c1Sopenharmony_ci        CompareResult result = CompareResult::INVALID;
1419596a2c1Sopenharmony_ci        if (item1.localName.length() != 0) {
1429596a2c1Sopenharmony_ci            result = collator->Compare(item1.localName, item2.localName);
1439596a2c1Sopenharmony_ci            if (result == CompareResult::SMALLER) {
1449596a2c1Sopenharmony_ci                return true;
1459596a2c1Sopenharmony_ci            }
1469596a2c1Sopenharmony_ci            if (result == CompareResult::INVALID) {
1479596a2c1Sopenharmony_ci                HILOG_ERROR_I18N("SystemLocaleManager: invalid compare result for local name.");
1489596a2c1Sopenharmony_ci            }
1499596a2c1Sopenharmony_ci            return false;
1509596a2c1Sopenharmony_ci        }
1519596a2c1Sopenharmony_ci        result = collator->Compare(item1.displayName, item2.displayName);
1529596a2c1Sopenharmony_ci        if (result == CompareResult::SMALLER) {
1539596a2c1Sopenharmony_ci            return true;
1549596a2c1Sopenharmony_ci        }
1559596a2c1Sopenharmony_ci        if (result == CompareResult::INVALID) {
1569596a2c1Sopenharmony_ci            HILOG_ERROR_I18N("SystemLocaleManager: invalid compare result for display name.");
1579596a2c1Sopenharmony_ci        }
1589596a2c1Sopenharmony_ci        return false;
1599596a2c1Sopenharmony_ci    };
1609596a2c1Sopenharmony_ci    std::sort(localeItemList.begin(), localeItemList.end(), compareFunc);
1619596a2c1Sopenharmony_ci    delete collator;
1629596a2c1Sopenharmony_ci}
1639596a2c1Sopenharmony_ci
1649596a2c1Sopenharmony_cistd::vector<TimeZoneCityItem> SystemLocaleManager::GetTimezoneCityInfoArray(I18nErrorCode& status)
1659596a2c1Sopenharmony_ci{
1669596a2c1Sopenharmony_ci    std::vector<TimeZoneCityItem> result;
1679596a2c1Sopenharmony_ci    status = I18nErrorCode::SUCCESS;
1689596a2c1Sopenharmony_ci    if (!CheckSystemPermission()) {
1699596a2c1Sopenharmony_ci        status = I18nErrorCode::NOT_SYSTEM_APP;
1709596a2c1Sopenharmony_ci        return result;
1719596a2c1Sopenharmony_ci    }
1729596a2c1Sopenharmony_ci    result = GetTimezoneCityInfoArray();
1739596a2c1Sopenharmony_ci    SortTimezoneCityItemList(LocaleConfig::GetSystemLocale(), result);
1749596a2c1Sopenharmony_ci    return result;
1759596a2c1Sopenharmony_ci}
1769596a2c1Sopenharmony_ci
1779596a2c1Sopenharmony_cistd::vector<TimeZoneCityItem> SystemLocaleManager::GetTimezoneCityInfoArray()
1789596a2c1Sopenharmony_ci{
1799596a2c1Sopenharmony_ci    std::vector<TimeZoneCityItem> result;
1809596a2c1Sopenharmony_ci    std::set<std::string> zoneCityIds = I18nTimeZone::GetAvailableZoneCityIDs();
1819596a2c1Sopenharmony_ci    std::string locale = LocaleConfig::GetSystemLocale();
1829596a2c1Sopenharmony_ci    std::string localeBaseName = I18nTimeZone::GetLocaleBaseName(locale);
1839596a2c1Sopenharmony_ci    std::map<std::string, std::string> displayNameMap = I18nTimeZone::FindCityDisplayNameMap(localeBaseName);
1849596a2c1Sopenharmony_ci    std::map<std::string, icu::TimeZone*> tzMap;
1859596a2c1Sopenharmony_ci    bool ifEnforce = GetPseudoLocalizationEnforce();
1869596a2c1Sopenharmony_ci    for (auto it = zoneCityIds.begin(); it != zoneCityIds.end(); ++it) {
1879596a2c1Sopenharmony_ci        std::string cityId = *it, cityDisplayName = "";
1889596a2c1Sopenharmony_ci        if (displayNameMap.find(cityId) != displayNameMap.end()) {
1899596a2c1Sopenharmony_ci            cityDisplayName = displayNameMap.find(cityId)->second;
1909596a2c1Sopenharmony_ci        }
1919596a2c1Sopenharmony_ci        int32_t rawOffset = 0, dstOffset = 0;
1929596a2c1Sopenharmony_ci        bool local = false;
1939596a2c1Sopenharmony_ci        UErrorCode status = U_ZERO_ERROR;
1949596a2c1Sopenharmony_ci        UDate date = icu::Calendar::getNow();
1959596a2c1Sopenharmony_ci        std::string timezoneId = I18nTimeZone::GetTimezoneIdByCityId(cityId);
1969596a2c1Sopenharmony_ci        if (timezoneId.length() == 0) {
1979596a2c1Sopenharmony_ci            continue;
1989596a2c1Sopenharmony_ci        }
1999596a2c1Sopenharmony_ci        if (tzMap.find(timezoneId) != tzMap.end()) {
2009596a2c1Sopenharmony_ci            icu::TimeZone *icuTimeZone = tzMap.find(timezoneId)->second;
2019596a2c1Sopenharmony_ci            icuTimeZone->getOffset(date, (UBool)local, rawOffset, dstOffset, status);
2029596a2c1Sopenharmony_ci        } else {
2039596a2c1Sopenharmony_ci            icu::UnicodeString unicodeString = icu::UnicodeString::fromUTF8(timezoneId);
2049596a2c1Sopenharmony_ci            icu::TimeZone *icuTimeZone = icu::TimeZone::createTimeZone(unicodeString);
2059596a2c1Sopenharmony_ci            if (icuTimeZone == nullptr) {
2069596a2c1Sopenharmony_ci                continue;
2079596a2c1Sopenharmony_ci            }
2089596a2c1Sopenharmony_ci            icuTimeZone->getOffset(date, (UBool)local, rawOffset, dstOffset, status);
2099596a2c1Sopenharmony_ci            tzMap.insert({timezoneId, icuTimeZone});
2109596a2c1Sopenharmony_ci        }
2119596a2c1Sopenharmony_ci        struct TimeZoneCityItem tzCityItem = {
2129596a2c1Sopenharmony_ci            timezoneId, cityId, PseudoLocalizationProcessor(cityDisplayName, ifEnforce), dstOffset + rawOffset,
2139596a2c1Sopenharmony_ci            PseudoLocalizationProcessor("", ifEnforce), rawOffset
2149596a2c1Sopenharmony_ci        };
2159596a2c1Sopenharmony_ci        result.push_back(tzCityItem);
2169596a2c1Sopenharmony_ci    }
2179596a2c1Sopenharmony_ci    for (auto it = tzMap.begin(); it != tzMap.end(); ++it) {
2189596a2c1Sopenharmony_ci        delete it->second;
2199596a2c1Sopenharmony_ci        it->second = nullptr;
2209596a2c1Sopenharmony_ci    }
2219596a2c1Sopenharmony_ci    return result;
2229596a2c1Sopenharmony_ci}
2239596a2c1Sopenharmony_ci
2249596a2c1Sopenharmony_civoid SystemLocaleManager::SortTimezoneCityItemList(const std::string &locale,
2259596a2c1Sopenharmony_ci                                                   std::vector<TimeZoneCityItem> &timezoneCityItemList)
2269596a2c1Sopenharmony_ci{
2279596a2c1Sopenharmony_ci    std::vector<std::string> collatorLocaleTags { locale };
2289596a2c1Sopenharmony_ci    std::map<std::string, std::string> collatorOptions {};
2299596a2c1Sopenharmony_ci    Collator *collator = new (std::nothrow) Collator(collatorLocaleTags, collatorOptions);
2309596a2c1Sopenharmony_ci    if (collator == nullptr) {
2319596a2c1Sopenharmony_ci        return;
2329596a2c1Sopenharmony_ci    }
2339596a2c1Sopenharmony_ci    auto sortFunc = [collator](TimeZoneCityItem item1, TimeZoneCityItem item2) {
2349596a2c1Sopenharmony_ci        CompareResult result = CompareResult::INVALID;
2359596a2c1Sopenharmony_ci        result = collator->Compare(item1.cityDisplayName, item2.cityDisplayName);
2369596a2c1Sopenharmony_ci        if (result == CompareResult::SMALLER) {
2379596a2c1Sopenharmony_ci            return true;
2389596a2c1Sopenharmony_ci        }
2399596a2c1Sopenharmony_ci        if (result == CompareResult::INVALID) {
2409596a2c1Sopenharmony_ci            HILOG_ERROR_I18N("SystemLocaleManager: invalid compare result for city display name.");
2419596a2c1Sopenharmony_ci        }
2429596a2c1Sopenharmony_ci        return false;
2439596a2c1Sopenharmony_ci    };
2449596a2c1Sopenharmony_ci    std::sort(timezoneCityItemList.begin(), timezoneCityItemList.end(), sortFunc);
2459596a2c1Sopenharmony_ci    delete collator;
2469596a2c1Sopenharmony_ci}
2479596a2c1Sopenharmony_ci} // I18n
2489596a2c1Sopenharmony_ci} // Global
2499596a2c1Sopenharmony_ci} // OHOS