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