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