1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "intl_addon.h"
17
18 #include <vector>
19 #include <set>
20 #include "error_util.h"
21 #include "i18n_hilog.h"
22 #include "js_utils.h"
23 #include "node_api.h"
24 #include "utils.h"
25 #include "locale_info_addon.h"
26 #include "number_format_addon.h"
27
28 namespace OHOS {
29 namespace Global {
30 namespace I18n {
31
IntlAddon()32 IntlAddon::IntlAddon() : env_(nullptr) {}
33
~IntlAddon()34 IntlAddon::~IntlAddon()
35 {
36 }
37
Destructor(napi_env env,void * nativeObject,void * hint)38 void IntlAddon::Destructor(napi_env env, void *nativeObject, void *hint)
39 {
40 if (!nativeObject) {
41 return;
42 }
43 delete reinterpret_cast<IntlAddon *>(nativeObject);
44 nativeObject = nullptr;
45 }
46
InitDateTimeFormat(napi_env env,napi_value exports)47 napi_value IntlAddon::InitDateTimeFormat(napi_env env, napi_value exports)
48 {
49 napi_status status = napi_ok;
50 napi_property_descriptor properties[] = {
51 DECLARE_NAPI_FUNCTION("format", FormatDateTime),
52 DECLARE_NAPI_FUNCTION("formatRange", FormatDateTimeRange),
53 DECLARE_NAPI_FUNCTION("resolvedOptions", GetDateTimeResolvedOptions)
54 };
55
56 napi_value constructor = nullptr;
57 status = napi_define_class(env, "DateTimeFormat", NAPI_AUTO_LENGTH, DateTimeFormatConstructor, nullptr,
58 sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
59 if (status != napi_ok) {
60 HILOG_ERROR_I18N("Define class failed when InitDateTimeFormat");
61 return nullptr;
62 }
63
64 status = napi_set_named_property(env, exports, "DateTimeFormat", constructor);
65 if (status != napi_ok) {
66 HILOG_ERROR_I18N("Set property failed when InitDateTimeFormat");
67 return nullptr;
68 }
69 return exports;
70 }
71
InitRelativeTimeFormat(napi_env env,napi_value exports)72 napi_value IntlAddon::InitRelativeTimeFormat(napi_env env, napi_value exports)
73 {
74 napi_status status = napi_ok;
75 napi_property_descriptor properties[] = {
76 DECLARE_NAPI_FUNCTION("format", FormatRelativeTime),
77 DECLARE_NAPI_FUNCTION("formatToParts", FormatToParts),
78 DECLARE_NAPI_FUNCTION("resolvedOptions", GetRelativeTimeResolvedOptions)
79 };
80
81 napi_value constructor = nullptr;
82 status = napi_define_class(env, "RelativeTimeFormat", NAPI_AUTO_LENGTH, RelativeTimeFormatConstructor, nullptr,
83 sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
84 if (status != napi_ok) {
85 HILOG_ERROR_I18N("Define class failed when InitRelativeTimeFormat");
86 return nullptr;
87 }
88
89 status = napi_set_named_property(env, exports, "RelativeTimeFormat", constructor);
90 if (status != napi_ok) {
91 HILOG_ERROR_I18N("Set property failed when InitRelativeTimeFormat");
92 return nullptr;
93 }
94 return exports;
95 }
96
GetDateOptionValues(napi_env env,napi_value options,std::map<std::string,std::string> & map)97 void GetDateOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)
98 {
99 JSUtils::GetOptionValue(env, options, "calendar", map);
100 JSUtils::GetOptionValue(env, options, "dateStyle", map);
101 JSUtils::GetOptionValue(env, options, "timeStyle", map);
102 JSUtils::GetOptionValue(env, options, "hourCycle", map);
103 JSUtils::GetOptionValue(env, options, "timeZone", map);
104 JSUtils::GetOptionValue(env, options, "timeZoneName", map);
105 JSUtils::GetOptionValue(env, options, "numberingSystem", map);
106 JSUtils::GetBoolOptionValue(env, options, "hour12", map);
107 JSUtils::GetOptionValue(env, options, "weekday", map);
108 JSUtils::GetOptionValue(env, options, "era", map);
109 JSUtils::GetOptionValue(env, options, "year", map);
110 JSUtils::GetOptionValue(env, options, "month", map);
111 JSUtils::GetOptionValue(env, options, "day", map);
112 JSUtils::GetOptionValue(env, options, "hour", map);
113 JSUtils::GetOptionValue(env, options, "minute", map);
114 JSUtils::GetOptionValue(env, options, "second", map);
115 JSUtils::GetOptionValue(env, options, "localeMatcher", map);
116 JSUtils::GetOptionValue(env, options, "formatMatcher", map);
117 JSUtils::GetOptionValue(env, options, "dayPeriod", map);
118 }
119
GetRelativeTimeOptionValues(napi_env env,napi_value options,std::map<std::string,std::string> & map)120 void GetRelativeTimeOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)
121 {
122 JSUtils::GetOptionValue(env, options, "localeMatcher", map);
123 JSUtils::GetOptionValue(env, options, "numeric", map);
124 JSUtils::GetOptionValue(env, options, "style", map);
125 }
126
GetLocaleTag(napi_env env,napi_value argv)127 std::string GetLocaleTag(napi_env env, napi_value argv)
128 {
129 std::string localeTag = "";
130 std::vector<char> buf;
131 if (argv != nullptr) {
132 napi_valuetype valueType = napi_valuetype::napi_undefined;
133 napi_typeof(env, argv, &valueType);
134 if (valueType != napi_valuetype::napi_string) {
135 HILOG_ERROR_I18N("GetLocaleTag: Parameter type does not match");
136 return "";
137 }
138 size_t len = 0;
139 napi_status status = napi_get_value_string_utf8(env, argv, nullptr, 0, &len);
140 if (status != napi_ok) {
141 HILOG_ERROR_I18N("GetLocaleTag -> string: Get locale tag length failed");
142 return "";
143 }
144 buf.resize(len + 1);
145 status = napi_get_value_string_utf8(env, argv, buf.data(), len + 1, &len);
146 if (status != napi_ok) {
147 HILOG_ERROR_I18N("GetLocaleTag: Get locale tag failed");
148 return "";
149 }
150 localeTag = buf.data();
151 } else {
152 localeTag = "";
153 }
154 return localeTag;
155 }
156
DateTimeFormatConstructor(napi_env env,napi_callback_info info)157 napi_value IntlAddon::DateTimeFormatConstructor(napi_env env, napi_callback_info info)
158 {
159 size_t argc = 2;
160 napi_value argv[2] = { nullptr };
161 napi_value thisVar = nullptr;
162 void *data = nullptr;
163 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
164 if (status != napi_ok) {
165 return nullptr;
166 }
167 std::vector<std::string> localeTags;
168 if (argc > 0) {
169 napi_valuetype valueType = napi_valuetype::napi_undefined;
170 napi_typeof(env, argv[0], &valueType);
171 bool isArray = false;
172 napi_is_array(env, argv[0], &isArray);
173 if (valueType == napi_valuetype::napi_string) {
174 JSUtils::GetLocaleTags(env, argv[0], localeTags);
175 } else if (isArray) {
176 uint32_t arrayLength = 0;
177 napi_get_array_length(env, argv[0], &arrayLength);
178 napi_value element = nullptr;
179 for (uint32_t i = 0; i < arrayLength; i++) {
180 napi_get_element(env, argv[0], i, &element);
181 JSUtils::GetLocaleTags(env, element, localeTags);
182 }
183 }
184 }
185 std::map<std::string, std::string> map = {};
186 if (argc > 1) {
187 GetDateOptionValues(env, argv[1], map);
188 }
189 std::unique_ptr<IntlAddon> obj = nullptr;
190 obj = std::make_unique<IntlAddon>();
191 status =
192 napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
193 if (status != napi_ok) {
194 HILOG_ERROR_I18N("DateTimeFormatConstructor: Wrap IntlAddon failed");
195 return nullptr;
196 }
197 if (!obj->InitDateTimeFormatContext(env, info, localeTags, map)) {
198 HILOG_ERROR_I18N("DateTimeFormatConstructor: Init DateTimeFormat failed");
199 return nullptr;
200 }
201 obj.release();
202 return thisVar;
203 }
204
InitDateTimeFormatContext(napi_env env,napi_callback_info info,std::vector<std::string> localeTags,std::map<std::string,std::string> & map)205 bool IntlAddon::InitDateTimeFormatContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
206 std::map<std::string, std::string> &map)
207 {
208 napi_value global = nullptr;
209 napi_status status = napi_get_global(env, &global);
210 if (status != napi_ok) {
211 HILOG_ERROR_I18N("InitDateTimeFormatContext: Get global failed");
212 return false;
213 }
214 env_ = env;
215 datefmt_ = DateTimeFormat::CreateInstance(localeTags, map);
216
217 return datefmt_ != nullptr;
218 }
219
RelativeTimeFormatConstructor(napi_env env,napi_callback_info info)220 napi_value IntlAddon::RelativeTimeFormatConstructor(napi_env env, napi_callback_info info)
221 {
222 size_t argc = 2;
223 napi_value argv[2] = { nullptr };
224 napi_value thisVar = nullptr;
225 void *data = nullptr;
226 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
227 if (status != napi_ok) {
228 return nullptr;
229 }
230 std::vector<std::string> localeTags;
231 if (argc > 0) {
232 napi_valuetype valueType = napi_valuetype::napi_undefined;
233 napi_typeof(env, argv[0], &valueType);
234 bool isArray = false;
235 napi_is_array(env, argv[0], &isArray);
236 if (valueType == napi_valuetype::napi_string) {
237 JSUtils::GetLocaleTags(env, argv[0], localeTags);
238 } else if (isArray) {
239 uint32_t arrayLength = 0;
240 napi_get_array_length(env, argv[0], &arrayLength);
241 napi_value element = nullptr;
242 for (uint32_t i = 0; i < arrayLength; i++) {
243 napi_get_element(env, argv[0], i, &element);
244 JSUtils::GetLocaleTags(env, element, localeTags);
245 }
246 }
247 }
248 std::map<std::string, std::string> map = {};
249 if (argc > 1) {
250 GetRelativeTimeOptionValues(env, argv[1], map);
251 }
252 std::unique_ptr<IntlAddon> obj = nullptr;
253 obj = std::make_unique<IntlAddon>();
254 status =
255 napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
256 if (status != napi_ok) {
257 HILOG_ERROR_I18N("RelativeTimeFormatConstructor: Wrap IntlAddon failed");
258 return nullptr;
259 }
260 if (!obj->InitRelativeTimeFormatContext(env, info, localeTags, map)) {
261 HILOG_ERROR_I18N("Init RelativeTimeFormat failed");
262 return nullptr;
263 }
264 obj.release();
265 return thisVar;
266 }
267
InitRelativeTimeFormatContext(napi_env env,napi_callback_info info,std::vector<std::string> localeTags,std::map<std::string,std::string> & map)268 bool IntlAddon::InitRelativeTimeFormatContext(napi_env env, napi_callback_info info,
269 std::vector<std::string> localeTags, std::map<std::string, std::string> &map)
270 {
271 env_ = env;
272 relativetimefmt_ = std::make_unique<RelativeTimeFormat>(localeTags, map);
273
274 return relativetimefmt_ != nullptr;
275 }
276
FormatDateTime(napi_env env,napi_callback_info info)277 napi_value IntlAddon::FormatDateTime(napi_env env, napi_callback_info info)
278 {
279 size_t argc = 1;
280 napi_value argv[1] = { 0 };
281 napi_value thisVar = nullptr;
282 void *data = nullptr;
283 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
284
285 int64_t milliseconds = GetMilliseconds(env, argv, 0);
286 if (milliseconds == -1) {
287 return nullptr;
288 }
289 IntlAddon *obj = nullptr;
290 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
291 if (status != napi_ok || !obj || !obj->datefmt_) {
292 HILOG_ERROR_I18N("FormatDateTime: Get DateTimeFormat object failed");
293 return nullptr;
294 }
295 std::string value = obj->datefmt_->Format(milliseconds);
296 napi_value result = nullptr;
297 status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
298 if (status != napi_ok) {
299 HILOG_ERROR_I18N("FormatDateTime: Create format string failed");
300 return nullptr;
301 }
302 return result;
303 }
304
FormatDateTimeRange(napi_env env,napi_callback_info info)305 napi_value IntlAddon::FormatDateTimeRange(napi_env env, napi_callback_info info)
306 {
307 size_t argc = 2;
308 napi_value argv[2] = { nullptr };
309 napi_value thisVar = nullptr;
310 void *data = nullptr;
311 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
312 if (argc < FUNC_ARGS_COUNT) {
313 HILOG_ERROR_I18N("Parameter wrong");
314 return nullptr;
315 }
316 int64_t firstMilliseconds = GetMilliseconds(env, argv, 0);
317 int64_t secondMilliseconds = GetMilliseconds(env, argv, 1);
318 if (firstMilliseconds == -1 || secondMilliseconds == -1) {
319 return nullptr;
320 }
321 IntlAddon *obj = nullptr;
322 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
323 if (status != napi_ok || !obj || !obj->datefmt_) {
324 HILOG_ERROR_I18N("FormatDateTimeRange: Get DateTimeFormat object failed");
325 return nullptr;
326 }
327 std::string value = obj->datefmt_->FormatRange(firstMilliseconds, secondMilliseconds);
328 napi_value result = nullptr;
329 status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
330 if (status != napi_ok) {
331 HILOG_ERROR_I18N("FormatDateTimeRange: Create format string failed");
332 return nullptr;
333 }
334 return result;
335 }
336
GetMilliseconds(napi_env env,napi_value * argv,int index)337 int64_t IntlAddon::GetMilliseconds(napi_env env, napi_value *argv, int index)
338 {
339 napi_value funcGetDateInfo = nullptr;
340 napi_status status = napi_get_named_property(env, argv[index], "getTime", &funcGetDateInfo);
341 if (status != napi_ok) {
342 HILOG_ERROR_I18N("Get Milliseconds property failed");
343 return -1;
344 }
345 napi_value ret_value = nullptr;
346 status = napi_call_function(env, argv[index], funcGetDateInfo, 0, nullptr, &ret_value);
347 if (status != napi_ok) {
348 HILOG_ERROR_I18N("Get Milliseconds function failed");
349 return -1;
350 }
351 int64_t milliseconds = 0;
352 status = napi_get_value_int64(env, ret_value, &milliseconds);
353 if (status != napi_ok) {
354 HILOG_ERROR_I18N("Get Milliseconds failed");
355 return -1;
356 }
357 return milliseconds;
358 }
359
GetRelativeTimeResolvedOptions(napi_env env,napi_callback_info info)360 napi_value IntlAddon::GetRelativeTimeResolvedOptions(napi_env env, napi_callback_info info)
361 {
362 napi_value thisVar = nullptr;
363 void *data = nullptr;
364 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
365
366 IntlAddon *obj = nullptr;
367 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
368 if (status != napi_ok || !obj || !obj->relativetimefmt_) {
369 HILOG_ERROR_I18N("GetRelativeTimeResolvedOptions: Get RelativeTimeFormat object failed");
370 return nullptr;
371 }
372 napi_value result = nullptr;
373 napi_create_object(env, &result);
374 std::map<std::string, std::string> options = {};
375 obj->relativetimefmt_->GetResolvedOptions(options);
376 JSUtils::SetOptionProperties(env, result, options, "locale");
377 JSUtils::SetOptionProperties(env, result, options, "style");
378 JSUtils::SetOptionProperties(env, result, options, "numeric");
379 JSUtils::SetOptionProperties(env, result, options, "numberingSystem");
380 return result;
381 }
382
GetDateTimeResolvedOptions(napi_env env,napi_callback_info info)383 napi_value IntlAddon::GetDateTimeResolvedOptions(napi_env env, napi_callback_info info)
384 {
385 napi_value thisVar = nullptr;
386 void *data = nullptr;
387 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
388
389 IntlAddon *obj = nullptr;
390 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
391 if (status != napi_ok || !obj || !obj->datefmt_) {
392 HILOG_ERROR_I18N("GetDateTimeResolvedOptions: Get DateTimeFormat object failed");
393 return nullptr;
394 }
395 napi_value result = nullptr;
396 napi_create_object(env, &result);
397 std::map<std::string, std::string> options = {};
398 obj->datefmt_->GetResolvedOptions(options);
399 JSUtils::SetOptionProperties(env, result, options, "locale");
400 JSUtils::SetOptionProperties(env, result, options, "calendar");
401 JSUtils::SetOptionProperties(env, result, options, "dateStyle");
402 JSUtils::SetOptionProperties(env, result, options, "timeStyle");
403 JSUtils::SetOptionProperties(env, result, options, "hourCycle");
404 JSUtils::SetOptionProperties(env, result, options, "timeZone");
405 JSUtils::SetOptionProperties(env, result, options, "timeZoneName");
406 JSUtils::SetOptionProperties(env, result, options, "numberingSystem");
407 JSUtils::SetBooleanOptionProperties(env, result, options, "hour12");
408 JSUtils::SetOptionProperties(env, result, options, "weekday");
409 JSUtils::SetOptionProperties(env, result, options, "era");
410 JSUtils::SetOptionProperties(env, result, options, "year");
411 JSUtils::SetOptionProperties(env, result, options, "month");
412 JSUtils::SetOptionProperties(env, result, options, "day");
413 JSUtils::SetOptionProperties(env, result, options, "hour");
414 JSUtils::SetOptionProperties(env, result, options, "minute");
415 JSUtils::SetOptionProperties(env, result, options, "second");
416 JSUtils::SetOptionProperties(env, result, options, "dayPeriod");
417 JSUtils::SetOptionProperties(env, result, options, "localeMatcher");
418 JSUtils::SetOptionProperties(env, result, options, "formatMatcher");
419 return result;
420 }
421
GetCollatorLocaleMatcher(napi_env env,napi_value options,std::map<std::string,std::string> & map)422 void GetCollatorLocaleMatcher(napi_env env, napi_value options, std::map<std::string, std::string> &map)
423 {
424 JSUtils::GetOptionValue(env, options, "localeMatcher", map);
425 auto it = map.find("localeMatcher");
426 if (it != map.end()) {
427 std::string localeMatcher = it->second;
428 if (localeMatcher != "lookup" && localeMatcher != "best fit") {
429 HILOG_ERROR_I18N("invalid localeMatcher");
430 return;
431 }
432 } else {
433 map.insert(std::make_pair("localeMatcher", "best fit"));
434 }
435 }
436
GetCollatorUsage(napi_env env,napi_value options,std::map<std::string,std::string> & map)437 void GetCollatorUsage(napi_env env, napi_value options, std::map<std::string, std::string> &map)
438 {
439 JSUtils::GetOptionValue(env, options, "usage", map);
440 auto it = map.find("usage");
441 if (it != map.end()) {
442 std::string usage = it->second;
443 if (usage != "sort" && usage != "search") {
444 HILOG_ERROR_I18N("invalid usage");
445 return;
446 }
447 } else {
448 map.insert(std::make_pair("usage", "sort"));
449 }
450 }
451
GetCollatorSensitivity(napi_env env,napi_value options,std::map<std::string,std::string> & map)452 void GetCollatorSensitivity(napi_env env, napi_value options, std::map<std::string, std::string> &map)
453 {
454 JSUtils::GetOptionValue(env, options, "sensitivity", map);
455 auto it = map.find("sensitivity");
456 if (it != map.end()) {
457 std::string sensitivity = it->second;
458 if (sensitivity != "base" && sensitivity != "accent" && sensitivity != "case" && sensitivity != "variant") {
459 HILOG_ERROR_I18N("invalid sensitivity");
460 return;
461 }
462 } else {
463 map.insert(std::make_pair("sensitivity", "variant"));
464 }
465 }
466
GetCollatorIgnorePunctuation(napi_env env,napi_value options,std::map<std::string,std::string> & map)467 void GetCollatorIgnorePunctuation(napi_env env, napi_value options, std::map<std::string, std::string> &map)
468 {
469 JSUtils::GetBoolOptionValue(env, options, "ignorePunctuation", map);
470 auto it = map.find("ignorePunctuation");
471 if (it != map.end()) {
472 std::string ignorePunctuation = it->second;
473 if (ignorePunctuation != "true" && ignorePunctuation != "false") {
474 HILOG_ERROR_I18N("invalid ignorePunctuation");
475 return;
476 }
477 } else {
478 map.insert(std::make_pair("ignorePunctuation", "false"));
479 }
480 }
481
GetCollatorNumeric(napi_env env,napi_value options,std::map<std::string,std::string> & map)482 void GetCollatorNumeric(napi_env env, napi_value options, std::map<std::string, std::string> &map)
483 {
484 JSUtils::GetBoolOptionValue(env, options, "numeric", map);
485 auto it = map.find("numeric");
486 if (it != map.end()) {
487 std::string numeric = it->second;
488 if (numeric != "true" && numeric != "false") {
489 HILOG_ERROR_I18N("invalid numeric");
490 return;
491 }
492 }
493 }
494
GetCollatorCaseFirst(napi_env env,napi_value options,std::map<std::string,std::string> & map)495 void GetCollatorCaseFirst(napi_env env, napi_value options, std::map<std::string, std::string> &map)
496 {
497 JSUtils::GetOptionValue(env, options, "caseFirst", map);
498 auto it = map.find("caseFirst");
499 if (it != map.end()) {
500 std::string caseFirst = it->second;
501 if (caseFirst != "upper" && caseFirst != "lower" && caseFirst != "false") {
502 HILOG_ERROR_I18N("invalid caseFirst");
503 return;
504 }
505 }
506 }
507
GetCollatorCollation(napi_env env,napi_value options,std::map<std::string,std::string> & map)508 void GetCollatorCollation(napi_env env, napi_value options, std::map<std::string, std::string> &map)
509 {
510 JSUtils::GetOptionValue(env, options, "collation", map);
511 auto it = map.find("collation");
512 if (it != map.end()) {
513 std::string collation = it->second;
514 std::set<std::string> validCollation;
515 validCollation.insert("big5han");
516 validCollation.insert("compat");
517 validCollation.insert("dict");
518 validCollation.insert("direct");
519 validCollation.insert("ducet");
520 validCollation.insert("eor");
521 validCollation.insert("gb2312");
522 validCollation.insert("phonebk");
523 validCollation.insert("phonetic");
524 validCollation.insert("pinyin");
525 validCollation.insert("reformed");
526 validCollation.insert("searchjl");
527 validCollation.insert("stroke");
528 validCollation.insert("trad");
529 validCollation.insert("unihan");
530 validCollation.insert("zhuyin");
531 if (validCollation.find(collation) == validCollation.end()) {
532 map["collation"] = "default";
533 }
534 }
535 }
536
GetCollatorOptionValue(napi_env env,napi_value options,std::map<std::string,std::string> & map)537 void GetCollatorOptionValue(napi_env env, napi_value options, std::map<std::string, std::string> &map)
538 {
539 GetCollatorLocaleMatcher(env, options, map);
540 GetCollatorUsage(env, options, map);
541 GetCollatorSensitivity(env, options, map);
542 GetCollatorIgnorePunctuation(env, options, map);
543 GetCollatorNumeric(env, options, map);
544 GetCollatorCaseFirst(env, options, map);
545 GetCollatorCollation(env, options, map);
546 }
547
InitCollator(napi_env env,napi_value exports)548 napi_value IntlAddon::InitCollator(napi_env env, napi_value exports)
549 {
550 napi_status status = napi_ok;
551 napi_property_descriptor properties[] = {
552 DECLARE_NAPI_FUNCTION("compare", CompareString),
553 DECLARE_NAPI_FUNCTION("resolvedOptions", GetCollatorResolvedOptions)
554 };
555
556 napi_value constructor;
557 status = napi_define_class(env, "Collator", NAPI_AUTO_LENGTH, CollatorConstructor, nullptr,
558 sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
559 if (status != napi_ok) {
560 HILOG_ERROR_I18N("Define class failed when InitCollator");
561 return nullptr;
562 }
563
564 status = napi_set_named_property(env, exports, "Collator", constructor);
565 if (status != napi_ok) {
566 HILOG_ERROR_I18N("Set property failed when InitCollator");
567 return nullptr;
568 }
569 return exports;
570 }
571
CollatorConstructor(napi_env env,napi_callback_info info)572 napi_value IntlAddon::CollatorConstructor(napi_env env, napi_callback_info info)
573 {
574 size_t argc = 2;
575 napi_value argv[2] = { nullptr };
576 napi_value thisVar = nullptr;
577 void *data = nullptr;
578 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
579 if (status != napi_ok) {
580 return nullptr;
581 }
582 std::vector<std::string> localeTags;
583 if (argc > 0) {
584 napi_valuetype valueType = napi_valuetype::napi_undefined;
585 napi_typeof(env, argv[0], &valueType);
586 bool isArray = false;
587 napi_is_array(env, argv[0], &isArray);
588 if (valueType == napi_valuetype::napi_string) {
589 JSUtils::GetLocaleTags(env, argv[0], localeTags);
590 } else if (isArray) {
591 uint32_t arrayLength = 0;
592 napi_get_array_length(env, argv[0], &arrayLength);
593 napi_value element = nullptr;
594 for (uint32_t i = 0; i < arrayLength; i++) {
595 napi_get_element(env, argv[0], i, &element);
596 JSUtils::GetLocaleTags(env, element, localeTags);
597 }
598 }
599 }
600 std::map<std::string, std::string> map = {};
601 if (argc > 1) {
602 GetCollatorOptionValue(env, argv[1], map);
603 }
604 std::unique_ptr<IntlAddon> obj = nullptr;
605 obj = std::make_unique<IntlAddon>();
606 status =
607 napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
608 if (status != napi_ok) {
609 HILOG_ERROR_I18N("CollatorConstructor: Wrap IntlAddon failed");
610 return nullptr;
611 }
612 if (!obj->InitCollatorContext(env, info, localeTags, map)) {
613 HILOG_ERROR_I18N("CollatorConstructor: Init DateTimeFormat failed");
614 return nullptr;
615 }
616 obj.release();
617 return thisVar;
618 }
619
InitCollatorContext(napi_env env,napi_callback_info info,std::vector<std::string> localeTags,std::map<std::string,std::string> & map)620 bool IntlAddon::InitCollatorContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
621 std::map<std::string, std::string> &map)
622 {
623 napi_value global = nullptr;
624 napi_status status = napi_get_global(env, &global);
625 if (status != napi_ok) {
626 HILOG_ERROR_I18N("InitCollatorContext: Get global failed");
627 return false;
628 }
629 env_ = env;
630 collator_ = std::make_unique<Collator>(localeTags, map);
631
632 return collator_ != nullptr;
633 }
634
GetStringParameter(napi_env env,napi_value value,std::vector<char> & buf)635 bool GetStringParameter(napi_env env, napi_value value, std::vector<char> &buf)
636 {
637 napi_valuetype valueType = napi_valuetype::napi_undefined;
638 napi_typeof(env, value, &valueType);
639 if (valueType != napi_valuetype::napi_string) {
640 HILOG_ERROR_I18N("Parameter type does not match");
641 return false;
642 }
643 size_t len = 0;
644 napi_status status = napi_get_value_string_utf8(env, value, nullptr, 0, &len);
645 if (status != napi_ok) {
646 HILOG_ERROR_I18N("Get first length failed");
647 return false;
648 }
649 buf.resize(len + 1);
650 status = napi_get_value_string_utf8(env, value, buf.data(), len + 1, &len);
651 if (status != napi_ok) {
652 HILOG_ERROR_I18N("Get first failed");
653 return false;
654 }
655
656 return true;
657 }
658
FormatRelativeTime(napi_env env,napi_callback_info info)659 napi_value IntlAddon::FormatRelativeTime(napi_env env, napi_callback_info info)
660 {
661 size_t argc = 2;
662 napi_value argv[2] = { 0 };
663 napi_value thisVar = nullptr;
664 void *data = nullptr;
665 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
666 napi_status status;
667 double number;
668 status = napi_get_value_double(env, argv[0], &number);
669 if (status != napi_ok) {
670 HILOG_ERROR_I18N("FormatRelativeTime: Get number failed");
671 return nullptr;
672 }
673 std::vector<char> unit;
674 if (!GetStringParameter(env, argv[1], unit)) {
675 return nullptr;
676 }
677 IntlAddon *obj = nullptr;
678 status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
679 if (status != napi_ok || !obj || !obj->relativetimefmt_) {
680 HILOG_ERROR_I18N("FormatRelativeTime: Get RelativeTimeFormat object failed");
681 return nullptr;
682 }
683 std::string value = obj->relativetimefmt_->Format(number, unit.data());
684 napi_value result = nullptr;
685 status = napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &result);
686 if (status != napi_ok) {
687 HILOG_ERROR_I18N("FormatRelativeTime: Create format string failed");
688 return nullptr;
689 }
690 return result;
691 }
692
FillInArrayElement(napi_env env,napi_value & result,napi_status & status,const std::vector<std::vector<std::string>> & timeVector)693 void IntlAddon::FillInArrayElement(napi_env env, napi_value &result, napi_status &status,
694 const std::vector<std::vector<std::string>> &timeVector)
695 {
696 for (size_t i = 0; i < timeVector.size(); i++) {
697 napi_value value = nullptr;
698 status = napi_create_string_utf8(env, timeVector[i][1].c_str(), NAPI_AUTO_LENGTH, &value);
699 if (status != napi_ok) {
700 HILOG_ERROR_I18N("Failed to create string item imeVector[i][1].");
701 return;
702 }
703 napi_value type = nullptr;
704 status = napi_create_string_utf8(env, timeVector[i][0].c_str(), NAPI_AUTO_LENGTH, &type);
705 if (status != napi_ok) {
706 HILOG_ERROR_I18N("Failed to create string item timeVector[i][0].");
707 return;
708 }
709 napi_value unit = nullptr;
710 size_t unitIndex = 2;
711 if (timeVector[i].size() > unitIndex) {
712 status = napi_create_string_utf8(env, timeVector[i][unitIndex].c_str(), NAPI_AUTO_LENGTH, &unit);
713 if (status != napi_ok) {
714 HILOG_ERROR_I18N("Failed to create string item timeVector[i][unitIndex].");
715 return;
716 }
717 } else {
718 napi_get_undefined(env, &unit);
719 }
720 napi_value formatInfo;
721 status = napi_create_object(env, &formatInfo);
722 if (status != napi_ok) {
723 HILOG_ERROR_I18N("Failed to create format info object.");
724 return;
725 }
726 napi_set_named_property(env, formatInfo, "type", type);
727 napi_set_named_property(env, formatInfo, "value", value);
728 napi_set_named_property(env, formatInfo, "unit", unit);
729 status = napi_set_element(env, result, i, formatInfo);
730 if (status != napi_ok) {
731 HILOG_ERROR_I18N("Failed to set array item");
732 return;
733 }
734 }
735 }
736
FormatToParts(napi_env env,napi_callback_info info)737 napi_value IntlAddon::FormatToParts(napi_env env, napi_callback_info info)
738 {
739 size_t argc = 2;
740 napi_value argv[2] = { 0 };
741 napi_value thisVar = nullptr;
742 void *data = nullptr;
743 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
744 double number = 0;
745 napi_get_value_double(env, argv[0], &number);
746 std::vector<char> unit;
747 if (!GetStringParameter(env, argv[1], unit)) {
748 return nullptr;
749 }
750 IntlAddon *obj = nullptr;
751 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
752 if (status != napi_ok || !obj || !obj->relativetimefmt_) {
753 HILOG_ERROR_I18N("FormatToParts: Get RelativeTimeFormat object failed");
754 return nullptr;
755 }
756 std::vector<std::vector<std::string>> timeVector;
757 obj->relativetimefmt_->FormatToParts(number, unit.data(), timeVector);
758 napi_value result = nullptr;
759 status = napi_create_array_with_length(env, timeVector.size(), &result);
760 if (status != napi_ok) {
761 HILOG_ERROR_I18N("Failed to create array");
762 return nullptr;
763 }
764 FillInArrayElement(env, result, status, timeVector);
765 return result;
766 }
767
CompareString(napi_env env,napi_callback_info info)768 napi_value IntlAddon::CompareString(napi_env env, napi_callback_info info)
769 {
770 size_t argc = 2;
771 napi_value argv[2] = { 0 };
772 napi_value thisVar = nullptr;
773 void *data = nullptr;
774 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
775
776 std::vector<char> first;
777 if (!GetStringParameter(env, argv[0], first)) {
778 return nullptr;
779 }
780
781 std::vector<char> second;
782 if (!GetStringParameter(env, argv[1], second)) {
783 return nullptr;
784 }
785
786 IntlAddon *obj = nullptr;
787 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
788 if (status != napi_ok || !obj || !obj->collator_) {
789 HILOG_ERROR_I18N("CompareString: Get Collator object failed");
790 return nullptr;
791 }
792
793 CompareResult compareResult = obj->collator_->Compare(first.data(), second.data());
794 napi_value result = nullptr;
795 status = napi_create_int32(env, compareResult, &result);
796 if (status != napi_ok) {
797 HILOG_ERROR_I18N("Create compare result failed");
798 return nullptr;
799 }
800
801 return result;
802 }
803
GetCollatorResolvedOptions(napi_env env,napi_callback_info info)804 napi_value IntlAddon::GetCollatorResolvedOptions(napi_env env, napi_callback_info info)
805 {
806 napi_value thisVar = nullptr;
807 void *data = nullptr;
808 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);
809
810 IntlAddon *obj = nullptr;
811 napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
812 if (status != napi_ok || !obj || !obj->collator_) {
813 HILOG_ERROR_I18N("GetCollatorResolvedOptions: Get Collator object failed");
814 return nullptr;
815 }
816 napi_value result = nullptr;
817 napi_create_object(env, &result);
818 std::map<std::string, std::string> options = {};
819 obj->collator_->ResolvedOptions(options);
820 JSUtils::SetOptionProperties(env, result, options, "localeMatcher");
821 JSUtils::SetOptionProperties(env, result, options, "locale");
822 JSUtils::SetOptionProperties(env, result, options, "usage");
823 JSUtils::SetOptionProperties(env, result, options, "sensitivity");
824 JSUtils::SetBooleanOptionProperties(env, result, options, "ignorePunctuation");
825 JSUtils::SetBooleanOptionProperties(env, result, options, "numeric");
826 JSUtils::SetOptionProperties(env, result, options, "caseFirst");
827 JSUtils::SetOptionProperties(env, result, options, "collation");
828 return result;
829 }
830
GetPluralRulesType(napi_env env,napi_value options,std::map<std::string,std::string> & map)831 void GetPluralRulesType(napi_env env, napi_value options, std::map<std::string, std::string> &map)
832 {
833 JSUtils::GetOptionValue(env, options, "type", map);
834 auto it = map.find("type");
835 if (it != map.end()) {
836 std::string type = it->second;
837 if (type != "cardinal" && type != "ordinal") {
838 HILOG_ERROR_I18N("invalid type");
839 return;
840 }
841 } else {
842 map.insert(std::make_pair("type", "cardinal"));
843 }
844 }
845
GetPluralRulesInteger(napi_env env,napi_value options,std::map<std::string,std::string> & map)846 void GetPluralRulesInteger(napi_env env, napi_value options, std::map<std::string, std::string> &map)
847 {
848 JSUtils::GetIntegerOptionValue(env, options, "minimumIntegerDigits", map);
849 auto it = map.find("minimumIntegerDigits");
850 if (it != map.end()) {
851 std::string minimumIntegerDigits = it->second;
852 int32_t status = 0;
853 int n = ConvertString2Int(minimumIntegerDigits, status);
854 if (status == -1 || n < 1 || n > 21) { // the valid range of minimumIntegerDigits is [1, 21]
855 HILOG_ERROR_I18N("invalid minimumIntegerDigits");
856 return;
857 }
858 } else {
859 map.insert(std::make_pair("minimumIntegerDigits", std::to_string(1)));
860 }
861 }
862
GetPluralRulesFractions(napi_env env,napi_value options,std::map<std::string,std::string> & map)863 void GetPluralRulesFractions(napi_env env, napi_value options, std::map<std::string, std::string> &map)
864 {
865 JSUtils::GetIntegerOptionValue(env, options, "minimumFractionDigits", map);
866 auto it = map.find("minimumFractionDigits");
867 if (it != map.end()) {
868 std::string minimumFractionDigits = it->second;
869 int32_t status = 0;
870 int n = ConvertString2Int(minimumFractionDigits, status);
871 if (status == -1 || n < 0 || n > 20) { // the valid range of minimumFractionDigits is [0, 20]
872 HILOG_ERROR_I18N("invalid minimumFractionDigits");
873 return;
874 }
875 }
876
877 JSUtils::GetIntegerOptionValue(env, options, "maximumFractionDigits", map);
878 it = map.find("maximumFractionDigits");
879 if (it != map.end()) {
880 std::string maximumFractionDigits = it->second;
881 int32_t status = 0;
882 int n = ConvertString2Int(maximumFractionDigits, status);
883 if (status == -1 || n < 0 || n > 20) { // the valid range of maximumFractionDigits is [0, 20]
884 HILOG_ERROR_I18N("invalid maximumFractionDigits");
885 return;
886 }
887 }
888 }
889
GetPluralRulesSignificant(napi_env env,napi_value options,std::map<std::string,std::string> & map)890 void GetPluralRulesSignificant(napi_env env, napi_value options, std::map<std::string, std::string> &map)
891 {
892 int minSignificant = -1;
893 JSUtils::GetIntegerOptionValue(env, options, "minimumSignificantDigits", map);
894 auto it = map.find("minimumSignificantDigits");
895 if (it != map.end()) {
896 std::string minSignificantStr = it->second;
897 int32_t status = 0;
898 int minSignificantInt = ConvertString2Int(minSignificantStr, status);
899 // the valid range of minSignificantInt is [1, 21]
900 if (status == -1 || minSignificantInt < 1 || minSignificantInt > 21) {
901 HILOG_ERROR_I18N("invalid minimumSignificantDigits");
902 return;
903 }
904 minSignificant = minSignificantInt;
905 } else {
906 minSignificant = 1;
907 }
908
909 JSUtils::GetIntegerOptionValue(env, options, "maximumSignificantDigits", map);
910 it = map.find("maximumSignificantDigits");
911 if (it != map.end()) {
912 std::string maxSignificantStr = it->second;
913 int32_t status = 0;
914 int maxSignificant = ConvertString2Int(maxSignificantStr, status);
915 // the valid range of minSignificant is [minSignificant, 21]
916 if (status == -1 || maxSignificant < minSignificant || maxSignificant > 21) {
917 HILOG_ERROR_I18N("invalid maximumSignificantDigits");
918 return;
919 }
920 }
921 }
922
GetPluralRulesOptionValues(napi_env env,napi_value options,std::map<std::string,std::string> & map)923 void GetPluralRulesOptionValues(napi_env env, napi_value options, std::map<std::string, std::string> &map)
924 {
925 GetCollatorLocaleMatcher(env, options, map);
926 GetPluralRulesType(env, options, map);
927 GetPluralRulesInteger(env, options, map);
928 GetPluralRulesFractions(env, options, map);
929 GetPluralRulesSignificant(env, options, map);
930 }
931
InitPluralRules(napi_env env,napi_value exports)932 napi_value IntlAddon::InitPluralRules(napi_env env, napi_value exports)
933 {
934 napi_status status = napi_ok;
935 napi_property_descriptor properties[] = {
936 DECLARE_NAPI_FUNCTION("select", Select)
937 };
938
939 napi_value constructor = nullptr;
940 status = napi_define_class(env, "PluralRules", NAPI_AUTO_LENGTH, PluralRulesConstructor, nullptr,
941 sizeof(properties) / sizeof(napi_property_descriptor), properties, &constructor);
942 if (status != napi_ok) {
943 HILOG_ERROR_I18N("Define class failed when InitPluralRules");
944 return nullptr;
945 }
946
947 status = napi_set_named_property(env, exports, "PluralRules", constructor);
948 if (status != napi_ok) {
949 HILOG_ERROR_I18N("Set property failed when InitPluralRules");
950 return nullptr;
951 }
952 return exports;
953 }
954
PluralRulesConstructor(napi_env env,napi_callback_info info)955 napi_value IntlAddon::PluralRulesConstructor(napi_env env, napi_callback_info info)
956 {
957 size_t argc = 2;
958 napi_value argv[2] = { nullptr };
959 napi_value thisVar = nullptr;
960 void *data = nullptr;
961 napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
962 if (status != napi_ok) {
963 return nullptr;
964 }
965 napi_valuetype valueType = napi_valuetype::napi_undefined;
966 std::vector<std::string> localeTags;
967 if (argc > 0) {
968 napi_typeof(env, argv[0], &valueType);
969 bool isArray = false;
970 napi_is_array(env, argv[0], &isArray);
971 if (valueType == napi_valuetype::napi_string) {
972 JSUtils::GetLocaleTags(env, argv[0], localeTags);
973 } else if (isArray) {
974 uint32_t arrayLength = 0;
975 napi_get_array_length(env, argv[0], &arrayLength);
976 napi_value element = nullptr;
977 for (uint32_t i = 0; i < arrayLength; i++) {
978 napi_get_element(env, argv[0], i, &element);
979 JSUtils::GetLocaleTags(env, element, localeTags);
980 }
981 }
982 }
983 std::map<std::string, std::string> map = {};
984 if (argc > 1) {
985 GetPluralRulesOptionValues(env, argv[1], map);
986 }
987 std::unique_ptr<IntlAddon> obj = nullptr;
988 obj = std::make_unique<IntlAddon>();
989 status =
990 napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()), IntlAddon::Destructor, nullptr, nullptr);
991 if (status != napi_ok) {
992 HILOG_ERROR_I18N("PluralRulesConstructor: Wrap IntlAddon failed");
993 return nullptr;
994 }
995 if (!obj->InitPluralRulesContext(env, info, localeTags, map)) {
996 HILOG_ERROR_I18N("PluralRulesConstructor: Init DateTimeFormat failed");
997 return nullptr;
998 }
999 obj.release();
1000 return thisVar;
1001 }
1002
InitPluralRulesContext(napi_env env,napi_callback_info info,std::vector<std::string> localeTags,std::map<std::string,std::string> & map)1003 bool IntlAddon::InitPluralRulesContext(napi_env env, napi_callback_info info, std::vector<std::string> localeTags,
1004 std::map<std::string, std::string> &map)
1005 {
1006 napi_value global = nullptr;
1007 napi_status status = napi_get_global(env, &global);
1008 if (status != napi_ok) {
1009 HILOG_ERROR_I18N("InitPluralRulesContext: Get global failed");
1010 return false;
1011 }
1012 env_ = env;
1013 pluralrules_ = std::make_unique<PluralRules>(localeTags, map);
1014
1015 return pluralrules_ != nullptr;
1016 }
1017
Select(napi_env env,napi_callback_info info)1018 napi_value IntlAddon::Select(napi_env env, napi_callback_info info)
1019 {
1020 size_t argc = 1;
1021 napi_value argv[1] = { 0 };
1022 napi_value thisVar = nullptr;
1023 void *data = nullptr;
1024 napi_get_cb_info(env, info, &argc, argv, &thisVar, &data);
1025 napi_valuetype valueType = napi_valuetype::napi_undefined;
1026 napi_typeof(env, argv[0], &valueType);
1027 if (valueType != napi_valuetype::napi_number) {
1028 HILOG_ERROR_I18N("Select: Parameter type does not match");
1029 return nullptr;
1030 }
1031
1032 double number = 0;
1033 napi_status status = napi_get_value_double(env, argv[0], &number);
1034 if (status != napi_ok) {
1035 HILOG_ERROR_I18N("Select: Get number failed");
1036 return nullptr;
1037 }
1038
1039 IntlAddon *obj = nullptr;
1040 status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
1041 if (status != napi_ok || !obj || !obj->pluralrules_) {
1042 HILOG_ERROR_I18N("Get PluralRules object failed");
1043 return nullptr;
1044 }
1045
1046 std::string res = obj->pluralrules_->Select(number);
1047 napi_value result = nullptr;
1048 status = napi_create_string_utf8(env, res.c_str(), NAPI_AUTO_LENGTH, &result);
1049 if (status != napi_ok) {
1050 HILOG_ERROR_I18N("get select result failed");
1051 return nullptr;
1052 }
1053 return result;
1054 }
1055
Init(napi_env env,napi_value exports)1056 napi_value Init(napi_env env, napi_value exports)
1057 {
1058 napi_value val = LocaleInfoAddon::InitLocale(env, exports);
1059 val = IntlAddon::InitDateTimeFormat(env, val);
1060 val = IntlAddon::InitCollator(env, val);
1061 val = IntlAddon::InitRelativeTimeFormat(env, val);
1062 val = IntlAddon::InitPluralRules(env, val);
1063 val = NumberFormatAddon::InitNumberFormat(env, val);
1064 return val;
1065 }
1066
1067 static napi_module g_intlModule = {
1068 .nm_version = 1,
1069 .nm_flags = 0,
1070 .nm_filename = nullptr,
1071 .nm_register_func = Init,
1072 .nm_modname = "intl",
1073 .nm_priv = nullptr,
1074 .reserved = { 0 }
1075 };
1076
AbilityRegister()1077 extern "C" __attribute__((constructor)) void AbilityRegister()
1078 {
1079 napi_module_register(&g_intlModule);
1080 }
1081 } // namespace I18n
1082 } // namespace Global
1083 } // namespace OHOS
1084