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 #ifdef SUPPORT_APP_PREFERRED_LANGUAGE
16 #include <regex>
17 #include "application_context.h"
18 #include "bundle_info.h"
19 #include "bundle_mgr_interface.h"
20 #include "iservice_registry.h"
21 #include "system_ability_definition.h"
22 #endif
23 #include "i18n_hilog.h"
24 #include "locale_config.h"
25 #include "locale_info.h"
26 #include "locale_matcher.h"
27 #include "parameter.h"
28 #include "preferred_language.h"
29 #include "utils.h"
30 #include "vector"
31
32 namespace OHOS {
33 namespace Global {
34 namespace I18n {
35 const char *PreferredLanguage::RESOURCE_PATH_HEAD = "/data/accounts/account_0/applications/";
36 const char *PreferredLanguage::RESOURCE_PATH_TAILOR = "/assets/entry/resources.index";
37 const char *PreferredLanguage::RESOURCE_PATH_SPLITOR = "/";
38 const char *PreferredLanguage::PREFERRED_LANGUAGES = "persist.global.preferredLanguages";
39 const char *PreferredLanguage::APP_LANGUAGE_KEY = "app_language";
40 const char *PreferredLanguage::I18N_PREFERENCES_FILE_NAME = "/i18n";
41 const char *PreferredLanguage::DEFAULT_PREFERRED_LANGUAGE = "en-Latn-US";
42 std::vector<std::string> PreferredLanguage::supportLanguageListExt = { "it", "ko", "th", "zz" };
43
GetPreferredLanguageList()44 std::vector<std::string> PreferredLanguage::GetPreferredLanguageList()
45 {
46 char preferredLanguageValue[CONFIG_LEN];
47 GetParameter(PREFERRED_LANGUAGES, "", preferredLanguageValue, CONFIG_LEN);
48 std::string systemLanguage = GetMatchedLanguage(LocaleConfig::GetSystemLanguage());
49 std::vector<std::string> list;
50 Split(preferredLanguageValue, ";", list);
51 list = FilterLanguages(list);
52 if (!list.size()) {
53 if (systemLanguage != "") {
54 list.push_back(systemLanguage);
55 }
56 return list;
57 }
58 if (list[0] == systemLanguage || systemLanguage == "") {
59 return list;
60 }
61 int systemLanguageIdx = -1;
62 for (size_t i = 0; i < list.size(); i++) {
63 if (list[i] == systemLanguage) {
64 systemLanguageIdx = (int)i;
65 }
66 }
67 if (systemLanguageIdx == -1) {
68 list.insert(list.begin(), systemLanguage);
69 } else {
70 for (size_t i = (size_t)systemLanguageIdx; i > 0; i--) {
71 list[i] = list[i - 1];
72 }
73 list[0] = systemLanguage;
74 }
75 return list;
76 }
77
FilterLanguages(std::vector<std::string> & preferredLanguagesList)78 std::vector<std::string> PreferredLanguage::FilterLanguages(std::vector<std::string>& preferredLanguagesList)
79 {
80 std::vector<std::string> matchedLanguagesList;
81 std::unordered_set<std::string> matchedSet;
82 for (auto& preferredLanguage : preferredLanguagesList) {
83 std::string matchedLanguage = GetMatchedLanguage(preferredLanguage);
84 if (matchedLanguage.empty()) {
85 HILOG_ERROR_I18N("FilterLanguages: the matching result of %{public}s is empty.",
86 preferredLanguage.c_str());
87 matchedLanguage = DEFAULT_PREFERRED_LANGUAGE;
88 }
89 if (matchedSet.find(matchedLanguage) == matchedSet.end()) {
90 matchedLanguagesList.push_back(matchedLanguage);
91 matchedSet.insert(matchedLanguage);
92 HILOG_ERROR_I18N("FilterLanguages: the matching result is %{public}s.", matchedLanguage.c_str());
93 }
94 }
95 return matchedLanguagesList;
96 }
97
GetMatchedLanguage(const std::string & language)98 std::string PreferredLanguage::GetMatchedLanguage(const std::string& language)
99 {
100 UErrorCode status = U_ZERO_ERROR;
101 icu::Locale locale = icu::Locale::forLanguageTag(language.c_str(), status);
102 if (U_FAILURE(status) || !IsValidLocaleTag(locale)) {
103 HILOG_ERROR_I18N("GetMatchedLanguage: %{public}s is an invalid locale.", language.c_str());
104 return "";
105 }
106 LocaleInfo* requestLocale = new LocaleInfo(language);
107 if (requestLocale == nullptr) {
108 HILOG_ERROR_I18N("GetMatchedLanguage: %{public}s failed to construct LocaleInfo.", language.c_str());
109 return "";
110 }
111 std::vector<LocaleInfo*> candidateLocales;
112 std::vector<std::string> supportLanguageList;
113 LocaleConfig::GetSystemLanguages(supportLanguageList);
114 supportLanguageList.insert(supportLanguageList.end(), supportLanguageListExt.begin(),
115 supportLanguageListExt.end());
116 for (auto& supportLanguage : supportLanguageList) {
117 LocaleInfo* supportLocaleInfo = new LocaleInfo(supportLanguage);
118 if (supportLocaleInfo == nullptr) {
119 HILOG_ERROR_I18N("GetMatchedLanguage: %{public}s failed to construct LocaleInfo.",
120 supportLanguage.c_str());
121 continue;
122 }
123 if (LocaleMatcher::Match(requestLocale, supportLocaleInfo)) {
124 candidateLocales.push_back(supportLocaleInfo);
125 } else {
126 delete supportLocaleInfo;
127 }
128 }
129 std::string matchedLanguage = LocaleMatcher::GetBestMatchedLocale(requestLocale, candidateLocales);
130 for (LocaleInfo* supportLocaleInfo : candidateLocales) {
131 delete supportLocaleInfo;
132 }
133 delete requestLocale;
134 return matchedLanguage;
135 }
136
GetFirstPreferredLanguage()137 std::string PreferredLanguage::GetFirstPreferredLanguage()
138 {
139 std::vector<std::string> preferredLanguageList = GetPreferredLanguageList();
140 return preferredLanguageList[0];
141 }
142
143 #ifdef SUPPORT_APP_PREFERRED_LANGUAGE
GetI18nAppPreferences()144 std::shared_ptr<NativePreferences::Preferences> PreferredLanguage::GetI18nAppPreferences()
145 {
146 std::shared_ptr<AbilityRuntime::ApplicationContext> appContext = AbilityRuntime::ApplicationContext::GetInstance();
147 std::string preferencesDirPath = appContext->GetPreferencesDir();
148 std::string i18nPreferencesFilePath = preferencesDirPath + I18N_PREFERENCES_FILE_NAME;
149 int status;
150 NativePreferences::Options options(i18nPreferencesFilePath);
151 std::shared_ptr<NativePreferences::Preferences> preferences =
152 NativePreferences::PreferencesHelper::GetPreferences(options, status);
153 if (status != 0) {
154 HILOG_ERROR_I18N("PreferredLanguage::GetAppPreferredLanguage get i18n app preferences failed.");
155 return nullptr;
156 }
157 return preferences;
158 }
159
IsSetAppPreferredLanguage()160 bool PreferredLanguage::IsSetAppPreferredLanguage()
161 {
162 std::shared_ptr<NativePreferences::Preferences> preferences = GetI18nAppPreferences();
163 if (preferences == nullptr) {
164 HILOG_ERROR_I18N(
165 "PreferredLanguage::IsSetAppPreferredLanguage get i18n preferences failed, return system language.");
166 return false;
167 }
168 std::string res = preferences->GetString(PreferredLanguage::APP_LANGUAGE_KEY, "");
169 if (res.length() == 0 || res.compare("default") == 0) {
170 return false;
171 }
172 return true;
173 }
174
GetAppPreferredLanguage()175 std::string PreferredLanguage::GetAppPreferredLanguage()
176 {
177 std::shared_ptr<NativePreferences::Preferences> preferences = GetI18nAppPreferences();
178 if (preferences == nullptr) {
179 HILOG_ERROR_I18N(
180 "PreferredLanguage::GetAppPreferredLanguage get i18n preferences failed, return system language.");
181 return LocaleConfig::GetSystemLocale();
182 }
183 std::string res = preferences->GetString(PreferredLanguage::APP_LANGUAGE_KEY, "");
184 if (res.length() == 0 || res.compare("default") == 0) {
185 return LocaleConfig::GetSystemLocale();
186 }
187 return res;
188 }
189
SetAppPreferredLanguage(const std::string & language,I18nErrorCode & errCode)190 void PreferredLanguage::SetAppPreferredLanguage(const std::string &language, I18nErrorCode &errCode)
191 {
192 std::shared_ptr<AbilityRuntime::ApplicationContext> appContext = AbilityRuntime::ApplicationContext::GetInstance();
193 if (language.compare("default") != 0) {
194 appContext->SetLanguage(language);
195 }
196 std::shared_ptr<NativePreferences::Preferences> preferences = GetI18nAppPreferences();
197 if (preferences == nullptr) {
198 errCode = I18nErrorCode::FAILED;
199 HILOG_ERROR_I18N("PreferredLanguage::SetAppPreferredLanguage get i18n preferences failed.");
200 return;
201 }
202 int32_t status = preferences->PutString(PreferredLanguage::APP_LANGUAGE_KEY, language);
203 if (status != 0) {
204 errCode = I18nErrorCode::FAILED;
205 HILOG_ERROR_I18N(
206 "PreferredLanguage::SetAppPreferredLanguage set app language to i18n preferences failed.");
207 return;
208 }
209 preferences->Flush();
210 }
211 #endif
212
GetPreferredLocale()213 std::string PreferredLanguage::GetPreferredLocale()
214 {
215 std::string systemLocale = LocaleConfig::GetSystemLocale();
216 LocaleInfo systemLocaleInfo(systemLocale);
217 std::string systemRegion = systemLocaleInfo.GetRegion();
218 std::string preferredLanguageLocale = GetFirstPreferredLanguage();
219 LocaleInfo preferredLanguageLocaleInfo(preferredLanguageLocale);
220 std::string preferredLanguage = preferredLanguageLocaleInfo.GetLanguage();
221 std::string preferredLocale = preferredLanguage + "-" + systemRegion;
222 return preferredLocale;
223 }
224
IsValidLanguage(const std::string & language)225 bool PreferredLanguage::IsValidLanguage(const std::string &language)
226 {
227 std::string::size_type size = language.size();
228 if ((size != LANGUAGE_LEN) && (size != LANGUAGE_LEN + 1)) {
229 return false;
230 }
231 for (size_t i = 0; i < size; ++i) {
232 if ((language[i] > 'z') || (language[i] < 'a')) {
233 return false;
234 }
235 }
236 return true;
237 }
238
IsValidTag(const std::string & tag)239 bool PreferredLanguage::IsValidTag(const std::string &tag)
240 {
241 if (!tag.size()) {
242 return false;
243 }
244 std::vector<std::string> splits;
245 Split(tag, "-", splits);
246 if (!IsValidLanguage(splits[0])) {
247 return false;
248 }
249 return true;
250 }
251
Split(const std::string & src,const std::string & sep,std::vector<std::string> & dest)252 void PreferredLanguage::Split(const std::string &src, const std::string &sep, std::vector<std::string> &dest)
253 {
254 std::string::size_type begin = 0;
255 std::string::size_type end = src.find(sep);
256 while (end != std::string::npos) {
257 dest.push_back(src.substr(begin, end - begin));
258 begin = end + sep.size();
259 end = src.find(sep, begin);
260 }
261 if (begin != src.size()) {
262 dest.push_back(src.substr(begin));
263 }
264 }
265
AddPreferredLanguage(const std::string & language,int32_t index)266 I18nErrorCode PreferredLanguage::AddPreferredLanguage(const std::string &language, int32_t index)
267 {
268 if (!IsValidTag(language)) {
269 HILOG_ERROR_I18N("PreferredLanguage::AddPreferredLanguage %{public}s is not valid language tag.",
270 language.c_str());
271 return I18nErrorCode::INVALID_LANGUAGE_TAG;
272 }
273 std::vector<std::string> preferredLanguages;
274 I18nErrorCode status = I18nErrorCode::SUCCESS;
275 if (FindLanguage(language) == -1) {
276 // Case: language not in current preferred language list.
277 AddNonExistPreferredLanguage(language, index, preferredLanguages, status);
278 } else {
279 // Case: language in current preferred language list.
280 AddExistPreferredLanguage(language, index, preferredLanguages, status);
281 }
282 if (status != I18nErrorCode::SUCCESS) {
283 HILOG_ERROR_I18N("PreferredLanguage::AddPreferredLanguage failed.");
284 return status;
285 }
286 return SetPreferredLanguages(JoinPreferredLanguages(preferredLanguages));
287 }
288
RemovePreferredLanguage(int32_t index)289 I18nErrorCode PreferredLanguage::RemovePreferredLanguage(int32_t index)
290 {
291 std::vector<std::string> preferredLanguages = GetPreferredLanguageList();
292 if (preferredLanguages.size() == 1) {
293 HILOG_ERROR_I18N("PreferredLanguage::RemovePreferredLanguage can't remove the only language.");
294 return I18nErrorCode::REMOVE_PREFERRED_LANGUAGE_FAILED;
295 }
296 // valid index is [0, preferredLanguages.size() - 1] for Remove
297 int32_t validIndex = NormalizeIndex(index, preferredLanguages.size() - 1);
298 preferredLanguages.erase(preferredLanguages.begin() + validIndex);
299 // The first language in preferred language list is system language, therefor when first language changed
300 // in preferred language list, we need to reset system language.
301 if (validIndex == 0) {
302 if (LocaleConfig::SetSystemLanguage(preferredLanguages[0]) != I18nErrorCode::SUCCESS) {
303 HILOG_ERROR_I18N("PreferredLanguage::RemovePreferredLanguage update system language failed.");
304 return I18nErrorCode::REMOVE_PREFERRED_LANGUAGE_FAILED;
305 }
306 }
307 return SetPreferredLanguages(JoinPreferredLanguages(preferredLanguages));
308 }
309
AddNonExistPreferredLanguage(const std::string & language,int32_t index,std::vector<std::string> & preferredLanguages,I18nErrorCode & errCode)310 void PreferredLanguage::AddNonExistPreferredLanguage(const std::string& language, int32_t index,
311 std::vector<std::string> &preferredLanguages, I18nErrorCode &errCode)
312 {
313 // valid index is [0, GetPreferredLanguageList().size()] for add non-exist language.
314 int32_t validIndex = NormalizeIndex(index, GetPreferredLanguageList().size());
315 preferredLanguages = GetPreferredLanguageList();
316 preferredLanguages.insert(preferredLanguages.begin() + validIndex, language);
317 // The first language in preferred language list is system language, therefor when first language changed
318 // in preferred language list, we need to reset system language.
319 if (validIndex == 0) {
320 if (LocaleConfig::SetSystemLanguage(preferredLanguages[0]) != I18nErrorCode::SUCCESS) {
321 HILOG_ERROR_I18N("PreferredLanguage::AddNonExistPreferredLanguage update system language failed.");
322 errCode = I18nErrorCode::ADD_PREFERRED_LANGUAGE_NON_EXIST_FAILED;
323 return;
324 }
325 }
326 errCode = I18nErrorCode::SUCCESS;
327 }
328
AddExistPreferredLanguage(const std::string & language,int32_t index,std::vector<std::string> & preferredLanguages,I18nErrorCode & errCode)329 void PreferredLanguage::AddExistPreferredLanguage(const std::string& language, int32_t index,
330 std::vector<std::string> &preferredLanguages, I18nErrorCode &errCode)
331 {
332 // throw error when current index is same with target index.
333 // valid index is [0, GetPreferredLanguageList().size() - 1] for add exist language.
334 int32_t validIndex = NormalizeIndex(index, GetPreferredLanguageList().size() - 1);
335 int32_t languageIdx = FindLanguage(language);
336 if (languageIdx == validIndex) {
337 errCode = I18nErrorCode::ADD_PREFERRED_LANGUAGE_EXIST_FAILED;
338 return;
339 }
340 // Move language from languageIdx to validIdx.
341 preferredLanguages = GetPreferredLanguageList();
342 preferredLanguages.erase(preferredLanguages.begin() + languageIdx);
343 preferredLanguages.insert(preferredLanguages.begin() + validIndex, language);
344 // The first language in preferred language list is system language, therefor when first language changed
345 // in preferred language list, we need to reset system language.
346 if (languageIdx == 0 || validIndex == 0) {
347 if (LocaleConfig::SetSystemLanguage(preferredLanguages[0]) != I18nErrorCode::SUCCESS) {
348 HILOG_ERROR_I18N("PreferredLanguage::AddExistPreferredLanguage update system language failed.");
349 errCode = I18nErrorCode::ADD_PREFERRED_LANGUAGE_EXIST_FAILED;
350 return;
351 }
352 }
353 errCode = I18nErrorCode::SUCCESS;
354 }
355
NormalizeIndex(int32_t index,int32_t max)356 int32_t PreferredLanguage::NormalizeIndex(int32_t index, int32_t max)
357 {
358 if (index <= 0) {
359 return 0;
360 }
361 if (index >= max) {
362 return max;
363 }
364 return index;
365 }
366
367 // Query the index of language in system preferred language list.
FindLanguage(const std::string & language)368 int32_t PreferredLanguage::FindLanguage(const std::string &language)
369 {
370 std::vector<std::string> preferredLanguageList = GetPreferredLanguageList();
371 for (size_t i = 0; i < preferredLanguageList.size(); ++i) {
372 if (preferredLanguageList[i] == language) {
373 return static_cast<int32_t>(i);
374 }
375 }
376 return -1;
377 }
378
379 // Join preferred language tags to string with ';'
JoinPreferredLanguages(const std::vector<std::string> preferredLanguages)380 std::string PreferredLanguage::JoinPreferredLanguages(const std::vector<std::string> preferredLanguages)
381 {
382 std::string result = "";
383 for (size_t i = 0; i < preferredLanguages.size(); ++i) {
384 result += preferredLanguages[i];
385 result += ";";
386 }
387 // delete the last ';'
388 result.pop_back();
389 return result;
390 }
391
392 // Set PREFERRED_LANGUAGES system parameter with preferredLanguages.
SetPreferredLanguages(const std::string & preferredLanguages)393 I18nErrorCode PreferredLanguage::SetPreferredLanguages(const std::string &preferredLanguages)
394 {
395 // System parameter value's length can't beyong CONFIG_LEN
396 if (preferredLanguages.length() > CONFIG_LEN) {
397 HILOG_ERROR_I18N("PreferredLanguage::SetPreferredLanguage preferred language list is too long.");
398 return I18nErrorCode::UPDATE_SYSTEM_PREFERRED_LANGUAGE_FAILED;
399 }
400 if (SetParameter(PREFERRED_LANGUAGES, preferredLanguages.data()) != 0) {
401 HILOG_ERROR_I18N("PreferredLanguage::AddPreferredLanguage udpate preferred language param failed.");
402 return I18nErrorCode::UPDATE_SYSTEM_PREFERRED_LANGUAGE_FAILED;
403 }
404 return I18nErrorCode::SUCCESS;
405 }
406 } // namespace I18n
407 } // namespace Global
408 } // namespace OHOS