diff --git a/frameworks/intl/BUILD.gn b/frameworks/intl/BUILD.gn index 5aaebddf..84643d38 100755 --- a/frameworks/intl/BUILD.gn +++ b/frameworks/intl/BUILD.gn @@ -46,11 +46,13 @@ ohos_shared_library("intl_util") { "//third_party/protobuf/src", ] sources = [ + "src/collator.cpp", "src/date_time_format.cpp", "src/locale_config.cpp", "src/locale_info.cpp", "src/number_format.cpp", "src/phone_number_format.cpp", + "src/plural_rules.cpp", ] cflags_cc = [ "-Wall", diff --git a/frameworks/intl/include/collator.h b/frameworks/intl/include/collator.h new file mode 100644 index 00000000..5347dee2 --- /dev/null +++ b/frameworks/intl/include/collator.h @@ -0,0 +1,68 @@ +/* + * 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 GLOBAL_I18N_STANDARD_COLLATOR_H +#define GLOBAL_I18N_STANDARD_COLLATOR_H + +#include +#include +#include +#include + +#include "unicode/locid.h" +#include "unicode/coll.h" + +#include "locale_info.h" + +namespace OHOS { +namespace Global { +namespace I18n { +class Collator { +public: + Collator(std::vector &localeTags, std::map &options); + ~Collator(); + int32_t Compare(const std::string &first, const std::string &second); + void ResolvedOptions(std::map &options); + +private: + std::string localeStr; + std::string localeMatcher; + std::string usage; + std::string sensitivity; + std::string ignorePunctuation; + std::string numeric; + std::string caseFirst; + std::string collation; + + LocaleInfo *localeInfo; + icu::Locale locale; + icu::Collator *collatorPtr; + + std::set GetValidLocales(); + std::string ParseOption(std::map &options, const std::string &key); + void ParseAllOptions(std::map &options); + bool IsValidCollation(std::string &collation, UErrorCode &status); + void SetCollation(UErrorCode &status); + void SetUsage(UErrorCode &status); + void SetNumeric(UErrorCode &status); + void SetCaseFirst(UErrorCode &status); + void SetSensitivity(UErrorCode &status); + void SetIgnorePunctuation(UErrorCode &status); + bool InitCollator(); +}; +} +} +} + +#endif // GLOBAL_I18N_STANDARD_COLLATOR_H diff --git a/frameworks/intl/include/plural_rules.h b/frameworks/intl/include/plural_rules.h new file mode 100644 index 00000000..45260c42 --- /dev/null +++ b/frameworks/intl/include/plural_rules.h @@ -0,0 +1,63 @@ +/* + * 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 GLOBAL_I18N_STANDARD_PLURAL_RULES_H +#define GLOBAL_I18N_STANDARD_PLURAL_RULES_H + +#include +#include +#include +#include + +#include "unicode/locid.h" +#include "unicode/plurrule.h" +#include "unicode/numberformatter.h" + +#include "locale_info.h" + +namespace OHOS { +namespace Global { +namespace I18n { +class PluralRules { +public: + PluralRules(std::vector &localeTags, std::map &options); + ~PluralRules(); + std::string Select(double number); + +private: + std::string localeStr; + LocaleInfo *localeInfo; + icu::Locale locale; + icu::PluralRules *pluralRules; + icu::number::LocalizedNumberFormatter numberFormatter; + + std::string localeMatcher; + std::string type; + int minInteger; + int minFraction; + int maxFraction; + int minSignificant; + int maxSignificant; + + std::set GetValidLocales(); + std::string ParseOption(std::map &options, const std::string &key); + void ParseAllOptions(std::map &options); + void InitPluralRules(std::vector &localeTags, std::map &options); + void InitNumberFormatter(); +}; +} +} +} + +#endif // GLOBAL_I18N_STANDARD_PLURAL_RULES_H diff --git a/frameworks/intl/src/collator.cpp b/frameworks/intl/src/collator.cpp new file mode 100644 index 00000000..1750a4d9 --- /dev/null +++ b/frameworks/intl/src/collator.cpp @@ -0,0 +1,280 @@ +// +// Created by s00619675 on 2021/10/25. +// + +/* + * 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 "collator.h" + +#include + +#include "unicode/ucol.h" +#include "unicode/errorcode.h" +#include "unicode/uloc.h" + +namespace OHOS { +namespace Global { +namespace I18n { +std::string Collator::ParseOption(std::map &options, const std::string &key) +{ + std::map::iterator it = options.find(key); + if (it != options.end()) { + return it->second; + } else { + return ""; + } +} + +void Collator::ParseAllOptions(std::map &options) +{ + localeMatcher = ParseOption(options, "localeMatcher"); + if (localeMatcher == "") { + localeMatcher = "best fit"; + } + + usage = ParseOption(options, "usage"); + if (usage == "") { + usage = "sort"; + } + + sensitivity = ParseOption(options, "sensitivity"); + if (sensitivity == "") { + sensitivity = "variant"; + } + + ignorePunctuation = ParseOption(options, "ignorePunctuation"); + if (ignorePunctuation == "") { + ignorePunctuation = "false"; + } + + numeric = ParseOption(options, "numeric"); + caseFirst = ParseOption(options, "caseFirst"); + collation = ParseOption(options, "collation"); +} + +std::set Collator::GetValidLocales() +{ + int32_t validCount = 1; + const icu::Locale *validLocaleArray = icu::Locale::getAvailableLocales(validCount); + std::set allValidLocales; + for (int i = 0; i < validCount; i++) { + allValidLocales.insert(validLocaleArray[i].getLanguage()); + } + return allValidLocales; +} + +Collator::Collator(std::vector &localeTags, std::map &options) +{ + ParseAllOptions(options); + + UErrorCode status = UErrorCode::U_ZERO_ERROR; + std::set allValidLocales = GetValidLocales(); + + for (size_t i = 0; i < localeTags.size(); i++) { + std::string curLocale = localeTags[i]; + locale = icu::Locale::forLanguageTag(icu::StringPiece(curLocale), status); + if (allValidLocales.count(locale.getLanguage()) > 0) { + localeInfo = new LocaleInfo(curLocale, options); + locale = localeInfo->GetLocale(); + localeStr = localeInfo->GetBaseName(); + bool createSuccess = InitCollator(); + if (!createSuccess) { + continue; + } + break; + } + } + + if (localeTags.size() == 0) { + icu::Locale defaultLocale; + localeInfo = new LocaleInfo(defaultLocale.getName(), options); + locale = localeInfo->GetLocale(); + localeStr = localeInfo->GetBaseName(); + InitCollator(); + } +} + +bool Collator::IsValidCollation(std::string &collation, UErrorCode &status) +{ + const char *currentCollation = uloc_toLegacyType("collation", collation.c_str()); + std::unique_ptr enumeration( + icu::Collator::getKeywordValuesForLocale("collation", icu::Locale(locale.getBaseName()), false, status)); + int length; + const char *validCollations = enumeration->next(&length, status); + while (validCollations != nullptr) { + if (strcmp(validCollations, currentCollation) == 0) { + return true; + } + validCollations = enumeration->next(&length, status); + } + return false; +} + +void Collator::SetCollation(UErrorCode &status) +{ + if (collation != "") { + if (IsValidCollation(collation, status)) { + locale.setUnicodeKeywordValue("co", collation, status); + } else { + collation = "default"; + locale.setUnicodeKeywordValue("co", nullptr, status); + } + } else { + collation = localeInfo->GetCollation(); + if (collation != "") { + if (IsValidCollation(collation, status)) { + locale.setUnicodeKeywordValue("co", collation, status); + } else { + locale.setUnicodeKeywordValue("co", nullptr, status); + collation = "default"; + } + } else { + locale.setUnicodeKeywordValue("co", nullptr, status); + collation = "default"; + } + } +} + +void Collator::SetUsage(UErrorCode &status) +{ + if (usage == "search") { + collation = "default"; + locale.setUnicodeKeywordValue("co", nullptr, status); + } +} + +void Collator::SetNumeric(UErrorCode &status) +{ + if (numeric == "") { + numeric = localeInfo->GetNumeric(); + if (numeric != "true" && numeric != "false") { + numeric = "false"; + } + } + if (numeric == "true") { + collatorPtr->setAttribute(UColAttribute::UCOL_NUMERIC_COLLATION, + UColAttributeValue::UCOL_ON, status); + } else { + collatorPtr->setAttribute(UColAttribute::UCOL_NUMERIC_COLLATION, + UColAttributeValue::UCOL_OFF, status); + } +} + +void Collator::SetCaseFirst(UErrorCode &status) +{ + if (caseFirst == "") { + caseFirst = localeInfo->GetCaseFirst(); + if (caseFirst != "upper" && caseFirst != "lower" && caseFirst != "false") { + caseFirst = "false"; + } + } + if (caseFirst == "upper") { + collatorPtr->setAttribute(UColAttribute::UCOL_CASE_FIRST, + UColAttributeValue::UCOL_UPPER_FIRST, status); + } else if (caseFirst == "lower") { + collatorPtr->setAttribute(UColAttribute::UCOL_CASE_FIRST, + UColAttributeValue::UCOL_LOWER_FIRST, status); + } else { + collatorPtr->setAttribute(UColAttribute::UCOL_CASE_FIRST, + UColAttributeValue::UCOL_OFF, status); + } +} + +void Collator::SetSensitivity(UErrorCode &status) +{ + if (sensitivity == "base") { + collatorPtr->setStrength(icu::Collator::PRIMARY); + } else if (sensitivity == "accent") { + collatorPtr->setStrength(icu::Collator::SECONDARY); + } else if (sensitivity == "case") { + collatorPtr->setStrength(icu::Collator::PRIMARY); + collatorPtr->setAttribute(UColAttribute::UCOL_CASE_LEVEL, + UColAttributeValue::UCOL_ON, status); + } else { + collatorPtr->setStrength(icu::Collator::TERTIARY); + } +} + +void Collator::SetIgnorePunctuation(UErrorCode &status) +{ + if (ignorePunctuation == "true") { + collatorPtr->setAttribute(UColAttribute::UCOL_ALTERNATE_HANDLING, + UColAttributeValue::UCOL_SHIFTED, status); + } +} + +bool Collator::InitCollator() +{ + UErrorCode status = UErrorCode::U_ZERO_ERROR; + SetCollation(status); + SetUsage(status); + collatorPtr = icu::Collator::createInstance(locale, status); + SetNumeric(status); + SetSensitivity(status); + SetIgnorePunctuation(status); + + if (collatorPtr == nullptr) { + if (localeInfo != nullptr) { + delete localeInfo; + localeInfo = nullptr; + } + return false; + } + return true; +} + +Collator::~Collator() +{ + if (localeInfo != nullptr) { + delete localeInfo; + localeInfo = nullptr; + } + + if (collatorPtr != nullptr) { + delete collatorPtr; + collatorPtr = nullptr; + } +} + +int32_t Collator::Compare(const std::string &first, const std::string &second) +{ + icu::Collator::EComparisonResult result = collatorPtr->compare(icu::UnicodeString(first.data(), first.length()), + icu::UnicodeString(second.data(), second.length())); + if (result == icu::Collator::EComparisonResult::LESS) { + return -1; + } else if (result == icu::Collator::EComparisonResult::EQUAL) { + return 0; + } else { + return 1; + } +} + +void Collator::ResolvedOptions(std::map &options) +{ + options.insert( + { + { "locale", localeStr }, + { "usage", usage }, + { "sensitivity", sensitivity }, + { "ignorePunctuation", ignorePunctuation }, + { "numeric", numeric }, + { "caseFirst", caseFirst }, + { "collation", collation } + } + ); +} +} +} +} \ No newline at end of file diff --git a/frameworks/intl/src/plural_rules.cpp b/frameworks/intl/src/plural_rules.cpp new file mode 100644 index 00000000..98904048 --- /dev/null +++ b/frameworks/intl/src/plural_rules.cpp @@ -0,0 +1,175 @@ +/* + * 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 + +#include "hilog/log.h" +#include "unicode/unistr.h" + +#include "plural_rules.h" + +namespace OHOS { +namespace Global { +namespace I18n { +static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, 0xD001E00, "IntlJs" }; +using namespace OHOS::HiviewDFX; + +std::set PluralRules::GetValidLocales() +{ + int32_t validCount = 1; + const icu::Locale *validLocales = icu::Locale::getAvailableLocales(validCount); + std::set allValidLocales; + for (int i = 0; i < validCount; i++) { + allValidLocales.insert(validLocales[i].getLanguage()); + } + return allValidLocales; +} + +std::string PluralRules::ParseOption(std::map &options, const std::string &key) +{ + std::map::iterator it = options.find(key); + if (it != options.end()) { + return it->second; + } else { + return ""; + } +} + +void PluralRules::ParseAllOptions(std::map &options) +{ + localeMatcher = ParseOption(options, "localeMatcher"); + localeMatcher = (localeMatcher == "") ? "best fit" : localeMatcher; + type = ParseOption(options, "type"); + type = (type == "") ? "cardinal" : type; + std::string minIntegerStr = ParseOption(options, "minimumIntegerDigits"); + minInteger = (minIntegerStr == "") ? 1 : std::stoi(minIntegerStr); + + minFraction = 0; + maxFraction = 0; + std::string minFractionStr = ParseOption(options, "minimumFractionDigits"); + std::string maxFractionStr = ParseOption(options, "maximumFractionDigits"); + std::string minSignificantStr = ParseOption(options, "minimumSignificantDigits"); + std::string maxSignificantStr = ParseOption(options, "maximumSignificantDigits"); + if (minSignificantStr != "" || maxSignificantStr != "") { + // 1 is the default value of minSignificant + minSignificant = (minSignificantStr == "") ? 1 : std::stoi(minSignificantStr); + // 21 is the default value of maxSignificant + maxSignificant = (maxSignificantStr == "") ? 21 : std::stoi(maxSignificantStr); + } else { + minSignificant = 0; + maxSignificant = 0; + + if (minFractionStr != "" || maxFractionStr != "") { + minFraction = (minFractionStr == "") ? 0 : std::stoi(minFractionStr); + int maxFractionDefault = std::max(3, minFraction); + maxFraction = (maxFractionStr == "") ? maxFractionDefault : std::stoi(maxFractionStr); + if (minFraction > maxFraction) { + HiLog::Error(LABEL, "minimumFractionDigits is greater than maximumFractionDigits"); + return; + } + } else { + minFraction = 0; // 0 is the default value of minFraction. + maxFraction = 3; // 3 is the default value of maxFraction + } + } +} + +void PluralRules::InitPluralRules(std::vector &localeTags, + std::map &options) +{ + UPluralType uPluralType = (type == "cardinal") ? UPLURAL_TYPE_CARDINAL : UPLURAL_TYPE_ORDINAL; + UErrorCode status = UErrorCode::U_ZERO_ERROR; + std::set allValidLocales = GetValidLocales(); + for (size_t i = 0; i < localeTags.size(); i++) { + std::string curLocale = localeTags[i]; + locale = icu::Locale::forLanguageTag(icu::StringPiece(curLocale), status); + if (allValidLocales.count(locale.getLanguage()) > 0) { + localeInfo = new LocaleInfo(curLocale, options); + locale = localeInfo->GetLocale(); + localeStr = localeInfo->GetBaseName(); + pluralRules = icu::PluralRules::forLocale(locale, uPluralType, status); + if (status != UErrorCode::U_ZERO_ERROR || pluralRules == nullptr) { + continue; + } + } + } + if (localeTags.size() == 0) { + icu::Locale defaultLocale; + localeInfo = new LocaleInfo(defaultLocale.getName(), options); + locale = localeInfo->GetLocale(); + localeStr = localeInfo->GetBaseName(); + pluralRules = icu::PluralRules::forLocale(locale, uPluralType, status); + } + if (status != UErrorCode::U_ZERO_ERROR || pluralRules == nullptr) { + HiLog::Error(LABEL, "PluralRules object created failed"); + return; + } +} + +void PluralRules::InitNumberFormatter() +{ + numberFormatter = icu::number::NumberFormatter::withLocale(locale).roundingMode(UNUM_ROUND_HALFUP); + if (minInteger > 1) { + numberFormatter = numberFormatter.integerWidth(icu::number::IntegerWidth::zeroFillTo(minInteger)); + } + + if (minSignificant >= 0) { + if (minSignificant > 0) { + icu::number::Precision precision = icu::number::Precision::minMaxSignificantDigits(minSignificant, + maxSignificant); + numberFormatter.precision(precision); + } else { + icu::number::Precision precision = icu::number::Precision::minMaxFraction(minFraction, maxFraction); + numberFormatter.precision(precision); + } + } +} + +PluralRules::PluralRules(std::vector &localeTags, std::map &options) +{ + ParseAllOptions(options); + InitPluralRules(localeTags, options); + InitNumberFormatter(); +} + +PluralRules::~PluralRules() +{ + if (localeInfo != nullptr) { + delete localeInfo; + localeInfo = nullptr; + } + + if (pluralRules == nullptr) { + delete pluralRules; + pluralRules = nullptr; + } +} + +std::string PluralRules::Select(double number) +{ + UErrorCode status = UErrorCode::U_ZERO_ERROR; + icu::number::FormattedNumber formattedNumber = numberFormatter.formatDouble(number, status); + if (status != UErrorCode::U_ZERO_ERROR) { + status = UErrorCode::U_ZERO_ERROR; + formattedNumber = numberFormatter.formatDouble(number, status); + } + icu::UnicodeString unicodeString = pluralRules->select(formattedNumber, status); + std::string result; + unicodeString.toUTF8String(result); + return result; +} +} +} +} \ No newline at end of file diff --git a/interfaces/js/kits/include/intl_addon.h b/interfaces/js/kits/include/intl_addon.h old mode 100755 new mode 100644 index 3bf5c699..00f1b4e0 --- a/interfaces/js/kits/include/intl_addon.h +++ b/interfaces/js/kits/include/intl_addon.h @@ -1,103 +1,122 @@ -/* - * 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 INTL_ADDON_H -#define INTL_ADDON_H - -#include - -#include "napi/native_api.h" -#include "napi/native_node_api.h" -#include "locale_info.h" -#include "date_time_format.h" -#include "number_format.h" - -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, - std::map &map); -static 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, - 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 SetOptionProperties(napi_env env, napi_value &result, std::map &options, - const std::string &option); -static void SetIntegerOptionProperties(napi_env env, napi_value &result, - std::map &options, const std::string &option); -static void SetBooleanOptionProperties(napi_env env, napi_value &result, - std::map &options, const std::string &option); - -class IntlAddon { -public: - static napi_value InitLocale(napi_env env, napi_value exports); - static napi_value InitDateTimeFormat(napi_env env, napi_value exports); - static napi_value InitNumberFormat(napi_env env, napi_value exports); - static void Destructor(napi_env env, void *nativeObject, void *finalize_hint); - - IntlAddon(); - virtual ~IntlAddon(); - -private: - static napi_value DateTimeFormatConstructor(napi_env env, napi_callback_info info); - static napi_value NumberFormatConstructor(napi_env env, napi_callback_info info); - static napi_value LocaleConstructor(napi_env env, napi_callback_info info); - static napi_value GetLanguage(napi_env env, napi_callback_info info); - static napi_value GetScript(napi_env env, napi_callback_info info); - static napi_value GetRegion(napi_env env, napi_callback_info info); - static napi_value GetBaseName(napi_env env, napi_callback_info info); - static napi_value GetCalendar(napi_env env, napi_callback_info info); - static napi_value GetCollation(napi_env env, napi_callback_info info); - static napi_value GetHourCycle(napi_env env, napi_callback_info info); - static napi_value GetNumberingSystem(napi_env env, napi_callback_info info); - static napi_value GetNumeric(napi_env env, napi_callback_info info); - static napi_value GetCaseFirst(napi_env env, napi_callback_info info); - static napi_value ToString(napi_env env, napi_callback_info info); - static napi_value Maximize(napi_env env, napi_callback_info info); - static napi_value Minimize(napi_env env, napi_callback_info info); - - static napi_value FormatDateTime(napi_env env, napi_callback_info info); - static napi_value FormatDateTimeRange(napi_env env, napi_callback_info info); - static napi_value GetDateTimeResolvedOptions(napi_env env, napi_callback_info info); - - static napi_value GetNumberResolvedOptions(napi_env env, napi_callback_info info); - static napi_value FormatNumber(napi_env env, napi_callback_info info); - - static int64_t GetYear(napi_env env, napi_value *argv, int index); - static int64_t GetMonth(napi_env env, napi_value *argv, int index); - static int64_t GetDay(napi_env env, napi_value *argv, int index); - static int64_t GetHour(napi_env env, napi_value *argv, int index); - static int64_t GetMinute(napi_env env, napi_value *argv, int index); - static int64_t GetSecond(napi_env env, napi_value *argv, int index); - bool InitLocaleContext(napi_env env, napi_callback_info info, const std::string localeTag, - std::map &map); - bool InitDateTimeFormatContext(napi_env env, napi_callback_info info, std::vector localeTags, - std::map &map); - bool InitNumberFormatContext(napi_env env, napi_callback_info info, std::vector localeTags, - std::map &map); - - napi_env env_; - napi_ref wrapper_; - std::unique_ptr locale_; - std::unique_ptr datefmt_; - std::unique_ptr numberfmt_; -}; -} // namespace I18n -} // namespace Global -} // namespace OHOS +/* + * 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 INTL_ADDON_H +#define INTL_ADDON_H + +#include + +#include "napi/native_api.h" +#include "napi/native_node_api.h" +#include "locale_info.h" +#include "date_time_format.h" +#include "number_format.h" +#include "collator.h" +#include "plural_rules.h" + +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, + std::map &map); +static 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, + 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, + const std::string &option); +static void SetIntegerOptionProperties(napi_env env, napi_value &result, + std::map &options, const std::string &option); +static void SetBooleanOptionProperties(napi_env env, napi_value &result, + std::map &options, const std::string &option); + +class IntlAddon { +public: + static napi_value InitLocale(napi_env env, napi_value exports); + static napi_value InitDateTimeFormat(napi_env env, napi_value exports); + static napi_value InitNumberFormat(napi_env env, napi_value exports); + static napi_value InitCollator(napi_env env, napi_value exports); + static napi_value InitPluralRules(napi_env env, napi_value exports); + static void Destructor(napi_env env, void *nativeObject, void *finalize_hint); + + IntlAddon(); + virtual ~IntlAddon(); + +private: + static napi_value DateTimeFormatConstructor(napi_env env, napi_callback_info info); + static napi_value NumberFormatConstructor(napi_env env, napi_callback_info info); + static napi_value LocaleConstructor(napi_env env, napi_callback_info info); + static napi_value GetLanguage(napi_env env, napi_callback_info info); + static napi_value GetScript(napi_env env, napi_callback_info info); + static napi_value GetRegion(napi_env env, napi_callback_info info); + static napi_value GetBaseName(napi_env env, napi_callback_info info); + static napi_value GetCalendar(napi_env env, napi_callback_info info); + static napi_value GetCollation(napi_env env, napi_callback_info info); + static napi_value GetHourCycle(napi_env env, napi_callback_info info); + static napi_value GetNumberingSystem(napi_env env, napi_callback_info info); + static napi_value GetNumeric(napi_env env, napi_callback_info info); + static napi_value GetCaseFirst(napi_env env, napi_callback_info info); + static napi_value ToString(napi_env env, napi_callback_info info); + static napi_value Maximize(napi_env env, napi_callback_info info); + static napi_value Minimize(napi_env env, napi_callback_info info); + + static napi_value FormatDateTime(napi_env env, napi_callback_info info); + static napi_value FormatDateTimeRange(napi_env env, napi_callback_info info); + static napi_value GetDateTimeResolvedOptions(napi_env env, napi_callback_info info); + + static napi_value GetNumberResolvedOptions(napi_env env, napi_callback_info info); + static napi_value FormatNumber(napi_env env, napi_callback_info info); + + static napi_value CollatorConstructor(napi_env env, napi_callback_info info); + static napi_value CompareString(napi_env env, napi_callback_info info); + static napi_value GetCollatorResolvedOptions(napi_env env, napi_callback_info info); + + static napi_value PluralRulesConstructor(napi_env env, napi_callback_info info); + static napi_value Select(napi_env env, napi_callback_info info); + + static int64_t GetYear(napi_env env, napi_value *argv, int index); + static int64_t GetMonth(napi_env env, napi_value *argv, int index); + static int64_t GetDay(napi_env env, napi_value *argv, int index); + static int64_t GetHour(napi_env env, napi_value *argv, int index); + static int64_t GetMinute(napi_env env, napi_value *argv, int index); + static int64_t GetSecond(napi_env env, napi_value *argv, int index); + bool InitLocaleContext(napi_env env, napi_callback_info info, const std::string localeTag, + std::map &map); + bool InitDateTimeFormatContext(napi_env env, napi_callback_info info, std::vector localeTags, + std::map &map); + bool InitNumberFormatContext(napi_env env, napi_callback_info info, std::vector localeTags, + std::map &map); + bool InitCollatorContext(napi_env env, napi_callback_info info, std::vector localeTags, + std::map &map); + bool InitPluralRulesContext(napi_env env, napi_callback_info info, std::vector localeTags, + std::map &map); + + napi_env env_; + napi_ref wrapper_; + std::unique_ptr locale_; + std::unique_ptr datefmt_; + std::unique_ptr numberfmt_; + std::unique_ptr collator_; + std::unique_ptr pluralrules_; +}; +} // namespace I18n +} // namespace Global +} // namespace OHOS #endif \ No newline at end of file diff --git a/interfaces/js/kits/src/intl_addon.cpp b/interfaces/js/kits/src/intl_addon.cpp old mode 100755 new mode 100644 index 28e95b1f..5d4c3d56 --- a/interfaces/js/kits/src/intl_addon.cpp +++ b/interfaces/js/kits/src/intl_addon.cpp @@ -1,1169 +1,1733 @@ -/* - * 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 "intl_addon.h" - -#include - -#include "hilog/log.h" -#include "node_api.h" - -namespace OHOS { -namespace Global { -namespace I18n { -#define GET_PARAMS(env, info, num) \ - size_t argc = num; \ - napi_value argv[num]; \ - napi_value thisVar = nullptr; \ - void *data = nullptr; \ - napi_get_cb_info(env, info, &argc, argv, &thisVar, &data) - -static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, 0xD001E00, "IntlJs" }; -using namespace OHOS::HiviewDFX; -static napi_ref *g_constructor = nullptr; - -IntlAddon::IntlAddon() : env_(nullptr), wrapper_(nullptr) {} - -IntlAddon::~IntlAddon() -{ - napi_delete_reference(env_, wrapper_); -} - -void IntlAddon::Destructor(napi_env env, void *nativeObject, void *hint) -{ - if (nativeObject == nullptr) { - return; - } - reinterpret_cast(nativeObject)->~IntlAddon(); -} - -napi_value IntlAddon::InitLocale(napi_env env, napi_value exports) -{ - napi_status status; - napi_property_descriptor properties[] = { - DECLARE_NAPI_GETTER("language", GetLanguage), - DECLARE_NAPI_GETTER("baseName", GetBaseName), - DECLARE_NAPI_GETTER("region", GetRegion), - DECLARE_NAPI_GETTER("script", GetScript), - DECLARE_NAPI_GETTER("calendar", GetCalendar), - DECLARE_NAPI_GETTER("collation", GetCollation), - DECLARE_NAPI_GETTER("hourCycle", GetHourCycle), - DECLARE_NAPI_GETTER("numberingSystem", GetNumberingSystem), - DECLARE_NAPI_GETTER("numeric", GetNumeric), - DECLARE_NAPI_GETTER("caseFirst", GetCaseFirst), - DECLARE_NAPI_FUNCTION("toString", ToString), - DECLARE_NAPI_FUNCTION("minimize", Minimize), - DECLARE_NAPI_FUNCTION("maximize", Maximize), - }; - - napi_value constructor; - status = napi_define_class(env, "Locale", NAPI_AUTO_LENGTH, LocaleConstructor, nullptr, - sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor); - if (status != napi_ok) { - HiLog::Error(LABEL, "Define class failed when InitLocale"); - return nullptr; - } - - status = napi_set_named_property(env, exports, "Locale", constructor); - if (status != napi_ok) { - HiLog::Error(LABEL, "Set property failed when InitLocale"); - return nullptr; - } - g_constructor = new (std::nothrow) napi_ref; - if (g_constructor == nullptr) { - HiLog::Error(LABEL, "Failed to create ref at init"); - return nullptr; - } - status = napi_create_reference(env, constructor, 1, g_constructor); - if (status != napi_ok) { - HiLog::Error(LABEL, "Failed to create reference at init"); - return nullptr; - } - return exports; -} - -napi_value IntlAddon::InitDateTimeFormat(napi_env env, napi_value exports) -{ - napi_status status; - napi_property_descriptor properties[] = { - DECLARE_NAPI_FUNCTION("format", FormatDateTime), - DECLARE_NAPI_FUNCTION("formatRange", FormatDateTimeRange), - DECLARE_NAPI_FUNCTION("resolvedOptions", GetDateTimeResolvedOptions) - }; - - napi_value constructor; - status = napi_define_class(env, "DateTimeFormat", NAPI_AUTO_LENGTH, DateTimeFormatConstructor, nullptr, - sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor); - if (status != napi_ok) { - HiLog::Error(LABEL, "Define class failed when InitDateTimeFormat"); - return nullptr; - } - - status = napi_set_named_property(env, exports, "DateTimeFormat", constructor); - if (status != napi_ok) { - HiLog::Error(LABEL, "Set property failed when InitDateTimeFormat"); - return nullptr; - } - return exports; -} - -napi_value IntlAddon::InitNumberFormat(napi_env env, napi_value exports) -{ - napi_status status; - napi_property_descriptor properties[] = { - DECLARE_NAPI_FUNCTION("format", FormatNumber), - DECLARE_NAPI_FUNCTION("resolvedOptions", GetNumberResolvedOptions) - }; - - napi_value constructor; - status = napi_define_class(env, "NumberFormat", NAPI_AUTO_LENGTH, NumberFormatConstructor, nullptr, - sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor); - if (status != napi_ok) { - HiLog::Error(LABEL, "Define class failed when InitNumberFormat"); - return nullptr; - } - - status = napi_set_named_property(env, exports, "NumberFormat", constructor); - if (status != napi_ok) { - HiLog::Error(LABEL, "Set property failed when InitNumberFormat"); - return nullptr; - } - return exports; -} - -void GetOptionValue(napi_env env, napi_value options, const std::string &optionName, - std::map &map) -{ - 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); - map.insert(make_pair(optionName, optionBuf.data())); - } - } -} - -void GetIntegerOptionValue(napi_env env, napi_value options, const std::string &optionName, - std::map &map) -{ - 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, "Set 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) { - int64_t integerValue = -1; - napi_get_value_int64(env, optionValue, &integerValue); - if (integerValue != -1) { - map.insert(make_pair(optionName, std::to_string(integerValue))); - } - } - } -} - -void GetBoolOptionValue(napi_env env, napi_value options, const std::string &optionName, - std::map &map) -{ - 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, "Set 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) { - bool boolValue = false; - napi_get_value_bool(env, optionValue, &boolValue); - std::string value = boolValue ? "true" : "false"; - map.insert(make_pair(optionName, value)); - } - } -} - -void GetDateOptionValues(napi_env env, napi_value options, std::map &map) -{ - GetOptionValue(env, options, "calendar", map); - GetOptionValue(env, options, "dateStyle", map); - GetOptionValue(env, options, "timeStyle", map); - GetOptionValue(env, options, "hourCycle", map); - GetOptionValue(env, options, "timeZone", map); - GetOptionValue(env, options, "timeZoneName", map); - GetOptionValue(env, options, "numberingSystem", map); - GetBoolOptionValue(env, options, "hour12", map); - GetOptionValue(env, options, "weekday", map); - GetOptionValue(env, options, "era", map); - GetOptionValue(env, options, "year", map); - GetOptionValue(env, options, "month", map); - GetOptionValue(env, options, "day", map); - GetOptionValue(env, options, "hour", map); - GetOptionValue(env, options, "minute", map); - GetOptionValue(env, options, "second", map); - GetOptionValue(env, options, "localeMatcher", map); - GetOptionValue(env, options, "formatMatcher", map); - GetOptionValue(env, options, "dayPeriod", map); -} - -napi_value IntlAddon::LocaleConstructor(napi_env env, napi_callback_info info) -{ - // Need to get one parameter of a locale in string format to create Locale object. - size_t argc = 2; - napi_value argv[2] = { 0 }; - napi_value thisVar = nullptr; - void *data = nullptr; - napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); - napi_valuetype valueType = napi_valuetype::napi_undefined; - napi_typeof(env, argv[0], &valueType); - if (valueType != napi_valuetype::napi_string) { - napi_throw_type_error(env, nullptr, "Parameter type does not match"); - return nullptr; - } - size_t len; - status = napi_get_value_string_utf8(env, argv[0], nullptr, 0, &len); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get locale tag length failed"); - return nullptr; - } - std::vector buf(len + 1); - status = napi_get_value_string_utf8(env, argv[0], buf.data(), len + 1, &len); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get locale tag failed"); - return nullptr; - } - std::map map = {}; - if (argv[1] != nullptr) { - GetOptionValue(env, argv[1], "calendar", map); - GetOptionValue(env, argv[1], "collation", map); - GetOptionValue(env, argv[1], "hourCycle", map); - GetOptionValue(env, argv[1], "numberingSystem", map); - GetBoolOptionValue(env, argv[1], "numeric", map); - GetOptionValue(env, argv[1], "caseFirst", map); - } - - std::unique_ptr obj = std::make_unique(); - if (obj == nullptr) { - HiLog::Error(LABEL, "Create IntlAddon failed"); - return nullptr; - } - - status = - napi_wrap(env, thisVar, reinterpret_cast(obj.get()), IntlAddon::Destructor, nullptr, &obj->wrapper_); - if (status != napi_ok) { - HiLog::Error(LABEL, "Wrap IntlAddon failed"); - return nullptr; - } - - std::string localeTag = buf.data(); - if (!obj->InitLocaleContext(env, info, localeTag, map)) { - return nullptr; - } - - obj.release(); - - return thisVar; -} - -bool IntlAddon::InitLocaleContext(napi_env env, napi_callback_info info, const std::string localeTag, - std::map &map) -{ - napi_value global; - napi_status status = napi_get_global(env, &global); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get global failed"); - return false; - } - env_ = env; - locale_ = std::make_unique(localeTag, map); - - return locale_ != nullptr; -} - -void GetLocaleTags(napi_env env, napi_value rawLocaleTag, std::vector &localeTags) -{ - size_t len; - napi_status status = napi_get_value_string_utf8(env, rawLocaleTag, nullptr, 0, &len); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get locale tag length failed"); - return; - } - std::vector buf(len + 1); - status = napi_get_value_string_utf8(env, rawLocaleTag, buf.data(), len + 1, &len); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get locale tag failed"); - return; - } - localeTags.push_back(buf.data()); -} - -napi_value IntlAddon::DateTimeFormatConstructor(napi_env env, napi_callback_info info) -{ - // Need to get one parameter of a locale in string format to create DateTimeFormat object. - size_t argc = 2; - napi_value argv[2] = { 0 }; - napi_value thisVar = nullptr; - void *data = nullptr; - napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); - napi_valuetype valueType = napi_valuetype::napi_undefined; - napi_typeof(env, argv[0], &valueType); - std::vector localeTags; - bool isArray = false; - napi_is_array(env, argv[0], &isArray); - if (valueType == napi_valuetype::napi_string) { - GetLocaleTags(env, argv[0], localeTags); - } else if (isArray) { - uint32_t arrayLength = 0; - napi_get_array_length(env, argv[0], &arrayLength); - napi_value element; - for (uint32_t i = 0; i < arrayLength; i++) { - napi_get_element(env, argv[0], i, &element); - GetLocaleTags(env, element, localeTags); - } - } else { - return nullptr; - } - - std::map map = {}; - if (argv[1] != nullptr) { - GetDateOptionValues(env, argv[1], map); - } - - std::unique_ptr obj = std::make_unique(); - if (obj == nullptr) { - HiLog::Error(LABEL, "Create IntlAddon failed"); - return nullptr; - } - - status = - napi_wrap(env, thisVar, reinterpret_cast(obj.get()), IntlAddon::Destructor, nullptr, &obj->wrapper_); - if (status != napi_ok) { - HiLog::Error(LABEL, "Wrap IntlAddon failed"); - return nullptr; - } - - if (!obj->InitDateTimeFormatContext(env, info, localeTags, map)) { - HiLog::Error(LABEL, "Init DateTimeFormat failed"); - return nullptr; - } - - obj.release(); - return thisVar; -} - -bool IntlAddon::InitDateTimeFormatContext(napi_env env, napi_callback_info info, std::vector localeTags, - std::map &map) -{ - napi_value global; - napi_status status = napi_get_global(env, &global); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get global failed"); - return false; - } - env_ = env; - datefmt_ = std::make_unique(localeTags, map); - - return datefmt_ != nullptr; -} - -napi_value IntlAddon::FormatDateTime(napi_env env, napi_callback_info info) -{ - GET_PARAMS(env, info, 1); // Need to get one parameter of a date object to format. - int64_t year = GetYear(env, argv, 0); - int64_t month = GetMonth(env, argv, 0); - int64_t day = GetDay(env, argv, 0); - int64_t hour = GetHour(env, argv, 0); - int64_t minute = GetMinute(env, argv, 0); - int64_t second = GetSecond(env, argv, 0); - if (year == -1 || month == -1 || day == -1 || hour == -1 || minute == -1 || second == -1) { - return nullptr; - } - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->datefmt_ == nullptr) { - HiLog::Error(LABEL, "Get DateTimeFormat object failed"); - return nullptr; - } - int64_t date[] = { year, month, day, hour, minute, second }; - std::string value = obj->datefmt_->Format(date, std::end(date) - std::begin(date)); - napi_value result; - status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); - if (status != napi_ok) { - HiLog::Error(LABEL, "Create format string failed"); - return nullptr; - } - return result; -} - -napi_value IntlAddon::FormatDateTimeRange(napi_env env, napi_callback_info info) -{ - GET_PARAMS(env, info, 2); // Need to get two parameter of date objects to format. - int64_t firstYear = GetYear(env, argv, 0); - int64_t firstMonth = GetMonth(env, argv, 0); - int64_t firstDay = GetDay(env, argv, 0); - int64_t firstHour = GetHour(env, argv, 0); - int64_t firstMinute = GetMinute(env, argv, 0); - int64_t firstSecond = GetSecond(env, argv, 0); - int64_t firstDate[] = { firstYear, firstMonth, firstDay, firstHour, firstMinute, firstSecond }; - int64_t secondYear = GetYear(env, argv, 1); - int64_t secondMonth = GetMonth(env, argv, 1); - int64_t secondDay = GetDay(env, argv, 1); - int64_t secondHour = GetHour(env, argv, 1); - int64_t secondMinute = GetMinute(env, argv, 1); - int64_t secondSecond = GetSecond(env, argv, 1); - int64_t secondDate[] = { secondYear, secondMonth, secondDay, secondHour, secondMinute, secondSecond }; - if (firstYear == -1 || firstMonth == -1 || firstDay == -1 || firstHour == -1 || firstMinute == -1 || - firstSecond == -1) { - return nullptr; - } - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->datefmt_ == nullptr) { - HiLog::Error(LABEL, "Get DateTimeFormat object failed"); - return nullptr; - } - std::string value = obj->datefmt_->FormatRange(firstDate, std::end(firstDate) - std::begin(firstDate), - secondDate, std::end(secondDate) - std::begin(secondDate)); - napi_value result; - status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); - if (status != napi_ok) { - HiLog::Error(LABEL, "Create format string failed"); - return nullptr; - } - return result; -} - -void GetNumberOptionValues(napi_env env, napi_value options, std::map &map) -{ - GetOptionValue(env, options, "currency", map); - GetOptionValue(env, options, "currencySign", map); - GetOptionValue(env, options, "currencyDisplay", map); - GetOptionValue(env, options, "unit", map); - GetOptionValue(env, options, "unitDisplay", map); - GetOptionValue(env, options, "compactDisplay", map); - GetOptionValue(env, options, "signDisplay", map); - GetOptionValue(env, options, "localeMatcher", map); - GetOptionValue(env, options, "style", map); - GetOptionValue(env, options, "numberingSystem", map); - GetOptionValue(env, options, "notation", map); - GetBoolOptionValue(env, options, "useGrouping", map); - GetIntegerOptionValue(env, options, "minimumIntegerDigits", map); - GetIntegerOptionValue(env, options, "minimumFractionDigits", map); - GetIntegerOptionValue(env, options, "maximumFractionDigits", map); - GetIntegerOptionValue(env, options, "minimumSignificantDigits", map); - GetIntegerOptionValue(env, options, "maximumSignificantDigits", map); -} - -napi_value IntlAddon::NumberFormatConstructor(napi_env env, napi_callback_info info) -{ - // Need to get one parameter of a locale in string format to create DateTimeFormat object. - size_t argc = 2; - napi_value argv[2] = { 0 }; - napi_value thisVar = nullptr; - void *data = nullptr; - napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); - napi_valuetype valueType = napi_valuetype::napi_undefined; - napi_typeof(env, argv[0], &valueType); - std::vector localeTags; - bool isArray = false; - napi_is_array(env, argv[0], &isArray); - if (valueType == napi_valuetype::napi_string) { - GetLocaleTags(env, argv[0], localeTags); - } else if (isArray) { - uint32_t arrayLength = 0; - napi_get_array_length(env, argv[0], &arrayLength); - napi_value element; - for (uint32_t i = 0; i < arrayLength; i++) { - napi_get_element(env, argv[0], i, &element); - GetLocaleTags(env, element, localeTags); - } - } else { - return nullptr; - } - - std::map map = {}; - if (argv[1] != nullptr) { - GetNumberOptionValues(env, argv[1], map); - } - - std::unique_ptr obj = std::make_unique(); - if (obj == nullptr) { - HiLog::Error(LABEL, "Create IntlAddon failed"); - return nullptr; - } - - status = - napi_wrap(env, thisVar, reinterpret_cast(obj.get()), IntlAddon::Destructor, nullptr, &obj->wrapper_); - if (status != napi_ok) { - HiLog::Error(LABEL, "Wrap IntlAddon failed"); - return nullptr; - } - - if (!obj->InitNumberFormatContext(env, info, localeTags, map)) { - HiLog::Error(LABEL, "Init NumberFormat failed"); - return nullptr; - } - - obj.release(); - return thisVar; -} - -bool IntlAddon::InitNumberFormatContext(napi_env env, napi_callback_info info, std::vector localeTags, - std::map &map) -{ - napi_value global; - napi_status status = napi_get_global(env, &global); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get global failed"); - return false; - } - env_ = env; - numberfmt_ = std::make_unique(localeTags, map); - - return numberfmt_ != nullptr; -} - -int64_t IntlAddon::GetYear(napi_env env, napi_value *argv, int index) -{ - napi_value funcGetDateInfo; - napi_status status = napi_get_named_property(env, argv[index], "getFullYear", &funcGetDateInfo); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get year property failed"); - return -1; - } - napi_value ret_value; - status = napi_call_function(env, argv[index], funcGetDateInfo, 0, nullptr, &ret_value); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get year function failed"); - return -1; - } - int64_t year; - status = napi_get_value_int64(env, ret_value, &year); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get year failed"); - return -1; - } - return year; -} - -int64_t IntlAddon::GetMonth(napi_env env, napi_value *argv, int index) -{ - napi_value funcGetDateInfo; - napi_status status = napi_get_named_property(env, argv[index], "getMonth", &funcGetDateInfo); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get month property failed"); - return -1; - } - napi_value ret_value; - status = napi_call_function(env, argv[index], funcGetDateInfo, 0, nullptr, &ret_value); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get month function failed"); - return -1; - } - int64_t month; - status = napi_get_value_int64(env, ret_value, &month); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get month failed"); - return -1; - } - return month; -} - -int64_t IntlAddon::GetDay(napi_env env, napi_value *argv, int index) -{ - napi_value funcGetDateInfo; - napi_status status = napi_get_named_property(env, argv[index], "getDate", &funcGetDateInfo); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get day property failed"); - return -1; - } - napi_value ret_value; - status = napi_call_function(env, argv[index], funcGetDateInfo, 0, nullptr, &ret_value); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get day function failed"); - return -1; - } - int64_t day; - status = napi_get_value_int64(env, ret_value, &day); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get day failed"); - return -1; - } - return day; -} - -int64_t IntlAddon::GetHour(napi_env env, napi_value *argv, int index) -{ - napi_value funcGetDateInfo; - napi_status status = napi_get_named_property(env, argv[index], "getHours", &funcGetDateInfo); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get hour property failed"); - return -1; - } - napi_value ret_value; - status = napi_call_function(env, argv[index], funcGetDateInfo, 0, nullptr, &ret_value); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get hour function failed"); - return -1; - } - int64_t hour; - status = napi_get_value_int64(env, ret_value, &hour); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get hour failed"); - return -1; - } - return hour; -} - -int64_t IntlAddon::GetMinute(napi_env env, napi_value *argv, int index) -{ - napi_value funcGetDateInfo; - napi_status status = napi_get_named_property(env, argv[index], "getMinutes", &funcGetDateInfo); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get minute property failed"); - return -1; - } - napi_value ret_value; - status = napi_call_function(env, argv[index], funcGetDateInfo, 0, nullptr, &ret_value); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get minute function failed"); - return -1; - } - int64_t minute; - status = napi_get_value_int64(env, ret_value, &minute); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get minute failed"); - return -1; - } - return minute; -} - -int64_t IntlAddon::GetSecond(napi_env env, napi_value *argv, int index) -{ - napi_value funcGetDateInfo; - napi_status status = napi_get_named_property(env, argv[index], "getSeconds", &funcGetDateInfo); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get second property failed"); - return -1; - } - napi_value ret_value; - status = napi_call_function(env, argv[index], funcGetDateInfo, 0, nullptr, &ret_value); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get second function failed"); - return -1; - } - int64_t second; - status = napi_get_value_int64(env, ret_value, &second); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get second failed"); - return -1; - } - return second; -} - -napi_value IntlAddon::GetLanguage(napi_env env, napi_callback_info info) -{ - // No parameters are needed to get the language. - GET_PARAMS(env, info, 0); - - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { - HiLog::Error(LABEL, "Get Locale object failed"); - return nullptr; - } - std::string value = obj->locale_->GetLanguage(); - - napi_value result; - status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); - if (status != napi_ok) { - HiLog::Error(LABEL, "Create language string failed"); - return nullptr; - } - return result; -} - -napi_value IntlAddon::GetScript(napi_env env, napi_callback_info info) -{ - // No parameters are needed to get the script. - GET_PARAMS(env, info, 0); - - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { - HiLog::Error(LABEL, "Get Locale object failed"); - return nullptr; - } - std::string value = obj->locale_->GetScript(); - - napi_value result; - status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); - if (status != napi_ok) { - HiLog::Error(LABEL, "Create script string failed"); - return nullptr; - } - return result; -} - -napi_value IntlAddon::GetRegion(napi_env env, napi_callback_info info) -{ - // No parameters are needed to get the region. - GET_PARAMS(env, info, 0); - - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { - HiLog::Error(LABEL, "Get Locale object failed"); - return nullptr; - } - std::string value = obj->locale_->GetRegion(); - - napi_value result; - status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); - if (status != napi_ok) { - HiLog::Error(LABEL, "Create region string failed"); - return nullptr; - } - return result; -} - -napi_value IntlAddon::GetBaseName(napi_env env, napi_callback_info info) -{ - // No parameters are needed to get the baseName. - GET_PARAMS(env, info, 0); - - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { - HiLog::Error(LABEL, "Get Locale object failed"); - return nullptr; - } - std::string value = obj->locale_->GetBaseName(); - - napi_value result; - status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); - if (status != napi_ok) { - HiLog::Error(LABEL, "Create base name string failed"); - return nullptr; - } - return result; -} - -napi_value IntlAddon::GetCalendar(napi_env env, napi_callback_info info) -{ - // No parameters are needed to get the baseName. - GET_PARAMS(env, info, 0); - - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { - HiLog::Error(LABEL, "Get Locale object failed"); - return nullptr; - } - std::string value = obj->locale_->GetCalendar(); - - napi_value result; - status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); - if (status != napi_ok) { - HiLog::Error(LABEL, "Create base name string failed"); - return nullptr; - } - return result; -} - -napi_value IntlAddon::GetCollation(napi_env env, napi_callback_info info) -{ - // No parameters are needed to get the baseName. - GET_PARAMS(env, info, 0); - - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { - HiLog::Error(LABEL, "Get Locale object failed"); - return nullptr; - } - std::string value = obj->locale_->GetCollation(); - - napi_value result; - status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); - if (status != napi_ok) { - HiLog::Error(LABEL, "Create base name string failed"); - return nullptr; - } - return result; -} - -napi_value IntlAddon::GetHourCycle(napi_env env, napi_callback_info info) -{ - // No parameters are needed to get the baseName. - GET_PARAMS(env, info, 0); - - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { - HiLog::Error(LABEL, "Get Locale object failed"); - return nullptr; - } - std::string value = obj->locale_->GetHourCycle(); - - napi_value result; - status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); - if (status != napi_ok) { - HiLog::Error(LABEL, "Create base name string failed"); - return nullptr; - } - return result; -} - -napi_value IntlAddon::GetNumberingSystem(napi_env env, napi_callback_info info) -{ - // No parameters are needed to get the baseName. - GET_PARAMS(env, info, 0); - - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { - HiLog::Error(LABEL, "Get Locale object failed"); - return nullptr; - } - std::string value = obj->locale_->GetNumberingSystem(); - - napi_value result; - status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); - if (status != napi_ok) { - HiLog::Error(LABEL, "Create base name string failed"); - return nullptr; - } - return result; -} - -napi_value IntlAddon::GetNumeric(napi_env env, napi_callback_info info) -{ - // No parameters are needed to get the baseName. - GET_PARAMS(env, info, 0); - - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { - HiLog::Error(LABEL, "Get Locale object failed"); - return nullptr; - } - std::string value = obj->locale_->GetNumeric(); - bool optionBoolValue = (value == "true"); - napi_value result; - status = napi_get_boolean(env, optionBoolValue, &result); - if (status != napi_ok) { - HiLog::Error(LABEL, "Create numeric boolean value failed"); - return nullptr; - } - return result; -} - -napi_value IntlAddon::GetCaseFirst(napi_env env, napi_callback_info info) -{ - // No parameters are needed to get the baseName. - GET_PARAMS(env, info, 0); - - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { - HiLog::Error(LABEL, "Get Locale object failed"); - return nullptr; - } - std::string value = obj->locale_->GetCaseFirst(); - napi_value result; - status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); - if (status != napi_ok) { - HiLog::Error(LABEL, "Create caseFirst string failed"); - return nullptr; - } - return result; -} - -napi_value IntlAddon::ToString(napi_env env, napi_callback_info info) -{ - // No parameters are needed to get the language. - GET_PARAMS(env, info, 0); - - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { - HiLog::Error(LABEL, "Get Locale object failed"); - return nullptr; - } - std::string value = obj->locale_->ToString(); - - napi_value result; - status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); - if (status != napi_ok) { - HiLog::Error(LABEL, "Create language string failed"); - return nullptr; - } - return result; -} - -napi_value IntlAddon::Maximize(napi_env env, napi_callback_info info) -{ - // No parameters are needed to get the language. - GET_PARAMS(env, info, 0); - - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { - HiLog::Error(LABEL, "Get Locale object failed"); - return nullptr; - } - std::string localeTag = obj->locale_->Maximize(); - - napi_value constructor; - status = napi_get_reference_value(env, *g_constructor, &constructor); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get locale constructor reference failed"); - return nullptr; - } - napi_value result = nullptr; - napi_value arg = nullptr; - status = napi_create_string_utf8(env, localeTag.c_str(), NAPI_AUTO_LENGTH, &arg); - if (status != napi_ok) { - HiLog::Error(LABEL, "Create localeTag string failed"); - return nullptr; - } - status = napi_new_instance(env, constructor, 1, &arg, &result); - if (status != napi_ok) { - HiLog::Error(LABEL, "Create new locale instance failed"); - return nullptr; - } - return result; -} - -napi_value IntlAddon::Minimize(napi_env env, napi_callback_info info) -{ - // No parameters are needed to get the language. - GET_PARAMS(env, info, 0); - - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { - HiLog::Error(LABEL, "Get Locale object failed"); - return nullptr; - } - std::string localeTag = obj->locale_->Minimize(); - - napi_value constructor; - status = napi_get_reference_value(env, *g_constructor, &constructor); - if (status != napi_ok) { - HiLog::Error(LABEL, "Get locale constructor reference failed"); - return nullptr; - } - napi_value result = nullptr; - napi_value arg = nullptr; - status = napi_create_string_utf8(env, localeTag.c_str(), NAPI_AUTO_LENGTH, &arg); - if (status != napi_ok) { - HiLog::Error(LABEL, "Create localeTag string failed"); - return nullptr; - } - status = napi_new_instance(env, constructor, 1, &arg, &result); - if (status != napi_ok) { - HiLog::Error(LABEL, "Create new locale instance failed"); - return nullptr; - } - return result; -} - -void SetOptionProperties(napi_env env, napi_value &result, std::map &options, - const std::string &option) -{ - if (options.count(option) > 0) { - std::string optionValue = options[option]; - napi_value optionJsValue; - napi_create_string_utf8(env, optionValue.c_str(), NAPI_AUTO_LENGTH, &optionJsValue); - napi_set_named_property(env, result, option.c_str(), optionJsValue); - } else { - napi_value undefined; - napi_get_undefined(env, &undefined); - napi_set_named_property(env, result, option.c_str(), undefined); - } -} - -void SetIntegerOptionProperties(napi_env env, napi_value &result, std::map &options, - const std::string &option) -{ - if (options.count(option) > 0) { - std::string optionValue = options[option]; - napi_value optionJsValue; - int64_t integerValue = std::stoi(optionValue); - napi_create_int64(env, integerValue, &optionJsValue); - napi_set_named_property(env, result, option.c_str(), optionJsValue); - } else { - napi_value undefined; - napi_get_undefined(env, &undefined); - napi_set_named_property(env, result, option.c_str(), undefined); - } -} - -void SetBooleanOptionProperties(napi_env env, napi_value &result, std::map &options, - const std::string &option) -{ - if (options.count(option) > 0) { - std::string optionValue = options[option]; - bool optionBoolValue = (optionValue == "true"); - napi_value optionJsValue; - napi_get_boolean(env, optionBoolValue, &optionJsValue); - napi_set_named_property(env, result, option.c_str(), optionJsValue); - } else { - napi_value undefined; - napi_get_undefined(env, &undefined); - napi_set_named_property(env, result, option.c_str(), undefined); - } -} - -napi_value IntlAddon::GetDateTimeResolvedOptions(napi_env env, napi_callback_info info) -{ - // No parameters are needed to get the baseName. - GET_PARAMS(env, info, 0); - - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->datefmt_ == nullptr) { - HiLog::Error(LABEL, "Get DateTimeFormat object failed"); - return nullptr; - } - napi_value result; - napi_create_object(env, &result); - std::map options = {}; - obj->datefmt_->GetResolvedOptions(options); - SetOptionProperties(env, result, options, "locale"); - SetOptionProperties(env, result, options, "calendar"); - SetOptionProperties(env, result, options, "dateStyle"); - SetOptionProperties(env, result, options, "timeStyle"); - SetOptionProperties(env, result, options, "hourCycle"); - SetOptionProperties(env, result, options, "timeZone"); - SetOptionProperties(env, result, options, "timeZoneName"); - SetOptionProperties(env, result, options, "numberingSystem"); - SetBooleanOptionProperties(env, result, options, "hour12"); - SetOptionProperties(env, result, options, "weekday"); - SetOptionProperties(env, result, options, "era"); - SetOptionProperties(env, result, options, "year"); - SetOptionProperties(env, result, options, "month"); - SetOptionProperties(env, result, options, "day"); - SetOptionProperties(env, result, options, "hour"); - SetOptionProperties(env, result, options, "minute"); - SetOptionProperties(env, result, options, "second"); - SetOptionProperties(env, result, options, "dayPeriod"); - SetOptionProperties(env, result, options, "localeMatcher"); - SetOptionProperties(env, result, options, "formatMatcher"); - return result; -} - -napi_value IntlAddon::GetNumberResolvedOptions(napi_env env, napi_callback_info info) -{ - // No parameters are needed to get the baseName. - GET_PARAMS(env, info, 0); - - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->numberfmt_ == nullptr) { - HiLog::Error(LABEL, "Get NumberFormat object failed"); - return nullptr; - } - napi_value result; - napi_create_object(env, &result); - std::map options = {}; - obj->numberfmt_->GetResolvedOptions(options); - SetOptionProperties(env, result, options, "locale"); - SetOptionProperties(env, result, options, "currency"); - SetOptionProperties(env, result, options, "currencySign"); - SetOptionProperties(env, result, options, "currencyDisplay"); - SetOptionProperties(env, result, options, "unit"); - SetOptionProperties(env, result, options, "unitDisplay"); - SetOptionProperties(env, result, options, "signDisplay"); - SetOptionProperties(env, result, options, "compactDisplay"); - SetOptionProperties(env, result, options, "notation"); - SetOptionProperties(env, result, options, "style"); - SetOptionProperties(env, result, options, "numberingSystem"); - SetBooleanOptionProperties(env, result, options, "useGrouping"); - SetIntegerOptionProperties(env, result, options, "minimumIntegerDigits"); - SetIntegerOptionProperties(env, result, options, "minimumFractionDigits"); - SetIntegerOptionProperties(env, result, options, "maximumFractionDigits"); - SetIntegerOptionProperties(env, result, options, "minimumSignificantDigits"); - SetIntegerOptionProperties(env, result, options, "maximumSignificantDigits"); - SetOptionProperties(env, result, options, "localeMatcher"); - return result; -} - -napi_value IntlAddon::FormatNumber(napi_env env, napi_callback_info info) -{ - GET_PARAMS(env, info, 1); // Need to get one parameter of a date object to format. - double number; - napi_get_value_double(env, argv[0], &number); - IntlAddon *obj = nullptr; - napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); - if (status != napi_ok || obj == nullptr || obj->numberfmt_ == nullptr) { - HiLog::Error(LABEL, "Get NumberFormat object failed"); - return nullptr; - } - std::string value = obj->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, "Create format string failed"); - return nullptr; - } - return result; -} - -napi_value Init(napi_env env, napi_value exports) -{ - napi_value val = IntlAddon::InitLocale(env, exports); - val = IntlAddon::InitDateTimeFormat(env, val); - return IntlAddon::InitNumberFormat(env, val); -} - -static napi_module g_intlModule = { - .nm_version = 1, - .nm_flags = 0, - .nm_filename = nullptr, - .nm_register_func = Init, - .nm_modname = "intl", - .nm_priv = ((void *)0), - .reserved = { 0 } -}; - -extern "C" __attribute__((constructor)) void AbilityRegister() -{ - napi_module_register(&g_intlModule); -} -} // namespace I18n -} // namespace Global +/* + * 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 "intl_addon.h" + +#include +#include + +#include "hilog/log.h" +#include "node_api.h" + +namespace OHOS { +namespace Global { +namespace I18n { +static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, 0xD001E00, "IntlJs" }; +using namespace OHOS::HiviewDFX; +static napi_ref *g_constructor = nullptr; + +IntlAddon::IntlAddon() : env_(nullptr), wrapper_(nullptr) {} + +IntlAddon::~IntlAddon() +{ + napi_delete_reference(env_, wrapper_); +} + +void IntlAddon::Destructor(napi_env env, void *nativeObject, void *hint) +{ + if (nativeObject == nullptr) { + return; + } + reinterpret_cast(nativeObject)->~IntlAddon(); +} + +napi_value IntlAddon::InitLocale(napi_env env, napi_value exports) +{ + napi_status status; + napi_property_descriptor properties[] = { + DECLARE_NAPI_GETTER("language", GetLanguage), + DECLARE_NAPI_GETTER("baseName", GetBaseName), + DECLARE_NAPI_GETTER("region", GetRegion), + DECLARE_NAPI_GETTER("script", GetScript), + DECLARE_NAPI_GETTER("calendar", GetCalendar), + DECLARE_NAPI_GETTER("collation", GetCollation), + DECLARE_NAPI_GETTER("hourCycle", GetHourCycle), + DECLARE_NAPI_GETTER("numberingSystem", GetNumberingSystem), + DECLARE_NAPI_GETTER("numeric", GetNumeric), + DECLARE_NAPI_GETTER("caseFirst", GetCaseFirst), + DECLARE_NAPI_FUNCTION("toString", ToString), + DECLARE_NAPI_FUNCTION("minimize", Minimize), + DECLARE_NAPI_FUNCTION("maximize", Maximize), + }; + + napi_value constructor; + status = napi_define_class(env, "Locale", NAPI_AUTO_LENGTH, LocaleConstructor, nullptr, + sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor); + if (status != napi_ok) { + HiLog::Error(LABEL, "Define class failed when InitLocale"); + return nullptr; + } + + status = napi_set_named_property(env, exports, "Locale", constructor); + if (status != napi_ok) { + HiLog::Error(LABEL, "Set property failed when InitLocale"); + return nullptr; + } + g_constructor = new (std::nothrow) napi_ref; + if (g_constructor == nullptr) { + HiLog::Error(LABEL, "Failed to create ref at init"); + return nullptr; + } + status = napi_create_reference(env, constructor, 1, g_constructor); + if (status != napi_ok) { + HiLog::Error(LABEL, "Failed to create reference at init"); + return nullptr; + } + return exports; +} + +napi_value IntlAddon::InitDateTimeFormat(napi_env env, napi_value exports) +{ + napi_status status; + napi_property_descriptor properties[] = { + DECLARE_NAPI_FUNCTION("format", FormatDateTime), + DECLARE_NAPI_FUNCTION("formatRange", FormatDateTimeRange), + DECLARE_NAPI_FUNCTION("resolvedOptions", GetDateTimeResolvedOptions) + }; + + napi_value constructor; + status = napi_define_class(env, "DateTimeFormat", NAPI_AUTO_LENGTH, DateTimeFormatConstructor, nullptr, + sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor); + if (status != napi_ok) { + HiLog::Error(LABEL, "Define class failed when InitDateTimeFormat"); + return nullptr; + } + + status = napi_set_named_property(env, exports, "DateTimeFormat", constructor); + if (status != napi_ok) { + HiLog::Error(LABEL, "Set property failed when InitDateTimeFormat"); + return nullptr; + } + return exports; +} + +napi_value IntlAddon::InitNumberFormat(napi_env env, napi_value exports) +{ + napi_status status; + napi_property_descriptor properties[] = { + DECLARE_NAPI_FUNCTION("format", FormatNumber), + DECLARE_NAPI_FUNCTION("resolvedOptions", GetNumberResolvedOptions) + }; + + napi_value constructor; + status = napi_define_class(env, "NumberFormat", NAPI_AUTO_LENGTH, NumberFormatConstructor, nullptr, + sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor); + if (status != napi_ok) { + HiLog::Error(LABEL, "Define class failed when InitNumberFormat"); + return nullptr; + } + + status = napi_set_named_property(env, exports, "NumberFormat", constructor); + if (status != napi_ok) { + HiLog::Error(LABEL, "Set property failed when InitNumberFormat"); + return nullptr; + } + return exports; +} + +void GetOptionValue(napi_env env, napi_value options, const std::string &optionName, + std::map &map) +{ + 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); + map.insert(make_pair(optionName, optionBuf.data())); + } + } +} + +void GetIntegerOptionValue(napi_env env, napi_value options, const std::string &optionName, + std::map &map) +{ + 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, "Set 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) { + int64_t integerValue = -1; + status = napi_get_value_int64(env, optionValue, &integerValue); + if (status == napi_ok) { + map.insert(make_pair(optionName, std::to_string(integerValue))); + } + } + } +} + +void GetBoolOptionValue(napi_env env, napi_value options, const std::string &optionName, + std::map &map) +{ + 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, "Set 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) { + bool boolValue = false; + napi_get_value_bool(env, optionValue, &boolValue); + std::string value = boolValue ? "true" : "false"; + map.insert(make_pair(optionName, value)); + } + } +} + +void GetDateOptionValues(napi_env env, napi_value options, std::map &map) +{ + GetOptionValue(env, options, "calendar", map); + GetOptionValue(env, options, "dateStyle", map); + GetOptionValue(env, options, "timeStyle", map); + GetOptionValue(env, options, "hourCycle", map); + GetOptionValue(env, options, "timeZone", map); + GetOptionValue(env, options, "timeZoneName", map); + GetOptionValue(env, options, "numberingSystem", map); + GetBoolOptionValue(env, options, "hour12", map); + GetOptionValue(env, options, "weekday", map); + GetOptionValue(env, options, "era", map); + GetOptionValue(env, options, "year", map); + GetOptionValue(env, options, "month", map); + GetOptionValue(env, options, "day", map); + GetOptionValue(env, options, "hour", map); + GetOptionValue(env, options, "minute", map); + GetOptionValue(env, options, "second", map); + GetOptionValue(env, options, "localeMatcher", map); + GetOptionValue(env, options, "formatMatcher", map); + GetOptionValue(env, options, "dayPeriod", map); +} + +napi_value IntlAddon::LocaleConstructor(napi_env env, napi_callback_info info) +{ + // Need to get one parameter of a locale in string format to create Locale object. + size_t argc = 2; + napi_value argv[2] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + napi_valuetype valueType = napi_valuetype::napi_undefined; + napi_typeof(env, argv[0], &valueType); + if (valueType != napi_valuetype::napi_string) { + napi_throw_type_error(env, nullptr, "Parameter type does not match"); + return nullptr; + } + size_t len; + status = napi_get_value_string_utf8(env, argv[0], nullptr, 0, &len); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get locale tag length failed"); + return nullptr; + } + std::vector buf(len + 1); + status = napi_get_value_string_utf8(env, argv[0], buf.data(), len + 1, &len); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get locale tag failed"); + return nullptr; + } + std::map map = {}; + if (argv[1] != nullptr) { + GetOptionValue(env, argv[1], "calendar", map); + GetOptionValue(env, argv[1], "collation", map); + GetOptionValue(env, argv[1], "hourCycle", map); + GetOptionValue(env, argv[1], "numberingSystem", map); + GetBoolOptionValue(env, argv[1], "numeric", map); + GetOptionValue(env, argv[1], "caseFirst", map); + } + + std::unique_ptr obj = std::make_unique(); + if (obj == nullptr) { + HiLog::Error(LABEL, "Create IntlAddon failed"); + return nullptr; + } + + status = + napi_wrap(env, thisVar, reinterpret_cast(obj.get()), IntlAddon::Destructor, nullptr, &obj->wrapper_); + if (status != napi_ok) { + HiLog::Error(LABEL, "Wrap IntlAddon failed"); + return nullptr; + } + + std::string localeTag = buf.data(); + if (!obj->InitLocaleContext(env, info, localeTag, map)) { + return nullptr; + } + + obj.release(); + + return thisVar; +} + +bool IntlAddon::InitLocaleContext(napi_env env, napi_callback_info info, const std::string localeTag, + std::map &map) +{ + napi_value global; + napi_status status = napi_get_global(env, &global); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get global failed"); + return false; + } + env_ = env; + locale_ = std::make_unique(localeTag, map); + + return locale_ != nullptr; +} + +void GetLocaleTags(napi_env env, napi_value rawLocaleTag, std::vector &localeTags) +{ + size_t len; + napi_status status = napi_get_value_string_utf8(env, rawLocaleTag, nullptr, 0, &len); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get locale tag length failed"); + return; + } + std::vector buf(len + 1); + status = napi_get_value_string_utf8(env, rawLocaleTag, buf.data(), len + 1, &len); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get locale tag failed"); + return; + } + localeTags.push_back(buf.data()); +} + +napi_value IntlAddon::DateTimeFormatConstructor(napi_env env, napi_callback_info info) +{ + // Need to get one parameter of a locale in string format to create DateTimeFormat object. + size_t argc = 2; + napi_value argv[2] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + napi_valuetype valueType = napi_valuetype::napi_undefined; + napi_typeof(env, argv[0], &valueType); + std::vector localeTags; + bool isArray = false; + napi_is_array(env, argv[0], &isArray); + if (valueType == napi_valuetype::napi_string) { + GetLocaleTags(env, argv[0], localeTags); + } else if (isArray) { + uint32_t arrayLength = 0; + napi_get_array_length(env, argv[0], &arrayLength); + napi_value element; + for (uint32_t i = 0; i < arrayLength; i++) { + napi_get_element(env, argv[0], i, &element); + GetLocaleTags(env, element, localeTags); + } + } else { + return nullptr; + } + + std::map map = {}; + if (argv[1] != nullptr) { + GetDateOptionValues(env, argv[1], map); + } + + std::unique_ptr obj = std::make_unique(); + if (obj == nullptr) { + HiLog::Error(LABEL, "Create IntlAddon failed"); + return nullptr; + } + + status = + napi_wrap(env, thisVar, reinterpret_cast(obj.get()), IntlAddon::Destructor, nullptr, &obj->wrapper_); + if (status != napi_ok) { + HiLog::Error(LABEL, "Wrap IntlAddon failed"); + return nullptr; + } + + if (!obj->InitDateTimeFormatContext(env, info, localeTags, map)) { + HiLog::Error(LABEL, "Init DateTimeFormat failed"); + return nullptr; + } + + obj.release(); + return thisVar; +} + +bool IntlAddon::InitDateTimeFormatContext(napi_env env, napi_callback_info info, std::vector localeTags, + std::map &map) +{ + napi_value global; + napi_status status = napi_get_global(env, &global); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get global failed"); + return false; + } + env_ = env; + datefmt_ = std::make_unique(localeTags, map); + + return datefmt_ != nullptr; +} + +napi_value IntlAddon::FormatDateTime(napi_env env, napi_callback_info info) +{ + size_t argc = 1; + napi_value argv[1] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + int64_t year = GetYear(env, argv, 0); + int64_t month = GetMonth(env, argv, 0); + int64_t day = GetDay(env, argv, 0); + int64_t hour = GetHour(env, argv, 0); + int64_t minute = GetMinute(env, argv, 0); + int64_t second = GetSecond(env, argv, 0); + if (year == -1 || month == -1 || day == -1 || hour == -1 || minute == -1 || second == -1) { + return nullptr; + } + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->datefmt_ == nullptr) { + HiLog::Error(LABEL, "Get DateTimeFormat object failed"); + return nullptr; + } + int64_t date[] = { year, month, day, hour, minute, second }; + std::string value = obj->datefmt_->Format(date, std::end(date) - std::begin(date)); + napi_value result; + status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create format string failed"); + return nullptr; + } + return result; +} + +napi_value IntlAddon::FormatDateTimeRange(napi_env env, napi_callback_info info) +{ + size_t argc = 2; + napi_value argv[2] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + int64_t firstYear = GetYear(env, argv, 0); + int64_t firstMonth = GetMonth(env, argv, 0); + int64_t firstDay = GetDay(env, argv, 0); + int64_t firstHour = GetHour(env, argv, 0); + int64_t firstMinute = GetMinute(env, argv, 0); + int64_t firstSecond = GetSecond(env, argv, 0); + int64_t firstDate[] = { firstYear, firstMonth, firstDay, firstHour, firstMinute, firstSecond }; + int64_t secondYear = GetYear(env, argv, 1); + int64_t secondMonth = GetMonth(env, argv, 1); + int64_t secondDay = GetDay(env, argv, 1); + int64_t secondHour = GetHour(env, argv, 1); + int64_t secondMinute = GetMinute(env, argv, 1); + int64_t secondSecond = GetSecond(env, argv, 1); + int64_t secondDate[] = { secondYear, secondMonth, secondDay, secondHour, secondMinute, secondSecond }; + if (firstYear == -1 || firstMonth == -1 || firstDay == -1 || firstHour == -1 || firstMinute == -1 || + firstSecond == -1) { + return nullptr; + } + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->datefmt_ == nullptr) { + HiLog::Error(LABEL, "Get DateTimeFormat object failed"); + return nullptr; + } + std::string value = obj->datefmt_->FormatRange(firstDate, std::end(firstDate) - std::begin(firstDate), + secondDate, std::end(secondDate) - std::begin(secondDate)); + napi_value result; + status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create format string failed"); + return nullptr; + } + return result; +} + +void GetNumberOptionValues(napi_env env, napi_value options, std::map &map) +{ + GetOptionValue(env, options, "currency", map); + GetOptionValue(env, options, "currencySign", map); + GetOptionValue(env, options, "currencyDisplay", map); + GetOptionValue(env, options, "unit", map); + GetOptionValue(env, options, "unitDisplay", map); + GetOptionValue(env, options, "compactDisplay", map); + GetOptionValue(env, options, "signDisplay", map); + GetOptionValue(env, options, "localeMatcher", map); + GetOptionValue(env, options, "style", map); + GetOptionValue(env, options, "numberingSystem", map); + GetOptionValue(env, options, "notation", map); + GetBoolOptionValue(env, options, "useGrouping", map); + GetIntegerOptionValue(env, options, "minimumIntegerDigits", map); + GetIntegerOptionValue(env, options, "minimumFractionDigits", map); + GetIntegerOptionValue(env, options, "maximumFractionDigits", map); + GetIntegerOptionValue(env, options, "minimumSignificantDigits", map); + GetIntegerOptionValue(env, options, "maximumSignificantDigits", map); +} + +napi_value IntlAddon::NumberFormatConstructor(napi_env env, napi_callback_info info) +{ + // Need to get one parameter of a locale in string format to create DateTimeFormat object. + size_t argc = 2; + napi_value argv[2] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + napi_valuetype valueType = napi_valuetype::napi_undefined; + napi_typeof(env, argv[0], &valueType); + std::vector localeTags; + bool isArray = false; + napi_is_array(env, argv[0], &isArray); + if (valueType == napi_valuetype::napi_string) { + GetLocaleTags(env, argv[0], localeTags); + } else if (isArray) { + uint32_t arrayLength = 0; + napi_get_array_length(env, argv[0], &arrayLength); + napi_value element; + for (uint32_t i = 0; i < arrayLength; i++) { + napi_get_element(env, argv[0], i, &element); + GetLocaleTags(env, element, localeTags); + } + } else { + return nullptr; + } + + std::map map = {}; + if (argv[1] != nullptr) { + GetNumberOptionValues(env, argv[1], map); + } + + std::unique_ptr obj = std::make_unique(); + if (obj == nullptr) { + HiLog::Error(LABEL, "Create IntlAddon failed"); + return nullptr; + } + + status = + napi_wrap(env, thisVar, reinterpret_cast(obj.get()), IntlAddon::Destructor, nullptr, &obj->wrapper_); + if (status != napi_ok) { + HiLog::Error(LABEL, "Wrap IntlAddon failed"); + return nullptr; + } + + if (!obj->InitNumberFormatContext(env, info, localeTags, map)) { + HiLog::Error(LABEL, "Init NumberFormat failed"); + return nullptr; + } + + obj.release(); + return thisVar; +} + +bool IntlAddon::InitNumberFormatContext(napi_env env, napi_callback_info info, std::vector localeTags, + std::map &map) +{ + napi_value global; + napi_status status = napi_get_global(env, &global); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get global failed"); + return false; + } + env_ = env; + numberfmt_ = std::make_unique(localeTags, map); + + return numberfmt_ != nullptr; +} + +int64_t IntlAddon::GetYear(napi_env env, napi_value *argv, int index) +{ + napi_value funcGetDateInfo; + napi_status status = napi_get_named_property(env, argv[index], "getFullYear", &funcGetDateInfo); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get year property failed"); + return -1; + } + napi_value ret_value; + status = napi_call_function(env, argv[index], funcGetDateInfo, 0, nullptr, &ret_value); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get year function failed"); + return -1; + } + int64_t year; + status = napi_get_value_int64(env, ret_value, &year); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get year failed"); + return -1; + } + return year; +} + +int64_t IntlAddon::GetMonth(napi_env env, napi_value *argv, int index) +{ + napi_value funcGetDateInfo; + napi_status status = napi_get_named_property(env, argv[index], "getMonth", &funcGetDateInfo); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get month property failed"); + return -1; + } + napi_value ret_value; + status = napi_call_function(env, argv[index], funcGetDateInfo, 0, nullptr, &ret_value); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get month function failed"); + return -1; + } + int64_t month; + status = napi_get_value_int64(env, ret_value, &month); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get month failed"); + return -1; + } + return month; +} + +int64_t IntlAddon::GetDay(napi_env env, napi_value *argv, int index) +{ + napi_value funcGetDateInfo; + napi_status status = napi_get_named_property(env, argv[index], "getDate", &funcGetDateInfo); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get day property failed"); + return -1; + } + napi_value ret_value; + status = napi_call_function(env, argv[index], funcGetDateInfo, 0, nullptr, &ret_value); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get day function failed"); + return -1; + } + int64_t day; + status = napi_get_value_int64(env, ret_value, &day); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get day failed"); + return -1; + } + return day; +} + +int64_t IntlAddon::GetHour(napi_env env, napi_value *argv, int index) +{ + napi_value funcGetDateInfo; + napi_status status = napi_get_named_property(env, argv[index], "getHours", &funcGetDateInfo); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get hour property failed"); + return -1; + } + napi_value ret_value; + status = napi_call_function(env, argv[index], funcGetDateInfo, 0, nullptr, &ret_value); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get hour function failed"); + return -1; + } + int64_t hour; + status = napi_get_value_int64(env, ret_value, &hour); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get hour failed"); + return -1; + } + return hour; +} + +int64_t IntlAddon::GetMinute(napi_env env, napi_value *argv, int index) +{ + napi_value funcGetDateInfo; + napi_status status = napi_get_named_property(env, argv[index], "getMinutes", &funcGetDateInfo); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get minute property failed"); + return -1; + } + napi_value ret_value; + status = napi_call_function(env, argv[index], funcGetDateInfo, 0, nullptr, &ret_value); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get minute function failed"); + return -1; + } + int64_t minute; + status = napi_get_value_int64(env, ret_value, &minute); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get minute failed"); + return -1; + } + return minute; +} + +int64_t IntlAddon::GetSecond(napi_env env, napi_value *argv, int index) +{ + napi_value funcGetDateInfo; + napi_status status = napi_get_named_property(env, argv[index], "getSeconds", &funcGetDateInfo); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get second property failed"); + return -1; + } + napi_value ret_value; + status = napi_call_function(env, argv[index], funcGetDateInfo, 0, nullptr, &ret_value); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get second function failed"); + return -1; + } + int64_t second; + status = napi_get_value_int64(env, ret_value, &second); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get second failed"); + return -1; + } + return second; +} + +napi_value IntlAddon::GetLanguage(napi_env env, napi_callback_info info) +{ + size_t argc = 0; + napi_value argv[0]; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { + HiLog::Error(LABEL, "Get Locale object failed"); + return nullptr; + } + std::string value = obj->locale_->GetLanguage(); + + napi_value result; + status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create language string failed"); + return nullptr; + } + return result; +} + +napi_value IntlAddon::GetScript(napi_env env, napi_callback_info info) +{ + size_t argc = 0; + napi_value argv[0]; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { + HiLog::Error(LABEL, "Get Locale object failed"); + return nullptr; + } + std::string value = obj->locale_->GetScript(); + + napi_value result; + status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create script string failed"); + return nullptr; + } + return result; +} + +napi_value IntlAddon::GetRegion(napi_env env, napi_callback_info info) +{ + size_t argc = 0; + napi_value argv[0]; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { + HiLog::Error(LABEL, "Get Locale object failed"); + return nullptr; + } + std::string value = obj->locale_->GetRegion(); + + napi_value result; + status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create region string failed"); + return nullptr; + } + return result; +} + +napi_value IntlAddon::GetBaseName(napi_env env, napi_callback_info info) +{ + size_t argc = 0; + napi_value argv[0]; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { + HiLog::Error(LABEL, "Get Locale object failed"); + return nullptr; + } + std::string value = obj->locale_->GetBaseName(); + + napi_value result; + status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create base name string failed"); + return nullptr; + } + return result; +} + +napi_value IntlAddon::GetCalendar(napi_env env, napi_callback_info info) +{ + size_t argc = 0; + napi_value argv[0]; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { + HiLog::Error(LABEL, "Get Locale object failed"); + return nullptr; + } + std::string value = obj->locale_->GetCalendar(); + + napi_value result; + status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create base name string failed"); + return nullptr; + } + return result; +} + +napi_value IntlAddon::GetCollation(napi_env env, napi_callback_info info) +{ + size_t argc = 0; + napi_value argv[0]; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { + HiLog::Error(LABEL, "Get Locale object failed"); + return nullptr; + } + std::string value = obj->locale_->GetCollation(); + + napi_value result; + status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create base name string failed"); + return nullptr; + } + return result; +} + +napi_value IntlAddon::GetHourCycle(napi_env env, napi_callback_info info) +{ + size_t argc = 0; + napi_value argv[0]; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { + HiLog::Error(LABEL, "Get Locale object failed"); + return nullptr; + } + std::string value = obj->locale_->GetHourCycle(); + + napi_value result; + status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create base name string failed"); + return nullptr; + } + return result; +} + +napi_value IntlAddon::GetNumberingSystem(napi_env env, napi_callback_info info) +{ + size_t argc = 0; + napi_value argv[0]; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { + HiLog::Error(LABEL, "Get Locale object failed"); + return nullptr; + } + std::string value = obj->locale_->GetNumberingSystem(); + + napi_value result; + status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create base name string failed"); + return nullptr; + } + return result; +} + +napi_value IntlAddon::GetNumeric(napi_env env, napi_callback_info info) +{ + size_t argc = 0; + napi_value argv[0]; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { + HiLog::Error(LABEL, "Get Locale object failed"); + return nullptr; + } + std::string value = obj->locale_->GetNumeric(); + bool optionBoolValue = (value == "true"); + napi_value result; + status = napi_get_boolean(env, optionBoolValue, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create numeric boolean value failed"); + return nullptr; + } + return result; +} + +napi_value IntlAddon::GetCaseFirst(napi_env env, napi_callback_info info) +{ + size_t argc = 0; + napi_value argv[0]; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { + HiLog::Error(LABEL, "Get Locale object failed"); + return nullptr; + } + std::string value = obj->locale_->GetCaseFirst(); + napi_value result; + status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create caseFirst string failed"); + return nullptr; + } + return result; +} + +napi_value IntlAddon::ToString(napi_env env, napi_callback_info info) +{ + size_t argc = 0; + napi_value argv[0]; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { + HiLog::Error(LABEL, "Get Locale object failed"); + return nullptr; + } + std::string value = obj->locale_->ToString(); + + napi_value result; + status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create language string failed"); + return nullptr; + } + return result; +} + +napi_value IntlAddon::Maximize(napi_env env, napi_callback_info info) +{ + size_t argc = 0; + napi_value argv[0]; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { + HiLog::Error(LABEL, "Get Locale object failed"); + return nullptr; + } + std::string localeTag = obj->locale_->Maximize(); + + napi_value constructor; + status = napi_get_reference_value(env, *g_constructor, &constructor); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get locale constructor reference failed"); + return nullptr; + } + napi_value result = nullptr; + napi_value arg = nullptr; + status = napi_create_string_utf8(env, localeTag.c_str(), NAPI_AUTO_LENGTH, &arg); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create localeTag string failed"); + return nullptr; + } + status = napi_new_instance(env, constructor, 1, &arg, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create new locale instance failed"); + return nullptr; + } + return result; +} + +napi_value IntlAddon::Minimize(napi_env env, napi_callback_info info) +{ + size_t argc = 0; + napi_value argv[0]; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->locale_ == nullptr) { + HiLog::Error(LABEL, "Get Locale object failed"); + return nullptr; + } + std::string localeTag = obj->locale_->Minimize(); + + napi_value constructor; + status = napi_get_reference_value(env, *g_constructor, &constructor); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get locale constructor reference failed"); + return nullptr; + } + napi_value result = nullptr; + napi_value arg = nullptr; + status = napi_create_string_utf8(env, localeTag.c_str(), NAPI_AUTO_LENGTH, &arg); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create localeTag string failed"); + return nullptr; + } + status = napi_new_instance(env, constructor, 1, &arg, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create new locale instance failed"); + return nullptr; + } + return result; +} + +void SetOptionProperties(napi_env env, napi_value &result, std::map &options, + const std::string &option) +{ + if (options.count(option) > 0) { + std::string optionValue = options[option]; + napi_value optionJsValue; + napi_create_string_utf8(env, optionValue.c_str(), NAPI_AUTO_LENGTH, &optionJsValue); + napi_set_named_property(env, result, option.c_str(), optionJsValue); + } else { + napi_value undefined; + napi_get_undefined(env, &undefined); + napi_set_named_property(env, result, option.c_str(), undefined); + } +} + +void SetIntegerOptionProperties(napi_env env, napi_value &result, std::map &options, + const std::string &option) +{ + if (options.count(option) > 0) { + std::string optionValue = options[option]; + napi_value optionJsValue; + int64_t integerValue = std::stoi(optionValue); + napi_create_int64(env, integerValue, &optionJsValue); + napi_set_named_property(env, result, option.c_str(), optionJsValue); + } else { + napi_value undefined; + napi_get_undefined(env, &undefined); + napi_set_named_property(env, result, option.c_str(), undefined); + } +} + +void SetBooleanOptionProperties(napi_env env, napi_value &result, std::map &options, + const std::string &option) +{ + if (options.count(option) > 0) { + std::string optionValue = options[option]; + bool optionBoolValue = (optionValue == "true"); + napi_value optionJsValue; + napi_get_boolean(env, optionBoolValue, &optionJsValue); + napi_set_named_property(env, result, option.c_str(), optionJsValue); + } else { + napi_value undefined; + napi_get_undefined(env, &undefined); + napi_set_named_property(env, result, option.c_str(), undefined); + } +} + +napi_value IntlAddon::GetDateTimeResolvedOptions(napi_env env, napi_callback_info info) +{ + size_t argc = 0; + napi_value argv[0]; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->datefmt_ == nullptr) { + HiLog::Error(LABEL, "Get DateTimeFormat object failed"); + return nullptr; + } + napi_value result; + napi_create_object(env, &result); + std::map options = {}; + obj->datefmt_->GetResolvedOptions(options); + SetOptionProperties(env, result, options, "locale"); + SetOptionProperties(env, result, options, "calendar"); + SetOptionProperties(env, result, options, "dateStyle"); + SetOptionProperties(env, result, options, "timeStyle"); + SetOptionProperties(env, result, options, "hourCycle"); + SetOptionProperties(env, result, options, "timeZone"); + SetOptionProperties(env, result, options, "timeZoneName"); + SetOptionProperties(env, result, options, "numberingSystem"); + SetBooleanOptionProperties(env, result, options, "hour12"); + SetOptionProperties(env, result, options, "weekday"); + SetOptionProperties(env, result, options, "era"); + SetOptionProperties(env, result, options, "year"); + SetOptionProperties(env, result, options, "month"); + SetOptionProperties(env, result, options, "day"); + SetOptionProperties(env, result, options, "hour"); + SetOptionProperties(env, result, options, "minute"); + SetOptionProperties(env, result, options, "second"); + SetOptionProperties(env, result, options, "dayPeriod"); + SetOptionProperties(env, result, options, "localeMatcher"); + SetOptionProperties(env, result, options, "formatMatcher"); + return result; +} + +napi_value IntlAddon::GetNumberResolvedOptions(napi_env env, napi_callback_info info) +{ + size_t argc = 0; + napi_value argv[0]; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->numberfmt_ == nullptr) { + HiLog::Error(LABEL, "Get NumberFormat object failed"); + return nullptr; + } + napi_value result; + napi_create_object(env, &result); + std::map options = {}; + obj->numberfmt_->GetResolvedOptions(options); + SetOptionProperties(env, result, options, "locale"); + SetOptionProperties(env, result, options, "currency"); + SetOptionProperties(env, result, options, "currencySign"); + SetOptionProperties(env, result, options, "currencyDisplay"); + SetOptionProperties(env, result, options, "unit"); + SetOptionProperties(env, result, options, "unitDisplay"); + SetOptionProperties(env, result, options, "signDisplay"); + SetOptionProperties(env, result, options, "compactDisplay"); + SetOptionProperties(env, result, options, "notation"); + SetOptionProperties(env, result, options, "style"); + SetOptionProperties(env, result, options, "numberingSystem"); + SetBooleanOptionProperties(env, result, options, "useGrouping"); + SetIntegerOptionProperties(env, result, options, "minimumIntegerDigits"); + SetIntegerOptionProperties(env, result, options, "minimumFractionDigits"); + SetIntegerOptionProperties(env, result, options, "maximumFractionDigits"); + SetIntegerOptionProperties(env, result, options, "minimumSignificantDigits"); + SetIntegerOptionProperties(env, result, options, "maximumSignificantDigits"); + SetOptionProperties(env, result, options, "localeMatcher"); + return result; +} + +napi_value IntlAddon::FormatNumber(napi_env env, napi_callback_info info) +{ + size_t argc = 1; + napi_value argv[1] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + double number; + napi_get_value_double(env, argv[0], &number); + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->numberfmt_ == nullptr) { + HiLog::Error(LABEL, "Get NumberFormat object failed"); + return nullptr; + } + std::string value = obj->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, "Create format string failed"); + return nullptr; + } + return result; +} + +void GetCollatorLocaleMatcher(napi_env env, napi_value options, std::map &map) +{ + GetOptionValue(env, options, "localeMatcher", map); + auto it = map.find("localeMatcher"); + if (it != map.end()) { + std::string localeMatcher = it->second; + if (localeMatcher != "lookup" && localeMatcher != "best fit") { + HiLog::Error(LABEL, "invalid localeMatcher"); + return; + } + } else { + map.insert(std::make_pair("localeMatcher", "best fit")); + } +} + +void GetCollatorUsage(napi_env env, napi_value options, std::map &map) +{ + GetOptionValue(env, options, "usage", map); + auto it = map.find("usage"); + if (it != map.end()) { + std::string usage = it->second; + if (usage != "sort" && usage != "search") { + HiLog::Error(LABEL, "invalid usage"); + return; + } + } else { + map.insert(std::make_pair("usage", "sort")); + } +} + +void GetCollatorSensitivity(napi_env env, napi_value options, std::map &map) +{ + GetOptionValue(env, options, "sensitivity", map); + auto it = map.find("sensitivity"); + if (it != map.end()) { + std::string sensitivity = it->second; + if (sensitivity != "base" && sensitivity != "accent" && sensitivity != "case" && sensitivity != "variant") { + HiLog::Error(LABEL, "invalid sensitivity"); + return; + } + } else { + map.insert(std::make_pair("sensitivity", "variant")); + } +} + +void GetCollatorIgnorePunctuation(napi_env env, napi_value options, std::map &map) +{ + GetBoolOptionValue(env, options, "ignorePunctuation", map); + auto it = map.find("ignorePunctuation"); + if (it != map.end()) { + std::string ignorePunctuation = it->second; + if (ignorePunctuation != "true" && ignorePunctuation != "false") { + HiLog::Error(LABEL, "invalid ignorePunctuation"); + return; + } + } else { + map.insert(std::make_pair("ignorePunctuation", "false")); + } +} + +void GetCollatorNumeric(napi_env env, napi_value options, std::map &map) +{ + GetBoolOptionValue(env, options, "numeric", map); + auto it = map.find("numeric"); + if (it != map.end()) { + std::string numeric = it->second; + if (numeric != "true" && numeric != "false") { + HiLog::Error(LABEL, "invalid numeric"); + return; + } + } +} + +void GetCollatorCaseFirst(napi_env env, napi_value options, std::map &map) +{ + GetOptionValue(env, options, "caseFirst", map); + auto it = map.find("caseFirst"); + if (it != map.end()) { + std::string caseFirst = it->second; + if (caseFirst != "upper" && caseFirst != "lower" && caseFirst != "false") { + HiLog::Error(LABEL, "invalid caseFirst"); + return; + } + } +} + +void GetCollatorCollation(napi_env env, napi_value options, std::map &map) +{ + GetOptionValue(env, options, "collation", map); + auto it = map.find("collation"); + if (it != map.end()) { + std::string collation = it->second; + std::set validCollation( + { "big5han", "compat", "dict", "direct", "ducet", "eor", "gb2312", + "phonebk", "phonetic", "pinyin", "reformed", "searchjl", "stroke", + "trad", "unihan", "zhuyin" } + ); + if (validCollation.find(collation) == validCollation.end()) { + map["collation"] = "default"; + } + } +} + +void GetCollatorOptionValue(napi_env env, napi_value options, std::map &map) +{ + GetCollatorLocaleMatcher(env, options, map); + GetCollatorUsage(env, options, map); + GetCollatorSensitivity(env, options, map); + GetCollatorIgnorePunctuation(env, options, map); + GetCollatorNumeric(env, options, map); + GetCollatorCaseFirst(env, options, map); + GetCollatorCollation(env, options, map); +} + +napi_value IntlAddon::InitCollator(napi_env env, napi_value exports) +{ + napi_status status = napi_ok; + napi_property_descriptor properties[] = { + DECLARE_NAPI_FUNCTION("compare", CompareString), + DECLARE_NAPI_FUNCTION("resolvedOptions", GetCollatorResolvedOptions) + }; + + napi_value constructor; + status = napi_define_class(env, "Collator", NAPI_AUTO_LENGTH, CollatorConstructor, nullptr, + sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor); + if (status != napi_ok) { + HiLog::Error(LABEL, "Define class failed when InitCollator"); + return nullptr; + } + + status = napi_set_named_property(env, exports, "Collator", constructor); + if (status != napi_ok) { + HiLog::Error(LABEL, "Set property failed when InitCollator"); + return nullptr; + } + return exports; +} + +napi_value IntlAddon::CollatorConstructor(napi_env env, napi_callback_info info) +{ + size_t argc = 2; + napi_value argv[2] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + std::vector localeTags; + if (argv[0] != nullptr) { + napi_valuetype valueType = napi_valuetype::napi_undefined; + napi_typeof(env, argv[0], &valueType); + bool isArray = false; + napi_is_array(env, argv[0], &isArray); + if (valueType == napi_valuetype::napi_string) { + GetLocaleTags(env, argv[0], localeTags); + } else if (isArray) { + uint32_t arrayLength = 0; + napi_get_array_length(env, argv[0], &arrayLength); + napi_value element; + for (uint32_t i = 0; i < arrayLength; i++) { + napi_get_element(env, argv[0], i, &element); + GetLocaleTags(env, element, localeTags); + } + } + } + + std::map map = {}; + if (argv[1] != nullptr) { + GetCollatorOptionValue(env, argv[1], map); + } + + std::unique_ptr obj = std::make_unique(); + if (obj == nullptr) { + HiLog::Error(LABEL, "Create IntlAddon failed"); + return nullptr; + } + status = + napi_wrap(env, thisVar, reinterpret_cast(obj.get()), IntlAddon::Destructor, nullptr, &obj->wrapper_); + if (status != napi_ok) { + HiLog::Error(LABEL, "Wrap IntlAddon failed"); + return nullptr; + } + if (!obj->InitCollatorContext(env, info, localeTags, map)) { + HiLog::Error(LABEL, "Init DateTimeFormat failed"); + return nullptr; + } + obj.release(); + return thisVar; +} + +bool IntlAddon::InitCollatorContext(napi_env env, napi_callback_info info, std::vector localeTags, + std::map &map) +{ + napi_value global; + napi_status status = napi_get_global(env, &global); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get global failed"); + return false; + } + env_ = env; + collator_ = std::make_unique(localeTags, map); + + return collator_ != nullptr; +} + +bool GetStringParameter(napi_env env, napi_value value, std::vector &buf) +{ + napi_valuetype valueType = napi_valuetype::napi_undefined; + napi_typeof(env, value, &valueType); + if (valueType != napi_valuetype::napi_string) { + napi_throw_type_error(env, nullptr, "Parameter type does not match"); + return false; + } + size_t len; + napi_status status = napi_get_value_string_utf8(env, value, nullptr, 0, &len); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get first length failed"); + return false; + } + buf.resize(len + 1); + status = napi_get_value_string_utf8(env, value, buf.data(), len + 1, &len); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get first failed"); + return false; + } + + return true; +} + +napi_value IntlAddon::CompareString(napi_env env, napi_callback_info info) +{ + size_t argc = 2; + napi_value argv[2] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + + std::vector first; + if (!GetStringParameter(env, argv[0], first)) { + return nullptr; + } + + std::vector second; + if (!GetStringParameter(env, argv[1], second)) { + return nullptr; + } + + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->collator_ == nullptr) { + HiLog::Error(LABEL, "Get Collator object failed"); + return nullptr; + } + + int32_t compareResult = obj->collator_->Compare(first.data(), second.data()); + napi_value result; + status = napi_create_int32(env, compareResult, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create compare result failed"); + return nullptr; + } + + return result; +} + +napi_value IntlAddon::GetCollatorResolvedOptions(napi_env env, napi_callback_info info) +{ + size_t argc = 0; + napi_value argv[0]; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + + IntlAddon *obj = nullptr; + napi_status status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->collator_ == nullptr) { + HiLog::Error(LABEL, "Get Collator object failed"); + return nullptr; + } + napi_value result; + napi_create_object(env, &result); + std::map options = {}; + obj->collator_->ResolvedOptions(options); + SetOptionProperties(env, result, options, "locale"); + SetOptionProperties(env, result, options, "usage"); + SetOptionProperties(env, result, options, "sensitivity"); + SetBooleanOptionProperties(env, result, options, "ignorePunctuation"); + SetBooleanOptionProperties(env, result, options, "numeric"); + SetOptionProperties(env, result, options, "caseFirst"); + SetOptionProperties(env, result, options, "collation"); + return result; +} + +void GetPluralRulesType(napi_env env, napi_value options, std::map &map) +{ + GetOptionValue(env, options, "type", map); + auto it = map.find("type"); + if (it != map.end()) { + std::string type = it->second; + if (type != "cardinal" && type != "ordinal") { + HiLog::Error(LABEL, "invalid type"); + return; + } + } else { + map.insert(std::make_pair("type", "cardinal")); + } +} + +void GetPluralRulesInteger(napi_env env, napi_value options, std::map &map) +{ + GetIntegerOptionValue(env, options, "minimumIntegerDigits", map); + auto it = map.find("minimumIntegerDigits"); + if (it != map.end()) { + std::string minimumIntegerDigits = it->second; + int n = std::stoi(minimumIntegerDigits); + if (n < 1 || n > 21) { // the valid range of minimumIntegerDigits is [1, 21] + HiLog::Error(LABEL, "invalid minimumIntegerDigits"); + return; + } + } else { + map.insert(std::make_pair("minimumIntegerDigits", std::to_string(1))); + } +} + +void GetPluralRulesFractions(napi_env env, napi_value options, std::map &map) +{ + GetIntegerOptionValue(env, options, "minimumFractionDigits", map); + auto it = map.find("minimumFractionDigits"); + if (it != map.end()) { + std::string minimumFractionDigits = it->second; + int n = std::stoi(minimumFractionDigits); + if (n < 0 || n > 20) { // the valid range of minimumFractionDigits is [0, 20] + HiLog::Error(LABEL, "invalid minimumFractionDigits"); + return; + } + } + + GetIntegerOptionValue(env, options, "maximumFractionDigits", map); + it = map.find("maximumFractionDigits"); + if (it != map.end()) { + std::string maximumFractionDigits = it->second; + int n = std::stoi(maximumFractionDigits); + if (n < 0 || n > 20) { // the valid range of maximumFractionDigits is [0, 20] + HiLog::Error(LABEL, "invalid maximumFractionDigits"); + return; + } + } +} + +void GetPluralRulesSignificant(napi_env env, napi_value options, std::map &map) +{ + int minSignificant = -1; + GetIntegerOptionValue(env, options, "minimumSignificantDigits", map); + auto it = map.find("minimumSignificantDigits"); + if (it != map.end()) { + std::string minSignificantStr = it->second; + int minSignificantInt = std::stoi(minSignificantStr); + // the valid range of minSignificantInt is [1, 21] + if (minSignificantInt < 1 || minSignificantInt > 21) { + HiLog::Error(LABEL, "invalid minimumSignificantDigits"); + return; + } + minSignificant = minSignificantInt; + } else { + minSignificant = 1; + } + + GetIntegerOptionValue(env, options, "maximumSignificantDigits", map); + it = map.find("maximumSignificantDigits"); + if (it != map.end()) { + std::string maxSignificantStr = it->second; + int maxSignificant = std::stoi(maxSignificantStr); + // the valid range of minSignificant is [minSignificant, 21] + if (maxSignificant < minSignificant || maxSignificant > 21) { + HiLog::Error(LABEL, "invalid maximumSignificantDigits"); + return; + } + } +} + +void GetPluralRulesOptionValues(napi_env env, napi_value options, std::map &map) +{ + GetCollatorLocaleMatcher(env, options, map); + GetPluralRulesType(env, options, map); + GetPluralRulesInteger(env, options, map); + GetPluralRulesFractions(env, options, map); + GetPluralRulesSignificant(env, options, map); +} + +napi_value IntlAddon::InitPluralRules(napi_env env, napi_value exports) +{ + napi_status status = napi_ok; + napi_property_descriptor properties[] = { + DECLARE_NAPI_FUNCTION("select", Select) + }; + + napi_value constructor; + status = napi_define_class(env, "PluralRules", NAPI_AUTO_LENGTH, PluralRulesConstructor, nullptr, + sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor); + if (status != napi_ok) { + HiLog::Error(LABEL, "Define class failed when InitPluralRules"); + return nullptr; + } + + status = napi_set_named_property(env, exports, "PluralRules", constructor); + if (status != napi_ok) { + HiLog::Error(LABEL, "Set property failed when InitPluralRules"); + return nullptr; + } + return exports; +} + +napi_value IntlAddon::PluralRulesConstructor(napi_env env, napi_callback_info info) +{ + size_t argc = 2; + napi_value argv[2] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + napi_valuetype valueType = napi_valuetype::napi_undefined; + std::vector localeTags; + if (argv[0] != nullptr) { + napi_typeof(env, argv[0], &valueType); + bool isArray = false; + napi_is_array(env, argv[0], &isArray); + if (valueType == napi_valuetype::napi_string) { + GetLocaleTags(env, argv[0], localeTags); + } else if (isArray) { + uint32_t arrayLength = 0; + napi_get_array_length(env, argv[0], &arrayLength); + napi_value element; + for (uint32_t i = 0; i < arrayLength; i++) { + napi_get_element(env, argv[0], i, &element); + GetLocaleTags(env, element, localeTags); + } + } + } + std::map map = {}; + if (argv[1] != nullptr) { + GetPluralRulesOptionValues(env, argv[1], map); + } + std::unique_ptr obj = std::make_unique(); + if (obj == nullptr) { + HiLog::Error(LABEL, "Create IntlAddon failed"); + return nullptr; + } + status = + napi_wrap(env, thisVar, reinterpret_cast(obj.get()), IntlAddon::Destructor, nullptr, &obj->wrapper_); + if (status != napi_ok) { + HiLog::Error(LABEL, "Wrap IntlAddon failed"); + return nullptr; + } + if (!obj->InitPluralRulesContext(env, info, localeTags, map)) { + HiLog::Error(LABEL, "Init DateTimeFormat failed"); + return nullptr; + } + obj.release(); + return thisVar; +} + +bool IntlAddon::InitPluralRulesContext(napi_env env, napi_callback_info info, std::vector localeTags, + std::map &map) +{ + napi_value global; + napi_status status = napi_get_global(env, &global); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get global failed"); + return false; + } + env_ = env; + pluralrules_ = std::make_unique(localeTags, map); + + return pluralrules_ != nullptr; +} + +napi_value IntlAddon::Select(napi_env env, napi_callback_info info) +{ + size_t argc = 1; + napi_value argv[1] = { 0 }; + napi_value thisVar = nullptr; + void *data = nullptr; + napi_get_cb_info(env, info, &argc, argv, &thisVar, &data); + napi_valuetype valueType = napi_valuetype::napi_undefined; + napi_typeof(env, argv[0], &valueType); + if (valueType != napi_valuetype::napi_number) { + napi_throw_type_error(env, nullptr, "Parameter type does not match"); + return nullptr; + } + + double number; + napi_status status = napi_get_value_double(env, argv[0], &number); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get number failed"); + return nullptr; + } + + IntlAddon *obj = nullptr; + status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->pluralrules_ == nullptr) { + HiLog::Error(LABEL, "Get PluralRules object failed"); + return nullptr; + } + + std::string res = obj->pluralrules_->Select(number); + napi_value result; + status = napi_create_string_utf8(env, res.c_str(), NAPI_AUTO_LENGTH, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "get select result failed"); + return nullptr; + } + return result; +} + +napi_value Init(napi_env env, napi_value exports) +{ + napi_value val = IntlAddon::InitLocale(env, exports); + val = IntlAddon::InitDateTimeFormat(env, val); + val = IntlAddon::InitNumberFormat(env, val); + val = IntlAddon::InitCollator(env, val); + return IntlAddon::InitPluralRules(env, val); +} + +static napi_module g_intlModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "intl", + .nm_priv = ((void *)0), + .reserved = { 0 } +}; + +extern "C" __attribute__((constructor)) void AbilityRegister() +{ + napi_module_register(&g_intlModule); +} +} // namespace I18n +} // namespace Global } // namespace OHOS \ No newline at end of file