From 7bf70bb26e6252fabda7afab9bb193a4ec79902a Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 12 Dec 2023 22:02:17 +0300 Subject: [PATCH] languages support (WIP) --- res/content/base/texts/en_US.txt | 22 +++++ res/content/base/texts/ru_RU.txt | 22 +++++ res/texts/en_US.txt | 39 +++++++++ res/texts/langs.json | 11 +++ res/texts/ru_RU.txt | 45 +++++++++++ src/coders/commons.cpp | 21 ++++- src/coders/commons.h | 3 +- src/coders/toml.cpp | 11 +-- src/constants.h | 3 +- src/content/ContentPack.cpp | 17 ++++ src/content/ContentPack.h | 18 +++++ src/engine.cpp | 14 ++++ src/files/settings_io.cpp | 3 + src/frontend/gui/gui_util.cpp | 9 ++- src/frontend/gui/gui_util.h | 2 +- src/frontend/locale/langs.cpp | 133 +++++++++++++++++++++++++++++++ src/frontend/locale/langs.h | 65 +++++++++++++++ src/frontend/menu.cpp | 41 +++++----- src/settings.h | 5 ++ src/util/platform.cpp | 9 +++ src/util/platform.h | 1 + 21 files changed, 456 insertions(+), 38 deletions(-) create mode 100644 res/content/base/texts/en_US.txt create mode 100644 res/content/base/texts/ru_RU.txt create mode 100644 res/texts/en_US.txt create mode 100644 res/texts/langs.json create mode 100644 res/texts/ru_RU.txt create mode 100644 src/content/ContentPack.cpp create mode 100644 src/content/ContentPack.h create mode 100644 src/frontend/locale/langs.cpp create mode 100644 src/frontend/locale/langs.h diff --git a/res/content/base/texts/en_US.txt b/res/content/base/texts/en_US.txt new file mode 100644 index 00000000..05c8d3dc --- /dev/null +++ b/res/content/base/texts/en_US.txt @@ -0,0 +1,22 @@ +bazalt=Bazalt +blue_lamp=Blue Lamp +brick=Brick +dirt=Dirt +flower=Flower +glass=Glass +grass_block=Ground +grass=Tall Grass +green_lamp=Green Lamp +lamp=Lamp +leaves=Leaves +lightbulb=Bulb +metal=Metal +pane=Pane +pipe=Pipe +planks=Planks +red_lamp=Red Lamp +rust=Rust +sand=Sand +stone=Stone +water=Water +wood=Wood \ No newline at end of file diff --git a/res/content/base/texts/ru_RU.txt b/res/content/base/texts/ru_RU.txt new file mode 100644 index 00000000..148c0a5a --- /dev/null +++ b/res/content/base/texts/ru_RU.txt @@ -0,0 +1,22 @@ +bazalt=Базальт +blue_lamp=Синяя Лампа +brick=Кирпич +dirt=Земля +flower=Цветок +glass=Стекло +grass_block=Дёрн +grass=Высокая Трава +green_lamp=Зелёная Лампа +lamp=Лампа +leaves=Листва +lightbulb=Лампочка +metal=Металл +pane=Панель +pipe=Труба +planks=Доски +red_lamp=Красная Лампа +rust=Ржавчина +sand=Песок +stone=Камень +water=Вода +wood=Бревно \ No newline at end of file diff --git a/res/texts/en_US.txt b/res/texts/en_US.txt new file mode 100644 index 00000000..d2683135 --- /dev/null +++ b/res/texts/en_US.txt @@ -0,0 +1,39 @@ +# Menu +menu.new-world=New World +menu.quit=Quit +menu.create-world=Create World +menu.save-and-quit=Save and Quit to Menu +menu.missing-content=Missing Content! +menu.controls=Controls +menu.back-to-menu=Back to Main Menu +menu.settings=Settings +world.seed=Seed +world.name=World Name +world.create=Create World + +# Settings +chunks.load-distance=Load Distance +chunks.load-speed=Load Speed +graphics.fog-curve=Fog Curve +graphics.backlight=Backlight +display.vsync=V-Sync +camera.fov=FOV +mouse.sensitivity=Mouse Sensitivity + +# Bindings +movement.forward=Forward +movement.back=Back +movement.left=Left +movement.right=Right +movement.jump=Jump +movement.sprint=Sprint +movement.crouch=Crouch +movement.cheat=Cheat +hud.inventory=Inventory +player.pick=Pick Block +player.attack=Attack / Break +player.build=Place Block +player.flight=Flight +player.noclip=No-clip +camera.zoom=Zoom +camera.mode=Switch Camera Mode \ No newline at end of file diff --git a/res/texts/langs.json b/res/texts/langs.json new file mode 100644 index 00000000..8e263a7a --- /dev/null +++ b/res/texts/langs.json @@ -0,0 +1,11 @@ +{ + "langs": { + "en_US": { + "name": "English" + }, + "ru_RU": { + "name": "Русский" + } + }, + "fallback": "en_US" +} diff --git a/res/texts/ru_RU.txt b/res/texts/ru_RU.txt new file mode 100644 index 00000000..1e98ade0 --- /dev/null +++ b/res/texts/ru_RU.txt @@ -0,0 +1,45 @@ +# Общее +Yes=Да +No=Нет +Ok=Ок +Back=Назад +Continue=Продолжить + +# Меню +menu.new-world=Новый Мир +menu.quit=Выход +menu.continue=Продолжить +menu.save-and-quit=Сохранить и Выйти в Меню +menu.missing-content=Отсутствует Контент! +menu.controls=Управление +menu.back-to-menu=Вернуться в Меню +menu.settings=Настройки +world.seed=Зерно +world.name=Название +world.create=Создать Мир + +# Настройки +chunks.load-distance=Дистанция Загрузки +chunks.load-speed=Скорость Загрузки +graphics.fog-curve=Кривая Тумана +graphics.backlight=Подсветка +display.vsync=V-Sync +camera.fov=Поле Зрения +mouse.sensitivity=Чувствительность Мыши + +# Управление +movement.forward=Вперёд +movement.back=Назад +movement.left=Влево +movement.right=Вправо +movement.jump=Прыжок +movement.sprint=Ускорение +movement.crouch=Красться +movement.cheat=Чит +hud.inventory=Инвентарь +player.pick=Подобрать Блок +player.attack=Атаковать / Сломать +player.build=Поставить Блок +player.flight=Полёт +camera.zoom=Приближение +camera.mode=Сменить Режим Камеры \ No newline at end of file diff --git a/src/coders/commons.cpp b/src/coders/commons.cpp index 09d57ec3..ee7a6841 100644 --- a/src/coders/commons.cpp +++ b/src/coders/commons.cpp @@ -98,6 +98,18 @@ void BasicParser::skipWhitespace() { } } +void BasicParser::skipLine() { + while (hasNext()) { + if (source[pos] == '\n') { + pos++; + linestart = pos; + line++; + break; + } + pos++; + } +} + bool BasicParser::hasNext() { return pos < source.length(); } @@ -250,7 +262,7 @@ bool BasicParser::parseNumber(int sign, number_u& out) { return true; } -string BasicParser::parseString(char quote) { +string BasicParser::parseString(char quote, bool closeRequired) { std::stringstream ss; while (hasNext()) { char c = source[pos]; @@ -282,13 +294,16 @@ string BasicParser::parseString(char quote) { } continue; } - if (c == '\n') { + if (c == '\n' && closeRequired) { throw error("non-closed string literal"); } ss << c; pos++; } - throw error("unexpected end"); + if (closeRequired) { + throw error("unexpected end"); + } + return ss.str(); } parsing_error BasicParser::error(std::string message) { diff --git a/src/coders/commons.h b/src/coders/commons.h index a76d0fd5..6c225309 100644 --- a/src/coders/commons.h +++ b/src/coders/commons.h @@ -70,6 +70,7 @@ protected: uint linestart = 0; virtual void skipWhitespace(); + void skipLine(); void expect(char expected); char peek(); char nextChar(); @@ -79,7 +80,7 @@ protected: std::string parseName(); int64_t parseSimpleInt(int base); bool parseNumber(int sign, number_u& out); - std::string parseString(char chr); + std::string parseString(char chr, bool closeRequired=true); parsing_error error(std::string message); diff --git a/src/coders/toml.cpp b/src/coders/toml.cpp index c10c3856..b772cbe0 100644 --- a/src/coders/toml.cpp +++ b/src/coders/toml.cpp @@ -115,16 +115,7 @@ Reader::Reader(Wrapper* wrapper, string file, string source) : BasicParser(file, void Reader::skipWhitespace() { BasicParser::skipWhitespace(); if (hasNext() && source[pos] == '#') { - pos++; - while (hasNext()) { - if (source[pos] == '\n') { - pos++; - linestart = pos; - line++; - break; - } - pos++; - } + skipLine(); if (hasNext() && is_whitespace(peek())) { skipWhitespace(); } diff --git a/src/constants.h b/src/constants.h index 4162d5fc..00d7f7d5 100644 --- a/src/constants.h +++ b/src/constants.h @@ -5,7 +5,7 @@ #include "typedefs.h" const int ENGINE_VERSION_MAJOR = 0; -const int ENGINE_VERSION_MINOR = 15; +const int ENGINE_VERSION_MINOR = 16; const int CHUNK_W = 16; const int CHUNK_H = 256; @@ -16,6 +16,7 @@ const int CHUNK_VOL = (CHUNK_W * CHUNK_H * CHUNK_D); /* BLOCK_VOID is block id used to mark non-existing voxel (voxel of missing chunk) */ const blockid_t BLOCK_VOID = std::numeric_limits::max(); +const blockid_t MAX_BLOCKS = BLOCK_VOID; inline uint vox_index(int x, int y, int z, int w=CHUNK_W, int d=CHUNK_D) { return (y * d + z) * w + x; diff --git a/src/content/ContentPack.cpp b/src/content/ContentPack.cpp new file mode 100644 index 00000000..5c7c85ed --- /dev/null +++ b/src/content/ContentPack.cpp @@ -0,0 +1,17 @@ +#include "ContentPack.h" + +using std::string; +using std::filesystem::path; + +ContentPack::ContentPack(const string id, + const path folder) + : id(id), folder(folder) { + +} +const string& ContentPack::getId() const { + return id; +} + +const path& ContentPack::getFolder() const { + return folder; +} diff --git a/src/content/ContentPack.h b/src/content/ContentPack.h new file mode 100644 index 00000000..df440a2b --- /dev/null +++ b/src/content/ContentPack.h @@ -0,0 +1,18 @@ +#ifndef CONTENT_CONTENT_PACK_H_ +#define CONTENT_CONTENT_PACK_H_ + +#include +#include + +class ContentPack { + const std::string id; + const std::filesystem::path folder; +public: + ContentPack(const std::string id, + const std::filesystem::path folder); + + const std::string& getId() const; + const std::filesystem::path& getFolder() const; +}; + +#endif // CONTENT_CONTENT_PACK_H_ diff --git a/src/engine.cpp b/src/engine.cpp index a974a83d..339e4196 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #define GLEW_STATIC @@ -27,6 +28,9 @@ #include "files/files.h" #include "files/engine_paths.h" +#include "content/ContentPack.h" +#include "frontend/locale/langs.h" + using std::unique_ptr; using std::shared_ptr; using std::string; @@ -55,6 +59,16 @@ Engine::Engine(EngineSettings& settings, EnginePaths* paths, Content* content) } Audio::initialize(); gui = new GUI(); + + std::vector packs; + auto resdir = paths->getResources(); + auto base = std::make_unique("base", resdir/path("content/base")); + packs.push_back(base.get()); + + if (settings.ui.language == "auto") { + settings.ui.language = platform::detect_locale(); + } + langs::setup(resdir, settings.ui.language, packs); std::cout << "-- initializing finished" << std::endl; } diff --git a/src/files/settings_io.cpp b/src/files/settings_io.cpp index 7185bfa9..27ae6c84 100644 --- a/src/files/settings_io.cpp +++ b/src/files/settings_io.cpp @@ -39,6 +39,9 @@ toml::Wrapper create_wrapper(EngineSettings& settings) { debug.add("generator-test-mode", &settings.debug.generatorTestMode); debug.add("show-chunk-borders", &settings.debug.showChunkBorders); debug.add("do-write-lights", &settings.debug.doWriteLights); + + toml::Section& ui = wrapper.add("ui"); + ui.add("language", &settings.ui.language); return wrapper; } diff --git a/src/frontend/gui/gui_util.cpp b/src/frontend/gui/gui_util.cpp index 8d527734..da1d15f1 100644 --- a/src/frontend/gui/gui_util.cpp +++ b/src/frontend/gui/gui_util.cpp @@ -4,6 +4,8 @@ #include +#include "../locale/langs.h" + using namespace gui; using glm::vec2; using glm::vec4; @@ -11,7 +13,7 @@ using std::string; using std::wstring; Button* guiutil::backButton(PagesControl* menu) { - return (new Button(L"Back", vec4(10.f)))->listenAction([=](GUI* gui) { + return (new Button(langs::get(L"Back"), vec4(10.f)))->listenAction([=](GUI* gui) { menu->back(); }); } @@ -27,7 +29,7 @@ void guiutil::alert(GUI* gui, wstring text, gui::runnable on_hidden) { Panel* panel = new Panel(vec2(500, 200), vec4(8.0f), 8.0f); panel->color(vec4(0.0f, 0.0f, 0.0f, 0.5f)); panel->add(new Label(text)); - panel->add((new Button(L"Ok", vec4(10.f)))->listenAction([=](GUI* gui) { + panel->add((new Button(langs::get(L"Ok"), vec4(10.f)))->listenAction([=](GUI* gui) { if (on_hidden) on_hidden(); menu->back(); @@ -39,6 +41,9 @@ void guiutil::alert(GUI* gui, wstring text, gui::runnable on_hidden) { void guiutil::confirm(GUI* gui, wstring text, gui::runnable on_confirm, wstring yestext, wstring notext) { + if (yestext.empty()) yestext = langs::get(L"Yes"); + if (notext.empty()) notext = langs::get(L"No"); + PagesControl* menu = gui->getMenu(); Panel* panel = new Panel(vec2(600, 200), vec4(8.0f), 8.0f); panel->color(vec4(0.0f, 0.0f, 0.0f, 0.5f)); diff --git a/src/frontend/gui/gui_util.h b/src/frontend/gui/gui_util.h index 30640178..f2e0b80f 100644 --- a/src/frontend/gui/gui_util.h +++ b/src/frontend/gui/gui_util.h @@ -13,7 +13,7 @@ namespace guiutil { gui::Button* gotoButton(std::wstring text, std::string page, gui::PagesControl* menu); void alert(gui::GUI* gui, std::wstring text, gui::runnable on_hidden=nullptr); void confirm(gui::GUI* gui, std::wstring text, gui::runnable on_confirm=nullptr, - std::wstring yestext=L"Yes", std::wstring notext=L"No"); + std::wstring yestext=L"", std::wstring notext=L""); } #endif // FRONTEND_GUI_GUI_UTIL_H_ diff --git a/src/frontend/locale/langs.cpp b/src/frontend/locale/langs.cpp new file mode 100644 index 00000000..598fd92a --- /dev/null +++ b/src/frontend/locale/langs.cpp @@ -0,0 +1,133 @@ +#include "langs.h" + +#include + +#include "../../coders/json.h" +#include "../../coders/commons.h" +#include "../../content/ContentPack.h" +#include "../../files/files.h" +#include "../../util/stringutil.h" + +using std::string; +using std::wstring; +using std::vector; +using std::unique_ptr; +using std::unordered_map; +using std::filesystem::path; +namespace fs = std::filesystem; + +unique_ptr langs::current; +vector langs::locales_info; + +langs::Lang::Lang(string locale) : locale(locale) { +} + +const wstring& langs::Lang::get(const wstring& key) const { + auto found = map.find(key); + if (found == map.end()) { + return key; + } + return found->second; +} + +void langs::Lang::put(const wstring& key, const wstring& text) { + map[key] = text; +} + +/* Language key-value txt files parser */ +class Reader : public BasicParser { + void skipWhitespace() override { + BasicParser::skipWhitespace(); + if (hasNext() && source[pos] == '#') { + skipLine(); + if (hasNext() && is_whitespace(peek())) { + skipWhitespace(); + } + } + } +public: + Reader(string file, string source) : BasicParser(file, source) { + } + + void read(langs::Lang& lang, std::string prefix) { + skipWhitespace(); + while (hasNext()) { + string key = parseString('=', true); + util::trim(key); + key = prefix + key; + string text = parseString('\n', false); + util::trim(text); + lang.put(util::str2wstr_utf8(key), + util::str2wstr_utf8(text)); + skipWhitespace(); + } + } +}; + +void langs::loadLocalesInfo(const path& resdir, string& fallback) { + path file = resdir/path(langs::TEXTS_FOLDER)/path("langs.json"); + unique_ptr root (files::read_json(file)); + + langs::locales_info.clear(); + root->str("fallback", fallback); + + auto langs = root->obj("langs"); + if (langs) { + for (auto& entry : langs->map) { + auto langInfo = entry.second; + + string name; + if (langInfo->type == json::valtype::object) { + name = langInfo->value.obj->getStr("name", "none"); + } else { + continue; + } + + std::cout << "locale " << entry.first << " (" << name << ") added" << std::endl; + langs::locales_info.push_back(LocaleInfo {entry.first, name}); + } + } +} + +void langs::load(const path& resdir, + const string& locale, + vector& packs, + Lang& lang) { + path filename = path(TEXTS_FOLDER)/path(locale + LANG_FILE_EXT); + path core_file = resdir/filename; + if (fs::is_regular_file(core_file)) { + string text = files::read_string(core_file); + Reader reader(core_file.string(), text); + reader.read(lang, ""); + } + for (auto pack : packs) { + path file = pack->getFolder()/filename; + if (fs::is_regular_file(file)) { + string text = files::read_string(file); + Reader reader(file.string(), text); + reader.read(lang, pack->getId()+":"); + } + } +} + +void langs::load(const path& resdir, + const string& locale, + const string& fallback, + vector& packs) { + unique_ptr lang (new Lang(locale)); + load(resdir, fallback, packs, *lang.get()); + load(resdir, locale, packs, *lang.get()); + current.reset(lang.release()); +} + +void langs::setup(const path& resdir, + const string& locale, + vector& packs) { + string fallback = langs::FALLBACK_DEFAULT; + langs::loadLocalesInfo(resdir, fallback); + langs::load(resdir, locale, fallback, packs); +} + +const wstring& langs::get(const wstring& key) { + return current->get(key); +} diff --git a/src/frontend/locale/langs.h b/src/frontend/locale/langs.h new file mode 100644 index 00000000..6dcdc458 --- /dev/null +++ b/src/frontend/locale/langs.h @@ -0,0 +1,65 @@ +#ifndef FRONTEND_LOCALE_LANGS_H +#define FRONTEND_LOCALE_LANGS_H + +#include +#include +#include +#include +#include + +class ContentPack; + +namespace langs { + const char LANG_FILE_EXT[] = ".txt"; + const char TEXTS_FOLDER[] = "texts"; + const char FALLBACK_DEFAULT[] = "en_US"; + + /* + Translation is mostly used for rendered text, + so Font.draw and Lang both use wstring for strings. + + Translation key is wstring too, allowing to use it as value + if no any translation found, without conversion required + + Key syntax: `modid:context.key` where modid is added at runtime for + content pack texts + */ + class Lang { + std::string locale; + std::unordered_map map; + public: + Lang(std::string locale); + + const std::wstring& get(const std::wstring& key) const; + void put(const std::wstring& key, const std::wstring& text); + }; + + struct LocaleInfo { + std::string locale; + std::string name; + }; + + extern std::unique_ptr current; + extern std::vector locales_info; + + extern void loadLocalesInfo( + const std::filesystem::path& resdir, + std::string& fallback); + + extern void load(const std::filesystem::path& resdir, + const std::string& locale, + std::vector& packs, + Lang& lang); + extern void load(const std::filesystem::path& resdir, + const std::string& locale, + const std::string& fallback, + std::vector& packs); + + extern const std::wstring& get(const std::wstring& key); + + extern void setup(const std::filesystem::path& resdir, + const std::string& locale, + std::vector& packs); +} + +#endif // FRONTEND_LOCALE_LANGS_H diff --git a/src/frontend/menu.cpp b/src/frontend/menu.cpp index 8ccf3fb5..922feecd 100644 --- a/src/frontend/menu.cpp +++ b/src/frontend/menu.cpp @@ -23,6 +23,7 @@ #include "../content/ContentLUT.h" #include "gui/gui_util.h" +#include "locale/langs.h" using glm::vec2; using glm::vec4; @@ -41,7 +42,7 @@ void show_content_missing(GUI* gui, const Content* content, ContentLUT* lut) { PagesControl* menu = gui->getMenu(); Panel* panel = new Panel(vec2(500, 200), vec4(8.0f), 8.0f); panel->color(vec4(0.0f, 0.0f, 0.0f, 0.5f)); - panel->add(new Label(L"Content missing!")); + panel->add(new Label(langs::get(L"menu.missing-content"))); Panel* subpanel = new Panel(vec2(500, 100)); subpanel->color(vec4(0.0f, 0.0f, 0.0f, 0.5f)); @@ -67,7 +68,7 @@ void show_content_missing(GUI* gui, const Content* content, ContentLUT* lut) { subpanel->maxLength(400); panel->add(subpanel); - panel->add((new Button(L"Back to Main Menu", vec4(8.0f)))->listenAction([=](GUI*){ + panel->add((new Button(langs::get(L"menu.back-to-menu"), vec4(8.0f)))->listenAction([=](GUI*){ menu->back(); })); panel->refresh(); @@ -97,7 +98,7 @@ Panel* create_main_menu_panel(Engine* engine, PagesControl* menu) { Panel* panel = new Panel(vec2(400, 200), vec4(5.0f), 1.0f); panel->color(vec4(0.0f)); - panel->add(guiutil::gotoButton(L"New World", "new-world", menu)); + panel->add(guiutil::gotoButton(langs::get(L"menu.new-world"), "new-world", menu)); Panel* worldsPanel = new Panel(vec2(390, 200), vec4(5.0f)); worldsPanel->color(vec4(1.0f, 1.0f, 1.0f, 0.07f)); @@ -135,8 +136,8 @@ Panel* create_main_menu_panel(Engine* engine, PagesControl* menu) { } } panel->add(worldsPanel); - panel->add(guiutil::gotoButton(L"Settings", "settings", menu)); - panel->add((new Button(L"Quit", vec4(10.f)))->listenAction([](GUI* gui) { + panel->add(guiutil::gotoButton(langs::get(L"menu.settings"), "settings", menu)); + panel->add((new Button(langs::get(L"menu.quit"), vec4(10.f)))->listenAction([](GUI* gui) { Window::setShouldClose(true); })); panel->refresh(); @@ -149,7 +150,7 @@ Panel* create_new_world_panel(Engine* engine, PagesControl* menu) { TextBox* worldNameInput; { - Label* label = new Label(L"World Name"); + Label* label = new Label(langs::get(L"world.name")); panel->add(label); TextBox* input = new TextBox(L"New World", vec4(6.0f)); @@ -159,7 +160,7 @@ Panel* create_new_world_panel(Engine* engine, PagesControl* menu) { TextBox* seedInput; { - Label* label = new Label(L"Seed"); + Label* label = new Label(langs::get(L"world.seed")); panel->add(shared_ptr(label)); uint64_t randseed = rand() ^ (rand() << 8) ^ @@ -173,7 +174,7 @@ Panel* create_new_world_panel(Engine* engine, PagesControl* menu) { } { - Button* button = new Button(L"Create World", vec4(10.0f)); + Button* button = new Button(langs::get(L"world.create"), vec4(10.0f)); button->margin(vec4(1, 20, 1, 1)); vec4 basecolor = worldNameInput->color(); button->listenAction([=](GUI*) { @@ -239,7 +240,7 @@ Panel* create_controls_panel(Engine* engine, PagesControl* menu) { std::wstringstream ss; ss << std::fixed << std::setprecision(1); ss << engine->getSettings().camera.sensitivity; - return L"Mouse Sensitivity: "+ss.str(); + return langs::get(L"mouse.sensitivity")+L": "+ss.str(); })); TrackBar* trackbar = new TrackBar(0.1, 10.0, 2.0, 0.1, 4); @@ -264,7 +265,7 @@ Panel* create_controls_panel(Engine* engine, PagesControl* menu) { InputBindBox* bindbox = new InputBindBox(entry.second); subpanel->add(bindbox); - Label* label = new Label(util::str2wstr_utf8(bindname)); + Label* label = new Label(langs::get(util::str2wstr_utf8(bindname))); label->margin(vec4(6.0f)); subpanel->add(label); scrollPanel->add(subpanel); @@ -282,7 +283,7 @@ Panel* create_settings_panel(Engine* engine, PagesControl* menu) { /* Load Distance setting track bar */{ panel->add((new Label(L""))->textSupplier([=]() { - return L"Load Distance: " + + return langs::get(L"chunks.load-distance")+L": " + std::to_wstring(engine->getSettings().chunks.loadDistance); })); @@ -298,7 +299,7 @@ Panel* create_settings_panel(Engine* engine, PagesControl* menu) { /* Load Speed setting track bar */{ panel->add((new Label(L""))->textSupplier([=]() { - return L"Load Speed: " + + return langs::get(L"chunks.load-speed")+L": " + std::to_wstring(engine->getSettings().chunks.loadSpeed); })); @@ -317,7 +318,7 @@ Panel* create_settings_panel(Engine* engine, PagesControl* menu) { std::wstringstream ss; ss << std::fixed << std::setprecision(1); ss << engine->getSettings().graphics.fogCurve; - return L"Fog Curve: " + ss.str(); + return langs::get(L"graphics.fog-curve")+L": " + ss.str(); })); TrackBar* trackbar = new TrackBar(1.0, 6.0, 1.0, 0.1, 2); @@ -333,7 +334,7 @@ Panel* create_settings_panel(Engine* engine, PagesControl* menu) { /* Fov setting track bar */{ panel->add((new Label(L""))->textSupplier([=]() { int fov = (int)engine->getSettings().camera.fov; - return L"FOV: "+std::to_wstring(fov)+L"°"; + return langs::get(L"camera.fov")+L": "+std::to_wstring(fov)+L"°"; })); TrackBar* trackbar = new TrackBar(30.0, 120.0, 90, 1, 4); @@ -360,7 +361,7 @@ Panel* create_settings_panel(Engine* engine, PagesControl* menu) { engine->getSettings().display.swapInterval = checked; }); checkpanel->add(checkbox); - checkpanel->add(new Label(L"V-Sync")); + checkpanel->add(new Label(langs::get(L"display.vsync"))); panel->add(checkpanel); } @@ -379,12 +380,12 @@ Panel* create_settings_panel(Engine* engine, PagesControl* menu) { engine->getSettings().graphics.backlight = checked; }); checkpanel->add(checkbox); - checkpanel->add(new Label(L"Backlight")); + checkpanel->add(new Label(langs::get(L"graphics.backlight"))); panel->add(checkpanel); } - panel->add(guiutil::gotoButton(L"Controls", "controls", menu)); + panel->add(guiutil::gotoButton(langs::get(L"menu.controls"), "controls", menu)); panel->add(guiutil::backButton(menu)); panel->refresh(); return panel; @@ -394,15 +395,15 @@ Panel* create_pause_panel(Engine* engine, PagesControl* menu) { Panel* panel = new Panel(vec2(400, 200)); panel->color(vec4(0.0f)); { - Button* button = new Button(L"Continue", vec4(10.0f)); + Button* button = new Button(langs::get(L"Continue"), vec4(10.0f)); button->listenAction([=](GUI*){ menu->reset(); }); panel->add(shared_ptr(button)); } - panel->add(guiutil::gotoButton(L"Settings", "settings", menu)); + panel->add(guiutil::gotoButton(langs::get(L"Settings"), "settings", menu)); { - Button* button = new Button(L"Save and Quit to Menu", vec4(10.f)); + Button* button = new Button(langs::get(L"menu.save-and-quit"), vec4(10.f)); button->listenAction([engine](GUI*){ engine->setScreen(shared_ptr(new MenuScreen(engine))); }); diff --git a/src/settings.h b/src/settings.h index 69f9ca75..dc21070f 100644 --- a/src/settings.h +++ b/src/settings.h @@ -61,12 +61,17 @@ struct DebugSettings { bool doWriteLights = true; }; +struct UiSettings { + std::string language = "auto"; +}; + struct EngineSettings { DisplaySettings display; ChunksSettings chunks; CameraSettings camera; GraphicsSettings graphics; DebugSettings debug; + UiSettings ui; }; #endif // SRC_SETTINGS_H_ \ No newline at end of file diff --git a/src/util/platform.cpp b/src/util/platform.cpp index d4f338d9..95edbbca 100644 --- a/src/util/platform.cpp +++ b/src/util/platform.cpp @@ -20,6 +20,15 @@ path platform::get_controls_file() { return path(CONTROLS_FILE); } +std::string platform::detect_locale() { + // TODO: implement + std::string name = setlocale(LC_ALL, nullptr); + if (name.find("ru_RU") != std::string::npos) { + return "ru_RU"; + } + return "en_US"; +} + #ifdef WIN32 #include diff --git a/src/util/platform.h b/src/util/platform.h index 65ed3ce8..2a320698 100644 --- a/src/util/platform.h +++ b/src/util/platform.h @@ -8,6 +8,7 @@ namespace platform { extern void configure_encoding(); extern std::filesystem::path get_settings_file(); extern std::filesystem::path get_controls_file(); + extern std::string detect_locale(); } #endif // UTIL_PLATFORM_H_ \ No newline at end of file