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
GetInstance()47 LanguageUI &LanguageUI::GetInstance()
48 {
49 static LanguageUI instance;
50 return instance;
51 }
52
Init(Language language)53 bool LanguageUI::Init(Language language)
54 {
55 language_ = language;
56 if (!Parse()) {
57 LOG(ERROR) << "parse language resources failed";
58 return false;
59 }
60 return true;
61 }
62
SetRes(const Res & res)63 bool LanguageUI::SetRes(const Res &res)
64 {
65 if (!CheckLevel(res.level)) {
66 return false;
67 }
68 res_[res.level] = res.path;
69 return true;
70 }
71
Parse()72 bool LanguageUI::Parse()
73 {
74 strMap_.clear();
75 for (auto &file : res_) {
76 if (file.empty()) {
77 LOG(WARNING) << "file name empty";
78 continue;
79 }
80 if (!ParseJson(file)) {
81 LOG(ERROR) << "parse file " << file << " error";
82 return false;
83 }
84 }
85 return true;
86 }
87
ParseJson(const std::string & file)88 bool LanguageUI::ParseJson(const std::string &file)
89 {
90 JsonNode root {std::filesystem::path { file }};
91 /*
92 * an example:
93 * {
94 * "LABEL_REBOOT_DEVICE": {
95 * "zh" : "",
96 * "en" : "",
97 * "spa" : ""
98 * }
99 * }
100 * , this is an object node
101 */
102 if (root.Type() != NodeType::OBJECT) {
103 LOG(ERROR) << file << " is invalid, nodetype is " << static_cast<int>(root.Type());
104 return false;
105 }
106 for (auto &node : root) {
107 const JsonNode &strNode = node.get();
108 std::string key = strNode.Key().value_or("");
109 if (key.empty()) {
110 LOG(ERROR) << "key is empty";
111 return false;
112 }
113 if (auto optionalStr = strNode[g_languageDataVars[language_]].As<std::string>();
114 optionalStr != std::nullopt) {
115 strMap_[key] = *optionalStr;
116 continue;
117 }
118 LOG(ERROR) << "dont have a " << g_languageDataVars[language_] << " string";
119 return false;
120 }
121 return true;
122 }
123
CheckLevel(int level)124 bool LanguageUI::CheckLevel(int level)
125 {
126 if (level < MIN_LVL || level > MAX_LVL) {
127 LOG(ERROR) << "level invalid : " << level;
128 return false;
129 }
130 return true;
131 }
132
Translate(const std::string & key) const133 const std::string &LanguageUI::Translate(const std::string &key) const
134 {
135 static std::string emptyStr;
136 if (auto it = strMap_.find(key); it != strMap_.end() && !it->second.empty()) {
137 return it->second;
138 }
139 if (auto it = strMap_.find(DEFAULT_KEY); it != strMap_.end()) {
140 return it->second;
141 }
142 return emptyStr;
143 }
144
LoadLangRes(const JsonNode & node)145 bool LanguageUI::LoadLangRes(const JsonNode &node)
146 {
147 langRes_ = {};
148 if (!Visit<SETVAL>(node[LANG_RES_KEY], langRes_)) {
149 LOG(ERROR) << "parse language res error";
150 return false;
151 }
152 // clear resources
153 std::vector<std::string>{3, ""}.swap(res_);
154 // load resources
155 for (auto &res : langRes_.res) {
156 if (!SetRes(res)) {
157 return false;
158 }
159 }
160 if (!Init(ParseLanguage())) {
161 LOG(ERROR) << "init failed";
162 return false;
163 }
164 LOG(INFO) << "load language resource success";
165 return true;
166 }
167
ParseLanguage() const168 Language LanguageUI::ParseLanguage() const
169 {
170 constexpr Language DEFAULT_LOCALE = Language::ENGLISH;
171 #ifndef UPDATER_UT
172 //read language type(en-Latn-US/zh-Hans) from misc
173 constexpr const char *CHINSES_LANGUAGE_PREFIX = "zh";
174 constexpr const char *ENGLISH_LANGUAGE_PREFIX = "en";
175 struct UpdaterPara para {};
176 if (!ReadUpdaterParaMisc(para)) {
177 LOG(ERROR) << "ReadUpdaterParaMisc failed";
178 return DEFAULT_LOCALE;
179 }
180 if (strcmp(para.language, "") == 0) {
181 LOG(INFO) << "Language in misc is empty";
182 return Language::CHINESE;
183 } else if (strncmp(para.language, CHINSES_LANGUAGE_PREFIX, strlen(CHINSES_LANGUAGE_PREFIX)) == 0) {
184 return Language::CHINESE;
185 } else if (strncmp(para.language, ENGLISH_LANGUAGE_PREFIX, strlen(ENGLISH_LANGUAGE_PREFIX)) == 0) {
186 return Language::ENGLISH;
187 }
188 #endif
189 constexpr size_t localeLen = 2; // zh|es|en
190 std::string realPath {};
191 if (!Utils::PathToRealPath(langRes_.localeFile, realPath)) {
192 LOG(ERROR) << "get real path failed";
193 return DEFAULT_LOCALE;
194 }
195
196 std::ifstream ifs(realPath);
197 std::string content {std::istreambuf_iterator<char> {ifs}, {}};
198 const std::string &locale = content.substr(0, localeLen);
199 if (auto it = LOCALES.find(locale); it != LOCALES.end()) {
200 return it->second;
201 }
202 LOG(ERROR) << "locale " << locale << " not recognized";
203 return DEFAULT_LOCALE;
204 }
205
GetCurLanguage() const206 Language LanguageUI::GetCurLanguage() const
207 {
208 return language_;
209 }
210 }
211 } // namespace Updater
212