1 /*
2 * Copyright (c) 2021 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 "i18n_memory_adapter.h"
17 #include "securec.h"
18 #include "str_util.h"
19 #include "types.h"
20 #include "locale_info.h"
21
22 static constexpr uint16_t OPT_LANG = 0x0001;
23 static constexpr uint16_t OPT_SCRIPT = 0x0002;
24 static constexpr uint16_t OPT_REGION = 0x0004;
25 static constexpr uint16_t OPT_EXTENSION = 0x0008;
26 static constexpr uint8_t TAG_COMMON = 0;
27 static constexpr uint8_t TAG_U = 1;
28 static constexpr uint8_t TAG_KEY = 2;
29 static constexpr uint8_t TAG_VALUE = 3;
30 static constexpr int LANGUAGE_MIN_LENGTH = 2;
31 static constexpr int LANGUAGE_MAX_LENGTH = 3;
32 static constexpr int REGION_LENGTH = 2;
33 static constexpr int SCRIPT_LENGTH = 4;
34
35 using namespace OHOS::I18N;
36
Init(const char * newLang,const char * newScript,const char * newRegion,int & status)37 void LocaleInfo::Init(const char *newLang, const char *newScript, const char *newRegion, int &status)
38 {
39 id = nullptr;
40 status = IERROR;
41 if (newLang == nullptr) {
42 return;
43 }
44 int langLength = LenCharArray(newLang);
45 // language consists of two or three letters
46 if ((langLength > LANGUAGE_MAX_LENGTH) || (langLength < LANGUAGE_MIN_LENGTH)) {
47 return;
48 }
49 I18nFree(static_cast<void *>(language));
50 language = NewArrayAndCopy(newLang, langLength);
51 if (newScript != nullptr) {
52 int scriptLength = LenCharArray(newScript);
53 if (scriptLength == SCRIPT_LENGTH) {
54 script = NewArrayAndCopy(newScript, scriptLength);
55 }
56 }
57 if (newRegion != nullptr) {
58 int regionLength = LenCharArray(newRegion);
59 if (regionLength == REGION_LENGTH) {
60 region = NewArrayAndCopy(newRegion, regionLength);
61 }
62 }
63 InitIdstr();
64 status = ISUCCESS;
65 }
66
InitIdstr()67 void LocaleInfo::InitIdstr()
68 {
69 if (language == nullptr) {
70 return;
71 }
72 std::string idStr(language);
73 // script consists of four letters
74 if ((script != nullptr) && (LenCharArray(script) > 0)) {
75 idStr = idStr + "-" + script;
76 }
77 if ((region != nullptr) && (LenCharArray(region) > 0)) {
78 idStr = idStr + "-" + region;
79 }
80 I18nFree(static_cast<void *>(id));
81 id = NewArrayAndCopy(idStr.data(), idStr.size());
82 }
83
LocaleInfo(const char * newLang,const char * newScript,const char * newRegion)84 LocaleInfo::LocaleInfo(const char *newLang, const char *newScript, const char *newRegion)
85 {
86 int status = ISUCCESS;
87 Init(newLang, newScript, newRegion, status);
88 if (status != ISUCCESS) {
89 SetFail();
90 }
91 }
92
IsDefaultLocale() const93 bool LocaleInfo::IsDefaultLocale() const
94 {
95 if ((GetLanguage() == nullptr) || (GetRegion() == nullptr)) {
96 return false;
97 }
98 return ((strcmp(GetLanguage(), "en") == 0) && (strcmp(GetRegion(), "US") == 0));
99 }
100
LocaleInfo(const char * newLang,const char * newRegion)101 LocaleInfo::LocaleInfo(const char *newLang, const char *newRegion)
102 {
103 int status = ISUCCESS;
104 Init(newLang, nullptr, newRegion, status);
105 if (status != ISUCCESS) {
106 SetFail();
107 }
108 }
109
LocaleInfo()110 LocaleInfo::LocaleInfo()
111 {
112 id = nullptr;
113 SetFail();
114 }
115
LocaleInfo(const LocaleInfo & o)116 LocaleInfo::LocaleInfo(const LocaleInfo &o)
117 {
118 int status = ISUCCESS;
119 Init(o.language, o.script, o.region, status);
120 if (status != ISUCCESS) {
121 SetFail();
122 }
123 }
124
~LocaleInfo()125 LocaleInfo::~LocaleInfo()
126 {
127 FreeResource();
128 }
129
FreeResource()130 void LocaleInfo::FreeResource()
131 {
132 I18nFree(static_cast<void *>(language));
133 I18nFree(static_cast<void *>(script));
134 I18nFree(static_cast<void *>(region));
135 I18nFree(static_cast<void *>(id));
136 I18nFree(static_cast<void *>(numberDigits));
137 }
138
operator ==(const LocaleInfo & other) const139 bool LocaleInfo::operator ==(const LocaleInfo &other) const
140 {
141 bool ret = CompareLocaleItem(language, other.language);
142 if (!ret) {
143 return false;
144 }
145 ret = CompareLocaleItem(script, other.script);
146 if (!ret) {
147 return false;
148 }
149 ret = CompareLocaleItem(region, other.region);
150 return ret;
151 }
152
operator =(const LocaleInfo & o)153 LocaleInfo &LocaleInfo::operator =(const LocaleInfo &o)
154 {
155 if (&o == this) {
156 return *this;
157 }
158 FreeResource();
159 if (o.language != nullptr) {
160 language = NewArrayAndCopy(o.language, strlen(o.language));
161 }
162 if (o.script != nullptr) {
163 script = NewArrayAndCopy(o.script, strlen(o.script));
164 }
165 if (o.region != nullptr) {
166 region = NewArrayAndCopy(o.region, strlen(o.region));
167 }
168 if (o.id != nullptr) {
169 id = NewArrayAndCopy(o.id, LenCharArray(o.id));
170 }
171 if (o.numberDigits != nullptr) {
172 numberDigits = NewArrayAndCopy(o.numberDigits, LenCharArray(o.numberDigits));
173 }
174 return *this;
175 }
176
GetLanguage() const177 const char *LocaleInfo::GetLanguage() const
178 {
179 return language;
180 }
181
GetScript() const182 const char *LocaleInfo::GetScript() const
183 {
184 return script;
185 }
186
GetRegion() const187 const char *LocaleInfo::GetRegion() const
188 {
189 return region;
190 }
191
GetId() const192 const char *LocaleInfo::GetId() const
193 {
194 const char *rid = id;
195 return rid;
196 }
197
IsSuccess()198 bool LocaleInfo::IsSuccess()
199 {
200 bool r = isSucc;
201 isSucc = true;
202 return r;
203 }
204
SetFail()205 void LocaleInfo::SetFail()
206 {
207 isSucc = false;
208 }
209
ChangeLanguageCode(char * lang,const int32_t dstSize,const char * src,const int32_t srcSize) const210 bool LocaleInfo::ChangeLanguageCode(char *lang, const int32_t dstSize, const char *src, const int32_t srcSize) const
211 {
212 if (lang == nullptr || src == nullptr) {
213 return false;
214 }
215 if (srcSize == (LANGUAGE_MIN_LENGTH + 1)) { // three letter language only support fil and mai
216 if (memcmp(src, "fil", srcSize) == 0) {
217 lang[0] = 't';
218 lang[1] = 'l';
219 } else if (memcmp(src, "mai", srcSize) == 0) {
220 lang[0] = 'm';
221 lang[1] = 'd';
222 } else {
223 return false;
224 }
225 return true;
226 } else if (srcSize == LANGUAGE_MIN_LENGTH) {
227 if (memcmp(src, "he", srcSize) == 0) {
228 lang[0] = 'i';
229 lang[1] = 'w';
230 } else if (memcmp(src, "id", srcSize) == 0) {
231 lang[0] = 'i';
232 lang[1] = 'n';
233 } else {
234 if (strcpy_s(lang, dstSize, src) != EOK) {
235 return false;
236 }
237 }
238 return true;
239 }
240 return false;
241 }
242
GetMask() const243 uint32_t LocaleInfo::GetMask() const
244 {
245 if (language == nullptr) {
246 return 0;
247 }
248 char lang[LANGUAGE_MAX_LENGTH];
249 bool isRight = ChangeLanguageCode(lang, LANGUAGE_MAX_LENGTH, language, LenCharArray(language));
250 if (!isRight) {
251 return 0;
252 }
253 // use 7bit to represent an English letter,
254 // 32--- language ---18--- script ---14--- region ---0
255 uint32_t tempLangFirst = (lang[0] - CHAR_OFF);
256 uint32_t tempLangSecond = (lang[1] - CHAR_OFF);
257 uint32_t mask = (tempLangFirst << LANG_FIRST_BEGIN) | (tempLangSecond << LANG_SECOND_BEGIN);
258 if ((script != nullptr) && (LenCharArray(script) > 0)) {
259 if (strcmp(script, "Hans") == 0) {
260 mask = mask | (HANS << SCRIPT_BEGIN);
261 } else if (strcmp(script, "Hant") == 0) {
262 mask = mask | (HANT << SCRIPT_BEGIN);
263 } else if (strcmp(script, "Latn") == 0) {
264 mask = mask | (LATN << SCRIPT_BEGIN);
265 } else if (strcmp(script, "Qaag") == 0) {
266 mask = mask | (QAAG << SCRIPT_BEGIN);
267 } else if (strcmp(script, "Cyrl") == 0) {
268 mask = mask | (CYRL << SCRIPT_BEGIN);
269 } else if (strcmp(script, "Deva") == 0) {
270 mask = mask | (DEVA << SCRIPT_BEGIN);
271 } else if (strcmp(script, "Guru") == 0) {
272 mask = mask | (GURU << SCRIPT_BEGIN);
273 }
274 }
275 if ((region != nullptr) && (LenCharArray(region) > 1)) {
276 uint32_t tempRegion = (region[0] - CHAR_OFF);
277 uint32_t tempRegionSecond = (region[1] - CHAR_OFF);
278 mask = mask | (tempRegion << REGION_FIRST_LETTER) | (tempRegionSecond);
279 }
280 return mask;
281 }
282
ForLanguageTag(const char * languageTag,I18nStatus & status)283 LocaleInfo LocaleInfo::ForLanguageTag(const char *languageTag, I18nStatus &status)
284 {
285 LocaleInfo locale;
286 if (languageTag == nullptr) {
287 status = IERROR;
288 return locale;
289 }
290 ParseLanguageTag(locale, languageTag, status);
291 locale.InitIdstr();
292 return locale;
293 }
294
ParseLanguageTag(LocaleInfo & locale,const char * languageTag,I18nStatus & status)295 void LocaleInfo::ParseLanguageTag(LocaleInfo &locale, const char *languageTag, I18nStatus &status)
296 {
297 const char *tag = languageTag;
298 uint16_t options = OPT_LANG;
299 const char *key = nullptr;
300 const char *value = nullptr;
301 uint8_t type = 0;
302 while (tag) {
303 const char *start = tag;
304 const char *end = tag;
305 while (*end) {
306 if (*end == '-') {
307 break;
308 }
309 ++end;
310 }
311 tag = end + 1;
312 if (*end == '\0') {
313 tag = nullptr;
314 }
315 auto tagLength = end - start;
316 ConfirmTagType(start, tagLength, type, key, value);
317 if (!ParseNormalSubTag(locale, start, tagLength, options, type)) {
318 if ((options & OPT_EXTENSION) && (type == TAG_VALUE)) {
319 ProcessExtension(locale, key, value);
320 type = TAG_COMMON;
321 }
322 }
323 }
324 I18nFree(static_cast<void *>(const_cast<char *>(key)));
325 I18nFree(static_cast<void *>(const_cast<char *>(value)));
326 }
327
ParseNormalSubTag(LocaleInfo & locale,const char * start,size_t tagLength,uint16_t & options,uint8_t & type)328 bool LocaleInfo::ParseNormalSubTag(LocaleInfo &locale, const char *start, size_t tagLength, uint16_t &options,
329 uint8_t &type)
330 {
331 if ((start == nullptr) || (tagLength == 0)) {
332 return false;
333 }
334 if ((options & OPT_LANG) && (type == TAG_COMMON)) {
335 if (IsLanguage(start, tagLength)) {
336 locale.language = I18nNewCharString(start, tagLength);
337 options &= ~OPT_LANG;
338 options |= OPT_SCRIPT | OPT_REGION | OPT_EXTENSION;
339 return true;
340 }
341 }
342 if ((options & OPT_SCRIPT) && (type == TAG_COMMON)) {
343 if (IsScript(start, tagLength)) {
344 options &= ~OPT_SCRIPT;
345 locale.script = I18nNewCharString(start, tagLength);
346 return true;
347 }
348 }
349 if ((options & OPT_REGION) && (type == TAG_COMMON)) {
350 if (IsRegion(start, tagLength)) {
351 options &= ~OPT_REGION;
352 options &= ~OPT_SCRIPT;
353 locale.region = I18nNewCharString(start, tagLength);
354 return true;
355 }
356 }
357 return false;
358 }
359
ConfirmTagType(const char * start,size_t length,uint8_t & type,const char * & key,const char * & value)360 void LocaleInfo::ConfirmTagType(const char *start, size_t length, uint8_t &type, const char* &key, const char* &value)
361 {
362 if (start == nullptr) {
363 return;
364 }
365 switch (type) {
366 case TAG_COMMON: {
367 if ((length == 1) && (*start == 'u')) {
368 type = TAG_U;
369 }
370 return;
371 }
372 case TAG_U: {
373 type = TAG_KEY;
374 I18nFree(static_cast<void *>(const_cast<char *>(key)));
375 key = I18nNewCharString(start, length);
376 return;
377 }
378 case TAG_KEY: {
379 type = TAG_VALUE;
380 I18nFree(static_cast<void *>(const_cast<char *>(value)));
381 value = I18nNewCharString(start, length);
382 return;
383 }
384 default: {
385 type = TAG_COMMON;
386 return;
387 }
388 }
389 }
390
ProcessExtension(LocaleInfo & locale,const char * key,const char * value)391 void LocaleInfo::ProcessExtension(LocaleInfo &locale, const char *key, const char *value)
392 {
393 if (key == nullptr || value == nullptr) {
394 return;
395 }
396 // now we only support numbering systems in extensions
397 if (strcmp(key, "nu") == 0) {
398 locale.numberDigits = NewArrayAndCopy(value, strlen(value));
399 return;
400 }
401 }
402
IsLanguage(const char * start,uint8_t length)403 bool LocaleInfo::IsLanguage(const char *start, uint8_t length)
404 {
405 if ((length != LANGUAGE_MAX_LENGTH) && (length != LANGUAGE_MIN_LENGTH)) {
406 return false;
407 }
408 for (uint8_t i = 0; i < length; ++i) {
409 const char ch = *(start + i);
410 if (ch < 'a' || ch > 'z') {
411 return false;
412 }
413 }
414 return true;
415 }
416
IsScript(const char * start,uint8_t length)417 bool LocaleInfo::IsScript(const char *start, uint8_t length)
418 {
419 // all scripts's length is 4,
420 // now we support Latn, Hans, Hant, Qaag, Cyrl, Deva, Guru
421 if (length != SCRIPT_LENGTH || start == nullptr) {
422 return false;
423 }
424 if (memcmp(start, "Hans", length) == 0) {
425 return true;
426 } else if (memcmp(start, "Latn", length) == 0) {
427 return true;
428 } else if (memcmp(start, "Hant", length) == 0) {
429 return true;
430 } else if (memcmp(start, "Qaag", length) == 0) {
431 return true;
432 } else if (memcmp(start, "Cyrl", length) == 0) {
433 return true;
434 } else if (memcmp(start, "Deva", length) == 0) {
435 return true;
436 } else if (memcmp(start, "Guru", length) == 0) {
437 return true;
438 } else {
439 return false;
440 }
441 }
442
IsRegion(const char * start,uint8_t length)443 bool LocaleInfo::IsRegion(const char *start, uint8_t length)
444 {
445 if (length != REGION_LENGTH) {
446 return false;
447 }
448 for (uint8_t i = 0; i < length; ++i) {
449 const char ch = *(start + i);
450 if (ch < 'A' || ch > 'Z') { // region characters should all be upper case.
451 return false;
452 }
453 }
454 return true;
455 }
456
GetExtension(const char * key)457 const char *LocaleInfo::GetExtension(const char *key)
458 {
459 if (strcmp(key, "nu") == 0) {
460 return numberDigits;
461 }
462 return nullptr;
463 }
464