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