diff --git a/frameworks/intl/BUILD.gn b/frameworks/intl/BUILD.gn index 8d03091e..5aaebddf 100755 --- a/frameworks/intl/BUILD.gn +++ b/frameworks/intl/BUILD.gn @@ -19,6 +19,8 @@ config("intl_config") { "//third_party/icu/icu4c/source/common", "//third_party/icu/icu4c/source/i18n", "//base/global/i18n_standard/frameworks/intl/include", + "//third_party/libphonenumber/cpp/src", + "//third_party/protobuf/src", ] } ohos_shared_library("intl_util") { @@ -39,12 +41,16 @@ ohos_shared_library("intl_util") { "//base/telephony/core_service/interfaces/kits/include", "//utils/native/base/include", "//third_party/libxml2/include", + "//third_party/libphonenumber/cpp/src", + "//third_party/libphonenumber", + "//third_party/protobuf/src", ] sources = [ "src/date_time_format.cpp", "src/locale_config.cpp", "src/locale_info.cpp", "src/number_format.cpp", + "src/phone_number_format.cpp", ] cflags_cc = [ "-Wall", @@ -58,6 +64,7 @@ ohos_shared_library("intl_util") { "//foundation/appexecfwk/standard/interfaces/innerkits/libeventhandler:libeventhandler", "//third_party/icu/icu4c:shared_icui18n", "//third_party/icu/icu4c:shared_icuuc", + "//third_party/libphonenumber/cpp:phonenumber_standard", "//third_party/libxml2:xml2", "//utils/native/base:utils", ] diff --git a/frameworks/intl/include/phone_number_format.h b/frameworks/intl/include/phone_number_format.h new file mode 100644 index 00000000..4c58b201 --- /dev/null +++ b/frameworks/intl/include/phone_number_format.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OHOS_GLOBAL_I18N_PHONE_NUMBER_FORMAT_H +#define OHOS_GLOBAL_I18N_PHONE_NUMBER_FORMAT_H + +#include +#include +#include "cpp/src/phonenumbers/phonenumberutil.h" + +namespace OHOS { +namespace Global { +namespace I18n { +using i18n::phonenumbers::PhoneNumberUtil; + +class PhoneNumberFormat { +public: + PhoneNumberFormat(const std::string &countryTag, const std::map &options); + virtual ~PhoneNumberFormat(); + bool isValidPhoneNumber(const std::string &number) const; + std::string format(const std::string &number) const; + +private: + PhoneNumberUtil *util; + std::string country; + PhoneNumberUtil::PhoneNumberFormat phoneNumberFormat; +}; +} +} +} + +#endif // OHOS_GLOBAL_I18N_PHONE_NUMBER_FORMAT_H diff --git a/frameworks/intl/src/phone_number_format.cpp b/frameworks/intl/src/phone_number_format.cpp new file mode 100644 index 00000000..d797e043 --- /dev/null +++ b/frameworks/intl/src/phone_number_format.cpp @@ -0,0 +1,77 @@ +/* + * 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 "phone_number_format.h" + +namespace OHOS { +namespace Global { +namespace I18n { +using i18n::phonenumbers::PhoneNumberUtil; + +PhoneNumberFormat::PhoneNumberFormat(const std::string &countryTag, + const std::map &options) +{ + util = PhoneNumberUtil::GetInstance(); + country = countryTag; + + std::string type = ""; + auto search = options.find("type"); + if (search != options.end()) { + type = search->second; + } + + std::map type2PhoneNumberFormat = { + {"E164", PhoneNumberUtil::PhoneNumberFormat::E164}, + {"RFC3966", PhoneNumberUtil::PhoneNumberFormat::RFC3966}, + {"INTERNATIONAL", PhoneNumberUtil::PhoneNumberFormat::INTERNATIONAL}, + {"NATIONAL", PhoneNumberUtil::PhoneNumberFormat::NATIONAL} + }; + + std::set validType = {"E164", "RFC3966", "INTERNATIONAL", "NATIONAL"}; + if (validType.find(type) != validType.end()) { + phoneNumberFormat = type2PhoneNumberFormat[type]; + } else { + phoneNumberFormat = PhoneNumberUtil::PhoneNumberFormat::NATIONAL; + } +} + +PhoneNumberFormat::~PhoneNumberFormat() +{ +} + +bool PhoneNumberFormat::isValidPhoneNumber(const std::string &number) const +{ + i18n::phonenumbers::PhoneNumber phoneNumber; + PhoneNumberUtil::ErrorType type = util->Parse(number, country, &phoneNumber); + if (type != PhoneNumberUtil::ErrorType::NO_PARSING_ERROR) { + return false; + } + return util->IsValidNumber(phoneNumber); +} + +std::string PhoneNumberFormat::format(const std::string &number) const +{ + i18n::phonenumbers::PhoneNumber phoneNumber; + PhoneNumberUtil::ErrorType type = util->Parse(number, country, &phoneNumber); + if (type != PhoneNumberUtil::ErrorType::NO_PARSING_ERROR) { + return ""; + } + std::string formatted_number; + util->Format(phoneNumber, phoneNumberFormat, &formatted_number); + return formatted_number; +} +} +} +} \ No newline at end of file diff --git a/interfaces/js/kits/BUILD.gn b/interfaces/js/kits/BUILD.gn index a7045042..95e8c01c 100755 --- a/interfaces/js/kits/BUILD.gn +++ b/interfaces/js/kits/BUILD.gn @@ -24,6 +24,9 @@ ohos_shared_library("intl") { "//third_party/icu/icu4c/source", "//third_party/icu/icu4c/source/common", "//third_party/icu/icu4c/source/i18n", + "//third_party/libphonenumber/cpp/src", + "//third_party/libphonenumber", + "//third_party/protobuf/src", ] sources = [ "src/intl_addon.cpp" ] @@ -33,6 +36,7 @@ ohos_shared_library("intl") { "//foundation/ace/napi:ace_napi", "//third_party/icu/icu4c:shared_icui18n", "//third_party/icu/icu4c:shared_icuuc", + "//third_party/libphonenumber/cpp:phonenumber_standard", ] external_deps = [ "hiviewdfx_hilog_native:libhilog" ] relative_install_dir = "module" @@ -51,6 +55,9 @@ ohos_shared_library("i18n") { "//third_party/icu/icu4c/source", "//third_party/icu/icu4c/source/common", "//third_party/icu/icu4c/source/i18n", + "//third_party/libphonenumber/cpp/src", + "//third_party/libphonenumber", + "//third_party/protobuf/src", ] sources = [ "src/i18n_addon.cpp" ] @@ -60,10 +67,10 @@ ohos_shared_library("i18n") { "//foundation/ace/napi:ace_napi", "//third_party/icu/icu4c:shared_icui18n", "//third_party/icu/icu4c:shared_icuuc", + "//third_party/libphonenumber/cpp:phonenumber_standard", ] external_deps = [ "hiviewdfx_hilog_native:libhilog" ] relative_install_dir = "module" subsystem_name = "global" part_name = "i18n_standard" -} - +} \ No newline at end of file diff --git a/interfaces/js/kits/include/i18n_addon.h b/interfaces/js/kits/include/i18n_addon.h index 1d68e563..5196aca5 100644 --- a/interfaces/js/kits/include/i18n_addon.h +++ b/interfaces/js/kits/include/i18n_addon.h @@ -19,6 +19,7 @@ #include "napi/native_api.h" #include "napi/native_node_api.h" #include "locale_config.h" +#include "phone_number_format.h" namespace OHOS { namespace Global { @@ -40,6 +41,18 @@ public: static napi_value SetSystemLanguage(napi_env env, napi_callback_info info); static napi_value SetSystemRegion(napi_env env, napi_callback_info info); static napi_value SetSystemLocale(napi_env env, napi_callback_info info); + static napi_value InitPhoneNumberFormat(napi_env env, napi_value exports); + +private: + static napi_value PhoneNumberFormatConstructor(napi_env env, napi_callback_info info); + static napi_value IsValidPhoneNumber(napi_env env, napi_callback_info info); + static napi_value FormatPhoneNumber(napi_env env, napi_callback_info info); + bool InitPhoneNumberFormatContext(napi_env env, napi_callback_info info, const std::string &country, + const std::map &options); + + napi_env env_; + napi_ref wrapper_; + std::unique_ptr phonenumberfmt_; }; } // namespace I18n } // namespace Global diff --git a/interfaces/js/kits/src/i18n_addon.cpp b/interfaces/js/kits/src/i18n_addon.cpp index 754276eb..110133d8 100644 --- a/interfaces/js/kits/src/i18n_addon.cpp +++ b/interfaces/js/kits/src/i18n_addon.cpp @@ -23,10 +23,11 @@ namespace I18n { static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, 0xD001E00, "I18nJs" }; using namespace OHOS::HiviewDFX; -I18nAddon::I18nAddon() {} +I18nAddon::I18nAddon() : env_(nullptr), wrapper_(nullptr) {} I18nAddon::~I18nAddon() { + napi_delete_reference(env_, wrapper_); } void I18nAddon::Destructor(napi_env env, void *nativeObject, void *hint) @@ -55,7 +56,8 @@ napi_value I18nAddon::Init(napi_env env, napi_value exports) }; status = napi_define_properties(env, exports, - sizeof(properties) / sizeof(napi_property_descriptor), properties); + sizeof(properties) / sizeof(napi_property_descriptor), + properties); if (status != napi_ok) { HiLog::Error(LABEL, "Failed to set properties at init"); return nullptr; @@ -354,9 +356,219 @@ napi_value I18nAddon::SetSystemLocale(napi_env env, napi_callback_info info) return result; } +napi_value I18nAddon::InitPhoneNumberFormat(napi_env env, napi_value exports) +{ + napi_status status; + napi_property_descriptor properties[] = { + DECLARE_NAPI_FUNCTION("isValidNumber", IsValidPhoneNumber), + DECLARE_NAPI_FUNCTION("format", FormatPhoneNumber) + }; + + napi_value constructor; + status = napi_define_class(env, "PhoneNumberFormat", NAPI_AUTO_LENGTH, PhoneNumberFormatConstructor, nullptr, + sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor); + if (status != napi_ok) { + HiLog::Error(LABEL, "Define class failed when InitPhoneNumberFormat"); + return nullptr; + } + + status = napi_set_named_property(env, exports, "PhoneNumberFormat", constructor); + if (status != napi_ok) { + HiLog::Error(LABEL, "Set property failed when InitPhoneNumberFormat"); + 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())); + } + } +} + +napi_value I18nAddon::PhoneNumberFormatConstructor(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; + 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 country tag length failed"); + return nullptr; + } + + std::vector country (len + 1); + status = napi_get_value_string_utf8(env, argv[0], country.data(), len + 1, &len); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get country tag failed"); + return nullptr; + } + + std::map options; + GetOptionValue(env, argv[1], "type", options); + + 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()), + I18nAddon::Destructor, nullptr, &obj->wrapper_); + if (status != napi_ok) { + HiLog::Error(LABEL, "Wrap IntlAddon failed"); + return nullptr; + } + + if (!obj->InitPhoneNumberFormatContext(env, info, country.data(), options)) { + return nullptr; + } + + obj.release(); + + return thisVar; +} + +bool I18nAddon::InitPhoneNumberFormatContext(napi_env env, napi_callback_info info, const std::string &country, + const std::map &options) +{ + 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; + phonenumberfmt_ = std::make_unique(country, options); + + return phonenumberfmt_ != nullptr; +} + +napi_value I18nAddon::IsValidPhoneNumber(napi_env env, napi_callback_info info) +{ + 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); + 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; + napi_status status = napi_get_value_string_utf8(env, argv[0], nullptr, 0, &len); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get phone number 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 phone number failed"); + return nullptr; + } + + I18nAddon *obj = nullptr; + status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->phonenumberfmt_ == nullptr) { + HiLog::Error(LABEL, "GetPhoneNumberFormat object failed"); + return nullptr; + } + + bool isValid = obj->phonenumberfmt_->isValidPhoneNumber(buf.data()); + + napi_value result; + status = napi_get_boolean(env, isValid, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create boolean failed"); + return nullptr; + } + + return result; +} + +napi_value I18nAddon::FormatPhoneNumber(napi_env env, napi_callback_info info) +{ + 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); + 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; + napi_status status = napi_get_value_string_utf8(env, argv[0], nullptr, 0, &len); + if (status != napi_ok) { + HiLog::Error(LABEL, "Get phone number 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 phone number failed"); + return nullptr; + } + + I18nAddon *obj = nullptr; + status = napi_unwrap(env, thisVar, reinterpret_cast(&obj)); + if (status != napi_ok || obj == nullptr || obj->phonenumberfmt_ == nullptr) { + HiLog::Error(LABEL, "Get PhoneNumberFormat object failed"); + return nullptr; + } + + std::string formattedPhoneNumber = obj->phonenumberfmt_->format(buf.data()); + + napi_value result; + status = napi_create_string_utf8(env, formattedPhoneNumber.c_str(), NAPI_AUTO_LENGTH, &result); + if (status != napi_ok) { + HiLog::Error(LABEL, "Create format phone number failed"); + return nullptr; + } + return result; +} + + napi_value Init(napi_env env, napi_value exports) { - return I18nAddon::Init(env, exports); + napi_value val = I18nAddon::Init(env, exports); + return I18nAddon::InitPhoneNumberFormat(env, val); } static napi_module g_i18nModule = {