yuzu: Port translation support from Citra

Co-Authored-By: Weiyi Wang <wwylele@gmail.com>
pull/8/head
FearlessTobi 5 years ago
parent 4a8cb9a706
commit 347b50ad43

@ -5,7 +5,7 @@ cd /yuzu
ccache -s ccache -s
mkdir build || true && cd build mkdir build || true && cd build
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 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
ninja ninja

@ -5,7 +5,7 @@ cd /yuzu
ccache -s ccache -s
mkdir build || true && cd build mkdir build || true && cd build
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 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
ninja ninja
ccache -s ccache -s

@ -13,6 +13,7 @@ project(yuzu)
option(ENABLE_SDL2 "Enable the SDL2 frontend" ON) option(ENABLE_SDL2 "Enable the SDL2 frontend" ON)
option(ENABLE_QT "Enable the Qt frontend" ON) option(ENABLE_QT "Enable the Qt frontend" ON)
option(ENABLE_QT_TRANSLATION "Enable translations for the Qt frontend" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF) CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF)
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
@ -224,6 +225,10 @@ if(ENABLE_QT)
if (YUZU_USE_QT_WEB_ENGINE) if (YUZU_USE_QT_WEB_ENGINE)
find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets) find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
endif() endif()
if (ENABLE_QT_TRANSLATION)
find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT})
endif()
if (NOT Qt5_FOUND) if (NOT Qt5_FOUND)
list(APPEND CONAN_REQUIRED_LIBS "qt/5.14.1@bincrafters/stable") list(APPEND CONAN_REQUIRED_LIBS "qt/5.14.1@bincrafters/stable")
endif() endif()

