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