1 /*
2 * Copyright (c) 2025 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 "intl_locale.h"
17
18 #include "i18n_hilog.h"
19 #include "locale_helper.h"
20 #include "ohos/init_data.h"
21
22 namespace OHOS {
23 namespace Global {
24 namespace I18n {
25 const std::string APPLY_OPTION_ERROR_MESSAGE = "apply option to tag failed";
26 const std::string OUT_OF_RANGE_ERROR_MESSAGE = "Value out of range for locale options property";
27 const std::string INSERT_OR_BUILD_ERROR_MESSAGE = "insert or build failed";
28 const std::string INVALID_LOCALE_ERROR_MESSAGE = "invalid locale";
29 const std::string IntlLocale::languageTag = "language";
30 const std::string IntlLocale::baseNameTag = "baseName";
31 const std::string IntlLocale::regionTag = "region";
32 const std::string IntlLocale::scriptTag = "script";
33 const std::string IntlLocale::calendarTag = "calendar";
34 const std::string IntlLocale::collationTag = "collation";
35 const std::string IntlLocale::hourCycleTag = "hourCycle";
36 const std::string IntlLocale::numberingSystemTag = "numberingSystem";
37 const std::string IntlLocale::numericTag = "numeric";
38 const std::string IntlLocale::caseFirstTag = "caseFirst";
39 const std::string calendarExtTag = "ca";
40 const std::string collationExtTag = "co";
41 const std::string hourCycleExtTag = "hc";
42 const std::string numberingSystemExtTag = "nu";
43 const std::string numericExtTag = "kn";
44 const std::string caseFirstExtTag = "kf";
45 const std::unordered_set<std::string> HOUR_CYCLE = { "h11", "h12", "h23", "h24" };
46 const std::unordered_set<std::string> CASE_FIRST = { "upper", "lower", "false" };
47 constexpr uint8_t INTL_INDEX_FOUR = 4;
48
IntlLocale(const std::string & localeTag,const std::unordered_map<std::string,std::string> & configs,std::string & errMessage)49 IntlLocale::IntlLocale(const std::string& localeTag, const std::unordered_map<std::string, std::string>& configs,
50 std::string& errMessage)
51 {
52 if (!CheckLocaleParam(localeTag, configs)) {
53 errMessage = APPLY_OPTION_ERROR_MESSAGE;
54 HILOG_ERROR_I18N("IntlLocale::IntlLocale: ApplyOptionsToTag failed.");
55 return;
56 }
57 icu::LocaleBuilder builder;
58 if (!SetLocaleParam(localeTag, configs, &builder)) {
59 errMessage = APPLY_OPTION_ERROR_MESSAGE;
60 HILOG_ERROR_I18N("IntlLocale::IntlLocale: BuildOptionsTags failed.");
61 return;
62 }
63 if (!CheckConfigsExtParam(configs)) {
64 errMessage = OUT_OF_RANGE_ERROR_MESSAGE;
65 HILOG_ERROR_I18N("IntlLocale::IntlLocale: CheckOption failed.");
66 return;
67 }
68 if (!SetConfigsExtParam(configs, &builder)) {
69 errMessage = INSERT_OR_BUILD_ERROR_MESSAGE;
70 HILOG_ERROR_I18N("IntlLocale::IntlLocale: InsertOptions failed.");
71 return;
72 }
73 initSuccess = true;
74 }
75
~IntlLocale()76 IntlLocale::~IntlLocale()
77 {
78 }
79
CheckLocaleParam(const std::string & localeTag,const std::unordered_map<std::string,std::string> & configs)80 bool IntlLocale::CheckLocaleParam(const std::string& localeTag,
81 const std::unordered_map<std::string, std::string>& configs)
82 {
83 if (localeTag.empty()) {
84 HILOG_ERROR_I18N("IntlLocale::CheckLocaleParam: localeTag is empty.");
85 return false;
86 }
87 if (!LocaleHelper::IsStructurallyValidLanguageTag(localeTag)) {
88 HILOG_ERROR_I18N("IntlLocale::CheckLocaleParam: Verify structurally valid languageTag failed.");
89 return false;
90 }
91 auto languageIterator = configs.find(languageTag);
92 if (languageIterator != configs.end()) {
93 std::string language = languageIterator->second;
94 if (language.empty() || LocaleHelper::IsAlpha(language, INTL_INDEX_FOUR, INTL_INDEX_FOUR)) {
95 HILOG_ERROR_I18N("IntlLocale::CheckLocaleParam: language is %{public}s.", language.c_str());
96 return false;
97 }
98 }
99 auto scriptIterator = configs.find(scriptTag);
100 if (scriptIterator != configs.end()) {
101 std::string script = scriptIterator->second;
102 if (script.empty()) {
103 HILOG_ERROR_I18N("IntlLocale::CheckLocaleParam: script is empty.");
104 return false;
105 }
106 }
107 auto regionIterator = configs.find(regionTag);
108 if (regionIterator != configs.end()) {
109 std::string region = regionIterator->second;
110 if (region.empty()) {
111 HILOG_ERROR_I18N("IntlLocale::CheckLocaleParam: region is empty.");
112 return false;
113 }
114 }
115 return true;
116 }
117
SetLocaleParam(const std::string & localeTag,const std::unordered_map<std::string,std::string> & configs,icu::LocaleBuilder * builder)118 bool IntlLocale::SetLocaleParam(const std::string& localeTag,
119 const std::unordered_map<std::string, std::string>& configs, icu::LocaleBuilder* builder)
120 {
121 if (builder == nullptr) {
122 HILOG_ERROR_I18N("IntlLocale::SetLocaleParam: builder is nullptr.");
123 return false;
124 }
125 builder->setLanguageTag(localeTag);
126 UErrorCode status = U_ZERO_ERROR;
127 icu::Locale locale = builder->build(status);
128 if (U_FAILURE(status)) {
129 HILOG_ERROR_I18N("IntlLocale::SetLocaleParam: Build locale failed, localeTag is %{public}s.",
130 localeTag.c_str());
131 return false;
132 }
133 locale.canonicalize(status);
134 if (U_FAILURE(status)) {
135 HILOG_ERROR_I18N("IntlLocale::SetLocaleParam: locale canonicalize failed.");
136 return false;
137 }
138 builder->setLocale(locale);
139
140 auto languageIterator = configs.find(languageTag);
141 if (languageIterator != configs.end()) {
142 std::string language = languageIterator->second;
143 builder->setLanguage(language);
144 }
145
146 auto scriptIterator = configs.find(scriptTag);
147 if (scriptIterator != configs.end()) {
148 std::string script = scriptIterator->second;
149 builder->setScript(script);
150 }
151
152 auto regionIterator = configs.find(regionTag);
153 if (regionIterator != configs.end()) {
154 std::string region = regionIterator->second;
155 builder->setRegion(region);
156 }
157
158 builder->build(status);
159 if (U_FAILURE(status)) {
160 HILOG_ERROR_I18N("IntlLocale::SetLocaleParam: Build locale failed.");
161 return false;
162 }
163 return true;
164 }
165
CheckConfigsExtParam(const std::unordered_map<std::string,std::string> & configs)166 bool IntlLocale::CheckConfigsExtParam(const std::unordered_map<std::string, std::string>& configs)
167 {
168 auto hourCycleIterator = configs.find(hourCycleTag);
169 if (hourCycleIterator != configs.end()) {
170 std::string hourCycle = hourCycleIterator->second;
171 if (HOUR_CYCLE.find(hourCycle) == HOUR_CYCLE.end()) {
172 HILOG_ERROR_I18N("IntlLocale::CheckConfigsExtParam: Check hourCycle failed, hourCycle is %{public}s.",
173 hourCycle.c_str());
174 return false;
175 }
176 }
177
178 auto caseFirstIterator = configs.find(caseFirstTag);
179 if (caseFirstIterator != configs.end()) {
180 std::string caseFirst = caseFirstIterator->second;
181 if (CASE_FIRST.find(caseFirst) == CASE_FIRST.end()) {
182 HILOG_ERROR_I18N("IntlLocale::CheckConfigsExtParam: Check caseFirst failed, caseFirst is %{public}s.",
183 caseFirst.c_str());
184 return false;
185 }
186 }
187
188 return true;
189 }
190
SetConfigsExtParam(const std::unordered_map<std::string,std::string> & configs,icu::LocaleBuilder * builder)191 bool IntlLocale::SetConfigsExtParam(const std::unordered_map<std::string, std::string>& configs,
192 icu::LocaleBuilder* builder)
193 {
194 if (!IntlLocale::SetExtParam(configs, calendarTag, calendarExtTag, builder)) {
195 return false;
196 }
197
198 if (!IntlLocale::SetExtParam(configs, collationTag, collationExtTag, builder)) {
199 return false;
200 }
201
202 if (!IntlLocale::SetExtParam(configs, hourCycleTag, hourCycleExtTag, builder)) {
203 return false;
204 }
205
206 if (!IntlLocale::SetExtParam(configs, caseFirstTag, caseFirstExtTag, builder)) {
207 return false;
208 }
209
210 if (!IntlLocale::SetExtParam(configs, numericTag, numericExtTag, builder)) {
211 return false;
212 }
213
214 if (!IntlLocale::SetExtParam(configs, numberingSystemTag, numberingSystemExtTag, builder)) {
215 return false;
216 }
217
218 UErrorCode status = U_ZERO_ERROR;
219 icuLocale = builder->build(status);
220 if (U_FAILURE(status)) {
221 HILOG_ERROR_I18N("IntlLocale::IntlLocale: Build icuLocale failed.");
222 return false;
223 }
224 icuLocale.canonicalize(status);
225 if (U_FAILURE(status)) {
226 HILOG_ERROR_I18N("IntlLocale::IntlLocale: icuLocale canonicalize failed.");
227 return false;
228 }
229 return true;
230 }
231
SetExtParam(const std::unordered_map<std::string,std::string> & configs,const std::string paramTag,const std::string paramExtTag,icu::LocaleBuilder * builder)232 bool IntlLocale::SetExtParam(const std::unordered_map<std::string, std::string>& configs, const std::string paramTag,
233 const std::string paramExtTag, icu::LocaleBuilder* builder)
234 {
235 auto paramIterator = configs.find(paramTag);
236 if (paramIterator != configs.end()) {
237 std::string param = paramIterator->second;
238 if (!uloc_toLegacyType(uloc_toLegacyKey(paramExtTag.c_str()), param.c_str())) {
239 HILOG_ERROR_I18N("IntlLocale::SetExtParam: Set %{public}s failed, calendar is %{public}s.",
240 paramTag.c_str(), param.c_str());
241 return false;
242 }
243 builder->setUnicodeLocaleKeyword(paramExtTag.c_str(), param.c_str());
244 }
245 return true;
246 }
247
GetLanguage()248 std::string IntlLocale::GetLanguage()
249 {
250 if (!initSuccess) {
251 return "";
252 }
253 return icuLocale.getLanguage();
254 }
255
GetScript()256 std::string IntlLocale::GetScript()
257 {
258 if (!initSuccess) {
259 return "";
260 }
261 return icuLocale.getScript();
262 }
263
GetRegion()264 std::string IntlLocale::GetRegion()
265 {
266 if (!initSuccess) {
267 return "";
268 }
269 return icuLocale.getCountry();
270 }
271
GetBaseName()272 std::string IntlLocale::GetBaseName()
273 {
274 if (!initSuccess) {
275 return "";
276 }
277 std::string result = icuLocale.getBaseName();
278 std::replace(result.begin(), result.end(), '_', '-');
279 return result;
280 }
281
GetCalendar()282 std::string IntlLocale::GetCalendar()
283 {
284 if (!initSuccess) {
285 return "";
286 }
287 UErrorCode status = U_ZERO_ERROR;
288 std::string calendar = icuLocale.getUnicodeKeywordValue<std::string>(calendarExtTag, status);
289 if (U_FAILURE(status)) {
290 return "";
291 }
292 return calendar;
293 }
294
GetCollation()295 std::string IntlLocale::GetCollation()
296 {
297 if (!initSuccess) {
298 return "";
299 }
300 UErrorCode status = U_ZERO_ERROR;
301 std::string collation = icuLocale.getUnicodeKeywordValue<std::string>(collationExtTag, status);
302 if (U_FAILURE(status)) {
303 return "";
304 }
305 return collation;
306 }
307
GetHourCycle()308 std::string IntlLocale::GetHourCycle()
309 {
310 if (!initSuccess) {
311 return "";
312 }
313 UErrorCode status = U_ZERO_ERROR;
314 std::string hourCycle = icuLocale.getUnicodeKeywordValue<std::string>(hourCycleExtTag, status);
315 if (U_FAILURE(status)) {
316 return "";
317 }
318 return hourCycle;
319 }
320
GetNumberingSystem()321 std::string IntlLocale::GetNumberingSystem()
322 {
323 if (!initSuccess) {
324 return "";
325 }
326 UErrorCode status = U_ZERO_ERROR;
327 std::string numberingSystem = icuLocale.getUnicodeKeywordValue<std::string>(numberingSystemExtTag, status);
328 if (U_FAILURE(status)) {
329 return "";
330 }
331 return numberingSystem;
332 }
333
GetNumeric()334 std::string IntlLocale::GetNumeric()
335 {
336 if (!initSuccess) {
337 return "";
338 }
339 UErrorCode status = U_ZERO_ERROR;
340 std::string numeric = icuLocale.getUnicodeKeywordValue<std::string>(numericExtTag, status);
341 if (U_FAILURE(status)) {
342 return "";
343 }
344 return numeric;
345 }
346
GetCaseFirst()347 std::string IntlLocale::GetCaseFirst()
348 {
349 if (!initSuccess) {
350 return "";
351 }
352 UErrorCode status = U_ZERO_ERROR;
353 std::string caseFirst = icuLocale.getUnicodeKeywordValue<std::string>(caseFirstExtTag, status);
354 if (U_FAILURE(status)) {
355 return "";
356 }
357 return caseFirst;
358 }
359
ToString(std::string & errMessage)360 std::string IntlLocale::ToString(std::string& errMessage)
361 {
362 if (!initSuccess) {
363 return "";
364 }
365 UErrorCode status = U_ZERO_ERROR;
366 std::string result = icuLocale.toLanguageTag<std::string>(status);
367 if (U_FAILURE(status)) {
368 errMessage = INVALID_LOCALE_ERROR_MESSAGE;
369 HILOG_ERROR_I18N("IntlLocale::ToString: Get languageTag from icuLocale failed.");
370 return "";
371 }
372 return result;
373 }
374
Maximize()375 std::string IntlLocale::Maximize()
376 {
377 if (!initSuccess) {
378 return "";
379 }
380 UErrorCode status = U_ZERO_ERROR;
381 icu::Locale curLocale = icuLocale;
382 curLocale.addLikelySubtags(status);
383 if (U_FAILURE(status)) {
384 HILOG_ERROR_I18N("IntlLocale::Maximize: Add likely subtags failed.");
385 return "";
386 }
387 std::string result = curLocale.toLanguageTag<std::string>(status);
388 if (U_FAILURE(status)) {
389 HILOG_ERROR_I18N("IntlLocale::Maximize: Get languageTag from curLocale failed.");
390 return "";
391 }
392 return result;
393 }
394
Minimize()395 std::string IntlLocale::Minimize()
396 {
397 if (!initSuccess) {
398 return "";
399 }
400 UErrorCode status = U_ZERO_ERROR;
401 icu::Locale curLocale = icuLocale;
402 curLocale.minimizeSubtags(status);
403 if (U_FAILURE(status)) {
404 HILOG_ERROR_I18N("IntlLocale::Minimize: Minimize subtags failed.");
405 return "";
406 }
407 std::string result = curLocale.toLanguageTag<std::string>(status);
408 if (U_FAILURE(status)) {
409 HILOG_ERROR_I18N("IntlLocale::Minimize: Get languageTag from curLocale failed.");
410 return "";
411 }
412 return result;
413 }
414
415 bool IntlLocale::icuInitialized = IntlLocale::Init();
416
Init()417 bool IntlLocale::Init()
418 {
419 SetHwIcuDirectory();
420 return true;
421 }
422 } // namespace I18n
423 } // namespace Global
424 } // namespace OHOS
425