From 180377181de42900d798230a99ad9252147abbe9 Mon Sep 17 00:00:00 2001 From: SternXD Date: Thu, 20 Nov 2025 17:50:21 -0500 Subject: [PATCH] Qt: Add language flag icons to settings and setup Signed-off-by: SternXD --- pcsx2-qt/GameList/GameListModel.cpp | 2 +- pcsx2-qt/QtUtils.cpp | 65 +++++++++++++++++++ pcsx2-qt/QtUtils.h | 8 +++ pcsx2-qt/Settings/BIOSSettingsWidget.cpp | 20 +++--- pcsx2-qt/Settings/GameSummaryWidget.cpp | 2 +- pcsx2-qt/Settings/InterfaceSettingsWidget.cpp | 10 ++- pcsx2-qt/SetupWizardDialog.cpp | 9 ++- 7 files changed, 103 insertions(+), 13 deletions(-) diff --git a/pcsx2-qt/GameList/GameListModel.cpp b/pcsx2-qt/GameList/GameListModel.cpp index 52fa567a39..7f9e74a0e8 100644 --- a/pcsx2-qt/GameList/GameListModel.cpp +++ b/pcsx2-qt/GameList/GameListModel.cpp @@ -484,7 +484,7 @@ QIcon GameListModel::getIconForType(const GameList::EntryType type) QIcon GameListModel::getIconForRegion(const GameList::Region region) { return QIcon( - QStringLiteral("%1/icons/flags/%2.svg").arg(QtHost::GetResourcesBasePath()).arg(GameList::RegionToString(region, false))); + QStringLiteral("%1/icons/flags/%2.svg").arg(QtHost::GetResourcesBasePath()).arg(GameList::RegionToFlagFilename(region))); } void GameListModel::loadThemeSpecificImages() diff --git a/pcsx2-qt/QtUtils.cpp b/pcsx2-qt/QtUtils.cpp index 47fb04df72..0bc2ca2673 100644 --- a/pcsx2-qt/QtUtils.cpp +++ b/pcsx2-qt/QtUtils.cpp @@ -5,10 +5,12 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -35,6 +37,7 @@ #include "common/CocoaTools.h" #include "common/Console.h" +#include "QtHost.h" #if defined(_WIN32) #include "common/RedtapeWindows.h" @@ -448,4 +451,66 @@ namespace QtUtils { new IconVariableDpiFilter(lbl, icon, size, lbl); } + + QString GetSystemLanguageCode() + { + std::vector> available = QtHost::GetAvailableLanguageList(); + QString locale = QLocale::system().name(); + locale.replace('_', '-'); + for (const std::pair& entry : available) + { + if (entry.second == locale) + return locale; + } + QStringView lang = QStringView(locale); + lang = lang.left(lang.indexOf('-')); + for (const std::pair& entry : available) + { + QStringView avail = QStringView(entry.second); + avail = avail.left(avail.indexOf('-')); + if (avail == lang) + return entry.second; + } + // No matches, default to English + return QStringLiteral("en-US"); + } + + QIcon GetFlagIconForLanguage(const QString& language_code) + { + QString actual_language_code = language_code; + if (language_code == QStringLiteral("system")) + { + actual_language_code = GetSystemLanguageCode(); + } + + QString country_code; + + const int dash_index = actual_language_code.indexOf('-'); + if (dash_index > 0 && dash_index < actual_language_code.length() - 1) + { + country_code = actual_language_code.mid(dash_index + 1); + } + else + { + if (actual_language_code == QStringLiteral("en")) + country_code = QStringLiteral("US"); + else + return QIcon(); // No flag available + } + + // Special cases + if (actual_language_code == QStringLiteral("es-419")) + { + // Latin America (es-419) use Mexico flag as representative + country_code = QStringLiteral("MX"); + } + else if (actual_language_code == QStringLiteral("sr-SP")) + { + // Serbia (SP) is not a valid ISO code, use RS (Serbia) + country_code = QStringLiteral("RS"); + } + + const QString flag_path = QStringLiteral("%1/icons/flags/%2.svg").arg(QtHost::GetResourcesBasePath()).arg(country_code.toLower()); + return QIcon(flag_path); + } } // namespace QtUtils diff --git a/pcsx2-qt/QtUtils.h b/pcsx2-qt/QtUtils.h index dac6bb5550..f901bc840b 100644 --- a/pcsx2-qt/QtUtils.h +++ b/pcsx2-qt/QtUtils.h @@ -199,4 +199,12 @@ namespace QtUtils /// Sets the scalable icon to a given label (svg icons, or icons with multiple size pixmaps) /// The icon will then be reloaded on DPR changes using an event filter void SetScalableIcon(QLabel* lbl, const QIcon& icon, const QSize& size); + + /// Gets the system language code, matching it against available languages + /// Returns the best matching language code, or "en-US" if no match is found + QString GetSystemLanguageCode(); + + /// Gets a flag icon for a given language code + /// Returns an empty QIcon if no flag is available for the language + QIcon GetFlagIconForLanguage(const QString& language_code); } // namespace QtUtils diff --git a/pcsx2-qt/Settings/BIOSSettingsWidget.cpp b/pcsx2-qt/Settings/BIOSSettingsWidget.cpp index be0c79513a..71348a62e9 100644 --- a/pcsx2-qt/Settings/BIOSSettingsWidget.cpp +++ b/pcsx2-qt/Settings/BIOSSettingsWidget.cpp @@ -79,40 +79,42 @@ void BIOSSettingsWidget::populateList(QTreeWidget* list, const std::string& dire switch (bios_region) { case 0: // Japan - item->setIcon(0, QIcon(QStringLiteral("%1/icons/flags/NTSC-J.svg").arg(res_path))); + item->setIcon(0, QIcon(QStringLiteral("%1/icons/flags/jp.svg").arg(res_path))); break; case 1: // USA - item->setIcon(0, QIcon(QStringLiteral("%1/icons/flags/NTSC-U.svg").arg(res_path))); + item->setIcon(0, QIcon(QStringLiteral("%1/icons/flags/us.svg").arg(res_path))); break; case 2: // Europe - item->setIcon(0, QIcon(QStringLiteral("%1/icons/flags/PAL-E.svg").arg(res_path))); + item->setIcon(0, QIcon(QStringLiteral("%1/icons/flags/eu.svg").arg(res_path))); break; case 3: // Oceania - item->setIcon(0, QIcon(QStringLiteral("%1/icons/flags/PAL-A.svg").arg(res_path))); + item->setIcon(0, QIcon(QStringLiteral("%1/icons/flags/au.svg").arg(res_path))); break; case 4: // Asia - item->setIcon(0, QIcon(QStringLiteral("%1/icons/flags/NTSC-HK.svg").arg(res_path))); + item->setIcon(0, QIcon(QStringLiteral("%1/icons/flags/hk.svg").arg(res_path))); break; case 5: // Russia - item->setIcon(0, QIcon(QStringLiteral("%1/icons/flags/PAL-R.svg").arg(res_path))); + item->setIcon(0, QIcon(QStringLiteral("%1/icons/flags/ru.svg").arg(res_path))); break; case 6: // China - item->setIcon(0, QIcon(QStringLiteral("%1/icons/flags/NTSC-C.svg").arg(res_path))); + item->setIcon(0, QIcon(QStringLiteral("%1/icons/flags/cn.svg").arg(res_path))); break; - case 7: // Mexico, flag is missing + case 7: // Mexico + item->setIcon(0, QIcon(QStringLiteral("%1/icons/flags/mx.svg").arg(res_path))); + break; case 8: // T10K case 9: // Test case 10: // Free default: - item->setIcon(0, QIcon(QStringLiteral("%1/icons/flags/NTSC-J.svg").arg(res_path))); + item->setIcon(0, QIcon(QStringLiteral("%1/icons/flags/jp.svg").arg(res_path))); break; } diff --git a/pcsx2-qt/Settings/GameSummaryWidget.cpp b/pcsx2-qt/Settings/GameSummaryWidget.cpp index 04388938aa..0b4fd50ada 100644 --- a/pcsx2-qt/Settings/GameSummaryWidget.cpp +++ b/pcsx2-qt/Settings/GameSummaryWidget.cpp @@ -33,7 +33,7 @@ GameSummaryWidget::GameSummaryWidget(const GameList::Entry* entry, SettingsWindo for (int i = 0; i < m_ui.region->count(); i++) { m_ui.region->setItemIcon(i, - QIcon(QStringLiteral("%1/icons/flags/%2.svg").arg(base_path).arg(GameList::RegionToString(static_cast(i), false)))); + QIcon(QStringLiteral("%1/icons/flags/%2.svg").arg(base_path).arg(GameList::RegionToFlagFilename(static_cast(i))))); } m_entry_path = entry->path; diff --git a/pcsx2-qt/Settings/InterfaceSettingsWidget.cpp b/pcsx2-qt/Settings/InterfaceSettingsWidget.cpp index a771691763..89665c1c3b 100644 --- a/pcsx2-qt/Settings/InterfaceSettingsWidget.cpp +++ b/pcsx2-qt/Settings/InterfaceSettingsWidget.cpp @@ -11,6 +11,8 @@ #include "SettingsWindow.h" #include "QtHost.h" +#include + const char* InterfaceSettingsWidget::THEME_NAMES[] = { QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Native"), //: Ignore what Crowdin says in this string about "[Light]/[Dark]" being untouchable here, these are not variables in this case and must be translated. @@ -247,7 +249,13 @@ void InterfaceSettingsWidget::onRenderToSeparateWindowChanged() void InterfaceSettingsWidget::populateLanguages() { for (const std::pair& it : QtHost::GetAvailableLanguageList()) - m_ui.language->addItem(it.first, it.second); + { + QIcon flag_icon = QtUtils::GetFlagIconForLanguage(it.second); + if (!flag_icon.isNull()) + m_ui.language->addItem(flag_icon, it.first, it.second); + else + m_ui.language->addItem(it.first, it.second); + } } void InterfaceSettingsWidget::onSetGameListBackgroundTriggered() diff --git a/pcsx2-qt/SetupWizardDialog.cpp b/pcsx2-qt/SetupWizardDialog.cpp index abb89321d7..5c6bc35563 100644 --- a/pcsx2-qt/SetupWizardDialog.cpp +++ b/pcsx2-qt/SetupWizardDialog.cpp @@ -10,6 +10,7 @@ #include "Settings/InterfaceSettingsWidget.h" #include "SetupWizardDialog.h" +#include #include SetupWizardDialog::SetupWizardDialog() @@ -178,7 +179,13 @@ void SetupWizardDialog::setupLanguagePage() connect(m_ui.theme, QOverload::of(&QComboBox::currentIndexChanged), this, &SetupWizardDialog::themeChanged); for (const std::pair& it : QtHost::GetAvailableLanguageList()) - m_ui.language->addItem(it.first, it.second); + { + QIcon flag_icon = QtUtils::GetFlagIconForLanguage(it.second); + if (!flag_icon.isNull()) + m_ui.language->addItem(flag_icon, it.first, it.second); + else + m_ui.language->addItem(it.first, it.second); + } SettingWidgetBinder::BindWidgetToStringSetting( nullptr, m_ui.language, "UI", "Language", QtHost::GetDefaultLanguage()); connect(