Added support for falling back to the closest matching locale if there is not an exact match

This commit is contained in:
Leon Styhre 2024-07-02 22:05:40 +02:00
parent 74691c8951
commit 2d3a559cee
3 changed files with 65 additions and 25 deletions

View file

@ -733,7 +733,7 @@ int main(int argc, char* argv[])
return 0; return 0;
} }
Utils::Localization::setLanguage(Utils::Localization::getLocale()); Utils::Localization::setLocale(Utils::Localization::getLocale());
Scripting::fireEvent("startup"); Scripting::fireEvent("startup");
#if defined(__EMSCRIPTEN__) #if defined(__EMSCRIPTEN__)

View file

@ -26,54 +26,93 @@ namespace Utils
{ {
namespace Localization namespace Localization
{ {
std::string getLocale() std::pair<std::string, std::string> getLocale()
{ {
#if defined(_WIN64) #if defined(_WIN64)
std::wstring localeName(LOCALE_NAME_MAX_LENGTH, '\0'); std::wstring localeNameWide(LOCALE_NAME_MAX_LENGTH, '\0');
if (GetUserDefaultLocaleName(&localeName[0], LOCALE_NAME_MAX_LENGTH) == 0) if (GetUserDefaultLocaleName(&localeNameWide[0], LOCALE_NAME_MAX_LENGTH) == 0)
return "en_US"; return std::make_pair("en", "US");
// Of course Windows doesn't follow standards and names locales with dashes instead std::string localeName {Utils::String::wideStringToString(localeNameWide)};
// of underscores, such as "sv-SE" instead of "sv_SE". localeName.erase(localeName.find('\0'));
std::string locale {
Utils::String::replace(Utils::String::wideStringToString(localeName), "-", "_")};
locale.erase(locale.find('\0'));
return locale; std::vector<std::string> localeVector;
// Of course Windows doesn't follow standards and names locales with dashes
// instead of underscores, such as "sv-SE" instead of "sv_SE". But who knows
// if this is consistent, so we check for underscores as an extra precaution.
if (localeName.find("_") != std::string::npos)
localeVector = Utils::String::delimitedStringToVector(localeName, "_");
else
localeVector = Utils::String::delimitedStringToVector(localeName, "-");
if (localeVector.size() == 1)
return std::make_pair(localeVector[0], "");
else
return std::make_pair(localeVector[0], localeVector[1]);
#else #else
// SDL_GetPreferredLocales() does not seem to always return accurate results // SDL_GetPreferredLocales() does not seem to always return accurate results
// on Windows but for all other operating systems we use it. // on Windows but for all other operating systems we use it.
SDL_Locale* preferredLocales {SDL_GetPreferredLocales()}; SDL_Locale* preferredLocales {SDL_GetPreferredLocales()};
if (preferredLocales == nullptr) if (preferredLocales == nullptr)
return "en_US"; return std::make_pair("en", "US");
std::string primaryLocale {preferredLocales->language}; std::string language {preferredLocales->language};
std::string country;
if (preferredLocales->country != nullptr) if (preferredLocales->country != nullptr)
primaryLocale.append("_").append(preferredLocales->country); country = preferredLocales->country;
SDL_free(preferredLocales); SDL_free(preferredLocales);
return primaryLocale; return std::make_pair(language, country);
#endif #endif
} }
void setLanguage(const std::string& locale) void setLocale(const std::pair<std::string, std::string>& localePair)
{ {
if (std::find(sSupportedLanguages.cbegin(), sSupportedLanguages.cend(), locale) == std::string locale;
sSupportedLanguages.cend()) { std::string localePairCombined;
LOG(LogInfo) << "No support for language \"" << locale
<< "\", reverting to default language \"en_US\""; if (localePair.second == "")
return; localePairCombined = localePair.first;
else
localePairCombined = localePair.first + "_" + localePair.second;
if (std::find(sSupportedLocales.cbegin(), sSupportedLocales.cend(), localePair) !=
sSupportedLocales.cend()) {
locale = localePairCombined;
LOG(LogInfo) << "Setting application locale to \"" << locale << "\"";
} }
else { else {
LOG(LogInfo) << "Setting application language to \"" << locale << "\""; for (auto& localeEntry : sSupportedLocales) {
if (localeEntry.first == localePair.first) {
LOG(LogInfo) << "No support for locale \"" << localePairCombined
<< "\", falling back to closest match \""
<< localeEntry.first + "_" + localeEntry.second << "\"";
locale = localeEntry.first + "_" + localeEntry.second;
break;
}
}
}
if (locale == "") {
LOG(LogInfo) << "No support for locale \"" << localePairCombined
<< "\", falling back to default \"en_US\"";
locale = "en_US";
} }
// No need to perform translations if we're using the default language. // No need to perform translations if we're using the default language.
if (locale == "en_US") { if (locale == "en_US") {
#if defined(_WIN64)
_configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
const LCID localeID {LocaleNameToLCID(
Utils::String::stringToWideString(locale).c_str(), LOCALE_ALLOW_NEUTRAL_NAMES)};
SetThreadLocale(localeID);
#else
setenv("LANGUAGE", locale.c_str(), 1); setenv("LANGUAGE", locale.c_str(), 1);
setenv("LANG", locale.c_str(), 1); setenv("LANG", locale.c_str(), 1);
setlocale(LC_MESSAGES, std::string {locale + ".UTF-8"}.c_str()); setlocale(LC_MESSAGES, std::string {locale + ".UTF-8"}.c_str());
#endif
textdomain(locale.c_str()); textdomain(locale.c_str());
return; return;
} }

View file

@ -20,10 +20,11 @@ namespace Utils
{ {
namespace Localization namespace Localization
{ {
static inline std::vector<std::string> sSupportedLanguages {"en_US", "sv_SE"}; static inline std::vector<std::pair<std::string, std::string>> sSupportedLocales {
{{"en"}, {"US"}}, {{"sv"}, {"SE"}}};
std::string getLocale(); std::pair<std::string, std::string> getLocale();
void setLanguage(const std::string& locale); void setLocale(const std::pair<std::string, std::string>& localePair);
} // namespace Localization } // namespace Localization