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 "number_format.h"
16 #include <locale>
17 #include <codecvt>
18 #include "ohos/init_data.h"
19 #include "locale_config.h"
20
21 namespace OHOS {
22 namespace Global {
23 namespace I18n {
24 bool NumberFormat::icuInitialized = NumberFormat::Init();
25
26 std::unordered_map<std::string, UNumberUnitWidth> NumberFormat::unitStyle = {
27 { "long", UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME },
28 { "short", UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT },
29 { "narrow", UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW }
30 };
31
32 std::unordered_map<std::string, UNumberUnitWidth> NumberFormat::currencyStyle = {
33 { "symbol", UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT },
34 { "code", UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE },
35 { "name", UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME },
36 { "narrowSymbol", UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW }
37 };
38
39 std::unordered_map<std::string, UNumberSignDisplay> NumberFormat::signAutoStyle = {
40 { "auto", UNumberSignDisplay::UNUM_SIGN_AUTO },
41 { "never", UNumberSignDisplay::UNUM_SIGN_NEVER },
42 { "always", UNumberSignDisplay::UNUM_SIGN_ALWAYS },
43 { "exceptZero", UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO }
44 };
45
46 std::unordered_map<std::string, UNumberSignDisplay> NumberFormat::signAccountingStyle = {
47 { "auto", UNumberSignDisplay::UNUM_SIGN_ACCOUNTING },
48 { "never", UNumberSignDisplay::UNUM_SIGN_NEVER },
49 { "always", UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS },
50 { "exceptZero", UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO }
51 };
52
53 std::unordered_map<UMeasurementSystem, std::string> NumberFormat::measurementSystem = {
54 { UMeasurementSystem::UMS_SI, "SI" },
55 { UMeasurementSystem::UMS_US, "US" },
56 { UMeasurementSystem::UMS_UK, "UK" },
57 };
58
NumberFormat(const std::vector<std::string> & localeTags,std::map<std::string,std::string> & configs)59 NumberFormat::NumberFormat(const std::vector<std::string> &localeTags, std::map<std::string, std::string> &configs)
60 {
61 UErrorCode status = U_ZERO_ERROR;
62 std::unique_ptr<icu::LocaleBuilder> builder = nullptr;
63 builder = std::make_unique<icu::LocaleBuilder>();
64 ParseConfigs(configs);
65 for (size_t i = 0; i < localeTags.size(); i++) {
66 std::string curLocale = localeTags[i];
67 locale = builder->setLanguageTag(icu::StringPiece(curLocale)).build(status);
68 if (LocaleInfo::allValidLocales.count(locale.getLanguage()) > 0) {
69 localeInfo = new LocaleInfo(curLocale, configs);
70 locale = localeInfo->GetLocale();
71 localeBaseName = localeInfo->GetBaseName();
72 numberFormat = icu::number::NumberFormatter::withLocale(locale);
73 icu::MeasureUnit::getAvailable(unitArray, MAX_UNIT_NUM, status);
74 break;
75 }
76 }
77 if (!localeInfo) {
78 localeInfo = new LocaleInfo(LocaleConfig::GetSystemLocale(), configs);
79 locale = localeInfo->GetLocale();
80 localeBaseName = localeInfo->GetBaseName();
81 numberFormat = icu::number::NumberFormatter::withLocale(locale);
82 icu::MeasureUnit::getAvailable(unitArray, MAX_UNIT_NUM, status);
83 }
84 InitProperties();
85 }
86
~NumberFormat()87 NumberFormat::~NumberFormat()
88 {
89 if (localeInfo != nullptr) {
90 delete localeInfo;
91 localeInfo = nullptr;
92 }
93 }
94
InitProperties()95 void NumberFormat::InitProperties()
96 {
97 if (!currency.empty()) {
98 UErrorCode status = U_ZERO_ERROR;
99 numberFormat =
100 numberFormat.unit(icu::CurrencyUnit(icu::UnicodeString(currency.c_str()).getBuffer(), status));
101 if (currencyDisplay != UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT) {
102 numberFormat = numberFormat.unitWidth(currencyDisplay);
103 }
104 }
105 if (!styleString.empty() && styleString == "percent") {
106 numberFormat = numberFormat.unit(icu::NoUnit::percent());
107 }
108 if (!styleString.empty() && styleString == "unit") {
109 for (icu::MeasureUnit curUnit : unitArray) {
110 if (!strcmp(curUnit.getSubtype(), unit.c_str())) {
111 numberFormat = numberFormat.unit(curUnit);
112 unitType = curUnit.getType();
113 }
114 }
115 UErrorCode status = U_ZERO_ERROR;
116 UMeasurementSystem measSys = ulocdata_getMeasurementSystem(localeBaseName.c_str(), &status);
117 if (status == U_ZERO_ERROR && measSys >= 0) {
118 unitMeasSys = measurementSystem[measSys];
119 }
120 numberFormat = numberFormat.unitWidth(unitDisplay);
121 numberFormat = numberFormat.precision(icu::number::Precision::maxFraction(DEFAULT_FRACTION_DIGITS));
122 }
123 if (!useGrouping.empty()) {
124 numberFormat.grouping((useGrouping == "true") ?
125 UNumberGroupingStrategy::UNUM_GROUPING_AUTO : UNumberGroupingStrategy::UNUM_GROUPING_OFF);
126 }
127 if (!currencySign.empty() || !signDisplayString.empty()) {
128 numberFormat = numberFormat.sign(signDisplay);
129 }
130 if (!notationString.empty()) {
131 numberFormat = numberFormat.notation(notation);
132 }
133 InitDigitsProperties();
134 }
135
InitDigitsProperties()136 void NumberFormat::InitDigitsProperties()
137 {
138 if (!maximumSignificantDigits.empty() || !minimumSignificantDigits.empty()) {
139 if (!maximumSignificantDigits.empty()) {
140 int32_t maxSignificantDigits = std::stoi(maximumSignificantDigits);
141 numberFormat = numberFormat.precision(icu::number::Precision::maxSignificantDigits(maxSignificantDigits));
142 }
143 if (!minimumSignificantDigits.empty()) {
144 int32_t minSignificantDigits = std::stoi(minimumSignificantDigits);
145 numberFormat = numberFormat.precision(icu::number::Precision::minSignificantDigits(minSignificantDigits));
146 }
147 } else {
148 if (!minimumIntegerDigits.empty() && std::stoi(minimumIntegerDigits) > 1) {
149 numberFormat =
150 numberFormat.integerWidth(icu::number::IntegerWidth::zeroFillTo(std::stoi(minimumIntegerDigits)));
151 }
152 if (!minimumFractionDigits.empty()) {
153 numberFormat =
154 numberFormat.precision(icu::number::Precision::minFraction(std::stoi(minimumFractionDigits)));
155 }
156 if (!maximumFractionDigits.empty()) {
157 numberFormat =
158 numberFormat.precision(icu::number::Precision::maxFraction(std::stoi(maximumFractionDigits)));
159 }
160 }
161 }
162
ParseConfigs(std::map<std::string,std::string> & configs)163 void NumberFormat::ParseConfigs(std::map<std::string, std::string> &configs)
164 {
165 if (configs.count("signDisplay") > 0) {
166 signDisplayString = configs["signDisplay"];
167 if (signAutoStyle.count(signDisplayString) > 0) {
168 signDisplay = signAutoStyle[signDisplayString];
169 }
170 }
171 if (configs.count("style") > 0) {
172 styleString = configs["style"];
173 }
174 if (styleString == "unit" && configs.count("unit") > 0) {
175 unit = configs["unit"];
176 if (configs.count("unitDisplay") > 0) {
177 unitDisplayString = configs["unitDisplay"];
178 if (unitStyle.count(unitDisplayString) > 0) {
179 unitDisplay = unitStyle[unitDisplayString];
180 }
181 }
182 if (configs.count("unitUsage") > 0) {
183 unitUsage = configs["unitUsage"];
184 }
185 }
186 if (styleString == "currency" && configs.count("currency") > 0) {
187 currency = configs["currency"];
188 if (configs.count("currencySign") > 0) {
189 currencySign = configs["currencySign"];
190 if (configs["currencySign"] != "accounting" && !signDisplayString.empty()) {
191 signDisplay = signAccountingStyle[signDisplayString];
192 }
193 }
194 if (configs.count("currencyDisplay") > 0 && currencyStyle.count(configs["currencyDisplay"]) > 0) {
195 currencyDisplayString = configs["currencyDisplay"];
196 currencyDisplay = currencyStyle[currencyDisplayString];
197 }
198 }
199 ParseDigitsConfigs(configs);
200 }
201
ParseDigitsConfigs(std::map<std::string,std::string> & configs)202 void NumberFormat::ParseDigitsConfigs(std::map<std::string, std::string> &configs)
203 {
204 if (configs.count("notation") > 0) {
205 notationString = configs["notation"];
206 if (notationString == "scientific") {
207 notation = icu::number::Notation::scientific();
208 } else if (notationString == "engineering") {
209 notation = icu::number::Notation::engineering();
210 }
211 if (notationString == "compact" && configs.count("compactDisplay") > 0) {
212 compactDisplay = configs["compactDisplay"];
213 if (compactDisplay == "long") {
214 notation = icu::number::Notation::compactLong();
215 } else {
216 notation = icu::number::Notation::compactShort();
217 }
218 }
219 }
220 if (configs.count("minimumIntegerDigits") > 0) {
221 minimumIntegerDigits = configs["minimumIntegerDigits"];
222 }
223 if (configs.count("minimumFractionDigits") > 0) {
224 minimumFractionDigits = configs["minimumFractionDigits"];
225 }
226 if (configs.count("maximumFractionDigits") > 0) {
227 maximumFractionDigits = configs["maximumFractionDigits"];
228 }
229 if (configs.count("minimumSignificantDigits") > 0) {
230 minimumSignificantDigits = configs["minimumSignificantDigits"];
231 }
232 if (configs.count("maximumSignificantDigits") > 0) {
233 maximumSignificantDigits = configs["maximumSignificantDigits"];
234 }
235 if (configs.count("numberingSystem") > 0) {
236 numberingSystem = configs["numberingSystem"];
237 }
238 if (configs.count("useGrouping") > 0) {
239 useGrouping = configs["useGrouping"];
240 }
241 if (configs.count("localeMatcher") > 0) {
242 localeMatcher = configs["localeMatcher"];
243 }
244 }
245
Format(double number)246 std::string NumberFormat::Format(double number)
247 {
248 double finalNumber = number;
249 if (!unitUsage.empty()) {
250 std::vector<std::string> preferredUnits;
251 if (unitUsage == "default") {
252 GetDefaultPreferredUnit(localeInfo->GetRegion(), unitType, preferredUnits);
253 } else {
254 GetPreferredUnit(localeInfo->GetRegion(), unitUsage, preferredUnits);
255 }
256 std::map<double, std::string> preferredValuesOverOne;
257 std::map<double, std::string> preferredValuesUnderOne;
258 double num = number;
259 for (size_t i = 0; i < preferredUnits.size(); i++) {
260 int status = Convert(num, unit, unitMeasSys, preferredUnits[i], unitMeasSys);
261 if (!status) {
262 continue;
263 }
264 if (num >= 1) {
265 preferredValuesOverOne.insert(std::make_pair(num, preferredUnits[i]));
266 } else {
267 preferredValuesUnderOne.insert(std::make_pair(num, preferredUnits[i]));
268 }
269 }
270 std::string preferredUnit;
271 if (preferredValuesOverOne.size() > 0) {
272 finalNumber = preferredValuesOverOne.begin()->first;
273 preferredUnit = preferredValuesOverOne.begin()->second;
274 } else if (preferredValuesUnderOne.size() > 0) {
275 finalNumber = preferredValuesUnderOne.rbegin()->first;
276 preferredUnit = preferredValuesUnderOne.rbegin()->second;
277 }
278 if (!preferredUnit.empty()) {
279 for (icu::MeasureUnit curUnit : unitArray) {
280 if (!strcmp(curUnit.getSubtype(), preferredUnit.c_str())) {
281 numberFormat = numberFormat.unit(curUnit);
282 }
283 }
284 }
285 }
286 std::string result;
287 UErrorCode status = U_ZERO_ERROR;
288 numberFormat.formatDouble(finalNumber, status).toString(status).toUTF8String(result);
289 return result;
290 }
291
GetResolvedOptions(std::map<std::string,std::string> & map)292 void NumberFormat::GetResolvedOptions(std::map<std::string, std::string> &map)
293 {
294 map.insert(std::make_pair("locale", localeBaseName));
295 if (!styleString.empty()) {
296 map.insert(std::make_pair("style", styleString));
297 }
298 if (!currency.empty()) {
299 map.insert(std::make_pair("currency", currency));
300 }
301 if (!currencySign.empty()) {
302 map.insert(std::make_pair("currencySign", currencySign));
303 }
304 if (!currencyDisplayString.empty()) {
305 map.insert(std::make_pair("currencyDisplay", currencyDisplayString));
306 }
307 if (!signDisplayString.empty()) {
308 map.insert(std::make_pair("signDisplay", signDisplayString));
309 }
310 if (!compactDisplay.empty()) {
311 map.insert(std::make_pair("compactDisplay", compactDisplay));
312 }
313 if (!unitDisplayString.empty()) {
314 map.insert(std::make_pair("unitDisplay", unitDisplayString));
315 }
316 if (!unitUsage.empty()) {
317 map.insert(std::make_pair("unitUsage", unitUsage));
318 }
319 if (!unit.empty()) {
320 map.insert(std::make_pair("unit", unit));
321 }
322 GetDigitsResolvedOptions(map);
323 }
324
GetDigitsResolvedOptions(std::map<std::string,std::string> & map)325 void NumberFormat::GetDigitsResolvedOptions(std::map<std::string, std::string> &map)
326 {
327 UErrorCode status = U_ZERO_ERROR;
328 if (!numberingSystem.empty()) {
329 map.insert(std::make_pair("numberingSystem", numberingSystem));
330 } else if (!(localeInfo->GetNumberingSystem()).empty()) {
331 map.insert(std::make_pair("numberingSystem", localeInfo->GetNumberingSystem()));
332 } else {
333 auto numSys = std::unique_ptr<icu::NumberingSystem>(icu::NumberingSystem::createInstance(locale, status));
334 map.insert(std::make_pair("numberingSystem", numSys->getName()));
335 }
336 if (!useGrouping.empty()) {
337 map.insert(std::make_pair("useGrouping", useGrouping));
338 }
339 if (!minimumIntegerDigits.empty()) {
340 map.insert(std::make_pair("minimumIntegerDigits", minimumIntegerDigits));
341 }
342 if (!minimumFractionDigits.empty()) {
343 map.insert(std::make_pair("minimumFractionDigits", minimumFractionDigits));
344 }
345 if (!maximumFractionDigits.empty()) {
346 map.insert(std::make_pair("maximumFractionDigits", maximumFractionDigits));
347 }
348 if (!minimumSignificantDigits.empty()) {
349 map.insert(std::make_pair("minimumSignificantDigits", minimumSignificantDigits));
350 }
351 if (!maximumSignificantDigits.empty()) {
352 map.insert(std::make_pair("maximumSignificantDigits", maximumSignificantDigits));
353 }
354 if (!localeMatcher.empty()) {
355 map.insert(std::make_pair("localeMatcher", localeMatcher));
356 }
357 if (!notationString.empty()) {
358 map.insert(std::make_pair("notation", notationString));
359 }
360 }
361
GetCurrency() const362 std::string NumberFormat::GetCurrency() const
363 {
364 return currency;
365 }
366
GetCurrencySign() const367 std::string NumberFormat::GetCurrencySign() const
368 {
369 return currencySign;
370 }
371
GetStyle() const372 std::string NumberFormat::GetStyle() const
373 {
374 return styleString;
375 }
376
GetNumberingSystem() const377 std::string NumberFormat::GetNumberingSystem() const
378 {
379 return numberingSystem;
380 }
381
GetUseGrouping() const382 std::string NumberFormat::GetUseGrouping() const
383 {
384 return useGrouping;
385 }
386
GetMinimumIntegerDigits() const387 std::string NumberFormat::GetMinimumIntegerDigits() const
388 {
389 return minimumIntegerDigits;
390 }
391
GetMinimumFractionDigits() const392 std::string NumberFormat::GetMinimumFractionDigits() const
393 {
394 return minimumFractionDigits;
395 }
396
GetMaximumFractionDigits() const397 std::string NumberFormat::GetMaximumFractionDigits() const
398 {
399 return maximumFractionDigits;
400 }
401
GetMinimumSignificantDigits() const402 std::string NumberFormat::GetMinimumSignificantDigits() const
403 {
404 return minimumSignificantDigits;
405 }
406
GetMaximumSignificantDigits() const407 std::string NumberFormat::GetMaximumSignificantDigits() const
408 {
409 return maximumSignificantDigits;
410 }
411
GetLocaleMatcher() const412 std::string NumberFormat::GetLocaleMatcher() const
413 {
414 return localeMatcher;
415 }
416
Init()417 bool NumberFormat::Init()
418 {
419 SetHwIcuDirectory();
420 return true;
421 }
422 } // namespace I18n
423 } // namespace Global
424 } // namespace OHOS
425