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 // 2 is the power of ten
121 numberFormat = numberFormat.unit(icu::NoUnit::percent()).scale(icu::number::Scale::powerOfTen(2)).precision(
122 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 int32_t minFdStatus = 0;
174 int32_t minFractionDigits = ConvertString2Int(minimumFractionDigits, minFdStatus);
175 int32_t maxFdStatus = 0;
176 int32_t maxFractionDigits = ConvertString2Int(maximumFractionDigits, maxFdStatus);
177 if (minFdStatus == 0 && maxFdStatus != 0) {
178 numberFormat =
179 numberFormat.precision(icu::number::Precision::minFraction(minFractionDigits));
180 } else if (minFdStatus != 0 && maxFdStatus == 0) {
181 numberFormat =
182 numberFormat.precision(icu::number::Precision::maxFraction(maxFractionDigits));
183 } else if (minFdStatus == 0 && maxFdStatus == 0) {
184 numberFormat =
185 numberFormat.precision(icu::number::Precision::minMaxFraction(minFractionDigits, maxFractionDigits));
186 }
187 }
188 }
189
ParseConfigs(std::map<std::string,std::string> & configs)190 void NumberFormat::ParseConfigs(std::map<std::string, std::string> &configs)
191 {
192 if (configs.count("signDisplay") > 0) {
193 signDisplayString = configs["signDisplay"];
194 }
195 if (signAutoStyle.count(signDisplayString) > 0) {
196 signDisplay = signAutoStyle[signDisplayString];
197 }
198 if (configs.count("style") > 0) {
199 styleString = configs["style"];
200 }
201 if (styleString == "unit" && configs.count("unit") > 0) {
202 unit = configs["unit"];
203 if (configs.count("unitDisplay") > 0) {
204 unitDisplayString = configs["unitDisplay"];
205 if (unitStyle.count(unitDisplayString) > 0) {
206 unitDisplay = unitStyle[unitDisplayString];
207 }
208 }
209 if (configs.count("unitUsage") > 0) {
210 unitUsage = configs["unitUsage"];
211 }
212 }
213 if (styleString == "currency" && configs.count("currency") > 0) {
214 currency = configs["currency"];
215 if (configs.count("currencySign") > 0) {
216 currencySign = configs["currencySign"];
217 }
218 if (currencySign.compare("accounting") == 0 && signAccountingStyle.count(signDisplayString) > 0) {
219 signDisplay = signAccountingStyle[signDisplayString];
220 }
221 if (configs.count("currencyDisplay") > 0 && currencyStyle.count(configs["currencyDisplay"]) > 0) {
222 currencyDisplayString = configs["currencyDisplay"];
223 currencyDisplay = currencyStyle[currencyDisplayString];
224 }
225 }
226 ParseDigitsConfigs(configs);
227 }
228
ParseDigitsConfigs(std::map<std::string,std::string> & configs)229 void NumberFormat::ParseDigitsConfigs(std::map<std::string, std::string> &configs)
230 {
231 if (configs.count("notation") > 0) {
232 notationString = configs["notation"];
233 if (notationString == "scientific") {
234 notation = icu::number::Notation::scientific();
235 } else if (notationString == "engineering") {
236 notation = icu::number::Notation::engineering();
237 }
238 if (notationString == "compact") {
239 if (configs.count("compactDisplay") > 0) {
240 compactDisplay = configs["compactDisplay"];
241 }
242 if (compactDisplay == "long") {
243 notation = icu::number::Notation::compactLong();
244 } else {
245 notation = icu::number::Notation::compactShort();
246 }
247 }
248 }
249 if (configs.count("minimumIntegerDigits") > 0) {
250 minimumIntegerDigits = configs["minimumIntegerDigits"];
251 }
252 if (configs.count("minimumFractionDigits") > 0) {
253 minimumFractionDigits = configs["minimumFractionDigits"];
254 }
255 if (configs.count("maximumFractionDigits") > 0) {
256 maximumFractionDigits = configs["maximumFractionDigits"];
257 }
258 if (configs.count("minimumSignificantDigits") > 0) {
259 minimumSignificantDigits = configs["minimumSignificantDigits"];
260 }
261 if (configs.count("maximumSignificantDigits") > 0) {
262 maximumSignificantDigits = configs["maximumSignificantDigits"];
263 }
264 if (configs.count("numberingSystem") > 0) {
265 numberingSystem = configs["numberingSystem"];
266 }
267 if (configs.count("useGrouping") > 0) {
268 useGrouping = configs["useGrouping"];
269 }
270 if (configs.count("localeMatcher") > 0) {
271 localeMatcher = configs["localeMatcher"];
272 }
273 }
274
SetUnit(std::string & preferredUnit)275 void NumberFormat::SetUnit(std::string &preferredUnit)
276 {
277 if (preferredUnit.empty()) {
278 return;
279 }
280 for (icu::MeasureUnit curUnit : unitArray) {
281 if (!strcmp(curUnit.getSubtype(), preferredUnit.c_str())) {
282 numberFormat = numberFormat.unit(curUnit);
283 }
284 }
285 }
286
Format(double number)287 std::string NumberFormat::Format(double number)
288 {
289 if (!createSuccess) {
290 return "";
291 }
292 double finalNumber = number;
293 if (!unitUsage.empty()) {
294 std::vector<std::string> preferredUnits;
295 if (unitUsage == "default") {
296 GetDefaultPreferredUnit(localeInfo->GetRegion(), unitType, preferredUnits);
297 } else {
298 GetPreferredUnit(localeInfo->GetRegion(), unitUsage, preferredUnits);
299 }
300 std::map<double, std::string> preferredValuesOverOne;
301 std::map<double, std::string> preferredValuesUnderOne;
302 double num = number;
303 for (size_t i = 0; i < preferredUnits.size(); i++) {
304 int status = Convert(num, unit, unitMeasSys, preferredUnits[i], unitMeasSys);
305 if (!status) {
306 continue;
307 }
308 if (num >= 1) {
309 preferredValuesOverOne.insert(std::make_pair(num, preferredUnits[i]));
310 } else {
311 preferredValuesUnderOne.insert(std::make_pair(num, preferredUnits[i]));
312 }
313 }
314 std::string preferredUnit;
315 if (preferredValuesOverOne.size() > 0) {
316 finalNumber = preferredValuesOverOne.begin()->first;
317 preferredUnit = preferredValuesOverOne.begin()->second;
318 } else if (preferredValuesUnderOne.size() > 0) {
319 finalNumber = preferredValuesUnderOne.rbegin()->first;
320 preferredUnit = preferredValuesUnderOne.rbegin()->second;
321 }
322 SetUnit(preferredUnit);
323 }
324 std::string result;
325 UErrorCode status = U_ZERO_ERROR;
326 numberFormat.formatDouble(finalNumber, status).toString(status).toUTF8String(result);
327 return result;
328 }
329
GetResolvedOptions(std::map<std::string,std::string> & map)330 void NumberFormat::GetResolvedOptions(std::map<std::string, std::string> &map)
331 {
332 map.insert(std::make_pair("locale", localeBaseName));
333 if (!styleString.empty()) {
334 map.insert(std::make_pair("style", styleString));
335 }
336 if (!currency.empty()) {
337 map.insert(std::make_pair("currency", currency));
338 }
339 if (!currencySign.empty()) {
340 map.insert(std::make_pair("currencySign", currencySign));
341 }
342 if (!currencyDisplayString.empty()) {
343 map.insert(std::make_pair("currencyDisplay", currencyDisplayString));
344 }
345 if (!signDisplayString.empty()) {
346 map.insert(std::make_pair("signDisplay", signDisplayString));
347 }
348 if (!compactDisplay.empty()) {
349 map.insert(std::make_pair("compactDisplay", compactDisplay));
350 }
351 if (!unitDisplayString.empty()) {
352 map.insert(std::make_pair("unitDisplay", unitDisplayString));
353 }
354 if (!unitUsage.empty()) {
355 map.insert(std::make_pair("unitUsage", unitUsage));
356 }
357 if (!unit.empty()) {
358 map.insert(std::make_pair("unit", unit));
359 }
360 GetDigitsResolvedOptions(map);
361 }
362
GetDigitsResolvedOptions(std::map<std::string,std::string> & map)363 void NumberFormat::GetDigitsResolvedOptions(std::map<std::string, std::string> &map)
364 {
365 if (!numberingSystem.empty()) {
366 map.insert(std::make_pair("numberingSystem", numberingSystem));
367 } else if (!(localeInfo->GetNumberingSystem()).empty()) {
368 map.insert(std::make_pair("numberingSystem", localeInfo->GetNumberingSystem()));
369 } else {
370 UErrorCode status = U_ZERO_ERROR;
371 auto numSys = std::unique_ptr<icu::NumberingSystem>(icu::NumberingSystem::createInstance(locale, status));
372 if (U_SUCCESS(status)) {
373 map.insert(std::make_pair("numberingSystem", numSys->getName()));
374 }
375 }
376 if (!useGrouping.empty()) {
377 map.insert(std::make_pair("useGrouping", useGrouping));
378 }
379 if (!minimumIntegerDigits.empty()) {
380 map.insert(std::make_pair("minimumIntegerDigits", minimumIntegerDigits));
381 }
382 if (!minimumFractionDigits.empty()) {
383 map.insert(std::make_pair("minimumFractionDigits", minimumFractionDigits));
384 }
385 if (!maximumFractionDigits.empty()) {
386 map.insert(std::make_pair("maximumFractionDigits", maximumFractionDigits));
387 }
388 if (!minimumSignificantDigits.empty()) {
389 map.insert(std::make_pair("minimumSignificantDigits", minimumSignificantDigits));
390 }
391 if (!maximumSignificantDigits.empty()) {
392 map.insert(std::make_pair("maximumSignificantDigits", maximumSignificantDigits));
393 }
394 if (!localeMatcher.empty()) {
395 map.insert(std::make_pair("localeMatcher", localeMatcher));
396 }
397 if (!notationString.empty()) {
398 map.insert(std::make_pair("notation", notationString));
399 }
400 }
401
GetCurrency() const402 std::string NumberFormat::GetCurrency() const
403 {
404 return currency;
405 }
406
GetCurrencySign() const407 std::string NumberFormat::GetCurrencySign() const
408 {
409 return currencySign;
410 }
411
GetStyle() const412 std::string NumberFormat::GetStyle() const
413 {
414 return styleString;
415 }
416
GetNumberingSystem() const417 std::string NumberFormat::GetNumberingSystem() const
418 {
419 return numberingSystem;
420 }
421
GetUseGrouping() const422 std::string NumberFormat::GetUseGrouping() const
423 {
424 return useGrouping;
425 }
426
GetMinimumIntegerDigits() const427 std::string NumberFormat::GetMinimumIntegerDigits() const
428 {
429 return minimumIntegerDigits;
430 }
431
GetMinimumFractionDigits() const432 std::string NumberFormat::GetMinimumFractionDigits() const
433 {
434 return minimumFractionDigits;
435 }
436
GetMaximumFractionDigits() const437 std::string NumberFormat::GetMaximumFractionDigits() const
438 {
439 return maximumFractionDigits;
440 }
441
GetMinimumSignificantDigits() const442 std::string NumberFormat::GetMinimumSignificantDigits() const
443 {
444 return minimumSignificantDigits;
445 }
446
GetMaximumSignificantDigits() const447 std::string NumberFormat::GetMaximumSignificantDigits() const
448 {
449 return maximumSignificantDigits;
450 }
451
GetLocaleMatcher() const452 std::string NumberFormat::GetLocaleMatcher() const
453 {
454 return localeMatcher;
455 }
456
Init()457 bool NumberFormat::Init()
458 {
459 SetHwIcuDirectory();
460 return true;
461 }
462 } // namespace I18n
463 } // namespace Global
464 } // namespace OHOS
465