diff --git a/Source/Core/Common/StringUtil.h b/Source/Core/Common/StringUtil.h index ec2e5c6337..ef84fb6dcd 100644 --- a/Source/Core/Common/StringUtil.h +++ b/Source/Core/Common/StringUtil.h @@ -51,10 +51,6 @@ std::string ThousandSeparate(I value, int spaces = 0) std::ostringstream stream; #endif -// std::locale("") seems to be broken on many platforms -#if defined _WIN32 || (defined __linux__ && !defined __clang__) - stream.imbue(std::locale("")); -#endif stream << std::setw(spaces) << value; #ifdef _WIN32 diff --git a/Source/Core/DolphinQt2/Translation.cpp b/Source/Core/DolphinQt2/Translation.cpp index e22b4dbb6b..191a721c3b 100644 --- a/Source/Core/DolphinQt2/Translation.cpp +++ b/Source/Core/DolphinQt2/Translation.cpp @@ -18,6 +18,7 @@ #include "Common/MsgHandler.h" #include "Common/StringUtil.h" #include "Core/ConfigManager.h" +#include "UICommon/UICommon.h" constexpr u32 MO_MAGIC_NUMBER = 0x950412de; @@ -270,6 +271,10 @@ static bool TryInstallTranslator(const QString& exact_language_code) if (translator->load(filename)) { QApplication::instance()->installTranslator(translator); + + QLocale::setDefault(QLocale(exact_language_code)); + UICommon::SetLocale(exact_language_code.toStdString()); + return true; } translator->deleteLater(); diff --git a/Source/Core/DolphinWX/Main.cpp b/Source/Core/DolphinWX/Main.cpp index ff6b3810cb..ab301df0be 100644 --- a/Source/Core/DolphinWX/Main.cpp +++ b/Source/Core/DolphinWX/Main.cpp @@ -343,6 +343,9 @@ void DolphinApp::InitLanguageSupport() _("Error")); m_locale.reset(new wxLocale(wxLANGUAGE_DEFAULT)); } + + // wxWidgets sets the C locale for us, but not the C++ locale, so let's do that ourselves + UICommon::SetLocale(language_code); } void DolphinApp::OnEndSession(wxCloseEvent& event) diff --git a/Source/Core/UICommon/UICommon.cpp b/Source/Core/UICommon/UICommon.cpp index 423fe2d0ea..f761313add 100644 --- a/Source/Core/UICommon/UICommon.cpp +++ b/Source/Core/UICommon/UICommon.cpp @@ -2,8 +2,13 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include +#include #include +#include +#include #include +#include #ifdef _WIN32 #include // for SHGetFolderPath #endif @@ -66,6 +71,63 @@ void Shutdown() Config::Shutdown(); } +void SetLocale(std::string locale_name) +{ + auto set_locale = [](const std::string& locale) { +#ifdef __linux__ + std::string adjusted_locale = locale; + if (!locale.empty()) + adjusted_locale += ".UTF-8"; +#else + const std::string& adjusted_locale = locale; +#endif + + // setlocale sets the C locale, and global sets the C and C++ locales, so the call to setlocale + // would be redundant if it wasn't for not having any other good way to check whether + // the locale name is valid. (Constructing a std::locale object for an unsupported + // locale name throws std::runtime_error, and exception handling is disabled in Dolphin.) + if (!std::setlocale(LC_ALL, adjusted_locale.c_str())) + return false; + std::locale::global(std::locale(adjusted_locale)); + return true; + }; + +#ifdef _WIN32 + constexpr char PREFERRED_SEPARATOR = '-'; + constexpr char OTHER_SEPARATOR = '_'; +#else + constexpr char PREFERRED_SEPARATOR = '_'; + constexpr char OTHER_SEPARATOR = '-'; +#endif + + // Users who use a system language other than English are unlikely to prefer American date and + // time formats, so let's explicitly request "en_GB" if Dolphin's language is set to "en". + // (The settings window only allows setting "en", not anything like "en_US" or "en_GB".) + // Users who prefer the American formats are likely to have their system language set to en_US, + // and are thus likely to leave Dolphin's language as the default value "" (). + if (locale_name == "en") + locale_name = "en_GB"; + + std::replace(locale_name.begin(), locale_name.end(), OTHER_SEPARATOR, PREFERRED_SEPARATOR); + + // Use the specified locale if supported. + if (set_locale(locale_name)) + return; + + // Remove subcodes until we get a supported locale. If that doesn't give us a supported locale, + // "" is passed to set_locale in order to get the system default locale. + while (!locale_name.empty()) + { + const size_t separator_index = locale_name.rfind(PREFERRED_SEPARATOR); + locale_name.erase(separator_index == std::string::npos ? 0 : separator_index); + if (set_locale(locale_name)) + return; + } + + // If none of the locales tried above are supported, we just keep using whatever locale is set + // (which is the classic locale by default). +} + void CreateDirectories() { // Copy initial Wii NAND data from Sys to User. @@ -346,7 +408,10 @@ std::string FormatSize(u64 bytes) // Don't need exact values, only 5 most significant digits const double unit_size = std::pow(2, unit * 10); - return StringFromFormat("%.2f %s", bytes / unit_size, GetStringT(unit_symbols[unit]).c_str()); + std::stringstream ss; + ss << std::fixed << std::setprecision(2); + ss << bytes / unit_size << ' ' << GetStringT(unit_symbols[unit]); + return ss.str(); } } // namespace UICommon diff --git a/Source/Core/UICommon/UICommon.h b/Source/Core/UICommon/UICommon.h index 8bbd864ec0..b32e0f410b 100644 --- a/Source/Core/UICommon/UICommon.h +++ b/Source/Core/UICommon/UICommon.h @@ -19,6 +19,10 @@ void EnableScreenSaver(Display* display, Window win, bool enable); void EnableScreenSaver(bool enable); #endif +// Calls std::locale::global, selecting a fallback locale if the +// requested locale isn't available +void SetLocale(std::string locale_name); + void CreateDirectories(); void SetUserDirectory(const std::string& custom_path);