afegit fixer de locales (textos centralitzats)

This commit is contained in:
2026-04-05 01:24:24 +02:00
parent 5ac570df0f
commit 788a883c71
9 changed files with 226 additions and 51 deletions

View File

@@ -19,6 +19,9 @@ set(APP_SOURCES
source/core/jail/jgame.cpp
source/core/jail/jinput.cpp
# Core - Locale (nova capa)
source/core/locale/locale.cpp
# Core - Capa de presentación (nueva)
source/core/rendering/menu.cpp
source/core/rendering/overlay.cpp

68
data/locale/ca.yaml Normal file
View File

@@ -0,0 +1,68 @@
# Aventures En Egipte — Textos en Valencià (idioma per defecte)
# Edita aquest fitxer per canviar els textos del joc.
# Les claus es referencien des del codi amb notació punt: "menu.titles.root"
menu:
titles:
root: "OPCIONS"
video: "VIDEO"
audio: "AUDIO"
controls: "CONTROLS"
items:
video: "VIDEO"
audio: "AUDIO"
controls: "CONTROLS"
zoom: "ZOOM"
screen: "PANTALLA"
shader: "SHADER"
aspect_4_3: "ASPECTE 4:3"
supersampling: "SUPERSAMPLING"
shader_type: "TIPUS SHADER"
preset: "PRESET"
stretch_filter: "FILTRE 4:3"
render_info: "RENDER INFO"
uptime: "TEMPS DE JOC"
master_enable: "AUDIO"
master_volume: "MASTER"
music: "MUSICA"
music_volume: "VOL MUSICA"
sounds: "SONS"
sounds_volume: "VOL SONS"
move_up: "MOU AMUNT"
move_down: "MOU AVALL"
move_left: "MOU ESQUERRA"
move_right: "MOU DRETA"
menu_key: "TECLA MENU"
values:
"yes": "SI"
"no": "NO"
"on": "ON"
"off": "OFF"
fullscreen: "COMPLETA"
windowed: "FINESTRA"
linear: "LINEAR"
nearest: "NEAREST"
top: "TOP"
bottom: "BOTTOM"
press_key: "<PREM TECLA>"
empty: "(BUIT)"
unknown: "---"
notifications:
exit_double_esc: "TORNA A PULSAR ESC PER EIXIR"
zoom_fmt: "ZOOM %dX"
fullscreen: "PANTALLA COMPLETA"
windowed: "FINESTRA"
shader_on: "SHADER ON"
shader_off: "SHADER OFF"
aspect_43: "4:3 CRT"
aspect_square: "PIXELS QUADRATS"
ss_on: "SUPERSAMPLING ON"
ss_off: "SUPERSAMPLING OFF"
preset_fmt: "PRESET: %s"
filter_linear: "FILTRE: LINEAR"
filter_nearest: "FILTRE: NEAREST"
pause: "PAUSA"
resume: "REPRES"

View File

@@ -4,6 +4,7 @@
#include <string>
#include "core/jail/jinput.hpp"
#include "core/locale/locale.hpp"
#include "core/rendering/overlay.hpp"
#include "core/rendering/screen.hpp"
#include "game/options.hpp"
@@ -29,7 +30,7 @@ namespace GlobalInputs {
if (dec_zoom && !dec_zoom_prev) {
Screen::get()->decZoom();
char msg[32];
snprintf(msg, sizeof(msg), "ZOOM %dx", Screen::get()->getZoom());
snprintf(msg, sizeof(msg), Locale::get("notifications.zoom_fmt"), Screen::get()->getZoom());
Overlay::showNotification(msg);
}
if (dec_zoom) consumed = true;
@@ -40,7 +41,7 @@ namespace GlobalInputs {
if (inc_zoom && !inc_zoom_prev) {
Screen::get()->incZoom();
char msg[32];
snprintf(msg, sizeof(msg), "ZOOM %dx", Screen::get()->getZoom());
snprintf(msg, sizeof(msg), Locale::get("notifications.zoom_fmt"), Screen::get()->getZoom());
Overlay::showNotification(msg);
}
if (inc_zoom) consumed = true;
@@ -50,7 +51,7 @@ namespace GlobalInputs {
bool fullscreen = JI_KeyPressed(Options::keys_gui.fullscreen);
if (fullscreen && !fullscreen_prev) {
Screen::get()->toggleFullscreen();
Overlay::showNotification(Screen::get()->isFullscreen() ? "PANTALLA COMPLETA" : "FINESTRA");
Overlay::showNotification(Screen::get()->isFullscreen() ? Locale::get("notifications.fullscreen") : Locale::get("notifications.windowed"));
}
if (fullscreen) consumed = true;
fullscreen_prev = fullscreen;
@@ -59,7 +60,7 @@ namespace GlobalInputs {
bool shader = JI_KeyPressed(Options::keys_gui.toggle_shader);
if (shader && !shader_prev) {
Screen::get()->toggleShaders();
Overlay::showNotification(Options::video.shader_enabled ? "SHADER ON" : "SHADER OFF");
Overlay::showNotification(Options::video.shader_enabled ? Locale::get("notifications.shader_on") : Locale::get("notifications.shader_off"));
}
if (shader) consumed = true;
shader_prev = shader;
@@ -68,7 +69,7 @@ namespace GlobalInputs {
bool aspect = JI_KeyPressed(Options::keys_gui.toggle_aspect_ratio);
if (aspect && !aspect_prev) {
Screen::get()->toggleAspectRatio();
Overlay::showNotification(Options::video.aspect_ratio_4_3 ? "4:3 CRT" : "PIXELS QUADRATS");
Overlay::showNotification(Options::video.aspect_ratio_4_3 ? Locale::get("notifications.aspect_43") : Locale::get("notifications.aspect_square"));
}
if (aspect) consumed = true;
aspect_prev = aspect;
@@ -77,7 +78,7 @@ namespace GlobalInputs {
bool ss = JI_KeyPressed(Options::keys_gui.toggle_supersampling);
if (ss && !ss_prev) {
Screen::get()->toggleSupersampling();
Overlay::showNotification(Options::video.supersampling ? "SUPERSAMPLING ON" : "SUPERSAMPLING OFF");
Overlay::showNotification(Options::video.supersampling ? Locale::get("notifications.ss_on") : Locale::get("notifications.ss_off"));
}
if (ss) consumed = true;
ss_prev = ss;
@@ -98,7 +99,7 @@ namespace GlobalInputs {
if (next_preset && !next_preset_prev) {
Screen::get()->nextPreset();
char msg[64];
snprintf(msg, sizeof(msg), "PRESET: %s", Screen::get()->getCurrentPresetName());
snprintf(msg, sizeof(msg), Locale::get("notifications.preset_fmt"), Screen::get()->getCurrentPresetName());
Overlay::showNotification(msg);
}
if (next_preset) consumed = true;
@@ -108,7 +109,7 @@ namespace GlobalInputs {
bool stretch_filter = JI_KeyPressed(Options::keys_gui.toggle_stretch_filter);
if (stretch_filter && !stretch_filter_prev) {
Screen::get()->toggleStretchFilter();
Overlay::showNotification(Options::video.stretch_filter_linear ? "FILTRE: LINEAR" : "FILTRE: NEAREST");
Overlay::showNotification(Options::video.stretch_filter_linear ? Locale::get("notifications.filter_linear") : Locale::get("notifications.filter_nearest"));
}
if (stretch_filter) consumed = true;
stretch_filter_prev = stretch_filter;

View File

@@ -0,0 +1,57 @@
#include "core/locale/locale.hpp"
#include <iostream>
#include <string>
#include <unordered_map>
#include "core/jail/jfile.hpp"
#include "external/fkyaml_node.hpp"
namespace Locale {
static std::unordered_map<std::string, std::string> strings_;
// Aplana un node YAML en claus amb notació punt
static void traverse(const fkyaml::node& node, const std::string& prefix) {
if (node.is_mapping()) {
for (auto it = node.begin(); it != node.end(); ++it) {
std::string key = it.key().get_value<std::string>();
std::string full = prefix.empty() ? key : prefix + "." + key;
traverse(it.value(), full);
}
} else if (node.is_scalar()) {
try {
strings_[prefix] = node.get_value<std::string>();
} catch (...) {}
}
}
bool load(const char* filename) {
int size = 0;
char* buffer = file_getfilebuffer(filename, size, true);
if (!buffer || size <= 0) {
std::cerr << "Locale: unable to load " << filename << '\n';
return false;
}
std::string content(buffer, size);
free(buffer);
try {
auto yaml = fkyaml::node::deserialize(content);
strings_.clear();
traverse(yaml, "");
std::cout << "Locale loaded: " << strings_.size() << " string(s) from " << filename << '\n';
return true;
} catch (const fkyaml::exception& e) {
std::cerr << "Locale: error parsing " << filename << ": " << e.what() << '\n';
return false;
}
}
auto get(const char* key) -> const char* {
auto it = strings_.find(key);
if (it != strings_.end()) return it->second.c_str();
return key; // fallback: retorna la clau mateixa
}
} // namespace Locale

View File

@@ -0,0 +1,12 @@
#pragma once
// Locale: carrega cadenes de text des d'un fitxer YAML i les exposa per clau.
// Les claus són nested amb notació punt ("menu.items.zoom").
// Si una clau no existeix, Locale::get torna la clau mateixa (útil per debug).
namespace Locale {
bool load(const char* filename);
// Retorna la cadena associada a la clau. El punter és estable durant tota la
// sessió (no canvia), per tant es pot guardar en const char*.
auto get(const char* key) -> const char*;
} // namespace Locale

View File

@@ -6,6 +6,7 @@
#include <string>
#include <vector>
#include "core/locale/locale.hpp"
#include "core/rendering/overlay.hpp"
#include "core/rendering/screen.hpp"
#include "core/rendering/text.hpp"
@@ -92,8 +93,8 @@ namespace Menu {
// --- Helpers ---
static std::string yesNo(bool b) { return b ? "SI" : "NO"; }
static std::string onOff(bool b) { return b ? "ON" : "OFF"; }
static std::string yesNo(bool b) { return b ? Locale::get("menu.values.yes") : Locale::get("menu.values.no"); }
static std::string onOff(bool b) { return b ? Locale::get("menu.values.on") : Locale::get("menu.values.off"); }
// --- Builders de pàgines ---
@@ -102,50 +103,65 @@ namespace Menu {
static Page buildControls();
static Page buildRoot() {
Page p{"OPCIONS", {}, 0};
p.items.push_back({"VIDEO", ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildVideo()); }, nullptr});
p.items.push_back({"AUDIO", ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildAudio()); }, nullptr});
p.items.push_back({"CONTROLS", ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildControls()); }, nullptr});
Page p{Locale::get("menu.titles.root"), {}, 0};
p.items.push_back({Locale::get("menu.items.video"), ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildVideo()); }, nullptr});
p.items.push_back({Locale::get("menu.items.audio"), ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildAudio()); }, nullptr});
p.items.push_back({Locale::get("menu.items.controls"), ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildControls()); }, nullptr});
return p;
}
static Page buildVideo() {
Page p{"VIDEO", {}, 0};
Page p{Locale::get("menu.titles.video"), {}, 0};
p.items.push_back({"ZOOM", ItemKind::IntRange, [] {
p.items.push_back({Locale::get("menu.items.zoom"), ItemKind::IntRange, [] {
char buf[16];
std::snprintf(buf, sizeof(buf), "%dX", Screen::get()->getZoom());
return std::string(buf); }, [](int dir) {
if (dir < 0) Screen::get()->decZoom();
else if (dir > 0) Screen::get()->incZoom(); }, nullptr});
p.items.push_back({"PANTALLA", ItemKind::Toggle, [] { return std::string(Screen::get()->isFullscreen() ? "COMPLETA" : "FINESTRA"); }, [](int) { Screen::get()->toggleFullscreen(); }, nullptr});
p.items.push_back({Locale::get("menu.items.screen"), ItemKind::Toggle,
[] { return std::string(Screen::get()->isFullscreen() ? Locale::get("menu.values.fullscreen") : Locale::get("menu.values.windowed")); },
[](int) { Screen::get()->toggleFullscreen(); }, nullptr});
p.items.push_back({"SHADER", ItemKind::Toggle, [] { return onOff(Options::video.shader_enabled); }, [](int) { Screen::get()->toggleShaders(); }, nullptr});
p.items.push_back({Locale::get("menu.items.shader"), ItemKind::Toggle,
[] { return onOff(Options::video.shader_enabled); },
[](int) { Screen::get()->toggleShaders(); }, nullptr});
p.items.push_back({"ASPECTE 4:3", ItemKind::Toggle, [] { return yesNo(Options::video.aspect_ratio_4_3); }, [](int) { Screen::get()->toggleAspectRatio(); }, nullptr});
p.items.push_back({Locale::get("menu.items.aspect_4_3"), ItemKind::Toggle,
[] { return yesNo(Options::video.aspect_ratio_4_3); },
[](int) { Screen::get()->toggleAspectRatio(); }, nullptr});
p.items.push_back({"SUPERSAMPLING", ItemKind::Toggle, [] { return onOff(Options::video.supersampling); }, [](int) { Screen::get()->toggleSupersampling(); }, nullptr});
p.items.push_back({Locale::get("menu.items.supersampling"), ItemKind::Toggle,
[] { return onOff(Options::video.supersampling); },
[](int) { Screen::get()->toggleSupersampling(); }, nullptr});
p.items.push_back({"TIPUS SHADER", ItemKind::Cycle, [] { return std::string(Screen::get()->getActiveShaderName()); }, [](int dir) {
p.items.push_back({Locale::get("menu.items.shader_type"), ItemKind::Cycle,
[] { return std::string(Screen::get()->getActiveShaderName()); }, [](int dir) {
if (dir < 0) Screen::get()->prevShaderType();
else Screen::get()->nextShaderType(); }, nullptr});
p.items.push_back({"PRESET", ItemKind::Cycle, [] { return std::string(Screen::get()->getCurrentPresetName()); }, [](int dir) {
p.items.push_back({Locale::get("menu.items.preset"), ItemKind::Cycle,
[] { return std::string(Screen::get()->getCurrentPresetName()); }, [](int dir) {
if (dir < 0) Screen::get()->prevPreset();
else Screen::get()->nextPreset(); }, nullptr});
p.items.push_back({"FILTRE 4:3", ItemKind::Toggle, [] { return std::string(Options::video.stretch_filter_linear ? "LINEAR" : "NEAREST"); }, [](int) { Screen::get()->toggleStretchFilter(); }, nullptr});
p.items.push_back({Locale::get("menu.items.stretch_filter"), ItemKind::Toggle,
[] { return std::string(Options::video.stretch_filter_linear ? Locale::get("menu.values.linear") : Locale::get("menu.values.nearest")); },
[](int) { Screen::get()->toggleStretchFilter(); }, nullptr});
p.items.push_back({"RENDER INFO", ItemKind::Cycle, [] {
p.items.push_back({Locale::get("menu.items.render_info"), ItemKind::Cycle, [] {
switch (Options::render_info.position) {
case Options::RenderInfoPosition::OFF: return std::string("OFF");
case Options::RenderInfoPosition::TOP: return std::string("TOP");
case Options::RenderInfoPosition::BOTTOM: return std::string("BOTTOM");
case Options::RenderInfoPosition::OFF: return std::string(Locale::get("menu.values.off"));
case Options::RenderInfoPosition::TOP: return std::string(Locale::get("menu.values.top"));
case Options::RenderInfoPosition::BOTTOM: return std::string(Locale::get("menu.values.bottom"));
}
return std::string("OFF"); }, [](int dir) { Overlay::cycleRenderInfo(dir); }, nullptr});
return std::string(Locale::get("menu.values.off")); },
[](int dir) { Overlay::cycleRenderInfo(dir); }, nullptr});
p.items.push_back({"HORA", ItemKind::Toggle, [] { return onOff(Options::render_info.show_time); }, [](int) { Options::render_info.show_time = !Options::render_info.show_time; }, nullptr});
p.items.push_back({Locale::get("menu.items.uptime"), ItemKind::Toggle,
[] { return onOff(Options::render_info.show_time); },
[](int) { Options::render_info.show_time = !Options::render_info.show_time; }, nullptr});
return p;
}
@@ -169,35 +185,47 @@ namespace Menu {
}
static Page buildControls() {
Page p{"CONTROLS", {}, 0};
p.items.push_back({"MOU AMUNT", ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.up});
p.items.push_back({"MOU AVALL", ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.down});
p.items.push_back({"MOU ESQUERRA", ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.left});
p.items.push_back({"MOU DRETA", ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.right});
p.items.push_back({"TECLA MENU", ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_gui.menu_toggle});
Page p{Locale::get("menu.titles.controls"), {}, 0};
p.items.push_back({Locale::get("menu.items.move_up"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.up});
p.items.push_back({Locale::get("menu.items.move_down"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.down});
p.items.push_back({Locale::get("menu.items.move_left"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.left});
p.items.push_back({Locale::get("menu.items.move_right"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.right});
p.items.push_back({Locale::get("menu.items.menu_key"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_gui.menu_toggle});
return p;
}
static Page buildAudio() {
Page p{"AUDIO", {}, 0};
Page p{Locale::get("menu.titles.audio"), {}, 0};
p.items.push_back({"AUDIO", ItemKind::Toggle, [] { return onOff(Options::audio.enabled); }, [](int) {
p.items.push_back({Locale::get("menu.items.master_enable"), ItemKind::Toggle,
[] { return onOff(Options::audio.enabled); }, [](int) {
Options::audio.enabled = !Options::audio.enabled;
Options::applyAudio(); }, nullptr});
Options::applyAudio(); },
nullptr});
p.items.push_back({"MASTER", ItemKind::IntRange, [] { return volPct(Options::audio.volume); }, [](int dir) { stepVolume(Options::audio.volume, dir); }, nullptr});
p.items.push_back({Locale::get("menu.items.master_volume"), ItemKind::IntRange,
[] { return volPct(Options::audio.volume); },
[](int dir) { stepVolume(Options::audio.volume, dir); }, nullptr});
p.items.push_back({"MUSICA", ItemKind::Toggle, [] { return onOff(Options::audio.music_enabled); }, [](int) {
p.items.push_back({Locale::get("menu.items.music"), ItemKind::Toggle,
[] { return onOff(Options::audio.music_enabled); }, [](int) {
Options::audio.music_enabled = !Options::audio.music_enabled;
Options::applyAudio(); }, nullptr});
Options::applyAudio(); },
nullptr});
p.items.push_back({"VOL MUSICA", ItemKind::IntRange, [] { return volPct(Options::audio.music_volume); }, [](int dir) { stepVolume(Options::audio.music_volume, dir); }, nullptr});
p.items.push_back({Locale::get("menu.items.music_volume"), ItemKind::IntRange,
[] { return volPct(Options::audio.music_volume); },
[](int dir) { stepVolume(Options::audio.music_volume, dir); }, nullptr});
p.items.push_back({"SONS", ItemKind::Toggle, [] { return onOff(Options::audio.sound_enabled); }, [](int) {
p.items.push_back({Locale::get("menu.items.sounds"), ItemKind::Toggle,
[] { return onOff(Options::audio.sound_enabled); }, [](int) {
Options::audio.sound_enabled = !Options::audio.sound_enabled;
Options::applyAudio(); }, nullptr});
Options::applyAudio(); },
nullptr});
p.items.push_back({"VOL SONS", ItemKind::IntRange, [] { return volPct(Options::audio.sound_volume); }, [](int dir) { stepVolume(Options::audio.sound_volume, dir); }, nullptr});
p.items.push_back({Locale::get("menu.items.sounds_volume"), ItemKind::IntRange,
[] { return volPct(Options::audio.sound_volume); },
[](int dir) { stepVolume(Options::audio.sound_volume, dir); }, nullptr});
return p;
}
@@ -381,7 +409,7 @@ namespace Menu {
// Items o placeholder buit
int items_y = title_line_y + 4;
if (page.items.empty()) {
const char* empty_text = "(BUIT)";
const char* empty_text = Locale::get("menu.values.empty");
int ew = font_->width(empty_text);
font_->drawClipped(pixel_data, box_x + (BOX_W - ew) / 2 + x_offset, items_y + 2, empty_text, EMPTY_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
return;
@@ -406,8 +434,8 @@ namespace Menu {
font_->drawClipped(pixel_data, box_x + BOX_W - ITEM_PAD_X - aw + x_offset, y, arrow, ac, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
} else if (item.kind == ItemKind::KeyBind) {
bool this_capturing = (capturing_ == item.scancode);
const char* text = this_capturing ? "<PREM TECLA>" : (item.scancode ? SDL_GetScancodeName(*item.scancode) : "");
if (!text || !*text) text = "---";
const char* text = this_capturing ? Locale::get("menu.values.press_key") : (item.scancode ? SDL_GetScancodeName(*item.scancode) : "");
if (!text || !*text) text = Locale::get("menu.values.unknown");
int tw = font_->width(text);
Uint32 tc = this_capturing ? 0xFF00FFFF : (selected ? CURSOR_COLOR : VALUE_COLOR);
font_->drawClipped(pixel_data, box_x + BOX_W - ITEM_PAD_X - tw + x_offset, y, text, tc, clip_x_min, clip_x_max, clip_y_min, clip_y_max);

View File

@@ -5,6 +5,7 @@
#include <string>
#include <vector>
#include "core/locale/locale.hpp"
#include "core/rendering/menu.hpp"
#include "core/rendering/text.hpp"
#include "core/system/director.hpp"
@@ -229,7 +230,7 @@ namespace Overlay {
// Indicador de pausa persistent (cantó superior dret)
if (Director::get() && Director::get()->isPaused()) {
const char* pause_text = "PAUSA";
const char* pause_text = Locale::get("notifications.pause");
int w = font_->width(pause_text);
int x = SCREEN_W - w - 4;
int y = 4;
@@ -287,7 +288,7 @@ namespace Overlay {
if (!esc_waiting_) {
// Primera pulsació: mostra avís i consumeix
esc_waiting_ = true;
showNotification("TORNA A PULSAR ESC PER EIXIR", 2.0F);
showNotification(Locale::get("notifications.exit_double_esc"), 2.0F);
return true; // Consumit
}
// Segona pulsació: deixa passar

View File

@@ -7,6 +7,7 @@
#include "core/input/global_inputs.hpp"
#include "core/input/key_remap.hpp"
#include "core/input/mouse.hpp"
#include "core/locale/locale.hpp"
#include "core/jail/jail_audio.hpp"
#include "core/jail/jgame.hpp"
#include "core/jail/jinput.hpp"
@@ -146,7 +147,7 @@ void Director::handleEvents() {
if (event.type == SDL_EVENT_KEY_DOWN && !event.key.repeat &&
event.key.scancode == Options::keys_gui.pause_toggle) {
togglePause();
Overlay::showNotification(paused_ ? "PAUSA" : "REPRES");
Overlay::showNotification(paused_ ? Locale::get("notifications.pause") : Locale::get("notifications.resume"));
menu_keys_held_[event.key.scancode] = true;
continue;
}

View File

@@ -5,6 +5,7 @@
#include "core/jail/jdraw8.hpp"
#include "core/jail/jfile.hpp"
#include "core/jail/jgame.hpp"
#include "core/locale/locale.hpp"
#include "core/rendering/menu.hpp"
#include "core/rendering/overlay.hpp"
#include "core/rendering/screen.hpp"
@@ -19,6 +20,9 @@ int main(int /*argc*/, char* /*args*/[]) {
Options::setConfigFile(std::string(file_getconfigfolder()) + "config.yaml");
Options::loadFromFile();
// Carrega textos (idioma per defecte: valencià)
Locale::load("locale/ca.yaml");
// Carrega presets de shaders
Options::setPostFXFile(std::string(file_getconfigfolder()) + "postfx.yaml");
Options::loadPostFXFromFile();