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