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