M module-apps/application-settings/windows/system/LanguagesWindow.cpp => module-apps/application-settings/windows/system/LanguagesWindow.cpp +1 -1
@@ 9,7 9,7 @@
namespace gui
{
LanguagesWindow::LanguagesWindow(app::ApplicationCommon *app, std::string name)
- : BaseSettingsWindow(app, std::move(name)), langList(loader.getAvailableDisplayLanguages())
+ : BaseSettingsWindow(app, std::move(name)), langList(utils::getAvailableDisplayLanguages())
{
setTitle(utils::translate("app_settings_title_languages"));
}
M module-apps/application-settings/windows/system/LanguagesWindow.hpp => module-apps/application-settings/windows/system/LanguagesWindow.hpp +0 -1
@@ 18,7 18,6 @@ namespace gui
void onBeforeShow(ShowMode mode, SwitchData *data) override;
void setLanguageIndex();
- utils::LangLoader loader;
const std::vector<Language> langList;
Language selectedLanguage;
unsigned int selectedLanguageIndex{0};
M module-utils/i18n/CMakeLists.txt => module-utils/i18n/CMakeLists.txt +3 -0
@@ 1,9 1,12 @@
add_library(i18n STATIC)
+add_library(utils::i18n ALIAS i18n)
target_sources(i18n
PRIVATE
i18n.cpp
i18nImpl.hpp
+ Metadata.hpp
+ JSONLoader.hpp
PUBLIC
include/i18n/i18n.hpp
)
A module-utils/i18n/JSONLoader.hpp => module-utils/i18n/JSONLoader.hpp +35 -0
@@ 0,0 1,35 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <optional>
+#include <filesystem>
+#include <fstream>
+#include <log/log.hpp>
+#include <json11.hpp>
+
+namespace utils
+{
+ inline std::optional<json11::Json> JSONLoader(const std::filesystem::path &path)
+ {
+ auto file = std::ifstream{path};
+ if (file.bad()) {
+ return {};
+ }
+ std::stringstream buf{};
+ buf << file.rdbuf();
+
+ std::string err;
+ json11::Json js = json11::Json::parse(buf.str(), err);
+
+ if (err.length() != 0) {
+ LOG_ERROR("Failed to parse input data, err: %s", err.c_str());
+ return {};
+ }
+ else {
+ return js;
+ }
+ }
+
+} // namespace utils
A module-utils/i18n/Metadata.hpp => module-utils/i18n/Metadata.hpp +31 -0
@@ 0,0 1,31 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <optional>
+#include <filesystem>
+#include <json11.hpp>
+
+namespace utils
+{
+ class LanguageMetadata
+ {
+ public:
+ static std::optional<LanguageMetadata> get(const std::filesystem::path &path, const json11::Json &contents)
+ {
+ if (contents[metadataKey].is_null() || contents[metadataKey][metadataDisplayKey].is_null()) {
+ return {};
+ }
+
+ return LanguageMetadata{.displayName = contents[metadataKey][metadataDisplayKey].string_value(),
+ .path = path};
+ }
+ const std::string displayName;
+ const std::filesystem::path path;
+
+ private:
+ static constexpr auto metadataKey = "metadata";
+ static constexpr auto metadataDisplayKey = "display_name";
+ };
+} // namespace utils
M module-utils/i18n/i18n.cpp => module-utils/i18n/i18n.cpp +62 -114
@@ 2,13 2,12 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "i18nImpl.hpp"
-#include <log/log.hpp>
-#include <algorithm>
-#include <optional>
-#include <cstdio>
+#include "Metadata.hpp"
+
#include <purefs/filesystem_paths.hpp>
#include <i18n/i18n.hpp>
-#include <utility>
+#include <algorithm>
+#include <optional>
namespace utils
{
@@ 19,74 18,11 @@ namespace utils
return str.empty() ? ret : str;
}
- std::optional<json11::Json> 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<char[]>(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<LanguageMetadata> 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<std::string> 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)
@@ 115,37 51,9 @@ namespace utils
return out;
}
- } // namespace
-
- i18nPrivateInterface localize;
-
- std::vector<Language> LangLoader::getAvailableDisplayLanguages() const
- {
- std::vector<std::string> 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;
- }
+ i18nPrivateInterface localize;
- std::vector<Language> LangLoader::getAvailableInputLanguages() const
- {
- std::vector<std::string> languageNames;
- for (const auto &entry : std::filesystem::directory_iterator(getInputLanguagePath())) {
- languageNames.push_back(std::filesystem::path(entry.path()).stem());
- }
- return languageNames;
- }
+ } // namespace
void i18n::resetAssetsPath(const std::filesystem::path &assets)
{
@@ 155,6 63,7 @@ namespace utils
bool i18n::setInputLanguage(const Language &lang)
{
+ cpp_freertos::LockGuard lock(mutex);
if (lang.empty() || lang == inputLanguage) {
return false;
}
@@ 177,38 86,37 @@ namespace utils
bool i18n::setDisplayLanguage(const Language &lang)
{
- if (fallbackLanguage == json11::Json()) {
+ cpp_freertos::LockGuard lock(mutex);
+ if (fallbackLanguage.is_null()) {
loadFallbackLanguage();
+ loadMetadata();
}
if ((lang.empty() || lang == currentDisplayLanguage) && !currentDisplayLanguage.empty()) {
-
return false;
}
- else if (const auto pack =
- loadJSONData(localize.getDisplayLanguagePath() / (lang + utils::files::jsonExtension))) {
-
+ else if (const auto result = getMetadata(lang)) {
currentDisplayLanguage = lang;
- changeDisplayLanguage(pack.value());
-
+ displayLanguage = loader(result->path).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();
+ loader(localize.getDisplayLanguagePath() / (fallbackLanguageName + utils::files::jsonExtension)).value();
+ }
+
+ void i18n::loadMetadata()
+ {
+ for (const auto &entry : std::filesystem::directory_iterator(getDisplayLanguagePath())) {
+ if (const auto data = fetchMetadata(entry.path())) {
+ metadata.push_back(*data);
+ }
+ }
}
const std::string &translate(const std::string &text)
@@ 263,6 171,42 @@ namespace utils
fallbackLanguage = json11::Json();
}
+ std::optional<LanguageMetadata> i18n::getMetadata(const Language &lang) const
+ {
+ const auto result =
+ std::find_if(metadata.begin(), metadata.end(), [&lang](const auto &e) { return e.displayName == lang; });
+ if (result != metadata.end()) {
+ return *result;
+ }
+ else {
+ return {};
+ }
+ }
+ std::optional<LanguageMetadata> i18n::fetchMetadata(const std::filesystem::path &path) const
+ {
+ if (const auto jsonData = loader(path)) {
+ return LanguageMetadata::get(path, jsonData.value());
+ }
+ return {};
+ }
+ std::vector<Language> i18n::getAvailableDisplayLanguages() const
+ {
+ std::vector<Language> languages{metadata.size()};
+ std::transform(
+ metadata.cbegin(), metadata.cend(), languages.begin(), [](const auto &meta) { return meta.displayName; });
+
+ std::sort(languages.begin(), languages.end());
+ return languages;
+ }
+ std::vector<Language> i18n::getAvailableInputLanguages() const
+ {
+ std::vector<Language> languageNames;
+ for (const auto &entry : std::filesystem::directory_iterator(getInputLanguagePath())) {
+ languageNames.push_back(std::filesystem::path(entry.path()).stem());
+ }
+ return languageNames;
+ }
+
void resetDisplayLanguages()
{
return localize.resetDisplayLanguages();
@@ 272,5 216,9 @@ namespace utils
{
return localize.resetAssetsPath(p);
}
+ std::vector<Language> getAvailableDisplayLanguages()
+ {
+ return localize.getAvailableDisplayLanguages();
+ }
} // namespace utils
M module-utils/i18n/i18nImpl.hpp => module-utils/i18n/i18nImpl.hpp +18 -6
@@ 3,6 3,10 @@
#pragma once
+#include "JSONLoader.hpp"
+#include "Metadata.hpp"
+
+#include <optional>
#include <mutex.hpp>
#include <json11.hpp>
#include <i18n/i18n.hpp>
@@ 12,6 16,9 @@ namespace utils
class i18n
{
private:
+ using Loader = std::function<std::optional<json11::Json>(const std::filesystem::path &path)>;
+ Loader loader = JSONLoader;
+
json11::Json displayLanguage;
json11::Json fallbackLanguage; // backup language if item not found
Language fallbackLanguageName = getDefaultLanguage();
@@ 20,13 27,15 @@ namespace utils
Language currentDisplayLanguage;
std::filesystem::path InputLanguageDirPath = "assets/profiles";
std::filesystem::path DisplayLanguageDirPath = "assets/lang";
- mutable cpp_freertos::MutexStandard mutex;
+ cpp_freertos::MutexStandard mutex;
+ std::vector<LanguageMetadata> metadata;
- void changeDisplayLanguage(const json11::Json &lang);
void loadFallbackLanguage();
+ void loadMetadata();
+ std::optional<LanguageMetadata> getMetadata(const Language &lang) const;
+ std::optional<LanguageMetadata> fetchMetadata(const std::filesystem::path &path) const;
- protected:
- const std::string &get(const std::string &str);
+ public:
json11::Json &getDisplayLanguageJSON()
{
return displayLanguage;
@@ 46,15 55,18 @@ namespace utils
const std::string &getInputLanguageFilename(const std::string &inputMode);
bool setInputLanguage(const Language &lang);
bool setDisplayLanguage(const Language &lang);
- const std::filesystem::path getInputLanguagePath()
+ const std::filesystem::path getInputLanguagePath() const
{
return InputLanguageDirPath;
}
- const std::filesystem::path getDisplayLanguagePath()
+ const std::filesystem::path getDisplayLanguagePath() const
{
return DisplayLanguageDirPath;
}
+ std::vector<Language> getAvailableDisplayLanguages() const;
+ std::vector<Language> getAvailableInputLanguages() const;
+
void resetDisplayLanguages();
void resetAssetsPath(const std::filesystem::path &);
};
M module-utils/i18n/include/i18n/i18n.hpp => module-utils/i18n/include/i18n/i18n.hpp +4 -7
@@ 6,6 6,7 @@
#include <string>
#include <filesystem>
#include <vector>
+#include <functional>
using Language = std::string;
@@ 17,19 18,15 @@ namespace utils
constexpr auto breakSign = "_";
} // namespace files
- class LangLoader
- {
- public:
- std::vector<Language> getAvailableDisplayLanguages() const;
- std::vector<Language> getAvailableInputLanguages() const;
- };
-
const std::string &translate(const std::string &text);
const std::vector<std::string> translate_array(const std::string &text);
const std::string &getDisplayLanguage();
const std::string &getInputLanguage();
const std::string &getInputLanguageFilename(const std::string &inputMode);
+ std::vector<Language> getAvailableDisplayLanguages();
+ std::vector<Language> getAvailableInputLanguages();
+
bool setInputLanguage(const Language &lang);
bool setDisplayLanguage(const Language &lang);
void resetDisplayLanguages();
M products/BellHybrid/apps/application-bell-onboarding/presenter/OnBoardingLanguageWindowPresenter.cpp => products/BellHybrid/apps/application-bell-onboarding/presenter/OnBoardingLanguageWindowPresenter.cpp +1 -1
@@ 11,7 11,7 @@ namespace app::OnBoarding
std::vector<std::string> OnBoardingLanguageWindowPresenter::getLanguages()
{
- return languages.getAvailableDisplayLanguages();
+ return utils::getAvailableDisplayLanguages();
}
unsigned OnBoardingLanguageWindowPresenter::getSelectedLanguageIndex()
M products/BellHybrid/apps/application-bell-onboarding/presenter/OnBoardingLanguageWindowPresenter.hpp => products/BellHybrid/apps/application-bell-onboarding/presenter/OnBoardingLanguageWindowPresenter.hpp +0 -1
@@ 32,7 32,6 @@ namespace app::OnBoarding
class OnBoardingLanguageWindowPresenter : public OnBoardingLanguageWindowContract::Presenter
{
private:
- utils::LangLoader languages;
app::ApplicationCommon *app;
public:
M products/BellHybrid/apps/application-bell-settings/presenter/LanguageWindowPresenter.cpp => products/BellHybrid/apps/application-bell-settings/presenter/LanguageWindowPresenter.cpp +1 -1
@@ 11,7 11,7 @@ namespace app::bell_settings
std::vector<std::string> LanguageWindowPresenter::getLanguages() const
{
- return languages.getAvailableDisplayLanguages();
+ return utils::getAvailableDisplayLanguages();
}
std::string LanguageWindowPresenter::getSelectedLanguage() const
M products/BellHybrid/apps/application-bell-settings/presenter/LanguageWindowPresenter.hpp => products/BellHybrid/apps/application-bell-settings/presenter/LanguageWindowPresenter.hpp +0 -1
@@ 32,7 32,6 @@ namespace app::bell_settings
class LanguageWindowPresenter : public LanguageWindowContract::Presenter
{
private:
- utils::LangLoader languages;
app::ApplicationCommon *app{};
public: