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