summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/yuzu/CMakeLists.txt33
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp9
-rw-r--r--src/yuzu/configuration/configure_dialog.h6
-rw-r--r--src/yuzu/configuration/configure_ui.cpp31
-rw-r--r--src/yuzu/configuration/configure_ui.h7
-rw-r--r--src/yuzu/configuration/configure_ui.ui188
-rw-r--r--src/yuzu/main.cpp42
-rw-r--r--src/yuzu/main.h5
-rw-r--r--src/yuzu/uisettings.h1
10 files changed, 240 insertions, 84 deletions
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index a862b2610..656096c9f 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -133,11 +133,44 @@ file(GLOB COMPAT_LIST
file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*)
file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*)
+if (ENABLE_QT_TRANSLATION)
+ set(YUZU_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend")
+ option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF)
+
+ # Update source TS file if enabled
+ if (GENERATE_QT_TRANSLATION)
+ get_target_property(SRCS yuzu SOURCES)
+ qt5_create_translation(QM_FILES ${SRCS} ${UIS} ${YUZU_QT_LANGUAGES}/en.ts)
+ add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts)
+ endif()
+
+ # Find all TS files except en.ts
+ file(GLOB_RECURSE LANGUAGES_TS ${YUZU_QT_LANGUAGES}/*.ts)
+ list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts)
+
+ # Compile TS files to QM files
+ qt5_add_translation(LANGUAGES_QM ${LANGUAGES_TS})
+
+ # Build a QRC file from the QM file list
+ set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc)
+ file(WRITE ${LANGUAGES_QRC} "<RCC><qresource prefix=\"languages\">\n")
+ foreach (QM ${LANGUAGES_QM})
+ get_filename_component(QM_FILE ${QM} NAME)
+ file(APPEND ${LANGUAGES_QRC} "<file>${QM_FILE}</file>\n")
+ endforeach (QM)
+ file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>")
+
+ # Add the QRC file to package in all QM files
+ qt5_add_resources(LANGUAGES ${LANGUAGES_QRC})
+else()
+ set(LANGUAGES)
+endif()
target_sources(yuzu
PRIVATE
${COMPAT_LIST}
${ICONS}
+ ${LANGUAGES}
${THEMES}
)
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 805bb954b..59a193edd 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -611,6 +611,7 @@ void Config::ReadPathValues() {
}
}
UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList();
+ UISettings::values.language = ReadSetting(QStringLiteral("language"), QString{}).toString();
qt_config->endGroup();
}
@@ -1095,6 +1096,7 @@ void Config::SavePathValues() {
}
qt_config->endArray();
WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files);
+ WriteSetting(QStringLiteral("language"), UISettings::values.language, QString{});
qt_config->endGroup();
}
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index a5afb354f..4e30dc51e 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -23,6 +23,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
SetConfiguration();
PopulateSelectionList();
+ connect(ui->uiTab, &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged);
connect(ui->selectorList, &QListWidget::itemSelectionChanged, this,
&ConfigureDialog::UpdateVisibleTabs);
@@ -98,6 +99,14 @@ void ConfigureDialog::PopulateSelectionList() {
}
}
+void ConfigureDialog::OnLanguageChanged(const QString& locale) {
+ emit LanguageChanged(locale);
+ // first apply the configuration, and then restore the display
+ ApplyConfiguration();
+ RetranslateUI();
+ SetConfiguration();
+}
+
void ConfigureDialog::UpdateVisibleTabs() {
const auto items = ui->selectorList->selectedItems();
if (items.isEmpty()) {
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index 2d3bfc2da..4289bc225 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -22,6 +22,12 @@ public:
void ApplyConfiguration();
+private slots:
+ void OnLanguageChanged(const QString& locale);
+
+signals:
+ void LanguageChanged(const QString& locale);
+
private:
void changeEvent(QEvent* event) override;
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp
index 94424ee44..24b6c5b72 100644
--- a/src/yuzu/configuration/configure_ui.cpp
+++ b/src/yuzu/configuration/configure_ui.cpp
@@ -5,6 +5,7 @@
#include <array>
#include <utility>
+#include <QDirIterator>
#include "common/common_types.h"
#include "core/settings.h"
#include "ui_configure_ui.h"
@@ -29,6 +30,8 @@ constexpr std::array row_text_names{
ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureUi) {
ui->setupUi(this);
+ InitializeLanguageComboBox();
+
for (const auto& theme : UISettings::themes) {
ui->theme_combobox->addItem(QString::fromUtf8(theme.first),
QString::fromUtf8(theme.second));
@@ -72,6 +75,8 @@ void ConfigureUi::RequestGameListUpdate() {
void ConfigureUi::SetConfiguration() {
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
+ ui->language_combobox->setCurrentIndex(
+ ui->language_combobox->findData(UISettings::values.language));
ui->show_add_ons->setChecked(UISettings::values.show_add_ons);
ui->icon_size_combobox->setCurrentIndex(
ui->icon_size_combobox->findData(UISettings::values.icon_size));
@@ -100,6 +105,25 @@ void ConfigureUi::RetranslateUI() {
}
}
+void ConfigureUi::InitializeLanguageComboBox() {
+ ui->language_combobox->addItem(tr("<System>"), QString{});
+ ui->language_combobox->addItem(tr("English"), QStringLiteral("en"));
+ QDirIterator it(QStringLiteral(":/languages"), QDirIterator::NoIteratorFlags);
+ while (it.hasNext()) {
+ QString locale = it.next();
+ locale.truncate(locale.lastIndexOf(QLatin1Char{'.'}));
+ locale.remove(0, locale.lastIndexOf(QLatin1Char{'/'}) + 1);
+ const QString lang = QLocale::languageToString(QLocale(locale).language());
+ ui->language_combobox->addItem(lang, locale);
+ }
+
+ // Unlike other configuration changes, interface language changes need to be reflected on the
+ // interface immediately. This is done by passing a signal to the main window, and then
+ // retranslating when passing back.
+ connect(ui->language_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
+ &ConfigureUi::OnLanguageChanged);
+}
+
void ConfigureUi::InitializeIconSizeComboBox() {
for (const auto& size : default_icon_sizes) {
ui->icon_size_combobox->addItem(QString::fromUtf8(size.second), size.first);
@@ -147,3 +171,10 @@ void ConfigureUi::UpdateSecondRowComboBox(bool init) {
ui->row_2_text_combobox->removeItem(
ui->row_2_text_combobox->findData(ui->row_1_text_combobox->currentData()));
}
+
+void ConfigureUi::OnLanguageChanged(int index) {
+ if (index == -1)
+ return;
+
+ emit LanguageChanged(ui->language_combobox->itemData(index).toString());
+}
diff --git a/src/yuzu/configuration/configure_ui.h b/src/yuzu/configuration/configure_ui.h
index d471afe99..c30bcf6ff 100644
--- a/src/yuzu/configuration/configure_ui.h
+++ b/src/yuzu/configuration/configure_ui.h
@@ -20,6 +20,12 @@ public:
void ApplyConfiguration();
+private slots:
+ void OnLanguageChanged(int index);
+
+signals:
+ void LanguageChanged(const QString& locale);
+
private:
void RequestGameListUpdate();
@@ -28,6 +34,7 @@ private:
void changeEvent(QEvent*) override;
void RetranslateUI();
+ void InitializeLanguageComboBox();
void InitializeIconSizeComboBox();
void InitializeRowComboBoxes();
diff --git a/src/yuzu/configuration/configure_ui.ui b/src/yuzu/configuration/configure_ui.ui
index bd5c5d3c2..0b81747d7 100644
--- a/src/yuzu/configuration/configure_ui.ui
+++ b/src/yuzu/configuration/configure_ui.ui
@@ -13,112 +13,132 @@
<property name="windowTitle">
<string>Form</string>
</property>
- <layout class="QHBoxLayout" name="HorizontalLayout">
+ <layout class="QVBoxLayout" name="verticalLayout">
<item>
- <layout class="QVBoxLayout" name="VerticalLayout">
- <item>
- <widget class="QGroupBox" name="GeneralGroupBox">
- <property name="title">
- <string>General</string>
- </property>
- <layout class="QHBoxLayout" name="horizontalLayout">
+ <widget class="QGroupBox" name="general_groupBox">
+ <property name="title">
+ <string>General</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
<item>
- <layout class="QVBoxLayout" name="verticalLayout">
+ <widget class="QLabel" name="label_change_language_info">
+ <property name="text">
+ <string>Note: Changing language will apply your configuration.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="language_label">
+ <property name="text">
+ <string>Interface language:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="language_combobox"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLabel" name="theme_label">
+ <property name="text">
+ <string>Theme:</string>
+ </property>
+ </widget>
+ </item>
<item>
- <layout class="QHBoxLayout" name="horizontalLayout_3">
- <item>
- <widget class="QLabel" name="theme_label">
- <property name="text">
- <string>Theme:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="theme_combobox"/>
- </item>
- </layout>
+ <widget class="QComboBox" name="theme_combobox"/>
</item>
</layout>
</item>
</layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="GameListGroupBox">
- <property name="title">
- <string>Game List</string>
- </property>
- <layout class="QHBoxLayout" name="GameListHorizontalLayout">
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="GameListGroupBox">
+ <property name="title">
+ <string>Game List</string>
+ </property>
+ <layout class="QHBoxLayout" name="GameListHorizontalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="GeneralVerticalLayout">
<item>
- <layout class="QVBoxLayout" name="GeneralVerticalLayout">
+ <widget class="QCheckBox" name="show_add_ons">
+ <property name="text">
+ <string>Show Add-Ons Column</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
<item>
- <widget class="QCheckBox" name="show_add_ons">
+ <widget class="QLabel" name="icon_size_label">
<property name="text">
- <string>Show Add-Ons Column</string>
+ <string>Icon Size:</string>
</property>
</widget>
</item>
<item>
- <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2">
- <item>
- <widget class="QLabel" name="icon_size_label">
- <property name="text">
- <string>Icon Size:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="icon_size_combobox"/>
- </item>
- </layout>
+ <widget class="QComboBox" name="icon_size_combobox"/>
</item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="row_1_qhbox_layout">
<item>
- <layout class="QHBoxLayout" name="row_1_qhbox_layout">
- <item>
- <widget class="QLabel" name="row_1_label">
- <property name="text">
- <string>Row 1 Text:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="row_1_text_combobox"/>
- </item>
- </layout>
+ <widget class="QLabel" name="row_1_label">
+ <property name="text">
+ <string>Row 1 Text:</string>
+ </property>
+ </widget>
</item>
<item>
- <layout class="QHBoxLayout" name="row_2_qhbox_layout">
- <item>
- <widget class="QLabel" name="row_2_label">
- <property name="text">
- <string>Row 2 Text:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="row_2_text_combobox"/>
- </item>
- </layout>
+ <widget class="QComboBox" name="row_1_text_combobox"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="row_2_qhbox_layout">
+ <item>
+ <widget class="QLabel" name="row_2_label">
+ <property name="text">
+ <string>Row 2 Text:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="row_2_text_combobox"/>
</item>
</layout>
</item>
</layout>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
</item>
</layout>
</widget>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 6909d65d0..31a635176 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -191,6 +191,8 @@ GMainWindow::GMainWindow()
provider(std::make_unique<FileSys::ManualContentProvider>()) {
InitializeLogging();
+ LoadTranslation();
+
setAcceptDrops(true);
ui.setupUi(this);
statusBar()->hide();
@@ -2048,6 +2050,9 @@ void GMainWindow::OnConfigure() {
const bool old_discord_presence = UISettings::values.enable_discord_presence;
ConfigureDialog configure_dialog(this, hotkey_registry);
+ connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this,
+ &GMainWindow::OnLanguageChanged);
+
const auto result = configure_dialog.exec();
if (result != QDialog::Accepted) {
return;
@@ -2620,6 +2625,43 @@ void GMainWindow::UpdateUITheme() {
QIcon::setThemeSearchPaths(theme_paths);
}
+void GMainWindow::LoadTranslation() {
+ // If the selected language is English, no need to install any translation
+ if (UISettings::values.language == QStringLiteral("en")) {
+ return;
+ }
+
+ bool loaded;
+
+ if (UISettings::values.language.isEmpty()) {
+ // If the selected language is empty, use system locale
+ loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/"));
+ } else {
+ // Otherwise load from the specified file
+ loaded = translator.load(UISettings::values.language, QStringLiteral(":/languages/"));
+ }
+
+ if (loaded) {
+ qApp->installTranslator(&translator);
+ } else {
+ UISettings::values.language = QStringLiteral("en");
+ }
+}
+
+void GMainWindow::OnLanguageChanged(const QString& locale) {
+ if (UISettings::values.language != QStringLiteral("en")) {
+ qApp->removeTranslator(&translator);
+ }
+
+ UISettings::values.language = locale;
+ LoadTranslation();
+ ui.retranslateUi(this);
+ UpdateWindowTitle();
+
+ if (emulation_running)
+ ui.action_Start->setText(tr("Continue"));
+}
+
void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
#ifdef USE_DISCORD_PRESENCE
if (state) {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 59d9073ae..db573d606 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -10,6 +10,7 @@
#include <QMainWindow>
#include <QTimer>
+#include <QTranslator>
#include "common/common_types.h"
#include "core/core.h"
@@ -225,6 +226,7 @@ private slots:
void OnCaptureScreenshot();
void OnCoreError(Core::System::ResultStatus, std::string);
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
+ void OnLanguageChanged(const QString& locale);
private:
std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
@@ -237,6 +239,7 @@ private:
void HideMouseCursor();
void ShowMouseCursor();
void OpenURL(const QUrl& url);
+ void LoadTranslation();
Ui::MainWindow ui;
@@ -285,6 +288,8 @@ private:
HotkeyRegistry hotkey_registry;
+ QTranslator translator;
+
// Install progress dialog
QProgressDialog* install_progress;
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 830932d45..6cc65736d 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -75,6 +75,7 @@ struct Values {
bool game_dir_deprecated_deepscan;
QVector<UISettings::GameDir> game_dirs;
QStringList recent_files;
+ QString language;
QString theme;