/* * 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 "plural_rules.h" #include #include "algorithm" #include "i18n_hilog.h" #include "locale_config.h" #include "unicode/locid.h" #include "plural_rules.h" #include "map" #include "set" #include "string" #include "unicode/unistr.h" #include "unicode/upluralrules.h" #include "utility" #include "utils.h" #include "unicode/utypes.h" #include "vector" namespace OHOS { namespace Global { namespace I18n { std::string PluralRules::ParseOption(std::map &options, const std::string &key) { std::map::iterator it = options.find(key); if (it != options.end()) { return it->second; } else { return ""; } } int PluralRules::GetValidInteger(std::string &integerStr, int minValue, int maxValue, int defaultValue) { int status = 0; int validInteger = ConvertString2Int(integerStr, status); if (status < 0) { validInteger = defaultValue; } if (validInteger < minValue) { validInteger = minValue; } if (validInteger > maxValue) { validInteger = maxValue; } return validInteger; } void PluralRules::ParseAllOptions(std::map &options) { localeMatcher = ParseOption(options, "localeMatcher"); localeMatcher = (localeMatcher == "") ? "best fit" : localeMatcher; type = ParseOption(options, "type"); type = (type == "") ? "cardinal" : type; std::string minIntegerStr = ParseOption(options, "minimumIntegerDigits"); // 1 is minValue and defaultValue, 21 is maxValue minInteger = GetValidInteger(minIntegerStr, 1, 21, 1); minFraction = 0; maxFraction = 0; std::string minFractionStr = ParseOption(options, "minimumFractionDigits"); std::string maxFractionStr = ParseOption(options, "maximumFractionDigits"); std::string minSignificantStr = ParseOption(options, "minimumSignificantDigits"); std::string maxSignificantStr = ParseOption(options, "maximumSignificantDigits"); if (minSignificantStr != "" || maxSignificantStr != "") { // 1 is minValue and defaultValue, 21 is maxValue minSignificant = GetValidInteger(minSignificantStr, 1, 21, 1); // 1 is minValue, 21 is maxValue and defaultValue maxSignificant = GetValidInteger(maxSignificantStr, 1, 21, 21); } else { minSignificant = 0; maxSignificant = 0; if (minFractionStr != "" || maxFractionStr != "") { // 0 is minValue and defaultValue, 20 is maxValue minFraction = GetValidInteger(minFractionStr, 0, 20, 0); int maxFractionDefault = std::max(3, minFraction); // 3 is the default value of minFraction int maxFractionMin = std::max(1, minFraction); // 1 is the min value of minFraction // 21 is max value maxFraction = GetValidInteger(maxFractionStr, maxFractionMin, 21, maxFractionDefault); } else { minFraction = 0; // 0 is the default value of minFraction. maxFraction = 3; // 3 is the default value of maxFraction } } } void PluralRules::InitPluralRules(std::vector &localeTags, std::map &options) { UPluralType uPluralType = (type == "cardinal") ? UPLURAL_TYPE_CARDINAL : UPLURAL_TYPE_ORDINAL; UErrorCode status = UErrorCode::U_ZERO_ERROR; localeTags.push_back(LocaleConfig::GetSystemLocale()); 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(curLocale, options); if (!localeInfo->InitSuccess()) { continue; } locale = localeInfo->GetLocale(); localeStr = localeInfo->GetBaseName(); pluralRules = icu::PluralRules::forLocale(locale, uPluralType, status); if (status != UErrorCode::U_ZERO_ERROR || !pluralRules) { continue; } createSuccess = true; break; } } if (status != UErrorCode::U_ZERO_ERROR || !pluralRules) { HILOG_ERROR_I18N("PluralRules object created failed"); return; } } void PluralRules::InitNumberFormatter() { numberFormatter = icu::number::NumberFormatter::withLocale(locale).roundingMode(UNUM_ROUND_HALFUP); if (minInteger > 1) { numberFormatter = numberFormatter.integerWidth(icu::number::IntegerWidth::zeroFillTo(minInteger)); } if (minSignificant >= 0) { if (minSignificant > 0) { icu::number::Precision precision = icu::number::Precision::minMaxSignificantDigits(minSignificant, maxSignificant); numberFormatter = numberFormatter.precision(precision); } else { icu::number::Precision precision = icu::number::Precision::minMaxFraction(minFraction, maxFraction); numberFormatter = numberFormatter.precision(precision); } } } PluralRules::PluralRules(std::vector &localeTags, std::map &options) { ParseAllOptions(options); InitPluralRules(localeTags, options); InitNumberFormatter(); } PluralRules::~PluralRules() { if (!pluralRules) { delete pluralRules; pluralRules = nullptr; } } std::string PluralRules::Select(double number) { if (!createSuccess || pluralRules == nullptr) { return "other"; } UErrorCode status = UErrorCode::U_ZERO_ERROR; icu::number::FormattedNumber formattedNumber = numberFormatter.formatDouble(number, status); if (status != UErrorCode::U_ZERO_ERROR) { status = UErrorCode::U_ZERO_ERROR; formattedNumber = numberFormatter.formatDouble(number, status); } icu::UnicodeString unicodeString = pluralRules->select(formattedNumber, status); std::string result; unicodeString.toUTF8String(result); return result; } } // namespace I18n } // namespace Global } // namespace OHOS