From 2e2682116f9adbb3ec777116334ea9931b57e998 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 14 Jul 2017 18:18:22 +0200 Subject: [PATCH 01/11] Try to return valid name from MSW wxLanguageInfo::GetLocaleName() Check if setlocale() really works with the name of the returned locale and return empty string if it doesn't. This fixes problem with setting locale when using old MSVC or MinGW CRT under new (Vista+) MSW versions, as the CRT doesn't support the locale names returned by LOCALE_SNAME and we need to fall back on LOCALE_SENGLANGUAGE. --- src/common/intl.cpp | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/common/intl.cpp b/src/common/intl.cpp index 5d816b284d..e10a29c269 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -111,6 +111,18 @@ inline wxString ExtractNotLang(const wxString& langFull) #endif // __UNIX__ +// Test if setting the given locale works without actually changing it. +bool CanSetLocale(const wxString& locale) +{ + const char* const orig = wxSetlocale(LC_ALL, NULL); + if ( !wxSetlocale(LC_ALL, locale) ) + return false; + + wxSetlocale(LC_ALL, orig); + + return true; +} + } // anonymous namespace // ---------------------------------------------------------------------------- @@ -152,22 +164,24 @@ wxString wxLanguageInfo::GetLocaleName() const wxChar buffer[256]; buffer[0] = wxT('\0'); - // wxLANGUAGE_NORWEGIAN_BOKMAL crashes mbstowcs, but using LOCALE_SNAME can fail - // for e.g. wxLANGUAGE_ENGLISH in VS 2010 (other versions?) -#if !defined(__VISUALC__) || (__VISUALC__ != 1600) + // Prefer to use the new (Vista and later) locale names instead of locale + // identifiers if supported, both at the OS level (LOCALE_SNAME) and by the + // CRT (check by calling setlocale()). if ( wxGetWinVersion() >= wxWinVersion_Vista ) { if ( ::GetLocaleInfo(lcid, LOCALE_SNAME, buffer, WXSIZEOF(buffer)) ) { - locale << buffer; + locale = buffer; } else { wxLogLastError(wxT("GetLocaleInfo(LOCALE_SNAME)")); } - return locale; + + if ( CanSetLocale(locale) ) + return locale; + //else: fall back to LOCALE_SENGLANGUAGE } -#endif if ( !::GetLocaleInfo(lcid, LOCALE_SENGLANGUAGE, buffer, WXSIZEOF(buffer)) ) { @@ -175,7 +189,7 @@ wxString wxLanguageInfo::GetLocaleName() const return locale; } - locale << buffer; + locale = buffer; if ( ::GetLocaleInfo(lcid, LOCALE_SENGCOUNTRY, buffer, WXSIZEOF(buffer)) > 0 ) { @@ -188,7 +202,7 @@ wxString wxLanguageInfo::GetLocaleName() const locale << wxT('.') << cp; } - return locale; + return CanSetLocale(locale) ? locale : wxString(); } #endif // __WINDOWS__ From 7836dfbc776cee01fd372691ba6a184098fbea10 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 14 Jul 2017 18:20:16 +0200 Subject: [PATCH 02/11] Make wxLanguageInfo::GetLocaleName() consistent across platforms Check that the locale can be indeed set to the given string in Unix version too, there doesn't seem to be any good reason to do it for MSW only. --- include/wx/intl.h | 10 ++-------- interface/wx/intl.h | 9 +++++++-- src/common/intl.cpp | 9 ++++++++- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/include/wx/intl.h b/include/wx/intl.h index c2020ddbfc..26c940fb1f 100644 --- a/include/wx/intl.h +++ b/include/wx/intl.h @@ -72,17 +72,11 @@ struct WXDLLIMPEXP_BASE wxLanguageInfo #endif // __WINDOWS__ // return the locale name corresponding to this language usable with - // setlocale() on the current system + // setlocale() on the current system or empty string if this locale is not + // supported wxString GetLocaleName() const; }; -// for Unix systems GetLocaleName() is trivial so implement it inline here, for -// MSW it's implemented in intl.cpp -#ifndef __WINDOWS__ -inline wxString wxLanguageInfo::GetLocaleName() const { return CanonicalName; } -#endif // !__WINDOWS__ - - // ---------------------------------------------------------------------------- // wxLocaleCategory: the category of locale settings // ---------------------------------------------------------------------------- diff --git a/interface/wx/intl.h b/interface/wx/intl.h index 70f21122b2..aa77038d23 100644 --- a/interface/wx/intl.h +++ b/interface/wx/intl.h @@ -55,8 +55,13 @@ struct wxLanguageInfo /// @onlyfor{wxmsw} wxUint32 GetLCID() const; - /// Return the locale name corresponding to this language usable with - /// @c setlocale() on the current system. + /** + Return the locale name corresponding to this language usable with + @c setlocale() on the current system. + + If setting locale for this language is not supported, the returned + string is empty. + */ wxString GetLocaleName() const; }; diff --git a/src/common/intl.cpp b/src/common/intl.cpp index e10a29c269..4c128e0014 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -205,7 +205,14 @@ wxString wxLanguageInfo::GetLocaleName() const return CanSetLocale(locale) ? locale : wxString(); } -#endif // __WINDOWS__ +#else // !__WINDOWS__ + +wxString wxLanguageInfo::GetLocaleName() const +{ + return CanSetLocale(CanonicalName) ? CanonicalName : wxString(); +} + +#endif // __WINDOWS__/!__WINDOWS__ // ---------------------------------------------------------------------------- // wxLocale From 9b387434a0dee2859f63e921fd25c83f471598d2 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 14 Jul 2017 18:53:29 +0200 Subject: [PATCH 03/11] Remove unnecessary checks for wxLANGUAGE_DEFAULT Simplify the code by not checking for language == wxLANGUAGE_DEFAULT in wxLocale::Init(): this can't happen because we use the system language when passed wxLANGUAGE_DEFAULT as parameter and return false immediately if we can't determine the system language. --- src/common/intl.cpp | 74 +++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/src/common/intl.cpp b/src/common/intl.cpp index 4c128e0014..448422f902 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -424,8 +424,7 @@ bool wxLocale::Init(int language, int flags) // Set the locale: #if defined(__UNIX__) && !defined(__WXMAC__) - if (language != wxLANGUAGE_DEFAULT) - locale = info->CanonicalName; + locale = info->CanonicalName; const char *retloc = wxSetlocaleTryUTF8(LC_ALL, locale); @@ -498,49 +497,41 @@ bool wxLocale::Init(int language, int flags) #elif defined(__WIN32__) const char *retloc = "C"; - if ( language != wxLANGUAGE_DEFAULT ) + if ( info->WinLang == 0 ) { - if ( info->WinLang == 0 ) + wxLogWarning(wxS("Locale '%s' not supported by OS."), name.c_str()); + // retloc already set to "C" + } + else // language supported by Windows + { + const wxUint32 lcid = info->GetLCID(); + + // change locale used by Windows functions + ::SetThreadLocale(lcid); + + // SetThreadUILanguage() is available on XP, but with unclear + // behavior, so avoid calling it there. + if ( wxGetWinVersion() >= wxWinVersion_Vista ) { - wxLogWarning(wxS("Locale '%s' not supported by OS."), name.c_str()); - // retloc already set to "C" + wxLoadedDLL dllKernel32(wxS("kernel32.dll")); + typedef LANGID(WINAPI *SetThreadUILanguage_t)(LANGID); + SetThreadUILanguage_t pfnSetThreadUILanguage = NULL; + wxDL_INIT_FUNC(pfn, SetThreadUILanguage, dllKernel32); + if (pfnSetThreadUILanguage) + pfnSetThreadUILanguage(LANGIDFROMLCID(lcid)); } - else // language supported by Windows + + // and also call setlocale() to change locale used by the CRT + locale = info->GetLocaleName(); + if ( locale.empty() ) { - const wxUint32 lcid = info->GetLCID(); - - // change locale used by Windows functions - ::SetThreadLocale(lcid); - - // SetThreadUILanguage() is available on XP, but with unclear - // behavior, so avoid calling it there. - if ( wxGetWinVersion() >= wxWinVersion_Vista ) - { - wxLoadedDLL dllKernel32(wxS("kernel32.dll")); - typedef LANGID(WINAPI *SetThreadUILanguage_t)(LANGID); - SetThreadUILanguage_t pfnSetThreadUILanguage = NULL; - wxDL_INIT_FUNC(pfn, SetThreadUILanguage, dllKernel32); - if (pfnSetThreadUILanguage) - pfnSetThreadUILanguage(LANGIDFROMLCID(lcid)); - } - - // and also call setlocale() to change locale used by the CRT - locale = info->GetLocaleName(); - if ( locale.empty() ) - { - ret = false; - } - else // have a valid locale - { - retloc = wxSetlocale(LC_ALL, locale); - } + ret = false; + } + else // have a valid locale + { + retloc = wxSetlocale(LC_ALL, locale); } } - else // language == wxLANGUAGE_DEFAULT - { - retloc = wxSetlocale(LC_ALL, wxEmptyString); - } - #if wxUSE_UNICODE && (defined(__VISUALC__) || defined(__MINGW32__)) // VC++ setlocale() (also used by Mingw) can't set locale to languages that // can only be written using Unicode, therefore wxSetlocale() call fails @@ -560,10 +551,7 @@ bool wxLocale::Init(int language, int flags) if ( !retloc ) ret = false; #elif defined(__WXMAC__) - if (lang == wxLANGUAGE_DEFAULT) - locale.clear(); - else - locale = info->CanonicalName; + locale = info->CanonicalName; const char *retloc = wxSetlocale(LC_ALL, locale); From 2d170d1bb6068862882fd9d63cf2946efe736219 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 14 Jul 2017 18:56:15 +0200 Subject: [PATCH 04/11] Get rid of common "locale" variable in wxLocale::Init() Try to make code less confusing by avoiding defining a variable in platform-independent part of the code and then actually using it only in platform-specific code. --- src/common/intl.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/common/intl.cpp b/src/common/intl.cpp index 448422f902..45c8255f43 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -420,11 +420,10 @@ bool wxLocale::Init(int language, int flags) wxString name = info->Description; wxString canonical = info->CanonicalName; - wxString locale; // Set the locale: #if defined(__UNIX__) && !defined(__WXMAC__) - locale = info->CanonicalName; + const wxString& locale = info->CanonicalName; const char *retloc = wxSetlocaleTryUTF8(LC_ALL, locale); @@ -522,7 +521,7 @@ bool wxLocale::Init(int language, int flags) } // and also call setlocale() to change locale used by the CRT - locale = info->GetLocaleName(); + const wxString locale = info->GetLocaleName(); if ( locale.empty() ) { ret = false; @@ -551,7 +550,7 @@ bool wxLocale::Init(int language, int flags) if ( !retloc ) ret = false; #elif defined(__WXMAC__) - locale = info->CanonicalName; + const wxString& locale = info->CanonicalName; const char *retloc = wxSetlocale(LC_ALL, locale); From ca3d86155c4d29c4eb31ffd05c7db0ddfb05b6f9 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 14 Jul 2017 18:58:21 +0200 Subject: [PATCH 05/11] Get rid of another unused variable in wxLocale::Init() This one was defined and used only once, just remove it completely. --- src/common/intl.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/common/intl.cpp b/src/common/intl.cpp index 45c8255f43..ee835c9d5c 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -419,7 +419,6 @@ bool wxLocale::Init(int language, int flags) } wxString name = info->Description; - wxString canonical = info->CanonicalName; // Set the locale: #if defined(__UNIX__) && !defined(__WXMAC__) @@ -579,7 +578,7 @@ bool wxLocale::Init(int language, int flags) // this language } - if ( !DoInit(name, canonical, retloc) ) + if ( !DoInit(name, info->CanonicalName, retloc) ) { ret = false; } From 9104e6fc9a320c70f543878905bf32cc0cd27e3f Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 14 Jul 2017 18:59:08 +0200 Subject: [PATCH 06/11] Avoid unnecessary copies in wxLocale code Micro-optimization: don't copy the strings unnecessarily when using just a const reference is sufficient. --- src/common/intl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/intl.cpp b/src/common/intl.cpp index ee835c9d5c..6da4e18a77 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -418,7 +418,7 @@ bool wxLocale::Init(int language, int flags) return false; } - wxString name = info->Description; + const wxString& name = info->Description; // Set the locale: #if defined(__UNIX__) && !defined(__WXMAC__) From 1003cf3e9269078aa2b7ec0f3d08d6397b5ec262 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 14 Jul 2017 19:00:12 +0200 Subject: [PATCH 07/11] Remove unnecessary c_str() calls in wxLocale code Pass wxString directly to wxLogWarning(), there is no need for c_str() since wx 3.0 any more. --- src/common/intl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/intl.cpp b/src/common/intl.cpp index 6da4e18a77..c819090e1f 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -497,7 +497,7 @@ bool wxLocale::Init(int language, int flags) const char *retloc = "C"; if ( info->WinLang == 0 ) { - wxLogWarning(wxS("Locale '%s' not supported by OS."), name.c_str()); + wxLogWarning(wxS("Locale '%s' not supported by OS."), name); // retloc already set to "C" } else // language supported by Windows @@ -567,7 +567,7 @@ bool wxLocale::Init(int language, int flags) #ifndef WX_NO_LOCALE_SUPPORT if ( !ret ) { - wxLogWarning(_("Cannot set locale to language \"%s\"."), name.c_str()); + wxLogWarning(_("Cannot set locale to language \"%s\"."), name); // As we failed to change locale, there is no need to restore the // previous one: it's still valid. From 8713d7346683db459d15054322c36cf730c79757 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 14 Jul 2017 19:07:20 +0200 Subject: [PATCH 08/11] Avoid at least some unnecessary setlocale() calls Instead of calling wxLanguageInfo::GetLocaleName(), which called setlocale() at least thrice (first to query the current locale, second to try to change it and third to restore the original locale) and then calling setlocale() again if it succeeded, use the new TrySetLocale() method which calls setlocale() just once and doesn't require calling it again in the caller. This makes the code slightly more efficient but, more importantly, shorter and more clear. --- include/wx/intl.h | 7 +++++++ src/common/intl.cpp | 50 ++++++++++++++++++++------------------------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/include/wx/intl.h b/include/wx/intl.h index 26c940fb1f..f1a9dafd9b 100644 --- a/include/wx/intl.h +++ b/include/wx/intl.h @@ -75,6 +75,13 @@ struct WXDLLIMPEXP_BASE wxLanguageInfo // setlocale() on the current system or empty string if this locale is not // supported wxString GetLocaleName() const; + + // Call setlocale() and return non-null value if it works for this language. + // + // This function is mostly for internal use, as changing locale involves + // more than just calling setlocale() on some platforms, use wxLocale to + // do everything that needs to be done instead of calling this method. + const char* TrySetLocale() const; }; // ---------------------------------------------------------------------------- diff --git a/src/common/intl.cpp b/src/common/intl.cpp index c819090e1f..a951a655c6 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -111,18 +111,6 @@ inline wxString ExtractNotLang(const wxString& langFull) #endif // __UNIX__ -// Test if setting the given locale works without actually changing it. -bool CanSetLocale(const wxString& locale) -{ - const char* const orig = wxSetlocale(LC_ALL, NULL); - if ( !wxSetlocale(LC_ALL, locale) ) - return false; - - wxSetlocale(LC_ALL, orig); - - return true; -} - } // anonymous namespace // ---------------------------------------------------------------------------- @@ -155,7 +143,7 @@ wxUint32 wxLanguageInfo::GetLCID() const return MAKELCID(MAKELANGID(WinLang, WinSublang), SORT_DEFAULT); } -wxString wxLanguageInfo::GetLocaleName() const +const char* wxLanguageInfo::TrySetLocale() const { wxString locale; @@ -178,15 +166,16 @@ wxString wxLanguageInfo::GetLocaleName() const wxLogLastError(wxT("GetLocaleInfo(LOCALE_SNAME)")); } - if ( CanSetLocale(locale) ) - return locale; + const char* const retloc = wxSetlocale(LC_ALL, locale); + if ( retloc ) + return retloc; //else: fall back to LOCALE_SENGLANGUAGE } if ( !::GetLocaleInfo(lcid, LOCALE_SENGLANGUAGE, buffer, WXSIZEOF(buffer)) ) { wxLogLastError(wxT("GetLocaleInfo(LOCALE_SENGLANGUAGE)")); - return locale; + return NULL; } locale = buffer; @@ -202,18 +191,31 @@ wxString wxLanguageInfo::GetLocaleName() const locale << wxT('.') << cp; } - return CanSetLocale(locale) ? locale : wxString(); + return wxSetlocale(LC_ALL, locale); } #else // !__WINDOWS__ -wxString wxLanguageInfo::GetLocaleName() const +const char* wxLanguageInfo::TrySetLocale() const { - return CanSetLocale(CanonicalName) ? CanonicalName : wxString(); + return wxSetlocale(LC_ALL, CanonicalName); } #endif // __WINDOWS__/!__WINDOWS__ +wxString wxLanguageInfo::GetLocaleName() const +{ + const char* const orig = wxSetlocale(LC_ALL, NULL); + + const char* const ret = TrySetLocale(); + if ( !ret ) + return wxString(); + + wxSetlocale(LC_ALL, orig); + + return ret; +} + // ---------------------------------------------------------------------------- // wxLocale // ---------------------------------------------------------------------------- @@ -520,15 +522,7 @@ bool wxLocale::Init(int language, int flags) } // and also call setlocale() to change locale used by the CRT - const wxString locale = info->GetLocaleName(); - if ( locale.empty() ) - { - ret = false; - } - else // have a valid locale - { - retloc = wxSetlocale(LC_ALL, locale); - } + retloc = info->TrySetLocale(); } #if wxUSE_UNICODE && (defined(__VISUALC__) || defined(__MINGW32__)) // VC++ setlocale() (also used by Mingw) can't set locale to languages that From 2deca99789cca1454b325c1e5c339f2adaa8a100 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 14 Jul 2017 19:28:33 +0200 Subject: [PATCH 09/11] Don't duplicate "ret" flag in wxLocale::Init() Simplify the code by just using "retloc != NULL" as success indicator until the call to DoInit() instead of keeping a separate "bool ret" variable in sync with "retloc". --- src/common/intl.cpp | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/common/intl.cpp b/src/common/intl.cpp index a951a655c6..9efc542bf2 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -396,8 +396,6 @@ bool wxLocale::Init(int language, int flags) wxS("wxLOCALE_CONV_ENCODING is no longer supported, add charset to your catalogs") ); #endif - bool ret = true; - int lang = language; if (lang == wxLANGUAGE_DEFAULT) { @@ -479,9 +477,6 @@ bool wxLocale::Init(int language, int flags) } } - if ( !retloc ) - ret = false; - #ifdef __AIX__ // at least in AIX 5.2 libc is buggy and the string returned from // setlocale(LC_ALL) can't be passed back to it because it returns 6 @@ -539,9 +534,6 @@ bool wxLocale::Init(int language, int flags) } } #endif // CRT not handling Unicode-only languages - - if ( !retloc ) - ret = false; #elif defined(__WXMAC__) const wxString& locale = info->CanonicalName; @@ -559,7 +551,7 @@ bool wxLocale::Init(int language, int flags) #endif #ifndef WX_NO_LOCALE_SUPPORT - if ( !ret ) + if ( !retloc ) { wxLogWarning(_("Cannot set locale to language \"%s\"."), name); @@ -572,10 +564,7 @@ bool wxLocale::Init(int language, int flags) // this language } - if ( !DoInit(name, info->CanonicalName, retloc) ) - { - ret = false; - } + const bool ret = DoInit(name, info->CanonicalName, retloc); if (IsOk()) // setlocale() succeeded m_language = lang; From 8cb4e70064717b23c5798a80482e33ef24aa1765 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 14 Jul 2017 19:54:45 +0200 Subject: [PATCH 10/11] Refactor wxLocale initialization code Don't call wxSetlocale() in DoInit() any more, this was redundant when it was called from Init(wxLanguage) overload. Add new DoCommonPostInit() called from both Init() overloads after setting the locale in whichever way they do it to avoid code duplication. As a side effect of this change, the error message given if the locale can't be set is the same now independently of the ctor/Init() overload used (previously it differed depending on whether a wxLanguage or the name of the language as string was passed) and it's always logged using wxLogWarning() and not it in one case and wxLogError() in the other one. --- include/wx/intl.h | 17 +++++- src/common/intl.cpp | 125 +++++++++++++++++++++----------------------- 2 files changed, 76 insertions(+), 66 deletions(-) diff --git a/include/wx/intl.h b/include/wx/intl.h index f1a9dafd9b..d66e3043d8 100644 --- a/include/wx/intl.h +++ b/include/wx/intl.h @@ -333,9 +333,11 @@ public: static void DestroyLanguagesDB(); private: - bool DoInit(const wxString& name, + // This method is trivial and just updates the corresponding member + // variables without doing anything else. + void DoInit(const wxString& name, const wxString& shortName, - const wxString& locale); + int language); // copy default table of languages from global static array to // m_langugagesInfo, called by InitLanguagesDB @@ -344,6 +346,17 @@ private: // initialize the member fields to default values void DoCommonInit(); + // After trying to set locale, call this method to give the appropriate + // error if it couldn't be set (success == false) and to load the + // translations for the given language, if necessary. + // + // The return value is the same as "success" parameter. + bool DoCommonPostInit(bool success, + const wxString& name, + const wxString& shortName, + bool bLoadDefault); + + wxString m_strLocale, // this locale name m_strShort; // short name for the locale int m_language; // this locale wxLanguage value diff --git a/src/common/intl.cpp b/src/common/intl.cpp index 9efc542bf2..4386df0bd4 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -288,33 +288,6 @@ bool wxLocale::Init(const wxString& name, wxS("wxLocale::Init with bConvertEncoding=false is no longer supported, add charset to your catalogs") ); #endif - bool ret = DoInit(name, shortName, locale); - - // NB: don't use 'lang' here, 'language' may be wxLANGUAGE_DEFAULT - wxTranslations *t = wxTranslations::Get(); - if ( t ) - { - t->SetLanguage(shortName); - - if ( bLoadDefault ) - t->AddStdCatalog(); - } - - return ret; -} - -bool wxLocale::DoInit(const wxString& name, - const wxString& shortName, - const wxString& locale) -{ - wxASSERT_MSG( !m_initialized, - wxS("you can't call wxLocale::Init more than once") ); - - m_initialized = true; - m_strLocale = name; - m_strShort = shortName; - m_language = wxLANGUAGE_UNKNOWN; - // change current locale (default: same as long name) wxString szLocale(locale); if ( szLocale.empty() ) @@ -326,27 +299,69 @@ bool wxLocale::DoInit(const wxString& name, wxS("no locale to set in wxLocale::Init()") ); } - if ( !wxSetlocale(LC_ALL, szLocale) ) - { - wxLogError(_("locale '%s' cannot be set."), szLocale); - } - // the short name will be used to look for catalog files as well, // so we need something here - if ( m_strShort.empty() ) { + wxString strShort(shortName); + if ( strShort.empty() ) { // FIXME I don't know how these 2 letter abbreviations are formed, // this wild guess is surely wrong if ( !szLocale.empty() ) { - m_strShort += (wxChar)wxTolower(szLocale[0]); + strShort += (wxChar)wxTolower(szLocale[0]); if ( szLocale.length() > 1 ) - m_strShort += (wxChar)wxTolower(szLocale[1]); + strShort += (wxChar)wxTolower(szLocale[1]); } } - return true; + DoInit(name, strShort, wxLANGUAGE_UNKNOWN); + + const bool ret = wxSetlocale(LC_ALL, szLocale) != NULL; + + return DoCommonPostInit(ret, szLocale, shortName, bLoadDefault); } +void wxLocale::DoInit(const wxString& name, + const wxString& shortName, + int language) +{ + wxASSERT_MSG( !m_initialized, + wxS("you can't call wxLocale::Init more than once") ); + + m_initialized = true; + m_strLocale = name; + m_strShort = shortName; + m_language = language; +} + +bool wxLocale::DoCommonPostInit(bool success, + const wxString& name, + const wxString& shortName, + bool bLoadDefault) +{ + if ( !success ) + { + wxLogWarning(_("Cannot set locale to language \"%s\"."), name); + + // As we failed to change locale, there is no need to restore the + // previous one: it's still valid. + free(const_cast(m_pszOldLocale)); + m_pszOldLocale = NULL; + + // continue nevertheless and try to load at least the translations for + // this language + } + + wxTranslations *t = wxTranslations::Get(); + if ( t ) + { + t->SetLanguage(shortName); + + if ( bLoadDefault ) + t->AddStdCatalog(); + } + + return success; +} #if defined(__UNIX__) && wxUSE_UNICODE && !defined(__WXMAC__) static const char *wxSetlocaleTryUTF8(int c, const wxString& lc) @@ -419,6 +434,7 @@ bool wxLocale::Init(int language, int flags) } const wxString& name = info->Description; + DoInit(name, info->CanonicalName, lang); // Set the locale: #if defined(__UNIX__) && !defined(__WXMAC__) @@ -551,35 +567,16 @@ bool wxLocale::Init(int language, int flags) #endif #ifndef WX_NO_LOCALE_SUPPORT - if ( !retloc ) - { - wxLogWarning(_("Cannot set locale to language \"%s\"."), name); - - // As we failed to change locale, there is no need to restore the - // previous one: it's still valid. - free(const_cast(m_pszOldLocale)); - m_pszOldLocale = NULL; - - // continue nevertheless and try to load at least the translations for - // this language - } - - const bool ret = DoInit(name, info->CanonicalName, retloc); - - if (IsOk()) // setlocale() succeeded - m_language = lang; - // NB: don't use 'lang' here, 'language' - wxTranslations *t = wxTranslations::Get(); - if ( t ) - { - t->SetLanguage(static_cast(language)); - - if ( flags & wxLOCALE_LOAD_DEFAULT ) - t->AddStdCatalog(); - } - - return ret; + return DoCommonPostInit + ( + retloc != NULL, + name, + language == wxLANGUAGE_DEFAULT + ? wxString() + : info->CanonicalName, + flags & wxLOCALE_LOAD_DEFAULT + ); #endif // !WX_NO_LOCALE_SUPPORT } From 019e9d041fba2544831ef653b8aeaee123ef22e6 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 14 Jul 2017 20:05:01 +0200 Subject: [PATCH 11/11] Fix setting locale under MSW when using locale name Previously, all MSW-specific stuff like calling SetThreadLocale() and SetThreadUILanguage() was only done when initializing wxLocale from a wxLanguage value, but not when using a string name for it. Fix this by implicitly calling Init(wxLanguage) from Init(wxString) if we can find the language corresponding to the given name, and if the other parameter is not incompatible with it. --- src/common/intl.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/common/intl.cpp b/src/common/intl.cpp index 4386df0bd4..eee34aae76 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -299,6 +299,23 @@ bool wxLocale::Init(const wxString& name, wxS("no locale to set in wxLocale::Init()") ); } + if ( const wxLanguageInfo* langInfo = FindLanguageInfo(szLocale) ) + { + // Prefer to use Init(wxLanguage) overload if possible as it will + // correctly set our m_language and also set the locale correctly under + // MSW, where just calling wxSetLocale() as we do below is not enough. + // + // However don't do it if the parameters are incompatible with this + // language, e.g. if we are called with something like ("French", "de") + // to use French locale but German translations: this seems unlikely to + // happen but, in principle, it could. + if ( langInfo->CanonicalName.StartsWith(shortName) ) + { + return Init(langInfo->Language, + bLoadDefault ? wxLOCALE_LOAD_DEFAULT : 0); + } + } + // the short name will be used to look for catalog files as well, // so we need something here wxString strShort(shortName);