diff --git a/src/duckstation-qt/mainwindow.cpp b/src/duckstation-qt/mainwindow.cpp index 704db0569..ec8cbfdb9 100644 --- a/src/duckstation-qt/mainwindow.cpp +++ b/src/duckstation-qt/mainwindow.cpp @@ -463,6 +463,31 @@ void MainWindow::setupAdditionalUi() }); } updateDebugMenuGPURenderer(); + + const QString current_language( + QString::fromStdString(m_host_interface->GetStringSettingValue("Main", "Language", ""))); + for (const std::pair& it : m_host_interface->getAvailableLanguageList()) + { + QAction* action = m_ui.menuSettingsLanguage->addAction(it.first); + action->setCheckable(true); + action->setChecked(it.second == current_language); + action->setData(it.second); + connect(action, &QAction::triggered, [this, action]() { + const QString new_language = action->data().toString(); + m_host_interface->SetStringSettingValue("Main", "Language", new_language.toUtf8().constData()); + QMessageBox::information(this, tr("DuckStation"), + tr("Language changed. Please restart the application to apply.")); + for (QObject* obj : m_ui.menuSettingsLanguage->children()) + { + QAction* other_action = qobject_cast(obj); + if (other_action) + { + QSignalBlocker blocker(other_action); + action->setChecked(other_action == action); + } + } + }); + } } void MainWindow::updateEmulationActions(bool starting, bool running) diff --git a/src/duckstation-qt/mainwindow.ui b/src/duckstation-qt/mainwindow.ui index 19e2a6e6f..13dfb1458 100644 --- a/src/duckstation-qt/mainwindow.ui +++ b/src/duckstation-qt/mainwindow.ui @@ -90,6 +90,11 @@ Theme + + + Language + + @@ -106,6 +111,7 @@ + diff --git a/src/duckstation-qt/qthostinterface.cpp b/src/duckstation-qt/qthostinterface.cpp index 6009ac051..73f7e389f 100644 --- a/src/duckstation-qt/qthostinterface.cpp +++ b/src/duckstation-qt/qthostinterface.cpp @@ -23,7 +23,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -49,10 +51,19 @@ const char* QtHostInterface::GetFrontendName() const return "DuckStation Qt Frontend"; } +std::vector> QtHostInterface::getAvailableLanguageList() +{ + return {{QStringLiteral("English"), QStringLiteral("")}}; +} + bool QtHostInterface::Initialize() { createThread(); - return m_worker_thread->waitForInit(); + if (!m_worker_thread->waitForInit()) + return false; + + installTranslator(); + return true; } void QtHostInterface::Shutdown() @@ -82,6 +93,36 @@ void QtHostInterface::shutdownOnThread() CommonHostInterface::Shutdown(); } +void QtHostInterface::installTranslator() +{ + m_translator = std::make_unique(); + + std::string language = GetStringSettingValue("Main", "Language", ""); + if (language.empty()) + return; + + const QString path = + QStringLiteral("%1/translations/duckstation-qt_%3.qm").arg(qApp->applicationDirPath()).arg(language.c_str()); + if (!QFile::exists(path)) + { + QMessageBox::warning( + nullptr, QStringLiteral("Translation Error"), + QStringLiteral("Failed to find translation file for language '%1':\n%2").arg(language.c_str()).arg(path)); + return; + } + + if (!m_translator->load(path)) + { + QMessageBox::warning( + nullptr, QStringLiteral("Translation Error"), + QStringLiteral("Failed to load translation file for language '%1':\n%2").arg(language.c_str()).arg(path)); + return; + } + + Log_InfoPrintf("Loaded translation file for language '%s'", language.c_str()); + qApp->installTranslator(m_translator.get()); +} + void QtHostInterface::ReportError(const char* message) { HostInterface::ReportError(message); diff --git a/src/duckstation-qt/qthostinterface.h b/src/duckstation-qt/qthostinterface.h index dfd9469b3..96de18982 100644 --- a/src/duckstation-qt/qthostinterface.h +++ b/src/duckstation-qt/qthostinterface.h @@ -22,6 +22,7 @@ class QEventLoop; class QMenu; class QWidget; class QTimer; +class QTranslator; class GameList; class INISettingsInterface; @@ -95,6 +96,9 @@ public: /// Returns a path relative to the application directory (for system files). QString getProgramDirectoryRelativePath(const QString& arg) const; + /// Returns a list of supported languages and codes (suffixes for translation files). + static std::vector> getAvailableLanguageList(); + Q_SIGNALS: void errorReported(const QString& message); void messageReported(const QString& message); @@ -213,6 +217,7 @@ private: void threadEntryPoint(); bool initializeOnThread(); void shutdownOnThread(); + void installTranslator(); void renderDisplay(); void connectDisplaySignals(QtDisplayWidget* widget); void updateDisplayState(); @@ -222,6 +227,8 @@ private: std::unique_ptr m_settings_interface; std::recursive_mutex m_settings_mutex; + std::unique_ptr m_translator; + MainWindow* m_main_window = nullptr; QThread* m_original_thread = nullptr; Thread* m_worker_thread = nullptr;