• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     return ret;
290 }
291 
FormatDuration(uint32_t duration,const std::string & format)292 std::string Localization::FormatDuration(uint32_t duration, const std::string& format)
293 {
294     WaitingForInit();
295     UErrorCode status = U_ZERO_ERROR;
296 
297     const char* engTimeFormat = format.c_str();
298     auto simpleDateFormat = std::make_unique<SimpleDateFormat>(UnicodeString(engTimeFormat), locale_->instance, status);
299     CHECK_RETURN(status, "");
300     TimeZone* timeZone = TimeZone::createTimeZone("GMT+0:00");
301     simpleDateFormat->setTimeZone(*timeZone);
302 
303     UnicodeString simpleStr;
304     simpleDateFormat->format(1.0 * duration, simpleStr, status);
305     CHECK_RETURN(status, "");
306 
307     std::string ret;
308     UnicodeString2String(simpleStr, ret);
309     return ret;
310 }
311 
FormatDateTime(DateTime dateTime,const std::string & format)312 const std::string Localization::FormatDateTime(DateTime dateTime, const std::string& format)
313 {
314     WaitingForInit();
315     UErrorCode status = U_ZERO_ERROR;
316     auto cal = Calendar::createInstance(locale_->instance, status);
317     CHECK_RETURN(status, "");
318     cal->set(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second);
319 
320     UDate date = cal->getTime(status);
321     delete cal;
322     CHECK_RETURN(status, "");
323 
324     auto patternGenerator = DateTimePatternGenerator::createInstance(locale_->instance, status);
325     CHECK_RETURN(status, "");
326     UnicodeString pattern = patternGenerator->getBestPattern(UnicodeString(format.c_str()), status);
327     delete patternGenerator;
328     CHECK_RETURN(status, "");
329 
330     auto dateFormat = std::make_unique<SimpleDateFormat>(pattern, locale_->instance, status);
331     CHECK_RETURN(status, "");
332 
333     UnicodeString dateTimeStr;
334     dateFormat->format(date, dateTimeStr, status);
335     CHECK_RETURN(status, "");
336 
337     std::string ret;
338     UnicodeString2String(dateTimeStr, ret);
339     return ret;
340 }
341 
GetDateColumnFormatOrder(std::vector<std::string> & outOrder)342 bool Localization::GetDateColumnFormatOrder(std::vector<std::string>& outOrder)
343 {
344     outOrder.clear();
345     WaitingForInit();
346     UErrorCode status = U_ZERO_ERROR;
347 
348     auto patternGenerator = DateTimePatternGenerator::createInstance(locale_->instance, status);
349     CHECK_RETURN(status, false);
350     std::string format = "yyyyMMdd";
351     UnicodeString pattern = patternGenerator->getBestPattern(UnicodeString(format.c_str()), status);
352     delete patternGenerator;
353     CHECK_RETURN(status, false);
354 
355     std::string result;
356     UnicodeString2String(pattern, result);
357     LOGD("order of date format is %{public}s", result.c_str());
358 
359     std::map<std::size_t, std::string> order;
360     std::size_t position = result.find("yyyy");
361     if (position == std::string::npos) {
362         return false;
363     }
364     order[position] = "year";
365 
366     position = result.find("MM");
367     if (position == std::string::npos) {
368         return false;
369     }
370     order[position] = "month";
371 
372     position = result.find("dd");
373     if (position == std::string::npos) {
374         return false;
375     }
376     order[position] = "day";
377 
378     for (auto it = order.begin(); it != order.end(); ++it) {
379         outOrder.emplace_back(it->second);
380     }
381 
382     return true;
383 }
384 
Contain(const std::string & str,const std::string & tag)385 bool Localization::Contain(const std::string& str, const std::string& tag)
386 {
387     auto pos = str.find(tag);
388     return (pos != std::string::npos);
389 }
390 
GetHourFormat(bool & isAmPm,bool & hasZero)391 bool Localization::GetHourFormat(bool& isAmPm, bool& hasZero)
392 {
393     WaitingForInit();
394     UErrorCode status = U_ZERO_ERROR;
395 
396     auto patternGenerator = DateTimePatternGenerator::createInstance(locale_->instance, status);
397     CHECK_RETURN(status, false);
398     std::string format = "J:mm";
399     UnicodeString pattern = patternGenerator->getBestPattern(UnicodeString(format.c_str()), status);
400     delete patternGenerator;
401     CHECK_RETURN(status, false);
402 
403     std::string result;
404     UnicodeString2String(pattern, result);
405     LOGI("hour format is %{public}s", result.c_str());
406 
407     if (Contain(result, "hh") || Contain(result, "KK")) {
408         isAmPm = true;
409         hasZero = true;
410         return true;
411     }
412 
413     if (Contain(result, "h") || Contain(result, "K")) {
414         isAmPm = true;
415         hasZero = false;
416         return true;
417     }
418 
419     if (Contain(result, "HH") || Contain(result, "kk")) {
420         isAmPm = false;
421         hasZero = true;
422         return true;
423     }
424 
425     if (Contain(result, "H") || Contain(result, "k")) {
426         isAmPm = false;
427         hasZero = false;
428         return true;
429     }
430 
431     LOGE("hour format is unknown[%{public}s]", result.c_str());
432     return false;
433 }
434 
FormatDateTime(DateTime dateTime,DateTimeStyle dateStyle,DateTimeStyle timeStyle)435 const std::string Localization::FormatDateTime(DateTime dateTime, DateTimeStyle dateStyle, DateTimeStyle timeStyle)
436 {
437     WaitingForInit();
438     UErrorCode status = U_ZERO_ERROR;
439     auto cal = Calendar::createInstance(locale_->instance, status);
440     CHECK_RETURN(status, "");
441     cal->set(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second);
442 
443     UDate date = cal->getTime(status);
444     delete cal;
445     CHECK_RETURN(status, "");
446 
447     auto dateFormat = DateFormat::createDateTimeInstance(
448         DateTimeStyle2EStyle(dateStyle), DateTimeStyle2EStyle(timeStyle), locale_->instance);
449     if (dateFormat == nullptr) {
450         return "";
451     }
452 
453     UnicodeString dateTimeStr;
454     dateFormat->format(date, dateTimeStr, status);
455     delete dateFormat;
456     CHECK_RETURN(status, "");
457 
458     std::string ret;
459     UnicodeString2String(dateTimeStr, ret);
460     return ret;
461 }
462 
GetMonths(bool isShortType,const std::string & calendarType)463 std::vector<std::string> Localization::GetMonths(bool isShortType, const std::string& calendarType)
464 {
465     WaitingForInit();
466     std::vector<std::string> months;
467     UErrorCode status = U_ZERO_ERROR;
468     DateFormatSymbols dateformat(locale_->instance, calendarType.c_str(), status);
469     CHECK_RETURN(status, months);
470 
471     int32_t count = 0;
472 
473     auto monthsUniStr = dateformat.getMonths(count, DateFormatSymbols::DtContextType::STANDALONE,
474         isShortType ? DateFormatSymbols::DtWidthType::SHORT : DateFormatSymbols::DtWidthType::WIDE);
475     if (count > 0) {
476         std::string month;
477         for (int32_t i = 0; i < count; i++) {
478             month.clear();
479             UnicodeString2String(monthsUniStr[i], month);
480             months.push_back(month);
481         }
482     }
483     return months;
484 }
485 
GetWeekdays(bool isShortType)486 std::vector<std::string> Localization::GetWeekdays(bool isShortType)
487 {
488     WaitingForInit();
489     std::vector<std::string> weekdays;
490     UErrorCode status = U_ZERO_ERROR;
491     DateFormatSymbols dateformat(locale_->instance, status);
492     CHECK_RETURN(status, weekdays);
493 
494     int32_t count = 0;
495 
496     auto language = locale_->instance.getLanguage();
497     auto widthType = isShortType ? (strcmp(language, "zh") == 0 || strcmp(language, "bo") == 0)
498                                        ? DateFormatSymbols::DtWidthType::NARROW
499                                        : DateFormatSymbols::DtWidthType::ABBREVIATED
500                                  : DateFormatSymbols::DtWidthType::WIDE;
501     auto weekdaysUniStr = dateformat.getWeekdays(count, DateFormatSymbols::DtContextType::STANDALONE, widthType);
502     if (count > 0) {
503         std::string weekday;
504         for (int32_t i = 0; i < count; i++) {
505             weekday.clear();
506             UnicodeString2String(weekdaysUniStr[i], weekday);
507             if (!weekday.empty()) {
508                 weekdays.push_back(weekday);
509             }
510         }
511     }
512     return weekdays;
513 }
514 
GetAmPmStrings()515 std::vector<std::string> Localization::GetAmPmStrings()
516 {
517     WaitingForInit();
518     std::vector<std::string> amPms;
519     UErrorCode status = U_ZERO_ERROR;
520     DateFormatSymbols dateformat(locale_->instance, status);
521     CHECK_RETURN(status, amPms);
522 
523     int32_t count = 0;
524 
525     auto amPmUniStr = dateformat.getAmPmStrings(count);
526     if (count > 0) {
527         std::string amPm;
528         for (int32_t i = 0; i < count; i++) {
529             amPm.clear();
530             UnicodeString2String(amPmUniStr[i], amPm);
531             amPms.push_back(amPm);
532         }
533     }
534     return amPms;
535 }
536 
GetRelativeDateTime(double offset)537 std::string Localization::GetRelativeDateTime(double offset)
538 {
539     WaitingForInit();
540     UErrorCode status = U_ZERO_ERROR;
541     RelativeDateTimeFormatter relativeDateformat(locale_->instance, status);
542     CHECK_RETURN(status, "");
543 
544     UnicodeString relativeDate;
545     relativeDateformat.format(offset, URelativeDateTimeUnit::UDAT_REL_UNIT_DAY, relativeDate, status);
546     CHECK_RETURN(status, "");
547 
548     std::string ret;
549     UnicodeString2String(relativeDate, ret);
550     return ret;
551 }
552 
GetLunarDate(Date date)553 LunarDate Localization::GetLunarDate(Date date)
554 {
555     WaitingForInit();
556     LunarDate dateRet;
557     UErrorCode status = U_ZERO_ERROR;
558     Locale locale("zh", "CN");
559     auto cal = Calendar::createInstance(locale, status);
560     CHECK_RETURN(status, dateRet);
561     // 0 means January,  1 means February, so month - 1
562     cal->set(date.year, date.month - 1, date.day);
563 
564     UDate udate = cal->getTime(status);
565     delete cal;
566     CHECK_RETURN(status, dateRet);
567 
568     ChineseCalendar chineseCalendar(locale, status);
569     CHECK_RETURN(status, dateRet);
570 
571     chineseCalendar.setTime(udate, status);
572     CHECK_RETURN(status, dateRet);
573 
574     int32_t lunarYear = chineseCalendar.get(UCalendarDateFields::UCAL_YEAR, status);
575     CHECK_RETURN(status, dateRet);
576     int32_t lunarMonth = chineseCalendar.get(UCalendarDateFields::UCAL_MONTH, status);
577     CHECK_RETURN(status, dateRet);
578     int32_t lunarDate = chineseCalendar.get(UCalendarDateFields::UCAL_DATE, status);
579     CHECK_RETURN(status, dateRet);
580     int32_t isLeapMonth = chineseCalendar.get(UCalendarDateFields::UCAL_IS_LEAP_MONTH, status);
581     CHECK_RETURN(status, dateRet);
582 
583     // Sexagenary cycle years convert to Western years
584     dateRet.year = static_cast<uint32_t>(lunarYear) + GUIHAI_YEAR_RECENT;
585     dateRet.year +=
586         ((static_cast<uint32_t>(date.year) - GUIHAI_YEAR_RECENT) / SEXAGENARY_CYCLE_SIZE) * SEXAGENARY_CYCLE_SIZE;
587     // 0 means January,  1 means February, so month + 1
588     dateRet.month = static_cast<uint32_t>(lunarMonth) + 1;
589     dateRet.day = static_cast<uint32_t>(lunarDate);
590     dateRet.isLeapMonth = !(isLeapMonth == 0);
591     return dateRet;
592 }
593 
GetLunarMonth(uint32_t month,bool isLeapMonth)594 std::string Localization::GetLunarMonth(uint32_t month, bool isLeapMonth)
595 {
596     WaitingForInit();
597     std::vector<std::string> months = Localization::GetInstance()->GetMonths(false, "chinese");
598     if (month <= months.size() && month > 0) {
599         std::string leap;
600         if (isLeapMonth) {
601             leap += std::string(CHINESE_LEAP);
602         }
603         return leap + months[month - 1];
604     } else {
605         LOGE("month parameter is illegal:%{public}d", month);
606         return "";
607     }
608 }
609 
GetLunarDay(uint32_t dayOfMonth)610 std::string Localization::GetLunarDay(uint32_t dayOfMonth)
611 {
612     WaitingForInit();
613     if (dayOfMonth > 30 || dayOfMonth == 0) {
614         LOGE("dayOfMonth parameter is illegal");
615         return "";
616     }
617 
618     std::string ret;
619     if (dayOfMonth < 10) {
620         ret = std::string(CHINESE_FIRST) + std::string(g_chineseOneToNine[dayOfMonth - 1]);
621     } else if (dayOfMonth == 10) {
622         ret = std::string(CHINESE_FIRST) + std::string(CHINESE_TEN);
623     } else if (dayOfMonth < 20) {
624         ret = std::string(CHINESE_TEN) + std::string(g_chineseOneToNine[dayOfMonth - 11]);
625     } else if (dayOfMonth == 20) {
626         ret = std::string(CHINESE_TWENTY) + std::string(CHINESE_TEN);
627     } else if (dayOfMonth == 30) {
628         ret = g_chineseOneToNine[2] + std::string(CHINESE_TEN);
629     } else {
630         ret = std::string(CHINESE_TWENTY) + std::string(g_chineseOneToNine[dayOfMonth - 21]);
631     }
632 
633     return ret;
634 }
635 
TimeUnitFormat(double timeValue,TimeUnitStyle timeStyle,MeasureFormatStyle formatStyle)636 std::string Localization::TimeUnitFormat(double timeValue, TimeUnitStyle timeStyle, MeasureFormatStyle formatStyle)
637 {
638     WaitingForInit();
639     UErrorCode status = U_ZERO_ERROR;
640     MeasureFormat measureFormat(locale_->instance, GetMeasureFormatWidth(formatStyle), status);
641     CHECK_RETURN(status, "");
642 
643     MeasureUnit* minuteUnit = GetMeasureUnit(timeStyle, status);
644     CHECK_RETURN(status, "");
645 
646     Formattable formattable(timeValue);
647     Measure measure(formattable, minuteUnit, status);
648     CHECK_RETURN(status, "");
649 
650     UnicodeString timeUnit;
651     FieldPosition fieldPosition;
652     measureFormat.formatMeasures(&measure, 1, timeUnit, fieldPosition, status);
653     CHECK_RETURN(status, "");
654 
655     std::string ret;
656     UnicodeString2String(timeUnit, ret);
657     return ret;
658 }
659 
PluralRulesFormat(double number,bool isCardinal)660 std::string Localization::PluralRulesFormat(double number, bool isCardinal)
661 {
662     WaitingForInit();
663     UErrorCode status = U_ZERO_ERROR;
664     UPluralType pluralType = isCardinal ? UPluralType::UPLURAL_TYPE_CARDINAL : UPluralType::UPLURAL_TYPE_ORDINAL;
665     PluralRules* pluralRules = PluralRules::forLocale(locale_->instance, pluralType, status);
666     CHECK_RETURN(status, "");
667 
668     UnicodeString numberFormat = pluralRules->select(number);
669     delete pluralRules;
670 
671     std::string ret;
672     UnicodeString2String(numberFormat, ret);
673     return ret;
674 }
675 
NumberFormat(double number)676 std::string Localization::NumberFormat(double number)
677 {
678     WaitingForInit();
679     UErrorCode status = U_ZERO_ERROR;
680 
681     icu::number::LocalizedNumberFormatter formatter = icu::number::NumberFormatter::withLocale(locale_->instance);
682     icu::number::FormattedNumber formattedNumber = formatter.formatDouble(number, status);
683     CHECK_RETURN(status, "");
684 
685     UnicodeString numberFormat = formattedNumber.toString(status);
686     CHECK_RETURN(status, "");
687 
688     std::string ret;
689     UnicodeString2String(numberFormat, ret);
690     return ret;
691 }
692 
GetLetters(bool alphabet)693 std::vector<std::u16string> Localization::GetLetters(bool alphabet)
694 {
695     WaitingForInit();
696     std::vector<std::u16string> letters;
697     size_t size = 0;
698     const uint8_t* buf =
699         InternalResource::GetInstance().GetResource(InternalResource::ResourceId::INDEXLETTER_BAR_JSON, size);
700     if (buf == nullptr) {
701         return letters;
702     }
703 
704     std::string jsonStr(reinterpret_cast<const char*>(buf), size);
705     const char* endMsg = nullptr;
706     auto indexLetterJson = JsonUtil::ParseJsonString(jsonStr, &endMsg);
707     if (indexLetterJson == nullptr) {
708         LOGE("read indexletter json failed. reason: %{private}s.", endMsg);
709         return letters;
710     }
711 
712     std::string language = locale_->instance.getLanguage();
713     if (language == "zh") {
714         language = language + "-" + std::string(locale_->instance.getCountry());
715     }
716     auto iter = LANGUAGE_CODE_MAP.find(language);
717     if (iter != LANGUAGE_CODE_MAP.end()) {
718         language = iter->second;
719     }
720     LOGI("[alphabet] Localization::GetLetters. language: %{private}s", language.c_str());
721     std::unique_ptr<JsonValue> lettersSet;
722     if (!indexLetterJson->Contains(language) || !indexLetterJson->GetValue(language)->IsObject()) {
723         lettersSet = indexLetterJson->GetValue("default");
724     } else {
725         lettersSet = indexLetterJson->GetValue(language);
726     }
727 
728     std::string letterType = alphabet ? "alphabet" : "index";
729     std::unique_ptr<JsonValue> lettersArray;
730     if (!lettersSet->Contains(letterType) || !lettersSet->GetValue(letterType)->IsArray()) {
731         LOGE("read letter array failed. Invalid type. %s", letterType.c_str());
732         return letters;
733     } else {
734         lettersArray = lettersSet->GetValue(letterType)->GetChild();
735     }
736 
737     while (lettersArray->IsValid()) {
738         letters.push_back(StringUtils::Str8ToStr16(lettersArray->GetString()));
739         lettersArray = lettersArray->GetNext();
740     }
741     return letters;
742 }
743 
GetIndexLetter()744 std::vector<std::u16string> Localization::GetIndexLetter()
745 {
746     return GetLetters(false);
747 }
748 
GetIndexAlphabet()749 std::vector<std::u16string> Localization::GetIndexAlphabet()
750 {
751     return GetLetters(true);
752 }
753 
GetEntryLetters(const std::string & lettersIndex)754 std::string Localization::GetEntryLetters(const std::string& lettersIndex)
755 {
756     WaitingForInit();
757     if (lettersIndex.empty()) {
758         return "";
759     }
760 
761     std::unique_ptr<JsonValue> localJsonEntry;
762     auto language = selectLanguage_;
763     auto iter = LANGUAGE_CODE_MAP.find(language);
764     if (iter != LANGUAGE_CODE_MAP.end()) {
765         language = iter->second;
766     }
767     GetLocalJsonObject(InternalResource::ResourceId::ENTRY_JSON, language, g_indexJsonEntry, localJsonEntry);
768     if (localJsonEntry == nullptr) {
769         LOGE("read JsonObject fail. language: %{public}s.", selectLanguage_.c_str());
770         return "";
771     }
772 
773     std::vector<std::string> jsonLetterIndex;
774     StringUtils::StringSplitter(lettersIndex, JSON_PATH_CARVE, jsonLetterIndex);
775 
776     for (const auto& letter : jsonLetterIndex) {
777         if (localJsonEntry && localJsonEntry->Contains(letter)) {
778             localJsonEntry = localJsonEntry->GetValue(letter);
779         } else {
780             LOGE("read entry json failed.");
781             return "";
782         }
783     }
784 
785     if (localJsonEntry->IsString()) {
786         return localJsonEntry->GetString();
787     }
788 
789     return "";
790 }
791 
GetErrorDescription(const std::string & errorIndex)792 std::string Localization::GetErrorDescription(const std::string& errorIndex)
793 {
794     WaitingForInit();
795     if (errorIndex.empty()) {
796         return "";
797     }
798 
799     std::unique_ptr<JsonValue> localJsonError;
800     auto language = selectLanguage_;
801     auto iter = LANGUAGE_CODE_MAP.find(language);
802     if (iter != LANGUAGE_CODE_MAP.end()) {
803         language = iter->second;
804     }
805     GetLocalJsonObject(InternalResource::ResourceId::ERRORINFO_JSON, language, g_indexJsonError, localJsonError);
806     if (localJsonError == nullptr) {
807         LOGE("read JsonObject fail. language: %{public}s.", selectLanguage_.c_str());
808         return "";
809     }
810 
811     if (localJsonError->Contains(errorIndex)) {
812         localJsonError = localJsonError->GetValue(errorIndex);
813     } else {
814         LOGE("read error json failed. error path: %{private}s.", errorIndex.c_str());
815         return "";
816     }
817 
818     if (localJsonError->IsString()) {
819         return localJsonError->GetString();
820     }
821 
822     return "";
823 }
824 
GetLanguageList(const std::string & language)825 const std::vector<std::string>& Localization::GetLanguageList(const std::string& language)
826 {
827     static const LinearMapNode<std::vector<std::string>> multiLanguageMap[] = {
828         { "am", { "am" } },
829         { "ar", { "ar" } },
830         { "as", { "as" } },
831         { "az", { "az-AZ" } },
832         { "be", { "be" } },
833         { "bg", { "bg" } },
834         { "bn", { "bn" } },
835         { "bo", { "bo-CN" } },
836         { "bs", { "bs" } },
837         { "ca", { "ca" } },
838         { "cs", { "cs" } },
839         { "da", { "da" } },
840         { "de", { "de" } },
841         { "el", { "el" } },
842         { "en", { "en-US", "en-GB" } },
843         { "es", { "es,es-US" } },
844         { "et", { "et" } },
845         { "fa", { "fa" } },
846         { "fi", { "fi" } },
847         { "fil", { "fil" } },
848         { "fr", { "fr" } },
849         { "gl", { "gl-ES" } },
850         { "he", { "he" } },
851         { "hi", { "hi" } },
852         { "hr", { "hr" } },
853         { "hu", { "hu" } },
854         { "id", { "id" } },
855         { "in", { "in" } },
856         { "it", { "it" } },
857         { "iw", { "iw" } },
858         { "ja", { "ja" } },
859         { "jv", { "jv-Latn" } },
860         { "ka", { "ka-GE" } },
861         { "kk", { "kk-KZ" } },
862         { "km", { "km-KH" } },
863         { "kn", { "kn" } },
864         { "ko", { "ko" } },
865         { "lo", { "lo-LA" } },
866         { "lt", { "lt" } },
867         { "lv", { "lv" } },
868         { "mai", { "mai" } },
869         { "mi", { "mi" } },
870         { "mk", { "mk" } },
871         { "ml", { "ml" } },
872         { "mn", { "mn" } },
873         { "mr", { "mr" } },
874         { "ms", { "ms" } },
875         { "my", { "my-ZG", "my-MM" } },
876         { "nb", { "nb" } },
877         { "ne", { "ne" } },
878         { "nl", { "nl" } },
879         { "or", { "or" } },
880         { "pa", { "pa" } },
881         { "pl", { "pl" } },
882         { "pt", { "pt", "pt-PT" } },
883         { "ro", { "ro" } },
884         { "ru", { "ru" } },
885         { "si", { "si-LK" } },
886         { "sk", { "sk" } },
887         { "sl", { "sl" } },
888         { "sr", { "sr-Latn" } },
889         { "sv", { "sv" } },
890         { "sw", { "sw" } },
891         { "ta", { "ta" } },
892         { "te", { "te" } },
893         { "th", { "th" } },
894         { "tl", { "tl" } },
895         { "tr", { "tr" } },
896         { "ug", { "ug" } },
897         { "uk", { "uk" } },
898         { "ur", { "ur" } },
899         { "uz", { "uz-UZ" } },
900         { "vi", { "vi" } },
901         { "zh", { "zh-CN", "zh-HK", "zh-TW" } },
902         { "zz", { "zz-ZX" } },
903     };
904     int64_t list = BinarySearchFindIndex(multiLanguageMap, ArraySize(multiLanguageMap), language.c_str());
905     if (list == -1) {
906         static const std::vector<std::string> defaultLanguage = { "en-US" };
907         return defaultLanguage;
908     }
909     return multiLanguageMap[list].value;
910 }
911 
912 std::mutex Localization::mutex_;
913 std::shared_ptr<Localization> Localization::instance_;
914 bool Localization::firstInstance_ = true;
915 
GetInstance()916 std::shared_ptr<Localization> Localization::GetInstance()
917 {
918     std::lock_guard<std::mutex> lock(mutex_);
919     if (!instance_) {
920         instance_ = std::make_shared<Localization>();
921     }
922     return instance_;
923 }
924 
SetLocale(const std::string & language,const std::string & countryOrRegion,const std::string & script,const std::string & selectLanguage,const std::string & keywordsAndValues)925 void Localization::SetLocale(const std::string& language, const std::string& countryOrRegion, const std::string& script,
926     const std::string& selectLanguage, const std::string& keywordsAndValues)
927 {
928     if (instance_) {
929         instance_->HandleOnChange();
930         instance_->HandleOnMymrChange(script == "Qaag");
931     }
932     std::shared_ptr<Localization> instance;
933     {
934         std::lock_guard<std::mutex> lock(mutex_);
935         if (!firstInstance_ || !instance_) {
936             if (instance_) {
937                 auto onMymrChange = instance_->GetOnMymrChange();
938                 instance_ = std::make_shared<Localization>();
939                 instance_->SetOnMymrChange(onMymrChange);
940             } else {
941                 instance_ = std::make_shared<Localization>();
942             }
943         }
944         firstInstance_ = false;
945         instance = instance_;
946     }
947     instance->SetLocaleImpl(language, countryOrRegion, script, selectLanguage, keywordsAndValues);
948 }
949 
ComputeScript(const std::string & language,const std::string & region)950 std::string Localization::ComputeScript(const std::string& language, const std::string& region)
951 {
952     icu::Locale locale(language.c_str(), region.c_str());
953     UErrorCode status = U_ZERO_ERROR;
954     locale.addLikelySubtags(status);
955     if (status != U_ZERO_ERROR) {
956         return std::string();
957     }
958     return locale.getScript();
959 }
960 
ParseLocaleTag(const std::string & localeTag,std::string & language,std::string & script,std::string & region,bool needAddSubtags)961 void Localization::ParseLocaleTag(
962     const std::string& localeTag, std::string& language, std::string& script, std::string& region, bool needAddSubtags)
963 {
964     UErrorCode status = U_ZERO_ERROR;
965     icu::Locale locale = icu::Locale::forLanguageTag(icu::StringPiece(localeTag.c_str()), status);
966     if (needAddSubtags) {
967         locale.addLikelySubtags(status);
968     }
969     if (status != U_ZERO_ERROR) {
970         LOGE("This localeTag is not valid.");
971         return;
972     }
973     language = locale.getLanguage();
974     script = locale.getScript();
975     region = locale.getCountry();
976 }
977 
978 } // namespace OHOS::Ace
979