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