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
ParseLanguage() const173 Language LanguageUI::ParseLanguage() const
174 {
175 Language DEFAULT_LOCALE = defaultLanguage_;
176 #ifndef UPDATER_UT
177 //read language type(en-Latn-US/zh-Hans) from misc
178 constexpr const char *CHINSES_LANGUAGE_PREFIX = "zh";
179 constexpr const char *ENGLISH_LANGUAGE_PREFIX = "en";
180 struct UpdaterPara para {};
181 if (!ReadUpdaterParaMisc(para)) {
182 LOG(ERROR) << "ReadUpdaterParaMisc failed";
183 return DEFAULT_LOCALE;
184 }
185 if (strcmp(para.language, "") == 0) {
186 LOG(INFO) << "Language in misc is empty";
187 return Language::CHINESE;
188 } else if (strncmp(para.language, CHINSES_LANGUAGE_PREFIX, strlen(CHINSES_LANGUAGE_PREFIX)) == 0) {
189 return Language::CHINESE;
190 } else if (strncmp(para.language, ENGLISH_LANGUAGE_PREFIX, strlen(ENGLISH_LANGUAGE_PREFIX)) == 0) {
191 return Language::ENGLISH;
192 }
193 #endif
194 constexpr size_t localeLen = 2; // zh|es|en
195 std::string realPath {};
196 if (!Utils::PathToRealPath(langRes_.localeFile, realPath)) {
197 LOG(ERROR) << "get real path failed";
198 return DEFAULT_LOCALE;
199 }
200
201 std::ifstream ifs(realPath);
202 std::string content {std::istreambuf_iterator<char> {ifs}, {}};
203 const std::string &locale = content.substr(0, localeLen);
204 if (auto it = LOCALES.find(locale); it != LOCALES.end()) {
205 return it->second;
206 }
207 LOG(ERROR) << "locale " << locale << " not recognized";
208 return DEFAULT_LOCALE;
209 }
210
GetCurLanguage() const211 Language LanguageUI::GetCurLanguage() const
212 {
213 return language_;
214 }
215 }
216 } // namespace Updater
217