From 8d55db334e0416f77188134bf1db128ec440a545 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sat, 27 Dec 2025 16:46:27 +0100 Subject: [PATCH] Qt: deploy and apply Qt translations --- .ci/deploy-windows-clang.sh | 2 +- .ci/setup-windows.sh | 2 + rpcs3/rpcs3.vcxproj | 4 +- rpcs3/rpcs3qt/gui_application.cpp | 69 +++++++++++++++++++++++++++---- rpcs3/rpcs3qt/gui_application.h | 3 +- 5 files changed, 67 insertions(+), 13 deletions(-) diff --git a/.ci/deploy-windows-clang.sh b/.ci/deploy-windows-clang.sh index c95f82e7b8..0bf731e7c8 100644 --- a/.ci/deploy-windows-clang.sh +++ b/.ci/deploy-windows-clang.sh @@ -38,7 +38,7 @@ else echo "Failed to download translations.zip. Continuing without translations." exit 0 } - unzip -o translations.zip -d "./bin/share/qt6/translations" >/dev/null 2>&1 || \ + 7z x translations.zip -o"./bin/share/qt6/translations" >/dev/null 2>&1 || \ echo "Failed to extract translations.zip. Continuing without translations." rm -f translations.zip fi diff --git a/.ci/setup-windows.sh b/.ci/setup-windows.sh index f637cec9ad..d874c7a7f0 100755 --- a/.ci/setup-windows.sh +++ b/.ci/setup-windows.sh @@ -14,6 +14,7 @@ QT_DECL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtdeclarative${QT_SUFFIX}" QT_TOOL_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qttools${QT_SUFFIX}" QT_MM_URL="${QT_HOST}${QT_PREFIX}addons.qtmultimedia.${QT_PREFIX_2}qtmultimedia${QT_SUFFIX}" QT_SVG_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtsvg${QT_SUFFIX}" +QT_TRANSLATIONS_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qttranslations${QT_SUFFIX}" LLVMLIBS_URL="https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-${LLVM_VER}/llvmlibs_mt.7z" VULKAN_SDK_URL="https://www.dropbox.com/scl/fi/sjjh0fc4ld281pjbl2xzu/VulkanSDK-${VULKAN_VER}-Installer.exe?rlkey=f6wzc0lvms5vwkt2z3qabfv9d&dl=1" CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.11.2/ccache-4.11.2-windows-x86_64.zip" @@ -24,6 +25,7 @@ DEP_URLS=" \ $QT_TOOL_URL \ $QT_MM_URL \ $QT_SVG_URL \ + $QT_TRANSLATIONS_URL \ $LLVMLIBS_URL \ $VULKAN_SDK_URL\ $CCACHE_URL" diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index b38aa78567..83de843746 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -110,7 +110,7 @@ - $(QTDIR)\bin\windeployqt6 --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --no-system-d3d-compiler --no-system-dxc-compiler --no-quick-import --plugindir "$(TargetDir)qt6\plugins" --release "$(TargetPath)" + $(QTDIR)\bin\windeployqt6 --no-compiler-runtime --no-opengl-sw --no-patchqt --no-quick --no-system-d3d-compiler --no-system-dxc-compiler --no-quick-import --plugindir "$(TargetDir)qt6\plugins" --translationdir "$(TargetDir)qt6\translations" --release "$(TargetPath)" xcopy /y /d "$(SolutionDir)3rdparty\opencv\opencv\opencv412\build\x64\bin\opencv_world4120.dll" "$(OutDir)" @@ -169,7 +169,7 @@ - $(QTDIR)\bin\windeployqt6 --no-compiler-runtime --no-opengl-sw --no-patchqt --no-translations --no-quick --no-system-d3d-compiler --no-system-dxc-compiler --no-quick-import --plugindir "$(TargetDir)qt6\plugins" --debug "$(TargetPath)" + $(QTDIR)\bin\windeployqt6 --no-compiler-runtime --no-opengl-sw --no-patchqt --no-quick --no-system-d3d-compiler --no-system-dxc-compiler --no-quick-import --plugindir "$(TargetDir)qt6\plugins" --translationdir "$(TargetDir)qt6\translations" --debug "$(TargetPath)" xcopy /y /d "$(SolutionDir)3rdparty\opencv\opencv\opencv412\build\x64\bin\opencv_world4120.dll" "$(OutDir)" diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index e028aaefa8..443dcb4e0c 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -216,29 +216,74 @@ bool gui_application::Init() return true; } -void gui_application::SwitchTranslator(QTranslator& translator, const QString& filename, const QString& language_code) +void gui_application::SwitchTranslator(const QString& language_code) { // remove the old translator - removeTranslator(&translator); + removeTranslator(&m_translator); + for (QTranslator* qt_translator : m_qt_translators) + { + removeTranslator(qt_translator); + qt_translator->deleteLater(); + } + m_qt_translators.clear(); + const QString default_code = QLocale(QLocale::English).bcp47Name(); const QString lang_path = QLibraryInfo::path(QLibraryInfo::TranslationsPath) + QStringLiteral("/"); - const QString file_path = lang_path + filename; + // Load qt translation files + const QDir dir(lang_path); + if (dir.exists()) + { + QStringList qm_files = dir.entryList(QStringList() << QStringLiteral("qt*_%1.qm").arg(language_code), QDir::Files | QDir::Readable); + if (qm_files.empty()) + { + qm_files = dir.entryList(QStringList() << QStringLiteral("qt*_%1.qm").arg(QLocale::languageToCode(QLocale(language_code).language())), QDir::Files | QDir::Readable); + } + + for (const QString& qm_file : qm_files) + { + const QString file_path = lang_path + qm_file; + QTranslator* qt_translator = new QTranslator(this); + + if (qt_translator->load(file_path)) + { + gui_log.notice("Installing translation: '%s'", file_path); + installTranslator(qt_translator); + m_qt_translators.push_back(std::move(qt_translator)); + } + else + { + gui_log.error("Failed to load translation: '%s'", file_path); + qt_translator->deleteLater(); + } + } + } + else + { + gui_log.error("Qt translation dir '%s' does not exist", lang_path); + } + + const QString file_path = lang_path + QStringLiteral("rpcs3_%1.qm").arg(language_code); if (QFileInfo(file_path).isFile()) { // load the new translator - if (translator.load(file_path)) + if (m_translator.load(file_path)) { - installTranslator(&translator); + gui_log.notice("Installing translation: '%s'", file_path); + installTranslator(&m_translator); + } + else + { + gui_log.error("Failed to load translation: '%s'", file_path); } } - else if (QString default_code = QLocale(QLocale::English).bcp47Name(); language_code != default_code) + else if (language_code != default_code) { // show error, but ignore default case "en", since it is handled in source code - gui_log.error("No translation file found in: %s", file_path); + gui_log.error("No translation file found in: '%s'", file_path); // reset current language to default "en" - set_language_code(std::move(default_code)); + set_language_code(default_code); } } @@ -260,7 +305,7 @@ void gui_application::LoadLanguage(const QString& language_code) // As per QT recommendations to avoid conflicts for POSIX functions std::setlocale(LC_NUMERIC, "C"); - SwitchTranslator(m_translator, QStringLiteral("rpcs3_%1.qm").arg(language_code), language_code); + SwitchTranslator(language_code); if (m_main_window) { @@ -285,6 +330,7 @@ QStringList gui_application::GetAvailableLanguageCodes() QStringList language_codes; const QString language_path = QLibraryInfo::path(QLibraryInfo::TranslationsPath); + gui_log.notice("Checking languages in '%s'", language_path); if (QFileInfo(language_path).isDir()) { @@ -303,10 +349,15 @@ QStringList gui_application::GetAvailableLanguageCodes() } else { + gui_log.notice("Found language '%s' (%s)", language_code, filename); language_codes << language_code; } } } + else + { + gui_log.error("Language dir not found: '%s'", language_path); + } return language_codes; } diff --git a/rpcs3/rpcs3qt/gui_application.h b/rpcs3/rpcs3qt/gui_application.h index bec8424da4..948965ef47 100644 --- a/rpcs3/rpcs3qt/gui_application.h +++ b/rpcs3/rpcs3qt/gui_application.h @@ -81,7 +81,7 @@ private: return thread(); } - void SwitchTranslator(QTranslator& translator, const QString& filename, const QString& language_code); + void SwitchTranslator(const QString& language_code); void LoadLanguage(const QString& language_code); static QStringList GetAvailableLanguageCodes(); @@ -101,6 +101,7 @@ private: } m_native_event_filter; + std::vector m_qt_translators; QTranslator m_translator; QString m_language_code; static s32 m_language_id;