19596a2c1Sopenharmony_ci/*
29596a2c1Sopenharmony_ci * Copyright (c) 2024 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 "locale_data.h"
179596a2c1Sopenharmony_ci#include "locale_matcher.h"
189596a2c1Sopenharmony_ci#include "locale_util.h"
199596a2c1Sopenharmony_ci
209596a2c1Sopenharmony_cinamespace OHOS {
219596a2c1Sopenharmony_cinamespace Global {
229596a2c1Sopenharmony_cinamespace I18n {
239596a2c1Sopenharmony_ciuint64_t LocaleMatcher::EN_GB_ENCODE = LocaleUtil::EncodeLocale("en", nullptr, "GB");
249596a2c1Sopenharmony_ciuint64_t LocaleMatcher::EN_QAAG_ENCODE = LocaleUtil::EncodeLocale("en", "Qaag", nullptr);
259596a2c1Sopenharmony_ciuint64_t LocaleMatcher::ZH_HANT_MO_ENCODE = LocaleUtil::EncodeLocale("zh", "Hant", "MO");
269596a2c1Sopenharmony_ciuint64_t LocaleMatcher::ZH_HK_ENCODE = LocaleUtil::EncodeLocale("zh", nullptr, "HK");
279596a2c1Sopenharmony_ciuint32_t LocaleMatcher::HANT_ENCODE = LocaleUtil::EncodeScript("Hant");
289596a2c1Sopenharmony_ci
299596a2c1Sopenharmony_cistd::string LocaleMatcher::GetBestMatchedLocale(const LocaleInfo* requestLocale,
309596a2c1Sopenharmony_ci    const std::vector<LocaleInfo*>& candidateLocales)
319596a2c1Sopenharmony_ci{
329596a2c1Sopenharmony_ci    if (candidateLocales.size() == 0) {
339596a2c1Sopenharmony_ci        return "";
349596a2c1Sopenharmony_ci    }
359596a2c1Sopenharmony_ci    LocaleInfo* bestMatch = candidateLocales[0];
369596a2c1Sopenharmony_ci    for (size_t i = 1; i < candidateLocales.size(); ++i) {
379596a2c1Sopenharmony_ci        if (IsMoreSuitable(bestMatch, candidateLocales[i], requestLocale) < 0) {
389596a2c1Sopenharmony_ci            bestMatch = candidateLocales[i];
399596a2c1Sopenharmony_ci        }
409596a2c1Sopenharmony_ci    }
419596a2c1Sopenharmony_ci    return bestMatch->ToString();
429596a2c1Sopenharmony_ci}
439596a2c1Sopenharmony_ci
449596a2c1Sopenharmony_ciint8_t LocaleMatcher::IsMoreSuitable(const LocaleInfo *current, const LocaleInfo *other, const LocaleInfo *request)
459596a2c1Sopenharmony_ci{
469596a2c1Sopenharmony_ci    if (request == nullptr) {
479596a2c1Sopenharmony_ci        // if request ResLocale is nullptr, the candidate is also nullptr will be more suitable
489596a2c1Sopenharmony_ci        if (current != nullptr && other == nullptr) {
499596a2c1Sopenharmony_ci        // -1 means other is more suitable
509596a2c1Sopenharmony_ci            return -1;
519596a2c1Sopenharmony_ci        }
529596a2c1Sopenharmony_ci        if (current == nullptr && other != nullptr) {
539596a2c1Sopenharmony_ci            // 1 means current is more suitable
549596a2c1Sopenharmony_ci            return 1;
559596a2c1Sopenharmony_ci        }
569596a2c1Sopenharmony_ci        return 0;
579596a2c1Sopenharmony_ci    }
589596a2c1Sopenharmony_ci    if (current == nullptr && other == nullptr) {
599596a2c1Sopenharmony_ci        return 0;
609596a2c1Sopenharmony_ci    }
619596a2c1Sopenharmony_ci    bool isLangEqual = CompareLanguage(current, other);
629596a2c1Sopenharmony_ci    if (!isLangEqual) {
639596a2c1Sopenharmony_ci        // current or other language is null, not null language is better
649596a2c1Sopenharmony_ci        bool result = CompareRegionWhenLangIsNotEqual(current, other, request);
659596a2c1Sopenharmony_ci        return result ? 1 : -1;
669596a2c1Sopenharmony_ci    }
679596a2c1Sopenharmony_ci    uint16_t currentEncodedRegion =
689596a2c1Sopenharmony_ci        LocaleUtil::EncodeRegionByLocaleInfo(current);
699596a2c1Sopenharmony_ci    uint16_t otherEncodedRegion =
709596a2c1Sopenharmony_ci        LocaleUtil::EncodeRegionByLocaleInfo(other);
719596a2c1Sopenharmony_ci    if (currentEncodedRegion == otherEncodedRegion) {
729596a2c1Sopenharmony_ci        // same language,same script,same region
739596a2c1Sopenharmony_ci        return CompareLanguageIgnoreOldNewCode(current, other, request);
749596a2c1Sopenharmony_ci    }
759596a2c1Sopenharmony_ci    // equal request region is better
769596a2c1Sopenharmony_ci    uint16_t requestEncodedRegion = LocaleUtil::EncodeRegionByLocaleInfo(request);
779596a2c1Sopenharmony_ci    if (currentEncodedRegion == requestEncodedRegion) {
789596a2c1Sopenharmony_ci        return 1;
799596a2c1Sopenharmony_ci    }
809596a2c1Sopenharmony_ci    if (otherEncodedRegion == requestEncodedRegion) {
819596a2c1Sopenharmony_ci        return -1;
829596a2c1Sopenharmony_ci    }
839596a2c1Sopenharmony_ci    int8_t isRegionEqual = CompareRegion(current, other, request);
849596a2c1Sopenharmony_ci    if (isRegionEqual == 0) {
859596a2c1Sopenharmony_ci        return CompareLanguageIgnoreOldNewCode(current, other, request);
869596a2c1Sopenharmony_ci    }
879596a2c1Sopenharmony_ci    return isRegionEqual;
889596a2c1Sopenharmony_ci}
899596a2c1Sopenharmony_ci
909596a2c1Sopenharmony_cibool LocaleMatcher::CompareLanguage(const LocaleInfo *current, const LocaleInfo *other)
919596a2c1Sopenharmony_ci{
929596a2c1Sopenharmony_ci    uint16_t currentEncodedLanguage = LocaleUtil::EncodeLanguageByLocaleInfo(current);
939596a2c1Sopenharmony_ci    uint16_t otherEncodedLanguage = LocaleUtil::EncodeLanguageByLocaleInfo(other);
949596a2c1Sopenharmony_ci    // 1, 2, 3, 4 is the index.
959596a2c1Sopenharmony_ci    return ((currentEncodedLanguage == otherEncodedLanguage) ||
969596a2c1Sopenharmony_ci        ((currentEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[0]) &&
979596a2c1Sopenharmony_ci        (otherEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[0])) ||
989596a2c1Sopenharmony_ci        ((otherEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[0]) &&
999596a2c1Sopenharmony_ci        (currentEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[0])) ||
1009596a2c1Sopenharmony_ci        ((currentEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[1]) &&
1019596a2c1Sopenharmony_ci        (otherEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[1])) ||
1029596a2c1Sopenharmony_ci        ((otherEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[1]) &&
1039596a2c1Sopenharmony_ci        (currentEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[1])) ||
1049596a2c1Sopenharmony_ci        ((currentEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[2]) && // 2 is index
1059596a2c1Sopenharmony_ci        (otherEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[2])) || // 2 is index
1069596a2c1Sopenharmony_ci        ((otherEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[2]) && // 2 is index
1079596a2c1Sopenharmony_ci        (currentEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[2])) || // 2 is index
1089596a2c1Sopenharmony_ci        ((currentEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[3]) && // 3 is index
1099596a2c1Sopenharmony_ci        (otherEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[3])) || // 3 is index
1109596a2c1Sopenharmony_ci        ((otherEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[3]) && // 3 is index
1119596a2c1Sopenharmony_ci        (currentEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[3])) || // 3 is index
1129596a2c1Sopenharmony_ci        ((currentEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[4]) && // 4 is index
1139596a2c1Sopenharmony_ci        (otherEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[4])) || // 4 is index
1149596a2c1Sopenharmony_ci        ((otherEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[4]) && // 4 is index
1159596a2c1Sopenharmony_ci        (currentEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[4]))); // 4 is index
1169596a2c1Sopenharmony_ci}
1179596a2c1Sopenharmony_ci
1189596a2c1Sopenharmony_cibool LocaleMatcher::CompareRegionWhenLangIsNotEqual(const LocaleInfo *current, const LocaleInfo *other,
1199596a2c1Sopenharmony_ci    const LocaleInfo *request)
1209596a2c1Sopenharmony_ci{
1219596a2c1Sopenharmony_ci    int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
1229596a2c1Sopenharmony_ci    if (qaagResult != 0) {
1239596a2c1Sopenharmony_ci        return qaagResult;
1249596a2c1Sopenharmony_ci    }
1259596a2c1Sopenharmony_ci    if (request != nullptr &&
1269596a2c1Sopenharmony_ci        (LocaleUtil::EncodeLanguage(request->GetLanguage().c_str())) == LocaleUtil::EncodeLanguage("en")) {
1279596a2c1Sopenharmony_ci        // when request is en-us,empty region is better
1289596a2c1Sopenharmony_ci        if ((LocaleUtil::EncodeRegion(request->GetRegion().c_str())) == LocaleUtil::EncodeRegion("US")) {
1299596a2c1Sopenharmony_ci            if (current != nullptr) {
1309596a2c1Sopenharmony_ci                return (current->GetRegion().length() == 0) ||
1319596a2c1Sopenharmony_ci                    ((LocaleUtil::EncodeRegion(current->GetRegion().c_str())) == LocaleUtil::EncodeRegion("US"));
1329596a2c1Sopenharmony_ci            } else {
1339596a2c1Sopenharmony_ci                return !(other->GetRegion().length() == 0 ||
1349596a2c1Sopenharmony_ci                    ((LocaleUtil::EncodeRegion(other->GetRegion().c_str())) == LocaleUtil::EncodeRegion("US")));
1359596a2c1Sopenharmony_ci            }
1369596a2c1Sopenharmony_ci        } else if (IsSimilarToUsEnglish(request)) {
1379596a2c1Sopenharmony_ci            if (current != nullptr) {
1389596a2c1Sopenharmony_ci                return IsSimilarToUsEnglish(current);
1399596a2c1Sopenharmony_ci            } else {
1409596a2c1Sopenharmony_ci                return !IsSimilarToUsEnglish(other);
1419596a2c1Sopenharmony_ci            }
1429596a2c1Sopenharmony_ci        }
1439596a2c1Sopenharmony_ci    }
1449596a2c1Sopenharmony_ci    return current != nullptr;
1459596a2c1Sopenharmony_ci}
1469596a2c1Sopenharmony_ci
1479596a2c1Sopenharmony_ciint8_t LocaleMatcher::CompareRegionWhenQaag(const LocaleInfo *current, const LocaleInfo *other,
1489596a2c1Sopenharmony_ci    const LocaleInfo *request)
1499596a2c1Sopenharmony_ci{
1509596a2c1Sopenharmony_ci    if ((request != nullptr) && (LocaleUtil::EncodeLocale(request->GetLanguage().c_str(), request->GetScript().c_str(),
1519596a2c1Sopenharmony_ci        nullptr) == LocaleMatcher::EN_QAAG_ENCODE)) {
1529596a2c1Sopenharmony_ci        if ((current != nullptr) && (LocaleUtil::EncodeLocale(current->GetLanguage().c_str(), nullptr,
1539596a2c1Sopenharmony_ci            current->GetRegion().c_str()) == LocaleMatcher::EN_GB_ENCODE)) {
1549596a2c1Sopenharmony_ci            return 1;
1559596a2c1Sopenharmony_ci        }
1569596a2c1Sopenharmony_ci        if ((other != nullptr) && (LocaleUtil::EncodeLocale(other->GetLanguage().c_str(), nullptr,
1579596a2c1Sopenharmony_ci            other->GetRegion().c_str()) == LocaleMatcher::EN_GB_ENCODE)) {
1589596a2c1Sopenharmony_ci            return -1;
1599596a2c1Sopenharmony_ci        }
1609596a2c1Sopenharmony_ci    }
1619596a2c1Sopenharmony_ci    return 0;
1629596a2c1Sopenharmony_ci}
1639596a2c1Sopenharmony_ci
1649596a2c1Sopenharmony_cibool LocaleMatcher::IsSimilarToUsEnglish(const LocaleInfo *localeInfo)
1659596a2c1Sopenharmony_ci{
1669596a2c1Sopenharmony_ci    uint64_t localeEncode = LocaleUtil::EncodeLocale("en", nullptr,
1679596a2c1Sopenharmony_ci        (localeInfo == nullptr) ? nullptr : localeInfo->GetRegion().c_str());
1689596a2c1Sopenharmony_ci    uint64_t loclaeEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
1699596a2c1Sopenharmony_ci    FindTrackPath(nullptr, LocaleMatcher::TRACKPATH_ARRAY_SIZE, localeEncode, loclaeEncodedTrackPath);
1709596a2c1Sopenharmony_ci    uint8_t len = LocaleMatcher::TRACKPATH_ARRAY_SIZE;
1719596a2c1Sopenharmony_ci    for (uint8_t i = 0; i < len; ++i) {
1729596a2c1Sopenharmony_ci        if (loclaeEncodedTrackPath[i] == LocaleUtil::EncodeLocale("en", nullptr, nullptr)) {
1739596a2c1Sopenharmony_ci            return true;
1749596a2c1Sopenharmony_ci        }
1759596a2c1Sopenharmony_ci        if (loclaeEncodedTrackPath[i] == LocaleUtil::EncodeLocale("en", nullptr, "001")) {
1769596a2c1Sopenharmony_ci            return false;
1779596a2c1Sopenharmony_ci        }
1789596a2c1Sopenharmony_ci    }
1799596a2c1Sopenharmony_ci    return false;
1809596a2c1Sopenharmony_ci}
1819596a2c1Sopenharmony_ci
1829596a2c1Sopenharmony_civoid LocaleMatcher::FindTrackPath(const LocaleInfo *request, size_t len, uint64_t encodedLocale, uint64_t *result)
1839596a2c1Sopenharmony_ci{
1849596a2c1Sopenharmony_ci    uint64_t currentEncodedLocale = encodedLocale;
1859596a2c1Sopenharmony_ci    size_t i = 0;
1869596a2c1Sopenharmony_ci    do {
1879596a2c1Sopenharmony_ci        result[i] = currentEncodedLocale;
1889596a2c1Sopenharmony_ci        currentEncodedLocale = SearchParentLocale(currentEncodedLocale, request);
1899596a2c1Sopenharmony_ci        ++i;
1909596a2c1Sopenharmony_ci    } while (currentEncodedLocale != LocaleMatcher::ROOT_LOCALE);
1919596a2c1Sopenharmony_ci    if (i < len) {
1929596a2c1Sopenharmony_ci        result[i] = LocaleMatcher::ROOT_LOCALE;
1939596a2c1Sopenharmony_ci    }
1949596a2c1Sopenharmony_ci}
1959596a2c1Sopenharmony_ci
1969596a2c1Sopenharmony_ciuint64_t LocaleMatcher::SearchParentLocale(uint64_t encodedLocale, const LocaleInfo *request)
1979596a2c1Sopenharmony_ci{
1989596a2c1Sopenharmony_ci    uint64_t tempEncodedLocale = encodedLocale;
1999596a2c1Sopenharmony_ci    if (LocaleUtil::EncodeScriptByLocaleInfo(request) == LocaleMatcher::HANT_ENCODE) {
2009596a2c1Sopenharmony_ci        tempEncodedLocale = AddScript(encodedLocale, LocaleMatcher::HANT_ENCODE);
2019596a2c1Sopenharmony_ci        if (tempEncodedLocale == LocaleMatcher::ZH_HANT_MO_ENCODE) {
2029596a2c1Sopenharmony_ci            return LocaleMatcher::ZH_HK_ENCODE;
2039596a2c1Sopenharmony_ci        }
2049596a2c1Sopenharmony_ci    }
2059596a2c1Sopenharmony_ci    if (IsContainRegion(encodedLocale)) {
2069596a2c1Sopenharmony_ci        for (size_t i = 0; i < LocaleData::LOCALE_PARENTS_KEY.size(); i++) {
2079596a2c1Sopenharmony_ci            if (LocaleData::LOCALE_PARENTS_KEY[i] == tempEncodedLocale) {
2089596a2c1Sopenharmony_ci                return LocaleData::LOCALE_PARENTS_VALUE[i];
2099596a2c1Sopenharmony_ci            }
2109596a2c1Sopenharmony_ci        }
2119596a2c1Sopenharmony_ci        return ClearRegion(encodedLocale);
2129596a2c1Sopenharmony_ci    }
2139596a2c1Sopenharmony_ci    return LocaleMatcher::ROOT_LOCALE;
2149596a2c1Sopenharmony_ci}
2159596a2c1Sopenharmony_ci
2169596a2c1Sopenharmony_ciuint64_t LocaleMatcher::AddScript(uint64_t encodedLocale, uint32_t encodedScript)
2179596a2c1Sopenharmony_ci{
2189596a2c1Sopenharmony_ci    // 16 is the offset of script
2199596a2c1Sopenharmony_ci    return (encodedLocale | ((static_cast<uint64_t>(encodedScript) & 0x00000000FFFFFFFFLU) << 16));
2209596a2c1Sopenharmony_ci}
2219596a2c1Sopenharmony_ci
2229596a2c1Sopenharmony_cibool LocaleMatcher::IsContainRegion(uint64_t encodedLocale)
2239596a2c1Sopenharmony_ci{
2249596a2c1Sopenharmony_ci    return (encodedLocale & 0x000000000000FFFFLU) != 0;
2259596a2c1Sopenharmony_ci}
2269596a2c1Sopenharmony_ci
2279596a2c1Sopenharmony_ciuint64_t LocaleMatcher::ClearRegion(uint64_t encodedLocale)
2289596a2c1Sopenharmony_ci{
2299596a2c1Sopenharmony_ci    return encodedLocale & 0xFFFFFFFFFFFF0000LU;
2309596a2c1Sopenharmony_ci}
2319596a2c1Sopenharmony_ci
2329596a2c1Sopenharmony_ciint8_t LocaleMatcher::CompareRegion(const LocaleInfo *current, const LocaleInfo *other, const LocaleInfo *request)
2339596a2c1Sopenharmony_ci{
2349596a2c1Sopenharmony_ci    uint16_t currentEncodedRegion = LocaleUtil::EncodeRegionByLocaleInfo(current);
2359596a2c1Sopenharmony_ci    uint16_t otherEncodedRegion = LocaleUtil::EncodeRegionByLocaleInfo(other);
2369596a2c1Sopenharmony_ci    if (request == nullptr || request->GetRegion().size() == 0) {
2379596a2c1Sopenharmony_ci        return CompareWhenRegionIsNull(currentEncodedRegion, otherEncodedRegion, current, other, request);
2389596a2c1Sopenharmony_ci    }
2399596a2c1Sopenharmony_ci    uint64_t requestEncodedLocale = LocaleUtil::EncodeLocale(request->GetLanguage().c_str(), nullptr,
2409596a2c1Sopenharmony_ci        request->GetRegion().c_str());
2419596a2c1Sopenharmony_ci    uint64_t requestEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
2429596a2c1Sopenharmony_ci    FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, requestEncodedLocale, requestEncodedTrackPath);
2439596a2c1Sopenharmony_ci    uint64_t currentEncodedLocale = LocaleUtil::EncodeLocale(request->GetLanguage().c_str(), nullptr,
2449596a2c1Sopenharmony_ci        (current == nullptr) ? nullptr : current->GetRegion().c_str());
2459596a2c1Sopenharmony_ci    uint64_t otherEncodedLocale = LocaleUtil::EncodeLocale(request->GetLanguage().c_str(), nullptr,
2469596a2c1Sopenharmony_ci        (other == nullptr) ? nullptr : other->GetRegion().c_str());
2479596a2c1Sopenharmony_ci    int8_t currentMatchDistance = SearchTrackPathDistance(requestEncodedTrackPath, LocaleMatcher::TRACKPATH_ARRAY_SIZE,
2489596a2c1Sopenharmony_ci        currentEncodedLocale);
2499596a2c1Sopenharmony_ci    int8_t otherMatchDistance = SearchTrackPathDistance(requestEncodedTrackPath, LocaleMatcher::TRACKPATH_ARRAY_SIZE,
2509596a2c1Sopenharmony_ci        otherEncodedLocale);
2519596a2c1Sopenharmony_ci    if (currentMatchDistance < otherMatchDistance) {
2529596a2c1Sopenharmony_ci        return 1;
2539596a2c1Sopenharmony_ci    }
2549596a2c1Sopenharmony_ci    if (currentMatchDistance > otherMatchDistance) {
2559596a2c1Sopenharmony_ci        return -1;
2569596a2c1Sopenharmony_ci    }
2579596a2c1Sopenharmony_ci    int8_t result = CompareDistance(currentEncodedLocale, otherEncodedLocale, requestEncodedTrackPath, request);
2589596a2c1Sopenharmony_ci    if (result != 0) {
2599596a2c1Sopenharmony_ci        return result;
2609596a2c1Sopenharmony_ci    }
2619596a2c1Sopenharmony_ci    result = CompareDefaultRegion(current, other, request);
2629596a2c1Sopenharmony_ci    if (result != 0) {
2639596a2c1Sopenharmony_ci        return result;
2649596a2c1Sopenharmony_ci    }
2659596a2c1Sopenharmony_ci    uint16_t requestDefaultRegion =
2669596a2c1Sopenharmony_ci        FindDefaultRegionEncode(request->GetLanguage().c_str(), request->GetScript().c_str());
2679596a2c1Sopenharmony_ci    if (requestDefaultRegion == currentEncodedRegion) {
2689596a2c1Sopenharmony_ci        return 1;
2699596a2c1Sopenharmony_ci    }
2709596a2c1Sopenharmony_ci    if (requestDefaultRegion == otherEncodedRegion) {
2719596a2c1Sopenharmony_ci        return -1;
2729596a2c1Sopenharmony_ci    }
2739596a2c1Sopenharmony_ci    return AlphabeticallyCompare(current, currentEncodedLocale, other, otherEncodedLocale);
2749596a2c1Sopenharmony_ci}
2759596a2c1Sopenharmony_ci
2769596a2c1Sopenharmony_ciint8_t LocaleMatcher::CompareWhenRegionIsNull(uint16_t currentEncodedRegion, uint16_t otherEncodedRegion,
2779596a2c1Sopenharmony_ci    const LocaleInfo *current, const LocaleInfo *other, const LocaleInfo *request)
2789596a2c1Sopenharmony_ci{
2799596a2c1Sopenharmony_ci    if (current == nullptr || current->GetRegion().length() == 0) {
2809596a2c1Sopenharmony_ci        return 1;
2819596a2c1Sopenharmony_ci    }
2829596a2c1Sopenharmony_ci    if (other == nullptr || other->GetRegion().length() == 0) {
2839596a2c1Sopenharmony_ci        return -1;
2849596a2c1Sopenharmony_ci    }
2859596a2c1Sopenharmony_ci    int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
2869596a2c1Sopenharmony_ci    if (qaagResult != 0) {
2879596a2c1Sopenharmony_ci        return qaagResult;
2889596a2c1Sopenharmony_ci    }
2899596a2c1Sopenharmony_ci    // get request default region
2909596a2c1Sopenharmony_ci    uint16_t requestDefaultRegion =
2919596a2c1Sopenharmony_ci        FindDefaultRegionEncode((request == nullptr) ? nullptr : request->GetLanguage().c_str(),
2929596a2c1Sopenharmony_ci            (request == nullptr) ? nullptr : request->GetScript().c_str());
2939596a2c1Sopenharmony_ci    if (requestDefaultRegion == currentEncodedRegion) {
2949596a2c1Sopenharmony_ci        return 1;
2959596a2c1Sopenharmony_ci    }
2969596a2c1Sopenharmony_ci    if (requestDefaultRegion == otherEncodedRegion) {
2979596a2c1Sopenharmony_ci        return -1;
2989596a2c1Sopenharmony_ci    }
2999596a2c1Sopenharmony_ci    // current and other region is not null.alphabetically
3009596a2c1Sopenharmony_ci    uint64_t currentEncodedLocale = LocaleUtil::EncodeLocale(
3019596a2c1Sopenharmony_ci        (request == nullptr) ? nullptr : request->GetLanguage().c_str(), nullptr,
3029596a2c1Sopenharmony_ci        (current == nullptr) ? nullptr : current->GetRegion().c_str());
3039596a2c1Sopenharmony_ci    uint64_t otherEncodedLocale = LocaleUtil::EncodeLocale(
3049596a2c1Sopenharmony_ci        (request == nullptr) ? nullptr : request->GetLanguage().c_str(), nullptr, other->GetRegion().c_str());
3059596a2c1Sopenharmony_ci    return AlphabeticallyCompare(current, currentEncodedLocale, other, otherEncodedLocale);
3069596a2c1Sopenharmony_ci}
3079596a2c1Sopenharmony_ci
3089596a2c1Sopenharmony_ciuint16_t LocaleMatcher::FindDefaultRegionEncode(const char *language, const char *script)
3099596a2c1Sopenharmony_ci{
3109596a2c1Sopenharmony_ci    /* first try language and script */
3119596a2c1Sopenharmony_ci    uint64_t encodedLocale = LocaleUtil::EncodeLocale(language, script, nullptr);
3129596a2c1Sopenharmony_ci    if (encodedLocale == LocaleUtil::EncodeLocale("en", "Qaag", nullptr)) {
3139596a2c1Sopenharmony_ci        encodedLocale = LocaleUtil::EncodeLocale("en", "Latn", nullptr);
3149596a2c1Sopenharmony_ci    }
3159596a2c1Sopenharmony_ci    for (size_t i = 0; i < LocaleData::LIKELY_TAGS_CODES_KEY.size(); i++) {
3169596a2c1Sopenharmony_ci        if (LocaleData::LIKELY_TAGS_CODES_KEY[i] == encodedLocale) {
3179596a2c1Sopenharmony_ci            return static_cast<uint16_t>((LocaleData::LIKELY_TAGS_CODES_VALUE[i] & 0x000000000000ffff));
3189596a2c1Sopenharmony_ci        }
3199596a2c1Sopenharmony_ci    }
3209596a2c1Sopenharmony_ci    /* if not found and script is not null,try language */
3219596a2c1Sopenharmony_ci    if (script != nullptr) {
3229596a2c1Sopenharmony_ci        encodedLocale = LocaleUtil::EncodeLocale(language, nullptr, nullptr);
3239596a2c1Sopenharmony_ci        for (size_t i = 0; i < LocaleData::LIKELY_TAGS_CODES_KEY.size(); i++) {
3249596a2c1Sopenharmony_ci            if (LocaleData::LIKELY_TAGS_CODES_KEY[i] == encodedLocale) {
3259596a2c1Sopenharmony_ci                return static_cast<uint16_t>((LocaleData::LIKELY_TAGS_CODES_VALUE[i] & 0x000000000000ffff));
3269596a2c1Sopenharmony_ci            }
3279596a2c1Sopenharmony_ci        }
3289596a2c1Sopenharmony_ci    }
3299596a2c1Sopenharmony_ci    return LocaleMatcher::NULL_REGION;
3309596a2c1Sopenharmony_ci}
3319596a2c1Sopenharmony_ci
3329596a2c1Sopenharmony_ciint8_t LocaleMatcher::AlphabeticallyCompare(const LocaleInfo *current, uint64_t currentEncodedLocale,
3339596a2c1Sopenharmony_ci    const LocaleInfo *other, uint64_t otherEncodedLocale)
3349596a2c1Sopenharmony_ci{
3359596a2c1Sopenharmony_ci    if (currentEncodedLocale == otherEncodedLocale) {
3369596a2c1Sopenharmony_ci        return 0;
3379596a2c1Sopenharmony_ci    }
3389596a2c1Sopenharmony_ci    if (current == nullptr || current->GetRegion().length() == 0) {
3399596a2c1Sopenharmony_ci        return -1;
3409596a2c1Sopenharmony_ci    }
3419596a2c1Sopenharmony_ci    if (other == nullptr || other->GetRegion().length() == 0) {
3429596a2c1Sopenharmony_ci        return 1;
3439596a2c1Sopenharmony_ci    }
3449596a2c1Sopenharmony_ci    // be here region is not null
3459596a2c1Sopenharmony_ci    char currentFirstChar = (current->GetRegion())[0];
3469596a2c1Sopenharmony_ci    char otherFirstChar = (other->GetRegion())[0];
3479596a2c1Sopenharmony_ci    if (currentFirstChar >= '0' && currentFirstChar <= '9') {
3489596a2c1Sopenharmony_ci        if (otherFirstChar < '0' || otherFirstChar > '9') {
3499596a2c1Sopenharmony_ci            return -1;
3509596a2c1Sopenharmony_ci        }
3519596a2c1Sopenharmony_ci    } else {
3529596a2c1Sopenharmony_ci        if (otherFirstChar >= '0' && otherFirstChar <= '9') {
3539596a2c1Sopenharmony_ci            return 1;
3549596a2c1Sopenharmony_ci        }
3559596a2c1Sopenharmony_ci    }
3569596a2c1Sopenharmony_ci    if (currentEncodedLocale > otherEncodedLocale) {
3579596a2c1Sopenharmony_ci        return -1;
3589596a2c1Sopenharmony_ci    }
3599596a2c1Sopenharmony_ci    if (otherEncodedLocale > currentEncodedLocale) {
3609596a2c1Sopenharmony_ci        return 1;
3619596a2c1Sopenharmony_ci    }
3629596a2c1Sopenharmony_ci    return 0;
3639596a2c1Sopenharmony_ci}
3649596a2c1Sopenharmony_ci
3659596a2c1Sopenharmony_ciint8_t LocaleMatcher::SearchTrackPathDistance(const uint64_t *paths, size_t len, uint64_t encodedLocale)
3669596a2c1Sopenharmony_ci{
3679596a2c1Sopenharmony_ci    size_t i = 0;
3689596a2c1Sopenharmony_ci    for (i = 0; i < len; ++i) {
3699596a2c1Sopenharmony_ci        if (paths[i] == LocaleMatcher::ROOT_LOCALE) {
3709596a2c1Sopenharmony_ci            return i;
3719596a2c1Sopenharmony_ci        }
3729596a2c1Sopenharmony_ci        if (paths[i] == encodedLocale) {
3739596a2c1Sopenharmony_ci            return i;
3749596a2c1Sopenharmony_ci        }
3759596a2c1Sopenharmony_ci    }
3769596a2c1Sopenharmony_ci    return static_cast<int8_t>(i);
3779596a2c1Sopenharmony_ci}
3789596a2c1Sopenharmony_ci
3799596a2c1Sopenharmony_ciint8_t LocaleMatcher::CompareDistance(uint64_t currentEncodedLocale, uint64_t otherEncodedLocale,
3809596a2c1Sopenharmony_ci    const uint64_t *requestEncodedTrackPath, const LocaleInfo *request)
3819596a2c1Sopenharmony_ci{
3829596a2c1Sopenharmony_ci    uint64_t currentEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
3839596a2c1Sopenharmony_ci    FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, currentEncodedLocale, currentEncodedTrackPath);
3849596a2c1Sopenharmony_ci    uint64_t otherEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
3859596a2c1Sopenharmony_ci    FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, otherEncodedLocale, otherEncodedTrackPath);
3869596a2c1Sopenharmony_ci    const size_t currentDistance = ComputeTrackPathDistance(requestEncodedTrackPath, currentEncodedTrackPath,
3879596a2c1Sopenharmony_ci        LocaleMatcher::TRACKPATH_ARRAY_SIZE);
3889596a2c1Sopenharmony_ci    const size_t targetDistance = ComputeTrackPathDistance(requestEncodedTrackPath, otherEncodedTrackPath,
3899596a2c1Sopenharmony_ci        LocaleMatcher::TRACKPATH_ARRAY_SIZE);
3909596a2c1Sopenharmony_ci    if (currentDistance < targetDistance) {
3919596a2c1Sopenharmony_ci        return 1;
3929596a2c1Sopenharmony_ci    }
3939596a2c1Sopenharmony_ci    if (currentDistance > targetDistance) {
3949596a2c1Sopenharmony_ci        return -1;
3959596a2c1Sopenharmony_ci    }
3969596a2c1Sopenharmony_ci    return 0;
3979596a2c1Sopenharmony_ci}
3989596a2c1Sopenharmony_ci
3999596a2c1Sopenharmony_cisize_t LocaleMatcher::ComputeTrackPathDistance(const uint64_t *requestPaths, const uint64_t *targetPaths, size_t len)
4009596a2c1Sopenharmony_ci{
4019596a2c1Sopenharmony_ci    for (size_t i = 0; i < len; ++i) {
4029596a2c1Sopenharmony_ci        if (targetPaths[i] == LocaleMatcher::ROOT_LOCALE) {
4039596a2c1Sopenharmony_ci            // targetpath not in request path,so distance is 2*len
4049596a2c1Sopenharmony_ci            return len * 2;
4059596a2c1Sopenharmony_ci        }
4069596a2c1Sopenharmony_ci        for (size_t j = 0; j < len; ++j) {
4079596a2c1Sopenharmony_ci            if (requestPaths[j] == targetPaths[i]) {
4089596a2c1Sopenharmony_ci                return i + j;
4099596a2c1Sopenharmony_ci            }
4109596a2c1Sopenharmony_ci        }
4119596a2c1Sopenharmony_ci    }
4129596a2c1Sopenharmony_ci    return len * 2; // targetpath not in request path,so distance is 2*len
4139596a2c1Sopenharmony_ci}
4149596a2c1Sopenharmony_ci
4159596a2c1Sopenharmony_ciint8_t LocaleMatcher::CompareDefaultRegion(const LocaleInfo *current, const LocaleInfo *other,
4169596a2c1Sopenharmony_ci    const LocaleInfo *request)
4179596a2c1Sopenharmony_ci{
4189596a2c1Sopenharmony_ci    int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
4199596a2c1Sopenharmony_ci    if (qaagResult != 0) {
4209596a2c1Sopenharmony_ci        return qaagResult;
4219596a2c1Sopenharmony_ci    } else {
4229596a2c1Sopenharmony_ci        bool isCurrentDefaultRegion = IsDefaultLocale((request == nullptr) ? nullptr : request->GetLanguage().c_str(),
4239596a2c1Sopenharmony_ci            (request == nullptr) ? nullptr : request->GetScript().c_str(),
4249596a2c1Sopenharmony_ci            (current == nullptr) ? nullptr : current->GetRegion().c_str());
4259596a2c1Sopenharmony_ci        bool isOtherDefaultRegion = IsDefaultLocale((request == nullptr) ? nullptr : request->GetLanguage().c_str(),
4269596a2c1Sopenharmony_ci            (request == nullptr) ? nullptr : request->GetScript().c_str(),
4279596a2c1Sopenharmony_ci            (other == nullptr) ? nullptr : other->GetRegion().c_str());
4289596a2c1Sopenharmony_ci        if (isCurrentDefaultRegion != isOtherDefaultRegion) {
4299596a2c1Sopenharmony_ci            if (isCurrentDefaultRegion) {
4309596a2c1Sopenharmony_ci                return 1;
4319596a2c1Sopenharmony_ci            } else {
4329596a2c1Sopenharmony_ci                return -1;
4339596a2c1Sopenharmony_ci            }
4349596a2c1Sopenharmony_ci        }
4359596a2c1Sopenharmony_ci    }
4369596a2c1Sopenharmony_ci    return 0;
4379596a2c1Sopenharmony_ci}
4389596a2c1Sopenharmony_ci
4399596a2c1Sopenharmony_cibool LocaleMatcher::IsDefaultLocale(const char *language, const char *script, const char *region)
4409596a2c1Sopenharmony_ci{
4419596a2c1Sopenharmony_ci    uint64_t encodedLocale = LocaleUtil::EncodeLocale(language, script, region);
4429596a2c1Sopenharmony_ci    if (ClearRegion(encodedLocale) == LocaleMatcher::EN_QAAG_ENCODE) {
4439596a2c1Sopenharmony_ci        encodedLocale = LocaleUtil::EncodeLocale("en", "Latn", region);
4449596a2c1Sopenharmony_ci    }
4459596a2c1Sopenharmony_ci    for (size_t i = 0; i < LocaleData::TYPICAL_CODES_VALUE.size(); i++) {
4469596a2c1Sopenharmony_ci        if (LocaleData::TYPICAL_CODES_VALUE[i] == encodedLocale) {
4479596a2c1Sopenharmony_ci            return true;
4489596a2c1Sopenharmony_ci        }
4499596a2c1Sopenharmony_ci    }
4509596a2c1Sopenharmony_ci    return false;
4519596a2c1Sopenharmony_ci}
4529596a2c1Sopenharmony_ci
4539596a2c1Sopenharmony_ciint8_t LocaleMatcher::CompareLanguageIgnoreOldNewCode(const LocaleInfo *current, const LocaleInfo *other,
4549596a2c1Sopenharmony_ci    const LocaleInfo *request)
4559596a2c1Sopenharmony_ci{
4569596a2c1Sopenharmony_ci    uint16_t currentLanguageEncode = LocaleUtil::EncodeLanguageByLocaleInfo(current);
4579596a2c1Sopenharmony_ci    uint16_t otherLanguageEncode = LocaleUtil::EncodeLanguageByLocaleInfo(other);
4589596a2c1Sopenharmony_ci    uint16_t requestLanguageEncode = LocaleUtil::EncodeLanguageByLocaleInfo(request);
4599596a2c1Sopenharmony_ci    if ((currentLanguageEncode == requestLanguageEncode) && (otherLanguageEncode != requestLanguageEncode)) {
4609596a2c1Sopenharmony_ci        return 1;
4619596a2c1Sopenharmony_ci    }
4629596a2c1Sopenharmony_ci    if ((otherLanguageEncode == requestLanguageEncode) && (currentLanguageEncode != requestLanguageEncode)) {
4639596a2c1Sopenharmony_ci        return -1;
4649596a2c1Sopenharmony_ci    }
4659596a2c1Sopenharmony_ci    return 0;
4669596a2c1Sopenharmony_ci}
4679596a2c1Sopenharmony_ci
4689596a2c1Sopenharmony_cibool LocaleMatcher::Match(const LocaleInfo *current, const LocaleInfo *other)
4699596a2c1Sopenharmony_ci{
4709596a2c1Sopenharmony_ci    if (current == nullptr || other == nullptr) {
4719596a2c1Sopenharmony_ci        return true;
4729596a2c1Sopenharmony_ci    }
4739596a2c1Sopenharmony_ci    // language is not null.
4749596a2c1Sopenharmony_ci    bool isLanguageEqual = CompareLanguage(current, other);
4759596a2c1Sopenharmony_ci    if (!isLanguageEqual) {
4769596a2c1Sopenharmony_ci        return false;
4779596a2c1Sopenharmony_ci    }
4789596a2c1Sopenharmony_ci    return CompareScript(current, other);
4799596a2c1Sopenharmony_ci}
4809596a2c1Sopenharmony_ci
4819596a2c1Sopenharmony_cibool LocaleMatcher::CompareScript(const LocaleInfo *current, const LocaleInfo *other)
4829596a2c1Sopenharmony_ci{
4839596a2c1Sopenharmony_ci    uint32_t currentEncodedScript = 0;
4849596a2c1Sopenharmony_ci    uint32_t otherEncodedScript = 0;
4859596a2c1Sopenharmony_ci    if ((current != nullptr) && (current->GetScript().length() == 0)) {
4869596a2c1Sopenharmony_ci        currentEncodedScript = FindDefaultScriptEncode(current->GetLanguage().c_str(), current->GetRegion().c_str());
4879596a2c1Sopenharmony_ci    } else {
4889596a2c1Sopenharmony_ci        currentEncodedScript = LocaleUtil::EncodeScriptByLocaleInfo(current);
4899596a2c1Sopenharmony_ci    }
4909596a2c1Sopenharmony_ci    if ((other != nullptr) && (other->GetScript().length() == 0)) {
4919596a2c1Sopenharmony_ci        otherEncodedScript = FindDefaultScriptEncode(other->GetLanguage().c_str(), other->GetRegion().c_str());
4929596a2c1Sopenharmony_ci    } else {
4939596a2c1Sopenharmony_ci        otherEncodedScript = LocaleUtil::EncodeScriptByLocaleInfo(other);
4949596a2c1Sopenharmony_ci    }
4959596a2c1Sopenharmony_ci    if (current != nullptr && other != nullptr) {
4969596a2c1Sopenharmony_ci        // when current locale is en-Qaag is equal en-Latn
4979596a2c1Sopenharmony_ci        if (LocaleUtil::EncodeLocale(current->GetLanguage().c_str(), current->GetScript().c_str(), nullptr) ==
4989596a2c1Sopenharmony_ci            LocaleUtil::EncodeLocale("en", "Qaag", nullptr)) {
4999596a2c1Sopenharmony_ci            if (LocaleUtil::EncodeLocale(other->GetLanguage().c_str(), other->GetScript().c_str(), nullptr) ==
5009596a2c1Sopenharmony_ci                LocaleUtil::EncodeLocale("en", "Latn", nullptr)) {
5019596a2c1Sopenharmony_ci                return true;
5029596a2c1Sopenharmony_ci            }
5039596a2c1Sopenharmony_ci        }
5049596a2c1Sopenharmony_ci    }
5059596a2c1Sopenharmony_ci    bool compareRegion = false;
5069596a2c1Sopenharmony_ci    if ((currentEncodedScript == LocaleMatcher::NULL_SCRIPT) || (otherEncodedScript == LocaleMatcher::NULL_SCRIPT)) {
5079596a2c1Sopenharmony_ci        // if request script is null, region must be same
5089596a2c1Sopenharmony_ci        compareRegion = true;
5099596a2c1Sopenharmony_ci    }
5109596a2c1Sopenharmony_ci    if (compareRegion) {
5119596a2c1Sopenharmony_ci        uint16_t currentRegionEncode = LocaleUtil::EncodeRegionByLocaleInfo(current);
5129596a2c1Sopenharmony_ci        uint16_t otherRegionEncode = LocaleUtil::EncodeRegionByLocaleInfo(other);
5139596a2c1Sopenharmony_ci        return (otherRegionEncode == LocaleMatcher::NULL_REGION) || (currentRegionEncode == otherRegionEncode);
5149596a2c1Sopenharmony_ci    }
5159596a2c1Sopenharmony_ci    return currentEncodedScript == otherEncodedScript;
5169596a2c1Sopenharmony_ci}
5179596a2c1Sopenharmony_ci
5189596a2c1Sopenharmony_ciuint32_t LocaleMatcher::FindDefaultScriptEncode(const char *language, const char *region)
5199596a2c1Sopenharmony_ci{
5209596a2c1Sopenharmony_ci    uint64_t encodedLocale = LocaleUtil::EncodeLocale(language, nullptr, region);
5219596a2c1Sopenharmony_ci    for (size_t i = 0; i < LocaleData::LIKELY_TAGS_CODES_KEY.size(); i++) {
5229596a2c1Sopenharmony_ci        if (LocaleData::LIKELY_TAGS_CODES_KEY[i] == encodedLocale) {
5239596a2c1Sopenharmony_ci            // 16 is the offset of script
5249596a2c1Sopenharmony_ci            return static_cast<uint32_t>((LocaleData::LIKELY_TAGS_CODES_VALUE[i] & 0x0000ffffffff0000) >> 16);
5259596a2c1Sopenharmony_ci        }
5269596a2c1Sopenharmony_ci    }
5279596a2c1Sopenharmony_ci    if (region != nullptr) {
5289596a2c1Sopenharmony_ci        encodedLocale = LocaleUtil::EncodeLocale(language, nullptr, nullptr);
5299596a2c1Sopenharmony_ci        for (size_t i = 0; i < LocaleData::LIKELY_TAGS_CODES_KEY.size(); i++) {
5309596a2c1Sopenharmony_ci            if (LocaleData::LIKELY_TAGS_CODES_KEY[i] == encodedLocale) {
5319596a2c1Sopenharmony_ci                // 16 is the offset of script
5329596a2c1Sopenharmony_ci                return static_cast<uint32_t>((LocaleData::LIKELY_TAGS_CODES_VALUE[i] & 0x0000ffffffff0000) >> 16);
5339596a2c1Sopenharmony_ci            }
5349596a2c1Sopenharmony_ci        }
5359596a2c1Sopenharmony_ci    }
5369596a2c1Sopenharmony_ci    return LocaleMatcher::NULL_SCRIPT;
5379596a2c1Sopenharmony_ci}
5389596a2c1Sopenharmony_ci} // namespace I18n
5399596a2c1Sopenharmony_ci} // namespace Global
5409596a2c1Sopenharmony_ci} // namespace OHOS