diff options
Diffstat (limited to '')
| -rwxr-xr-x | .ci/scripts/linux/docker.sh | 2 | ||||
| -rwxr-xr-x | .ci/scripts/windows/docker.sh | 2 | ||||
| -rw-r--r-- | CMakeLists.txt | 5 | ||||
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | dist/languages/.gitignore | 2 | ||||
| -rw-r--r-- | dist/languages/.tx/config | 8 | ||||
| -rw-r--r-- | dist/languages/README.md | 1 | ||||
| -rw-r--r-- | src/yuzu/CMakeLists.txt | 33 | ||||
| -rw-r--r-- | src/yuzu/configuration/config.cpp | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_dialog.cpp | 9 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_dialog.h | 6 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_ui.cpp | 31 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_ui.h | 7 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_ui.ui | 188 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 42 | ||||
| -rw-r--r-- | src/yuzu/main.h | 5 | ||||
| -rw-r--r-- | src/yuzu/uisettings.h | 1 |
17 files changed, 260 insertions, 86 deletions
diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh index 5559a527c..277775ef6 100755 --- a/.ci/scripts/linux/docker.sh +++ b/.ci/scripts/linux/docker.sh | |||
| @@ -5,7 +5,7 @@ cd /yuzu | |||
| 5 | ccache -s | 5 | ccache -s |
| 6 | 6 | ||
| 7 | mkdir build || true && cd build | 7 | mkdir build || true && cd build |
| 8 | cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON | 8 | cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON |
| 9 | 9 | ||
| 10 | ninja | 10 | ninja |
| 11 | 11 | ||
diff --git a/.ci/scripts/windows/docker.sh b/.ci/scripts/windows/docker.sh index d53281741..adfd636fa 100755 --- a/.ci/scripts/windows/docker.sh +++ b/.ci/scripts/windows/docker.sh | |||
| @@ -5,7 +5,7 @@ cd /yuzu | |||
| 5 | ccache -s | 5 | ccache -s |
| 6 | 6 | ||
| 7 | mkdir build || true && cd build | 7 | mkdir build || true && cd build |
| 8 | cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release | 8 | cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON |
| 9 | ninja | 9 | ninja |
| 10 | 10 | ||
| 11 | ccache -s | 11 | ccache -s |
diff --git a/CMakeLists.txt b/CMakeLists.txt index ce46a2c2b..7a49318aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -13,6 +13,7 @@ project(yuzu) | |||
| 13 | option(ENABLE_SDL2 "Enable the SDL2 frontend" ON) | 13 | option(ENABLE_SDL2 "Enable the SDL2 frontend" ON) |
| 14 | 14 | ||
| 15 | option(ENABLE_QT "Enable the Qt frontend" ON) | 15 | option(ENABLE_QT "Enable the Qt frontend" ON) |
| 16 | option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF) | ||
| 16 | CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF) | 17 | CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF) |
| 17 | 18 | ||
| 18 | option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) | 19 | option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) |
| @@ -224,6 +225,10 @@ if(ENABLE_QT) | |||
| 224 | if (YUZU_USE_QT_WEB_ENGINE) | 225 | if (YUZU_USE_QT_WEB_ENGINE) |
| 225 | find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets) | 226 | find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets) |
| 226 | endif() | 227 | endif() |
| 228 | |||
| 229 | if (ENABLE_QT_TRANSLATION) | ||
| 230 | find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT}) | ||
| 231 | endif() | ||
| 227 | if (NOT Qt5_FOUND) | 232 | if (NOT Qt5_FOUND) |
| 228 | list(APPEND CONAN_REQUIRED_LIBS "qt/5.14.1@bincrafters/stable") | 233 | list(APPEND CONAN_REQUIRED_LIBS "qt/5.14.1@bincrafters/stable") |
| 229 | endif() | 234 | endif() |
| @@ -24,6 +24,8 @@ Most of the development happens on GitHub. It's also where [our central reposito | |||
| 24 | 24 | ||
| 25 | If you want to contribute please take a look at the [Contributor's Guide](https://github.com/yuzu-emu/yuzu/wiki/Contributing) and [Developer Information](https://github.com/yuzu-emu/yuzu/wiki/Developer-Information). You should also contact any of the developers on Discord in order to know about the current state of the emulator. | 25 | If you want to contribute please take a look at the [Contributor's Guide](https://github.com/yuzu-emu/yuzu/wiki/Contributing) and [Developer Information](https://github.com/yuzu-emu/yuzu/wiki/Developer-Information). You should also contact any of the developers on Discord in order to know about the current state of the emulator. |
| 26 | 26 | ||
| 27 | If you want to contribute to the user interface translation, please check out the [yuzu project on transifex](https://www.transifex.com/yuzu-emulator/yuzu). We centralize translation work there, and periodically upstream translations. | ||
| 28 | |||
| 27 | ### Building | 29 | ### Building |
| 28 | 30 | ||
| 29 | * __Windows__: [Windows Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Windows) | 31 | * __Windows__: [Windows Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Windows) |
diff --git a/dist/languages/.gitignore b/dist/languages/.gitignore new file mode 100644 index 000000000..27e5a0158 --- /dev/null +++ b/dist/languages/.gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | # Ignore the source language file | ||
| 2 | en.ts | ||
diff --git a/dist/languages/.tx/config b/dist/languages/.tx/config new file mode 100644 index 000000000..0d9b512ea --- /dev/null +++ b/dist/languages/.tx/config | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | [main] | ||
| 2 | host = https://www.transifex.com | ||
| 3 | |||
| 4 | [yuzu.emulator] | ||
| 5 | file_filter = <lang>.ts | ||
| 6 | source_file = en.ts | ||
| 7 | source_lang = en | ||
| 8 | type = QT | ||
diff --git a/dist/languages/README.md b/dist/languages/README.md new file mode 100644 index 000000000..61981ab1d --- /dev/null +++ b/dist/languages/README.md | |||
| @@ -0,0 +1 @@ | |||
| This directory stores translation patches (TS files) for yuzu Qt frontend. This directory is linked with [yuzu project on transifex](https://www.transifex.com/yuzu-emulator/yuzu), so you can update the translation by executing `tx pull -a`. If you want to contribute to the translation, please go the transifex link and submit your translation there. This directory on the main repo will be synchronized with transifex periodically. Do not directly open PRs on github to modify the translation. | |||
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 | |||
| 133 | file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*) | 133 | file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*) |
| 134 | file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*) | 134 | file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*) |
| 135 | 135 | ||
| 136 | if (ENABLE_QT_TRANSLATION) | ||
| 137 | set(YUZU_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend") | ||
| 138 | option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF) | ||
| 139 | |||
| 140 | # Update source TS file if enabled | ||
| 141 | if (GENERATE_QT_TRANSLATION) | ||
| 142 | get_target_property(SRCS yuzu SOURCES) | ||
| 143 | qt5_create_translation(QM_FILES ${SRCS} ${UIS} ${YUZU_QT_LANGUAGES}/en.ts) | ||
| 144 | add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts) | ||
| 145 | endif() | ||
| 146 | |||
| 147 | # Find all TS files except en.ts | ||
| 148 | file(GLOB_RECURSE LANGUAGES_TS ${YUZU_QT_LANGUAGES}/*.ts) | ||
| 149 | list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts) | ||
| 150 | |||
| 151 | # Compile TS files to QM files | ||
| 152 | qt5_add_translation(LANGUAGES_QM ${LANGUAGES_TS}) | ||
| 153 | |||
| 154 | # Build a QRC file from the QM file list | ||
| 155 | set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc) | ||
| 156 | file(WRITE ${LANGUAGES_QRC} "<RCC><qresource prefix=\"languages\">\n") | ||
| 157 | foreach (QM ${LANGUAGES_QM}) | ||
| 158 | get_filename_component(QM_FILE ${QM} NAME) | ||
| 159 | file(APPEND ${LANGUAGES_QRC} "<file>${QM_FILE}</file>\n") | ||
| 160 | endforeach (QM) | ||
| 161 | file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>") | ||
| 162 | |||
| 163 | # Add the QRC file to package in all QM files | ||
| 164 | qt5_add_resources(LANGUAGES ${LANGUAGES_QRC}) | ||
| 165 | else() | ||
| 166 | set(LANGUAGES) | ||
| 167 | endif() | ||
| 136 | 168 | ||
| 137 | target_sources(yuzu | 169 | target_sources(yuzu |
| 138 | PRIVATE | 170 | PRIVATE |
| 139 | ${COMPAT_LIST} | 171 | ${COMPAT_LIST} |
| 140 | ${ICONS} | 172 | ${ICONS} |
| 173 | ${LANGUAGES} | ||
| 141 | ${THEMES} | 174 | ${THEMES} |
| 142 | ) | 175 | ) |
| 143 | 176 | ||
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() { | |||
| 611 | } | 611 | } |
| 612 | } | 612 | } |
| 613 | UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList(); | 613 | UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList(); |
| 614 | UISettings::values.language = ReadSetting(QStringLiteral("language"), QString{}).toString(); | ||
| 614 | 615 | ||
| 615 | qt_config->endGroup(); | 616 | qt_config->endGroup(); |
| 616 | } | 617 | } |
| @@ -1095,6 +1096,7 @@ void Config::SavePathValues() { | |||
| 1095 | } | 1096 | } |
| 1096 | qt_config->endArray(); | 1097 | qt_config->endArray(); |
| 1097 | WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files); | 1098 | WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files); |
| 1099 | WriteSetting(QStringLiteral("language"), UISettings::values.language, QString{}); | ||
| 1098 | 1100 | ||
| 1099 | qt_config->endGroup(); | 1101 | qt_config->endGroup(); |
| 1100 | } | 1102 | } |
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) | |||
| 23 | SetConfiguration(); | 23 | SetConfiguration(); |
| 24 | PopulateSelectionList(); | 24 | PopulateSelectionList(); |
| 25 | 25 | ||
| 26 | connect(ui->uiTab, &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged); | ||
| 26 | connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, | 27 | connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, |
| 27 | &ConfigureDialog::UpdateVisibleTabs); | 28 | &ConfigureDialog::UpdateVisibleTabs); |
| 28 | 29 | ||
| @@ -98,6 +99,14 @@ void ConfigureDialog::PopulateSelectionList() { | |||
| 98 | } | 99 | } |
| 99 | } | 100 | } |
| 100 | 101 | ||
| 102 | void ConfigureDialog::OnLanguageChanged(const QString& locale) { | ||
| 103 | emit LanguageChanged(locale); | ||
| 104 | // first apply the configuration, and then restore the display | ||
| 105 | ApplyConfiguration(); | ||
| 106 | RetranslateUI(); | ||
| 107 | SetConfiguration(); | ||
| 108 | } | ||
| 109 | |||
| 101 | void ConfigureDialog::UpdateVisibleTabs() { | 110 | void ConfigureDialog::UpdateVisibleTabs() { |
| 102 | const auto items = ui->selectorList->selectedItems(); | 111 | const auto items = ui->selectorList->selectedItems(); |
| 103 | if (items.isEmpty()) { | 112 | 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: | |||
| 22 | 22 | ||
| 23 | void ApplyConfiguration(); | 23 | void ApplyConfiguration(); |
| 24 | 24 | ||
| 25 | private slots: | ||
| 26 | void OnLanguageChanged(const QString& locale); | ||
| 27 | |||
| 28 | signals: | ||
| 29 | void LanguageChanged(const QString& locale); | ||
| 30 | |||
| 25 | private: | 31 | private: |
| 26 | void changeEvent(QEvent* event) override; | 32 | void changeEvent(QEvent* event) override; |
| 27 | 33 | ||
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 @@ | |||
| 5 | #include <array> | 5 | #include <array> |
| 6 | #include <utility> | 6 | #include <utility> |
| 7 | 7 | ||
| 8 | #include <QDirIterator> | ||
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 9 | #include "core/settings.h" | 10 | #include "core/settings.h" |
| 10 | #include "ui_configure_ui.h" | 11 | #include "ui_configure_ui.h" |
| @@ -29,6 +30,8 @@ constexpr std::array row_text_names{ | |||
| 29 | ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureUi) { | 30 | ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureUi) { |
| 30 | ui->setupUi(this); | 31 | ui->setupUi(this); |
| 31 | 32 | ||
| 33 | InitializeLanguageComboBox(); | ||
| 34 | |||
| 32 | for (const auto& theme : UISettings::themes) { | 35 | for (const auto& theme : UISettings::themes) { |
| 33 | ui->theme_combobox->addItem(QString::fromUtf8(theme.first), | 36 | ui->theme_combobox->addItem(QString::fromUtf8(theme.first), |
| 34 | QString::fromUtf8(theme.second)); | 37 | QString::fromUtf8(theme.second)); |
| @@ -72,6 +75,8 @@ void ConfigureUi::RequestGameListUpdate() { | |||
| 72 | 75 | ||
| 73 | void ConfigureUi::SetConfiguration() { | 76 | void ConfigureUi::SetConfiguration() { |
| 74 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); | 77 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); |
| 78 | ui->language_combobox->setCurrentIndex( | ||
| 79 | ui->language_combobox->findData(UISettings::values.language)); | ||
| 75 | ui->show_add_ons->setChecked(UISettings::values.show_add_ons); | 80 | ui->show_add_ons->setChecked(UISettings::values.show_add_ons); |
| 76 | ui->icon_size_combobox->setCurrentIndex( | 81 | ui->icon_size_combobox->setCurrentIndex( |
| 77 | ui->icon_size_combobox->findData(UISettings::values.icon_size)); | 82 | ui->icon_size_combobox->findData(UISettings::values.icon_size)); |
| @@ -100,6 +105,25 @@ void ConfigureUi::RetranslateUI() { | |||
| 100 | } | 105 | } |
| 101 | } | 106 | } |
| 102 | 107 | ||
| 108 | void ConfigureUi::InitializeLanguageComboBox() { | ||
| 109 | ui->language_combobox->addItem(tr("<System>"), QString{}); | ||
| 110 | ui->language_combobox->addItem(tr("English"), QStringLiteral("en")); | ||
| 111 | QDirIterator it(QStringLiteral(":/languages"), QDirIterator::NoIteratorFlags); | ||
| 112 | while (it.hasNext()) { | ||
| 113 | QString locale = it.next(); | ||
| 114 | locale.truncate(locale.lastIndexOf(QLatin1Char{'.'})); | ||
| 115 | locale.remove(0, locale.lastIndexOf(QLatin1Char{'/'}) + 1); | ||
| 116 | const QString lang = QLocale::languageToString(QLocale(locale).language()); | ||
| 117 | ui->language_combobox->addItem(lang, locale); | ||
| 118 | } | ||
| 119 | |||
| 120 | // Unlike other configuration changes, interface language changes need to be reflected on the | ||
| 121 | // interface immediately. This is done by passing a signal to the main window, and then | ||
| 122 | // retranslating when passing back. | ||
| 123 | connect(ui->language_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, | ||
| 124 | &ConfigureUi::OnLanguageChanged); | ||
| 125 | } | ||
| 126 | |||
| 103 | void ConfigureUi::InitializeIconSizeComboBox() { | 127 | void ConfigureUi::InitializeIconSizeComboBox() { |
| 104 | for (const auto& size : default_icon_sizes) { | 128 | for (const auto& size : default_icon_sizes) { |
| 105 | ui->icon_size_combobox->addItem(QString::fromUtf8(size.second), size.first); | 129 | ui->icon_size_combobox->addItem(QString::fromUtf8(size.second), size.first); |
| @@ -147,3 +171,10 @@ void ConfigureUi::UpdateSecondRowComboBox(bool init) { | |||
| 147 | ui->row_2_text_combobox->removeItem( | 171 | ui->row_2_text_combobox->removeItem( |
| 148 | ui->row_2_text_combobox->findData(ui->row_1_text_combobox->currentData())); | 172 | ui->row_2_text_combobox->findData(ui->row_1_text_combobox->currentData())); |
| 149 | } | 173 | } |
| 174 | |||
| 175 | void ConfigureUi::OnLanguageChanged(int index) { | ||
| 176 | if (index == -1) | ||
| 177 | return; | ||
| 178 | |||
| 179 | emit LanguageChanged(ui->language_combobox->itemData(index).toString()); | ||
| 180 | } | ||
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: | |||
| 20 | 20 | ||
| 21 | void ApplyConfiguration(); | 21 | void ApplyConfiguration(); |
| 22 | 22 | ||
| 23 | private slots: | ||
| 24 | void OnLanguageChanged(int index); | ||
| 25 | |||
| 26 | signals: | ||
| 27 | void LanguageChanged(const QString& locale); | ||
| 28 | |||
| 23 | private: | 29 | private: |
| 24 | void RequestGameListUpdate(); | 30 | void RequestGameListUpdate(); |
| 25 | 31 | ||
| @@ -28,6 +34,7 @@ private: | |||
| 28 | void changeEvent(QEvent*) override; | 34 | void changeEvent(QEvent*) override; |
| 29 | void RetranslateUI(); | 35 | void RetranslateUI(); |
| 30 | 36 | ||
| 37 | void InitializeLanguageComboBox(); | ||
| 31 | void InitializeIconSizeComboBox(); | 38 | void InitializeIconSizeComboBox(); |
| 32 | void InitializeRowComboBoxes(); | 39 | void InitializeRowComboBoxes(); |
| 33 | 40 | ||
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 @@ | |||
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| 14 | <string>Form</string> | 14 | <string>Form</string> |
| 15 | </property> | 15 | </property> |
| 16 | <layout class="QHBoxLayout" name="HorizontalLayout"> | 16 | <layout class="QVBoxLayout" name="verticalLayout"> |
| 17 | <item> | 17 | <item> |
| 18 | <layout class="QVBoxLayout" name="VerticalLayout"> | 18 | <widget class="QGroupBox" name="general_groupBox"> |
| 19 | <item> | 19 | <property name="title"> |
| 20 | <widget class="QGroupBox" name="GeneralGroupBox"> | 20 | <string>General</string> |
| 21 | <property name="title"> | 21 | </property> |
| 22 | <string>General</string> | 22 | <layout class="QHBoxLayout" name="horizontalLayout"> |
| 23 | </property> | 23 | <item> |
| 24 | <layout class="QHBoxLayout" name="horizontalLayout"> | 24 | <layout class="QVBoxLayout" name="verticalLayout_2"> |
| 25 | <item> | 25 | <item> |
| 26 | <layout class="QVBoxLayout" name="verticalLayout"> | 26 | <widget class="QLabel" name="label_change_language_info"> |
| 27 | <property name="text"> | ||
| 28 | <string>Note: Changing language will apply your configuration.</string> | ||
| 29 | </property> | ||
| 30 | <property name="wordWrap"> | ||
| 31 | <bool>true</bool> | ||
| 32 | </property> | ||
| 33 | </widget> | ||
| 34 | </item> | ||
| 35 | <item> | ||
| 36 | <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||
| 37 | <item> | ||
| 38 | <widget class="QLabel" name="language_label"> | ||
| 39 | <property name="text"> | ||
| 40 | <string>Interface language:</string> | ||
| 41 | </property> | ||
| 42 | </widget> | ||
| 43 | </item> | ||
| 44 | <item> | ||
| 45 | <widget class="QComboBox" name="language_combobox"/> | ||
| 46 | </item> | ||
| 47 | </layout> | ||
| 48 | </item> | ||
| 49 | <item> | ||
| 50 | <layout class="QHBoxLayout" name="horizontalLayout_3"> | ||
| 51 | <item> | ||
| 52 | <widget class="QLabel" name="theme_label"> | ||
| 53 | <property name="text"> | ||
| 54 | <string>Theme:</string> | ||
| 55 | </property> | ||
| 56 | </widget> | ||
| 57 | </item> | ||
| 27 | <item> | 58 | <item> |
| 28 | <layout class="QHBoxLayout" name="horizontalLayout_3"> | 59 | <widget class="QComboBox" name="theme_combobox"/> |
| 29 | <item> | ||
| 30 | <widget class="QLabel" name="theme_label"> | ||
| 31 | <property name="text"> | ||
| 32 | <string>Theme:</string> | ||
| 33 | </property> | ||
| 34 | </widget> | ||
| 35 | </item> | ||
| 36 | <item> | ||
| 37 | <widget class="QComboBox" name="theme_combobox"/> | ||
| 38 | </item> | ||
| 39 | </layout> | ||
| 40 | </item> | 60 | </item> |
| 41 | </layout> | 61 | </layout> |
| 42 | </item> | 62 | </item> |
| 43 | </layout> | 63 | </layout> |
| 44 | </widget> | 64 | </item> |
| 45 | </item> | 65 | </layout> |
| 46 | <item> | 66 | </widget> |
| 47 | <widget class="QGroupBox" name="GameListGroupBox"> | 67 | </item> |
| 48 | <property name="title"> | 68 | <item> |
| 49 | <string>Game List</string> | 69 | <widget class="QGroupBox" name="GameListGroupBox"> |
| 50 | </property> | 70 | <property name="title"> |
| 51 | <layout class="QHBoxLayout" name="GameListHorizontalLayout"> | 71 | <string>Game List</string> |
| 72 | </property> | ||
| 73 | <layout class="QHBoxLayout" name="GameListHorizontalLayout"> | ||
| 74 | <item> | ||
| 75 | <layout class="QVBoxLayout" name="GeneralVerticalLayout"> | ||
| 52 | <item> | 76 | <item> |
| 53 | <layout class="QVBoxLayout" name="GeneralVerticalLayout"> | 77 | <widget class="QCheckBox" name="show_add_ons"> |
| 78 | <property name="text"> | ||
| 79 | <string>Show Add-Ons Column</string> | ||
| 80 | </property> | ||
| 81 | </widget> | ||
| 82 | </item> | ||
| 83 | <item> | ||
| 84 | <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2"> | ||
| 54 | <item> | 85 | <item> |
| 55 | <widget class="QCheckBox" name="show_add_ons"> | 86 | <widget class="QLabel" name="icon_size_label"> |
| 56 | <property name="text"> | 87 | <property name="text"> |
| 57 | <string>Show Add-Ons Column</string> | 88 | <string>Icon Size:</string> |
| 58 | </property> | 89 | </property> |
| 59 | </widget> | 90 | </widget> |
| 60 | </item> | 91 | </item> |
| 61 | <item> | 92 | <item> |
| 62 | <layout class="QHBoxLayout" name="icon_size_qhbox_layout_2"> | 93 | <widget class="QComboBox" name="icon_size_combobox"/> |
| 63 | <item> | ||
| 64 | <widget class="QLabel" name="icon_size_label"> | ||
| 65 | <property name="text"> | ||
| 66 | <string>Icon Size:</string> | ||
| 67 | </property> | ||
| 68 | </widget> | ||
| 69 | </item> | ||
| 70 | <item> | ||
| 71 | <widget class="QComboBox" name="icon_size_combobox"/> | ||
| 72 | </item> | ||
| 73 | </layout> | ||
| 74 | </item> | 94 | </item> |
| 95 | </layout> | ||
| 96 | </item> | ||
| 97 | <item> | ||
| 98 | <layout class="QHBoxLayout" name="row_1_qhbox_layout"> | ||
| 75 | <item> | 99 | <item> |
| 76 | <layout class="QHBoxLayout" name="row_1_qhbox_layout"> | 100 | <widget class="QLabel" name="row_1_label"> |
| 77 | <item> | 101 | <property name="text"> |
| 78 | <widget class="QLabel" name="row_1_label"> | 102 | <string>Row 1 Text:</string> |
| 79 | <property name="text"> | 103 | </property> |
| 80 | <string>Row 1 Text:</string> | 104 | </widget> |
| 81 | </property> | ||
| 82 | </widget> | ||
| 83 | </item> | ||
| 84 | <item> | ||
| 85 | <widget class="QComboBox" name="row_1_text_combobox"/> | ||
| 86 | </item> | ||
| 87 | </layout> | ||
| 88 | </item> | 105 | </item> |
| 89 | <item> | 106 | <item> |
| 90 | <layout class="QHBoxLayout" name="row_2_qhbox_layout"> | 107 | <widget class="QComboBox" name="row_1_text_combobox"/> |
| 91 | <item> | 108 | </item> |
| 92 | <widget class="QLabel" name="row_2_label"> | 109 | </layout> |
| 93 | <property name="text"> | 110 | </item> |
| 94 | <string>Row 2 Text:</string> | 111 | <item> |
| 95 | </property> | 112 | <layout class="QHBoxLayout" name="row_2_qhbox_layout"> |
| 96 | </widget> | 113 | <item> |
| 97 | </item> | 114 | <widget class="QLabel" name="row_2_label"> |
| 98 | <item> | 115 | <property name="text"> |
| 99 | <widget class="QComboBox" name="row_2_text_combobox"/> | 116 | <string>Row 2 Text:</string> |
| 100 | </item> | 117 | </property> |
| 101 | </layout> | 118 | </widget> |
| 119 | </item> | ||
| 120 | <item> | ||
| 121 | <widget class="QComboBox" name="row_2_text_combobox"/> | ||
| 102 | </item> | 122 | </item> |
| 103 | </layout> | 123 | </layout> |
| 104 | </item> | 124 | </item> |
| 105 | </layout> | 125 | </layout> |
| 106 | </widget> | 126 | </item> |
| 107 | </item> | 127 | </layout> |
| 108 | <item> | 128 | </widget> |
| 109 | <spacer name="verticalSpacer"> | 129 | </item> |
| 110 | <property name="orientation"> | 130 | <item> |
| 111 | <enum>Qt::Vertical</enum> | 131 | <spacer name="verticalSpacer"> |
| 112 | </property> | 132 | <property name="orientation"> |
| 113 | <property name="sizeHint" stdset="0"> | 133 | <enum>Qt::Vertical</enum> |
| 114 | <size> | 134 | </property> |
| 115 | <width>20</width> | 135 | <property name="sizeHint" stdset="0"> |
| 116 | <height>40</height> | 136 | <size> |
| 117 | </size> | 137 | <width>20</width> |
| 118 | </property> | 138 | <height>40</height> |
| 119 | </spacer> | 139 | </size> |
| 120 | </item> | 140 | </property> |
| 121 | </layout> | 141 | </spacer> |
| 122 | </item> | 142 | </item> |
| 123 | </layout> | 143 | </layout> |
| 124 | </widget> | 144 | </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() | |||
| 191 | provider(std::make_unique<FileSys::ManualContentProvider>()) { | 191 | provider(std::make_unique<FileSys::ManualContentProvider>()) { |
| 192 | InitializeLogging(); | 192 | InitializeLogging(); |
| 193 | 193 | ||
| 194 | LoadTranslation(); | ||
| 195 | |||
| 194 | setAcceptDrops(true); | 196 | setAcceptDrops(true); |
| 195 | ui.setupUi(this); | 197 | ui.setupUi(this); |
| 196 | statusBar()->hide(); | 198 | statusBar()->hide(); |
| @@ -2048,6 +2050,9 @@ void GMainWindow::OnConfigure() { | |||
| 2048 | const bool old_discord_presence = UISettings::values.enable_discord_presence; | 2050 | const bool old_discord_presence = UISettings::values.enable_discord_presence; |
| 2049 | 2051 | ||
| 2050 | ConfigureDialog configure_dialog(this, hotkey_registry); | 2052 | ConfigureDialog configure_dialog(this, hotkey_registry); |
| 2053 | connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this, | ||
| 2054 | &GMainWindow::OnLanguageChanged); | ||
| 2055 | |||
| 2051 | const auto result = configure_dialog.exec(); | 2056 | const auto result = configure_dialog.exec(); |
| 2052 | if (result != QDialog::Accepted) { | 2057 | if (result != QDialog::Accepted) { |
| 2053 | return; | 2058 | return; |
| @@ -2620,6 +2625,43 @@ void GMainWindow::UpdateUITheme() { | |||
| 2620 | QIcon::setThemeSearchPaths(theme_paths); | 2625 | QIcon::setThemeSearchPaths(theme_paths); |
| 2621 | } | 2626 | } |
| 2622 | 2627 | ||
| 2628 | void GMainWindow::LoadTranslation() { | ||
| 2629 | // If the selected language is English, no need to install any translation | ||
| 2630 | if (UISettings::values.language == QStringLiteral("en")) { | ||
| 2631 | return; | ||
| 2632 | } | ||
| 2633 | |||
| 2634 | bool loaded; | ||
| 2635 | |||
| 2636 | if (UISettings::values.language.isEmpty()) { | ||
| 2637 | // If the selected language is empty, use system locale | ||
| 2638 | loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/")); | ||
| 2639 | } else { | ||
| 2640 | // Otherwise load from the specified file | ||
| 2641 | loaded = translator.load(UISettings::values.language, QStringLiteral(":/languages/")); | ||
| 2642 | } | ||
| 2643 | |||
| 2644 | if (loaded) { | ||
| 2645 | qApp->installTranslator(&translator); | ||
| 2646 | } else { | ||
| 2647 | UISettings::values.language = QStringLiteral("en"); | ||
| 2648 | } | ||
| 2649 | } | ||
| 2650 | |||
| 2651 | void GMainWindow::OnLanguageChanged(const QString& locale) { | ||
| 2652 | if (UISettings::values.language != QStringLiteral("en")) { | ||
| 2653 | qApp->removeTranslator(&translator); | ||
| 2654 | } | ||
| 2655 | |||
| 2656 | UISettings::values.language = locale; | ||
| 2657 | LoadTranslation(); | ||
| 2658 | ui.retranslateUi(this); | ||
| 2659 | UpdateWindowTitle(); | ||
| 2660 | |||
| 2661 | if (emulation_running) | ||
| 2662 | ui.action_Start->setText(tr("Continue")); | ||
| 2663 | } | ||
| 2664 | |||
| 2623 | void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { | 2665 | void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { |
| 2624 | #ifdef USE_DISCORD_PRESENCE | 2666 | #ifdef USE_DISCORD_PRESENCE |
| 2625 | if (state) { | 2667 | 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 @@ | |||
| 10 | 10 | ||
| 11 | #include <QMainWindow> | 11 | #include <QMainWindow> |
| 12 | #include <QTimer> | 12 | #include <QTimer> |
| 13 | #include <QTranslator> | ||
| 13 | 14 | ||
| 14 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 15 | #include "core/core.h" | 16 | #include "core/core.h" |
| @@ -225,6 +226,7 @@ private slots: | |||
| 225 | void OnCaptureScreenshot(); | 226 | void OnCaptureScreenshot(); |
| 226 | void OnCoreError(Core::System::ResultStatus, std::string); | 227 | void OnCoreError(Core::System::ResultStatus, std::string); |
| 227 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); | 228 | void OnReinitializeKeys(ReinitializeKeyBehavior behavior); |
| 229 | void OnLanguageChanged(const QString& locale); | ||
| 228 | 230 | ||
| 229 | private: | 231 | private: |
| 230 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); | 232 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); |
| @@ -237,6 +239,7 @@ private: | |||
| 237 | void HideMouseCursor(); | 239 | void HideMouseCursor(); |
| 238 | void ShowMouseCursor(); | 240 | void ShowMouseCursor(); |
| 239 | void OpenURL(const QUrl& url); | 241 | void OpenURL(const QUrl& url); |
| 242 | void LoadTranslation(); | ||
| 240 | 243 | ||
| 241 | Ui::MainWindow ui; | 244 | Ui::MainWindow ui; |
| 242 | 245 | ||
| @@ -285,6 +288,8 @@ private: | |||
| 285 | 288 | ||
| 286 | HotkeyRegistry hotkey_registry; | 289 | HotkeyRegistry hotkey_registry; |
| 287 | 290 | ||
| 291 | QTranslator translator; | ||
| 292 | |||
| 288 | // Install progress dialog | 293 | // Install progress dialog |
| 289 | QProgressDialog* install_progress; | 294 | QProgressDialog* install_progress; |
| 290 | 295 | ||
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 { | |||
| 75 | bool game_dir_deprecated_deepscan; | 75 | bool game_dir_deprecated_deepscan; |
| 76 | QVector<UISettings::GameDir> game_dirs; | 76 | QVector<UISettings::GameDir> game_dirs; |
| 77 | QStringList recent_files; | 77 | QStringList recent_files; |
| 78 | QString language; | ||
| 78 | 79 | ||
| 79 | QString theme; | 80 | QString theme; |
| 80 | 81 | ||