yuzu: Port translation support from Citra

Co-Authored-By: Weiyi Wang <wwylele@gmail.com>
This commit is contained in:
FearlessTobi 2020-01-26 00:26:07 +01:00
parent 4a8cb9a706
commit 347b50ad43
17 changed files with 261 additions and 92 deletions

View file

@ -133,12 +133,45 @@ 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}
${THEMES}
${LANGUAGES}
)
if (APPLE)

View file

@ -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();
}

View file

@ -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()) {

View file

@ -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;

View file

@ -5,6 +5,7 @@
#include <array>
#include <utility>
#include <QDirIterator>
#include "common/common_types.h"
#include "core/settings.h"
#include "ui_configure_ui.h"
@ -28,6 +29,23 @@ constexpr std::array row_text_names{
ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureUi) {
ui->setupUi(this);
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);
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,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
&ConfigureUi::OnLanguageChanged);
for (const auto& theme : UISettings::themes) {
ui->theme_combobox->addItem(QString::fromUtf8(theme.first),
@ -72,6 +90,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));
@ -147,3 +167,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());
}

View file

@ -20,6 +20,12 @@ public:
void ApplyConfiguration();
private slots:
void OnLanguageChanged(int index);
signals:
void LanguageChanged(const QString& locale);
private:
void RequestGameListUpdate();

View file

@ -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">
<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>
</item>
</layout>
<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>
</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">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="show_add_ons">
<widget class="QLabel" name="language_label">
<property name="text">
<string>Show Add-Ons Column</string>
<string>Interface language:</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="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="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>
</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="theme_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>
<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>
<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="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>
</item>
<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>
</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>
</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>

View file

@ -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) {

View file

@ -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;

View file

@ -75,6 +75,7 @@ struct Values {
bool game_dir_deprecated_deepscan;
QVector<UISettings::GameDir> game_dirs;
QStringList recent_files;
QString language;
QString theme;