From 3f8fd90c31e290c8fdd6fbf5370f03ce5b1feea7 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Wed, 1 Sep 2021 01:02:00 +0200 Subject: [PATCH] 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. --- include/wx/private/uilocale.h | 6 ++++ src/common/intl.cpp | 12 ++++++-- src/unix/uilocale.cpp | 56 +++++++++++++++++++++++++---------- 3 files changed, 57 insertions(+), 17 deletions(-) diff --git a/include/wx/private/uilocale.h b/include/wx/private/uilocale.h index abe4f13b14..e2ab82279d 100644 --- a/include/wx/private/uilocale.h +++ b/include/wx/private/uilocale.h @@ -12,6 +12,12 @@ #include "wx/localedefs.h" #include "wx/string.h" +#include "wx/vector.h" + +typedef wxVector wxLanguageInfos; + +// Return the vector of all known languages. +const wxLanguageInfos& wxGetLanguageInfos(); // Function returning hard-coded values for the "C" locale. wxString wxGetStdCLocaleInfo(wxLocaleInfo index, wxLocaleCategory cat); diff --git a/src/common/intl.cpp b/src/common/intl.cpp index 795110052f..d8f66cd703 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -51,7 +51,8 @@ #include "wx/stdpaths.h" #include "wx/hashset.h" #include "wx/uilocale.h" -#include "wx/vector.h" + +#include "wx/private/uilocale.h" #ifdef __WIN32__ #include "wx/msw/private/uilocale.h" @@ -193,9 +194,16 @@ wxString wxLanguageInfo::GetLocaleName() const // wxLocale // ---------------------------------------------------------------------------- -static wxVector gs_languagesDB; +static wxLanguageInfos gs_languagesDB; static bool gs_languagesDBInitialized = false; +const wxLanguageInfos& wxGetLanguageInfos() +{ + wxLocale::CreateLanguagesDB(); + + return gs_languagesDB; +} + /*static*/ void wxLocale::CreateLanguagesDB() { if (!gs_languagesDBInitialized) diff --git a/src/unix/uilocale.cpp b/src/unix/uilocale.cpp index a470f2a3c6..2334e7010e 100644 --- a/src/unix/uilocale.cpp +++ b/src/unix/uilocale.cpp @@ -80,6 +80,22 @@ inline locale_t TryCreateLocale(const wxLocaleIdent& locId) 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 } // anonymous namespace @@ -337,7 +353,7 @@ wxUILocaleImpl* wxUILocaleImpl::CreateForLocale(const wxLocaleIdent& locIdOrig) // Make a copy of it because it can be modified below. wxLocaleIdent locId = locIdOrig; - locale_t loc = TryCreateLocale(locId); + locale_t loc = TryCreateLocaleWithUTF8(locId); if ( !loc ) { // 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. if ( locId.GetRegion().empty() ) { - const wxLanguageInfo* const info = - wxLocale::FindLanguageInfo(locId.GetLanguage()); - if ( info ) + const wxString lang = locId.GetLanguage(); + + const wxLanguageInfos& infos = wxGetLanguageInfos(); + for ( wxLanguageInfos::const_iterator it = infos.begin(); + it != infos.end(); + ++it ) { - wxString region = info->CanonicalName.AfterFirst('_'); - if ( !region.empty() ) + const wxString& fullname = it->CanonicalName; + if ( fullname.BeforeFirst('_') == lang ) { // We never have encoding in our canonical names, but we // 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 )