// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include "i18nImpl.hpp" #include #include #include #include #include #include #include namespace utils { namespace { auto returnNonEmptyString(const std::string &str, const std::string &ret) -> const std::string & { return str.empty() ? ret : str; } std::optional loadJSONData(const std::filesystem::path &path) { auto fd = std::fopen(path.c_str(), "r"); if (fd == nullptr) { LOG_FATAL("Error during opening file %s", path.c_str()); return {}; } const auto fsize = std::filesystem::file_size(path); auto stream = std::make_unique(fsize + 1); // +1 for NULL terminator std::fread(stream.get(), 1, fsize, fd); std::string err; json11::Json js = json11::Json::parse(stream.get(), err); std::fclose(fd); // Error if (err.length() != 0) { LOG_FATAL("%s", err.c_str()); return {}; } else { return js; } } struct LanguageMetadata { static std::optional get(const std::filesystem::path &path) { const auto contents = loadJSONData(path); if (not contents) { return {}; } const auto contentsValue = *contents; if (contentsValue[metadataKey].is_null() || contentsValue[metadataKey][metadataDisplayKey].is_null()) { return {}; } return LanguageMetadata{.displayName = contentsValue[metadataKey][metadataDisplayKey].string_value()}; } const std::string displayName; private: static constexpr auto metadataKey = "metadata"; static constexpr auto metadataDisplayKey = "display_name"; }; } // namespace namespace { class i18nPrivateInterface : public i18n { public: const std::string &get(const std::string &str); const std::vector get_array(const std::string &str); using i18n::getDisplayLanguage; using i18n::getDisplayLanguagePath; using i18n::getInputLanguage; using i18n::getInputLanguageFilename; using i18n::getInputLanguagePath; using i18n::resetAssetsPath; using i18n::resetDisplayLanguages; using i18n::setDisplayLanguage; using i18n::setInputLanguage; }; const std::string &i18nPrivateInterface::get(const std::string &str) { // if language pack returned nothing then try default language if (getDisplayLanguageJSON()[str].string_value().empty()) { return returnNonEmptyString(getFallbackLanguageJSON()[str].string_value(), str); } return returnNonEmptyString(getDisplayLanguageJSON()[str].string_value(), str); } const std::vector i18nPrivateInterface::get_array(const std::string &str) { std::vector out; auto object = getDisplayLanguageJSON()[str]; // if language pack returned nothing then try default language if (not object.is_array()) { object = getFallbackLanguageJSON()[str]; } if (object.is_array()) { out.reserve(object.array_items().size()); std::for_each(object.array_items().begin(), object.array_items().end(), [&out](const auto &o) { out.emplace_back(o.string_value()); }); } return out; } } // namespace i18nPrivateInterface localize; std::vector LangLoader::getAvailableDisplayLanguages() const { std::vector languageNames; for (const auto &entry : std::filesystem::directory_iterator(getDisplayLanguagePath())) { const auto metadata = LanguageMetadata::get(entry.path()); if (metadata) { languageNames.push_back(metadata->displayName); } else { /// If metadata is not valid use file name string as a display language languageNames.push_back(std::filesystem::path(entry).stem()); } } std::sort(languageNames.begin(), languageNames.end()); return languageNames; } std::vector LangLoader::getAvailableInputLanguages() const { std::vector languageNames; for (const auto &entry : std::filesystem::directory_iterator(getInputLanguagePath())) { languageNames.push_back(std::filesystem::path(entry.path()).stem()); } return languageNames; } void i18n::resetAssetsPath(const std::filesystem::path &assets) { DisplayLanguageDirPath = assets / "lang"; InputLanguageDirPath = assets / "profiles"; } bool i18n::setInputLanguage(const Language &lang) { if (lang.empty() || lang == inputLanguage) { return false; } inputLanguage = lang; return true; } const std::string &i18n::getInputLanguageFilename(const std::string &inputMode) { // if language pack returned nothing then try default language if (inputLanguage.empty()) { inputLanguageFilename = fallbackLanguageName + utils::files::breakSign + inputMode; } else { inputLanguageFilename = inputLanguage + utils::files::breakSign + inputMode; } return inputLanguageFilename; } bool i18n::setDisplayLanguage(const Language &lang) { if (fallbackLanguage == json11::Json()) { loadFallbackLanguage(); } if ((lang.empty() || lang == currentDisplayLanguage) && !currentDisplayLanguage.empty()) { return false; } else if (const auto pack = loadJSONData(localize.getDisplayLanguagePath() / (lang + utils::files::jsonExtension))) { currentDisplayLanguage = lang; changeDisplayLanguage(pack.value()); return true; } return false; } void i18n::changeDisplayLanguage(const json11::Json &lang) { cpp_freertos::LockGuard lock(mutex); displayLanguage = lang; } void i18n::loadFallbackLanguage() { cpp_freertos::LockGuard lock(mutex); currentDisplayLanguage = fallbackLanguageName; fallbackLanguage = loadJSONData(localize.getDisplayLanguagePath() / (fallbackLanguageName + utils::files::jsonExtension)) .value(); } const std::string &translate(const std::string &text) { return utils::localize.get(text); } const std::vector translate_array(const std::string &text) { return utils::localize.get_array(text); } const std::string &getDisplayLanguage() { return utils::localize.getDisplayLanguage(); } const std::string &getInputLanguage() { return utils::localize.getInputLanguage(); } const std::string &getInputLanguageFilename(const std::string &inputMode) { return localize.getInputLanguageFilename(inputMode); } bool setInputLanguage(const Language &lang) { return localize.setInputLanguage(lang); } bool setDisplayLanguage(const Language &lang) { return localize.setDisplayLanguage(lang); } const std::filesystem::path getInputLanguagePath() { return localize.getInputLanguagePath(); } const std::filesystem::path getDisplayLanguagePath() { return localize.getDisplayLanguagePath(); } void i18n::resetDisplayLanguages() { currentDisplayLanguage.clear(); displayLanguage = json11::Json(); fallbackLanguage = json11::Json(); } void resetDisplayLanguages() { return localize.resetDisplayLanguages(); } void resetAssetsPath(const std::filesystem::path &p) { return localize.resetAssetsPath(p); } } // namespace utils