* Copyright (c) 2021-2026 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 <vector>
#include <set>
#include "icu_data.h"
#include "log.h"
#include "node_api.h"
#include "plugin_utils.h"
#ifdef ANDROID_PLATFORM
#include "plugins/intl/android/java/jni/intl_plugin_jni.h"
#endif
namespace OHOS {
namespace Global {
namespace I18n {
static thread_local napi_ref *g_constructor = nullptr;
IntlAddon::IntlAddon() : env_(nullptr) {}
IntlAddon::~IntlAddon()
{
}
void IntlAddon::Destructor(napi_env env, void *nativeObject, void *hint)
{
if (!nativeObject) {
return;
}
reinterpret_cast<IntlAddon *>(nativeObject)->~IntlAddon();
}
napi_value IntlAddon::SetProperty(napi_env env, napi_callback_info info)
{
napi_value result = nullptr;
NAPI_CALL(env, napi_get_undefined(env, &result));
return result;
}
napi_value IntlAddon::InitLocale(napi_env env, napi_value exports)
{
napi_status status = napi_ok;
napi_property_descriptor properties[] = {
DECLARE_NAPI_GETTER_SETTER("language", GetLanguage, SetProperty),
DECLARE_NAPI_GETTER_SETTER("baseName", GetBaseName, SetProperty),
DECLARE_NAPI_GETTER_SETTER("region", GetRegion, SetProperty),
DECLARE_NAPI_GETTER_SETTER("script", GetScript, SetProperty),
DECLARE_NAPI_GETTER_SETTER("calendar", GetCalendar, SetProperty),
DECLARE_NAPI_GETTER_SETTER("collation", GetCollation, SetProperty),
DECLARE_NAPI_GETTER_SETTER("hourCycle", GetHourCycle, SetProperty),
DECLARE_NAPI_GETTER_SETTER("numberingSystem", GetNumberingSystem, SetProperty),
DECLARE_NAPI_GETTER_SETTER("numeric", GetNumeric, SetProperty),
DECLARE_NAPI_GETTER_SETTER("caseFirst", GetCaseFirst, SetProperty),
DECLARE_NAPI_FUNCTION("toString", ToString),
DECLARE_NAPI_FUNCTION("minimize", Minimize),
DECLARE_NAPI_FUNCTION("maximize", Maximize),
};
napi_value constructor = nullptr;
status = napi_define_class(env, "Locale", NAPI_AUTO_LENGTH, LocaleConstructor, nullptr,
sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
if (status != napi_ok) {
LOGE("Define class failed when InitLocale");
return nullptr;
}
status = napi_set_named_property(env, exports, "Locale", constructor);
if (status != napi_ok) {
LOGE("Set property failed when InitLocale");
return nullptr;
}
g_constructor = new (std::nothrow) napi_ref;
if (!g_constructor) {
LOGE("Failed to create ref at init");
return nullptr;
}
status = napi_create_reference(env, constructor, 1, g_constructor);
if (status != napi_ok) {
LOGE("Failed to create reference at init");
return nullptr;
}
return exports;
}
napi_value IntlAddon::InitDateTimeFormat(napi_env env, napi_value exports)
{
napi_status status = napi_ok;
napi_property_descriptor properties[] = {
DECLARE_NAPI_FUNCTION("format", FormatDateTime),
DECLARE_NAPI_FUNCTION("formatRange", FormatDateTimeRange),
DECLARE_NAPI_FUNCTION("resolvedOptions", GetDateTimeResolvedOptions)
};
napi_value constructor = nullptr;
status = napi_define_class(env, "DateTimeFormat", NAPI_AUTO_LENGTH, DateTimeFormatConstructor, nullptr,
sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
if (status != napi_ok) {
LOGE("Define class failed when InitDateTimeFormat");
return nullptr;
}
status = napi_set_named_property(env, exports, "DateTimeFormat", constructor);
if (status != napi_ok) {
LOGE("Set property failed when InitDateTimeFormat");
return nullptr;
}
return exports;
}
napi_value IntlAddon::InitRelativeTimeFormat(napi_env env, napi_value exports)
{
napi_status status = napi_ok;
napi_property_descriptor properties[] = {
DECLARE_NAPI_FUNCTION("format", FormatRelativeTime),
DECLARE_NAPI_FUNCTION("formatToParts", FormatToParts),
DECLARE_NAPI_FUNCTION("resolvedOptions", GetRelativeTimeResolvedOptions)
};
napi_value constructor = nullptr;
status = napi_define_class(env, "RelativeTimeFormat", NAPI_AUTO_LENGTH, RelativeTimeFormatConstructor, nullptr,
sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
if (status != napi_ok) {
LOGE("Define class failed when InitRelativeTimeFormat");
return nullptr;
}
status = napi_set_named_property(env, exports, "RelativeTimeFormat", constructor);
if (status != napi_ok) {
LOGE("Set property failed when InitRelativeTimeFormat");
return nullptr;
}
return exports;
}
napi_value IntlAddon::InitNumberFormat(napi_env env, napi_value exports)
{
napi_status status = napi_ok;
napi_property_descriptor properties[] = {
DECLARE_NAPI_FUNCTION("format", FormatNumber),
DECLARE_NAPI_FUNCTION("resolvedOptions", GetNumberResolvedOptions)
};
napi_value constructor = nullptr;
status = napi_define_class(env, "NumberFormat", NAPI_AUTO_LENGTH, NumberFormatConstructor, nullptr,
sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
if (status != napi_ok) {
LOGE("Define class failed when InitNumberFormat");
return nullptr;
}
status = napi_set_named_property(env, exports, "NumberFormat", constructor);
if (status != napi_ok) {
LOGE("Set property failed when InitNumberFormat");
return nullptr;
}
return exports;
}
void GetOptionValue(napi_env env, napi_value options, const std::string &optionName,
std::map<std::string, std::string> &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) {
LOGE("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 = 0;
napi_get_value_string_utf8(env, optionValue, nullptr, 0, &len);
std::vector<char> optionBuf(len + 1);
status = napi_get_value_string_utf8(env, optionValue, optionBuf.data(), len + 1, &len);
if (status != napi_ok) {
return;
}
map.insert(make_pair(optionName, optionBuf.data()));
}
}
}
void GetIntegerOptionValue(napi_env env, napi_value options, const std::string &optionName,
std::map<std::string, std::string> &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) {
LOGE("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<std::string, std::string> &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) {
LOGE("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<std::string, std::string> &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);
}
void GetRelativeTimeOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)
{
GetOptionValue(env, options, "localeMatcher", map);
GetOptionValue(env, options, "numeric", map);
GetOptionValue(env, options, "style", map);
}
std::string GetLocaleTag(napi_env env, napi_value argv)
{
std::string localeTag = "";
std::vector<char> buf;
if (argv != nullptr) {
napi_valuetype valueType = napi_valuetype::napi_undefined;
napi_typeof(env, argv, &valueType);
if (valueType != napi_valuetype::napi_string) {
return "";
}
size_t len = 0;
napi_status status = napi_get_value_string_utf8(env, argv, nullptr, 0, &len);
if (status != napi_ok) {
LOGE("Get locale tag length failed");
return "";
}
buf.resize(len + 1);
status = napi_get_value_string_utf8(env, argv, buf.data(), len + 1, &len);
if (status != napi_ok) {
LOGE("Get locale tag failed");
return "";
}
localeTag = buf.data();
} else {
localeTag = "";
}
return localeTag;
}
napi_value IntlAddon::LocaleConstructor(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);
if (status != napi_ok) {
return nullptr;
}
std::string localeTag = GetLocaleTag(env, argv[0]);
std::map<std::string, std::string> map = {};
if (argc > 1) {
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<IntlAddon> obj = nullptr;
obj = std::make_unique<IntlAddon>();
status =
napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
if (status != napi_ok) {
LOGE("Wrap IntlAddon failed");
return nullptr;
}
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<std::string, std::string> &map)
{
napi_value global = nullptr;
napi_status status = napi_get_global(env, &global);
if (status != napi_ok) {
LOGE("Get global failed");
return false;
}
env_ = env;
locale_ = std::make_unique<LocaleInfo>(localeTag, map);
return locale_ != nullptr;
}
void GetLocaleTags(napi_env env, napi_value rawLocaleTag, std::vector<std::string> &localeTags)
{
size_t len = 0;
napi_status status = napi_get_value_string_utf8(env, rawLocaleTag, nullptr, 0, &len);
if (status != napi_ok) {
LOGE("Get locale tag length failed");
return;
}
std::vector<char> buf(len + 1);
status = napi_get_value_string_utf8(env, rawLocaleTag, buf.data(), len + 1, &len);
if (status != napi_ok) {
LOGE("Get locale tag failed");
return;
}
localeTags.push_back(buf.data());
}
napi_value IntlAddon::DateTimeFormatConstructor(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);
if (status != napi_ok) {
return nullptr;
}
std::vector<std::string> localeTags;
if (argc > 0) {
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 = nullptr;
for (uint32_t i = 0; i < arrayLength; i++) {
napi_get_element(env, argv[0], i, &element);
GetLocaleTags(env, element, localeTags);
}
}
}
std::map<std::string, std::string> map = {};
if (argc > 1) {
GetDateOptionValues(env, argv[1], map);
}
std::unique_ptr<IntlAddon> obj = nullptr;
obj = std::make_unique<IntlAddon>();
status =
napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
if (status != napi_ok) {
LOGE("Wrap IntlAddon failed");
return nullptr;
}
if (!obj->InitDateTimeFormatContext(env, info, localeTags, map)) {
LOGE("Init DateTimeFormat failed");
return nullptr;
}
obj.release();
return thisVar;
}
bool IntlAddon::InitDateTimeFormatContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
std::map<std::string, std::string> &map)
{
napi_value global = nullptr;
napi_status status = napi_get_global(env, &global);
if (status != napi_ok) {
LOGE("Get global failed");
return false;
}
env_ = env;
datefmt_ = DateTimeFormat::CreateInstance(localeTags, map);
return datefmt_ != nullptr;
}
napi_value IntlAddon::RelativeTimeFormatConstructor(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);
if (status != napi_ok) {
return nullptr;
}
std::vector<std::string> localeTags;
if (argc > 0) {
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 = nullptr;
for (uint32_t i = 0; i < arrayLength; i++) {
napi_get_element(env, argv[0], i, &element);
GetLocaleTags(env, element, localeTags);
}
}
}
std::map<std::string, std::string> map = {};
if (argc > 1) {
GetRelativeTimeOptionValues(env, argv[1], map);
}
std::unique_ptr<IntlAddon> obj = nullptr;
obj = std::make_unique<IntlAddon>();
status =
napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
if (status != napi_ok) {
LOGE("Wrap IntlAddon failed");
return nullptr;
}
if (!obj->InitRelativeTimeFormatContext(env, info, localeTags, map)) {
LOGE("Init RelativeTimeFormat failed");
return nullptr;
}
obj.release();
return thisVar;
}
bool IntlAddon::InitRelativeTimeFormatContext(napi_env env, napi_callback_info info,
std::vector<std::string> localeTags, std::map<std::string, std::string> &map)
{
env_ = env;
relativetimefmt_ = std::make_unique<RelativeTimeFormat>(localeTags, map);
return relativetimefmt_ != 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 milliseconds = GetMilliseconds(env, argv, 0);
if (milliseconds == -1) {
return nullptr;
}
IntlAddon *obj = nullptr;
napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
if (status != napi_ok || !obj || !obj->datefmt_) {
LOGE("Get DateTimeFormat object failed");
return nullptr;
}
std::string value = obj->datefmt_->Format(milliseconds);
napi_value result = nullptr;
status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
if (status != napi_ok) {
LOGE("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);
if (argc < 2) {
LOGE("Parameter wrong");
return nullptr;
}
int64_t firstMilliseconds = GetMilliseconds(env, argv, 0);
int64_t secondMilliseconds = GetMilliseconds(env, argv, 1);
if (firstMilliseconds == -1 || secondMilliseconds == -1) {
return nullptr;
}
IntlAddon *obj = nullptr;
napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
if (status != napi_ok || !obj || !obj->datefmt_) {
LOGE("Get DateTimeFormat object failed");
return nullptr;
}
std::string value = obj->datefmt_->FormatRange(firstMilliseconds, secondMilliseconds);
napi_value result = nullptr;
status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
if (status != napi_ok) {
LOGE("Create format string failed");
return nullptr;
}
return result;
}
void GetNumberOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &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);
GetOptionValue(env, options, "unitUsage", 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)
{
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);
if (status != napi_ok) {
return nullptr;
}
std::vector<std::string> localeTags;
if (argc > 0) {
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 = nullptr;
for (uint32_t i = 0; i < arrayLength; i++) {
napi_get_element(env, argv[0], i, &element);
GetLocaleTags(env, element, localeTags);
}
}
}
std::map<std::string, std::string> map = {};
if (argc > 1) {
GetNumberOptionValues(env, argv[1], map);
}
std::unique_ptr<IntlAddon> obj = nullptr;
obj = std::make_unique<IntlAddon>();
status =
napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
if (status != napi_ok) {
LOGE("Wrap IntlAddon failed");
return nullptr;
}
if (!obj->InitNumberFormatContext(env, info, localeTags, map)) {
LOGE("Init NumberFormat failed");
return nullptr;
}
obj.release();
return thisVar;
}
bool IntlAddon::InitNumberFormatContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
std::map<std::string, std::string> &map)
{
napi_value global = nullptr;
napi_status status = napi_get_global(env, &global);
if (status != napi_ok) {
LOGE("Get global failed");
return false;
}
env_ = env;
numberfmt_ = std::make_unique<NumberFormat>(localeTags, map);
return numberfmt_ != nullptr;
}
int64_t IntlAddon::GetMilliseconds(napi_env env, napi_value *argv, int index)
{
napi_value funcGetDateInfo = nullptr;
napi_status status = napi_get_named_property(env, argv[index], "getTime", &funcGetDateInfo);
if (status != napi_ok) {
LOGE("Get Milliseconds property failed");
return -1;
}
napi_value ret_value = nullptr;
status = napi_call_function(env, argv[index], funcGetDateInfo, 0, nullptr, &ret_value);
if (status != napi_ok) {
LOGE("Get Milliseconds function failed");
return -1;
}
int64_t milliseconds = 0;
status = napi_get_value_int64(env, ret_value, &milliseconds);
if (status != napi_ok) {
LOGE("Get Milliseconds failed");
return -1;
}
return milliseconds;
}
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<void **>(&obj));
if (status != napi_ok || !obj || !obj->locale_) {
LOGE("Get Locale object failed");
return nullptr;
}
std::string value = obj->locale_->GetLanguage();
napi_value result = nullptr;
status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
if (status != napi_ok) {
LOGE("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<void **>(&obj));
if (status != napi_ok || !obj || !obj->locale_) {
LOGE("Get Locale object failed");
return nullptr;
}
std::string value = obj->locale_->GetScript();
napi_value result = nullptr;
status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
if (status != napi_ok) {
LOGE("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<void **>(&obj));
if (status != napi_ok || !obj || !obj->locale_) {
LOGE("Get Locale object failed");
return nullptr;
}
std::string value = obj->locale_->GetRegion();
napi_value result = nullptr;
status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
if (status != napi_ok) {
LOGE("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<void **>(&obj));
if (status != napi_ok || !obj || !obj->locale_) {
LOGE("Get Locale object failed");
return nullptr;
}
std::string value = obj->locale_->GetBaseName();
napi_value result = nullptr;
status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
if (status != napi_ok) {
LOGE("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<void **>(&obj));
if (status != napi_ok || !obj || !obj->locale_) {
LOGE("Get Locale object failed");
return nullptr;
}
std::string value = obj->locale_->GetCalendar();
napi_value result = nullptr;
status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
if (status != napi_ok) {
LOGE("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<void **>(&obj));
if (status != napi_ok || !obj || !obj->locale_) {
LOGE("Get Locale object failed");
return nullptr;
}
std::string value = obj->locale_->GetCollation();
napi_value result = nullptr;
status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
if (status != napi_ok) {
LOGE("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<void **>(&obj));
if (status != napi_ok || !obj || !obj->locale_) {
LOGE("Get Locale object failed");
return nullptr;
}
std::string value = obj->locale_->GetHourCycle();
napi_value result = nullptr;
status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
if (status != napi_ok) {
LOGE("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<void **>(&obj));
if (status != napi_ok || !obj || !obj->locale_) {
LOGE("Get Locale object failed");
return nullptr;
}
std::string value = obj->locale_->GetNumberingSystem();
napi_value result = nullptr;
status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
if (status != napi_ok) {
LOGE("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<void **>(&obj));
if (status != napi_ok || !obj || !obj->locale_) {
LOGE("Get Locale object failed");
return nullptr;
}
std::string value = obj->locale_->GetNumeric();
bool optionBoolValue = (value == "true");
napi_value result = nullptr;
status = napi_get_boolean(env, optionBoolValue, &result);
if (status != napi_ok) {
LOGE("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<void **>(&obj));
if (status != napi_ok || !obj || !obj->locale_) {
LOGE("Get Locale object failed");
return nullptr;
}
std::string value = obj->locale_->GetCaseFirst();
napi_value result = nullptr;
status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
if (status != napi_ok) {
LOGE("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<void **>(&obj));
if (status != napi_ok || !obj || !obj->locale_) {
LOGE("Get Locale object failed");
return nullptr;
}
std::string value = obj->locale_->ToString();
napi_value result = nullptr;
status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
if (status != napi_ok) {
LOGE("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<void **>(&obj));
if (status != napi_ok || !obj || !obj->locale_) {
LOGE("Get Locale object failed");
return nullptr;
}
std::string localeTag = obj->locale_->Maximize();
napi_value constructor = nullptr;
status = napi_get_reference_value(env, *g_constructor, &constructor);
if (status != napi_ok) {
LOGE("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) {
LOGE("Create localeTag string failed");
return nullptr;
}
status = napi_new_instance(env, constructor, 1, &arg, &result);
if (status != napi_ok) {
LOGE("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<void **>(&obj));
if (status != napi_ok || !obj || !obj->locale_) {
LOGE("Get Locale object failed");
return nullptr;
}
std::string localeTag = obj->locale_->Minimize();
napi_value constructor = nullptr;
status = napi_get_reference_value(env, *g_constructor, &constructor);
if (status != napi_ok) {
LOGE("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) {
LOGE("Create localeTag string failed");
return nullptr;
}
status = napi_new_instance(env, constructor, 1, &arg, &result);
if (status != napi_ok) {
LOGE("Create new locale instance failed");
return nullptr;
}
return result;
}
void SetOptionProperties(napi_env env, napi_value &result, std::map<std::string, std::string> &options,
const std::string &option)
{
if (options.count(option) > 0) {
std::string optionValue = options[option];
napi_value optionJsValue = nullptr;
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 = nullptr;
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<std::string, std::string> &options,
const std::string &option)
{
if (options.count(option) > 0) {
std::string optionValue = options[option];
napi_value optionJsValue = nullptr;
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 = nullptr;
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<std::string, std::string> &options,
const std::string &option)
{
if (options.count(option) > 0) {
std::string optionValue = options[option];
bool optionBoolValue = (optionValue == "true");
napi_value optionJsValue = nullptr;
napi_get_boolean(env, optionBoolValue, &optionJsValue);
napi_set_named_property(env, result, option.c_str(), optionJsValue);
} else {
napi_value undefined = nullptr;
napi_get_undefined(env, &undefined);
napi_set_named_property(env, result, option.c_str(), undefined);
}
}
napi_value IntlAddon::GetRelativeTimeResolvedOptions(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<void **>(&obj));
if (status != napi_ok || !obj || !obj->relativetimefmt_) {
LOGE("Get RelativeTimeFormat object failed");
return nullptr;
}
napi_value result = nullptr;
napi_create_object(env, &result);
std::map<std::string, std::string> options = {};
obj->relativetimefmt_->GetResolvedOptions(options);
SetOptionProperties(env, result, options, "locale");
SetOptionProperties(env, result, options, "style");
SetOptionProperties(env, result, options, "numeric");
SetOptionProperties(env, result, options, "numberingSystem");
return result;
}
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<void **>(&obj));
if (status != napi_ok || !obj || !obj->datefmt_) {
LOGE("Get DateTimeFormat object failed");
return nullptr;
}
napi_value result = nullptr;
napi_create_object(env, &result);
std::map<std::string, std::string> 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<void **>(&obj));
if (status != napi_ok || !obj || !obj->numberfmt_) {
LOGE("Get NumberFormat object failed");
return nullptr;
}
napi_value result = nullptr;
napi_create_object(env, &result);
std::map<std::string, std::string> 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");
SetOptionProperties(env, result, options, "unitUsage");
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 = 0;
napi_get_value_double(env, argv[0], &number);
IntlAddon *obj = nullptr;
napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
if (status != napi_ok || !obj || !obj->numberfmt_) {
LOGE("Get NumberFormat object failed");
return nullptr;
}
std::string value = obj->numberfmt_->Format(number);
napi_value result = nullptr;
status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
if (status != napi_ok) {
LOGE("Create format string failed");
return nullptr;
}
return result;
}
void GetCollatorLocaleMatcher(napi_env env, napi_value options, std::map<std::string, std::string> &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") {
LOGE("invalid localeMatcher");
return;
}
} else {
map.insert(std::make_pair("localeMatcher", "best fit"));
}
}
void GetCollatorUsage(napi_env env, napi_value options, std::map<std::string, std::string> &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") {
LOGE("invalid usage");
return;
}
} else {
map.insert(std::make_pair("usage", "sort"));
}
}
void GetCollatorSensitivity(napi_env env, napi_value options, std::map<std::string, std::string> &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") {
LOGE("invalid sensitivity");
return;
}
} else {
map.insert(std::make_pair("sensitivity", "variant"));
}
}
void GetCollatorIgnorePunctuation(napi_env env, napi_value options, std::map<std::string, std::string> &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") {
LOGE("invalid ignorePunctuation");
return;
}
} else {
map.insert(std::make_pair("ignorePunctuation", "false"));
}
}
void GetCollatorNumeric(napi_env env, napi_value options, std::map<std::string, std::string> &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") {
LOGE("invalid numeric");
return;
}
}
}
void GetCollatorCaseFirst(napi_env env, napi_value options, std::map<std::string, std::string> &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") {
LOGE("invalid caseFirst");
return;
}
}
}
void GetCollatorCollation(napi_env env, napi_value options, std::map<std::string, std::string> &map)
{
GetOptionValue(env, options, "collation", map);
auto it = map.find("collation");
if (it != map.end()) {
std::string collation = it->second;
std::set<std::string> validCollation;
validCollation.insert("big5han");
validCollation.insert("compat");
validCollation.insert("dict");
validCollation.insert("direct");
validCollation.insert("ducet");
validCollation.insert("eor");
validCollation.insert("gb2312");
validCollation.insert("phonebk");
validCollation.insert("phonetic");
validCollation.insert("pinyin");
validCollation.insert("reformed");
validCollation.insert("searchjl");
validCollation.insert("stroke");
validCollation.insert("trad");
validCollation.insert("unihan");
validCollation.insert("zhuyin");
if (validCollation.find(collation) == validCollation.end()) {
map["collation"] = "default";
}
}
}
void GetCollatorOptionValue(napi_env env, napi_value options, std::map<std::string, std::string> &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) {
LOGE("Define class failed when InitCollator");
return nullptr;
}
status = napi_set_named_property(env, exports, "Collator", constructor);
if (status != napi_ok) {
LOGE("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);
if (status != napi_ok) {
return nullptr;
}
std::vector<std::string> localeTags;
if (argc > 0) {
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 = nullptr;
for (uint32_t i = 0; i < arrayLength; i++) {
napi_get_element(env, argv[0], i, &element);
GetLocaleTags(env, element, localeTags);
}
}
}
std::map<std::string, std::string> map = {};
if (argc > 1) {
GetCollatorOptionValue(env, argv[1], map);
}
std::unique_ptr<IntlAddon> obj = nullptr;
obj = std::make_unique<IntlAddon>();
status =
napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
if (status != napi_ok) {
LOGE("Wrap IntlAddon failed");
return nullptr;
}
if (!obj->InitCollatorContext(env, info, localeTags, map)) {
LOGE("Init DateTimeFormat failed");
return nullptr;
}
obj.release();
return thisVar;
}
bool IntlAddon::InitCollatorContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
std::map<std::string, std::string> &map)
{
napi_value global = nullptr;
napi_status status = napi_get_global(env, &global);
if (status != napi_ok) {
LOGE("Get global failed");
return false;
}
env_ = env;
collator_ = std::make_unique<Collator>(localeTags, map);
return collator_ != nullptr;
}
bool GetStringParameter(napi_env env, napi_value value, std::vector<char> &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 = 0;
napi_status status = napi_get_value_string_utf8(env, value, nullptr, 0, &len);
if (status != napi_ok) {
LOGE("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) {
LOGE("Get first failed");
return false;
}
return true;
}
napi_value IntlAddon::FormatRelativeTime(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);
napi_status status;
double number;
status = napi_get_value_double(env, argv[0], &number);
if (status != napi_ok) {
LOGE("Get number failed");
return nullptr;
}
std::vector<char> unit;
if (!GetStringParameter(env, argv[1], unit)) {
return nullptr;
}
IntlAddon *obj = nullptr;
status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
if (status != napi_ok || !obj || !obj->relativetimefmt_) {
LOGE("Get RelativeTimeFormat object failed");
return nullptr;
}
std::string value = obj->relativetimefmt_->Format(number, unit.data());
napi_value result = nullptr;
status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
if (status != napi_ok) {
LOGE("Create format string failed");
return nullptr;
}
return result;
}
void IntlAddon::FillInArrayElement(napi_env env, napi_value &result, napi_status &status,
const std::vector<std::vector<std::string>> &timeVector)
{
for (size_t i = 0; i < timeVector.size(); i++) {
napi_value value = nullptr;
status = napi_create_string_utf8(env, timeVector[i][1].c_str(), NAPI_AUTO_LENGTH, &value);
if (status != napi_ok) {
LOGE("Failed to create string item.");
return;
}
napi_value type = nullptr;
status = napi_create_string_utf8(env, timeVector[i][0].c_str(), NAPI_AUTO_LENGTH, &type);
if (status != napi_ok) {
LOGE("Failed to create string item.");
return;
}
napi_value unit = nullptr;
size_t unitIndex = 2;
if (timeVector[i].size() > unitIndex) {
status = napi_create_string_utf8(env, timeVector[i][unitIndex].c_str(), NAPI_AUTO_LENGTH, &unit);
if (status != napi_ok) {
LOGE("Failed to create string item.");
return;
}
} else {
napi_get_undefined(env, &unit);
}
napi_value formatInfo;
status = napi_create_object(env, &formatInfo);
if (status != napi_ok) {
LOGE("Failed to create format info object.");
return;
}
napi_set_named_property(env, formatInfo, "type", type);
napi_set_named_property(env, formatInfo, "value", value);
napi_set_named_property(env, formatInfo, "unit", unit);
status = napi_set_element(env, result, i, formatInfo);
if (status != napi_ok) {
LOGE("Failed to set array item");
return;
}
}
}
napi_value IntlAddon::FormatToParts(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);
double number = 0;
napi_get_value_double(env, argv[0], &number);
std::vector<char> unit;
if (!GetStringParameter(env, argv[1], unit)) {
return nullptr;
}
IntlAddon *obj = nullptr;
napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
if (status != napi_ok || !obj || !obj->relativetimefmt_) {
LOGE("Get RelativeTimeFormat object failed");
return nullptr;
}
std::vector<std::vector<std::string>> timeVector;
obj->relativetimefmt_->FormatToParts(number, unit.data(), timeVector);
napi_value result = nullptr;
status = napi_create_array_with_length(env, timeVector.size(), &result);
if (status != napi_ok) {
LOGE("Failed to create array");
return nullptr;
}
FillInArrayElement(env, result, status, timeVector);
return result;
}
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<char> first;
if (!GetStringParameter(env, argv[0], first)) {
return nullptr;
}
std::vector<char> second;
if (!GetStringParameter(env, argv[1], second)) {
return nullptr;
}
IntlAddon *obj = nullptr;
napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
if (status != napi_ok || !obj || !obj->collator_) {
LOGE("Get Collator object failed");
return nullptr;
}
CompareResult compareResult = obj->collator_->Compare(first.data(), second.data());
napi_value result = nullptr;
status = napi_create_int32(env, compareResult, &result);
if (status != napi_ok) {
LOGE("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<void **>(&obj));
if (status != napi_ok || !obj || !obj->collator_) {
LOGE("Get Collator object failed");
return nullptr;
}
napi_value result = nullptr;
napi_create_object(env, &result);
std::map<std::string, std::string> options = {};
obj->collator_->ResolvedOptions(options);
SetOptionProperties(env, result, options, "localeMatcher");
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<std::string, std::string> &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") {
LOGE("invalid type");
return;
}
} else {
map.insert(std::make_pair("type", "cardinal"));
}
}
void GetPluralRulesInteger(napi_env env, napi_value options, std::map<std::string, std::string> &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) {
LOGE("invalid minimumIntegerDigits");
return;
}
} else {
map.insert(std::make_pair("minimumIntegerDigits", std::to_string(1)));
}
}
void GetPluralRulesFractions(napi_env env, napi_value options, std::map<std::string, std::string> &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) {
LOGE("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) {
LOGE("invalid maximumFractionDigits");
return;
}
}
}
void GetPluralRulesSignificant(napi_env env, napi_value options, std::map<std::string, std::string> &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);
if (minSignificantInt < 1 || minSignificantInt > 21) {
LOGE("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);
if (maxSignificant < minSignificant || maxSignificant > 21) {
LOGE("invalid maximumSignificantDigits");
return;
}
}
}
void GetPluralRulesOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &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 = nullptr;
status = napi_define_class(env, "PluralRules", NAPI_AUTO_LENGTH, PluralRulesConstructor, nullptr,
sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
if (status != napi_ok) {
LOGE("Define class failed when InitPluralRules");
return nullptr;
}
status = napi_set_named_property(env, exports, "PluralRules", constructor);
if (status != napi_ok) {
LOGE("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);
if (status != napi_ok) {
return nullptr;
}
napi_valuetype valueType = napi_valuetype::napi_undefined;
std::vector<std::string> localeTags;
if (argc > 0) {
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 = nullptr;
for (uint32_t i = 0; i < arrayLength; i++) {
napi_get_element(env, argv[0], i, &element);
GetLocaleTags(env, element, localeTags);
}
}
}
std::map<std::string, std::string> map = {};
if (argc > 1) {
GetPluralRulesOptionValues(env, argv[1], map);
}
std::unique_ptr<IntlAddon> obj = nullptr;
obj = std::make_unique<IntlAddon>();
status =
napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
if (status != napi_ok) {
LOGE("Wrap IntlAddon failed");
return nullptr;
}
if (!obj->InitPluralRulesContext(env, info, localeTags, map)) {
LOGE("Init DateTimeFormat failed");
return nullptr;
}
obj.release();
return thisVar;
}
bool IntlAddon::InitPluralRulesContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
std::map<std::string, std::string> &map)
{
napi_value global = nullptr;
napi_status status = napi_get_global(env, &global);
if (status != napi_ok) {
LOGE("Get global failed");
return false;
}
env_ = env;
pluralrules_ = std::make_unique<PluralRules>(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 = 0;
napi_status status = napi_get_value_double(env, argv[0], &number);
if (status != napi_ok) {
LOGE("Get number failed");
return nullptr;
}
IntlAddon *obj = nullptr;
status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
if (status != napi_ok || !obj || !obj->pluralrules_) {
LOGE("Get PluralRules object failed");
return nullptr;
}
std::string res = obj->pluralrules_->Select(number);
napi_value result = nullptr;
status = napi_create_string_utf8(env, res.c_str(), NAPI_AUTO_LENGTH, &result);
if (status != napi_ok) {
LOGE("get select result failed");
return nullptr;
}
return result;
}
bool IntlAddon::IsLocaleInfo(napi_env env, napi_value argv)
{
napi_value constructor = nullptr;
if (g_constructor == nullptr) {
LOGE("IntlAddon::IsLocaleInfo: g_constructor is nullptr.");
return false;
}
napi_status status = napi_get_reference_value(env, *g_constructor, &constructor);
if (status != napi_ok || constructor == nullptr) {
LOGE("IntlAddon::IsLocaleInfo: Failed to create reference.");
return false;
}
bool isLocaleInfo = false;
status = napi_instanceof(env, argv, constructor, &isLocaleInfo);
if (status != napi_ok) {
LOGE("IntlAddon::IsLocaleInfo: Failed to get instance of argv.");
return false;
}
return isLocaleInfo;
}
std::shared_ptr<LocaleInfo> IntlAddon::GetLocaleInfo()
{
if (!this->locale_) {
return nullptr;
}
return this->locale_;
}
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);
val = IntlAddon::InitRelativeTimeFormat(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 = nullptr,
.reserved = { 0 }
};
#ifdef ANDROID_PLATFORM
static void INTLPluginJniRegister()
{
const char className[] = "ohos.ace.plugin.intlplugin.INTLPlugin";
ARKUI_X_Plugin_RegisterJavaPlugin(&Plugin::INTLPluginJni::Register, className);
}
#endif
extern "C" __attribute__((constructor)) void INTLPluginRegister()
{
#ifdef ANDROID_PLATFORM
INTLPluginJniRegister();
#else
InitIcuData();
#endif
napi_module_register(&g_intlModule);
}
}
}
}