1fb299fa2Sopenharmony_ci/* 2fb299fa2Sopenharmony_ci * Copyright (c) 2022 Huawei Device Co., Ltd. 3fb299fa2Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 4fb299fa2Sopenharmony_ci * you may not use this file except in compliance with the License. 5fb299fa2Sopenharmony_ci * You may obtain a copy of the License at 6fb299fa2Sopenharmony_ci * 7fb299fa2Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 8fb299fa2Sopenharmony_ci * 9fb299fa2Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 10fb299fa2Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 11fb299fa2Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12fb299fa2Sopenharmony_ci * See the License for the specific language governing permissions and 13fb299fa2Sopenharmony_ci * limitations under the License. 14fb299fa2Sopenharmony_ci */ 15fb299fa2Sopenharmony_ci#include "language_ui.h" 16fb299fa2Sopenharmony_ci 17fb299fa2Sopenharmony_ci#include "json_node.h" 18fb299fa2Sopenharmony_ci#include "log/log.h" 19fb299fa2Sopenharmony_ci#include "utils.h" 20fb299fa2Sopenharmony_ci#include "misc_info/misc_info.h" 21fb299fa2Sopenharmony_ci 22fb299fa2Sopenharmony_cinamespace Updater { 23fb299fa2Sopenharmony_cinamespace Lang { 24fb299fa2Sopenharmony_ciconstexpr int MIN_LVL = 0; // 0 : min resource level 25fb299fa2Sopenharmony_ciconstexpr int MAX_LVL = 2; // 2 : max resource level 26fb299fa2Sopenharmony_ciconstexpr const char *DEFAULT_KEY = "DEFAULT_STRING"; 27fb299fa2Sopenharmony_ci 28fb299fa2Sopenharmony_ci// map value zh/en/spa is used in string.json to specify language type for each string key 29fb299fa2Sopenharmony_cistd::unordered_map<Language, std::string> g_languageDataVars = { 30fb299fa2Sopenharmony_ci {Language::CHINESE, "zh"}, 31fb299fa2Sopenharmony_ci {Language::ENGLISH, "en"}, 32fb299fa2Sopenharmony_ci {Language::SPANISH, "spa"}, 33fb299fa2Sopenharmony_ci}; 34fb299fa2Sopenharmony_ci 35fb299fa2Sopenharmony_ci// map key zh/es/en is used in locale file to specify locale env for updater 36fb299fa2Sopenharmony_ciconst std::unordered_map<std::string, Language> LanguageUI::LOCALES { 37fb299fa2Sopenharmony_ci {"zh", Language::CHINESE}, 38fb299fa2Sopenharmony_ci {"en", Language::ENGLISH}, 39fb299fa2Sopenharmony_ci {"es", Language::SPANISH} 40fb299fa2Sopenharmony_ci}; 41fb299fa2Sopenharmony_ci 42fb299fa2Sopenharmony_ciLanguageUI::LanguageUI() : strMap_ {}, res_ {}, langRes_ {}, language_ {Language::ENGLISH} 43fb299fa2Sopenharmony_ci{ 44fb299fa2Sopenharmony_ci res_.resize(MAX_LVL + 1); 45fb299fa2Sopenharmony_ci} 46fb299fa2Sopenharmony_ci 47fb299fa2Sopenharmony_civoid LanguageUI::SetDefaultLanguage(Language language) 48fb299fa2Sopenharmony_ci{ 49fb299fa2Sopenharmony_ci defaultLanguage_ = language; 50fb299fa2Sopenharmony_ci} 51fb299fa2Sopenharmony_ci 52fb299fa2Sopenharmony_ciLanguageUI &LanguageUI::GetInstance() 53fb299fa2Sopenharmony_ci{ 54fb299fa2Sopenharmony_ci static LanguageUI instance; 55fb299fa2Sopenharmony_ci return instance; 56fb299fa2Sopenharmony_ci} 57fb299fa2Sopenharmony_ci 58fb299fa2Sopenharmony_cibool LanguageUI::Init(Language language) 59fb299fa2Sopenharmony_ci{ 60fb299fa2Sopenharmony_ci language_ = language; 61fb299fa2Sopenharmony_ci if (!Parse()) { 62fb299fa2Sopenharmony_ci LOG(ERROR) << "parse language resources failed"; 63fb299fa2Sopenharmony_ci return false; 64fb299fa2Sopenharmony_ci } 65fb299fa2Sopenharmony_ci return true; 66fb299fa2Sopenharmony_ci} 67fb299fa2Sopenharmony_ci 68fb299fa2Sopenharmony_cibool LanguageUI::SetRes(const Res &res) 69fb299fa2Sopenharmony_ci{ 70fb299fa2Sopenharmony_ci if (!CheckLevel(res.level)) { 71fb299fa2Sopenharmony_ci return false; 72fb299fa2Sopenharmony_ci } 73fb299fa2Sopenharmony_ci res_[res.level] = res.path; 74fb299fa2Sopenharmony_ci return true; 75fb299fa2Sopenharmony_ci} 76fb299fa2Sopenharmony_ci 77fb299fa2Sopenharmony_cibool LanguageUI::Parse() 78fb299fa2Sopenharmony_ci{ 79fb299fa2Sopenharmony_ci strMap_.clear(); 80fb299fa2Sopenharmony_ci for (auto &file : res_) { 81fb299fa2Sopenharmony_ci if (file.empty()) { 82fb299fa2Sopenharmony_ci LOG(WARNING) << "file name empty"; 83fb299fa2Sopenharmony_ci continue; 84fb299fa2Sopenharmony_ci } 85fb299fa2Sopenharmony_ci if (!ParseJson(file)) { 86fb299fa2Sopenharmony_ci LOG(ERROR) << "parse file " << file << " error"; 87fb299fa2Sopenharmony_ci return false; 88fb299fa2Sopenharmony_ci } 89fb299fa2Sopenharmony_ci } 90fb299fa2Sopenharmony_ci return true; 91fb299fa2Sopenharmony_ci} 92fb299fa2Sopenharmony_ci 93fb299fa2Sopenharmony_cibool LanguageUI::ParseJson(const std::string &file) 94fb299fa2Sopenharmony_ci{ 95fb299fa2Sopenharmony_ci JsonNode root {std::filesystem::path { file }}; 96fb299fa2Sopenharmony_ci /* 97fb299fa2Sopenharmony_ci * an example: 98fb299fa2Sopenharmony_ci * { 99fb299fa2Sopenharmony_ci * "LABEL_REBOOT_DEVICE": { 100fb299fa2Sopenharmony_ci * "zh" : "", 101fb299fa2Sopenharmony_ci * "en" : "", 102fb299fa2Sopenharmony_ci * "spa" : "" 103fb299fa2Sopenharmony_ci * } 104fb299fa2Sopenharmony_ci * } 105fb299fa2Sopenharmony_ci * , this is an object node 106fb299fa2Sopenharmony_ci */ 107fb299fa2Sopenharmony_ci if (root.Type() != NodeType::OBJECT) { 108fb299fa2Sopenharmony_ci LOG(ERROR) << file << " is invalid, nodetype is " << static_cast<int>(root.Type()); 109fb299fa2Sopenharmony_ci return false; 110fb299fa2Sopenharmony_ci } 111fb299fa2Sopenharmony_ci for (auto &node : root) { 112fb299fa2Sopenharmony_ci const JsonNode &strNode = node.get(); 113fb299fa2Sopenharmony_ci std::string key = strNode.Key().value_or(""); 114fb299fa2Sopenharmony_ci if (key.empty()) { 115fb299fa2Sopenharmony_ci LOG(ERROR) << "key is empty"; 116fb299fa2Sopenharmony_ci return false; 117fb299fa2Sopenharmony_ci } 118fb299fa2Sopenharmony_ci if (auto optionalStr = strNode[g_languageDataVars[language_]].As<std::string>(); 119fb299fa2Sopenharmony_ci optionalStr != std::nullopt) { 120fb299fa2Sopenharmony_ci strMap_[key] = *optionalStr; 121fb299fa2Sopenharmony_ci continue; 122fb299fa2Sopenharmony_ci } 123fb299fa2Sopenharmony_ci LOG(ERROR) << "dont have a " << g_languageDataVars[language_] << " string"; 124fb299fa2Sopenharmony_ci return false; 125fb299fa2Sopenharmony_ci } 126fb299fa2Sopenharmony_ci return true; 127fb299fa2Sopenharmony_ci} 128fb299fa2Sopenharmony_ci 129fb299fa2Sopenharmony_cibool LanguageUI::CheckLevel(int level) 130fb299fa2Sopenharmony_ci{ 131fb299fa2Sopenharmony_ci if (level < MIN_LVL || level > MAX_LVL) { 132fb299fa2Sopenharmony_ci LOG(ERROR) << "level invalid : " << level; 133fb299fa2Sopenharmony_ci return false; 134fb299fa2Sopenharmony_ci } 135fb299fa2Sopenharmony_ci return true; 136fb299fa2Sopenharmony_ci} 137fb299fa2Sopenharmony_ci 138fb299fa2Sopenharmony_ciconst std::string &LanguageUI::Translate(const std::string &key) const 139fb299fa2Sopenharmony_ci{ 140fb299fa2Sopenharmony_ci static std::string emptyStr; 141fb299fa2Sopenharmony_ci if (auto it = strMap_.find(key); it != strMap_.end() && !it->second.empty()) { 142fb299fa2Sopenharmony_ci return it->second; 143fb299fa2Sopenharmony_ci } 144fb299fa2Sopenharmony_ci if (auto it = strMap_.find(DEFAULT_KEY); it != strMap_.end()) { 145fb299fa2Sopenharmony_ci return it->second; 146fb299fa2Sopenharmony_ci } 147fb299fa2Sopenharmony_ci return emptyStr; 148fb299fa2Sopenharmony_ci} 149fb299fa2Sopenharmony_ci 150fb299fa2Sopenharmony_cibool LanguageUI::LoadLangRes(const JsonNode &node) 151fb299fa2Sopenharmony_ci{ 152fb299fa2Sopenharmony_ci langRes_ = {}; 153fb299fa2Sopenharmony_ci if (!Visit<SETVAL>(node[LANG_RES_KEY], langRes_)) { 154fb299fa2Sopenharmony_ci LOG(ERROR) << "parse language res error"; 155fb299fa2Sopenharmony_ci return false; 156fb299fa2Sopenharmony_ci } 157fb299fa2Sopenharmony_ci // clear resources 158fb299fa2Sopenharmony_ci std::vector<std::string>{3, ""}.swap(res_); 159fb299fa2Sopenharmony_ci // load resources 160fb299fa2Sopenharmony_ci for (auto &res : langRes_.res) { 161fb299fa2Sopenharmony_ci if (!SetRes(res)) { 162fb299fa2Sopenharmony_ci return false; 163fb299fa2Sopenharmony_ci } 164fb299fa2Sopenharmony_ci } 165fb299fa2Sopenharmony_ci if (!Init(ParseLanguage())) { 166fb299fa2Sopenharmony_ci LOG(ERROR) << "init failed"; 167fb299fa2Sopenharmony_ci return false; 168fb299fa2Sopenharmony_ci } 169fb299fa2Sopenharmony_ci LOG(INFO) << "load language resource success"; 170fb299fa2Sopenharmony_ci return true; 171fb299fa2Sopenharmony_ci} 172fb299fa2Sopenharmony_ci 173fb299fa2Sopenharmony_ciLanguage LanguageUI::ParseLanguage() const 174fb299fa2Sopenharmony_ci{ 175fb299fa2Sopenharmony_ci Language DEFAULT_LOCALE = defaultLanguage_; 176fb299fa2Sopenharmony_ci#ifndef UPDATER_UT 177fb299fa2Sopenharmony_ci //read language type(en-Latn-US/zh-Hans) from misc 178fb299fa2Sopenharmony_ci constexpr const char *CHINSES_LANGUAGE_PREFIX = "zh"; 179fb299fa2Sopenharmony_ci constexpr const char *ENGLISH_LANGUAGE_PREFIX = "en"; 180fb299fa2Sopenharmony_ci struct UpdaterPara para {}; 181fb299fa2Sopenharmony_ci if (!ReadUpdaterParaMisc(para)) { 182fb299fa2Sopenharmony_ci LOG(ERROR) << "ReadUpdaterParaMisc failed"; 183fb299fa2Sopenharmony_ci return DEFAULT_LOCALE; 184fb299fa2Sopenharmony_ci } 185fb299fa2Sopenharmony_ci if (strcmp(para.language, "") == 0) { 186fb299fa2Sopenharmony_ci LOG(INFO) << "Language in misc is empty"; 187fb299fa2Sopenharmony_ci return Language::CHINESE; 188fb299fa2Sopenharmony_ci } else if (strncmp(para.language, CHINSES_LANGUAGE_PREFIX, strlen(CHINSES_LANGUAGE_PREFIX)) == 0) { 189fb299fa2Sopenharmony_ci return Language::CHINESE; 190fb299fa2Sopenharmony_ci } else if (strncmp(para.language, ENGLISH_LANGUAGE_PREFIX, strlen(ENGLISH_LANGUAGE_PREFIX)) == 0) { 191fb299fa2Sopenharmony_ci return Language::ENGLISH; 192fb299fa2Sopenharmony_ci } 193fb299fa2Sopenharmony_ci#endif 194fb299fa2Sopenharmony_ci constexpr size_t localeLen = 2; // zh|es|en 195fb299fa2Sopenharmony_ci std::string realPath {}; 196fb299fa2Sopenharmony_ci if (!Utils::PathToRealPath(langRes_.localeFile, realPath)) { 197fb299fa2Sopenharmony_ci LOG(ERROR) << "get real path failed"; 198fb299fa2Sopenharmony_ci return DEFAULT_LOCALE; 199fb299fa2Sopenharmony_ci } 200fb299fa2Sopenharmony_ci 201fb299fa2Sopenharmony_ci std::ifstream ifs(realPath); 202fb299fa2Sopenharmony_ci std::string content {std::istreambuf_iterator<char> {ifs}, {}}; 203fb299fa2Sopenharmony_ci const std::string &locale = content.substr(0, localeLen); 204fb299fa2Sopenharmony_ci if (auto it = LOCALES.find(locale); it != LOCALES.end()) { 205fb299fa2Sopenharmony_ci return it->second; 206fb299fa2Sopenharmony_ci } 207fb299fa2Sopenharmony_ci LOG(ERROR) << "locale " << locale << " not recognized"; 208fb299fa2Sopenharmony_ci return DEFAULT_LOCALE; 209fb299fa2Sopenharmony_ci} 210fb299fa2Sopenharmony_ci 211fb299fa2Sopenharmony_ciLanguage LanguageUI::GetCurLanguage() const 212fb299fa2Sopenharmony_ci{ 213fb299fa2Sopenharmony_ci return language_; 214fb299fa2Sopenharmony_ci} 215fb299fa2Sopenharmony_ci} 216fb299fa2Sopenharmony_ci} // namespace Updater 217