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