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()).scale(icu::number::Scale::powerOfTen(2)).precision(
121 icu::number::Precision::fixedFraction(0));
122 }
123 if (!styleString.empty() && styleString == "unit") {
124 for (icu::MeasureUnit curUnit : unitArray) {
125 if (!strcmp(curUnit.getSubtype(), unit.c_str())) {
126 numberFormat = numberFormat.unit(curUnit);
127 unitType = curUnit.getType();
128 }
129 }
130 UErrorCode status = U_ZERO_ERROR;
131 UMeasurementSystem measSys = ulocdata_getMeasurementSystem(localeBaseName.c_str(), &status);
132 if (U_SUCCESS(status) && measSys >= 0) {
133 unitMeasSys = measurementSystem[measSys];
134 }
135 numberFormat = numberFormat.unitWidth(unitDisplay);
136 numberFormat = numberFormat.precision(icu::number::Precision::maxFraction(DEFAULT_FRACTION_DIGITS));
137 }
138 if (!useGrouping.empty()) {
139 numberFormat = numberFormat.grouping((useGrouping == "true") ?
140 UNumberGroupingStrategy::UNUM_GROUPING_AUTO : UNumberGroupingStrategy::UNUM_GROUPING_OFF);
141 }
142 if (!currencySign.empty() || !signDisplayString.empty()) {
143 numberFormat = numberFormat.sign(signDisplay);
144 }
145 if (!notationString.empty()) {
146 numberFormat = numberFormat.notation(notation);
147 }
148 InitDigitsProperties();
149 }
150
InitDigitsProperties()151 void NumberFormat::InitDigitsProperties()
152 {
153 int32_t status = 0;
154 if (!maximumSignificantDigits.empty() || !minimumSignificantDigits.empty()) {
155 int32_t maxSignificantDigits = ConvertString2Int(maximumSignificantDigits, status);
156 if (status == 0) {
157 numberFormat = numberFormat.precision(icu::number::Precision::maxSignificantDigits(maxSignificantDigits));
158 }
159
160 status = 0;
161 int32_t minSignificantDigits = ConvertString2Int(minimumSignificantDigits, status);
162 if (status == 0) {
163 numberFormat = numberFormat.precision(icu::number::Precision::minSignificantDigits(minSignificantDigits));
164 }
165 } else {
166 int32_t minIntegerDigits = ConvertString2Int(minimumIntegerDigits, status);
167 if (status == 0 && minIntegerDigits > 1) {
168 numberFormat =
169 numberFormat.integerWidth(icu::number::IntegerWidth::zeroFillTo(minIntegerDigits));
170 }
171
172 status = 0;
173 int32_t minFractionDigits = ConvertString2Int(minimumFractionDigits, status);
174 if (status == 0) {
175 numberFormat =
176 numberFormat.precision(icu::number::Precision::minFraction(minFractionDigits));
177 }
178
179 status = 0;
180 int32_t maxFractionDigits = ConvertString2Int(maximumFractionDigits, status);
181 if (status == 0) {
182 numberFormat =
183 numberFormat.precision(icu::number::Precision::maxFraction(maxFractionDigits));
184 }
185 }
186 }
187
ParseConfigs(std::map<std::string,std::string> & configs)188 void NumberFormat::ParseConfigs(std::map<std::string, std::string> &configs)
189 {
190 if (configs.count("signDisplay") > 0) {
191 signDisplayString = configs["signDisplay"];
192 }
193 if (signAutoStyle.count(signDisplayString) > 0) {
194 signDisplay = signAutoStyle[signDisplayString];
195 }
196 if (configs.count("style") > 0) {
197 styleString = configs["style"];
198 }
199 if (styleString == "unit" && configs.count("unit") > 0) {
200 unit = configs["unit"];
201 if (configs.count("unitDisplay") > 0) {
202 unitDisplayString = configs["unitDisplay"];
203 if (unitStyle.count(unitDisplayString) > 0) {
204 unitDisplay = unitStyle[unitDisplayString];
205 }
206 }
207 if (configs.count("unitUsage") > 0) {
208 unitUsage = configs["unitUsage"];
209 }
210 }
211 if (styleString == "currency" && configs.count("currency") > 0) {
212 currency = configs["currency"];
213 if (configs.count("currencySign") > 0) {
214 currencySign = configs["currencySign"];
215 }
216 if (currencySign.compare("accounting") == 0 && signAccountingStyle.count(signDisplayString) > 0) {
217 signDisplay = signAccountingStyle[signDisplayString];
218 }
219 if (configs.count("currencyDisplay") > 0 && currencyStyle.count(configs["currencyDisplay"]) > 0) {
220 currencyDisplayString = configs["currencyDisplay"];
221 currencyDisplay = currencyStyle[currencyDisplayString];
222 }
223 }
224 ParseDigitsConfigs(configs);
225 }
226
ParseDigitsConfigs(std::map<std::string,std::string> & configs)227 void NumberFormat::ParseDigitsConfigs(std::map<std::string, std::string> &configs)
228 {
229 if (configs.count("notation") > 0) {
230 notationString = configs["notation"];
231 if (notationString == "scientific") {
232 notation = icu::number::Notation::scientific();
233 } else if (notationString == "engineering") {
234 notation = icu::number::Notation::engineering();
235 }
236 if (notationString == "compact" && configs.count("compactDisplay") > 0) {
237 compactDisplay = configs["compactDisplay"];
238 if (compactDisplay == "long") {
239 notation = icu::number::Notation::compactLong();
240 } else {
241 notation = icu::number::Notation::compactShort();
242 }
243 }
244 }
245 if (configs.count("minimumIntegerDigits") > 0) {
246 minimumIntegerDigits = configs["minimumIntegerDigits"];
247 }
248 if (configs.count("minimumFractionDigits") > 0) {
249 minimumFractionDigits = configs["minimumFractionDigits"];
250 }
251 if (configs.count("maximumFractionDigits") > 0) {
252 maximumFractionDigits = configs["maximumFractionDigits"];
253 }
254 if (configs.count("minimumSignificantDigits") > 0) {
255 minimumSignificantDigits = configs["minimumSignificantDigits"];
256 }
257 if (configs.count("maximumSignificantDigits") > 0) {
258 maximumSignificantDigits = configs["maximumSignificantDigits"];
259 }
260 if (configs.count("numberingSystem") > 0) {
261 numberingSystem = configs["numberingSystem"];
262 }
263 if (configs.count("useGrouping") > 0) {
264 useGrouping = configs["useGrouping"];
265 }
266 if (configs.count("localeMatcher") > 0) {
267 localeMatcher = configs["localeMatcher"];
268 }
269 }
270
SetUnit(std::string & preferredUnit)271 void NumberFormat::SetUnit(std::string &preferredUnit)
272 {
273 if (preferredUnit.empty()) {
274 return;
275 }
276 for (icu::MeasureUnit curUnit : unitArray) {
277 if (!strcmp(curUnit.getSubtype(), preferredUnit.c_str())) {
278 numberFormat = numberFormat.unit(curUnit);
279 }
280 }
281 }
282
Format(double number)283 std::string NumberFormat::Format(double number)
284 {
285 if (!createSuccess) {
286 return "";
287 }
288 double finalNumber = number;
289 if (!unitUsage.empty()) {
290 std::vector<std::string> preferredUnits;
291 if (unitUsage == "default") {
292 GetDefaultPreferredUnit(localeInfo->GetRegion(), unitType, preferredUnits);
293 } else {
294 GetPreferredUnit(localeInfo->GetRegion(), unitUsage, preferredUnits);
295 }
296 std::map<double, std::string> preferredValuesOverOne;
297 std::map<double, std::string> preferredValuesUnderOne;
298 double num = number;
299 for (size_t i = 0; i < preferredUnits.size(); i++) {
300 int status = Convert(num, unit, unitMeasSys, preferredUnits[i], unitMeasSys);
301 if (!status) {
302 continue;
303 }
304 if (num >= 1) {
305 preferredValuesOverOne.insert(std::make_pair(num, preferredUnits[i]));
306 } else {
307 preferredValuesUnderOne.insert(std::make_pair(num, preferredUnits[i]));
308 }
309 }
310 std::string preferredUnit;
311 if (preferredValuesOverOne.size() > 0) {
312 finalNumber = preferredValuesOverOne.begin()->first;
313 preferredUnit = preferredValuesOverOne.begin()->second;
314 } else if (preferredValuesUnderOne.size() > 0) {
315 finalNumber = preferredValuesUnderOne.rbegin()->first;
316 preferredUnit = preferredValuesUnderOne.rbegin()->second;
317 }
318 SetUnit(preferredUnit);
319 }
320 std::string result;
321 UErrorCode status = U_ZERO_ERROR;
322 numberFormat.formatDouble(finalNumber, status).toString(status).toUTF8String(result);
323 return result;
324 }
325
GetResolvedOptions(std::map<std::string,std::string> & map)326 void NumberFormat::GetResolvedOptions(std::map<std::string, std::string> &map)
327 {
328 map.insert(std::make_pair("locale", localeBaseName));
329 if (!styleString.empty()) {
330 map.insert(std::make_pair("style", styleString));
331 }
332 if (!currency.empty()) {
333 map.insert(std::make_pair("currency", currency));
334 }
335 if (!currencySign.empty()) {
336 map.insert(std::make_pair("currencySign", currencySign));
337 }
338 if (!currencyDisplayString.empty()) {
339 map.insert(std::make_pair("currencyDisplay", currencyDisplayString));
340 }
341 if (!signDisplayString.empty()) {
342 map.insert(std::make_pair("signDisplay", signDisplayString));
343 }
344 if (!compactDisplay.empty()) {
345 map.insert(std::make_pair("compactDisplay", compactDisplay));
346 }
347 if (!unitDisplayString.empty()) {
348 map.insert(std::make_pair("unitDisplay", unitDisplayString));
349 }
350 if (!unitUsage.empty()) {
351 map.insert(std::make_pair("unitUsage", unitUsage));
352 }
353 if (!unit.empty()) {
354 map.insert(std::make_pair("unit", unit));
355 }
356 GetDigitsResolvedOptions(map);
357 }
358
GetDigitsResolvedOptions(std::map<std::string,std::string> & map)359 void NumberFormat::GetDigitsResolvedOptions(std::map<std::string, std::string> &map)
360 {
361 if (!numberingSystem.empty()) {
362 map.insert(std::make_pair("numberingSystem", numberingSystem));
363 } else if (!(localeInfo->GetNumberingSystem()).empty()) {
364 map.insert(std::make_pair("numberingSystem", localeInfo->GetNumberingSystem()));
365 } else {
366 UErrorCode status = U_ZERO_ERROR;
367 auto numSys = std::unique_ptr<icu::NumberingSystem>(icu::NumberingSystem::createInstance(locale, status));
368 if (U_SUCCESS(status)) {
369 map.insert(std::make_pair("numberingSystem", numSys->getName()));
370 }
371 }
372 if (!useGrouping.empty()) {
373 map.insert(std::make_pair("useGrouping", useGrouping));
374 }
375 if (!minimumIntegerDigits.empty()) {
376 map.insert(std::make_pair("minimumIntegerDigits", minimumIntegerDigits));
377 }
378 if (!minimumFractionDigits.empty()) {
379 map.insert(std::make_pair("minimumFractionDigits", minimumFractionDigits));
380 }
381 if (!maximumFractionDigits.empty()) {
382 map.insert(std::make_pair("maximumFractionDigits", maximumFractionDigits));
383 }
384 if (!minimumSignificantDigits.empty()) {
385 map.insert(std::make_pair("minimumSignificantDigits", minimumSignificantDigits));
386 }
387 if (!maximumSignificantDigits.empty()) {
388 map.insert(std::make_pair("maximumSignificantDigits", maximumSignificantDigits));
389 }
390 if (!localeMatcher.empty()) {
391 map.insert(std::make_pair("localeMatcher", localeMatcher));
392 }
393 if (!notationString.empty()) {
394 map.insert(std::make_pair("notation", notationString));
395 }
396 }
397
GetCurrency() const398 std::string NumberFormat::GetCurrency() const
399 {
400 return currency;
401 }
402
GetCurrencySign() const403 std::string NumberFormat::GetCurrencySign() const
404 {
405 return currencySign;
406 }
407
GetStyle() const408 std::string NumberFormat::GetStyle() const
409 {
410 return styleString;
411 }
412
GetNumberingSystem() const413 std::string NumberFormat::GetNumberingSystem() const
414 {
415 return numberingSystem;
416 }
417
GetUseGrouping() const418 std::string NumberFormat::GetUseGrouping() const
419 {
420 return useGrouping;
421 }
422
GetMinimumIntegerDigits() const423 std::string NumberFormat::GetMinimumIntegerDigits() const
424 {
425 return minimumIntegerDigits;
426 }
427
GetMinimumFractionDigits() const428 std::string NumberFormat::GetMinimumFractionDigits() const
429 {
430 return minimumFractionDigits;
431 }
432
GetMaximumFractionDigits() const433 std::string NumberFormat::GetMaximumFractionDigits() const
434 {
435 return maximumFractionDigits;
436 }
437
GetMinimumSignificantDigits() const438 std::string NumberFormat::GetMinimumSignificantDigits() const
439 {
440 return minimumSignificantDigits;
441 }
442
GetMaximumSignificantDigits() const443 std::string NumberFormat::GetMaximumSignificantDigits() const
444 {
445 return maximumSignificantDigits;
446 }
447
GetLocaleMatcher() const448 std::string NumberFormat::GetLocaleMatcher() const
449 {
450 return localeMatcher;
451 }
452
Init()453 bool NumberFormat::Init()
454 {
455 SetHwIcuDirectory();
456 return true;
457 }
458 } // namespace I18n
459 } // namespace Global
460 } // namespace OHOS
461