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 22namespace Updater { 23namespace Lang { 24constexpr int MIN_LVL = 0; // 0 : min resource level 25constexpr int MAX_LVL = 2; // 2 : max resource level 26constexpr 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 29std::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 36const std::unordered_map<std::string, Language> LanguageUI::LOCALES { 37 {"zh", Language::CHINESE}, 38 {"en", Language::ENGLISH}, 39 {"es", Language::SPANISH} 40}; 41 42LanguageUI::LanguageUI() : strMap_ {}, res_ {}, langRes_ {}, language_ {Language::ENGLISH} 43{ 44 res_.resize(MAX_LVL + 1); 45} 46 47void LanguageUI::SetDefaultLanguage(Language language) 48{ 49 defaultLanguage_ = language; 50} 51 52LanguageUI &LanguageUI::GetInstance() 53{ 54 static LanguageUI instance; 55 return instance; 56} 57 58bool 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 68bool 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 77bool 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 93bool 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 129bool 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 138const 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 150bool 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 173Language 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 211Language LanguageUI::GetCurLanguage() const 212{ 213 return language_; 214} 215} 216} // namespace Updater 217