ES-DE/es-core/src/utils/LocalizationUtil.cpp

159 lines
6 KiB
C++
Raw Normal View History

2024-06-29 19:24:28 +00:00
// SPDX-License-Identifier: MIT
//
// ES-DE Frontend
// LocalizationUtil.cpp
//
// Localization functions.
// Provides support for translations using gettext/libintl.
//
#include "utils/LocalizationUtil.h"
#include "Log.h"
#include "resources/ResourceManager.h"
#include "utils/StringUtil.h"
2024-06-30 21:10:04 +00:00
#include <SDL2/SDL_locale.h>
2024-06-29 19:24:28 +00:00
#include <algorithm>
#include <iostream>
2024-06-30 20:27:22 +00:00
#if defined(_WIN64)
#include <Windows.h>
#endif
2024-06-29 19:24:28 +00:00
namespace Utils
{
namespace Localization
{
std::pair<std::string, std::string> getLocale()
2024-06-29 19:24:28 +00:00
{
2024-06-30 20:27:22 +00:00
#if defined(_WIN64)
std::wstring localeNameWide(LOCALE_NAME_MAX_LENGTH, '\0');
if (GetUserDefaultLocaleName(&localeNameWide[0], LOCALE_NAME_MAX_LENGTH) == 0)
return std::make_pair("en", "US");
2024-06-30 20:27:22 +00:00
std::string localeName {Utils::String::wideStringToString(localeNameWide)};
localeName.erase(localeName.find('\0'));
2024-06-30 20:27:22 +00:00
// This should never happen, but who knows with Windows.
if (localeName.empty())
return std::make_pair("en", "US");
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
// SDL_GetPreferredLocales() does not seem to always return accurate results
// on Windows but for all other operating systems we use it.
2024-06-30 21:10:04 +00:00
SDL_Locale* preferredLocales {SDL_GetPreferredLocales()};
if (preferredLocales == nullptr)
return std::make_pair("en", "US");
2024-06-30 21:10:04 +00:00
std::string language {preferredLocales->language};
std::string country;
2024-06-30 21:10:04 +00:00
if (preferredLocales->country != nullptr)
country = preferredLocales->country;
2024-06-30 21:10:04 +00:00
SDL_free(preferredLocales);
return std::make_pair(language, country);
2024-06-30 20:27:22 +00:00
#endif
2024-06-29 19:24:28 +00:00
}
void setLocale(const std::pair<std::string, std::string>& localePair)
2024-06-29 19:24:28 +00:00
{
std::string locale;
std::string localePairCombined;
if (localePair.second == "")
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 << "\"";
2024-06-29 19:24:28 +00:00
}
else {
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";
2024-06-29 19:24:28 +00:00
}
// No need to perform translations if we're using the default language.
2024-07-02 16:04:42 +00:00
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
2024-07-02 16:04:42 +00:00
setenv("LANGUAGE", locale.c_str(), 1);
setenv("LANG", locale.c_str(), 1);
setlocale(LC_MESSAGES, std::string {locale + ".UTF-8"}.c_str());
#endif
2024-07-02 16:04:42 +00:00
textdomain(locale.c_str());
2024-06-29 19:24:28 +00:00
return;
2024-07-02 16:04:42 +00:00
}
2024-06-29 19:24:28 +00:00
std::string localePath;
localePath.append("/")
.append(locale)
.append("/LC_MESSAGES/")
.append(locale)
.append(".mo");
// If the message catalog file is not found then an emergency shutdown will be
// initiated by ResourceManager.
std::string objectPath {
ResourceManager::getInstance().getResourcePath(":/locale" + localePath)};
// This makes it possible to override the message catalog with a file in the
// application data directory.
if (objectPath.length() > localePath.length())
objectPath = objectPath.substr(0, objectPath.length() - localePath.length());
2024-06-30 20:27:22 +00:00
#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);
2024-07-02 16:04:42 +00:00
setenv("LANG", locale.c_str(), 1);
2024-06-30 20:27:22 +00:00
setlocale(LC_MESSAGES, std::string {locale + ".UTF-8"}.c_str());
#endif
2024-06-29 19:24:28 +00:00
textdomain(locale.c_str());
bindtextdomain(locale.c_str(), objectPath.c_str());
bind_textdomain_codeset(locale.c_str(), "UTF-8");
}
} // namespace Localization
} // namespace Utils