/* * 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 "locale_config.h" #include "ohos/init_data.h" #include "utils.h" #include "parameter.h" #include "relative_time_format.h" namespace OHOS { namespace Global { namespace I18n { const char* RelativeTimeFormat::DEVICE_TYPE_NAME = "const.product.devicetype"; std::unordered_map<std::string, UDateRelativeDateTimeFormatterStyle> RelativeTimeFormat::relativeFormatStyle = { { "long", UDAT_STYLE_LONG }, { "short", UDAT_STYLE_SHORT }, { "narrow", UDAT_STYLE_NARROW } }; std::unordered_map<std::string, std::string> RelativeTimeFormat::defaultFormatStyle = { { "wearable", "narrow" }, { "liteWearable", "narrow" }, { "watch", "narrow" } }; std::unordered_map<std::string, URelativeDateTimeUnit> RelativeTimeFormat::relativeUnits = { { "second", UDAT_REL_UNIT_SECOND }, { "seconds", UDAT_REL_UNIT_SECOND }, { "minute", UDAT_REL_UNIT_MINUTE }, { "minutes", UDAT_REL_UNIT_MINUTE }, { "hour", UDAT_REL_UNIT_HOUR }, { "hours", UDAT_REL_UNIT_HOUR }, { "day", UDAT_REL_UNIT_DAY }, { "days", UDAT_REL_UNIT_DAY }, { "week", UDAT_REL_UNIT_WEEK }, { "weeks", UDAT_REL_UNIT_WEEK }, { "month", UDAT_REL_UNIT_MONTH }, { "months", UDAT_REL_UNIT_MONTH }, { "quarter", UDAT_REL_UNIT_QUARTER }, { "quarters", UDAT_REL_UNIT_QUARTER }, { "year", UDAT_REL_UNIT_YEAR }, { "years", UDAT_REL_UNIT_YEAR }, }; RelativeTimeFormat::RelativeTimeFormat(const std::vector<std::string> &localeTags, std::map<std::string, std::string> &configs) { SetDefaultStyle(); UErrorCode status = U_ZERO_ERROR; ParseConfigs(configs); for (size_t i = 0; i < localeTags.size(); i++) { std::string curLocale = localeTags[i]; locale = icu::Locale::forLanguageTag(icu::StringPiece(curLocale), status); if (status != U_ZERO_ERROR) { status = U_ZERO_ERROR; continue; } if (LocaleInfo::allValidLocales.count(locale.getLanguage()) > 0) { localeInfo = std::make_unique<LocaleInfo>(curLocale, configs); if (!localeInfo->InitSuccess()) { continue; } locale = localeInfo->GetLocale(); localeBaseName = localeInfo->GetBaseName(); relativeTimeFormat = std::make_unique<icu::RelativeDateTimeFormatter>(locale, nullptr, style, UDISPCTX_CAPITALIZATION_NONE, status); if (!U_SUCCESS(status)) { status = U_ZERO_ERROR; continue; } createSuccess = true; break; } } if (!createSuccess) { localeInfo = std::make_unique<LocaleInfo>(LocaleConfig::GetSystemLocale(), configs); if (localeInfo->InitSuccess()) { locale = localeInfo->GetLocale(); localeBaseName = localeInfo->GetBaseName(); relativeTimeFormat = std::make_unique<icu::RelativeDateTimeFormatter>(locale, nullptr, style, UDISPCTX_CAPITALIZATION_NONE, status); if (U_SUCCESS(status)) { createSuccess = true; } } } numberingSystem = localeInfo->GetNumberingSystem(); if (numberingSystem == "") { numberingSystem = "latn"; } } RelativeTimeFormat::~RelativeTimeFormat() { } void RelativeTimeFormat::ParseConfigs(std::map<std::string, std::string> &configs) { if (configs.count("style") > 0) { styleString = configs["style"]; } if (relativeFormatStyle.count(styleString) > 0) { style = relativeFormatStyle[styleString]; } if (configs.count("numeric") > 0) { numeric = configs["numeric"]; } } std::string RelativeTimeFormat::Format(double number, const std::string &unit) { if (!createSuccess || !relativeUnits.count(unit)) { return PseudoLocalizationProcessor(""); } UErrorCode status = U_ZERO_ERROR; icu::UnicodeString formattedTime; std::string result; if (!strcmp(numeric.c_str(), "always")) { formattedTime = relativeTimeFormat->formatNumericToValue(number, relativeUnits[unit], status).toString(status); } else { formattedTime = relativeTimeFormat->formatToValue(number, relativeUnits[unit], status).toString(status); } formattedTime.toUTF8String(result); return PseudoLocalizationProcessor(result); } void RelativeTimeFormat::InsertInfo(std::vector<std::vector<std::string>> &timeVector, const std::string &unit, bool isInteger, const std::string &value) { std::vector<std::string> info; if (isInteger) { info.push_back("integer"); info.push_back(value); info.push_back(unit); } else { info.push_back("literal"); info.push_back(value); } timeVector.push_back(info); } void RelativeTimeFormat::ProcessIntegerField(const std::map<size_t, size_t> &indexMap, std::vector<std::vector<std::string>> &timeVector, size_t &startIndex, const std::string &unit, const std::string &result) { for (auto iter = indexMap.begin(); iter != indexMap.end(); iter++) { if (iter->first > startIndex) { InsertInfo(timeVector, unit, true, result.substr(startIndex, iter->first - startIndex)); InsertInfo(timeVector, unit, true, result.substr(iter->first, iter->second - iter->first)); startIndex = iter->second; } } } void RelativeTimeFormat::FormatToParts(double number, const std::string &unit, std::vector<std::vector<std::string>> &timeVector) { if (!createSuccess || !relativeUnits.count(unit)) { return; } UErrorCode status = U_ZERO_ERROR; std::string result; icu::FormattedRelativeDateTime fmtRelativeTime; if (numeric.empty() || !strcmp(numeric.c_str(), "always")) { fmtRelativeTime = relativeTimeFormat->formatNumericToValue(number, relativeUnits[unit], status); } else { fmtRelativeTime = relativeTimeFormat->formatToValue(number, relativeUnits[unit], status); } fmtRelativeTime.toString(status).toUTF8String(result); icu::ConstrainedFieldPosition constrainedPos; constrainedPos.constrainCategory(UFIELD_CATEGORY_NUMBER); size_t prevIndex = 0; size_t length = result.length(); std::map<size_t, size_t> indexMap; while (fmtRelativeTime.nextPosition(constrainedPos, status)) { size_t startIndex = (size_t)constrainedPos.getStart(); if (constrainedPos.getCategory() == UFIELD_CATEGORY_NUMBER) { if (constrainedPos.getField() == UNUM_GROUPING_SEPARATOR_FIELD) { indexMap.insert(std::make_pair(startIndex, (size_t)constrainedPos.getLimit())); continue; } if (startIndex > prevIndex) { InsertInfo(timeVector, unit, false, result.substr(prevIndex, startIndex - prevIndex)); } if (constrainedPos.getField() == UNUM_INTEGER_FIELD) { ProcessIntegerField(indexMap, timeVector, startIndex, unit, result); } InsertInfo(timeVector, unit, true, result.substr(startIndex, (size_t)constrainedPos.getLimit() - startIndex)); prevIndex = (size_t)constrainedPos.getLimit(); } } if (prevIndex < length) { InsertInfo(timeVector, unit, false, result.substr(prevIndex, length - prevIndex)); } } void RelativeTimeFormat::GetResolvedOptions(std::map<std::string, std::string> &map) { map.insert(std::make_pair("locale", localeBaseName)); if (!styleString.empty()) { map.insert(std::make_pair("style", styleString)); } if (!numeric.empty()) { map.insert(std::make_pair("numeric", numeric)); } if (!numberingSystem.empty()) { map.insert(std::make_pair("numberingSystem", numberingSystem)); } } void RelativeTimeFormat::SetDefaultStyle() { char value[BUFFER_LEN]; int code = GetParameter(DEVICE_TYPE_NAME, "", value, BUFFER_LEN); if (code > 0) { std::string deviceType = value; if (defaultFormatStyle.find(deviceType) != defaultFormatStyle.end()) { styleString = defaultFormatStyle[deviceType]; } } } } // namespace I18n } // namespace Global } // namespace OHOS