mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-01-18 15:15:37 +00:00
Added basic configuration support and menu entries for theme localization
This commit is contained in:
parent
3f4ed60649
commit
eecd84ac09
|
@ -479,6 +479,49 @@ void GuiMenu::openUIOptions()
|
|||
themeTransitionsFunc(Settings::getInstance()->getString("Theme"),
|
||||
Settings::getInstance()->getString("ThemeTransitions"));
|
||||
|
||||
// Theme language.
|
||||
auto themeLanguage = std::make_shared<OptionListComponent<std::string>>(
|
||||
getHelpStyle(), _("THEME LANGUAGE"), false);
|
||||
s->addWithLabel(_("THEME LANGUAGE"), themeLanguage);
|
||||
s->addSaveFunc([themeLanguage, s] {
|
||||
if (themeLanguage->getSelected() != Settings::getInstance()->getString("ThemeLanguage")) {
|
||||
Settings::getInstance()->setString("ThemeLanguage", themeLanguage->getSelected());
|
||||
s->setNeedsSaving();
|
||||
s->setNeedsReloading();
|
||||
s->setInvalidateCachedBackground();
|
||||
}
|
||||
});
|
||||
|
||||
auto themeLanguageFunc = [=](const std::string& selectedTheme,
|
||||
const std::string& selectedLanguage) {
|
||||
std::map<std::string, ThemeData::Theme, ThemeData::StringComparator>::const_iterator
|
||||
currentSet {themes.find(selectedTheme)};
|
||||
if (currentSet == themes.cend())
|
||||
return;
|
||||
// We need to recreate the OptionListComponent entries.
|
||||
themeLanguage->clearEntries();
|
||||
if (currentSet->second.capabilities.languages.size() > 0) {
|
||||
for (auto& language : currentSet->second.capabilities.languages) {
|
||||
themeLanguage->add(
|
||||
Utils::String::toUpper(_(ThemeData::getLanguageLabel(language).c_str())),
|
||||
language, language == selectedLanguage);
|
||||
}
|
||||
if (themeLanguage->getSelectedObjects().size() == 0)
|
||||
themeLanguage->selectEntry(0);
|
||||
}
|
||||
else {
|
||||
themeLanguage->add(_("NONE DEFINED"), "none", true);
|
||||
themeLanguage->setEnabled(false);
|
||||
themeLanguage->setOpacity(DISABLED_OPACITY);
|
||||
themeLanguage->getParent()
|
||||
->getChild(themeLanguage->getChildIndex() - 1)
|
||||
->setOpacity(DISABLED_OPACITY);
|
||||
}
|
||||
};
|
||||
|
||||
themeLanguageFunc(Settings::getInstance()->getString("Theme"),
|
||||
Settings::getInstance()->getString("ThemeLanguage"));
|
||||
|
||||
// Application language.
|
||||
auto applicationLanguage = std::make_shared<OptionListComponent<std::string>>(
|
||||
getHelpStyle(), _("APPLICATION LANGUAGE"), false);
|
||||
|
@ -519,6 +562,8 @@ void GuiMenu::openUIOptions()
|
|||
s->setNeedsSaving();
|
||||
s->setNeedsCloseMenu([this] { delete this; });
|
||||
s->setNeedsRescanROMDirectory();
|
||||
s->setNeedsReloading();
|
||||
s->setNeedsCollectionsUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1008,6 +1053,7 @@ void GuiMenu::openUIOptions()
|
|||
themeColorSchemesFunc(themeName, themeColorScheme->getSelected());
|
||||
themeFontSizeFunc(themeName, themeFontSize->getSelected());
|
||||
themeAspectRatiosFunc(themeName, themeAspectRatio->getSelected());
|
||||
themeLanguageFunc(themeName, themeLanguage->getSelected());
|
||||
themeTransitionsFunc(themeName, themeTransitions->getSelected());
|
||||
}
|
||||
int selectableVariants {0};
|
||||
|
@ -1057,6 +1103,20 @@ void GuiMenu::openUIOptions()
|
|||
->getChild(themeFontSize->getChildIndex() - 1)
|
||||
->setOpacity(DISABLED_OPACITY);
|
||||
}
|
||||
if (selectedTheme->second.capabilities.languages.size() > 0) {
|
||||
themeLanguage->setEnabled(true);
|
||||
themeLanguage->setOpacity(1.0f);
|
||||
themeLanguage->getParent()
|
||||
->getChild(themeLanguage->getChildIndex() - 1)
|
||||
->setOpacity(1.0f);
|
||||
}
|
||||
else {
|
||||
themeLanguage->setEnabled(false);
|
||||
themeLanguage->setOpacity(DISABLED_OPACITY);
|
||||
themeLanguage->getParent()
|
||||
->getChild(themeLanguage->getChildIndex() - 1)
|
||||
->setOpacity(DISABLED_OPACITY);
|
||||
}
|
||||
if (selectedTheme->second.capabilities.aspectRatios.size() > 0) {
|
||||
themeAspectRatio->setEnabled(true);
|
||||
themeAspectRatio->setOpacity(1.0f);
|
||||
|
|
|
@ -77,7 +77,6 @@ void GuiSettings::save()
|
|||
mCloseMenuFunction = nullptr;
|
||||
}
|
||||
ViewController::getInstance()->rescanROMDirectory();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mNeedsCollectionsUpdate) {
|
||||
|
|
|
@ -169,6 +169,7 @@ void Settings::setDefaults()
|
|||
mStringMap["ThemeFontSize"] = {"", ""};
|
||||
mStringMap["ThemeAspectRatio"] = {"", ""};
|
||||
mStringMap["ThemeTransitions"] = {"automatic", "automatic"};
|
||||
mStringMap["ThemeLanguage"] = {"automatic", "automatic"};
|
||||
mStringMap["ApplicationLanguage"] = {"automatic", "automatic"};
|
||||
mStringMap["QuickSystemSelect"] = {"leftrightshoulders", "leftrightshoulders"};
|
||||
mStringMap["StartupSystem"] = {"", ""};
|
||||
|
|
|
@ -102,6 +102,25 @@ std::map<std::string, float> ThemeData::sAspectRatioMap {
|
|||
{"32:9_vertical", 0.2813f},
|
||||
{"1:1", 1.0f}};
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> ThemeData::sSupportedLanguages {
|
||||
{"automatic", "automatic"},
|
||||
{"en_US", "ENGLISH (UNITED STATES)"},
|
||||
{"en_GB", "ENGLISH (UNITED KINGDOM)"},
|
||||
{"el_GR", "ΕΛΛΗΝΙΚΆ"},
|
||||
{"de_DE", "DEUTSCH"},
|
||||
{"es_ES", "ESPAÑOL (ESPAÑA)"},
|
||||
{"fr_FR", "FRANÇAIS"},
|
||||
{"it_IT", "ITALIANO"},
|
||||
{"nl_NL", "NEDERLANDS"},
|
||||
{"pl_PL", "POLSKI"},
|
||||
{"pt_BR", "PORTUGUÊS (BRASIL)"},
|
||||
{"ro_RO", "ROMÂNĂ"},
|
||||
{"ru_RU", "РУССКИЙ"},
|
||||
{"sv_SE", "SVENSKA"},
|
||||
{"ja_JP", "日本語"},
|
||||
{"zh_CN", "简体中文"},
|
||||
{"ar_EG", "العربية"}};
|
||||
|
||||
std::map<std::string, std::map<std::string, std::string>> ThemeData::sPropertyAttributeMap
|
||||
// The data type is defined by the parent property.
|
||||
{
|
||||
|
@ -629,6 +648,7 @@ void ThemeData::loadFile(const std::map<std::string, std::string>& sysDataMap,
|
|||
}
|
||||
|
||||
sAspectRatioMatch = false;
|
||||
sThemeLanguage = "";
|
||||
|
||||
if (sCurrentTheme->second.capabilities.aspectRatios.size() > 0) {
|
||||
if (std::find(sCurrentTheme->second.capabilities.aspectRatios.cbegin(),
|
||||
|
@ -663,6 +683,35 @@ void ThemeData::loadFile(const std::map<std::string, std::string>& sysDataMap,
|
|||
}
|
||||
}
|
||||
|
||||
if (sCurrentTheme->second.capabilities.languages.size() > 0) {
|
||||
std::string langSetting {Settings::getInstance()->getString("ThemeLanguage")};
|
||||
if (langSetting == "automatic")
|
||||
langSetting = Utils::Localization::sCurrentLocale;
|
||||
|
||||
// Check if there is an exact match.
|
||||
if (std::find(sCurrentTheme->second.capabilities.languages.cbegin(),
|
||||
sCurrentTheme->second.capabilities.languages.cend(),
|
||||
langSetting) != sCurrentTheme->second.capabilities.languages.cend()) {
|
||||
sThemeLanguage = langSetting;
|
||||
}
|
||||
else {
|
||||
// We assume all locales are in the correct format.
|
||||
const std::string currLanguage {langSetting.substr(0, 2)};
|
||||
// Select the closest matching locale (i.e. same language but possibly for a
|
||||
// different country).
|
||||
for (const auto& lang : sCurrentTheme->second.capabilities.languages) {
|
||||
if (lang.substr(0, 2) == currLanguage) {
|
||||
sThemeLanguage = lang;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If there is no match then fall back to the default language en_US, which is
|
||||
// mandatory for all themes that provide language support.
|
||||
if (sThemeLanguage == "")
|
||||
sThemeLanguage = "en_US";
|
||||
}
|
||||
}
|
||||
|
||||
parseVariables(root);
|
||||
parseColorSchemes(root);
|
||||
parseFontSizes(root);
|
||||
|
@ -795,8 +844,11 @@ void ThemeData::populateThemes()
|
|||
LOG(LogInfo) << "Added theme \"" << *it << "\"" << themeName;
|
||||
#endif
|
||||
int aspectRatios {0};
|
||||
int languages {0};
|
||||
if (capabilities.aspectRatios.size() > 0)
|
||||
aspectRatios = static_cast<int>(capabilities.aspectRatios.size()) - 1;
|
||||
if (capabilities.languages.size() > 0)
|
||||
languages = static_cast<int>(capabilities.languages.size()) - 1;
|
||||
LOG(LogDebug) << "Theme includes support for " << capabilities.variants.size()
|
||||
<< " variant" << (capabilities.variants.size() != 1 ? "s" : "")
|
||||
<< ", " << capabilities.colorSchemes.size() << " color scheme"
|
||||
|
@ -804,6 +856,7 @@ void ThemeData::populateThemes()
|
|||
<< capabilities.fontSizes.size() << " font size"
|
||||
<< (capabilities.fontSizes.size() != 1 ? "s" : "") << ", "
|
||||
<< aspectRatios << " aspect ratio" << (aspectRatios != 1 ? "s" : "")
|
||||
<< ", " << languages << " language" << (languages != 1 ? "s" : "")
|
||||
<< " and " << capabilities.transitions.size() << " transition"
|
||||
<< (capabilities.transitions.size() != 1 ? "s" : "");
|
||||
|
||||
|
@ -878,6 +931,18 @@ const std::string ThemeData::getAspectRatioLabel(const std::string& aspectRatio)
|
|||
return "invalid ratio";
|
||||
}
|
||||
|
||||
const std::string ThemeData::getLanguageLabel(const std::string& language)
|
||||
{
|
||||
auto it = std::find_if(sSupportedLanguages.cbegin(), sSupportedLanguages.cend(),
|
||||
[&language](const std::pair<std::string, std::string>& entry) {
|
||||
return entry.first == language;
|
||||
});
|
||||
if (it != sSupportedLanguages.cend())
|
||||
return it->second;
|
||||
else
|
||||
return "invalid language";
|
||||
}
|
||||
|
||||
void ThemeData::setThemeTransitions()
|
||||
{
|
||||
auto setTransitionsFunc = [](int transitionAnim) {
|
||||
|
@ -971,6 +1036,7 @@ ThemeData::getCurrentThemeSelectedVariantOverrides()
|
|||
const void ThemeData::themeLoadedLogOutput()
|
||||
{
|
||||
LOG(LogInfo) << "Finished loading theme \"" << sCurrentTheme->first << "\"";
|
||||
|
||||
if (sSelectedAspectRatio != "") {
|
||||
const bool autoDetect {Settings::getInstance()->getString("ThemeAspectRatio") ==
|
||||
"automatic"};
|
||||
|
@ -980,6 +1046,13 @@ const void ThemeData::themeLoadedLogOutput()
|
|||
<< "set to " << (autoDetect ? match : "") << "\""
|
||||
<< Utils::String::replace(sSelectedAspectRatio, "_", " ") << "\"";
|
||||
}
|
||||
|
||||
if (sThemeLanguage != "") {
|
||||
LOG(LogInfo) << "Theme language set to \"" << sThemeLanguage << "\"";
|
||||
}
|
||||
else {
|
||||
LOG(LogInfo) << "Theme does not have multilingual support";
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int ThemeData::getHexColor(const std::string& str)
|
||||
|
@ -1028,6 +1101,7 @@ ThemeData::ThemeCapability ThemeData::parseThemeCapabilities(const std::string&
|
|||
ThemeCapability capabilities;
|
||||
std::vector<std::string> aspectRatiosTemp;
|
||||
std::vector<std::string> fontSizesTemp;
|
||||
std::vector<std::string> languagesTemp;
|
||||
bool hasTriggers {false};
|
||||
|
||||
const std::string capFile {path + "/capabilities.xml"};
|
||||
|
@ -1305,6 +1379,36 @@ ThemeData::ThemeCapability ThemeData::parseThemeCapabilities(const std::string&
|
|||
}
|
||||
}
|
||||
|
||||
for (pugi::xml_node language {themeCapabilities.child("language")}; language;
|
||||
language = language.next_sibling("language")) {
|
||||
const std::string& value {language.text().get()};
|
||||
if (std::find_if(sSupportedLanguages.cbegin(), sSupportedLanguages.cend(),
|
||||
[&value](const std::pair<std::string, std::string>& entry) {
|
||||
return entry.first == value;
|
||||
}) == sSupportedLanguages.cend()) {
|
||||
LOG(LogWarning) << "Declared language \"" << value
|
||||
<< "\" is not supported, ignoring entry in \"" << capFile << "\"";
|
||||
}
|
||||
else {
|
||||
if (std::find(languagesTemp.cbegin(), languagesTemp.cend(), value) !=
|
||||
languagesTemp.cend()) {
|
||||
LOG(LogWarning)
|
||||
<< "Language \"" << value
|
||||
<< "\" is declared multiple times, ignoring entry in \"" << capFile << "\"";
|
||||
}
|
||||
else {
|
||||
languagesTemp.emplace_back(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (languagesTemp.size() > 0 && std::find(languagesTemp.cbegin(), languagesTemp.cend(),
|
||||
"en_US") == languagesTemp.cend()) {
|
||||
LOG(LogError) << "Theme has declared language support but is missing mandatory "
|
||||
<< "\"en_US\" entry in \"" << capFile << "\"";
|
||||
languagesTemp.clear();
|
||||
}
|
||||
|
||||
for (pugi::xml_node transitions {themeCapabilities.child("transitions")}; transitions;
|
||||
transitions = transitions.next_sibling("transitions")) {
|
||||
std::map<ViewTransition, ViewTransitionAnimation> readTransitions;
|
||||
|
@ -1482,6 +1586,20 @@ ThemeData::ThemeCapability ThemeData::parseThemeCapabilities(const std::string&
|
|||
}
|
||||
}
|
||||
|
||||
// Add the languages in the order they are defined in sSupportedLanguages so they always
|
||||
// show up in the same order in the UI Settings menu.
|
||||
if (!languagesTemp.empty()) {
|
||||
// Add the "automatic" language if there is at least one entry.
|
||||
if (!languagesTemp.empty())
|
||||
capabilities.languages.emplace_back(sSupportedLanguages.front().first);
|
||||
for (auto& language : sSupportedLanguages) {
|
||||
if (std::find(languagesTemp.cbegin(), languagesTemp.cend(), language.first) !=
|
||||
languagesTemp.cend()) {
|
||||
capabilities.languages.emplace_back(language.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the font sizes in the order they are defined in sSupportedFontSizes so they always
|
||||
// show up in the same order in the UI Settings menu.
|
||||
if (!fontSizesTemp.empty()) {
|
||||
|
|
|
@ -186,6 +186,7 @@ public:
|
|||
std::vector<ThemeColorScheme> colorSchemes;
|
||||
std::vector<std::string> fontSizes;
|
||||
std::vector<std::string> aspectRatios;
|
||||
std::vector<std::string> languages;
|
||||
std::vector<ThemeTransitions> transitions;
|
||||
std::vector<std::string> suppressedTransitionProfiles;
|
||||
bool validTheme;
|
||||
|
@ -225,6 +226,7 @@ public:
|
|||
const static std::string getSystemThemeFile(const std::string& system);
|
||||
const static std::string getFontSizeLabel(const std::string& fontSize);
|
||||
const static std::string getAspectRatioLabel(const std::string& aspectRatio);
|
||||
const static std::string getLanguageLabel(const std::string& language);
|
||||
static void setThemeTransitions();
|
||||
|
||||
const std::map<ThemeTriggers::TriggerType, std::pair<std::string, std::vector<std::string>>>
|
||||
|
@ -275,6 +277,7 @@ private:
|
|||
|
||||
static std::vector<std::pair<std::string, std::string>> sSupportedFontSizes;
|
||||
static std::vector<std::pair<std::string, std::string>> sSupportedAspectRatios;
|
||||
static std::vector<std::pair<std::string, std::string>> sSupportedLanguages;
|
||||
static std::map<std::string, float> sAspectRatioMap;
|
||||
|
||||
static std::map<std::string, std::map<std::string, std::string>> sPropertyAttributeMap;
|
||||
|
@ -295,6 +298,7 @@ private:
|
|||
std::string mSelectedFontSize;
|
||||
static inline std::string sSelectedAspectRatio;
|
||||
static inline bool sAspectRatioMatch {false};
|
||||
static inline std::string sThemeLanguage;
|
||||
bool mCustomCollection;
|
||||
};
|
||||
|
||||
|
|
|
@ -28,23 +28,26 @@ namespace Utils
|
|||
namespace Localization
|
||||
{
|
||||
// clang-format off
|
||||
// When adding a new locale, then make sure to also update ThemeData::sSupportedLanguages.
|
||||
const std::vector<std::pair<std::string, std::string>> sSupportedLocales {{{"en"}, {"US"}},
|
||||
{{"en"}, {"GB"}},
|
||||
{{"ar"}, {"EG"}},
|
||||
{{"de"}, {"DE"}},
|
||||
{{"el"}, {"GR"}},
|
||||
{{"de"}, {"DE"}},
|
||||
{{"es"}, {"ES"}},
|
||||
{{"fr"}, {"FR"}},
|
||||
{{"it"}, {"IT"}},
|
||||
{{"ja"}, {"JP"}},
|
||||
{{"nl"}, {"NL"}},
|
||||
{{"pl"}, {"PL"}},
|
||||
{{"pt"}, {"BR"}},
|
||||
{{"ro"}, {"RO"}},
|
||||
{{"ru"}, {"RU"}},
|
||||
{{"sv"}, {"SE"}},
|
||||
{{"zh"}, {"CN"}}};
|
||||
{{"ja"}, {"JP"}},
|
||||
{{"zh"}, {"CN"}},
|
||||
{{"ar"}, {"EG"}}};
|
||||
// clang-format on
|
||||
|
||||
std::string sCurrentLocale {"en_US"};
|
||||
float sMenuTitleScaleFactor {1.0f};
|
||||
|
||||
const char* pgettextBuiltin(const char* msgctxt, const char* msgid)
|
||||
|
@ -136,6 +139,7 @@ namespace Utils
|
|||
}
|
||||
|
||||
sMenuTitleScaleFactor = 1.0f;
|
||||
sCurrentLocale = "en_US";
|
||||
std::string languageSetting {Settings::getInstance()->getString("ApplicationLanguage")};
|
||||
std::vector<std::string> localeVector;
|
||||
std::pair<std::string, std::string> localePair;
|
||||
|
@ -163,12 +167,12 @@ namespace Utils
|
|||
if (std::find(sSupportedLocales.cbegin(), sSupportedLocales.cend(), localePair) !=
|
||||
sSupportedLocales.cend()) {
|
||||
locale = localePairCombined;
|
||||
LOG(LogInfo) << "Setting application locale to \"" << locale << "\"";
|
||||
LOG(LogInfo) << "Application language set to \"" << locale << "\"";
|
||||
}
|
||||
else {
|
||||
for (auto& localeEntry : sSupportedLocales) {
|
||||
if (localeEntry.first == localePair.first) {
|
||||
LOG(LogInfo) << "No support for locale \"" << localePairCombined
|
||||
LOG(LogInfo) << "No support for language \"" << localePairCombined
|
||||
<< "\", falling back to closest match \""
|
||||
<< localeEntry.first + "_" + localeEntry.second << "\"";
|
||||
locale = localeEntry.first + "_" + localeEntry.second;
|
||||
|
@ -178,16 +182,16 @@ namespace Utils
|
|||
}
|
||||
|
||||
if (locale == "") {
|
||||
LOG(LogInfo) << "No support for locale \"" << localePairCombined
|
||||
LOG(LogInfo) << "No support for language \"" << localePairCombined
|
||||
<< "\", falling back to default \"en_US\"";
|
||||
locale = "en_US";
|
||||
}
|
||||
|
||||
// Language-specific menu title scale factor.
|
||||
if (localePair.first == "de")
|
||||
sMenuTitleScaleFactor = 0.92f;
|
||||
else if (localePair.first == "el")
|
||||
if (localePair.first == "el")
|
||||
sMenuTitleScaleFactor = 0.94f;
|
||||
else if (localePair.first == "de")
|
||||
sMenuTitleScaleFactor = 0.92f;
|
||||
else if (localePair.first == "es")
|
||||
sMenuTitleScaleFactor = 0.90f;
|
||||
else if (localePair.first == "fr")
|
||||
|
@ -243,6 +247,7 @@ namespace Utils
|
|||
textdomain(locale.c_str());
|
||||
bindtextdomain(locale.c_str(), objectPath.c_str());
|
||||
bind_textdomain_codeset(locale.c_str(), "UTF-8");
|
||||
sCurrentLocale = locale;
|
||||
}
|
||||
|
||||
} // namespace Localization
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace Utils
|
|||
namespace Localization
|
||||
{
|
||||
extern const std::vector<std::pair<std::string, std::string>> sSupportedLocales;
|
||||
extern std::string sCurrentLocale;
|
||||
extern float sMenuTitleScaleFactor;
|
||||
|
||||
const char* pgettextBuiltin(const char* msgctxt, const char* msgid);
|
||||
|
|
Loading…
Reference in a new issue