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
21 namespace Updater {
22 namespace Lang {
23 constexpr int MIN_LVL = 0; // 0 : min resource level
24 constexpr int MAX_LVL = 2; // 2 : max resource level
25 constexpr const char *DEFAULT_KEY = "DEFAULT_STRING";
26
27 // map value zh/en/spa is used in string.json to specify language type for each string key
28 std::unordered_map<Language, std::string> g_languageDataVars = {
29 {Language::CHINESE, "zh"},
30 {Language::ENGLISH, "en"},
31 {Language::SPANISH, "spa"},
32 };
33
34 // map key zh/es/en is used in locale file to specify locale env for updater
35 const std::unordered_map<std::string, Language> LanguageUI::LOCALES {
36 {"zh", Language::CHINESE},
37 {"en", Language::ENGLISH},
38 {"es", Language::SPANISH}
39 };
40
LanguageUI()41 LanguageUI::LanguageUI() : strMap_ {}, res_ {}, langRes_ {}, language_ {Language::ENGLISH}
42 {
43 res_.resize(MAX_LVL + 1);
44 }
45
GetInstance()46 LanguageUI &LanguageUI::GetInstance()
47 {
48 static LanguageUI instance;
49 return instance;
50 }
51
Init(Language language)52 bool LanguageUI::Init(Language language)
53 {
54 language_ = language;
55 if (!Parse()) {
56 LOG(ERROR) << "parse language resources failed";
57 return false;
58 }
59 return true;
60 }
61
SetRes(const Res & res)62 bool LanguageUI::SetRes(const Res &res)
63 {
64 if (!CheckLevel(res.level)) {
65 return false;
66 }
67 res_[res.level] = res.path;
68 return true;
69 }
70
Parse()71 bool LanguageUI::Parse()
72 {
73 strMap_.clear();
74 for (auto &file : res_) {
75 if (file.empty()) {
76 LOG(WARNING) << "file name empty";
77 continue;
78 }
79 if (!ParseJson(file)) {
80 LOG(ERROR) << "parse file " << file << " error";
81 return false;
82 }
83 }
84 return true;
85 }
86
ParseJson(const std::string & file)87 bool LanguageUI::ParseJson(const std::string &file)
88 {
89 JsonNode root {std::filesystem::path { file }};
90 /*
91 * an example:
92 * {
93 * "LABEL_REBOOT_DEVICE": {
94 * "zh" : "",
95 * "en" : "",
96 * "spa" : ""
97 * }
98 * }
99 * , this is an object node
100 */
101 if (root.Type() != NodeType::OBJECT) {
102 LOG(ERROR) << file << " is invalid, nodetype is " << static_cast<int>(root.Type());
103 return false;
104 }
105 for (auto &node : root) {
106 const JsonNode &strNode = node.get();
107 std::string key = strNode.Key().value_or("");
108 if (key.empty()) {
109 LOG(ERROR) << "key is empty";
110 return false;
111 }
112 if (auto optionalStr = strNode[g_languageDataVars[language_]].As<std::string>();
113 optionalStr != std::nullopt) {
114 strMap_[key] = *optionalStr;
115 continue;
116 }
117 LOG(ERROR) << "dont have a " << g_languageDataVars[language_] << " string";
118 return false;
119 }
120 return true;
121 }
122
CheckLevel(int level)123 bool LanguageUI::CheckLevel(int level)
124 {
125 if (level < MIN_LVL || level > MAX_LVL) {
126 LOG(ERROR) << "level invalid : " << level;
127 return false;
128 }
129 return true;
130 }
131
Translate(const std::string & key) const132 const std::string &LanguageUI::Translate(const std::string &key) const
133 {
134 static std::string emptyStr;
135 if (auto it = strMap_.find(key); it != strMap_.end() && !it->second.empty()) {
136 return it->second;
137 }
138 if (auto it = strMap_.find(DEFAULT_KEY); it != strMap_.end()) {
139 return it->second;
140 }
141 return emptyStr;
142 }
143
LoadLangRes(const JsonNode & node)144 bool LanguageUI::LoadLangRes(const JsonNode &node)
145 {
146 langRes_ = {};
147 if (!Visit<SETVAL>(node[LANG_RES_KEY], langRes_)) {
148 LOG(ERROR) << "parse language res error";
149 return false;
150 }
151 // clear resources
152 std::vector<std::string>{3, ""}.swap(res_);
153 // load resources
154 for (auto &res : langRes_.res) {
155 if (!SetRes(res)) {
156 return false;
157 }
158 }
159 if (!Init(ParseLanguage())) {
160 LOG(ERROR) << "init failed";
161 return false;
162 }
163 LOG(INFO) << "load language resource success";
164 return true;
165 }
166
ParseLanguage() const167 Language LanguageUI::ParseLanguage() const
168 {
169 constexpr Language DEFAULT_LOCALE = Language::CHINESE;
170 constexpr size_t localeLen = 2; // zh|es|en
171 std::string realPath {};
172 if (!Utils::PathToRealPath(langRes_.localeFile, realPath)) {
173 LOG(ERROR) << "get real path failed";
174 return DEFAULT_LOCALE;
175 }
176
177 std::ifstream ifs(realPath);
178 std::string content {std::istreambuf_iterator<char> {ifs}, {}};
179 const std::string &locale = content.substr(0, localeLen);
180 if (auto it = LOCALES.find(locale); it != LOCALES.end()) {
181 return it->second;
182 }
183 LOG(ERROR) << "locale " << locale << " not recognized";
184 return DEFAULT_LOCALE;
185 }
186 }
187 } // namespace Updater
188