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