Support creating Unix wxUILocale for languages without region

Unlike MSW and macOS, creating wxUILocale for e.g. "en" doesn't
necessarily succeed under Linux even if "en_US" is available, so try to
find a working language+region combination if just language doesn't
work.

This requires access to the languages database, so add a private
wxGetLanguageInfos() to avoid having to depend on wxLocale just for
this.
This commit is contained in:
Vadim Zeitlin
2021-09-01 01:02:00 +02:00
parent e92da29272
commit 3f8fd90c31
3 changed files with 57 additions and 17 deletions

View File

@@ -12,6 +12,12 @@
#include "wx/localedefs.h" #include "wx/localedefs.h"
#include "wx/string.h" #include "wx/string.h"
#include "wx/vector.h"
typedef wxVector<wxLanguageInfo> wxLanguageInfos;
// Return the vector of all known languages.
const wxLanguageInfos& wxGetLanguageInfos();
// Function returning hard-coded values for the "C" locale. // Function returning hard-coded values for the "C" locale.
wxString wxGetStdCLocaleInfo(wxLocaleInfo index, wxLocaleCategory cat); wxString wxGetStdCLocaleInfo(wxLocaleInfo index, wxLocaleCategory cat);

View File

@@ -51,7 +51,8 @@
#include "wx/stdpaths.h" #include "wx/stdpaths.h"
#include "wx/hashset.h" #include "wx/hashset.h"
#include "wx/uilocale.h" #include "wx/uilocale.h"
#include "wx/vector.h"
#include "wx/private/uilocale.h"
#ifdef __WIN32__ #ifdef __WIN32__
#include "wx/msw/private/uilocale.h" #include "wx/msw/private/uilocale.h"
@@ -193,9 +194,16 @@ wxString wxLanguageInfo::GetLocaleName() const
// wxLocale // wxLocale
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
static wxVector<wxLanguageInfo> gs_languagesDB; static wxLanguageInfos gs_languagesDB;
static bool gs_languagesDBInitialized = false; static bool gs_languagesDBInitialized = false;
const wxLanguageInfos& wxGetLanguageInfos()
{
wxLocale::CreateLanguagesDB();
return gs_languagesDB;
}
/*static*/ void wxLocale::CreateLanguagesDB() /*static*/ void wxLocale::CreateLanguagesDB()
{ {
if (!gs_languagesDBInitialized) if (!gs_languagesDBInitialized)

View File

@@ -80,6 +80,22 @@ inline locale_t TryCreateLocale(const wxLocaleIdent& locId)
return newlocale(LC_ALL_MASK, locId.GetName().mb_str(), NULL); return newlocale(LC_ALL_MASK, locId.GetName().mb_str(), NULL);
} }
// Wrapper around newlocale() also trying to append UTF-8 codeset (and
// modifying its wxLocaleIdent argument if it succeeds).
locale_t TryCreateLocaleWithUTF8(wxLocaleIdent& locId)
{
locale_t loc = TryCreateLocale(locId);
if ( !loc && locId.GetCharset().empty() )
{
wxLocaleIdent locIdUTF8 = wxLocaleIdent(locId).Charset("UTF-8");
loc = TryCreateLocale(locIdUTF8);
if ( loc )
locId = locIdUTF8;
}
return loc;
}
#endif // HAVE_LOCALE_T #endif // HAVE_LOCALE_T
} // anonymous namespace } // anonymous namespace
@@ -337,7 +353,7 @@ wxUILocaleImpl* wxUILocaleImpl::CreateForLocale(const wxLocaleIdent& locIdOrig)
// Make a copy of it because it can be modified below. // Make a copy of it because it can be modified below.
wxLocaleIdent locId = locIdOrig; wxLocaleIdent locId = locIdOrig;
locale_t loc = TryCreateLocale(locId); locale_t loc = TryCreateLocaleWithUTF8(locId);
if ( !loc ) if ( !loc )
{ {
// Try to find a variant of this locale available on this system: first // Try to find a variant of this locale available on this system: first
@@ -345,28 +361,38 @@ wxUILocaleImpl* wxUILocaleImpl::CreateForLocale(const wxLocaleIdent& locIdOrig)
// does _not_ work under Linux, so try adding one if we don't have it. // does _not_ work under Linux, so try adding one if we don't have it.
if ( locId.GetRegion().empty() ) if ( locId.GetRegion().empty() )
{ {
const wxLanguageInfo* const info = const wxString lang = locId.GetLanguage();
wxLocale::FindLanguageInfo(locId.GetLanguage());
if ( info ) const wxLanguageInfos& infos = wxGetLanguageInfos();
for ( wxLanguageInfos::const_iterator it = infos.begin();
it != infos.end();
++it )
{ {
wxString region = info->CanonicalName.AfterFirst('_'); const wxString& fullname = it->CanonicalName;
if ( !region.empty() ) if ( fullname.BeforeFirst('_') == lang )
{ {
// We never have encoding in our canonical names, but we // We never have encoding in our canonical names, but we
// can have modifiers, so get rid of them if necessary. // can have modifiers, so get rid of them if necessary.
region = region.BeforeFirst('@'); const wxString&
region = fullname.AfterFirst('_').BeforeFirst('@');
if ( !region.empty() )
{
loc = TryCreateLocaleWithUTF8(locId.Region(region));
if ( loc )
{
// We take the first available region, we don't
// have enough data to know how to prioritize them
// (and wouldn't want to start any geopolitical
// disputes).
break;
}
}
loc = TryCreateLocale(locId.Region(region)); // Don't bother reverting region to the old value as it will
// be overwritten during the next loop iteration anyhow.
} }
} }
} }
// And sometimes the locale without encoding is not available, but one
// with UTF-8 encoding is, so try this too.
if ( !loc && locId.GetCharset().empty() )
{
loc = TryCreateLocale(locId.Charset("UTF-8"));
}
} }
if ( !loc ) if ( !loc )