• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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