• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 #include "language_ui.h"
16 
17 #include "json_node.h"
18 #include "log/log.h"
19 #include "utils.h"
20 #include "misc_info/misc_info.h"
21 
22 namespace Updater {
23 namespace Lang {
24 constexpr int MIN_LVL = 0; // 0 : min resource level
25 constexpr int MAX_LVL = 2; // 2 : max resource level
26 constexpr const char *DEFAULT_KEY = "DEFAULT_STRING";
27 
28 // map value zh/en/spa is used in string.json to specify language type for each string key
29 std::unordered_map<Language, std::string> g_languageDataVars = {
30     {Language::CHINESE, "zh"},
31     {Language::ENGLISH, "en"},
32     {Language::SPANISH, "spa"},
33 };
34 
35 // map key zh/es/en is used in locale file to specify locale env for updater
36 const std::unordered_map<std::string, Language> LanguageUI::LOCALES {
37     {"zh", Language::CHINESE},
38     {"en", Language::ENGLISH},
39     {"es", Language::SPANISH}
40 };
41 
LanguageUI()42 LanguageUI::LanguageUI() : strMap_ {}, res_ {}, langRes_ {}, language_ {Language::ENGLISH}
43 {
44     res_.resize(MAX_LVL + 1);
45 }
46 
SetDefaultLanguage(Language language)47 void LanguageUI::SetDefaultLanguage(Language language)
48 {
49     defaultLanguage_ = language;
50 }
51 
GetInstance()52 LanguageUI &LanguageUI::GetInstance()
53 {
54     static LanguageUI instance;
55     return instance;
56 }
57 
Init(Language language)58 bool LanguageUI::Init(Language language)
59 {
60     language_ = language;
61     if (!Parse()) {
62         LOG(ERROR) << "parse language resources failed";
63         return false;
64     }
65     return true;
66 }
67 
SetRes(const Res & res)68 bool LanguageUI::SetRes(const Res &res)
69 {
70     if (!CheckLevel(res.level)) {
71         return false;
72     }
73     res_[res.level] = res.path;
74     return true;
75 }
76 
Parse()77 bool LanguageUI::Parse()
78 {
79     strMap_.clear();
80     for (auto &file : res_) {
81         if (file.empty()) {
82             LOG(WARNING) << "file name empty";
83             continue;
84         }
85         if (!ParseJson(file)) {
86             LOG(ERROR) << "parse file " << file << " error";
87             return false;
88         }
89     }
90     return true;
91 }
92 
ParseJson(const std::string & file)93 bool LanguageUI::ParseJson(const std::string &file)
94 {
95     JsonNode root {std::filesystem::path { file }};
96     /*
97      * an example:
98      *	{
99      *      "LABEL_REBOOT_DEVICE": {
100      *            "zh" : "",
101      *            "en" : "",
102      *            "spa" : ""
103      *      }
104      *  }
105      *  , this is an object node
106      */
107     if (root.Type() != NodeType::OBJECT) {
108         LOG(ERROR) << file << " is invalid, nodetype is " << static_cast<int>(root.Type());
109         return false;
110     }
111     for (auto &node : root) {
112         const JsonNode &strNode = node.get();
113         std::string key = strNode.Key().value_or("");
114         if (key.empty()) {
115             LOG(ERROR) << "key is empty";
116             return false;
117         }
118         if (auto optionalStr = strNode[g_languageDataVars[language_]].As<std::string>();
119             optionalStr != std::nullopt) {
120             strMap_[key] = *optionalStr;
121             continue;
122         }
123         LOG(ERROR) << "dont have a " << g_languageDataVars[language_] << " string";
124         return false;
125     }
126     return true;
127 }
128 
CheckLevel(int level)129 bool LanguageUI::CheckLevel(int level)
130 {
131     if (level < MIN_LVL || level > MAX_LVL) {
132         LOG(ERROR) << "level invalid : " << level;
133         return false;
134     }
135     return true;
136 }
137 
Translate(const std::string & key) const138 const std::string &LanguageUI::Translate(const std::string &key) const
139 {
140     static std::string emptyStr;
141     if (auto it = strMap_.find(key); it != strMap_.end() && !it->second.empty()) {
142         return it->second;
143     }
144     if (auto it = strMap_.find(DEFAULT_KEY); it != strMap_.end()) {
145         return it->second;
146     }
147     return emptyStr;
148 }
149 
LoadLangRes(const JsonNode & node)150 bool LanguageUI::LoadLangRes(const JsonNode &node)
151 {
152     langRes_ = {};
153     if (!Visit<SETVAL>(node[LANG_RES_KEY], langRes_)) {
154         LOG(ERROR) << "parse language res error";
155         return false;
156     }
157     // clear resources
158     std::vector<std::string>{3, ""}.swap(res_);
159     // load resources
160     for (auto &res : langRes_.res) {
161         if (!SetRes(res)) {
162             return false;
163         }
164     }
165     if (!Init(ParseLanguage())) {
166         LOG(ERROR) << "init failed";
167         return false;
168     }
169     LOG(INFO) << "load language resource success";
170     return true;
171 }
172 
ParseLangIfZh(const std::string & globalLang) const173 Language LanguageUI::ParseLangIfZh(const std::string &globalLang) const
174 {
175     const std::string ZH_CN_REGION_UNDERLINE_PREFIX = "zh_CN";
176     const std::string ZH_CN_REGION_DASH_PREFIX = "zh-CN";
177     const std::string CN_REGION_UNDERLINE_SUFFIX = "_CN";
178     const std::string CN_REGION_DASH_SUFFIX = "-CN";
179 
180     if (globalLang.empty()) {
181         LOG(ERROR) << "globalLang is empty";
182         return Language::CHINESE;
183     }
184     if (globalLang.find("-") == std::string::npos && globalLang.find("_") == std::string::npos) {
185         LOG(ERROR) << "globalLang doesn't contain '-' or '_'";
186         return Language::CHINESE;
187     }
188     if (globalLang.find(ZH_CN_REGION_UNDERLINE_PREFIX) == 0 || globalLang.find(ZH_CN_REGION_DASH_PREFIX) == 0) {
189         LOG(INFO) << "starts with zh_CN or zh-CN";
190         return Language::CHINESE;
191     }
192     if ((globalLang.size() >= CN_REGION_DASH_SUFFIX.size()) &&
193         (globalLang.rfind(CN_REGION_DASH_SUFFIX) == globalLang.size() - CN_REGION_DASH_SUFFIX.size() ||
194         globalLang.rfind(CN_REGION_UNDERLINE_SUFFIX) == globalLang.size() - CN_REGION_UNDERLINE_SUFFIX.size())) {
195         LOG(INFO) << "ends with _CN or -CN";
196         return Language::CHINESE;
197     }
198     return Language::ENGLISH;
199 }
200 
ParseLanguage() const201 Language LanguageUI::ParseLanguage() const
202 {
203     Language DEFAULT_LOCALE = defaultLanguage_;
204 #ifndef UPDATER_UT
205     // read locale type(en-Latn-CN/zh-Hans-CN) from misc
206     constexpr const char *CHINESE_LANGUAGE_PREFIX = "zh";
207     constexpr const char *SPANISH_LANGUAGE_PREFIX = "es";
208     struct UpdaterPara para {};
209     if (!ReadUpdaterParaMisc(para)) {
210         LOG(ERROR) << "ReadUpdaterParaMisc failed";
211         return DEFAULT_LOCALE;
212     }
213     if (strcmp(para.language, "") == 0) {
214         LOG(INFO) << "Language in misc is empty";
215         return Language::CHINESE;
216     } else if (strncmp(para.language, CHINESE_LANGUAGE_PREFIX, strlen(CHINESE_LANGUAGE_PREFIX)) == 0) {
217         LOG(INFO) << "para language starts with zh";
218         return ParseLangIfZh(para.language);
219     } else if (strncmp(para.language, SPANISH_LANGUAGE_PREFIX, strlen(SPANISH_LANGUAGE_PREFIX)) == 0) {
220         LOG(INFO) << "parsed language is Spanish";
221         return Language::SPANISH;
222     } else {
223         LOG(INFO) << "parsed language is English";
224         return Language::ENGLISH;
225     }
226 #endif
227     constexpr size_t localeLen = 2; // zh|es|en
228     std::string realPath {};
229     if (!Utils::PathToRealPath(langRes_.localeFile, realPath)) {
230         LOG(ERROR) << "get real path failed";
231         return DEFAULT_LOCALE;
232     }
233 
234     std::ifstream ifs(realPath);
235     std::string content {std::istreambuf_iterator<char> {ifs}, {}};
236     const std::string &locale = content.substr(0, localeLen);
237     if (auto it = LOCALES.find(locale); it != LOCALES.end()) {
238         return it->second;
239     }
240     LOG(ERROR) << "locale " << locale << " not recognized";
241     return DEFAULT_LOCALE;
242 }
243 
GetCurLanguage() const244 Language LanguageUI::GetCurLanguage() const
245 {
246     return language_;
247 }
248 }
249 } // namespace Updater
250