diff --git a/CMakeLists.txt b/CMakeLists.txt index 42a5d402..4f3883c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -647,7 +647,7 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE) set(FRONTEND_SOURCE_FILES src/panda_qt/main.cpp src/panda_qt/screen.cpp src/panda_qt/main_window.cpp src/panda_qt/about_window.cpp src/panda_qt/config_window.cpp src/panda_qt/zep.cpp src/panda_qt/text_editor.cpp src/panda_qt/cheats_window.cpp src/panda_qt/mappings.cpp - src/panda_qt/patch_window.cpp src/panda_qt/elided_label.cpp src/panda_qt/shader_editor.cpp + src/panda_qt/patch_window.cpp src/panda_qt/elided_label.cpp src/panda_qt/shader_editor.cpp src/panda_qt/translations.cpp ) set(FRONTEND_HEADER_FILES include/panda_qt/screen.hpp include/panda_qt/main_window.hpp include/panda_qt/about_window.hpp include/panda_qt/config_window.hpp include/panda_qt/text_editor.hpp include/panda_qt/cheats_window.hpp @@ -701,8 +701,8 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE) docs/img/rnap_icon.png docs/img/rcow_icon.png docs/img/skyemu_icon.png ) - set(QT_TRANSLATIONS "${PROJECT_SOURCE_DIR}/docs/translations") - file(GLOB_RECURSE TRANSLATIONS_TS ${QT_TRANSLATIONS}/*.ts) + # Translation files in Qt's .ts format. Will be converted into binary files and embedded into the executable + set(TRANSLATIONS_TS docs/translations/en.ts docs/translations/el.ts) set_source_files_properties(${TRANSLATIONS_TS} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations") qt_add_translation(TRANSLATIONS_QM ${TRANSLATIONS_TS}) diff --git a/docs/translations/el.ts b/docs/translations/el.ts index 6473258b..0368d8bd 100644 --- a/docs/translations/el.ts +++ b/docs/translations/el.ts @@ -134,371 +134,396 @@ Εικονίδιο Παραθύρου - + + Language + Γλώσσα + + + Show version on window title Εμφάνιση έκδοσης στον τίτλο του παραθύρου - + Alber v%1 Αλβέρτος v%1 - + Alber Αλβέρτος - + Remember window position Αποθήκευση θέσης παραθύρου - + General Settings Γενικές Ρυθμίσεις - + Browse... Περιήγηση - + Select Directory Επιλογή φακέλου - + Default ROMs path Προεπιλεγμένος φάκελος ROM - + Enable Discord RPC Ενεργοποίηση Discord RPC - + Use portable build Ενεργοποίηση φορητής εγκατάστασης - + Print version in console output Εκτύπωση έκδοσης στην κονσόλα - + Graphics Settings Ρυθμίσεις Γραφικών - - + + Null Κανένα - + OpenGL - + Vulkan - + GPU renderer Πυρήνας GPU - + Enable Renderdoc Ενεργοποίηση Renderdoc - + Enable shader JIT Ενεργοποίηση μεταγλωττιστή shaders - + Enable VSync Ενεργοποίηση VSync - + Use ubershaders (No stutter, maybe slower) Χρήση ubershaders (Χωρίς stutter, ίσως πιο αργό) - + Accurate shader multiplication Ακριβής πολλαπλασιασμός στα shaders - + Accelerate shaders Επιτάχυνση shaders - + Force shadergen when rendering lights Εξαναγκασμός shadergen όταν υπάρχουν φώτα - + Light threshold for forcing shadergen Αριθμός φωτών για εξαναγκασμό shadergen - + Audio Settings Ρυθμίσεις Ήχου - + LLE - + HLE - + DSP emulation Εξομοίωση DSP - + Enable audio Ενεργοποίηση ήχου - + Enable AAC audio Ενεργοποίηση ήχου AAC - + Print DSP firmware Εκτύπωση λογισμικού DSP - + Mute audio device Σίγαση συσκευής ήχου - + Cubic Κυβική - + Linear Γραμμική - + Volume curve Κλίμακα ήχου - + Audio device volume Ένταση ήχου - + Battery Settings Ρυθμίσεις μπαταρίας - + Battery percentage Ποσοστό μπαταρίας - + Charger plugged Φορτιστής - + SD Card Settings Ρυθμίσης κάρτας SD - + Enable virtual SD card Ενεργοποίηση εικονικής SD - + Write protect virtual SD card Προστασία της SD από εγγραφή - + Interface Διεπαφή - + User Interface settings Ρυθμίσεις διεπαφής - + General Γενικά - + General emulator settings Γενικές ρυθμίσεις εξομοιωτή - + Graphics Γραφικά - + Graphics emulation and output settings Ρυθμίσεις εξομοίωσης γραφικών - + Audio Ήχος - + Audio emulation and output settings Ρυθμίσεις εξομοίωσης ήχου - + Battery Μπαταρία - + Battery emulation settings Ρυθμίσεις εξομοίωσης μπαταρίας - + SD Card Κάρτα SD - + SD Card emulation settings Ρυθμίσεις εξομοίωσης κάρτας SD + + + Language change successful + Επιτυχία αλλαγής γλώσσας + + + + Restart Panda3DS for the new language to be used. + Επανεκκινήστε το Panda3DS για να εφαρμοστεί η νέα γλώσσα. + + + + Language change failed + Αποτυχία αλλαγής γλώσσας + + + + The language you selected is not included in Panda3DS. If you're seeing this, someone messed up the language UI code... + Το Panda3DS δεν υποστηρίζει τον γλώσσα που επιλέξατε. Αν το βλέπετε αυτό, κάποιος έκανε λάθος στον κώδικα, κατηγορήστε τον Πάρη... + MainWindow - + Alber Αλβέρτος - + File - Αρχείο + Αρχεία - + Emulation Εξομοίωση - + Tools Εργαλεία - + About Σχετικά - + Load game Φόρτωση παιχνιδιού - + Load Lua script Φόρτωση αρχείου Lua - + Open Panda3DS folder Άνοιγμα φακέλου Panda3DS - + Pause Παύση - + Resume Συνέχεια - + Reset Επανέναρξη - + Configure Ρύθμιση - + Dump RomFS - + Open Lua Editor Άνοιγμα Lua Editor - + Open Cheats Editor Άνοιγμα Editor κωδικών - + Open Patch Window Άνοιγμα παραθύρου για patching - + Open Shader Editor - + Dump loaded DSP firmware - + About Panda3DS Σχετικά με το Panda3DS @@ -552,7 +577,7 @@ No RomFS partition was found in the loaded app - + Δεν βρέθηκε RomFS στην εφαρμογή που έχει φορτωθεί @@ -567,7 +592,7 @@ No DSP firmware loaded - + Δεν έχει φορτωθεί DSP firmware @@ -577,7 +602,7 @@ Failed to open output file - + Αποτυχία ανοίγματος του αρχείου εξόδου @@ -638,7 +663,7 @@ Please provide paths for both the input file and the patch file - + Παρακαλούμε διαλέξτε και αρχείο εισόδου και αρχείο patch @@ -648,32 +673,32 @@ No output path - + Δεν επιλέχθηκε φάκελος εξόδου No path was provided for the output file, no patching was done - + Δεν επιλέχθηκε αρχείο εξόδου. Δεν έγινε patching Unknown patch format - + Άγνωστο είδος patch Unknown format for patch file. Currently IPS, UPS and BPS are supported - + Άγνωστο είδος αρχείου patch. Υποστηρίζονται αρχεία IPS, UPS και BPS Failed to open input files - + Αποτυχία ανοίγματος των αρχείων εισόδου Make sure they're in a directory Panda3DS has access to - + Βεβαιωθείτε ότι είναι σε φάκελο που έχει πρόσβαση το Panda3DS @@ -693,17 +718,17 @@ Patch was applied successfully but a checksum mismatch was detected. The input or output files might not be correct - + Το patch εφαρμόστηκε με επιτυχία αλλά ανιχνεύτηκε σφάλμα στο checksum. Ενδέχεται τα αρχεία εισόδου η εξόδου να είναι λανθασμένα Patching error - + Σφάλμα στο patching An error occured while patching - + Προέκυψε σφάλμα στο patching @@ -719,7 +744,7 @@ Reload shader - + Επαναφόρτωση shader diff --git a/docs/translations/en.ts b/docs/translations/en.ts index 258089ab..4a70fabb 100644 --- a/docs/translations/en.ts +++ b/docs/translations/en.ts @@ -136,371 +136,397 @@ - + + Language + + + + Show version on window title - + + Alber v%1 - + Alber - + Remember window position - + General Settings - + Browse... - + Select Directory - + Default ROMs path - + Enable Discord RPC - + Use portable build - + Print version in console output - + Graphics Settings - - + + Null - + OpenGL - + Vulkan - + GPU renderer - + Enable Renderdoc - + Enable shader JIT - + Enable VSync - + Use ubershaders (No stutter, maybe slower) - + Accurate shader multiplication - + Accelerate shaders - + Force shadergen when rendering lights - + Light threshold for forcing shadergen - + Audio Settings - + LLE - + HLE - + DSP emulation - + Enable audio - + Enable AAC audio - + Print DSP firmware - + Mute audio device - + Cubic - + Linear - + Volume curve - + Audio device volume - + Battery Settings - + Battery percentage - + Charger plugged - + SD Card Settings - + Enable virtual SD card - + Write protect virtual SD card - + Interface - + User Interface settings - + General - + General emulator settings - + Graphics - + Graphics emulation and output settings - + Audio - + Audio emulation and output settings - + Battery - + Battery emulation settings - + SD Card - + SD Card emulation settings + + + Language change successful + + + + + Restart Panda3DS for the new language to be used. + + + + + Language change failed + + + + + The language you selected is not included in Panda3DS. If you're seeing this, someone messed up the language UI code... + + MainWindow - + Alber - + File - + Emulation - + Tools - + About - + Load game - + Load Lua script - + Open Panda3DS folder - + Pause - + Resume - + Reset - + Configure - + Dump RomFS - + Open Lua Editor - + Open Cheats Editor - + Open Patch Window - + Open Shader Editor - + Dump loaded DSP firmware - + About Panda3DS diff --git a/include/frontend_settings.hpp b/include/frontend_settings.hpp index 1a78ab66..ae967879 100644 --- a/include/frontend_settings.hpp +++ b/include/frontend_settings.hpp @@ -24,6 +24,7 @@ struct FrontendSettings { Theme theme = Theme::Dark; WindowIcon icon = WindowIcon::Rpog; + std::string language = "en"; static Theme themeFromString(std::string inString); static const char* themeToString(Theme theme); diff --git a/include/panda_qt/config_window.hpp b/include/panda_qt/config_window.hpp index 9c003753..1d37a8ca 100644 --- a/include/panda_qt/config_window.hpp +++ b/include/panda_qt/config_window.hpp @@ -45,6 +45,8 @@ class ConfigWindow : public QDialog { void setTheme(FrontendSettings::Theme theme); void setIcon(FrontendSettings::WindowIcon icon); + QComboBox* createLanguageSelect(); + public: ConfigWindow(ConfigCallback configCallback, MainWindowCallback windowCallback, const EmulatorConfig& config, QWidget* parent = nullptr); ~ConfigWindow(); diff --git a/src/config.cpp b/src/config.cpp index fd4d969d..73b5d664 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -141,6 +141,7 @@ void EmulatorConfig::load() { frontendSettings.theme = FrontendSettings::themeFromString(toml::find_or(ui, "Theme", "dark")); frontendSettings.icon = FrontendSettings::iconFromString(toml::find_or(ui, "WindowIcon", "rpog")); + frontendSettings.language = toml::find_or(ui, "Language", "en"); } } } @@ -202,6 +203,7 @@ void EmulatorConfig::save() { data["UI"]["Theme"] = std::string(FrontendSettings::themeToString(frontendSettings.theme)); data["UI"]["WindowIcon"] = std::string(FrontendSettings::iconToString(frontendSettings.icon)); + data["UI"]["Language"] = frontendSettings.language; std::ofstream file(path, std::ios::out); file << data; diff --git a/src/panda_qt/config_window.cpp b/src/panda_qt/config_window.cpp index 549f8990..519a4a22 100644 --- a/src/panda_qt/config_window.cpp +++ b/src/panda_qt/config_window.cpp @@ -12,7 +12,7 @@ ConfigWindow::ConfigWindow(ConfigCallback configCallback, MainWindowCallback win // Set the window title of the main window appropriately if we enable showing the app version on the window if (config.windowSettings.showAppVersion) { - getMainWindow()->setWindowTitle("Alber v" PANDA3DS_VERSION); + getMainWindow()->setWindowTitle(tr("Alber v%1").arg(PANDA3DS_VERSION)); } // Initialize the widget list and the widget container widgets @@ -96,6 +96,9 @@ ConfigWindow::ConfigWindow(ConfigCallback configCallback, MainWindowCallback win }); guiLayout->addRow(tr("Window icon"), iconSelect); + QComboBox* languageSelect = createLanguageSelect(); + guiLayout->addRow(tr("Language"), languageSelect); + QCheckBox* showAppVersion = new QCheckBox(tr("Show version on window title")); showAppVersion->setChecked(config.windowSettings.showAppVersion); connect(showAppVersion, &QCheckBox::toggled, this, [&](bool checked) { diff --git a/src/panda_qt/main_window.cpp b/src/panda_qt/main_window.cpp index c3a04f88..c4d53697 100644 --- a/src/panda_qt/main_window.cpp +++ b/src/panda_qt/main_window.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -15,6 +14,8 @@ #include "version.hpp" MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent), keyboardMappings(InputMappings::defaultKeyboardMappings()) { + emu = new Emulator(); + loadTranslation(); setWindowTitle(tr("Alber")); @@ -77,7 +78,6 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent) auto aboutAction = aboutMenu->addAction(tr("About Panda3DS")); connect(aboutAction, &QAction::triggered, this, &MainWindow::showAboutMenu); - emu = new Emulator(); emu->setOutputSize(screen->surfaceWidth, screen->surfaceHeight); // Set up misc objects @@ -680,32 +680,4 @@ void MainWindow::setupControllerSensors(SDL_GameController* controller) { if (haveAccelerometer) { SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE); } -} - -void MainWindow::loadTranslation() { - // TODO: This should become a member variable when we allow changing language at runtime. - QTranslator* translator = nullptr; - - auto language = QString::fromStdString("el"); - const QString baseDir = QStringLiteral(":/translations"); - QString basePath = QStringLiteral("%1/%2.qm").arg(baseDir).arg(language); - - if (QFile::exists(basePath)) { - if (translator != nullptr) { - qApp->removeTranslator(translator); - } - - translator = new QTranslator(qApp); - if (!translator->load(basePath)) { - QMessageBox::warning( - nullptr, QStringLiteral("Translation Error"), - QStringLiteral("Failed to find load translation file for '%1':\n%2").arg(language).arg(basePath) - ); - delete translator; - } else { - qApp->installTranslator(translator); - } - } else { - printf("Language file %s does not exist\n", basePath.toStdString().c_str()); - } } \ No newline at end of file diff --git a/src/panda_qt/translations.cpp b/src/panda_qt/translations.cpp new file mode 100644 index 00000000..5054a0d4 --- /dev/null +++ b/src/panda_qt/translations.cpp @@ -0,0 +1,91 @@ +#include +#include +#include +#include + +#include "panda_qt/config_window.hpp" +#include "panda_qt/main_window.hpp" + +void MainWindow::loadTranslation() { + // TODO: This should become a member variable when we allow changing language at runtime. + QTranslator* translator = nullptr; + + // Fetch the .qm file for our language and load it + auto language = QString::fromStdString(emu->getConfig().frontendSettings.language); + const QString baseDir = QStringLiteral(":/translations"); + const QString basePath = QStringLiteral("%1/%2.qm").arg(baseDir).arg(language); + + if (QFile::exists(basePath)) { + if (translator != nullptr) { + qApp->removeTranslator(translator); + } + + translator = new QTranslator(qApp); + if (!translator->load(basePath)) { + QMessageBox::warning( + nullptr, QStringLiteral("Translation Error"), + QStringLiteral("Failed to find load translation file for '%1':\n%2").arg(language).arg(basePath) + ); + delete translator; + } else { + qApp->installTranslator(translator); + } + } else { + printf("Language file %s does not exist. Defaulting to English\n", basePath.toStdString().c_str()); + } +} + +struct LanguageInfo { + QString name; // Full name of the language (for example "English (US)") + const char* code; // ISO 639 language code (for example "en_us") + + explicit LanguageInfo(const QString& name, const char* code) : name(name), code(code) {} +}; + +// List of languages in the order they should appear in the menu +// Please keep this list mostly in alphabetical order. +// Also, for Unicode characters in language names, use Unicode keycodes instead of writing out the name, +// as some compilers/toolchains may not enjoy Unicode in source files. +static std::array languages = { + LanguageInfo(QStringLiteral(u"English"), "en"), // English + LanguageInfo(QStringLiteral(u"\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC"), "el"), // Greek +}; + +QComboBox* ConfigWindow::createLanguageSelect() { + QComboBox* select = new QComboBox(); + + for (usize i = 0; i < languages.size(); i++) { + const auto& lang = languages[i]; + select->addItem(lang.name); + + if (config.frontendSettings.language == lang.code) { + select->setCurrentIndex(i); + } + } + + connect(select, &QComboBox::currentIndexChanged, this, [&](int index) { + const QString baseDir = QStringLiteral(":/translations"); + const QString basePath = QStringLiteral("%1/%2.qm").arg(baseDir).arg(languages[index].code); + + if (QFile::exists(basePath)) { + config.frontendSettings.language = languages[index].code; + updateConfig(); + + QMessageBox messageBox( + QMessageBox::Icon::Information, tr("Language change successful"), + tr("Restart Panda3DS for the new language to be used.") + ); + + messageBox.exec(); + } else { + QMessageBox messageBox( + QMessageBox::Icon::Warning, tr("Language change failed"), + tr("The language you selected is not included in Panda3DS. If you're seeing this, someone messed up the language UI code...") + ); + + messageBox.exec(); + } + }); + + return select; +} \ No newline at end of file