1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "locale_data.h"
17 #include "locale_matcher.h"
18 #include "locale_util.h"
19 
20 namespace OHOS {
21 namespace Global {
22 namespace I18n {
23 uint64_t LocaleMatcher::EN_GB_ENCODE = LocaleUtil::EncodeLocale("en", nullptr, "GB");
24 uint64_t LocaleMatcher::EN_QAAG_ENCODE = LocaleUtil::EncodeLocale("en", "Qaag", nullptr);
25 uint64_t LocaleMatcher::ZH_HANT_MO_ENCODE = LocaleUtil::EncodeLocale("zh", "Hant", "MO");
26 uint64_t LocaleMatcher::ZH_HK_ENCODE = LocaleUtil::EncodeLocale("zh", nullptr, "HK");
27 uint32_t LocaleMatcher::HANT_ENCODE = LocaleUtil::EncodeScript("Hant");
28 
GetBestMatchedLocale(const LocaleInfo * requestLocale,const std::vector<LocaleInfo * > & candidateLocales)29 std::string LocaleMatcher::GetBestMatchedLocale(const LocaleInfo* requestLocale,
30     const std::vector<LocaleInfo*>& candidateLocales)
31 {
32     if (candidateLocales.size() == 0) {
33         return "";
34     }
35     LocaleInfo* bestMatch = candidateLocales[0];
36     for (size_t i = 1; i < candidateLocales.size(); ++i) {
37         if (IsMoreSuitable(bestMatch, candidateLocales[i], requestLocale) < 0) {
38             bestMatch = candidateLocales[i];
39         }
40     }
41     return bestMatch->ToString();
42 }
43 
IsMoreSuitable(const LocaleInfo * current,const LocaleInfo * other,const LocaleInfo * request)44 int8_t LocaleMatcher::IsMoreSuitable(const LocaleInfo *current, const LocaleInfo *other, const LocaleInfo *request)
45 {
46     if (request == nullptr) {
47         // if request ResLocale is nullptr, the candidate is also nullptr will be more suitable
48         if (current != nullptr && other == nullptr) {
49         // -1 means other is more suitable
50             return -1;
51         }
52         if (current == nullptr && other != nullptr) {
53             // 1 means current is more suitable
54             return 1;
55         }
56         return 0;
57     }
58     if (current == nullptr && other == nullptr) {
59         return 0;
60     }
61     bool isLangEqual = CompareLanguage(current, other);
62     if (!isLangEqual) {
63         // current or other language is null, not null language is better
64         bool result = CompareRegionWhenLangIsNotEqual(current, other, request);
65         return result ? 1 : -1;
66     }
67     uint16_t currentEncodedRegion =
68         LocaleUtil::EncodeRegionByLocaleInfo(current);
69     uint16_t otherEncodedRegion =
70         LocaleUtil::EncodeRegionByLocaleInfo(other);
71     if (currentEncodedRegion == otherEncodedRegion) {
72         // same language,same script,same region
73         return CompareLanguageIgnoreOldNewCode(current, other, request);
74     }
75     // equal request region is better
76     uint16_t requestEncodedRegion = LocaleUtil::EncodeRegionByLocaleInfo(request);
77     if (currentEncodedRegion == requestEncodedRegion) {
78         return 1;
79     }
80     if (otherEncodedRegion == requestEncodedRegion) {
81         return -1;
82     }
83     int8_t isRegionEqual = CompareRegion(current, other, request);
84     if (isRegionEqual == 0) {
85         return CompareLanguageIgnoreOldNewCode(current, other, request);
86     }
87     return isRegionEqual;
88 }
89 
CompareLanguage(const LocaleInfo * current,const LocaleInfo * other)90 bool LocaleMatcher::CompareLanguage(const LocaleInfo *current, const LocaleInfo *other)
91 {
92     uint16_t currentEncodedLanguage = LocaleUtil::EncodeLanguageByLocaleInfo(current);
93     uint16_t otherEncodedLanguage = LocaleUtil::EncodeLanguageByLocaleInfo(other);
94     // 1, 2, 3, 4 is the index.
95     return ((currentEncodedLanguage == otherEncodedLanguage) ||
96         ((currentEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[0]) &&
97         (otherEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[0])) ||
98         ((otherEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[0]) &&
99         (currentEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[0])) ||
100         ((currentEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[1]) &&
101         (otherEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[1])) ||
102         ((otherEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[1]) &&
103         (currentEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[1])) ||
104         ((currentEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[2]) && // 2 is index
105         (otherEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[2])) || // 2 is index
106         ((otherEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[2]) && // 2 is index
107         (currentEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[2])) || // 2 is index
108         ((currentEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[3]) && // 3 is index
109         (otherEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[3])) || // 3 is index
110         ((otherEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[3]) && // 3 is index
111         (currentEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[3])) || // 3 is index
112         ((currentEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[4]) && // 4 is index
113         (otherEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[4])) || // 4 is index
114         ((otherEncodedLanguage == LocaleData::NEW_LANGUAGES_CODES[4]) && // 4 is index
115         (currentEncodedLanguage == LocaleData::OLD_LANGUAGES_CODES[4]))); // 4 is index
116 }
117 
CompareRegionWhenLangIsNotEqual(const LocaleInfo * current,const LocaleInfo * other,const LocaleInfo * request)118 bool LocaleMatcher::CompareRegionWhenLangIsNotEqual(const LocaleInfo *current, const LocaleInfo *other,
119     const LocaleInfo *request)
120 {
121     int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
122     if (qaagResult != 0) {
123         return qaagResult;
124     }
125     if (request != nullptr &&
126         (LocaleUtil::EncodeLanguage(request->GetLanguage().c_str())) == LocaleUtil::EncodeLanguage("en")) {
127         // when request is en-us,empty region is better
128         if ((LocaleUtil::EncodeRegion(request->GetRegion().c_str())) == LocaleUtil::EncodeRegion("US")) {
129             if (current != nullptr) {
130                 return (current->GetRegion().length() == 0) ||
131                     ((LocaleUtil::EncodeRegion(current->GetRegion().c_str())) == LocaleUtil::EncodeRegion("US"));
132             } else {
133                 return !(other->GetRegion().length() == 0 ||
134                     ((LocaleUtil::EncodeRegion(other->GetRegion().c_str())) == LocaleUtil::EncodeRegion("US")));
135             }
136         } else if (IsSimilarToUsEnglish(request)) {
137             if (current != nullptr) {
138                 return IsSimilarToUsEnglish(current);
139             } else {
140                 return !IsSimilarToUsEnglish(other);
141             }
142         }
143     }
144     return current != nullptr;
145 }
146 
CompareRegionWhenQaag(const LocaleInfo * current,const LocaleInfo * other,const LocaleInfo * request)147 int8_t LocaleMatcher::CompareRegionWhenQaag(const LocaleInfo *current, const LocaleInfo *other,
148     const LocaleInfo *request)
149 {
150     if ((request != nullptr) && (LocaleUtil::EncodeLocale(request->GetLanguage().c_str(), request->GetScript().c_str(),
151         nullptr) == LocaleMatcher::EN_QAAG_ENCODE)) {
152         if ((current != nullptr) && (LocaleUtil::EncodeLocale(current->GetLanguage().c_str(), nullptr,
153             current->GetRegion().c_str()) == LocaleMatcher::EN_GB_ENCODE)) {
154             return 1;
155         }
156         if ((other != nullptr) && (LocaleUtil::EncodeLocale(other->GetLanguage().c_str(), nullptr,
157             other->GetRegion().c_str()) == LocaleMatcher::EN_GB_ENCODE)) {
158             return -1;
159         }
160     }
161     return 0;
162 }
163 
IsSimilarToUsEnglish(const LocaleInfo * localeInfo)164 bool LocaleMatcher::IsSimilarToUsEnglish(const LocaleInfo *localeInfo)
165 {
166     uint64_t localeEncode = LocaleUtil::EncodeLocale("en", nullptr,
167         (localeInfo == nullptr) ? nullptr : localeInfo->GetRegion().c_str());
168     uint64_t loclaeEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
169     FindTrackPath(nullptr, LocaleMatcher::TRACKPATH_ARRAY_SIZE, localeEncode, loclaeEncodedTrackPath);
170     uint8_t len = LocaleMatcher::TRACKPATH_ARRAY_SIZE;
171     for (uint8_t i = 0; i < len; ++i) {
172         if (loclaeEncodedTrackPath[i] == LocaleUtil::EncodeLocale("en", nullptr, nullptr)) {
173             return true;
174         }
175         if (loclaeEncodedTrackPath[i] == LocaleUtil::EncodeLocale("en", nullptr, "001")) {
176             return false;
177         }
178     }
179     return false;
180 }
181 
FindTrackPath(const LocaleInfo * request,size_t len,uint64_t encodedLocale,uint64_t * result)182 void LocaleMatcher::FindTrackPath(const LocaleInfo *request, size_t len, uint64_t encodedLocale, uint64_t *result)
183 {
184     uint64_t currentEncodedLocale = encodedLocale;
185     size_t i = 0;
186     do {
187         result[i] = currentEncodedLocale;
188         currentEncodedLocale = SearchParentLocale(currentEncodedLocale, request);
189         ++i;
190     } while (currentEncodedLocale != LocaleMatcher::ROOT_LOCALE);
191     if (i < len) {
192         result[i] = LocaleMatcher::ROOT_LOCALE;
193     }
194 }
195 
SearchParentLocale(uint64_t encodedLocale,const LocaleInfo * request)196 uint64_t LocaleMatcher::SearchParentLocale(uint64_t encodedLocale, const LocaleInfo *request)
197 {
198     uint64_t tempEncodedLocale = encodedLocale;
199     if (LocaleUtil::EncodeScriptByLocaleInfo(request) == LocaleMatcher::HANT_ENCODE) {
200         tempEncodedLocale = AddScript(encodedLocale, LocaleMatcher::HANT_ENCODE);
201         if (tempEncodedLocale == LocaleMatcher::ZH_HANT_MO_ENCODE) {
202             return LocaleMatcher::ZH_HK_ENCODE;
203         }
204     }
205     if (IsContainRegion(encodedLocale)) {
206         for (size_t i = 0; i < LocaleData::LOCALE_PARENTS_KEY.size(); i++) {
207             if (LocaleData::LOCALE_PARENTS_KEY[i] == tempEncodedLocale) {
208                 return LocaleData::LOCALE_PARENTS_VALUE[i];
209             }
210         }
211         return ClearRegion(encodedLocale);
212     }
213     return LocaleMatcher::ROOT_LOCALE;
214 }
215 
AddScript(uint64_t encodedLocale,uint32_t encodedScript)216 uint64_t LocaleMatcher::AddScript(uint64_t encodedLocale, uint32_t encodedScript)
217 {
218     // 16 is the offset of script
219     return (encodedLocale | ((static_cast<uint64_t>(encodedScript) & 0x00000000FFFFFFFFLU) << 16));
220 }
221 
IsContainRegion(uint64_t encodedLocale)222 bool LocaleMatcher::IsContainRegion(uint64_t encodedLocale)
223 {
224     return (encodedLocale & 0x000000000000FFFFLU) != 0;
225 }
226 
ClearRegion(uint64_t encodedLocale)227 uint64_t LocaleMatcher::ClearRegion(uint64_t encodedLocale)
228 {
229     return encodedLocale & 0xFFFFFFFFFFFF0000LU;
230 }
231 
CompareRegion(const LocaleInfo * current,const LocaleInfo * other,const LocaleInfo * request)232 int8_t LocaleMatcher::CompareRegion(const LocaleInfo *current, const LocaleInfo *other, const LocaleInfo *request)
233 {
234     uint16_t currentEncodedRegion = LocaleUtil::EncodeRegionByLocaleInfo(current);
235     uint16_t otherEncodedRegion = LocaleUtil::EncodeRegionByLocaleInfo(other);
236     if (request == nullptr || request->GetRegion().size() == 0) {
237         return CompareWhenRegionIsNull(currentEncodedRegion, otherEncodedRegion, current, other, request);
238     }
239     uint64_t requestEncodedLocale = LocaleUtil::EncodeLocale(request->GetLanguage().c_str(), nullptr,
240         request->GetRegion().c_str());
241     uint64_t requestEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
242     FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, requestEncodedLocale, requestEncodedTrackPath);
243     uint64_t currentEncodedLocale = LocaleUtil::EncodeLocale(request->GetLanguage().c_str(), nullptr,
244         (current == nullptr) ? nullptr : current->GetRegion().c_str());
245     uint64_t otherEncodedLocale = LocaleUtil::EncodeLocale(request->GetLanguage().c_str(), nullptr,
246         (other == nullptr) ? nullptr : other->GetRegion().c_str());
247     int8_t currentMatchDistance = SearchTrackPathDistance(requestEncodedTrackPath, LocaleMatcher::TRACKPATH_ARRAY_SIZE,
248         currentEncodedLocale);
249     int8_t otherMatchDistance = SearchTrackPathDistance(requestEncodedTrackPath, LocaleMatcher::TRACKPATH_ARRAY_SIZE,
250         otherEncodedLocale);
251     if (currentMatchDistance < otherMatchDistance) {
252         return 1;
253     }
254     if (currentMatchDistance > otherMatchDistance) {
255         return -1;
256     }
257     int8_t result = CompareDistance(currentEncodedLocale, otherEncodedLocale, requestEncodedTrackPath, request);
258     if (result != 0) {
259         return result;
260     }
261     result = CompareDefaultRegion(current, other, request);
262     if (result != 0) {
263         return result;
264     }
265     uint16_t requestDefaultRegion =
266         FindDefaultRegionEncode(request->GetLanguage().c_str(), request->GetScript().c_str());
267     if (requestDefaultRegion == currentEncodedRegion) {
268         return 1;
269     }
270     if (requestDefaultRegion == otherEncodedRegion) {
271         return -1;
272     }
273     return AlphabeticallyCompare(current, currentEncodedLocale, other, otherEncodedLocale);
274 }
275 
CompareWhenRegionIsNull(uint16_t currentEncodedRegion,uint16_t otherEncodedRegion,const LocaleInfo * current,const LocaleInfo * other,const LocaleInfo * request)276 int8_t LocaleMatcher::CompareWhenRegionIsNull(uint16_t currentEncodedRegion, uint16_t otherEncodedRegion,
277     const LocaleInfo *current, const LocaleInfo *other, const LocaleInfo *request)
278 {
279     if (current == nullptr || current->GetRegion().length() == 0) {
280         return 1;
281     }
282     if (other == nullptr || other->GetRegion().length() == 0) {
283         return -1;
284     }
285     int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
286     if (qaagResult != 0) {
287         return qaagResult;
288     }
289     // get request default region
290     uint16_t requestDefaultRegion =
291         FindDefaultRegionEncode((request == nullptr) ? nullptr : request->GetLanguage().c_str(),
292             (request == nullptr) ? nullptr : request->GetScript().c_str());
293     if (requestDefaultRegion == currentEncodedRegion) {
294         return 1;
295     }
296     if (requestDefaultRegion == otherEncodedRegion) {
297         return -1;
298     }
299     // current and other region is not null.alphabetically
300     uint64_t currentEncodedLocale = LocaleUtil::EncodeLocale(
301         (request == nullptr) ? nullptr : request->GetLanguage().c_str(), nullptr,
302         (current == nullptr) ? nullptr : current->GetRegion().c_str());
303     uint64_t otherEncodedLocale = LocaleUtil::EncodeLocale(
304         (request == nullptr) ? nullptr : request->GetLanguage().c_str(), nullptr, other->GetRegion().c_str());
305     return AlphabeticallyCompare(current, currentEncodedLocale, other, otherEncodedLocale);
306 }
307 
FindDefaultRegionEncode(const char * language,const char * script)308 uint16_t LocaleMatcher::FindDefaultRegionEncode(const char *language, const char *script)
309 {
310     /* first try language and script */
311     uint64_t encodedLocale = LocaleUtil::EncodeLocale(language, script, nullptr);
312     if (encodedLocale == LocaleUtil::EncodeLocale("en", "Qaag", nullptr)) {
313         encodedLocale = LocaleUtil::EncodeLocale("en", "Latn", nullptr);
314     }
315     for (size_t i = 0; i < LocaleData::LIKELY_TAGS_CODES_KEY.size(); i++) {
316         if (LocaleData::LIKELY_TAGS_CODES_KEY[i] == encodedLocale) {
317             return static_cast<uint16_t>((LocaleData::LIKELY_TAGS_CODES_VALUE[i] & 0x000000000000ffff));
318         }
319     }
320     /* if not found and script is not null,try language */
321     if (script != nullptr) {
322         encodedLocale = LocaleUtil::EncodeLocale(language, nullptr, nullptr);
323         for (size_t i = 0; i < LocaleData::LIKELY_TAGS_CODES_KEY.size(); i++) {
324             if (LocaleData::LIKELY_TAGS_CODES_KEY[i] == encodedLocale) {
325                 return static_cast<uint16_t>((LocaleData::LIKELY_TAGS_CODES_VALUE[i] & 0x000000000000ffff));
326             }
327         }
328     }
329     return LocaleMatcher::NULL_REGION;
330 }
331 
AlphabeticallyCompare(const LocaleInfo * current,uint64_t currentEncodedLocale,const LocaleInfo * other,uint64_t otherEncodedLocale)332 int8_t LocaleMatcher::AlphabeticallyCompare(const LocaleInfo *current, uint64_t currentEncodedLocale,
333     const LocaleInfo *other, uint64_t otherEncodedLocale)
334 {
335     if (currentEncodedLocale == otherEncodedLocale) {
336         return 0;
337     }
338     if (current == nullptr || current->GetRegion().length() == 0) {
339         return -1;
340     }
341     if (other == nullptr || other->GetRegion().length() == 0) {
342         return 1;
343     }
344     // be here region is not null
345     char currentFirstChar = (current->GetRegion())[0];
346     char otherFirstChar = (other->GetRegion())[0];
347     if (currentFirstChar >= '0' && currentFirstChar <= '9') {
348         if (otherFirstChar < '0' || otherFirstChar > '9') {
349             return -1;
350         }
351     } else {
352         if (otherFirstChar >= '0' && otherFirstChar <= '9') {
353             return 1;
354         }
355     }
356     if (currentEncodedLocale > otherEncodedLocale) {
357         return -1;
358     }
359     if (otherEncodedLocale > currentEncodedLocale) {
360         return 1;
361     }
362     return 0;
363 }
364 
SearchTrackPathDistance(const uint64_t * paths,size_t len,uint64_t encodedLocale)365 int8_t LocaleMatcher::SearchTrackPathDistance(const uint64_t *paths, size_t len, uint64_t encodedLocale)
366 {
367     size_t i = 0;
368     for (i = 0; i < len; ++i) {
369         if (paths[i] == LocaleMatcher::ROOT_LOCALE) {
370             return i;
371         }
372         if (paths[i] == encodedLocale) {
373             return i;
374         }
375     }
376     return static_cast<int8_t>(i);
377 }
378 
CompareDistance(uint64_t currentEncodedLocale,uint64_t otherEncodedLocale,const uint64_t * requestEncodedTrackPath,const LocaleInfo * request)379 int8_t LocaleMatcher::CompareDistance(uint64_t currentEncodedLocale, uint64_t otherEncodedLocale,
380     const uint64_t *requestEncodedTrackPath, const LocaleInfo *request)
381 {
382     uint64_t currentEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
383     FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, currentEncodedLocale, currentEncodedTrackPath);
384     uint64_t otherEncodedTrackPath[LocaleMatcher::TRACKPATH_ARRAY_SIZE] = {0, 0, 0, 0, 0};
385     FindTrackPath(request, LocaleMatcher::TRACKPATH_ARRAY_SIZE, otherEncodedLocale, otherEncodedTrackPath);
386     const size_t currentDistance = ComputeTrackPathDistance(requestEncodedTrackPath, currentEncodedTrackPath,
387         LocaleMatcher::TRACKPATH_ARRAY_SIZE);
388     const size_t targetDistance = ComputeTrackPathDistance(requestEncodedTrackPath, otherEncodedTrackPath,
389         LocaleMatcher::TRACKPATH_ARRAY_SIZE);
390     if (currentDistance < targetDistance) {
391         return 1;
392     }
393     if (currentDistance > targetDistance) {
394         return -1;
395     }
396     return 0;
397 }
398 
ComputeTrackPathDistance(const uint64_t * requestPaths,const uint64_t * targetPaths,size_t len)399 size_t LocaleMatcher::ComputeTrackPathDistance(const uint64_t *requestPaths, const uint64_t *targetPaths, size_t len)
400 {
401     for (size_t i = 0; i < len; ++i) {
402         if (targetPaths[i] == LocaleMatcher::ROOT_LOCALE) {
403             // targetpath not in request path,so distance is 2*len
404             return len * 2;
405         }
406         for (size_t j = 0; j < len; ++j) {
407             if (requestPaths[j] == targetPaths[i]) {
408                 return i + j;
409             }
410         }
411     }
412     return len * 2; // targetpath not in request path,so distance is 2*len
413 }
414 
CompareDefaultRegion(const LocaleInfo * current,const LocaleInfo * other,const LocaleInfo * request)415 int8_t LocaleMatcher::CompareDefaultRegion(const LocaleInfo *current, const LocaleInfo *other,
416     const LocaleInfo *request)
417 {
418     int8_t qaagResult = CompareRegionWhenQaag(current, other, request);
419     if (qaagResult != 0) {
420         return qaagResult;
421     } else {
422         bool isCurrentDefaultRegion = IsDefaultLocale((request == nullptr) ? nullptr : request->GetLanguage().c_str(),
423             (request == nullptr) ? nullptr : request->GetScript().c_str(),
424             (current == nullptr) ? nullptr : current->GetRegion().c_str());
425         bool isOtherDefaultRegion = IsDefaultLocale((request == nullptr) ? nullptr : request->GetLanguage().c_str(),
426             (request == nullptr) ? nullptr : request->GetScript().c_str(),
427             (other == nullptr) ? nullptr : other->GetRegion().c_str());
428         if (isCurrentDefaultRegion != isOtherDefaultRegion) {
429             if (isCurrentDefaultRegion) {
430                 return 1;
431             } else {
432                 return -1;
433             }
434         }
435     }
436     return 0;
437 }
438 
IsDefaultLocale(const char * language,const char * script,const char * region)439 bool LocaleMatcher::IsDefaultLocale(const char *language, const char *script, const char *region)
440 {
441     uint64_t encodedLocale = LocaleUtil::EncodeLocale(language, script, region);
442     if (ClearRegion(encodedLocale) == LocaleMatcher::EN_QAAG_ENCODE) {
443         encodedLocale = LocaleUtil::EncodeLocale("en", "Latn", region);
444     }
445     for (size_t i = 0; i < LocaleData::TYPICAL_CODES_VALUE.size(); i++) {
446         if (LocaleData::TYPICAL_CODES_VALUE[i] == encodedLocale) {
447             return true;
448         }
449     }
450     return false;
451 }
452 
CompareLanguageIgnoreOldNewCode(const LocaleInfo * current,const LocaleInfo * other,const LocaleInfo * request)453 int8_t LocaleMatcher::CompareLanguageIgnoreOldNewCode(const LocaleInfo *current, const LocaleInfo *other,
454     const LocaleInfo *request)
455 {
456     uint16_t currentLanguageEncode = LocaleUtil::EncodeLanguageByLocaleInfo(current);
457     uint16_t otherLanguageEncode = LocaleUtil::EncodeLanguageByLocaleInfo(other);
458     uint16_t requestLanguageEncode = LocaleUtil::EncodeLanguageByLocaleInfo(request);
459     if ((currentLanguageEncode == requestLanguageEncode) && (otherLanguageEncode != requestLanguageEncode)) {
460         return 1;
461     }
462     if ((otherLanguageEncode == requestLanguageEncode) && (currentLanguageEncode != requestLanguageEncode)) {
463         return -1;
464     }
465     return 0;
466 }
467 
Match(const LocaleInfo * current,const LocaleInfo * other)468 bool LocaleMatcher::Match(const LocaleInfo *current, const LocaleInfo *other)
469 {
470     if (current == nullptr || other == nullptr) {
471         return true;
472     }
473     // language is not null.
474     bool isLanguageEqual = CompareLanguage(current, other);
475     if (!isLanguageEqual) {
476         return false;
477     }
478     return CompareScript(current, other);
479 }
480 
CompareScript(const LocaleInfo * current,const LocaleInfo * other)481 bool LocaleMatcher::CompareScript(const LocaleInfo *current, const LocaleInfo *other)
482 {
483     uint32_t currentEncodedScript = 0;
484     uint32_t otherEncodedScript = 0;
485     if ((current != nullptr) && (current->GetScript().length() == 0)) {
486         currentEncodedScript = FindDefaultScriptEncode(current->GetLanguage().c_str(), current->GetRegion().c_str());
487     } else {
488         currentEncodedScript = LocaleUtil::EncodeScriptByLocaleInfo(current);
489     }
490     if ((other != nullptr) && (other->GetScript().length() == 0)) {
491         otherEncodedScript = FindDefaultScriptEncode(other->GetLanguage().c_str(), other->GetRegion().c_str());
492     } else {
493         otherEncodedScript = LocaleUtil::EncodeScriptByLocaleInfo(other);
494     }
495     if (current != nullptr && other != nullptr) {
496         // when current locale is en-Qaag is equal en-Latn
497         if (LocaleUtil::EncodeLocale(current->GetLanguage().c_str(), current->GetScript().c_str(), nullptr) ==
498             LocaleUtil::EncodeLocale("en", "Qaag", nullptr)) {
499             if (LocaleUtil::EncodeLocale(other->GetLanguage().c_str(), other->GetScript().c_str(), nullptr) ==
500                 LocaleUtil::EncodeLocale("en", "Latn", nullptr)) {
501                 return true;
502             }
503         }
504     }
505     bool compareRegion = false;
506     if ((currentEncodedScript == LocaleMatcher::NULL_SCRIPT) || (otherEncodedScript == LocaleMatcher::NULL_SCRIPT)) {
507         // if request script is null, region must be same
508         compareRegion = true;
509     }
510     if (compareRegion) {
511         uint16_t currentRegionEncode = LocaleUtil::EncodeRegionByLocaleInfo(current);
512         uint16_t otherRegionEncode = LocaleUtil::EncodeRegionByLocaleInfo(other);
513         return (otherRegionEncode == LocaleMatcher::NULL_REGION) || (currentRegionEncode == otherRegionEncode);
514     }
515     return currentEncodedScript == otherEncodedScript;
516 }
517 
FindDefaultScriptEncode(const char * language,const char * region)518 uint32_t LocaleMatcher::FindDefaultScriptEncode(const char *language, const char *region)
519 {
520     uint64_t encodedLocale = LocaleUtil::EncodeLocale(language, nullptr, region);
521     for (size_t i = 0; i < LocaleData::LIKELY_TAGS_CODES_KEY.size(); i++) {
522         if (LocaleData::LIKELY_TAGS_CODES_KEY[i] == encodedLocale) {
523             // 16 is the offset of script
524             return static_cast<uint32_t>((LocaleData::LIKELY_TAGS_CODES_VALUE[i] & 0x0000ffffffff0000) >> 16);
525         }
526     }
527     if (region != nullptr) {
528         encodedLocale = LocaleUtil::EncodeLocale(language, nullptr, nullptr);
529         for (size_t i = 0; i < LocaleData::LIKELY_TAGS_CODES_KEY.size(); i++) {
530             if (LocaleData::LIKELY_TAGS_CODES_KEY[i] == encodedLocale) {
531                 // 16 is the offset of script
532                 return static_cast<uint32_t>((LocaleData::LIKELY_TAGS_CODES_VALUE[i] & 0x0000ffffffff0000) >> 16);
533             }
534         }
535     }
536     return LocaleMatcher::NULL_SCRIPT;
537 }
538 } // namespace I18n
539 } // namespace Global
540 } // namespace OHOS