1 /*
2 * Copyright (c) 2021-2022 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 "base/i18n/localization.h"
17
18 #include <cstddef>
19 #include <cstring>
20 #include <map>
21 #include <type_traits>
22 #include <unordered_map>
23 #include <utility>
24
25 #include "chnsecal.h"
26 #include "unicode/calendar.h"
27 #include "unicode/datefmt.h"
28 #include "unicode/dtfmtsym.h"
29 #include "unicode/dtptngen.h"
30 #include "unicode/fieldpos.h"
31 #include "unicode/fmtable.h"
32 #include "unicode/locid.h"
33 #include "unicode/measfmt.h"
34 #include "unicode/measunit.h"
35 #include "unicode/measure.h"
36 #include "unicode/numberformatter.h"
37 #include "unicode/plurrule.h"
38 #include "unicode/reldatefmt.h"
39 #include "unicode/smpdtfmt.h"
40 #include "unicode/stringpiece.h"
41 #include "unicode/ucal.h"
42 #include "unicode/unistr.h"
43 #include "unicode/upluralrules.h"
44 #include "unicode/ureldatefmt.h"
45 #include "unicode/utypes.h"
46 #include "unicode/uversion.h"
47
48 #include "base/json/json_util.h"
49 #include "base/log/log.h"
50 #include "base/resource/internal_resource.h"
51 #include "base/utils/linear_map.h"
52 #include "base/utils/string_utils.h"
53 #include "base/utils/utils.h"
54
55 namespace OHOS::Ace {
56
57 using namespace icu;
58
59 struct LocaleProxy final {
LocaleProxyOHOS::Ace::LocaleProxy60 LocaleProxy(const char* language, const char* countryOrRegion, const char* variant, const char* keywordsAndValues)
61 : instance(language, countryOrRegion, variant, keywordsAndValues)
62 {}
63 ~LocaleProxy() = default;
64
65 Locale instance;
66 };
67
68 namespace {
69
70 #define CHECK_RETURN(status, ret) \
71 do { \
72 if ((status) > U_ZERO_ERROR) { \
73 LOGE("status = %{public}d", static_cast<int32_t>(status)); \
74 return (ret); \
75 } \
76 } while (0)
77
78 #define CHECK_NO_RETURN(status) \
79 do { \
80 if ((status) > U_ZERO_ERROR) { \
81 LOGE("status = %{public}d", static_cast<int32_t>(status)); \
82 } \
83 } while (0)
84
85 const char JSON_PATH_CARVE = '.';
86 const char DEFAULT_LANGUAGE[] = "en-US";
87 constexpr uint32_t SEXAGENARY_CYCLE_SIZE = 60;
88 constexpr uint32_t GUIHAI_YEAR_RECENT = 3;
89 constexpr uint32_t SECONDS_IN_HOUR = 3600;
90
91 const char CHINESE_LEAP[] = u8"\u95f0";
92 const char CHINESE_FIRST[] = u8"\u521d";
93 const char CHINESE_TEN[] = u8"\u5341";
94 const char CHINESE_TWENTY[] = u8"\u5eff";
95 const char* g_chineseOneToNine[] = { u8"\u4e00", u8"\u4e8c", u8"\u4e09", u8"\u56db", u8"\u4e94", u8"\u516d", u8"\u4e03",
96 u8"\u516b", u8"\u4e5d" };
97 const std::unordered_map<std::string, std::string> LANGUAGE_CODE_MAP {
98 { "he", "iw" },
99 { "fil", "tl" },
100 { "id", "in" },
101 };
102
UnicodeString2String(const UnicodeString & source,std::string & result)103 inline void UnicodeString2String(const UnicodeString& source, std::string& result)
104 {
105 source.toUTF8String(result);
106 }
107
DateTimeStyle2EStyle(DateTimeStyle dateTimeStyle)108 DateFormat::EStyle DateTimeStyle2EStyle(DateTimeStyle dateTimeStyle)
109 {
110 switch (dateTimeStyle) {
111 case DateTimeStyle::NONE:
112 return DateFormat::EStyle::kNone;
113 case DateTimeStyle::FULL:
114 return DateFormat::EStyle::kFull;
115 case DateTimeStyle::LONG:
116 return DateFormat::EStyle::kLong;
117 case DateTimeStyle::MEDIUM:
118 return DateFormat::EStyle::kMedium;
119 case DateTimeStyle::SHORT:
120 return DateFormat::EStyle::kShort;
121 default:
122 return DateFormat::EStyle::kNone;
123 }
124 }
125
GetMeasureFormatWidth(MeasureFormatStyle formatStyle)126 UMeasureFormatWidth GetMeasureFormatWidth(MeasureFormatStyle formatStyle)
127 {
128 switch (formatStyle) {
129 case MeasureFormatStyle::WIDTH_WIDE:
130 return UMeasureFormatWidth::UMEASFMT_WIDTH_WIDE;
131 case MeasureFormatStyle::WIDTH_SHORT:
132 return UMeasureFormatWidth::UMEASFMT_WIDTH_SHORT;
133 case MeasureFormatStyle::WIDTH_NARROW:
134 return UMeasureFormatWidth::UMEASFMT_WIDTH_NARROW;
135 case MeasureFormatStyle::WIDTH_NUMERIC:
136 return UMeasureFormatWidth::UMEASFMT_WIDTH_NUMERIC;
137 case MeasureFormatStyle::WIDTH_COUNT:
138 return UMeasureFormatWidth::UMEASFMT_WIDTH_COUNT;
139 default:
140 return UMeasureFormatWidth::UMEASFMT_WIDTH_WIDE;
141 }
142 }
143
GetMeasureUnit(TimeUnitStyle timeUnitStyle,UErrorCode & status)144 MeasureUnit* GetMeasureUnit(TimeUnitStyle timeUnitStyle, UErrorCode& status)
145 {
146 switch (timeUnitStyle) {
147 case TimeUnitStyle::YEAR:
148 return MeasureUnit::createYear(status);
149 case TimeUnitStyle::MONTH:
150 return MeasureUnit::createMonth(status);
151 case TimeUnitStyle::DAY:
152 return MeasureUnit::createDay(status);
153 case TimeUnitStyle::HOUR:
154 return MeasureUnit::createHour(status);
155 case TimeUnitStyle::MINUTE:
156 return MeasureUnit::createMinute(status);
157 case TimeUnitStyle::SECOND:
158 return MeasureUnit::createSecond(status);
159 case TimeUnitStyle::MILLISECOND:
160 return MeasureUnit::createMillisecond(status);
161 default:
162 return MeasureUnit::createYear(status);
163 }
164 }
165
GetLocalJsonObject(InternalResource::ResourceId id,std::string language,std::unique_ptr<JsonValue> & indexJson,std::unique_ptr<JsonValue> & json)166 void GetLocalJsonObject(InternalResource::ResourceId id, std::string language, std::unique_ptr<JsonValue>& indexJson,
167 std::unique_ptr<JsonValue>& json)
168 {
169 if (indexJson == nullptr) {
170 size_t size = 0;
171 const uint8_t* buf = InternalResource::GetInstance().GetResource(id, size);
172 if (buf == nullptr) {
173 return;
174 }
175
176 std::string jsonStr(reinterpret_cast<const char*>(buf), size);
177 const char* endMsg = nullptr;
178 indexJson = JsonUtil::ParseJsonString(jsonStr, &endMsg);
179 if (indexJson == nullptr) {
180 LOGE("read indexletter json failed. reason: %{private}s.", endMsg);
181 return;
182 }
183 }
184
185 if (indexJson->Contains(language) && indexJson->GetValue(language)->IsObject()) {
186 json = indexJson->GetValue(language);
187 } else if (indexJson->Contains(DEFAULT_LANGUAGE) && indexJson->GetValue(DEFAULT_LANGUAGE)->IsObject()) {
188 json = indexJson->GetValue(DEFAULT_LANGUAGE);
189 }
190 }
191
192 } // namespace
193
194 // for entry.json
195 static std::unique_ptr<JsonValue> g_indexJsonEntry = nullptr;
196 static std::unique_ptr<JsonValue> g_indexJsonError = nullptr;
197
198 Localization::~Localization() = default;
199
SetLocaleImpl(const std::string & language,const std::string & countryOrRegion,const std::string & script,const std::string & selectLanguage,const std::string & keywordsAndValues)200 void Localization::SetLocaleImpl(const std::string& language, const std::string& countryOrRegion,
201 const std::string& script, const std::string& selectLanguage, const std::string& keywordsAndValues)
202 {
203 locale_ = std::make_unique<LocaleProxy>(language.c_str(), countryOrRegion.c_str(), "", keywordsAndValues.c_str());
204
205 UErrorCode status = U_ZERO_ERROR;
206 std::vector<std::string> keyValuePairs;
207 StringUtils::StringSplitter(keywordsAndValues, ';', keyValuePairs);
208 for (const auto& pair : keyValuePairs) {
209 // [pair] is like "nu=arab" or "nu=" for most occasions, but may be "=" under extreme scenarios
210 std::vector<std::string> res;
211 StringUtils::StringSplitter(pair, '=', res);
212 if (res.size() == 0) {
213 continue;
214 }
215 auto value = (res.size() == 2) ? res[1] : "";
216 locale_->instance.setUnicodeKeywordValue(res[0], value, status);
217 CHECK_NO_RETURN(status);
218 }
219
220 languageTag_ = language;
221 if (!script.empty()) {
222 languageTag_.append("-").append(script);
223 }
224 languageTag_.append("-").append(countryOrRegion);
225 fontLocale_ = languageTag_;
226 // Simple chinese
227 if (languageTag_ == "zh-Hans-CN") {
228 languageTag_ = "zh-CN";
229 fontLocale_ = "";
230 }
231
232 selectLanguage_ = selectLanguage;
233 // match json of latin
234 if (selectLanguage_ == "jv-Latn") {
235 selectLanguage_ = "b+jv+Latn";
236 } else if (selectLanguage_ == "sr-Latn") {
237 selectLanguage_ = "b+sr+Latn";
238 }
239
240 LOGI("SetLocale language tag: %{public}s, select language: %{public}s", languageTag_.c_str(),
241 selectLanguage_.c_str());
242 if (!isPromiseUsed_) {
243 promise_.set_value(true);
244 isPromiseUsed_ = true;
245 }
246 }
247
GetLanguage()248 std::string Localization::GetLanguage()
249 {
250 WaitingForInit();
251 if (locale_) {
252 return locale_->instance.getLanguage();
253 }
254 return "";
255 }
256
GetLanguageTag()257 std::string Localization::GetLanguageTag()
258 {
259 WaitingForInit();
260 return languageTag_;
261 }
262
GetFontLocale()263 std::string Localization::GetFontLocale()
264 {
265 WaitingForInit();
266 return fontLocale_;
267 }
268
FormatDuration(uint32_t duration,bool needShowHour)269 const std::string Localization::FormatDuration(uint32_t duration, bool needShowHour)
270 {
271 WaitingForInit();
272 UErrorCode status = U_ZERO_ERROR;
273 // duration greater than 1 hour, use HH:mm:ss;
274 if (!needShowHour && duration > SECONDS_IN_HOUR) {
275 needShowHour = true;
276 }
277 const char* engTimeFormat = needShowHour ? "HH:mm:ss" : "mm:ss";
278 auto simpleDateFormat = std::make_unique<SimpleDateFormat>(UnicodeString(engTimeFormat), locale_->instance, status);
279 CHECK_RETURN(status, "");
280 TimeZone* timeZone = TimeZone::createTimeZone("GMT+0:00");
281 simpleDateFormat->setTimeZone(*timeZone);
282
283 UnicodeString simpleStr;
284 simpleDateFormat->format(1000.0 * duration, simpleStr, status);
285 CHECK_RETURN(status, "");
286
287 std::string ret;
288 UnicodeString2String(simpleStr, ret);
289
290 delete(timeZone);
291 return ret;
292 }
293
FormatDuration(uint32_t duration,const std::string & format)294 std::string Localization::FormatDuration(uint32_t duration, const std::string& format)
295 {
296 WaitingForInit();
297 UErrorCode status = U_ZERO_ERROR;
298
299 const char* engTimeFormat = format.c_str();
300 auto simpleDateFormat = std::make_unique<SimpleDateFormat>(UnicodeString(engTimeFormat), locale_->instance, status);
301 CHECK_RETURN(status, "");
302 TimeZone* timeZone = TimeZone::createTimeZone("GMT+0:00");
303 simpleDateFormat->setTimeZone(*timeZone);
304
305 UnicodeString simpleStr;
306 simpleDateFormat->format(1.0 * duration, simpleStr, status);
307 CHECK_RETURN(status, "");
308
309 std::string ret;
310 UnicodeString2String(simpleStr, ret);
311
312 delete(timeZone);
313 return ret;
314 }
315
FormatDateTime(DateTime dateTime,const std::string & format)316 const std::string Localization::FormatDateTime(DateTime dateTime, const std::string& format)
317 {
318 WaitingForInit();
319 UErrorCode status = U_ZERO_ERROR;
320 auto cal = Calendar::createInstance(locale_->instance, status);
321 CHECK_RETURN(status, "");
322 cal->set(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second);
323
324 UDate date = cal->getTime(status);
325 delete cal;
326 CHECK_RETURN(status, "");
327
328 auto patternGenerator = DateTimePatternGenerator::createInstance(locale_->instance, status);
329 CHECK_RETURN(status, "");
330 UnicodeString pattern = patternGenerator->getBestPattern(UnicodeString(format.c_str()), status);
331 delete patternGenerator;
332 CHECK_RETURN(status, "");
333
334 auto dateFormat = std::make_unique<SimpleDateFormat>(pattern, locale_->instance, status);
335 CHECK_RETURN(status, "");
336
337 UnicodeString dateTimeStr;
338 dateFormat->format(date, dateTimeStr, status);
339 CHECK_RETURN(status, "");
340
341 std::string ret;
342 UnicodeString2String(dateTimeStr, ret);
343 return ret;
344 }
345
GetDateColumnFormatOrder(std::vector<std::string> & outOrder)346 bool Localization::GetDateColumnFormatOrder(std::vector<std::string>& outOrder)
347 {
348 outOrder.clear();
349 WaitingForInit();
350 UErrorCode status = U_ZERO_ERROR;
351
352 auto patternGenerator = DateTimePatternGenerator::createInstance(locale_->instance, status);
353 CHECK_RETURN(status, false);
354 std::string format = "yyyyMMdd";
355 UnicodeString pattern = patternGenerator->getBestPattern(UnicodeString(format.c_str()), status);
356 delete patternGenerator;
357 CHECK_RETURN(status, false);
358
359 std::string result;
360 UnicodeString2String(pattern, result);
361 LOGD("order of date format is %{public}s", result.c_str());
362
363 std::map<std::size_t, std::string> order;
364 std::size_t position = result.find("yyyy");
365 if (position == std::string::npos) {
366 return false;
367 }
368 order[position] = "year";
369
370 position = result.find("MM");
371 if (position == std::string::npos) {
372 return false;
373 }
374 order[position] = "month";
375
376 position = result.find("dd");
377 if (position == std::string::npos) {
378 return false;
379 }
380 order[position] = "day";
381
382 for (auto it = order.begin(); it != order.end(); ++it) {
383 outOrder.emplace_back(it->second);
384 }
385
386 return true;
387 }
388
Contain(const std::string & str,const std::string & tag)389 bool Localization::Contain(const std::string& str, const std::string& tag)
390 {
391 auto pos = str.find(tag);
392 return (pos != std::string::npos);
393 }
394
GetHourFormat(bool & isAmPm,bool & hasZero)395 bool Localization::GetHourFormat(bool& isAmPm, bool& hasZero)
396 {
397 WaitingForInit();
398 UErrorCode status = U_ZERO_ERROR;
399
400 auto patternGenerator = DateTimePatternGenerator::createInstance(locale_->instance, status);
401 CHECK_RETURN(status, false);
402 std::string format = "J:mm";
403 UnicodeString pattern = patternGenerator->getBestPattern(UnicodeString(format.c_str()), status);
404 delete patternGenerator;
405 CHECK_RETURN(status, false);
406
407 std::string result;
408 UnicodeString2String(pattern, result);
409 LOGI("hour format is %{public}s", result.c_str());
410
411 if (Contain(result, "hh") || Contain(result, "KK")) {
412 isAmPm = true;
413 hasZero = true;
414 return true;
415 }
416
417 if (Contain(result, "h") || Contain(result, "K")) {
418 isAmPm = true;
419 hasZero = false;
420 return true;
421 }
422
423 if (Contain(result, "HH") || Contain(result, "kk")) {
424 isAmPm = false;
425 hasZero = true;
426 return true;
427 }
428
429 if (Contain(result, "H") || Contain(result, "k")) {
430 isAmPm = false;
431 hasZero = false;
432 return true;
433 }
434
435 LOGE("hour format is unknown[%{public}s]", result.c_str());
436 return false;
437 }
438
FormatDateTime(DateTime dateTime,DateTimeStyle dateStyle,DateTimeStyle timeStyle)439 const std::string Localization::FormatDateTime(DateTime dateTime, DateTimeStyle dateStyle, DateTimeStyle timeStyle)
440 {
441 WaitingForInit();
442 UErrorCode status = U_ZERO_ERROR;
443 auto cal = Calendar::createInstance(locale_->instance, status);
444 CHECK_RETURN(status, "");
445 cal->set(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second);
446
447 UDate date = cal->getTime(status);
448 delete cal;
449 CHECK_RETURN(status, "");
450
451 auto dateFormat = DateFormat::createDateTimeInstance(
452 DateTimeStyle2EStyle(dateStyle), DateTimeStyle2EStyle(timeStyle), locale_->instance);
453 if (dateFormat == nullptr) {
454 return "";
455 }
456
457 UnicodeString dateTimeStr;
458 dateFormat->format(date, dateTimeStr, status);
459 delete dateFormat;
460 CHECK_RETURN(status, "");
461
462 std::string ret;
463 UnicodeString2String(dateTimeStr, ret);
464 return ret;
465 }
466
GetMonths(bool isShortType,const std::string & calendarType)467 std::vector<std::string> Localization::GetMonths(bool isShortType, const std::string& calendarType)
468 {
469 WaitingForInit();
470 std::vector<std::string> months;
471 UErrorCode status = U_ZERO_ERROR;
472 DateFormatSymbols dateformat(locale_->instance, calendarType.c_str(), status);
473 CHECK_RETURN(status, months);
474
475 int32_t count = 0;
476
477 auto monthsUniStr = dateformat.getMonths(count, DateFormatSymbols::DtContextType::STANDALONE,
478 isShortType ? DateFormatSymbols::DtWidthType::SHORT : DateFormatSymbols::DtWidthType::WIDE);
479 if (count > 0) {
480 std::string month;
481 for (int32_t i = 0; i < count; i++) {
482 month.clear();
483 UnicodeString2String(monthsUniStr[i], month);
484 months.push_back(month);
485 }
486 }
487 return months;
488 }
489
GetWeekdays(bool isShortType)490 std::vector<std::string> Localization::GetWeekdays(bool isShortType)
491 {
492 WaitingForInit();
493 std::vector<std::string> weekdays;
494 UErrorCode status = U_ZERO_ERROR;
495 DateFormatSymbols dateformat(locale_->instance, status);
496 CHECK_RETURN(status, weekdays);
497
498 int32_t count = 0;
499
500 auto language = locale_->instance.getLanguage();
501 auto widthType = isShortType ? (strcmp(language, "zh") == 0 || strcmp(language, "bo") == 0)
502 ? DateFormatSymbols::DtWidthType::NARROW
503 : DateFormatSymbols::DtWidthType::ABBREVIATED
504 : DateFormatSymbols::DtWidthType::WIDE;
505 auto weekdaysUniStr = dateformat.getWeekdays(count, DateFormatSymbols::DtContextType::STANDALONE, widthType);
506 if (count > 0) {
507 std::string weekday;
508 for (int32_t i = 0; i < count; i++) {
509 weekday.clear();
510 UnicodeString2String(weekdaysUniStr[i], weekday);
511 if (!weekday.empty()) {
512 weekdays.push_back(weekday);
513 }
514 }
515 }
516 return weekdays;
517 }
518
GetAmPmStrings()519 std::vector<std::string> Localization::GetAmPmStrings()
520 {
521 WaitingForInit();
522 std::vector<std::string> amPms;
523 UErrorCode status = U_ZERO_ERROR;
524 DateFormatSymbols dateformat(locale_->instance, status);
525 CHECK_RETURN(status, amPms);
526
527 int32_t count = 0;
528
529 auto amPmUniStr = dateformat.getAmPmStrings(count);
530 if (count > 0) {
531 std::string amPm;
532 for (int32_t i = 0; i < count; i++) {
533 amPm.clear();
534 UnicodeString2String(amPmUniStr[i], amPm);
535 amPms.push_back(amPm);
536 }
537 }
538 return amPms;
539 }
540
GetRelativeDateTime(double offset)541 std::string Localization::GetRelativeDateTime(double offset)
542 {
543 WaitingForInit();
544 UErrorCode status = U_ZERO_ERROR;
545 RelativeDateTimeFormatter relativeDateformat(locale_->instance, status);
546 CHECK_RETURN(status, "");
547
548 UnicodeString relativeDate;
549 relativeDateformat.format(offset, URelativeDateTimeUnit::UDAT_REL_UNIT_DAY, relativeDate, status);
550 CHECK_RETURN(status, "");
551
552 std::string ret;
553 UnicodeString2String(relativeDate, ret);
554 return ret;
555 }
556
GetLunarDate(Date date)557 LunarDate Localization::GetLunarDate(Date date)
558 {
559 WaitingForInit();
560 LunarDate dateRet;
561 UErrorCode status = U_ZERO_ERROR;
562 Locale locale("zh", "CN");
563 auto cal = Calendar::createInstance(locale, status);
564 CHECK_RETURN(status, dateRet);
565 // 0 means January, 1 means February, so month - 1
566 cal->set(date.year, date.month - 1, date.day);
567
568 UDate udate = cal->getTime(status);
569 delete cal;
570 CHECK_RETURN(status, dateRet);
571
572 ChineseCalendar chineseCalendar(locale, status);
573 CHECK_RETURN(status, dateRet);
574
575 chineseCalendar.setTime(udate, status);
576 CHECK_RETURN(status, dateRet);
577
578 int32_t lunarYear = chineseCalendar.get(UCalendarDateFields::UCAL_YEAR, status);
579 CHECK_RETURN(status, dateRet);
580 int32_t lunarMonth = chineseCalendar.get(UCalendarDateFields::UCAL_MONTH, status);
581 CHECK_RETURN(status, dateRet);
582 int32_t lunarDate = chineseCalendar.get(UCalendarDateFields::UCAL_DATE, status);
583 CHECK_RETURN(status, dateRet);
584 int32_t isLeapMonth = chineseCalendar.get(UCalendarDateFields::UCAL_IS_LEAP_MONTH, status);
585 CHECK_RETURN(status, dateRet);
586
587 // Sexagenary cycle years convert to Western years
588 dateRet.year = static_cast<uint32_t>(lunarYear) + GUIHAI_YEAR_RECENT;
589 dateRet.year +=
590 ((static_cast<uint32_t>(date.year) - GUIHAI_YEAR_RECENT) / SEXAGENARY_CYCLE_SIZE) * SEXAGENARY_CYCLE_SIZE;
591 // 0 means January, 1 means February, so month + 1
592 dateRet.month = static_cast<uint32_t>(lunarMonth) + 1;
593 dateRet.day = static_cast<uint32_t>(lunarDate);
594 dateRet.isLeapMonth = !(isLeapMonth == 0);
595 return dateRet;
596 }
597
GetLunarMonth(uint32_t month,bool isLeapMonth)598 std::string Localization::GetLunarMonth(uint32_t month, bool isLeapMonth)
599 {
600 WaitingForInit();
601 std::vector<std::string> months = Localization::GetInstance()->GetMonths(false, "chinese");
602 if (month <= months.size() && month > 0) {
603 std::string leap;
604 if (isLeapMonth) {
605 leap += std::string(CHINESE_LEAP);
606 }
607 return leap + months[month - 1];
608 } else {
609 LOGE("month parameter is illegal:%{public}d", month);
610 return "";
611 }
612 }
613
GetLunarDay(uint32_t dayOfMonth)614 std::string Localization::GetLunarDay(uint32_t dayOfMonth)
615 {
616 WaitingForInit();
617 if (dayOfMonth > 30 || dayOfMonth == 0) {
618 LOGE("dayOfMonth parameter is illegal");
619 return "";
620 }
621
622 std::string ret;
623 if (dayOfMonth < 10) {
624 ret = std::string(CHINESE_FIRST) + std::string(g_chineseOneToNine[dayOfMonth - 1]);
625 } else if (dayOfMonth == 10) {
626 ret = std::string(CHINESE_FIRST) + std::string(CHINESE_TEN);
627 } else if (dayOfMonth < 20) {
628 ret = std::string(CHINESE_TEN) + std::string(g_chineseOneToNine[dayOfMonth - 11]);
629 } else if (dayOfMonth == 20) {
630 ret = std::string(CHINESE_TWENTY) + std::string(CHINESE_TEN);
631 } else if (dayOfMonth == 30) {
632 ret = g_chineseOneToNine[2] + std::string(CHINESE_TEN);
633 } else {
634 ret = std::string(CHINESE_TWENTY) + std::string(g_chineseOneToNine[dayOfMonth - 21]);
635 }
636
637 return ret;
638 }
639
TimeUnitFormat(double timeValue,TimeUnitStyle timeStyle,MeasureFormatStyle formatStyle)640 std::string Localization::TimeUnitFormat(double timeValue, TimeUnitStyle timeStyle, MeasureFormatStyle formatStyle)
641 {
642 WaitingForInit();
643 UErrorCode status = U_ZERO_ERROR;
644 MeasureFormat measureFormat(locale_->instance, GetMeasureFormatWidth(formatStyle), status);
645 CHECK_RETURN(status, "");
646
647 MeasureUnit* minuteUnit = GetMeasureUnit(timeStyle, status);
648 CHECK_RETURN(status, "");
649
650 Formattable formattable(timeValue);
651 Measure measure(formattable, minuteUnit, status);
652 CHECK_RETURN(status, "");
653
654 UnicodeString timeUnit;
655 FieldPosition fieldPosition;
656 measureFormat.formatMeasures(&measure, 1, timeUnit, fieldPosition, status);
657 CHECK_RETURN(status, "");
658
659 std::string ret;
660 UnicodeString2String(timeUnit, ret);
661 return ret;
662 }
663
PluralRulesFormat(double number,bool isCardinal)664 std::string Localization::PluralRulesFormat(double number, bool isCardinal)
665 {
666 WaitingForInit();
667 UErrorCode status = U_ZERO_ERROR;
668 UPluralType pluralType = isCardinal ? UPluralType::UPLURAL_TYPE_CARDINAL : UPluralType::UPLURAL_TYPE_ORDINAL;
669 PluralRules* pluralRules = PluralRules::forLocale(locale_->instance, pluralType, status);
670 CHECK_RETURN(status, "");
671
672 UnicodeString numberFormat = pluralRules->select(number);
673 delete pluralRules;
674
675 std::string ret;
676 UnicodeString2String(numberFormat, ret);
677 return ret;
678 }
679
NumberFormat(double number)680 std::string Localization::NumberFormat(double number)
681 {
682 WaitingForInit();
683 UErrorCode status = U_ZERO_ERROR;
684
685 icu::number::LocalizedNumberFormatter formatter = icu::number::NumberFormatter::withLocale(locale_->instance);
686 icu::number::FormattedNumber formattedNumber = formatter.formatDouble(number, status);
687 CHECK_RETURN(status, "");
688
689 UnicodeString numberFormat = formattedNumber.toString(status);
690 CHECK_RETURN(status, "");
691
692 std::string ret;
693 UnicodeString2String(numberFormat, ret);
694 return ret;
695 }
696
GetLetters(bool alphabet)697 std::vector<std::u16string> Localization::GetLetters(bool alphabet)
698 {
699 WaitingForInit();
700 std::vector<std::u16string> letters;
701 size_t size = 0;
702 const uint8_t* buf =
703 InternalResource::GetInstance().GetResource(InternalResource::ResourceId::INDEXLETTER_BAR_JSON, size);
704 if (buf == nullptr) {
705 return letters;
706 }
707
708 std::string jsonStr(reinterpret_cast<const char*>(buf), size);
709 const char* endMsg = nullptr;
710 auto indexLetterJson = JsonUtil::ParseJsonString(jsonStr, &endMsg);
711 if (indexLetterJson == nullptr) {
712 LOGE("read indexletter json failed. reason: %{private}s.", endMsg);
713 return letters;
714 }
715
716 std::string language = locale_->instance.getLanguage();
717 if (language == "zh") {
718 language = language + "-" + std::string(locale_->instance.getCountry());
719 }
720 auto iter = LANGUAGE_CODE_MAP.find(language);
721 if (iter != LANGUAGE_CODE_MAP.end()) {
722 language = iter->second;
723 }
724 LOGI("[alphabet] Localization::GetLetters. language: %{private}s", language.c_str());
725 std::unique_ptr<JsonValue> lettersSet;
726 if (!indexLetterJson->Contains(language) || !indexLetterJson->GetValue(language)->IsObject()) {
727 lettersSet = indexLetterJson->GetValue("default");
728 } else {
729 lettersSet = indexLetterJson->GetValue(language);
730 }
731
732 std::string letterType = alphabet ? "alphabet" : "index";
733 std::unique_ptr<JsonValue> lettersArray;
734 if (!lettersSet->Contains(letterType) || !lettersSet->GetValue(letterType)->IsArray()) {
735 LOGE("read letter array failed. Invalid type. %s", letterType.c_str());
736 return letters;
737 } else {
738 lettersArray = lettersSet->GetValue(letterType)->GetChild();
739 }
740
741 while (lettersArray->IsValid()) {
742 letters.push_back(StringUtils::Str8ToStr16(lettersArray->GetString()));
743 lettersArray = lettersArray->GetNext();
744 }
745 return letters;
746 }
747
GetIndexLetter()748 std::vector<std::u16string> Localization::GetIndexLetter()
749 {
750 return GetLetters(false);
751 }
752
GetIndexAlphabet()753 std::vector<std::u16string> Localization::GetIndexAlphabet()
754 {
755 return GetLetters(true);
756 }
757
GetEntryLetters(const std::string & lettersIndex)758 std::string Localization::GetEntryLetters(const std::string& lettersIndex)
759 {
760 WaitingForInit();
761 if (lettersIndex.empty()) {
762 return "";
763 }
764
765 std::unique_ptr<JsonValue> localJsonEntry;
766 auto language = selectLanguage_;
767 auto iter = LANGUAGE_CODE_MAP.find(language);
768 if (iter != LANGUAGE_CODE_MAP.end()) {
769 language = iter->second;
770 }
771 GetLocalJsonObject(InternalResource::ResourceId::ENTRY_JSON, language, g_indexJsonEntry, localJsonEntry);
772 if (localJsonEntry == nullptr) {
773 LOGE("read JsonObject fail. language: %{public}s.", selectLanguage_.c_str());
774 return "";
775 }
776
777 std::vector<std::string> jsonLetterIndex;
778 StringUtils::StringSplitter(lettersIndex, JSON_PATH_CARVE, jsonLetterIndex);
779
780 for (const auto& letter : jsonLetterIndex) {
781 if (localJsonEntry && localJsonEntry->Contains(letter)) {
782 localJsonEntry = localJsonEntry->GetValue(letter);
783 } else {
784 LOGE("read entry json failed.");
785 return "";
786 }
787 }
788
789 if (localJsonEntry->IsString()) {
790 return localJsonEntry->GetString();
791 }
792
793 return "";
794 }
795
GetErrorDescription(const std::string & errorIndex)796 std::string Localization::GetErrorDescription(const std::string& errorIndex)
797 {
798 WaitingForInit();
799 if (errorIndex.empty()) {
800 return "";
801 }
802
803 std::unique_ptr<JsonValue> localJsonError;
804 auto language = selectLanguage_;
805 auto iter = LANGUAGE_CODE_MAP.find(language);
806 if (iter != LANGUAGE_CODE_MAP.end()) {
807 language = iter->second;
808 }
809 GetLocalJsonObject(InternalResource::ResourceId::ERRORINFO_JSON, language, g_indexJsonError, localJsonError);
810 if (localJsonError == nullptr) {
811 LOGE("read JsonObject fail. language: %{public}s.", selectLanguage_.c_str());
812 return "";
813 }
814
815 if (localJsonError->Contains(errorIndex)) {
816 localJsonError = localJsonError->GetValue(errorIndex);
817 } else {
818 LOGE("read error json failed. error path: %{private}s.", errorIndex.c_str());
819 return "";
820 }
821
822 if (localJsonError->IsString()) {
823 return localJsonError->GetString();
824 }
825
826 return "";
827 }
828
GetLanguageList(const std::string & language)829 const std::vector<std::string>& Localization::GetLanguageList(const std::string& language)
830 {
831 static const LinearMapNode<std::vector<std::string>> multiLanguageMap[] = {
832 { "am", { "am" } },
833 { "ar", { "ar" } },
834 { "as", { "as" } },
835 { "az", { "az-AZ" } },
836 { "be", { "be" } },
837 { "bg", { "bg" } },
838 { "bn", { "bn" } },
839 { "bo", { "bo-CN" } },
840 { "bs", { "bs" } },
841 { "ca", { "ca" } },
842 { "cs", { "cs" } },
843 { "da", { "da" } },
844 { "de", { "de" } },
845 { "el", { "el" } },
846 { "en", { "en-US", "en-GB" } },
847 { "es", { "es,es-US" } },
848 { "et", { "et" } },
849 { "fa", { "fa" } },
850 { "fi", { "fi" } },
851 { "fil", { "fil" } },
852 { "fr", { "fr" } },
853 { "gl", { "gl-ES" } },
854 { "he", { "he" } },
855 { "hi", { "hi" } },
856 { "hr", { "hr" } },
857 { "hu", { "hu" } },
858 { "id", { "id" } },
859 { "in", { "in" } },
860 { "it", { "it" } },
861 { "iw", { "iw" } },
862 { "ja", { "ja" } },
863 { "jv", { "jv-Latn" } },
864 { "ka", { "ka-GE" } },
865 { "kk", { "kk-KZ" } },
866 { "km", { "km-KH" } },
867 { "kn", { "kn" } },
868 { "ko", { "ko" } },
869 { "lo", { "lo-LA" } },
870 { "lt", { "lt" } },
871 { "lv", { "lv" } },
872 { "mai", { "mai" } },
873 { "mi", { "mi" } },
874 { "mk", { "mk" } },
875 { "ml", { "ml" } },
876 { "mn", { "mn" } },
877 { "mr", { "mr" } },
878 { "ms", { "ms" } },
879 { "my", { "my-ZG", "my-MM" } },
880 { "nb", { "nb" } },
881 { "ne", { "ne" } },
882 { "nl", { "nl" } },
883 { "or", { "or" } },
884 { "pa", { "pa" } },
885 { "pl", { "pl" } },
886 { "pt", { "pt", "pt-PT" } },
887 { "ro", { "ro" } },
888 { "ru", { "ru" } },
889 { "si", { "si-LK" } },
890 { "sk", { "sk" } },
891 { "sl", { "sl" } },
892 { "sr", { "sr-Latn" } },
893 { "sv", { "sv" } },
894 { "sw", { "sw" } },
895 { "ta", { "ta" } },
896 { "te", { "te" } },
897 { "th", { "th" } },
898 { "tl", { "tl" } },
899 { "tr", { "tr" } },
900 { "ug", { "ug" } },
901 { "uk", { "uk" } },
902 { "ur", { "ur" } },
903 { "uz", { "uz-UZ" } },
904 { "vi", { "vi" } },
905 { "zh", { "zh-CN", "zh-HK", "zh-TW" } },
906 { "zz", { "zz-ZX" } },
907 };
908 int64_t list = BinarySearchFindIndex(multiLanguageMap, ArraySize(multiLanguageMap), language.c_str());
909 if (list == -1) {
910 static const std::vector<std::string> defaultLanguage = { "en-US" };
911 return defaultLanguage;
912 }
913 return multiLanguageMap[list].value;
914 }
915
916 std::mutex Localization::mutex_;
917 std::shared_ptr<Localization> Localization::instance_;
918 bool Localization::firstInstance_ = true;
919
GetInstance()920 std::shared_ptr<Localization> Localization::GetInstance()
921 {
922 std::lock_guard<std::mutex> lock(mutex_);
923 if (!instance_) {
924 instance_ = std::make_shared<Localization>();
925 }
926 return instance_;
927 }
928
SetLocale(const std::string & language,const std::string & countryOrRegion,const std::string & script,const std::string & selectLanguage,const std::string & keywordsAndValues)929 void Localization::SetLocale(const std::string& language, const std::string& countryOrRegion, const std::string& script,
930 const std::string& selectLanguage, const std::string& keywordsAndValues)
931 {
932 if (instance_) {
933 instance_->HandleOnChange();
934 instance_->HandleOnMymrChange(script == "Qaag");
935 }
936 std::shared_ptr<Localization> instance;
937 {
938 std::lock_guard<std::mutex> lock(mutex_);
939 if (!firstInstance_ || !instance_) {
940 if (instance_) {
941 auto onMymrChange = instance_->GetOnMymrChange();
942 instance_ = std::make_shared<Localization>();
943 instance_->SetOnMymrChange(onMymrChange);
944 } else {
945 instance_ = std::make_shared<Localization>();
946 }
947 }
948 firstInstance_ = false;
949 instance = instance_;
950 }
951 instance->SetLocaleImpl(language, countryOrRegion, script, selectLanguage, keywordsAndValues);
952 }
953
ComputeScript(const std::string & language,const std::string & region)954 std::string Localization::ComputeScript(const std::string& language, const std::string& region)
955 {
956 icu::Locale locale(language.c_str(), region.c_str());
957 UErrorCode status = U_ZERO_ERROR;
958 locale.addLikelySubtags(status);
959 if (status != U_ZERO_ERROR) {
960 return std::string();
961 }
962 return locale.getScript();
963 }
964
ParseLocaleTag(const std::string & localeTag,std::string & language,std::string & script,std::string & region,bool needAddSubtags)965 void Localization::ParseLocaleTag(
966 const std::string& localeTag, std::string& language, std::string& script, std::string& region, bool needAddSubtags)
967 {
968 UErrorCode status = U_ZERO_ERROR;
969 icu::Locale locale = icu::Locale::forLanguageTag(icu::StringPiece(localeTag.c_str()), status);
970 if (needAddSubtags) {
971 locale.addLikelySubtags(status);
972 }
973 if (status != U_ZERO_ERROR) {
974 LOGE("This localeTag is not valid.");
975 return;
976 }
977 language = locale.getLanguage();
978 script = locale.getScript();
979 region = locale.getCountry();
980 }
981
982 } // namespace OHOS::Ace
983