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 
34 namespace OHOS {
35 namespace Global {
36 namespace I18n {
ParseOption(std::map<std::string,std::string> & options,const std::string & key)37 std::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 
GetValidInteger(std::string & integerStr,int minValue,int maxValue,int defaultValue)47 int 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 
ParseAllOptions(std::map<std::string,std::string> & options)63 void 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 
InitPluralRules(std::vector<std::string> & localeTags,std::map<std::string,std::string> & options)102 void 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 (status != U_ZERO_ERROR) {
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 
InitNumberFormatter()136 void 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 
PluralRules(std::vector<std::string> & localeTags,std::map<std::string,std::string> & options)155 PluralRules::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 
~PluralRules()162 PluralRules::~PluralRules()
163 {
164     if (!pluralRules) {
165         delete pluralRules;
166         pluralRules = nullptr;
167     }
168 }
169 
Select(double number)170 std::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