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