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