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#include "plural_rules.h" 16 17#include <unicode/stringpiece.h> 18 19#include "algorithm" 20#include "i18n_hilog.h" 21#include "locale_config.h" 22#include "unicode/locid.h" 23#include "plural_rules.h" 24#include "map" 25#include "set" 26#include "string" 27#include "unicode/unistr.h" 28#include "unicode/upluralrules.h" 29#include "utility" 30#include "utils.h" 31#include "unicode/utypes.h" 32#include "vector" 33 34namespace OHOS { 35namespace Global { 36namespace I18n { 37std::string PluralRules::ParseOption(std::map<std::string, std::string> &options, const std::string &key) 38{ 39 std::map<std::string, std::string>::iterator it = options.find(key); 40 if (it != options.end()) { 41 return it->second; 42 } else { 43 return ""; 44 } 45} 46 47int PluralRules::GetValidInteger(std::string &integerStr, int minValue, int maxValue, int defaultValue) 48{ 49 int status = 0; 50 int validInteger = ConvertString2Int(integerStr, status); 51 if (status < 0) { 52 validInteger = defaultValue; 53 } 54 if (validInteger < minValue) { 55 validInteger = minValue; 56 } 57 if (validInteger > maxValue) { 58 validInteger = maxValue; 59 } 60 return validInteger; 61} 62 63void PluralRules::ParseAllOptions(std::map<std::string, std::string> &options) 64{ 65 localeMatcher = ParseOption(options, "localeMatcher"); 66 localeMatcher = (localeMatcher == "") ? "best fit" : localeMatcher; 67 type = ParseOption(options, "type"); 68 type = (type == "") ? "cardinal" : type; 69 std::string minIntegerStr = ParseOption(options, "minimumIntegerDigits"); 70 // 1 is minValue and defaultValue, 21 is maxValue 71 minInteger = GetValidInteger(minIntegerStr, 1, 21, 1); 72 73 minFraction = 0; 74 maxFraction = 0; 75 std::string minFractionStr = ParseOption(options, "minimumFractionDigits"); 76 std::string maxFractionStr = ParseOption(options, "maximumFractionDigits"); 77 std::string minSignificantStr = ParseOption(options, "minimumSignificantDigits"); 78 std::string maxSignificantStr = ParseOption(options, "maximumSignificantDigits"); 79 if (minSignificantStr != "" || maxSignificantStr != "") { 80 // 1 is minValue and defaultValue, 21 is maxValue 81 minSignificant = GetValidInteger(minSignificantStr, 1, 21, 1); 82 // 1 is minValue, 21 is maxValue and defaultValue 83 maxSignificant = GetValidInteger(maxSignificantStr, 1, 21, 21); 84 } else { 85 minSignificant = 0; 86 maxSignificant = 0; 87 88 if (minFractionStr != "" || maxFractionStr != "") { 89 // 0 is minValue and defaultValue, 20 is maxValue 90 minFraction = GetValidInteger(minFractionStr, 0, 20, 0); 91 int maxFractionDefault = std::max(3, minFraction); // 3 is the default value of minFraction 92 int maxFractionMin = std::max(1, minFraction); // 1 is the min value of minFraction 93 // 21 is max value 94 maxFraction = GetValidInteger(maxFractionStr, maxFractionMin, 21, maxFractionDefault); 95 } else { 96 minFraction = 0; // 0 is the default value of minFraction. 97 maxFraction = 3; // 3 is the default value of maxFraction 98 } 99 } 100} 101 102void PluralRules::InitPluralRules(std::vector<std::string> &localeTags, 103 std::map<std::string, std::string> &options) 104{ 105 UPluralType uPluralType = (type == "cardinal") ? UPLURAL_TYPE_CARDINAL : UPLURAL_TYPE_ORDINAL; 106 UErrorCode status = UErrorCode::U_ZERO_ERROR; 107 localeTags.push_back(LocaleConfig::GetSystemLocale()); 108 for (size_t i = 0; i < localeTags.size(); i++) { 109 std::string curLocale = localeTags[i]; 110 locale = icu::Locale::forLanguageTag(icu::StringPiece(curLocale), status); 111 if (U_FAILURE(status)) { 112 status = U_ZERO_ERROR; 113 continue; 114 } 115 if (LocaleInfo::allValidLocales.count(locale.getLanguage()) > 0) { 116 localeInfo = std::make_unique<LocaleInfo>(curLocale, options); 117 if (!localeInfo->InitSuccess()) { 118 continue; 119 } 120 locale = localeInfo->GetLocale(); 121 localeStr = localeInfo->GetBaseName(); 122 pluralRules = icu::PluralRules::forLocale(locale, uPluralType, status); 123 if (status != UErrorCode::U_ZERO_ERROR || !pluralRules) { 124 continue; 125 } 126 createSuccess = true; 127 break; 128 } 129 } 130 if (status != UErrorCode::U_ZERO_ERROR || !pluralRules) { 131 HILOG_ERROR_I18N("PluralRules object created failed"); 132 return; 133 } 134} 135 136void PluralRules::InitNumberFormatter() 137{ 138 numberFormatter = icu::number::NumberFormatter::withLocale(locale).roundingMode(UNUM_ROUND_HALFUP); 139 if (minInteger > 1) { 140 numberFormatter = numberFormatter.integerWidth(icu::number::IntegerWidth::zeroFillTo(minInteger)); 141 } 142 143 if (minSignificant >= 0) { 144 if (minSignificant > 0) { 145 icu::number::Precision precision = icu::number::Precision::minMaxSignificantDigits(minSignificant, 146 maxSignificant); 147 numberFormatter = numberFormatter.precision(precision); 148 } else { 149 icu::number::Precision precision = icu::number::Precision::minMaxFraction(minFraction, maxFraction); 150 numberFormatter = numberFormatter.precision(precision); 151 } 152 } 153} 154 155PluralRules::PluralRules(std::vector<std::string> &localeTags, std::map<std::string, std::string> &options) 156{ 157 ParseAllOptions(options); 158 InitPluralRules(localeTags, options); 159 InitNumberFormatter(); 160} 161 162PluralRules::~PluralRules() 163{ 164 if (!pluralRules) { 165 delete pluralRules; 166 pluralRules = nullptr; 167 } 168} 169 170std::string PluralRules::Select(double number) 171{ 172 if (!createSuccess || pluralRules == nullptr) { 173 return "other"; 174 } 175 UErrorCode status = UErrorCode::U_ZERO_ERROR; 176 icu::number::FormattedNumber formattedNumber = numberFormatter.formatDouble(number, status); 177 if (status != UErrorCode::U_ZERO_ERROR) { 178 status = UErrorCode::U_ZERO_ERROR; 179 formattedNumber = numberFormatter.formatDouble(number, status); 180 } 181 icu::UnicodeString unicodeString = pluralRules->select(formattedNumber, status); 182 std::string result; 183 unicodeString.toUTF8String(result); 184 return result; 185} 186} // namespace I18n 187} // namespace Global 188} // namespace OHOS