Merge branch 'msw-locale'

Fixes for setting locale under MSW when using older compilers and minor
simplifications and optimizations in wxLocale code.

See https://github.com/wxWidgets/wxWidgets/pull/517
This commit is contained in:
Vadim Zeitlin
2017-07-16 15:03:48 +02:00
3 changed files with 166 additions and 143 deletions

View File

@@ -72,17 +72,18 @@ struct WXDLLIMPEXP_BASE wxLanguageInfo
#endif // __WINDOWS__ #endif // __WINDOWS__
// return the locale name corresponding to this language usable with // 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; 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;
}; };
// 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 // wxLocaleCategory: the category of locale settings
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@@ -332,9 +333,11 @@ public:
static void DestroyLanguagesDB(); static void DestroyLanguagesDB();
private: 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& shortName,
const wxString& locale); int language);
// copy default table of languages from global static array to // copy default table of languages from global static array to
// m_langugagesInfo, called by InitLanguagesDB // m_langugagesInfo, called by InitLanguagesDB
@@ -343,6 +346,17 @@ private:
// initialize the member fields to default values // initialize the member fields to default values
void DoCommonInit(); 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 wxString m_strLocale, // this locale name
m_strShort; // short name for the locale m_strShort; // short name for the locale
int m_language; // this locale wxLanguage value int m_language; // this locale wxLanguage value

View File

@@ -55,8 +55,13 @@ struct wxLanguageInfo
/// @onlyfor{wxmsw} /// @onlyfor{wxmsw}
wxUint32 GetLCID() const; 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; wxString GetLocaleName() const;
}; };

View File

