diff --git a/include/wx/intl.h b/include/wx/intl.h index c2020ddbfc..d66e3043d8 100644 --- a/include/wx/intl.h +++ b/include/wx/intl.h @@ -72,17 +72,18 @@ 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; + + // 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 // ---------------------------------------------------------------------------- @@ -332,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 @@ -343,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/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 d3453894ec..1fc2b8d989 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -143,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; @@ -152,30 +152,33 @@ 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; + + 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)) ) { wxLogLastError(wxT("GetLocaleInfo(LOCALE_SENGLANGUAGE)")); - return locale; + return NULL; } - locale << buffer; + locale = buffer; if ( ::GetLocaleInfo(lcid, LOCALE_SENGCOUNTRY, buffer, WXSIZEOF(buffer)) > 0 ) { @@ -188,10 +191,30 @@ wxString wxLanguageInfo::GetLocaleName() const 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 @@ -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") ); #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() ) @@ -303,27 +299,86 @@ bool wxLocale::DoInit(const wxString& name, 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, // 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) @@ -373,8 +428,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) { @@ -397,14 +450,12 @@ bool wxLocale::Init(int language, int flags) return false; } - wxString name = info->Description; - wxString canonical = info->CanonicalName; - wxString locale; + const wxString& name = info->Description; + DoInit(name, info->CanonicalName, lang); // Set the locale: #if defined(__UNIX__) && !defined(__WXMAC__) - if (language != wxLANGUAGE_DEFAULT) - locale = info->CanonicalName; + const wxString& locale = info->CanonicalName; const char *retloc = wxSetlocaleTryUTF8(LC_ALL, locale); @@ -459,9 +510,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 @@ -477,49 +525,33 @@ 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 ) - { - 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); - } - } + wxLogWarning(wxS("Locale '%s' not supported by OS."), name); + // retloc already set to "C" } - 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__)) // VC++ setlocale() (also used by Mingw) can't set locale to languages that // 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 - - if ( !retloc ) - ret = false; #elif defined(__WXMAC__) - if (lang == wxLANGUAGE_DEFAULT) - locale.clear(); - else - locale = info->CanonicalName; + const wxString& locale = info->CanonicalName; const char *retloc = wxSetlocale(LC_ALL, locale); @@ -558,38 +584,16 @@ bool wxLocale::Init(int language, int flags) #endif #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(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' - 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 }