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