@@ -143,7 +143,7 @@ wxUint32 wxLanguageInfo::GetLCID() const
return MAKELCID(MAKELANGID(WinLang, WinSublang), SORT_DEFAULT); return MAKELCID(MAKELANGID(WinLang, WinSublang), SORT_DEFAULT);
} }
wxString wxLanguageInfo::GetLocaleName() const const char* wxLanguageInfo::TrySetLocale() const
{ {
wxString locale; wxString locale;
@@ -152,30 +152,33 @@ wxString wxLanguageInfo::GetLocaleName() const
wxChar buffer[256]; wxChar buffer[256];
buffer[0] = wxT('\0'); buffer[0] = wxT('\0');
// wxLANGUAGE_NORWEGIAN_BOKMAL crashes mbstowcs, but using LOCALE_SNAME can fail // Prefer to use the new (Vista and later) locale names instead of locale
// for e.g. wxLANGUAGE_ENGLISH in VS 2010 (other versions?) // identifiers if supported, both at the OS level (LOCALE_SNAME) and by the
#if !defined(__VISUALC__) || (__VISUALC__ != 1600) // CRT (check by calling setlocale()).
if ( wxGetWinVersion() >= wxWinVersion_Vista ) if ( wxGetWinVersion() >= wxWinVersion_Vista )
{ {
if ( ::GetLocaleInfo(lcid, LOCALE_SNAME, buffer, WXSIZEOF(buffer)) ) if ( ::GetLocaleInfo(lcid, LOCALE_SNAME, buffer, WXSIZEOF(buffer)) )
{ {
locale << buffer; locale = buffer;
} }
else else
{ {
wxLogLastError(wxT("GetLocaleInfo(LOCALE_SNAME)")); wxLogLastError(wxT("GetLocaleInfo(LOCALE_SNAME)"));
} }
return locale;
const char* const retloc = wxSetlocale(LC_ALL, locale);
if ( retloc )
return retloc;
//else: fall back to LOCALE_SENGLANGUAGE
} }
#endif
if ( !::GetLocaleInfo(lcid, LOCALE_SENGLANGUAGE, buffer, WXSIZEOF(buffer)) ) if ( !::GetLocaleInfo(lcid, LOCALE_SENGLANGUAGE, buffer, WXSIZEOF(buffer)) )
{ {
wxLogLastError(wxT("GetLocaleInfo(LOCALE_SENGLANGUAGE)")); wxLogLastError(wxT("GetLocaleInfo(LOCALE_SENGLANGUAGE)"));
return locale; return NULL;
} }
locale << buffer; locale = buffer;
if ( ::GetLocaleInfo(lcid, LOCALE_SENGCOUNTRY, if ( ::GetLocaleInfo(lcid, LOCALE_SENGCOUNTRY,
buffer, WXSIZEOF(buffer)) > 0 ) buffer, WXSIZEOF(buffer)) > 0 )
{ {
@@ -188,10 +191,30 @@ wxString wxLanguageInfo::GetLocaleName() const
locale << wxT('.') << cp; locale << wxT('.') << cp;
} }
return locale; return wxSetlocale(LC_ALL, locale);
} }
#endif // __WINDOWS__ #else // !__WINDOWS__
const char* wxLanguageInfo::TrySetLocale() const
{
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 // wxLocale
@@ -265,33 +288,6 @@ bool wxLocale::Init(const wxString& name,
wxS("wxLocale::Init with bConvertEncoding=false is no longer supported, add charset to your catalogs") ); wxS("wxLocale::Init with bConvertEncoding=false is no longer supported, add charset to your catalogs") );
#endif #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) // change current locale (default: same as long name)
wxString szLocale(locale); wxString szLocale(locale);
if ( szLocale.empty() ) if ( szLocale.empty() )
@@ -303,27 +299,86 @@ bool wxLocale::DoInit(const wxString& name,
wxS("no locale to set in wxLocale::Init()") ); wxS("no locale to set in wxLocale::Init()") );
} }
if ( !wxSetlocale(LC_ALL, szLocale) ) if ( const wxLanguageInfo* langInfo = FindLanguageInfo(szLocale) )
{ {
wxLogError(_("locale '%s' cannot be set."), 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, // the short name will be used to look for catalog files as well,
// so we need something here // 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, // FIXME I don't know how these 2 letter abbreviations are formed,
// this wild guess is surely wrong // this wild guess is surely wrong
if ( !szLocale.empty() ) if ( !szLocale.empty() )
{ {
m_strShort += (wxChar)wxTolower(szLocale[0]); strShort += (wxChar)wxTolower(szLocale[0]);
if ( szLocale.length() > 1 ) 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<char *>(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__) #if defined(__UNIX__) && wxUSE_UNICODE && !defined(__WXMAC__)
static const char *wxSetlocaleTryUTF8(int c, const wxString& lc) static const char *wxSetlocaleTryUTF8(int c, const wxString& lc)
@@ -373,8 +428,6 @@ bool wxLocale::Init(int language, int flags)
wxS("wxLOCALE_CONV_ENCODING is no longer supported, add charset to your catalogs") ); wxS("wxLOCALE_CONV_ENCODING is no longer supported, add charset to your catalogs") );
#endif #endif
bool ret = true;
int lang = language; int lang = language;
if (lang == wxLANGUAGE_DEFAULT) if (lang == wxLANGUAGE_DEFAULT)
{ {
@@ -397,14 +450,12 @@ bool wxLocale::Init(int language, int flags)
return false; return false;
} }
wxString name = info->Description; const wxString& name = info->Description;
wxString canonical = info->CanonicalName; DoInit(name, info->CanonicalName, lang);
wxString locale;
// Set the locale: // Set the locale:
#if defined(__UNIX__) && !defined(__WXMAC__) #if defined(__UNIX__) && !defined(__WXMAC__)
if (language != wxLANGUAGE_DEFAULT) const wxString& locale = info->CanonicalName;
locale = info->CanonicalName;
const char *retloc = wxSetlocaleTryUTF8(LC_ALL, locale); const char *retloc = wxSetlocaleTryUTF8(LC_ALL, locale);
@@ -459,9 +510,6 @@ bool wxLocale::Init(int language, int flags)
} }
} }
if ( !retloc )
ret = false;
#ifdef __AIX__ #ifdef __AIX__
// at least in AIX 5.2 libc is buggy and the string returned from // 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 // setlocale(LC_ALL) can't be passed back to it because it returns 6
@@ -477,49 +525,33 @@ bool wxLocale::Init(int language, int flags)
#elif defined(__WIN32__) #elif defined(__WIN32__)
const char *retloc = "C"; 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);
{ // retloc already set to "C"
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 )
{
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);
}
}
} }
else // language == wxLANGUAGE_DEFAULT else // language supported by Windows
{ {
retloc = wxSetlocale(LC_ALL, wxEmptyString); 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
retloc = info->TrySetLocale();
}
#if wxUSE_UNICODE && (defined(__VISUALC__) || defined(__MINGW32__)) #if wxUSE_UNICODE && (defined(__VISUALC__) || defined(__MINGW32__))
// VC++ setlocale() (also used by Mingw) can't set locale to languages that // VC++ setlocale() (also used by Mingw) can't set locale to languages that
// can only be written using Unicode, therefore wxSetlocale() call fails // can only be written using Unicode, therefore wxSetlocale() call fails
@@ -535,14 +567,8 @@ bool wxLocale::Init(int language, int flags)
} }
} }
#endif // CRT not handling Unicode-only languages #endif // CRT not handling Unicode-only languages
if ( !retloc )
ret = false;
#elif defined(__WXMAC__) #elif defined(__WXMAC__)
if (lang == wxLANGUAGE_DEFAULT) const wxString& locale = info->CanonicalName;
locale.clear();
else
locale = info->CanonicalName;
const char *retloc = wxSetlocale(LC_ALL, locale); const char *retloc = wxSetlocale(LC_ALL, locale);
@@ -558,38 +584,16 @@ bool wxLocale::Init(int language, int flags)
#endif #endif
#ifndef WX_NO_LOCALE_SUPPORT #ifndef WX_NO_LOCALE_SUPPORT
if ( !ret )
{
wxLogWarning(_("Cannot set locale to language \"%s\"."), name.c_str());
// As we failed to change locale, there is no need to restore the
// previous one: it's still valid.
free(const_cast<char *>(m_pszOldLocale));
m_pszOldLocale = NULL;
// continue nevertheless and try to load at least the translations for
// this language
}
if ( !DoInit(name, canonical, retloc) )
{
ret = false;
}
if (IsOk()) // setlocale() succeeded
m_language = lang;
// NB: don't use 'lang' here, 'language' // NB: don't use 'lang' here, 'language'
wxTranslations *t = wxTranslations::Get(); return DoCommonPostInit
if ( t ) (
{ retloc != NULL,
t->SetLanguage(static_cast<wxLanguage>(language)); name,
language == wxLANGUAGE_DEFAULT
if ( flags & wxLOCALE_LOAD_DEFAULT ) ? wxString()
t->AddStdCatalog(); : info->CanonicalName,
} flags & wxLOCALE_LOAD_DEFAULT
);
return ret;
#endif // !WX_NO_LOCALE_SUPPORT #endif // !WX_NO_LOCALE_SUPPORT
} }