1 /*
2 * Copyright (c) 2023 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 <filesystem>
17 #include <sys/stat.h>
18 #include <string.h>
19 #include "hilog/log.h"
20 #include "libxml/globals.h"
21 #include "libxml/tree.h"
22 #include "libxml/xmlstring.h"
23 #include "locale_compare.h"
24 #include "taboo.h"
25
26 namespace OHOS {
27 namespace Global {
28 namespace I18n {
29 static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, 0xD001E00, "Taboo" };
30 using namespace OHOS::HiviewDFX;
31 const char* Taboo::ROOT_TAG = "taboo";
32 const char* Taboo::ITEM_TAG = "item";
33 const char* Taboo::NAME_TAG = "name";
34 const char* Taboo::VALUE_TAG = "value";
35
Taboo()36 Taboo::Taboo()
37 {
38 }
39
Taboo(const std::string & path)40 Taboo::Taboo(const std::string& path) : tabooDataPath(path)
41 {
42 using std::filesystem::directory_iterator;
43
44 std::string tabooConfigFilePath = tabooDataPath + tabooConfigFileName;
45 struct stat s;
46 isTabooDataExist = stat(tabooConfigFilePath.c_str(), &s) == 0;
47 if (isTabooDataExist) {
48 // parse taboo-config.xml to obtain supported regions and languages for name replacement.
49 ParseTabooData(tabooConfigFilePath, DataFileType::CONFIG_FILE);
50 ReadResourceList();
51 }
52 }
53
~Taboo()54 Taboo::~Taboo()
55 {
56 }
57
ReplaceCountryName(const std::string & region,const std::string & displayLanguage,const std::string & name)58 std::string Taboo::ReplaceCountryName(const std::string& region, const std::string& displayLanguage,
59 const std::string& name)
60 {
61 if (!isTabooDataExist) {
62 HiLog::Info(LABEL, "Taboo::ReplaceCountryName Taboo data not exist.");
63 return name;
64 }
65 if (supportedRegions.find(region) == supportedRegions.end()) {
66 HiLog::Info(LABEL, "Taboo::ReplaceCountryName taboo data don't support region %{public}s", region.c_str());
67 return name;
68 }
69 std::string key = regionKey + region;
70 std::vector<std::string> fallbackRegionKeys = QueryKeyFallBack(key);
71 std::string fallbackLanguage;
72 std::string fileName;
73 std::tie(fallbackLanguage, fileName) = LanguageFallBack(displayLanguage);
74 if (fallbackLanguage.length() == 0) {
75 HiLog::Info(LABEL, "Taboo::ReplaceCountryName language %{public}s fallback failed", displayLanguage.c_str());
76 return name;
77 }
78 if (localeTabooData.find(fallbackLanguage) == localeTabooData.end()) {
79 localeTabooData[fallbackLanguage] = {};
80 std::string localeTabooDataFilePath = tabooDataPath + fileName + filePathSplitor + tabooLocaleDataFileName;
81 ParseTabooData(localeTabooDataFilePath, DataFileType::DATA_FILE, fallbackLanguage);
82 }
83 auto tabooData = localeTabooData[fallbackLanguage];
84 for (auto it = fallbackRegionKeys.begin(); it != fallbackRegionKeys.end(); ++it) {
85 if (tabooData.find(*it) != tabooData.end()) {
86 return tabooData[*it];
87 }
88 }
89 HiLog::Info(LABEL, "Taboo::ReplaceCountryName not find taboo data correspond to region %{public}s",
90 region.c_str());
91 return name;
92 }
93
ReplaceLanguageName(const std::string & language,const std::string & displayLanguage,const std::string & name)94 std::string Taboo::ReplaceLanguageName(const std::string& language, const std::string& displayLanguage,
95 const std::string& name)
96 {
97 if (!isTabooDataExist) {
98 HiLog::Info(LABEL, "Taboo::ReplaceLanguageName Taboo data not exist.");
99 return name;
100 }
101 if (supportedLanguages.find(language) == supportedLanguages.end()) {
102 HiLog::Error(LABEL, "Taboo::ReplaceLanguageName taboo data don't support language %{public}s",
103 language.c_str());
104 return name;
105 }
106 std::string key = languageKey + language;
107 std::vector<std::string> fallbackLanguageKeys = QueryKeyFallBack(key);
108 std::string fallbackLanguage;
109 std::string fileName;
110 std::tie(fallbackLanguage, fileName) = LanguageFallBack(displayLanguage);
111 if (fallbackLanguage.size() == 0) {
112 HiLog::Error(LABEL, "Taboo::ReplaceLanguageName language %{public}s fallback failed", displayLanguage.c_str());
113 return name;
114 }
115 if (localeTabooData.find(fallbackLanguage) == localeTabooData.end()) {
116 localeTabooData[fallbackLanguage] = {};
117 std::string localeTabooDataFilePath = tabooDataPath + fileName + filePathSplitor + tabooLocaleDataFileName;
118 ParseTabooData(localeTabooDataFilePath, DataFileType::DATA_FILE, fallbackLanguage);
119 }
120 auto tabooData = localeTabooData[fallbackLanguage];
121 for (auto it = fallbackLanguageKeys.begin(); it != fallbackLanguageKeys.end(); ++it) {
122 if (tabooData.find(*it) != tabooData.end()) {
123 return tabooData[*it];
124 }
125 }
126 HiLog::Error(LABEL, "Taboo::ReplaceLanguageName not find taboo data correspond to language %{public}s",
127 language.c_str());
128 return name;
129 }
130
ParseTabooData(const std::string & path,DataFileType fileType,const std::string & locale)131 void Taboo::ParseTabooData(const std::string& path, DataFileType fileType, const std::string& locale)
132 {
133 xmlKeepBlanksDefault(0);
134 xmlDocPtr doc = xmlParseFile(path.c_str());
135 if (doc == nullptr) {
136 HiLog::Error(LABEL, "Taboo parse taboo data file failed: %{public}s", path.c_str());
137 return;
138 }
139 xmlNodePtr cur = xmlDocGetRootElement(doc);
140 if (cur == nullptr || xmlStrcmp(cur->name, reinterpret_cast<const xmlChar*>(ROOT_TAG)) != 0) {
141 xmlFreeDoc(doc);
142 HiLog::Error(LABEL, "Taboo get root tag from taboo data file failed: %{public}s", path.c_str());
143 return;
144 }
145 cur = cur->xmlChildrenNode;
146 const xmlChar* nameTag = reinterpret_cast<const xmlChar*>(NAME_TAG);
147 const xmlChar* valueTag = reinterpret_cast<const xmlChar*>(VALUE_TAG);
148 while (cur != nullptr && xmlStrcmp(cur->name, reinterpret_cast<const xmlChar*>(ITEM_TAG)) == 0) {
149 xmlChar* name = xmlGetProp(cur, nameTag);
150 xmlChar* value = xmlGetProp(cur, valueTag);
151 if (name == nullptr || value == nullptr) {
152 HiLog::Error(LABEL, "Taboo get name and value property failed: %{public}s", path.c_str());
153 cur = cur->next;
154 continue;
155 }
156 std::string nameStr = reinterpret_cast<const char*>(name);
157 std::string valueStr = reinterpret_cast<const char*>(value);
158 switch (fileType) {
159 case DataFileType::CONFIG_FILE:
160 ProcessTabooConfigData(nameStr, valueStr);
161 break;
162 case DataFileType::DATA_FILE:
163 ProcessTabooLocaleData(locale, nameStr, valueStr);
164 break;
165 }
166 xmlFree(name);
167 xmlFree(value);
168 cur = cur->next;
169 }
170 }
171
ProcessTabooConfigData(const std::string & name,const std::string & value)172 void Taboo::ProcessTabooConfigData(const std::string& name, const std::string& value)
173 {
174 if (name.compare(supportedRegionsTag) == 0) {
175 SplitValue(value, supportedRegions);
176 } else if (name.compare(supportedLanguagesTag) == 0) {
177 SplitValue(value, supportedLanguages);
178 }
179 }
180
ProcessTabooLocaleData(const std::string & locale,const std::string & name,const std::string & value)181 void Taboo::ProcessTabooLocaleData(const std::string& locale, const std::string& name, const std::string& value)
182 {
183 if (localeTabooData.find(locale) != localeTabooData.end()) {
184 localeTabooData[locale][name] = value;
185 } else {
186 std::map<std::string, std::string> data;
187 data[name] = value;
188 localeTabooData[locale] = data;
189 }
190 }
191
SplitValue(const std::string & value,std::set<std::string> & collation)192 void Taboo::SplitValue(const std::string& value, std::set<std::string>& collation)
193 {
194 size_t startPos = 0;
195 while (startPos < value.length()) {
196 size_t endPos = value.find(tabooDataSplitor, startPos);
197 if (endPos == std::string::npos) {
198 collation.insert(value.substr(startPos));
199 endPos = value.length();
200 } else {
201 collation.insert(value.substr(startPos, endPos - startPos));
202 }
203 startPos = endPos + 1;
204 }
205 }
206
QueryKeyFallBack(const std::string & key)207 std::vector<std::string> Taboo::QueryKeyFallBack(const std::string& key)
208 {
209 std::vector<std::string> fallback;
210 fallback.push_back(key + "_r_all");
211 return fallback;
212 }
213
LanguageFallBack(const std::string & language)214 std::tuple<std::string, std::string> Taboo::LanguageFallBack(const std::string& language)
215 {
216 std::string bestMatch;
217 std::string fileName;
218 int32_t bestScore = -1;
219
220 for (auto it = resources.begin(); it != resources.end(); ++it) {
221 std::string resLanguage = it->first;
222 int32_t score = LocaleCompare::Compare(language, resLanguage);
223 if (score > bestScore) {
224 bestMatch = resLanguage;
225 fileName = it->second;
226 bestScore = score;
227 }
228 }
229 if (bestScore < 0) {
230 return std::make_tuple("", "");
231 }
232 return std::make_tuple(bestMatch, fileName);
233 }
234
ReadResourceList()235 void Taboo::ReadResourceList()
236 {
237 using std::filesystem::directory_iterator;
238 struct stat s;
239 for (const auto &dirEntry : directory_iterator{tabooDataPath}) {
240 std::string path = dirEntry.path();
241 if (stat(path.c_str(), &s) != 0) {
242 HiLog::Error(LABEL, "get path status failed");
243 continue;
244 }
245 if (s.st_mode & S_IFDIR) {
246 std::string fileName = path.substr(tabooDataPath.length());
247 std::string language = GetLanguageFromFileName(fileName);
248 resources[language] = fileName;
249 }
250 }
251 }
252
GetLanguageFromFileName(const std::string & fileName)253 std::string Taboo::GetLanguageFromFileName(const std::string& fileName)
254 {
255 if (fileName.length() == DIR_XML_LENGTH) {
256 return "en";
257 }
258 std::string language = fileName.substr(4);
259 if (language[0] == 'b' && language[1] == '+') {
260 language = language.substr(LANGUAGE_START_INDEX);
261 }
262 size_t pos = language.find("+");
263 if (pos != std::string::npos) {
264 language = language.replace(pos, 1, "-");
265 }
266 pos = language.find("-r");
267 if (pos != std::string::npos) {
268 language = language.replace(pos, LANGUAGE_START_INDEX, "-");
269 }
270 return language;
271 }
272 } // namespace I18n
273 } // namespace Global
274 } // namespace OHOS