languages support (WIP)

This commit is contained in:
MihailRis
2023-12-12 22:02:17 +03:00
parent 0837cf4f03
commit 7bf70bb26e
21 changed files with 456 additions and 38 deletions
+7 -2
View File
@@ -4,6 +4,8 @@
#include <glm/glm.hpp>
#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));
+1 -1
View File
@@ -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_
+133
View File
@@ -0,0 +1,133 @@
#include "langs.h"
#include <iostream>
#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::Lang> langs::current;
vector<langs::LocaleInfo> 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<json::JObject> 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<const ContentPack*>& 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<const ContentPack*>& packs) {
unique_ptr<Lang> 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<const ContentPack*>& 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);
}
+65
View File
@@ -0,0 +1,65 @@
#ifndef FRONTEND_LOCALE_LANGS_H
#define FRONTEND_LOCALE_LANGS_H
#include <string>
#include <vector>
#include <memory>
#include <filesystem>
#include <unordered_map>
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<std::wstring, std::wstring> 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<Lang> current;
extern std::vector<LocaleInfo> 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<const ContentPack*>& packs,
Lang& lang);
extern void load(const std::filesystem::path& resdir,
const std::string& locale,
const std::string& fallback,
std::vector<const ContentPack*>& packs);
extern const std::wstring& get(const std::wstring& key);
extern void setup(const std::filesystem::path& resdir,
const std::string& locale,
std::vector<const ContentPack*>& packs);
}
#endif // FRONTEND_LOCALE_LANGS_H
+21 -20
View File
@@ -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<UINode>(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<UINode>(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<Screen>(new MenuScreen(engine)));
});