• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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