/* * Copyright (c) 2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "language_ui.h" #include "json_node.h" #include "log/log.h" #include "utils.h" #include "misc_info/misc_info.h" namespace Updater { namespace Lang { constexpr int MIN_LVL = 0; // 0 : min resource level constexpr int MAX_LVL = 2; // 2 : max resource level constexpr const char *DEFAULT_KEY = "DEFAULT_STRING"; // map value zh/en/spa is used in string.json to specify language type for each string key std::unordered_map g_languageDataVars = { {Language::CHINESE, "zh"}, {Language::ENGLISH, "en"}, {Language::SPANISH, "spa"}, }; // map key zh/es/en is used in locale file to specify locale env for updater const std::unordered_map LanguageUI::LOCALES { {"zh", Language::CHINESE}, {"en", Language::ENGLISH}, {"es", Language::SPANISH} }; LanguageUI::LanguageUI() : strMap_ {}, res_ {}, langRes_ {}, language_ {Language::ENGLISH} { res_.resize(MAX_LVL + 1); } void LanguageUI::SetDefaultLanguage(Language language) { defaultLanguage_ = language; } LanguageUI &LanguageUI::GetInstance() { static LanguageUI instance; return instance; } bool LanguageUI::Init(Language language) { language_ = language; if (!Parse()) { LOG(ERROR) << "parse language resources failed"; return false; } return true; } bool LanguageUI::SetRes(const Res &res) { if (!CheckLevel(res.level)) { return false; } res_[res.level] = res.path; return true; } bool LanguageUI::Parse() { strMap_.clear(); for (auto &file : res_) { if (file.empty()) { LOG(WARNING) << "file name empty"; continue; } if (!ParseJson(file)) { LOG(ERROR) << "parse file " << file << " error"; return false; } } return true; } bool LanguageUI::ParseJson(const std::string &file) { JsonNode root {std::filesystem::path { file }}; /* * an example: * { * "LABEL_REBOOT_DEVICE": { * "zh" : "", * "en" : "", * "spa" : "" * } * } * , this is an object node */ if (root.Type() != NodeType::OBJECT) { LOG(ERROR) << file << " is invalid, nodetype is " << static_cast(root.Type()); return false; } for (auto &node : root) { const JsonNode &strNode = node.get(); std::string key = strNode.Key().value_or(""); if (key.empty()) { LOG(ERROR) << "key is empty"; return false; } if (auto optionalStr = strNode[g_languageDataVars[language_]].As(); optionalStr != std::nullopt) { strMap_[key] = *optionalStr; continue; } LOG(ERROR) << "dont have a " << g_languageDataVars[language_] << " string"; return false; } return true; } bool LanguageUI::CheckLevel(int level) { if (level < MIN_LVL || level > MAX_LVL) { LOG(ERROR) << "level invalid : " << level; return false; } return true; } const std::string &LanguageUI::Translate(const std::string &key) const { static std::string emptyStr; if (auto it = strMap_.find(key); it != strMap_.end() && !it->second.empty()) { return it->second; } if (auto it = strMap_.find(DEFAULT_KEY); it != strMap_.end()) { return it->second; } return emptyStr; } bool LanguageUI::LoadLangRes(const JsonNode &node) { langRes_ = {}; if (!Visit(node[LANG_RES_KEY], langRes_)) { LOG(ERROR) << "parse language res error"; return false; } // clear resources std::vector{3, ""}.swap(res_); // load resources for (auto &res : langRes_.res) { if (!SetRes(res)) { return false; } } if (!Init(ParseLanguage())) { LOG(ERROR) << "init failed"; return false; } LOG(INFO) << "load language resource success"; return true; } Language LanguageUI::ParseLanguage() const { Language DEFAULT_LOCALE = defaultLanguage_; #ifndef UPDATER_UT //read language type(en-Latn-US/zh-Hans) from misc constexpr const char *CHINSES_LANGUAGE_PREFIX = "zh"; constexpr const char *ENGLISH_LANGUAGE_PREFIX = "en"; struct UpdaterPara para {}; if (!ReadUpdaterParaMisc(para)) { LOG(ERROR) << "ReadUpdaterParaMisc failed"; return DEFAULT_LOCALE; } if (strcmp(para.language, "") == 0) { LOG(INFO) << "Language in misc is empty"; return Language::CHINESE; } else if (strncmp(para.language, CHINSES_LANGUAGE_PREFIX, strlen(CHINSES_LANGUAGE_PREFIX)) == 0) { return Language::CHINESE; } else if (strncmp(para.language, ENGLISH_LANGUAGE_PREFIX, strlen(ENGLISH_LANGUAGE_PREFIX)) == 0) { return Language::ENGLISH; } #endif constexpr size_t localeLen = 2; // zh|es|en std::string realPath {}; if (!Utils::PathToRealPath(langRes_.localeFile, realPath)) { LOG(ERROR) << "get real path failed"; return DEFAULT_LOCALE; } std::ifstream ifs(realPath); std::string content {std::istreambuf_iterator {ifs}, {}}; const std::string &locale = content.substr(0, localeLen); if (auto it = LOCALES.find(locale); it != LOCALES.end()) { return it->second; } LOG(ERROR) << "locale " << locale << " not recognized"; return DEFAULT_LOCALE; } Language LanguageUI::GetCurLanguage() const { return language_; } } } // namespace Updater