M changelog.md => changelog.md +1 -0
@@ 10,6 10,7 @@
### Changed
* Input keyboard language files parser from KBD to JSON.
+* Input language files are now loaded based on files in "profiles" folder.
### Fixed
M doc/i18n.md => doc/i18n.md +5 -12
@@ 24,16 24,7 @@ The keys on the left side refer to the values on the right side. These values ar
### Keyboard input language
-The first four values of JSON language files tell MuditaOS, which keyboard input language is the specific language setting using:
-```c++
-"common_kbd_lower": "English_lower",
-"common_kbd_upper": "English_upper",
-"common_kbd_numeric": "numeric",
-"common_kbd_phone": "phone",
-(...)
-```
-
-These values are names of JSON files, which are located in [the image/assets/profiles folder](../image/assets/profiles/).
+Keyboard input language files have JSON extension and are located in [the image/assets/profiles folder](../image/assets/profiles/).
Every language has its own files for upper and lower letters. Here's an example of a working JSON file for `English_lower.json`:
```c++
@@ 57,9 48,11 @@ The first value declares the type of this file:
- `normal` - they are shown in input language settings, user can change it through GUI (e.g. from English to Polish).
- `special` - they won't show in input language settings, they can be modified only in code (e.g. numeric keyboard).
-Normal-type files will be displayed in settings by their filename (e.g. English_lower will be shown as English). When you add a new input language you should always include files for lower and upper letters for it.
+Normal-type files will be displayed in settings by their filename (e.g. `English_lower.json` and `English_upper.json` will be shown as `English`). When you add a new input language you should always include files for lower and upper letters for it.
-Next key-value pairs includes code of the key (key) and characters available under this key (value).
+Next key-value pairs includes code of the key (key) and characters available under this key (value). However, there are two buttons with hexadecimal number (under `61` and `63` buttons). Their value tells MuditaOS what they do:
+- `0x0A` - after shortpress (explained below) button change input mode (from lower letters to upper, from upper to numbers etc.). After longpress they show special characters window.
+- `0x08` - after shortpress button deletes character.
Files naming pattern should be: `<language>_<lower/upper>`, eg. correct implementation of Rodian input language should consist of two files: `Rodian_lower.json` and `Rodian_upper.json`.
M image/assets/lang/Deutsch.json => image/assets/lang/Deutsch.json +0 -3
@@ 1,7 1,4 @@
{
- "common_kbd_lower": "Deutsch_lower",
- "common_kbd_upper": "Deutsch_upper",
- "common_kbd_numeric": "numeric",
"common_add": "ADD",
"common_open": "OPEN",
"common_call": "CALL",
M image/assets/lang/English.json => image/assets/lang/English.json +0 -4
@@ 1,8 1,4 @@
{
- "common_kbd_lower": "English_lower",
- "common_kbd_upper": "English_upper",
- "common_kbd_numeric": "numeric",
- "common_kbd_phone": "phone",
"common_add": "ADD",
"common_open": "OPEN",
"common_call": "CALL",
M image/assets/lang/Espanol.json => image/assets/lang/Espanol.json +0 -3
@@ 1,7 1,4 @@
{
- "common_kbd_lower": "Espanol_lower",
- "common_kbd_upper": "Espanol_upper",
- "common_kbd_numeric": "numeric",
"common_add": "ADD",
"common_open": "ABIERTA",
"common_call": "CALL",
M image/assets/lang/Francais.json => image/assets/lang/Francais.json +0 -3
@@ 1,7 1,4 @@
{
- "common_kbd_lower": "Francais_lower",
- "common_kbd_upper": "Francais_upper",
- "common_kbd_numeric": "numeric",
"common_add": "ADD",
"common_open": "OPEN",
"common_call": "CALL",
M image/assets/lang/Polski.json => image/assets/lang/Polski.json +0 -3
@@ 1,7 1,4 @@
{
- "common_kbd_lower": "Polski_lower",
- "common_kbd_upper": "Polski_upper",
- "common_kbd_numeric": "numeric",
"common_add": "DODAJ",
"common_open": "OTWÓRZ",
"common_call": "DZWOŃ",
M module-apps/application-settings-new/windows/InputLanguageWindow.cpp => module-apps/application-settings-new/windows/InputLanguageWindow.cpp +1 -1
@@ 20,7 20,7 @@ namespace gui
auto InputLanguageWindow::buildOptionsList() -> std::list<gui::Option>
{
std::list<gui::Option> optionsList;
- const auto &langList = loader.getAvailableDisplayLanguages();
+ const auto &langList = profiles.getAvailableInputLanguages();
for (const auto &lang : langList) {
optionsList.emplace_back(std::make_unique<gui::OptionSettings>(
lang,
M module-apps/application-settings-new/windows/InputLanguageWindow.hpp => module-apps/application-settings-new/windows/InputLanguageWindow.hpp +2 -1
@@ 4,6 4,7 @@
#pragma once
#include "BaseSettingsWindow.hpp"
+#include "Translator.hpp"
namespace gui
{
@@ 18,6 19,6 @@ namespace gui
private:
Language selectedLang;
- utils::LangLoader loader;
+ Profiles profiles;
};
} // namespace gui
M module-gui/gui/input/Profile.cpp => module-gui/gui/input/Profile.cpp +10 -2
@@ 10,14 10,22 @@
namespace gui
{
- Profile::Profile(const std::string &filepath) : name(filepath), inputChars(createJson(filepath))
- {}
+ Profile::Profile(const std::filesystem::path &filepath)
+ {
+ name = filepath.stem();
+ inputChars = createJson(filepath);
+ }
const std::string &Profile::getName() noexcept
{
return name;
}
+ const json11::Json &Profile::getJson() const noexcept
+ {
+ return inputChars;
+ }
+
const json11::Json Profile::createJson(const std::string &filepath)
{
auto fd = std::fopen(filepath.c_str(), "r");
M module-gui/gui/input/Profile.hpp => module-gui/gui/input/Profile.hpp +3 -1
@@ 7,6 7,7 @@
#include <vector>
#include <map>
#include "json/json11.hpp"
+#include <filesystem>
namespace gui
{
@@ 22,10 23,11 @@ namespace gui
public:
static constexpr uint32_t none_key = 0;
Profile() = default;
- explicit Profile(const std::string &filepath);
+ explicit Profile(const std::filesystem::path &filepath);
[[nodiscard]] const std::string &getName() noexcept;
[[nodiscard]] uint32_t getCharKey(bsp::KeyCodes code, uint32_t times);
+ [[nodiscard]] const json11::Json &getJson() const noexcept;
};
} /* namespace gui */
M module-gui/gui/input/Translator.cpp => module-gui/gui/input/Translator.cpp +42 -19
@@ 4,16 4,17 @@
#include "Translator.hpp"
#include "log/log.hpp"
#include <algorithm>
-#include <log/log.hpp>
#include <filesystem>
+#include "i18n/i18n.hpp"
namespace gui
{
- namespace
+ namespace filetype
{
- constexpr auto profilesFolder = "assets/profiles";
- constexpr auto extension = ".json";
- } // namespace
+ constexpr auto jsonKey = "filetype";
+ constexpr auto normal = "normal";
+ constexpr auto special = "special";
+ } // namespace filetype
void recon_long_press(InputEvent &evt, const RawKey &key, const RawKey &prev_key_press, uint32_t time)
{
@@ 202,24 203,46 @@ namespace gui
}
}
- std::vector<std::string> Profiles::getProfilesPaths()
+ std::vector<std::string> Profiles::getProfilesNames()
{
- std::vector<std::string> profileFiles;
- LOG_INFO("Scanning %s profiles folder: %s", extension, profilesFolder);
+ std::vector<std::string> profilesNames;
+ LOG_INFO("Scanning %s profiles folder: %s",
+ utils::files::jsonExtension,
+ utils::localize.InputLanguageDirPath.c_str());
- for (const auto &entry : std::filesystem::directory_iterator(profilesFolder)) {
- profileFiles.push_back(std::filesystem::path(entry.path()));
+ for (const auto &entry : std::filesystem::directory_iterator(utils::localize.InputLanguageDirPath)) {
+ profilesNames.push_back(std::filesystem::path(entry.path().stem()));
}
- LOG_INFO("Total number of profiles: %u", static_cast<unsigned int>(profileFiles.size()));
- return profileFiles;
+ LOG_INFO("Total number of profiles: %u", static_cast<unsigned int>(profilesNames.size()));
+ return profilesNames;
+ }
+
+ std::vector<std::string> Profiles::getAvailableInputLanguages()
+ {
+ std::vector<std::string> profilesNames = getProfilesNames(), availableProfiles;
+
+ for (auto &name : profilesNames) {
+ auto profile = get().profilesList[name];
+ if (profile.getJson()[filetype::jsonKey] == filetype::normal) {
+ auto breakSignPosition = name.find_last_of(utils::files::breakSign);
+ std::string displayedLanguageName = name.substr(0, breakSignPosition);
+
+ if (std::find(availableProfiles.begin(), availableProfiles.end(), displayedLanguageName) ==
+ availableProfiles.end()) {
+ availableProfiles.push_back(displayedLanguageName);
+ }
+ }
+ }
+ return availableProfiles;
}
void Profiles::init()
{
- std::vector<std::string> profileFilesPaths = getProfilesPaths();
- for (std::string filePath : profileFilesPaths) {
- if (std::size(filePath)) {
+ std::vector<std::string> profilesNames = getProfilesNames();
+ for (const auto &profileName : profilesNames) {
+ if (!profileName.empty()) {
+ auto filePath = utils::localize.InputLanguageDirPath / (profileName + utils::files::jsonExtension);
loadProfile(filePath);
}
}
@@ 240,16 263,16 @@ namespace gui
Profile &Profiles::get(const std::string &name)
{
- auto filepath = std::string(profilesFolder) + "/" + name + extension;
+ std::filesystem::path filepath = utils::localize.InputLanguageDirPath / (name + utils::files::jsonExtension);
// if profile not in profile map -> load
- if (std::size(filepath) == 0) {
+ if (filepath.empty()) {
LOG_ERROR("Request for nonexistent profile: %s", filepath.c_str());
return get().empty;
}
- if (get().profilesList.find(filepath) == get().profilesList.end()) {
+ if (get().profilesList.find(name) == get().profilesList.end()) {
get().loadProfile(filepath);
}
- return get().profilesList[filepath];
+ return get().profilesList[name];
}
} /* namespace gui */
M module-gui/gui/input/Translator.hpp => module-gui/gui/input/Translator.hpp +2 -1
@@ 57,13 57,14 @@ namespace gui
std::map<std::string, gui::Profile> profilesList = {};
void loadProfile(const std::string &filepath);
- std::vector<std::string> getProfilesPaths();
+ std::vector<std::string> getProfilesNames();
void init();
Profile empty;
static Profiles &get();
public:
+ std::vector<std::string> getAvailableInputLanguages();
Profile &get(const std::string &name);
};
M module-gui/gui/widgets/InputMode.cpp => module-gui/gui/widgets/InputMode.cpp +10 -5
@@ 8,10 8,10 @@
/// input mode strings - as these are stored in json (in files...)
const std::map<InputMode::Mode, std::string> input_mode = {
- {InputMode::digit, "common_kbd_numeric"},
- {InputMode::ABC, "common_kbd_upper"},
- {InputMode::abc, "common_kbd_lower"},
- {InputMode::phone, "common_kbd_phone"},
+ {InputMode::digit, "numeric"},
+ {InputMode::ABC, "upper"},
+ {InputMode::abc, "lower"},
+ {InputMode::phone, "phone"},
};
static std::string getInputName(InputMode::Mode m)
@@ 61,7 61,12 @@ void InputMode::next()
const std::string &InputMode::get()
{
- return utils::localize.getInputLanguage(input_mode.at(modeNow()));
+ auto actualInputMode = input_mode.at(modeNow());
+ if (actualInputMode == input_mode.find(InputMode::digit)->second ||
+ actualInputMode == input_mode.find(InputMode::phone)->second) {
+ return input_mode.at(modeNow());
+ }
+ return utils::localize.getInputLanguage(actualInputMode);
}
void InputMode::show_input_type()
M module-utils/i18n/i18n.cpp => module-utils/i18n/i18n.cpp +21 -19
@@ 5,16 5,12 @@
#include "i18n.hpp"
#include "Utils.hpp"
#include <cstdio>
-#include <filesystem>
#include <purefs/filesystem_paths.hpp>
namespace utils
{
namespace
{
- const auto LanguageDirPath = std::filesystem::path{"assets"} / "lang";
- constexpr auto extension = ".json";
-
auto returnNonEmptyString(const std::string &str, const std::string &ret) -> const std::string &
{
return str.empty() ? ret : str;
@@ 24,7 20,7 @@ namespace utils
i18n localize;
json11::Json LangLoader::createJson(const std::string &filename)
{
- const auto path = LanguageDirPath / (filename + extension);
+ const auto path = utils::localize.DisplayLanguageDirPath / (filename + utils::files::jsonExtension);
auto fd = std::fopen(path.c_str(), "r");
if (fd == nullptr) {
LOG_FATAL("Error during opening file %s", path.c_str());
@@ 55,7 51,16 @@ namespace utils
std::vector<Language> LangLoader::getAvailableDisplayLanguages() const
{
std::vector<std::string> languageNames;
- for (const auto &entry : std::filesystem::directory_iterator(LanguageDirPath)) {
+ for (const auto &entry : std::filesystem::directory_iterator(utils::localize.DisplayLanguageDirPath)) {
+ languageNames.push_back(std::filesystem::path(entry.path()).stem());
+ }
+ return languageNames;
+ }
+
+ std::vector<Language> LangLoader::getAvailableInputLanguages() const
+ {
+ std::vector<std::string> languageNames;
+ for (const auto &entry : std::filesystem::directory_iterator(utils::localize.InputLanguageDirPath)) {
languageNames.push_back(std::filesystem::path(entry.path()).stem());
}
return languageNames;
@@ 63,25 68,22 @@ namespace utils
void i18n::setInputLanguage(const Language &lang)
{
- if (lang == currentInputLanguage) {
+ if (lang == inputLanguage) {
return;
}
- currentInputLanguage = lang;
- if (lang == fallbackLanguageName) {
- inputLanguage = fallbackLanguage;
- }
- else {
- json11::Json pack = loader.createJson(lang);
- inputLanguage = pack;
- }
+ inputLanguage = lang;
}
- const std::string &i18n::getInputLanguage(const std::string &str)
+
+ const std::string &i18n::getInputLanguage(const std::string &inputMode)
{
// if language pack returned nothing then try default language
- if (inputLanguage[str].string_value().empty()) {
- return returnNonEmptyString(fallbackLanguage[str].string_value(), str);
+ if (inputLanguage.empty()) {
+ inputLanguageFilename = fallbackLanguageName + utils::files::breakSign + inputMode;
+ }
+ else {
+ inputLanguageFilename = inputLanguage + utils::files::breakSign + inputMode;
}
- return returnNonEmptyString(inputLanguage[str].string_value(), str);
+ return inputLanguageFilename;
}
const std::string &i18n::get(const std::string &str)
M module-utils/i18n/i18n.hpp => module-utils/i18n/i18n.hpp +14 -4
@@ 5,16 5,24 @@
#include "json/json11.hpp"
#include <string>
#include <vfs.hpp>
+#include <filesystem>
using Language = std::string;
namespace utils
{
+ namespace files
+ {
+ constexpr auto jsonExtension = ".json";
+ constexpr auto breakSign = "_";
+ } // namespace files
+
class LangLoader
{
public:
virtual ~LangLoader() = default;
std::vector<Language> getAvailableDisplayLanguages() const;
+ std::vector<Language> getAvailableInputLanguages() const;
json11::Json createJson(const std::string &filename);
};
@@ 22,20 30,22 @@ namespace utils
{
private:
json11::Json displayLanguage;
- json11::Json inputLanguage;
json11::Json fallbackLanguage; // backup language if item not found
LangLoader loader;
Language fallbackLanguageName;
+ Language inputLanguage = fallbackLanguageName;
+ Language inputLanguageFilename;
Language currentDisplayLanguage = fallbackLanguageName;
- Language currentInputLanguage = fallbackLanguageName;
bool backupLanguageInitializer = false;
public:
- static constexpr auto DefaultLanguage = "English";
+ static constexpr auto DefaultLanguage = "English";
+ const std::filesystem::path DisplayLanguageDirPath = "assets/lang";
+ const std::filesystem::path InputLanguageDirPath = "assets/profiles";
virtual ~i18n() = default;
void setInputLanguage(const Language &lang);
- const std::string &getInputLanguage(const std::string &str);
+ const std::string &getInputLanguage(const std::string &inputMode);
const std::string &get(const std::string &str);
void setDisplayLanguage(const Language &lang);
void setFallbackLanguage(const Language &lang);