1 /*
2 * Copyright (c) 2023 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 "hilog/log.h"
17 #include "locale_compare.h"
18 #include "ohos/init_data.h"
19 #include "unicode/locid.h"
20
21 namespace OHOS {
22 namespace Global {
23 namespace I18n {
24 static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, 0xD001E00, "LocaleCompare" };
25 using namespace OHOS::HiviewDFX;
26 std::string LocaleCompare::hantSegment = "-Hant-";
27 std::string LocaleCompare::latnSegment = "-Latn-";
28 std::string LocaleCompare::qaagSegment = "-Qaag-";
29 std::set<std::string> LocaleCompare::scriptLocales {
30 "zh", "en", "es", "pt"
31 };
32 std::map<std::string, std::string> LocaleCompare::hantParent {
33 { "zh-MO", "zh-Hant-HK" }
34 };
35 std::map<std::string, std::string> LocaleCompare::latnParent {
36 { "en-150", "en-001" },
37 { "en-AG", "en-001" },
38 { "en-AI", "en-001" },
39 { "en-AU", "en-001" },
40 { "en-BB", "en-001" },
41 { "en-BE", "en-001" },
42 { "en-BM", "en-001" },
43 { "en-BS", "en-001" },
44 { "en-BZ", "en-001" },
45 { "en-CC", "en-001" },
46 { "en-CK", "en-001" },
47 { "en-CX", "en-001" },
48 { "en-DG", "en-001" },
49 { "en-ER", "en-001" },
50 { "en-FK", "en-001" },
51 { "en-FM", "en-001" },
52 { "en-GB", "en-001" },
53 { "en-GD", "en-001" },
54 { "en-GG", "en-001" },
55 { "en-GI", "en-001" },
56 { "en-GY", "en-001" },
57 { "en-HK", "en-001" },
58 { "en-IE", "en-001" },
59 { "en-IM", "en-001" },
60 { "en-IN", "en-001" },
61 { "en-IO", "en-001" },
62 { "en-JE", "en-001" },
63 { "en-KI", "en-001" },
64 { "en-KN", "en-001" },
65 { "en-KY", "en-001" },
66 { "en-LC", "en-001" },
67 { "en-LR", "en-001" },
68 { "en-LS", "en-001" },
69 { "en-MM", "en-001" },
70 { "en-MO", "en-001" },
71 { "en-MS", "en-001" },
72 { "en-MT", "en-001" },
73 { "en-MY", "en-001" },
74 { "en-NF", "en-001" },
75 { "en-NR", "en-001" },
76 { "en-NU", "en-001" },
77 { "en-NZ", "en-001" },
78 { "en-PG", "en-001" },
79 { "en-PK", "en-001" },
80 { "en-PN", "en-001" },
81 { "en-PW", "en-001" },
82 { "en-SB", "en-001" },
83 { "en-SC", "en-001" },
84 { "en-SD", "en-001" },
85 { "en-SG", "en-001" },
86 { "en-SH", "en-001" },
87 { "en-SL", "en-001" },
88 { "en-SS", "en-001" },
89 { "en-SX", "en-001" },
90 { "en-SZ", "en-001" },
91 { "en-TC", "en-001" },
92 { "en-TK", "en-001" },
93 { "en-TT", "en-001" },
94 { "en-TV", "en-001" },
95 { "en-VC", "en-001" },
96 { "en-VG", "en-001" },
97 { "en-WS", "en-001" },
98 { "en-ZG", "en-001" },
99 { "es-AR", "es-419" },
100 { "es-BO", "es-419" },
101 { "es-BR", "es-419" },
102 { "es-CL", "es-419" },
103 { "es-CO", "es-419" },
104 { "es-CR", "es-419" },
105 { "es-CU", "es-419" },
106 { "es-DO", "es-419" },
107 { "es-EC", "es-419" },
108 { "es-GT", "es-419" },
109 { "es-HN", "es-419" },
110 { "es-MX", "es-419" },
111 { "es-NI", "es-419" },
112 { "es-PA", "es-419" },
113 { "es-PE", "es-419" },
114 { "es-PR", "es-419" },
115 { "es-PY", "es-419" },
116 { "es-SV", "es-419" },
117 { "es-US", "es-419" },
118 { "es-UY", "es-419" },
119 { "es-VE", "es-419" },
120 { "pt-AO", "pt-PT" },
121 { "pt-CH", "pt-PT" },
122 { "pt-CV", "pt-PT" },
123 { "pt-GQ", "pt-PT" },
124 { "pt-GW", "pt-PT" },
125 { "pt-LU", "pt-PT" },
126 { "pt-MO", "pt-PT" },
127 { "pt-MZ", "pt-PT" },
128 { "pt-ST", "pt-PT" },
129 { "pt-TL", "pt-PT" }
130 };
131 std::map<std::string, std::string> LocaleCompare::extendedHantParent {};
132 std::map<std::string, std::string> LocaleCompare::extendedLatnParent {};
133
Compare(const std::string & localeTag1,const std::string & localeTag2)134 int32_t LocaleCompare::Compare(const std::string& localeTag1, const std::string& localeTag2)
135 {
136 UErrorCode status = U_ZERO_ERROR;
137 UErrorCode status2 = U_ZERO_ERROR;
138 icu::Locale locale1 = icu::Locale::forLanguageTag(icu::StringPiece(localeTag1), status);
139 icu::Locale locale2 = icu::Locale::forLanguageTag(icu::StringPiece(localeTag2), status2);
140 if (U_FAILURE(status) || U_FAILURE(status2)) {
141 HiLog::Error(LABEL, "localeTag1: %{public}s or localeTag2: %{public}s is invalid.",
142 localeTag1.c_str(), localeTag2.c_str());
143 return -1;
144 }
145 int32_t segmentScore = 3;
146 const int32_t mapScore = 8;
147 int32_t score = 0;
148 std::string language1 = locale1.getLanguage();
149 std::string language2 = locale2.getLanguage();
150 if (IsSameLanguage(language1, language2)) {
151 score += segmentScore;
152 } else {
153 return -1;
154 }
155 const char* baseName1 = locale1.getBaseName();
156 const char* baseName2 = locale2.getBaseName();
157 if (IsBaseNameRelation(baseName1, baseName2, language1)) {
158 return mapScore;
159 }
160 std::string region1 = locale1.getCountry();
161 std::string region2 = locale2.getCountry();
162 locale1.addLikelySubtags(status);
163 locale2.addLikelySubtags(status);
164 if (U_FAILURE(status)) {
165 HiLog::Error(LABEL, "LocaleCompare::Compare add likely subtags failed.");
166 return -1;
167 }
168 std::string script1 = locale1.getScript();
169 std::string script2 = locale2.getScript();
170 if (script1.compare(script2) == 0 || (language1.compare("en") == 0 && IsSameEnglishScript(script1, script2))) {
171 score += segmentScore;
172 if (region2.length() == 0) {
173 ++score;
174 }
175 } else {
176 return -1;
177 }
178 if (region1.length() != 0 && region1.compare(region2) == 0) {
179 score += segmentScore;
180 }
181 return score;
182 }
183
IsSameLanguage(const std::string & langTag1,const std::string & langTag2)184 bool LocaleCompare::IsSameLanguage(const std::string& langTag1, const std::string& langTag2)
185 {
186 if (langTag1.compare(langTag2) == 0) {
187 return true;
188 }
189 if (langTag1.compare("tl") == 0 && langTag2.compare("fil") == 0) {
190 return true;
191 }
192 if (langTag1.compare("fil") == 0 && langTag2.compare("tl") == 0) {
193 return true;
194 }
195 return false;
196 }
197
IsSameEnglishScript(const std::string & scriptTag1,const std::string & scriptTag2)198 bool LocaleCompare::IsSameEnglishScript(const std::string& scriptTag1, const std::string& scriptTag2)
199 {
200 if (scriptTag1.compare("Qaag") == 0 && scriptTag2.compare("Latn") == 0) {
201 return true;
202 }
203 if (scriptTag1.compare("Latn") == 0 && scriptTag2.compare("Qaag") == 0) {
204 return true;
205 }
206 return false;
207 }
208
HasMapRelation(const std::string & languageTag,const std::string & localeTag1,const std::string & localeTag2)209 bool LocaleCompare::HasMapRelation(const std::string& languageTag, const std::string& localeTag1,
210 const std::string& localeTag2)
211 {
212 if (scriptLocales.find(languageTag) == scriptLocales.end()) {
213 return false;
214 }
215 if (hantParent.find(localeTag1) != hantParent.end()) {
216 if (localeTag2.compare(hantParent[localeTag1]) == 0) {
217 return true;
218 }
219 }
220 if (latnParent.find(localeTag1) != latnParent.end()) {
221 if (localeTag2.compare(latnParent[localeTag1]) == 0) {
222 return true;
223 }
224 }
225 if (extendedHantParent.size() == 0) {
226 for (auto it = hantParent.begin(); it != hantParent.end(); ++it) {
227 std::string key = it->first;
228 size_t languageLength = key.find("-");
229 std::string language = key.substr(0, languageLength);
230 std::string region = key.substr(languageLength + 1);
231 extendedHantParent[language + hantSegment + region] = it->second;
232 }
233 }
234 if (extendedHantParent.find(localeTag1) != extendedHantParent.end()) {
235 if (localeTag2.compare(extendedHantParent[localeTag1]) == 0) {
236 return true;
237 }
238 }
239 if (extendedLatnParent.size() == 0) {
240 for (auto it = latnParent.begin(); it != latnParent.end(); ++it) {
241 std::string key = it->first;
242 size_t languageLength = key.find("-");
243 std::string language = key.substr(0, languageLength);
244 std::string region = key.substr(languageLength + 1);
245 extendedLatnParent[language + latnSegment + region] = it->second;
246 if (language.compare("en") == 0) {
247 extendedLatnParent[language + qaagSegment + region] = it->second;
248 }
249 }
250 }
251 if (extendedLatnParent.find(localeTag1) != extendedLatnParent.end()) {
252 if (localeTag2.compare(extendedLatnParent[localeTag1]) == 0) {
253 return true;
254 }
255 }
256 return false;
257 }
258
IsBaseNameRelation(const char * baseName1,const char * baseName2,const std::string & languageTag)259 bool LocaleCompare::IsBaseNameRelation(const char* baseName1, const char* baseName2,
260 const std::string& languageTag)
261 {
262 if (baseName1 != nullptr && baseName2 != nullptr) {
263 std::string localeBaseName1(baseName1);
264 std::string localeBaseName2(baseName2);
265 std::replace(localeBaseName1.begin(), localeBaseName1.end(), '_', '-');
266 std::replace(localeBaseName2.begin(), localeBaseName2.end(), '_', '-');
267 if (HasMapRelation(languageTag, localeBaseName1, localeBaseName2)) {
268 return true;
269 }
270 }
271 return false;
272 }
273 } // namespace I18n
274 } // namespace Global
275 } // OHOS
276