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