@ -24,6 +24,8 @@ Most of the development happens on GitHub. It's also where [our central reposito
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. 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.
If you want to contribute to the user interface translation, please checkout [yuzu project on transifex](https://www.transifex.com/yuzu-emulator/yuzu). We centralize the translation work there, and periodically upstream translation.
### Building ### Building
* __Windows__: [Windows Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Windows) * __Windows__: [Windows Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Windows)

@ -0,0 +1,2 @@
# Ignore the source language file
en.ts

@ -0,0 +1,8 @@
[main]
host = https://www.transifex.com
[yuzu.emulator]
file_filter = <lang>.ts
source_file = en.ts
source_lang = en
type = QT

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

@ -133,12 +133,45 @@ file(GLOB COMPAT_LIST
file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*) file(GLOB_RECURSE ICONS ${PROJECT_SOURCE_DIR}/dist/icons/*)
file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*) 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 target_sources(yuzu
PRIVATE PRIVATE
${COMPAT_LIST} ${COMPAT_LIST}
${ICONS} ${ICONS}
${THEMES} ${THEMES}
${LANGUAGES}
) )
if (APPLE) if (APPLE)

@ -611,6 +611,7 @@ void Config::ReadPathValues() {
} }
} }
UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList(); UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList();
UISettings::values.language = ReadSetting(QStringLiteral("language"), QString{}).toString();
qt_config->endGroup(); qt_config->endGroup();
} }
@ -1095,6 +1096,7 @@ void Config::SavePathValues() {
} }
qt_config->endArray(); qt_config->endArray();
WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files); WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files);
WriteSetting(QStringLiteral("language"), UISettings::values.language, QString{});
qt_config->endGroup(); qt_config->endGroup();
} }

@ -23,6 +23,7 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
SetConfiguration(); SetConfiguration();
PopulateSelectionList(); PopulateSelectionList();
connect(ui->uiTab, &ConfigureUi::LanguageChanged, this, &ConfigureDialog::OnLanguageChanged);
connect(ui->selectorList, &QListWidget::itemSelectionChanged, this, connect(ui->selectorList, &QListWidget::itemSelectionChanged, this,
&ConfigureDialog::UpdateVisibleTabs); &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() { void ConfigureDialog::UpdateVisibleTabs() {
const auto items = ui->selectorList->selectedItems(); const auto items = ui->selectorList->selectedItems();
if (items.isEmpty()) { if (items.isEmpty()) {

@ -22,6 +22,12 @@ public:
void ApplyConfiguration(); void ApplyConfiguration();
private slots:
void OnLanguageChanged(const QString& locale);
signals:
void LanguageChanged(const QString& locale);
private: private:
void changeEvent(QEvent* event) override; void changeEvent(QEvent* event) override;

@ -5,6 +5,7 @@
#include <array> #include <array>
#include <utility> #include <utility>
#include <QDirIterator>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/settings.h" #include "core/settings.h"
#include "ui_configure_ui.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) { ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureUi) {
ui->setupUi(this); 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) { for (const auto& theme : UISettings::themes) {
ui->theme_combobox->addItem(QString::fromUtf8(theme.first), ui->theme_combobox->addItem(QString::fromUtf8(theme.first),
@ -72,6 +90,8 @@ void ConfigureUi::RequestGameListUpdate() {
void ConfigureUi::SetConfiguration() { void ConfigureUi::SetConfiguration() {
ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); 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->show_add_ons->setChecked(UISettings::values.show_add_ons);
ui->icon_size_combobox->setCurrentIndex( ui->icon_size_combobox->setCurrentIndex(
ui->icon_size_combobox->findData(UISettings::values.icon_size)); 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->removeItem(
ui->row_2_text_combobox->findData(ui->row_1_text_combobox->currentData())); 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());
}

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

@ -13,112 +13,132 @@
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QHBoxLayout" name="HorizontalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<layout class="QVBoxLayout" name="VerticalLayout"> <widget class="QGroupBox" name="general_groupBox">
<item> <property name="title">
<widget class="QGroupBox" name="GeneralGroupBox"> <string>General</string>
<property name="title"> </property>
<string>General</string> <layout class="QHBoxLayout" name="horizontalLayout">
</property> <item>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <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> <item>
<layout class="QHBoxLayout" name="horizontalLayout_3"> <widget class="QComboBox" name="theme_combobox"/>
<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> </item>
</layout> </layout>
</item> </item>
</layout> </layout>
</widget> </item>
</item> </layout>
<item> </widget>
<widget class="QGroupBox" name="GameListGroupBox"> </item>
<property name="title"> <item>
<string>Game List</string> <widget class="QGroupBox" name="GameListGroupBox">
</property> <property name="title">
<layout class="QHBoxLayout" name="GameListHorizontalLayout"> <string>Game List</string>
</property>
<layout class="QHBoxLayout" name="GameListHorizontalLayout">
<item>
<layout class="QVBoxLayout" name="GeneralVerticalLayout">
<item> <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> <item>
<widget class="QCheckBox" name="show_add_ons"> <widget class="QLabel" name="icon_size_label">
<property name="text"> <property name="text">
<string>Show Add-Ons Column</string> <string>Icon Size:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="icon_size_qhbox_layout_2"> <widget class="QComboBox" name="icon_size_combobox"/>
<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>
</item>
<item>
<layout class="QHBoxLayout" name="row_1_qhbox_layout">
<item> <item>
<layout class="QHBoxLayout" name="row_1_qhbox_layout"> <widget class="QLabel" name="row_1_label">
<item> <property name="text">
<widget class="QLabel" name="row_1_label"> <string>Row 1 Text:</string>
<property name="text"> </property>
<string>Row 1 Text:</string> </widget>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="row_1_text_combobox"/>
</item>
</layout>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="row_2_qhbox_layout"> <widget class="QComboBox" name="row_1_text_combobox"/>
<item> </item>
<widget class="QLabel" name="row_2_label"> </layout>
<property name="text"> </item>
<string>Row 2 Text:</string> <item>
</property> <layout class="QHBoxLayout" name="row_2_qhbox_layout">
</widget> <item>
</item> <widget class="QLabel" name="row_2_label">
<item> <property name="text">
<widget class="QComboBox" name="row_2_text_combobox"/> <string>Row 2 Text:</string>
</item> </property>
</layout> </widget>
</item>
<item>
<widget class="QComboBox" name="row_2_text_combobox"/>
</item> </item>
</layout> </layout>
</item> </item>
</layout> </layout>
</widget> </item>
</item> </layout>
<item> </widget>
<spacer name="verticalSpacer"> </item>
<property name="orientation"> <item>
<enum>Qt::Vertical</enum> <spacer name="verticalSpacer">
</property> <property name="orientation">
<property name="sizeHint" stdset="0"> <enum>Qt::Vertical</enum>
<size> </property>
<width>20</width> <property name="sizeHint" stdset="0">
<height>40</height> <size>
</size> <width>20</width>
</property> <height>40</height>
</spacer> </size>
</item> </property>
</layout> </spacer>
</item> </item>
</layout> </layout>
</widget> </widget>

@ -191,6 +191,8 @@ GMainWindow::GMainWindow()
provider(std::make_unique<FileSys::ManualContentProvider>()) { provider(std::make_unique<FileSys::ManualContentProvider>()) {
InitializeLogging(); InitializeLogging();
LoadTranslation();
setAcceptDrops(true); setAcceptDrops(true);
ui.setupUi(this); ui.setupUi(this);
statusBar()->hide(); statusBar()->hide();
@ -2048,6 +2050,9 @@ void GMainWindow::OnConfigure() {
const bool old_discord_presence = UISettings::values.enable_discord_presence; const bool old_discord_presence = UISettings::values.enable_discord_presence;
ConfigureDialog configure_dialog(this, hotkey_registry); ConfigureDialog configure_dialog(this, hotkey_registry);
connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this,
&GMainWindow::OnLanguageChanged);
const auto result = configure_dialog.exec(); const auto result = configure_dialog.exec();
if (result != QDialog::Accepted) { if (result != QDialog::Accepted) {
return; return;
@ -2620,6 +2625,43 @@ void GMainWindow::UpdateUITheme() {
QIcon::setThemeSearchPaths(theme_paths); 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) { void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
#ifdef USE_DISCORD_PRESENCE #ifdef USE_DISCORD_PRESENCE
if (state) { if (state) {

@ -10,6 +10,7 @@
#include <QMainWindow> #include <QMainWindow>
#include <QTimer> #include <QTimer>
#include <QTranslator>
#include "common/common_types.h" #include "common/common_types.h"
#include "core/core.h" #include "core/core.h"
@ -225,6 +226,7 @@ private slots:
void OnCaptureScreenshot(); void OnCaptureScreenshot();
void OnCoreError(Core::System::ResultStatus, std::string); void OnCoreError(Core::System::ResultStatus, std::string);
void OnReinitializeKeys(ReinitializeKeyBehavior behavior); void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
void OnLanguageChanged(const QString& locale);
private: private:
std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
@ -237,6 +239,7 @@ private:
void HideMouseCursor(); void HideMouseCursor();
void ShowMouseCursor(); void ShowMouseCursor();
void OpenURL(const QUrl& url); void OpenURL(const QUrl& url);
void LoadTranslation();
Ui::MainWindow ui; Ui::MainWindow ui;
@ -285,6 +288,8 @@ private:
HotkeyRegistry hotkey_registry; HotkeyRegistry hotkey_registry;
QTranslator translator;
// Install progress dialog // Install progress dialog
QProgressDialog* install_progress; QProgressDialog* install_progress;

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

Loading…
Cancel
Save