diff --git a/frameworks/intl/BUILD.gn b/frameworks/intl/BUILD.gn index cceb647c..4f513246 100755 --- a/frameworks/intl/BUILD.gn +++ b/frameworks/intl/BUILD.gn @@ -52,9 +52,11 @@ ohos_shared_library("intl_util") { "src/i18n_calendar.cpp", "src/locale_config.cpp", "src/locale_info.cpp", + "src/measure_data.cpp", "src/number_format.cpp", "src/phone_number_format.cpp", "src/plural_rules.cpp", + "src/str_util.cpp", ] cflags_cc = [ "-Wall", diff --git a/frameworks/intl/include/measure_data.h b/frameworks/intl/include/measure_data.h new file mode 100644 index 00000000..a1117a33 --- /dev/null +++ b/frameworks/intl/include/measure_data.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except", "in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef OHOS_GLOBAL_I18N_MEASURE_DATA_H +#define OHOS_GLOBAL_I18N_MEASURE_DATA_H + +#include +#include + +namespace OHOS { +namespace Global { +namespace I18n { +uint32_t GetMask(const std::string ®ion); +void GetDefaultPreferredUnit(const std::string ®ion, const std::string &type, std::vector &units); +void GetFallbackPreferredUnit(const std::string ®ion, const std::string &usage, std::vector &units); +void GetRestPreferredUnit(const std::string ®ion, const std::string &usage, std::vector &units); +void GetPreferredUnit(const std::string ®ion, const std::string &usage, std::vector &units); +void ComputeFactorValue(const std::string &unit, const std::string &measSys, std::vector &factors); +double ComputeSIPrefixValue(const std::string &unit); +void ComputePowerValue(const std::string &unit, const std::string &measSys, std::vector &factors); +int ComputeValue(const std::string &unit, const std::string &measSys, std::vector &factors); +int Convert(double &value, const std::string &fromUnit, const std::string &fromMeasSys, const std::string &toUnit, + const std::string &toMeasSys); +} // namespace I18n +} // namespace Global +} // namespace OHOS +#endif \ No newline at end of file diff --git a/frameworks/intl/include/number_format.h b/frameworks/intl/include/number_format.h index 23fedffb..800282c9 100644 --- a/frameworks/intl/include/number_format.h +++ b/frameworks/intl/include/number_format.h @@ -15,6 +15,10 @@ #ifndef OHOS_GLOBAL_I18N_NUMBER_FORMAT_H #define OHOS_GLOBAL_I18N_NUMBER_FORMAT_H +#include +#include +#include +#include #include "unicode/numberformatter.h" #include "unicode/locid.h" #include "unicode/numfmt.h" @@ -27,12 +31,12 @@ #include "unicode/measure.h" #include "unicode/currunit.h" #include "unicode/fmtable.h" +#include "unicode/ures.h" +#include "unicode/ulocdata.h" #include "number_utils.h" #include "number_utypes.h" #include "locale_info.h" -#include -#include -#include +#include "measure_data.h" namespace OHOS { namespace Global { @@ -75,6 +79,9 @@ private: std::string maximumSignificantDigits; std::string localeBaseName; std::string localeMatcher; + std::string unitUsage; + std::string unitType; + std::string unitMeasSys; LocaleInfo *localeInfo = nullptr; icu::number::LocalizedNumberFormatter numberFormat; icu::number::Notation notation = icu::number::Notation::simple(); @@ -82,13 +89,15 @@ private: UNumberUnitWidth currencyDisplay = UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT; UNumberSignDisplay signDisplay = UNumberSignDisplay::UNUM_SIGN_AUTO; static const int MAX_UNIT_NUM = 500; + static const int DEFAULT_FRACTION_DIGITS = 3; icu::MeasureUnit unitArray[MAX_UNIT_NUM]; static bool icuInitialized; static bool Init(); - static std::map unitStyle; - static std::map currencyStyle; - static std::map signAutoStyle; - static std::map signAccountingStyle; + static std::unordered_map unitStyle; + static std::unordered_map currencyStyle; + static std::unordered_map signAutoStyle; + static std::unordered_map signAccountingStyle; + static std::unordered_map measurementSystem; static std::set allValidLocales; static std::set GetValidLocales(); void ParseConfigs(std::map &configs); diff --git a/frameworks/intl/include/str_util.h b/frameworks/intl/include/str_util.h new file mode 100644 index 00000000..f0070dd2 --- /dev/null +++ b/frameworks/intl/include/str_util.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except", "in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef OHOS_GLOBAL_I18N_STR_UTIL_H +#define OHOS_GLOBAL_I18N_STR_UTIL_H + +#include +#include + +namespace OHOS { +namespace Global { +namespace I18n { +void Split(const std::string &src, const std::string &sep, std::vector &dest); +} // namespace I18n +} // namespace Global +} // namespace OHOS +#endif \ No newline at end of file diff --git a/frameworks/intl/src/measure_data.cpp b/frameworks/intl/src/measure_data.cpp new file mode 100644 index 00000000..1586c3ef --- /dev/null +++ b/frameworks/intl/src/measure_data.cpp @@ -0,0 +1,570 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "measure_data.h" +#include +#include +#include +#include "str_util.h" + +namespace OHOS { +namespace Global { +namespace I18n { +using namespace std; +const double FT_TO_M = 0.3048; +const double FT2_TO_M2 = FT_TO_M * FT_TO_M; +const double FT3_TO_M3 = FT_TO_M * FT2_TO_M2; +const double IN3_TO_M3 = FT3_TO_M3 / (12 * 12 * 12); +const double GAL_IMP_TO_M3 = 0.00454609; +const double GAL_TO_M3 = 231 * IN3_TO_M3; +const int REGION_OFFSET = 7; +const int BASE_VALUE_SIZE = 2; +const int FACTOR_SIZE = 2; +const int CHAR_OFFSET = 48; + +const std::unordered_map> USAGE_001 { + { "area-land-agricult", { "hectare" } }, + { "area-land-commercl", { "hectare" } }, + { "area-land-residntl", { "hectare" } }, + { "length-person", { "centimeter" } }, + { "length-person-small", { "centimeter" } }, + { "length-rainfall", { "millimeter" } }, + { "length-road", { "kilometer" } }, + { "length-road-small", { "meter" } }, + { "length-snowfall", { "centimeter" } }, + { "length-vehicle", { "meter" } }, + { "length-visiblty", { "kilometer" } }, + { "length-visiblty-small", { "meter" } }, + { "speed-road-travel", { "kilometer-per-hour" } }, + { "speed-wind", { "kilometer-per-hour" } }, + { "temperature-person", { "celsius" } }, + { "temperature-weather", { "celsius" } }, + { "volume-vehicle-fuel", { "liter" } }, +}; + +const std::unordered_map> USAGE_DEFAULT_001 { + { "length", { "kilometer", "meter", "centimeter" } }, + { "area", { "square-kilometer", "hectare", "square-meter", "square-centimeter" } }, + { "speed", { "kilometer-per-hour" } }, + { "temperature", { "celsius" } }, + { "volume", { "cubic-meter", "cubic-centimeter" } }, +}; + +const std::unordered_map> USAGE_AT { + { "length-person", { "meter", "centimeter" } }, +}; + +const std::unordered_map> USAGE_BR { + { "length-person-informal", { "meter", "centimeter" } }, + { "length-rainfall", { "centimeter" } }, +}; + +const std::unordered_map> USAGE_BS { + { "temperature-weather", { "fahrenheit" } }, +}; + +const std::unordered_map> USAGE_CA { + { "length-person-informal", { "foot", "inch" } }, + { "length-person-small-informal", { "inch" } }, +}; + +const std::unordered_map> USAGE_CN { + { "length-person-informal", { "meter", "centimeter" } }, +}; + +const std::unordered_map> USAGE_DE { + { "length-person-informal", { "meter", "centimeter" } }, + { "length-visiblty", { "meter" } }, +}; + +const std::unordered_map> USAGE_GB { + { "area-land-agricult", { "acre" } }, + { "area-land-commercl", { "acre" } }, + { "area-land-residntl", { "acre" } }, + { "length-person-informal", { "foot", "inch" } }, + { "length-person-small-informal", { "inch" } }, + { "length-road", { "mile" } }, + { "length-road-small", { "yard" } }, + { "length-vehicle", { "foot", "inch" } }, + { "length-visiblty", { "mile" } }, + { "length-visiblty-small", { "foot" } }, + { "speed-road-travel", { "mile-per-hour" } }, +}; + +const std::unordered_map> USAGE_DEFAULT_GB { + { "length", { "mile", "foot", "inch" } }, + { "area", { "square-mile", "acre", "square-foot", "square-inch" } }, + { "speed", { "mile-per-hour" } }, + { "volume", { "cubic-foot", "cubic-inch" } }, +}; + +const std::unordered_map> USAGE_KR { + { "speed-wind", { "meter-per-second" } }, +}; + +const std::unordered_map> USAGE_MX { + { "length-person-informal", { "meter", "centimeter" } }, + { "length-vehicle", { "meter", "centimeter" } }, +}; + +const std::unordered_map> USAGE_NL { + { "length-person-informal", { "meter", "centimeter" } }, + { "length-visiblty", { "meter", "centimeter" } }, +}; + +const std::unordered_map> USAGE_NO { + { "length-person-informal", { "meter", "centimeter" } }, + { "speed-wind", { "meter-per-second" } }, +}; + +const std::unordered_map> USAGE_SE { + { "length-person", { "meter", "centimeter" } }, + { "length-road-informal", { "mile-scandinavian" } }, + { "speed-wind", { "meter-per-second" } }, +}; + +const std::unordered_map> USAGE_US { + { "area-land-agricult", { "acre" } }, + { "area-land-commercl", { "acre" } }, + { "area-land-residntl", { "acre" } }, + { "length-person", { "inch" } }, + { "length-person-informal", { "foot", "inch" } }, + { "length-person-small", { "inch" } }, + { "length-rainfall", { "inch" } }, + { "length-road", { "mile" } }, + { "length-road-small", { "foot" } }, + { "length-snowfall", { "inch" } }, + { "length-vehicle", { "foot", "inch" } }, + { "length-visiblty", { "mile" } }, + { "length-visiblty-small", { "foot" } }, + { "speed-road-travel", { "mile-per-hour" } }, + { "speed-wind", { "mile-per-hour" } }, + { "temperature-person", { "fahrenheit" } }, + { "temperature-weather", { "fahrenheit" } }, + { "volume-vehicle-fuel", { "gallon" } }, +}; + +const std::unordered_map> USAGE_DEFAULT_US { + { "length", { "mile", "foot", "inch" } }, + { "area", { "square-mile", "acre", "square-foot", "square-inch" } }, + { "speed", { "mile-per-hour" } }, + { "temperature", { "fahrenheit" } }, + { "volume", { "cubic-foot", "cubic-inch" } }, +}; + +const std::unordered_map> CONVERT_FACTORS { + { "acre", { FT2_TO_M2 * 43560, 0 } }, + { "hectare", { 10000, 0 } }, + { "dunam", { 1000, 0 } }, + { "fathom", { FT_TO_M * 6, 0 } }, + { "foot", { FT_TO_M, 0 } }, + { "furlong", { FT_TO_M * 660, 0 } }, + { "inch", { FT_TO_M / 12, 0 } }, + { "meter", { 1, 0 } }, + { "mile", { FT_TO_M * 5280, 0 } }, + { "mile-scandinavian", { 10000, 0 } }, + { "nautical-mile", { 1852, 0 } }, + { "point", { FT_TO_M / 864, 0 } }, + { "yard", { FT_TO_M * 3, 0 } }, + { "knot", { 1852 / 3600.0, 0 } }, + { "kilometer-per-hour", { 1000 / 3600.0, 0 } }, + { "celsius", { 1, 273.15 }}, + { "fahrenheit", {5.0 / 9, 2298.35 / 9 }}, + { "kelvin", { 1, 0 } }, + { "acre-foot", { 43560 * FT3_TO_M3, 0 } }, + { "bushel", { GAL_IMP_TO_M3 * 8, 0 } }, + { "bushel-UK", { GAL_IMP_TO_M3 * 8, 0 } }, + { "bushel-US", { IN3_TO_M3 * 2150.42, 0 } }, + { "cup", { GAL_IMP_TO_M3 / 16, 0 } }, + { "cup-UK", { GAL_IMP_TO_M3 / 16, 0 } }, + { "cup-US", { GAL_TO_M3 / 16, 0 } }, + { "cup-metric", {0.00025, 0 } }, + { "fluid-ounce", { GAL_IMP_TO_M3 / 128, 0 } }, + { "fluid-ounce-imperial", { GAL_IMP_TO_M3 / 160, 0 } }, + { "gallon", { GAL_TO_M3, 0 } }, + { "gallon-imperial", { GAL_IMP_TO_M3, 0 } }, + { "liter", {0.001, 0 } }, + { "pint-metric", { 0.0005, 0 } }, + { "pint", { GAL_IMP_TO_M3 / 8, 0 } }, + { "pint-UK", { GAL_IMP_TO_M3 / 8, 0 } }, + { "pint-US", { GAL_TO_M3 / 8, 0 } }, + { "quart", { GAL_IMP_TO_M3 / 4, 0 } }, + { "quart-UK", { GAL_IMP_TO_M3 / 4, 0 } }, + { "quart-US", { GAL_TO_M3 / 4, 0 } }, + { "tablespoon", { GAL_IMP_TO_M3 / 256, 0 } }, + { "tablespoon-UK", { GAL_IMP_TO_M3 / 256, 0 } }, + { "tablespoon-US", { GAL_TO_M3 / 256, 0 } }, + { "teaspoon", { GAL_IMP_TO_M3 / (16 * 48), 0 } }, + { "teaspoon-UK", { GAL_IMP_TO_M3 / (16 * 48), 0 } }, + { "teaspoon-US", { GAL_TO_M3 / (16 * 48), 0 } }, + { "barrel", { GAL_IMP_TO_M3 * 36, 0 } }, + { "barrel-UK", { GAL_IMP_TO_M3 * 36, 0 } }, + { "barrel-US", { GAL_TO_M3 * 42, 0 } }, + { "hour", { 3600, 0 } }, + { "second", { 1, 0 } }, +}; + +const std::unordered_map CONVERT_TO_UNIT { + { "acre", "sqaure-meter" }, + { "hectare", "sqaure-meter" }, + { "dunam", "sqaure-meter" }, + { "fathom", "meter" }, + { "foot", "meter" }, + { "furlong", "meter" }, + { "inch", "meter" }, + { "meter", "meter" }, + { "mile", "meter" }, + { "mile-scandinavian", "meter" }, + { "nautical-mile", "meter" }, + { "point", "meter" }, + { "yard", "meter" }, + { "knot", "meter-per-second" }, + { "kilometer-per-hour", "meter-per-second" }, + { "celsius", "kelvin" }, + { "fahrenheit", "kelvin" }, + { "kelvin", "kelvin" }, + { "acre-foot", "cubic-meter" }, + { "bushel", "cubic-meter" }, + { "bushel-UK", "cubic-meter" }, + { "bushel-US", "cubic-meter" }, + { "cup", "cubic-meter" }, + { "cup-UK", "cubic-meter" }, + { "cup-US", "cubic-meter" }, + { "cup-metric", "cubic-meter" }, + { "fluid-ounce", "cubic-meter" }, + { "fluid-ounce-imperial", "cubic-meter" }, + { "gallon", "cubic-meter" }, + { "gallon-imperial", "cubic-meter" }, + { "liter", "cubic-meter" }, + { "pint-metric", "cubic-meter" }, + { "pint", "cubic-meter" }, + { "pint-UK", "cubic-meter" }, + { "pint-US", "cubic-meter" }, + { "quart", "cubic-meter" }, + { "quart-UK", "cubic-meter" }, + { "quart-US", "cubic-meter" }, + { "tablespoon", "cubic-meter" }, + { "tablespoon-UK", "cubic-meter" }, + { "tablespoon-US", "cubic-meter" }, + { "teaspoon", "cubic-meter" }, + { "teaspoon-UK", "cubic-meter" }, + { "teaspoon-US", "cubic-meter" }, + { "barrel", "cubic-meter" }, + { "barrel-US", "cubic-meter" }, + { "barrel-UK", "cubic-meter" }, + { "hour", "second" }, + { "second", "second" }, +}; + +const std::unordered_map PREFIX_VALUE { + { "deci", pow(10, -1) }, + { "centi", pow(10, -2) }, + { "milli", pow(10, -3) }, + { "micro", pow(10, -6) }, + { "nano", pow(10, -9) }, + { "pico", pow(10, -12) }, + { "kilo", pow(10, 3) }, + { "hecto", pow(10, 2) }, + { "mega", pow(10, 6) }, +}; + +const std::unordered_map POWER_VALUE { + { "square-", 2 }, + { "cubic-", 3 }, +}; + +uint32_t GetMask(const string ®ion) +{ + uint32_t firstChar = (region.c_str()[0] - CHAR_OFFSET); + uint32_t secondChar = (region.c_str()[1] - CHAR_OFFSET); + return (firstChar << REGION_OFFSET) | secondChar; +} + +void GetDefaultPreferredUnit(const string ®ion, const string &type, vector &units) +{ + switch (GetMask(region)) { + case 0x00000B92: { + if (USAGE_DEFAULT_GB.count(type) > 0) { + units.assign(USAGE_DEFAULT_GB.at(type).begin(), USAGE_DEFAULT_GB.at(type).end()); + } + break; + } + case 0x000012A3: { + if (USAGE_DEFAULT_US.count(type) > 0) { + units.assign(USAGE_DEFAULT_US.at(type).begin(), USAGE_DEFAULT_US.at(type).end()); + } + break; + } + default: { + if (USAGE_DEFAULT_001.count(type) > 0) { + units.assign(USAGE_DEFAULT_001.at(type).begin(), USAGE_DEFAULT_001.at(type).end()); + } + break; + } + } +} + +void GetFallbackPreferredUnit(const string ®ion, const string &usage, vector &units) +{ + switch (GetMask(region)) { + case 0x00000EA8: { + if (USAGE_MX.count(usage) > 0) { + units.assign(USAGE_MX.at(usage).begin(), USAGE_MX.at(usage).end()); + return; + } + break; + } + case 0x00000F1C: { + if (USAGE_NL.count(usage) > 0) { + units.assign(USAGE_NL.at(usage).begin(), USAGE_NL.at(usage).end()); + return; + } + break; + } + case 0x00000F1F: + case 0x0000101C: + case 0x00001125: { + if (USAGE_NO.count(usage) > 0) { + units.assign(USAGE_NO.at(usage).begin(), USAGE_NO.at(usage).end()); + return; + } + break; + } + case 0x00001195: { + if (USAGE_SE.count(usage) > 0) { + units.assign(USAGE_SE.at(usage).begin(), USAGE_SE.at(usage).end()); + return; + } + break; + } + case 0x000012A3: { + if (USAGE_US.count(usage) > 0) { + units.assign(USAGE_US.at(usage).begin(), USAGE_US.at(usage).end()); + return; + } + break; + } + default: { + if (USAGE_001.count(usage) > 0) { + units.assign(USAGE_001.at(usage).begin(), USAGE_001.at(usage).end()); + return; + } + break; + } + } +} + +void GetRestPreferredUnit(const string ®ion, const string &usage, vector &units) +{ + switch (GetMask(region)) { + case 0x00000991: + case 0x00000C9E: { + if (USAGE_CA.count(usage) > 0) { + units.assign(USAGE_CA.at(usage).begin(), USAGE_CA.at(usage).end()); + return; + } + break; + } + case 0x0000099E: + case 0x00000A1B: + case 0x00001024: { + if (USAGE_CN.count(usage) > 0) { + units.assign(USAGE_CN.at(usage).begin(), USAGE_CN.at(usage).end()); + return; + } + break; + } + case 0x00000A15: { + if (USAGE_DE.count(usage) > 0) { + units.assign(USAGE_DE.at(usage).begin(), USAGE_DE.at(usage).end()); + return; + } + break; + } + case 0x00000B92: { + if (USAGE_GB.count(usage) > 0) { + units.assign(USAGE_GB.at(usage).begin(), USAGE_GB.at(usage).end()); + return; + } + break; + } + case 0x00000DA2: { + if (USAGE_KR.count(usage) > 0) { + units.assign(USAGE_KR.at(usage).begin(), USAGE_KR.at(usage).end()); + return; + } + break; + } + default: { + GetFallbackPreferredUnit(region, usage, units); + break; + } + } + if (units.size() == 0) { + GetFallbackPreferredUnit(region, usage, units); + } +} + +void GetPreferredUnit(const string ®ion, const string &usage, vector &units) +{ + switch (GetMask(region)) { + case 0x000008A4: + case 0x00000915: + case 0x00000A2A: + case 0x00000A97: + case 0x00000AA3: + case 0x00000B22: + case 0x00000C1B: + case 0x00000C94: + case 0x00000C9C: + case 0x00000CA4: + case 0x00000D1F: + case 0x00000EA9: + case 0x00001191: + case 0x00001222: + case 0x0000131E: { + if (USAGE_AT.count(usage) > 0) { + units.assign(USAGE_AT.at(usage).begin(), USAGE_AT.at(usage).end()); + return; + } + break; + } + case 0x00000922: { + if (USAGE_BR.count(usage) > 0) { + units.assign(USAGE_BR.at(usage).begin(), USAGE_BR.at(usage).end()); + return; + } + break; + } + case 0x00000923: + case 0x0000092A: + case 0x00001022: + case 0x00001027: { + if (USAGE_BS.count(usage) > 0) { + units.assign(USAGE_BS.at(usage).begin(), USAGE_BS.at(usage).end()); + return; + } + break; + } + default: { + GetRestPreferredUnit(region, usage, units); + break; + } + } + if (units.size() == 0) { + GetRestPreferredUnit(region, usage, units); + } +} + +void ComputeFactorValue(const string &unit, const string &measSys, vector &factors) +{ + string unitKey = unit + "-" + measSys; + if (CONVERT_FACTORS.count(unitKey) > 0) { + factors[0] = CONVERT_FACTORS.at(unitKey)[0]; + factors[1] = CONVERT_FACTORS.at(unitKey)[1]; + return; + } + if (CONVERT_FACTORS.count(unit) > 0) { + factors[0] = CONVERT_FACTORS.at(unit)[0]; + factors[1] = CONVERT_FACTORS.at(unit)[1]; + } +} + +double ComputeSIPrefixValue(const string &unit) +{ + for (auto& prefixValue : PREFIX_VALUE) { + if (unit.rfind(prefixValue.first, 0) == 0) { + return prefixValue.second; + } + } + return 0.0; +} + +void ComputePowerValue(const string &unit, const string &measSys, vector &factors) +{ + for (auto& powerValue : POWER_VALUE) { + if (unit.rfind(powerValue.first, 0) == 0) { + string baseUnit = unit.substr(powerValue.first.length()); + double value = ComputeSIPrefixValue(baseUnit); + double compare = 0.0; + if (fabs(value - compare) < 1e-6) { + ComputeFactorValue(baseUnit, measSys, factors); + } + factors[0] = pow(factors[0], powerValue.second); + } + } +} + +int ComputeValue(const string &unit, const string &measSys, vector &factors) +{ + if (unit.find("-per-") != string::npos) { + vector baseValues; + Split(unit, "-per-", baseValues); + if (baseValues.size() == BASE_VALUE_SIZE) { + vector numerator = { 1.0, 0.0 }; + int status = ComputeValue(baseValues[0], measSys, numerator); + if (status == 0) { + return 0; + } + vector denominator = { 1.0, 0.0 }; + status = ComputeValue(baseValues[1], measSys, denominator); + if (status == 0) { + return 0; + } + factors[0] = numerator[0] / denominator[0]; + return 1; + } + } + double compare = 0.0; + factors[0] = ComputeSIPrefixValue(unit); + if (fabs(factors[0] - compare) < 1e-6) { + ComputePowerValue(unit, measSys, factors); + } + if (fabs(factors[0] - compare) < 1e-6) { + ComputeFactorValue(unit, measSys, factors); + } + if (fabs(factors[0] - compare) < 1e-6) { + factors[0] = 1.0; + return 0; + } + return 1; +} + +int Convert(double &value, const string &fromUnit, const string &fromMeasSys, const string &toUnit, + const string &toMeasSys) +{ + double baseResult = 0.0; + double result = 0.0; + vector fromFactors = {0.0, 0.0}; + int status = ComputeValue(fromUnit, fromMeasSys, fromFactors); + if (status == 0) { + return 0; + } + vector toFactors = {0.0, 0.0}; + status = ComputeValue(toUnit, toMeasSys, toFactors); + if (status == 0) { + return 0; + } + if (fromFactors.size() == FACTOR_SIZE) { + baseResult = fromFactors[0] * value + fromFactors[1]; + } + if (toFactors.size() == FACTOR_SIZE) { + result = (baseResult - toFactors[1]) / toFactors[0]; + } + value = result; + return 1; +} +} // I18n +} // Global +} // OHOS \ No newline at end of file diff --git a/frameworks/intl/src/number_format.cpp b/frameworks/intl/src/number_format.cpp index 59a0753e..f2236739 100644 --- a/frameworks/intl/src/number_format.cpp +++ b/frameworks/intl/src/number_format.cpp @@ -36,33 +36,39 @@ std::set NumberFormat::GetValidLocales() return allValidLocales; } -std::map NumberFormat::unitStyle = { +std::unordered_map NumberFormat::unitStyle = { { "long", UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME }, { "short", UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT }, { "narrow", UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW } }; -std::map NumberFormat::currencyStyle = { +std::unordered_map NumberFormat::currencyStyle = { { "symbol", UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT }, { "code", UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE }, { "name", UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME }, { "narrowSymbol", UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW } }; -std::map NumberFormat::signAutoStyle = { +std::unordered_map NumberFormat::signAutoStyle = { { "auto", UNumberSignDisplay::UNUM_SIGN_AUTO }, { "never", UNumberSignDisplay::UNUM_SIGN_NEVER }, { "always", UNumberSignDisplay::UNUM_SIGN_ALWAYS }, { "exceptZero", UNumberSignDisplay::UNUM_SIGN_EXCEPT_ZERO } }; -std::map NumberFormat::signAccountingStyle = { +std::unordered_map NumberFormat::signAccountingStyle = { { "auto", UNumberSignDisplay::UNUM_SIGN_ACCOUNTING }, { "never", UNumberSignDisplay::UNUM_SIGN_NEVER }, { "always", UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_ALWAYS }, { "exceptZero", UNumberSignDisplay::UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO } }; +std::unordered_map NumberFormat::measurementSystem = { + { UMeasurementSystem::UMS_SI, "SI" }, + { UMeasurementSystem::UMS_US, "US" }, + { UMeasurementSystem::UMS_UK, "UK" }, +}; + NumberFormat::NumberFormat(const std::vector &localeTags, std::map &configs) { UErrorCode status = U_ZERO_ERROR; @@ -115,9 +121,14 @@ void NumberFormat::InitProperties() for (icu::MeasureUnit curUnit : unitArray) { if (strcmp(curUnit.getSubtype(), unit.c_str()) == 0) { numberFormat = numberFormat.unit(curUnit); + unitType = curUnit.getType(); } } + UErrorCode status = U_ZERO_ERROR; + UMeasurementSystem measSys = ulocdata_getMeasurementSystem(localeBaseName.c_str(), &status); + unitMeasSys = measurementSystem[measSys]; numberFormat = numberFormat.unitWidth(unitDisplay); + numberFormat = numberFormat.precision(icu::number::Precision::maxFraction(DEFAULT_FRACTION_DIGITS)); } if (!useGrouping.empty()) { numberFormat.grouping((useGrouping == "true") ? @@ -176,6 +187,9 @@ void NumberFormat::ParseConfigs(std::map &configs) unitDisplayString = configs["unitDisplay"]; unitDisplay = unitStyle[unitDisplayString]; } + if (configs.count("unitUsage") > 0) { + unitUsage = configs["unitUsage"]; + } } if (styleString == "currency" && configs.count("currency") > 0) { currency = configs["currency"]; @@ -239,9 +253,47 @@ void NumberFormat::ParseDigitsConfigs(std::map &config std::string NumberFormat::Format(double number) { + double finalNumber = number; + if (!unitUsage.empty()) { + std::vector preferredUnits; + if (unitUsage == "default") { + GetDefaultPreferredUnit(localeInfo->GetRegion(), unitType, preferredUnits); + } else { + GetPreferredUnit(localeInfo->GetRegion(), unitUsage, preferredUnits); + } + std::map preferredValuesOverOne; + std::map preferredValuesUnderOne; + double num = number; + for (size_t i = 0; i < preferredUnits.size(); i++) { + int status = Convert(num, unit, unitMeasSys, preferredUnits[i], unitMeasSys); + if (status == 0) { + continue; + } + if (num >= 1) { + preferredValuesOverOne.insert(std::make_pair(num, preferredUnits[i])); + } else { + preferredValuesUnderOne.insert(std::make_pair(num, preferredUnits[i])); + } + } + std::string preferredUnit; + if (preferredValuesOverOne.size() > 0) { + finalNumber = preferredValuesOverOne.begin()->first; + preferredUnit = preferredValuesOverOne.begin()->second; + } else if (preferredValuesUnderOne.size() > 0) { + finalNumber = preferredValuesUnderOne.rbegin()->first; + preferredUnit = preferredValuesUnderOne.rbegin()->second; + } + if (!preferredUnit.empty()) { + for (icu::MeasureUnit curUnit : unitArray) { + if (strcmp(curUnit.getSubtype(), preferredUnit.c_str()) == 0) { + numberFormat = numberFormat.unit(curUnit); + } + } + } + } std::string result; UErrorCode status = U_ZERO_ERROR; - numberFormat.formatDouble(number, status).toString(status).toUTF8String(result); + numberFormat.formatDouble(finalNumber, status).toString(status).toUTF8String(result); return result; } @@ -269,6 +321,9 @@ void NumberFormat::GetResolvedOptions(std::map &map) if (!unitDisplayString.empty()) { map.insert(std::make_pair("unitDisplay", unitDisplayString)); } + if (!unitUsage.empty()) { + map.insert(std::make_pair("unitUsage", unitUsage)); + } if (!unit.empty()) { map.insert(std::make_pair("unit", unit)); } diff --git a/frameworks/intl/src/str_util.cpp b/frameworks/intl/src/str_util.cpp new file mode 100644 index 00000000..ef21b4f6 --- /dev/null +++ b/frameworks/intl/src/str_util.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "str_util.h" + +namespace OHOS { +namespace Global { +namespace I18n { +using namespace std; + +void Split(const string &src, const string &sep, vector &dest) +{ + if (src == "") { + return; + } + string::size_type begin = 0; + string::size_type end = src.find(sep); + while (end != string::npos) { + dest.push_back(src.substr(begin, end - begin)); + begin = end + sep.size(); + end = src.find(sep, begin); + } + if (begin != src.size()) { + dest.push_back(src.substr(begin)); + } +} +} // namespace I18n +} // namespace Global +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/js/kits/include/i18n_addon.h b/interfaces/js/kits/include/i18n_addon.h index 1a1321d3..93cced06 100644 --- a/interfaces/js/kits/include/i18n_addon.h +++ b/interfaces/js/kits/include/i18n_addon.h @@ -21,11 +21,17 @@ #include "i18n_calendar.h" #include "napi/native_node_api.h" #include "locale_config.h" +#include "measure_data.h" +#include "number_format.h" #include "phone_number_format.h" namespace OHOS { namespace Global { namespace I18n { +void GetOptionMap(napi_env env, napi_value argv, std::map &map); +void GetOptionValue(napi_env env, napi_value options, const std::string &optionName, + std::string &value); + class I18nAddon { public: static napi_value Init(napi_env env, napi_value exports); @@ -46,6 +52,7 @@ public: static napi_value IsRTL(napi_env env, napi_callback_info info); static napi_value InitPhoneNumberFormat(napi_env env, napi_value exports); static napi_value InitI18nCalendar(napi_env env, napi_value exports); + static napi_value UnitConvert(napi_env env, napi_callback_info info); private: static napi_value PhoneNumberFormatConstructor(napi_env env, napi_callback_info info); diff --git a/interfaces/js/kits/include/intl_addon.h b/interfaces/js/kits/include/intl_addon.h index 00f1b4e0..98f92ab9 100644 --- a/interfaces/js/kits/include/intl_addon.h +++ b/interfaces/js/kits/include/intl_addon.h @@ -29,22 +29,22 @@ namespace OHOS { namespace Global { namespace I18n { -static void GetLocaleTags(napi_env env, napi_value rawLocaleTag, std::vector &localeTags); -static void GetOptionValue(napi_env env, napi_value options, const std::string &optionName, +void GetLocaleTags(napi_env env, napi_value rawLocaleTag, std::vector &localeTags); +void GetOptionValue(napi_env env, napi_value options, const std::string &optionName, std::map &map); -static void GetBoolOptionValue(napi_env env, napi_value options, const std::string &optionName, +void GetBoolOptionValue(napi_env env, napi_value options, const std::string &optionName, std::map &map); -static void GetIntegerOptionValue(napi_env env, napi_value options, const std::string &optionName, +void GetIntegerOptionValue(napi_env env, napi_value options, const std::string &optionName, std::map &map); -static void GetDateOptionValues(napi_env env, napi_value options, std::map &map); -static void GetNumberOptionValues(napi_env env, napi_value options, std::map &map); -static void GetCollatorOptionValue(napi_env env, napi_value options, std::map &map); -static void GetPluralRulesOptionValues(napi_env env, napi_value options, std::map &map); -static void SetOptionProperties(napi_env env, napi_value &result, std::map &options, +void GetDateOptionValues(napi_env env, napi_value options, std::map &map); +void GetNumberOptionValues(napi_env env, napi_value options, std::map &map); +void GetCollatorOptionValue(napi_env env, napi_value options, std::map &map); +void GetPluralRulesOptionValues(napi_env env, napi_value options, std::map &map); +void SetOptionProperties(napi_env env, napi_value &result, std::map &options, const std::string &option); -static void SetIntegerOptionProperties(napi_env env, napi_value &result, +void SetIntegerOptionProperties(napi_env env, napi_value &result, std::map &options, const std::string &option); -static void SetBooleanOptionProperties(napi_env env, napi_value &result, +void SetBooleanOptionProperties(napi_env env, napi_value &result, std::map &options, const std::string &option); class IntlAddon { diff --git a/interfaces/js/kits/src/i18n_addon.cpp b/interfaces/js/kits/src/i18n_addon.cpp index 088076d4..da4873ee 100644 --- a/interfaces/js/kits/src/i18n_addon.cpp +++ b/interfaces/js/kits/src/i18n_addon.cpp @@ -83,6 +83,22 @@ void I18nAddon::Destructor(napi_env env, void *nativeObject, void *hint) napi_value I18nAddon::Init(napi_env env, napi_value exports) { napi_status status = napi_ok; + napi_value util; + status = napi_create_object(env, &util); + if (status != napi_ok) { + HiLog::Error(LABEL, "Failed to create util object at init"); + return nullptr; + } + napi_property_descriptor utilProperties[] = { + DECLARE_NAPI_FUNCTION("unitConvert", UnitConvert), + }; + status = napi_define_properties(env, util, + sizeof(utilProperties) / sizeof(napi_property_descriptor), + utilProperties); + if (status != napi_ok) { + HiLog::Error(LABEL, "Failed to set properties of util at init"); + return nullptr; + } napi_property_descriptor properties[] = { DECLARE_NAPI_FUNCTION("getSystemLanguages", GetSystemLanguages), DECLARE_NAPI_FUNCTION("getSystemCountries", GetSystemCountries), @@ -97,6 +113,7 @@ napi_value I18nAddon::Init(napi_env env, napi_value exports) DECLARE_NAPI_FUNCTION("setSystemLocale", SetSystemLocale), DECLARE_NAPI_FUNCTION("getCalendar", GetCalendar), DECLARE_NAPI_FUNCTION("isRTL", IsRTL), + DECLARE_NAPI_PROPERTY("Util", util), }; status = napi_define_properties(env, exports, @@ -109,6 +126,99 @@ napi_value I18nAddon::Init(napi_env env, napi_value exports) return exports; } +void GetOptionValue(napi_env env, napi_value options, const std::string &optionName, + std::string &value) +{ + napi_value optionValue = nullptr; + napi_valuetype type = napi_undefined; + napi_status status = napi_typeof(env, options, &type); + if (status != napi_ok && type != napi_object) { + HiLog::Error(LABEL, "Get option failed, option is not an object"); + return; + } + bool hasProperty = false; + napi_status propStatus = napi_has_named_property(env, options, optionName.c_str(), &hasProperty); + if (propStatus == napi_ok && hasProperty) { + status = napi_get_named_property(env, options, optionName.c_str(), &optionValue); + if (status == napi_ok) { + size_t len; + napi_get_value_string_utf8(env, optionValue, nullptr, 0, &len); + std::vector optionBuf(len + 1); + status = napi_get_value_string_utf8(env, optionValue, optionBuf.data(), len + 1, &len); + if (status != napi_ok) { + HiLog::Error(LABEL, "Failed to get string item"); + return; + } + value = optionBuf.data(); + } + } +} + +void GetOptionMap(napi_env env, napi_value option, std::map &map) +{ + if (option != nullptr) { + size_t len; + napi_get_value_string_utf8(env, option, nullptr, 0, &len); + std::vector styleBuf(len + 1); + napi_status status = napi_get_value_string_utf8(env, option, styleBuf.data(), len + 1, &len); + if (status != napi_ok) { + HiLog::Error(LABEL, "Failed to get string item"); + return; + } + map.insert(std::make_pair("unitDisplay", styleBuf.data())); + } +} + +napi_value I18nAddon::UnitConvert(napi_env env, napi_callback_info info) +{ + size_t argc = 5; + napi_value argv[5] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + std::string fromUnit; + GetOptionValue(env, argv[0], "unit", fromUnit); + std::string fromMeasSys; + GetOptionValue(env, argv[0], "measureSystem", fromMeasSys); + std::string toUnit; + GetOptionValue(env, argv[1], "unit", toUnit); + std::string toMeasSys; + GetOptionValue(env, argv[1], "measureSystem", toMeasSys); + double number; + napi_get_value_double(env, argv[2], &number); // 2 is the index of value + int convertStatus = Convert(number, fromUnit, fromMeasSys, toUnit, toMeasSys); + size_t len; + napi_get_value_string_utf8(env, argv[3], nullptr, 0, &len); // 3 is the index of value + std::vector localeBuf(len + 1); + // 3 is the index of value + status = napi_get_value_string_utf8(env, argv[3], localeBuf.data(), len + 1, &len); + if (status != napi_ok) { + HiLog::Error(LABEL, "Failed to get string item"); + return nullptr; + } + std::vector localeTags; + localeTags.push_back(localeBuf.data()); + std::map map = {}; + map.insert(std::make_pair("style", "unit")); + if (convertStatus == 0) { + HiLog::Error(LABEL, "Do not support the conversion"); + map.insert(std::make_pair("unit", fromUnit)); + } else { + map.insert(std::make_pair("unit", toUnit)); + } + // 4 is the index of value + GetOptionMap(env, argv[4], map); + auto numberFmt = std::make_unique(localeTags, map); + std::string value = numberFmt->Format(number); + napi_value result; + status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Failed to create string item"); + return nullptr; + } + return result; +} + napi_value I18nAddon::GetSystemLanguages(napi_env env, napi_callback_info info) { std::vector systemLanguages; diff --git a/interfaces/js/kits/src/intl_addon.cpp b/interfaces/js/kits/src/intl_addon.cpp index 8eb432bc..b3b3f262 100644 --- a/interfaces/js/kits/src/intl_addon.cpp +++ b/interfaces/js/kits/src/intl_addon.cpp @@ -487,6 +487,7 @@ void GetNumberOptionValues(napi_env env, napi_value options, std::map