/* * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "base/i18n/localization.h" #include "chnsecal.h" #if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM) #include "lunar_calendar.h" #endif #include "unicode/dtfmtsym.h" #include "unicode/dtptngen.h" #include "unicode/measfmt.h" #include "unicode/numberformatter.h" #include "unicode/reldatefmt.h" #include "unicode/smpdtfmt.h" #include "date_time_sequence.h" #include "base/json/json_util.h" #include "base/resource/internal_resource.h" #include "base/utils/string_utils.h" #include "base/utils/utils.h" namespace OHOS::Ace { using namespace icu; struct LocaleProxy final { LocaleProxy(const char* language, const char* countryOrRegion, const char* variant, const char* keywordsAndValues) : instance(language, countryOrRegion, variant, keywordsAndValues) {} ~LocaleProxy() = default; Locale instance; }; namespace { #define CHECK_RETURN(status, ret) \ do { \ if ((status) > U_ZERO_ERROR) { \ LOGW("status = %{public}d", static_cast(status)); \ return (ret); \ } \ } while (0) #define CHECK_NO_RETURN(status) \ do { \ if ((status) > U_ZERO_ERROR) { \ LOGW("status = %{public}d", static_cast(status)); \ } \ } while (0) const char JSON_PATH_CARVE = '.'; const char DEFAULT_LANGUAGE[] = "en-US"; constexpr uint32_t SEXAGENARY_CYCLE_SIZE = 60; constexpr uint32_t GUIHAI_YEAR_RECENT = 3; constexpr uint32_t SECONDS_IN_HOUR = 3600; const char CHINESE_LEAP[] = u8"\u95f0"; const char CHINESE_FIRST[] = u8"\u521d"; const char CHINESE_TEN[] = u8"\u5341"; const char CHINESE_TWENTY[] = u8"\u5eff"; const char* g_chineseOneToNine[] = { u8"\u4e00", u8"\u4e8c", u8"\u4e09", u8"\u56db", u8"\u4e94", u8"\u516d", u8"\u4e03", u8"\u516b", u8"\u4e5d" }; const std::unordered_map LANGUAGE_CODE_MAP { { "he", "iw" }, { "fil", "tl" }, { "id", "in" }, }; inline void UnicodeString2String(const UnicodeString& source, std::string& result) { source.toUTF8String(result); } DateFormat::EStyle DateTimeStyle2EStyle(DateTimeStyle dateTimeStyle) { switch (dateTimeStyle) { case DateTimeStyle::NONE: return DateFormat::EStyle::kNone; case DateTimeStyle::FULL: return DateFormat::EStyle::kFull; case DateTimeStyle::LONG: return DateFormat::EStyle::kLong; case DateTimeStyle::MEDIUM: return DateFormat::EStyle::kMedium; case DateTimeStyle::SHORT: return DateFormat::EStyle::kShort; default: return DateFormat::EStyle::kNone; } } UMeasureFormatWidth GetMeasureFormatWidth(MeasureFormatStyle formatStyle) { switch (formatStyle) { case MeasureFormatStyle::WIDTH_WIDE: return UMeasureFormatWidth::UMEASFMT_WIDTH_WIDE; case MeasureFormatStyle::WIDTH_SHORT: return UMeasureFormatWidth::UMEASFMT_WIDTH_SHORT; case MeasureFormatStyle::WIDTH_NARROW: return UMeasureFormatWidth::UMEASFMT_WIDTH_NARROW; case MeasureFormatStyle::WIDTH_NUMERIC: return UMeasureFormatWidth::UMEASFMT_WIDTH_NUMERIC; case MeasureFormatStyle::WIDTH_COUNT: return UMeasureFormatWidth::UMEASFMT_WIDTH_COUNT; default: return UMeasureFormatWidth::UMEASFMT_WIDTH_WIDE; } } MeasureUnit* GetMeasureUnit(TimeUnitStyle timeUnitStyle, UErrorCode& status) { switch (timeUnitStyle) { case TimeUnitStyle::YEAR: return MeasureUnit::createYear(status); case TimeUnitStyle::MONTH: return MeasureUnit::createMonth(status); case TimeUnitStyle::DAY: return MeasureUnit::createDay(status); case TimeUnitStyle::HOUR: return MeasureUnit::createHour(status); case TimeUnitStyle::MINUTE: return MeasureUnit::createMinute(status); case TimeUnitStyle::SECOND: return MeasureUnit::createSecond(status); case TimeUnitStyle::MILLISECOND: return MeasureUnit::createMillisecond(status); default: return MeasureUnit::createYear(status); } } void GetLocalJsonObject(InternalResource::ResourceId id, std::string language, std::unique_ptr& indexJson, std::unique_ptr& json) { if (indexJson == nullptr) { size_t size = 0; const uint8_t* buf = InternalResource::GetInstance().GetResource(id, size); if (buf == nullptr) { return; } std::string jsonStr(reinterpret_cast(buf), size); const char* endMsg = nullptr; indexJson = JsonUtil::ParseJsonString(jsonStr, &endMsg); if (indexJson == nullptr) { LOGE("read indexletter json failed. reason: %{private}s.", endMsg); return; } } if (indexJson->Contains(language) && indexJson->GetValue(language)->IsObject()) { json = indexJson->GetValue(language); } else if (indexJson->Contains(DEFAULT_LANGUAGE) && indexJson->GetValue(DEFAULT_LANGUAGE)->IsObject()) { json = indexJson->GetValue(DEFAULT_LANGUAGE); } } } // namespace // for entry.json static std::unique_ptr g_indexJsonEntry = nullptr; static std::unique_ptr g_indexJsonError = nullptr; Localization::~Localization() = default; void Localization::SetLocaleImpl(const std::string& language, const std::string& countryOrRegion, const std::string& script, const std::string& selectLanguage, const std::string& keywordsAndValues) { locale_ = std::make_unique(language.c_str(), countryOrRegion.c_str(), "", keywordsAndValues.c_str()); UErrorCode status = U_ZERO_ERROR; std::vector keyValuePairs; StringUtils::StringSplitter(keywordsAndValues, ';', keyValuePairs); for (const auto& pair : keyValuePairs) { // [pair] is like "nu=arab" or "nu=" for most occasions, but may be "=" under extreme scenarios std::vector res; StringUtils::StringSplitter(pair, '=', res); if (res.size() == 0) { continue; } auto value = (res.size() == 2) ? res[1] : ""; locale_->instance.setUnicodeKeywordValue(res[0], value, status); CHECK_NO_RETURN(status); } languageTag_ = language; if (!script.empty()) { languageTag_.append("-").append(script); } if (!countryOrRegion.empty()) { languageTag_.append("-").append(countryOrRegion); } fontLocale_ = languageTag_; // Simple chinese if (languageTag_ == "zh-Hans-CN") { languageTag_ = "zh-CN"; fontLocale_ = ""; } selectLanguage_ = selectLanguage; // match json of latin if (selectLanguage_ == "jv-Latn") { selectLanguage_ = "b+jv+Latn"; } else if (selectLanguage_ == "sr-Latn") { selectLanguage_ = "b+sr+Latn"; } LOGI("SetLocale language tag: %{public}s, select language: %{public}s", languageTag_.c_str(), selectLanguage_.c_str()); if (!isPromiseUsed_) { promise_.set_value(true); isPromiseUsed_ = true; } } std::string Localization::GetLanguage() { WaitingForInit(); if (locale_) { return locale_->instance.getLanguage(); } return ""; } std::string Localization::GetLanguageTag() { WaitingForInit(); return languageTag_; } std::string Localization::GetFontLocale() { WaitingForInit(); return fontLocale_; } const std::string Localization::FormatDuration(uint32_t duration, bool needShowHour) { WaitingForInit(); UErrorCode status = U_ZERO_ERROR; // duration greater than 1 hour, use HH:mm:ss; if (!needShowHour && duration > SECONDS_IN_HOUR) { needShowHour = true; } const char* engTimeFormat = needShowHour ? "HH:mm:ss" : "mm:ss"; auto simpleDateFormat = std::make_unique(UnicodeString(engTimeFormat), locale_->instance, status); CHECK_RETURN(status, ""); TimeZone* timeZone = TimeZone::createTimeZone("GMT+0:00"); simpleDateFormat->setTimeZone(*timeZone); UnicodeString simpleStr; simpleDateFormat->format(1000.0 * duration, simpleStr, status); delete(timeZone); CHECK_RETURN(status, ""); std::string ret; UnicodeString2String(simpleStr, ret); return ret; } std::string Localization::FormatDuration(uint32_t duration, const std::string& format) { WaitingForInit(); UErrorCode status = U_ZERO_ERROR; const char* engTimeFormat = format.c_str(); auto simpleDateFormat = std::make_unique(UnicodeString(engTimeFormat), locale_->instance, status); CHECK_RETURN(status, ""); TimeZone* timeZone = TimeZone::createTimeZone("GMT+0:00"); simpleDateFormat->setTimeZone(*timeZone); UnicodeString simpleStr; simpleDateFormat->format(1.0 * duration, simpleStr, status); CHECK_RETURN(status, ""); std::string ret; UnicodeString2String(simpleStr, ret); delete(timeZone); return ret; } const std::string Localization::FormatDateTime(DateTime dateTime, const std::string& format) { WaitingForInit(); UErrorCode status = U_ZERO_ERROR; auto cal = Calendar::createInstance(locale_->instance, status); CHECK_RETURN(status, ""); cal->set(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second); UDate date = cal->getTime(status); delete cal; CHECK_RETURN(status, ""); auto patternGenerator = DateTimePatternGenerator::createInstance(locale_->instance, status); CHECK_RETURN(status, ""); UnicodeString pattern = patternGenerator->getBestPattern(UnicodeString(format.c_str()), status); delete patternGenerator; CHECK_RETURN(status, ""); auto dateFormat = std::make_unique(pattern, locale_->instance, status); CHECK_RETURN(status, ""); UnicodeString dateTimeStr; dateFormat->format(date, dateTimeStr, status); CHECK_RETURN(status, ""); std::string ret; UnicodeString2String(dateTimeStr, ret); return ret; } bool Localization::GetDateColumnFormatOrder(std::vector& outOrder) { outOrder.clear(); WaitingForInit(); UErrorCode status = U_ZERO_ERROR; auto patternGenerator = DateTimePatternGenerator::createInstance(locale_->instance, status); CHECK_RETURN(status, false); std::string format = "yyyyMMdd"; UnicodeString pattern = patternGenerator->getBestPattern(UnicodeString(format.c_str()), status); delete patternGenerator; CHECK_RETURN(status, false); std::string result; UnicodeString2String(pattern, result); std::map order; std::size_t position = result.find("yyyy"); if (position == std::string::npos) { return false; } order[position] = "year"; position = result.find("MM"); if (position == std::string::npos) { return false; } order[position] = "month"; position = result.find("dd"); if (position == std::string::npos) { return false; } order[position] = "day"; for (auto it = order.begin(); it != order.end(); ++it) { outOrder.emplace_back(it->second); } return true; } bool Localization::GetDateOrder(std::vector& outOrder) { std::string dateOrder; DateTimeSequence sequence; std::string language = locale_->instance.getLanguage(); OrderResult orderResult = sequence.GetDateOrder(language); dateOrder = orderResult.dateOrder; std::map order; std::size_t position = dateOrder.find("y"); if (position == std::string::npos) { return false; } order[position] = "year"; position = dateOrder.find("M"); if (position == std::string::npos) { return false; } order[position] = "month"; position = dateOrder.find("d"); if (position == std::string::npos) { return false; } order[position] = "day"; for (auto it = order.begin(); it != order.end(); ++it) { outOrder.emplace_back(it->second); } return true; } bool Localization::Contain(const std::string& str, const std::string& tag) { auto pos = str.find(tag); return (pos != std::string::npos); } bool Localization::GetHourFormat(bool& isAmPm, bool& hasZero) { WaitingForInit(); UErrorCode status = U_ZERO_ERROR; auto patternGenerator = DateTimePatternGenerator::createInstance(locale_->instance, status); CHECK_RETURN(status, false); std::string format = "J:mm"; UnicodeString pattern = patternGenerator->getBestPattern(UnicodeString(format.c_str()), status); delete patternGenerator; CHECK_RETURN(status, false); std::string result; UnicodeString2String(pattern, result); if (Contain(result, "hh") || Contain(result, "KK")) { isAmPm = true; hasZero = true; return true; } if (Contain(result, "h") || Contain(result, "K")) { isAmPm = true; hasZero = false; return true; } if (Contain(result, "HH") || Contain(result, "kk")) { isAmPm = false; hasZero = true; return true; } if (Contain(result, "H") || Contain(result, "k")) { isAmPm = false; hasZero = false; return true; } return false; } const std::string Localization::FormatDateTime(DateTime dateTime, DateTimeStyle dateStyle, DateTimeStyle timeStyle) { WaitingForInit(); UErrorCode status = U_ZERO_ERROR; auto cal = Calendar::createInstance(locale_->instance, status); CHECK_RETURN(status, ""); cal->set(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second); UDate date = cal->getTime(status); delete cal; CHECK_RETURN(status, ""); auto dateFormat = DateFormat::createDateTimeInstance( DateTimeStyle2EStyle(dateStyle), DateTimeStyle2EStyle(timeStyle), locale_->instance); if (dateFormat == nullptr) { return ""; } UnicodeString dateTimeStr; dateFormat->format(date, dateTimeStr, status); delete dateFormat; CHECK_RETURN(status, ""); std::string ret; UnicodeString2String(dateTimeStr, ret); return ret; } std::vector Localization::GetMonths(bool isShortType, const std::string& calendarType) { WaitingForInit(); std::vector months; UErrorCode status = U_ZERO_ERROR; DateFormatSymbols dateformat(locale_->instance, calendarType.c_str(), status); CHECK_RETURN(status, months); int32_t count = 0; auto monthsUniStr = dateformat.getMonths(count, DateFormatSymbols::DtContextType::STANDALONE, isShortType ? DateFormatSymbols::DtWidthType::SHORT : DateFormatSymbols::DtWidthType::WIDE); if (count > 0) { std::string month; for (int32_t i = 0; i < count; i++) { month.clear(); UnicodeString2String(monthsUniStr[i], month); months.push_back(month); } } return months; } std::vector Localization::GetWeekdays(bool isShortType) { WaitingForInit(); std::vector weekdays; UErrorCode status = U_ZERO_ERROR; DateFormatSymbols dateformat(locale_->instance, status); CHECK_RETURN(status, weekdays); int32_t count = 0; auto language = locale_->instance.getLanguage(); auto widthType = isShortType ? (strcmp(language, "zh") == 0 || strcmp(language, "bo") == 0) ? DateFormatSymbols::DtWidthType::NARROW : DateFormatSymbols::DtWidthType::ABBREVIATED : DateFormatSymbols::DtWidthType::WIDE; auto weekdaysUniStr = dateformat.getWeekdays(count, DateFormatSymbols::DtContextType::STANDALONE, widthType); if (count > 0) { std::string weekday; for (int32_t i = 0; i < count; i++) { weekday.clear(); UnicodeString2String(weekdaysUniStr[i], weekday); if (!weekday.empty()) { weekdays.push_back(weekday); } } } return weekdays; } std::vector Localization::GetAmPmStrings() { WaitingForInit(); std::vector amPms; UErrorCode status = U_ZERO_ERROR; DateFormatSymbols dateformat(locale_->instance, status); CHECK_RETURN(status, amPms); int32_t count = 0; auto amPmUniStr = dateformat.getAmPmStrings(count); if (count > 0) { std::string amPm; for (int32_t i = 0; i < count; i++) { amPm.clear(); UnicodeString2String(amPmUniStr[i], amPm); amPms.push_back(amPm); } } return amPms; } std::string Localization::GetRelativeDateTime(double offset) { WaitingForInit(); UErrorCode status = U_ZERO_ERROR; RelativeDateTimeFormatter relativeDateformat(locale_->instance, status); CHECK_RETURN(status, ""); UnicodeString relativeDate; relativeDateformat.format(offset, URelativeDateTimeUnit::UDAT_REL_UNIT_DAY, relativeDate, status); CHECK_RETURN(status, ""); std::string ret; UnicodeString2String(relativeDate, ret); return ret; } LunarDate Localization::GetLunarDate(Date date) { WaitingForInit(); #if defined(IOS_PLATFORM) || defined(ANDROID_PLATFORM) return GetIcuLunarDate(date); #else LunarDate dateRet; auto chineseCalendar = std::make_unique(); CHECK_NULL_RETURN(chineseCalendar, dateRet); chineseCalendar->SetGregorianDate(date.year, date.month, date.day); int32_t lunarYear = chineseCalendar->GetLunarYear(); CHECK_EQUAL_RETURN(lunarYear, -1, dateRet); int32_t lunarMonth = chineseCalendar->GetLunarMonth(); CHECK_EQUAL_RETURN(lunarMonth, -1, dateRet); int32_t lunarDay = chineseCalendar->GetLunarDay(); CHECK_EQUAL_RETURN(lunarDay, -1, dateRet); dateRet.year = static_cast(lunarYear); dateRet.month = static_cast(lunarMonth); dateRet.day = static_cast(lunarDay); dateRet.isLeapMonth = chineseCalendar->IsLeapMonth(); return dateRet; #endif } LunarDate Localization::GetIcuLunarDate(Date date) { LunarDate dateRet; UErrorCode status = U_ZERO_ERROR; Locale locale("zh", "CN"); auto cal = Calendar::createInstance(locale, status); CHECK_RETURN(status, dateRet); // 0 means January, 1 means February, so month - 1 if (date.month == 0u) { date.month = 11u; cal->set(date.year, date.month, date.day); } else { cal->set(date.year, date.month - 1u, date.day); } UDate udate = cal->getTime(status); delete cal; CHECK_RETURN(status, dateRet); ChineseCalendar chineseCalendar(locale, status); CHECK_RETURN(status, dateRet); chineseCalendar.setTime(udate, status); CHECK_RETURN(status, dateRet); int32_t lunarYear = chineseCalendar.get(UCalendarDateFields::UCAL_YEAR, status); CHECK_RETURN(status, dateRet); int32_t lunarMonth = chineseCalendar.get(UCalendarDateFields::UCAL_MONTH, status); CHECK_RETURN(status, dateRet); int32_t lunarDate = chineseCalendar.get(UCalendarDateFields::UCAL_DATE, status); CHECK_RETURN(status, dateRet); int32_t isLeapMonth = chineseCalendar.get(UCalendarDateFields::UCAL_IS_LEAP_MONTH, status); CHECK_RETURN(status, dateRet); // Sexagenary cycle years convert to Western years bool repeatCalc = false; if ((static_cast(date.year) - GUIHAI_YEAR_RECENT) % SEXAGENARY_CYCLE_SIZE == 0 || static_cast(lunarYear) == SEXAGENARY_CYCLE_SIZE) { repeatCalc = true; } dateRet.year = static_cast(lunarYear) + GUIHAI_YEAR_RECENT; dateRet.year += ((static_cast(date.year) - GUIHAI_YEAR_RECENT) / SEXAGENARY_CYCLE_SIZE - (repeatCalc ? 1 : 0)) * SEXAGENARY_CYCLE_SIZE; // 0 means January, 1 means February, so month + 1 dateRet.month = static_cast(lunarMonth) + 1; dateRet.day = static_cast(lunarDate); dateRet.isLeapMonth = !(isLeapMonth == 0); return dateRet; } std::string Localization::GetLunarMonth(uint32_t month, bool isLeapMonth) { WaitingForInit(); std::vector months = Localization::GetInstance()->GetMonths(false, "chinese"); if (month <= months.size() && month > 0) { std::string leap; if (isLeapMonth) { leap += std::string(CHINESE_LEAP); } return leap + months[month - 1]; } else { return ""; } } std::string Localization::GetLunarDay(uint32_t dayOfMonth) { WaitingForInit(); if (dayOfMonth > 30 || dayOfMonth == 0) { return ""; } std::string ret; if (dayOfMonth < 10) { ret = std::string(CHINESE_FIRST) + std::string(g_chineseOneToNine[dayOfMonth - 1]); } else if (dayOfMonth == 10) { ret = std::string(CHINESE_FIRST) + std::string(CHINESE_TEN); } else if (dayOfMonth < 20) { ret = std::string(CHINESE_TEN) + std::string(g_chineseOneToNine[dayOfMonth - 11]); } else if (dayOfMonth == 20) { ret = std::string(CHINESE_TWENTY) + std::string(CHINESE_TEN); } else if (dayOfMonth == 30) { ret = g_chineseOneToNine[2] + std::string(CHINESE_TEN); } else { ret = std::string(CHINESE_TWENTY) + std::string(g_chineseOneToNine[dayOfMonth - 21]); } return ret; } std::string Localization::TimeUnitFormat(double timeValue, TimeUnitStyle timeStyle, MeasureFormatStyle formatStyle) { WaitingForInit(); UErrorCode status = U_ZERO_ERROR; MeasureFormat measureFormat(locale_->instance, GetMeasureFormatWidth(formatStyle), status); CHECK_RETURN(status, ""); MeasureUnit* minuteUnit = GetMeasureUnit(timeStyle, status); CHECK_RETURN(status, ""); Formattable formattable(timeValue); Measure measure(formattable, minuteUnit, status); CHECK_RETURN(status, ""); UnicodeString timeUnit; FieldPosition fieldPosition; measureFormat.formatMeasures(&measure, 1, timeUnit, fieldPosition, status); CHECK_RETURN(status, ""); std::string ret; UnicodeString2String(timeUnit, ret); return ret; } std::string Localization::PluralRulesFormat(double number, bool isCardinal) { WaitingForInit(); UErrorCode status = U_ZERO_ERROR; UPluralType pluralType = isCardinal ? UPluralType::UPLURAL_TYPE_CARDINAL : UPluralType::UPLURAL_TYPE_ORDINAL; PluralRules* pluralRules = PluralRules::forLocale(locale_->instance, pluralType, status); CHECK_RETURN(status, ""); UnicodeString numberFormat = pluralRules->select(number); delete pluralRules; std::string ret; UnicodeString2String(numberFormat, ret); return ret; } std::string Localization::NumberFormat(double number) { WaitingForInit(); UErrorCode status = U_ZERO_ERROR; icu::number::LocalizedNumberFormatter formatter = icu::number::NumberFormatter::withLocale(locale_->instance); icu::number::FormattedNumber formattedNumber = formatter.formatDouble(number, status); CHECK_RETURN(status, ""); UnicodeString numberFormat = formattedNumber.toString(status); CHECK_RETURN(status, ""); std::string ret; UnicodeString2String(numberFormat, ret); return ret; } std::vector Localization::GetLetters(bool alphabet) { WaitingForInit(); std::vector letters; size_t size = 0; const uint8_t* buf = InternalResource::GetInstance().GetResource(InternalResource::ResourceId::INDEXLETTER_BAR_JSON, size); if (buf == nullptr) { return letters; } std::string jsonStr(reinterpret_cast(buf), size); const char* endMsg = nullptr; auto indexLetterJson = JsonUtil::ParseJsonString(jsonStr, &endMsg); if (indexLetterJson == nullptr) { return letters; } std::string language = locale_->instance.getLanguage(); if (language == "zh") { language = language + "-" + std::string(locale_->instance.getCountry()); } auto iter = LANGUAGE_CODE_MAP.find(language); if (iter != LANGUAGE_CODE_MAP.end()) { language = iter->second; } LOGI("[alphabet] Localization::GetLetters. language: %{private}s", language.c_str()); std::unique_ptr lettersSet; if (!indexLetterJson->Contains(language) || !indexLetterJson->GetValue(language)->IsObject()) { lettersSet = indexLetterJson->GetValue("default"); } else { lettersSet = indexLetterJson->GetValue(language); } std::string letterType = alphabet ? "alphabet" : "index"; std::unique_ptr lettersArray; if (!lettersSet->Contains(letterType) || !lettersSet->GetValue(letterType)->IsArray()) { return letters; } else { lettersArray = lettersSet->GetValue(letterType)->GetChild(); } while (lettersArray->IsValid()) { letters.push_back(StringUtils::Str8ToStr16(lettersArray->GetString())); lettersArray = lettersArray->GetNext(); } return letters; } std::vector Localization::GetIndexLetter() { return GetLetters(false); } std::vector Localization::GetIndexAlphabet() { return GetLetters(true); } std::string Localization::GetEntryLetters(const std::string& lettersIndex) { WaitingForInit(); if (lettersIndex.empty()) { return ""; } std::unique_ptr localJsonEntry; auto language = selectLanguage_; auto iter = LANGUAGE_CODE_MAP.find(language); if (iter != LANGUAGE_CODE_MAP.end()) { language = iter->second; } GetLocalJsonObject(InternalResource::ResourceId::ENTRY_JSON, language, g_indexJsonEntry, localJsonEntry); if (localJsonEntry == nullptr) { LOGW("read JsonObject fail. language: %{public}s.", selectLanguage_.c_str()); return ""; } std::vector jsonLetterIndex; StringUtils::StringSplitter(lettersIndex, JSON_PATH_CARVE, jsonLetterIndex); for (const auto& letter : jsonLetterIndex) { if (localJsonEntry && localJsonEntry->Contains(letter)) { localJsonEntry = localJsonEntry->GetValue(letter); } else { return ""; } } if (localJsonEntry->IsString()) { return localJsonEntry->GetString(); } return ""; } std::string Localization::GetErrorDescription(const std::string& errorIndex) { WaitingForInit(); if (errorIndex.empty()) { return ""; } std::unique_ptr localJsonError; auto language = selectLanguage_; auto iter = LANGUAGE_CODE_MAP.find(language); if (iter != LANGUAGE_CODE_MAP.end()) { language = iter->second; } GetLocalJsonObject(InternalResource::ResourceId::ERRORINFO_JSON, language, g_indexJsonError, localJsonError); if (localJsonError == nullptr) { LOGW("read JsonObject fail. language: %{public}s.", selectLanguage_.c_str()); return ""; } if (localJsonError->Contains(errorIndex)) { localJsonError = localJsonError->GetValue(errorIndex); } else { LOGW("read error json failed. error path: %{private}s.", errorIndex.c_str()); return ""; } if (localJsonError->IsString()) { return localJsonError->GetString(); } return ""; } const std::vector& Localization::GetLanguageList(const std::string& language) { static const LinearMapNode> multiLanguageMap[] = { { "am", { "am" } }, { "ar", { "ar" } }, { "as", { "as" } }, { "az", { "az-AZ" } }, { "be", { "be" } }, { "bg", { "bg" } }, { "bn", { "bn" } }, { "bo", { "bo-CN" } }, { "bs", { "bs" } }, { "ca", { "ca" } }, { "cs", { "cs" } }, { "da", { "da" } }, { "de", { "de" } }, { "el", { "el" } }, { "en", { "en-US", "en-GB" } }, { "es", { "es,es-US" } }, { "et", { "et" } }, { "fa", { "fa" } }, { "fi", { "fi" } }, { "fil", { "fil" } }, { "fr", { "fr" } }, { "gl", { "gl-ES" } }, { "he", { "he" } }, { "hi", { "hi" } }, { "hr", { "hr" } }, { "hu", { "hu" } }, { "id", { "id" } }, { "in", { "in" } }, { "it", { "it" } }, { "iw", { "iw" } }, { "ja", { "ja" } }, { "jv", { "jv-Latn" } }, { "ka", { "ka-GE" } }, { "kk", { "kk-KZ" } }, { "km", { "km-KH" } }, { "kn", { "kn" } }, { "ko", { "ko" } }, { "lo", { "lo-LA" } }, { "lt", { "lt" } }, { "lv", { "lv" } }, { "mai", { "mai" } }, { "mi", { "mi" } }, { "mk", { "mk" } }, { "ml", { "ml" } }, { "mn", { "mn" } }, { "mr", { "mr" } }, { "ms", { "ms" } }, { "my", { "my-ZG", "my-MM" } }, { "nb", { "nb" } }, { "ne", { "ne" } }, { "nl", { "nl" } }, { "or", { "or" } }, { "pa", { "pa" } }, { "pl", { "pl" } }, { "pt", { "pt", "pt-PT" } }, { "ro", { "ro" } }, { "ru", { "ru" } }, { "si", { "si-LK" } }, { "sk", { "sk" } }, { "sl", { "sl" } }, { "sr", { "sr-Latn" } }, { "sv", { "sv" } }, { "sw", { "sw" } }, { "ta", { "ta" } }, { "te", { "te" } }, { "th", { "th" } }, { "tl", { "tl" } }, { "tr", { "tr" } }, { "ug", { "ug" } }, { "uk", { "uk" } }, { "ur", { "ur" } }, { "uz", { "uz-UZ" } }, { "vi", { "vi" } }, { "zh", { "zh-CN", "zh-HK", "zh-TW" } }, { "zz", { "zz-ZX" } }, }; int64_t list = BinarySearchFindIndex(multiLanguageMap, ArraySize(multiLanguageMap), language.c_str()); if (list == -1) { static const std::vector defaultLanguage = { "en-US" }; return defaultLanguage; } return multiLanguageMap[list].value; } std::mutex Localization::mutex_; std::shared_ptr Localization::instance_; bool Localization::firstInstance_ = true; std::shared_ptr Localization::GetInstance() { std::lock_guard lock(mutex_); if (!instance_) { instance_ = std::make_shared(); } return instance_; } void Localization::SetLocale(const std::string& language, const std::string& countryOrRegion, const std::string& script, const std::string& selectLanguage, const std::string& keywordsAndValues) { std::lock_guard lock(mutex_); if (instance_) { instance_->HandleOnChange(); instance_->HandleOnMymrChange(script == "Qaag"); } std::shared_ptr instance; if (!firstInstance_ || !instance_) { if (instance_) { auto onMymrChange = instance_->GetOnMymrChange(); instance_ = std::make_shared(); instance_->SetOnMymrChange(onMymrChange); } else { instance_ = std::make_shared(); } } firstInstance_ = false; instance = instance_; instance->SetLocaleImpl(language, countryOrRegion, script, selectLanguage, keywordsAndValues); } std::string Localization::ComputeScript(const std::string& language, const std::string& region) { icu::Locale locale(language.c_str(), region.c_str()); UErrorCode status = U_ZERO_ERROR; locale.addLikelySubtags(status); if (status != U_ZERO_ERROR) { return std::string(); } return locale.getScript(); } void Localization::ParseLocaleTag( const std::string& localeTag, std::string& language, std::string& script, std::string& region, bool needAddSubtags) { UErrorCode status = U_ZERO_ERROR; icu::Locale locale = icu::Locale::forLanguageTag(icu::StringPiece(localeTag.c_str()), status); if (needAddSubtags) { locale.addLikelySubtags(status); } if (status != U_ZERO_ERROR) { return; } language = locale.getLanguage(); script = locale.getScript(); region = locale.getCountry(); } } // namespace OHOS::Ace