Merge branch 'multi-fallback-langs'

Closes https://github.com/wxWidgets/wxWidgets/pull/1006
This commit is contained in:
Vadim Zeitlin 2018-11-18 01:45:38 +01:00
commit 69b837cabf
3 changed files with 109 additions and 30 deletions

View File

@ -145,6 +145,12 @@ public:
wxString GetBestTranslation(const wxString& domain,
const wxString& msgIdLanguage = "en");
// find best and all other suitable translation languages for given domain
wxArrayString GetAcceptableTranslations(const wxString& domain,
wxLanguage msgIdLanguage);
wxArrayString GetAcceptableTranslations(const wxString& domain,
const wxString& msgIdLanguage = "en");
// add standard wxWidgets catalog ("wxstd")
bool AddStdCatalog();

View File

@ -137,6 +137,34 @@ public:
const wxString& msgIdLanguage = "en");
/**
Returns the languages of all translations that can be used for the @a
domain.
This is a more general version of GetBestTranslation(), which returns
the whole list of preferred UI languages for which a translation for
the @a domain was found instead of just the first, i.e. the most
preferred, element of this list.
@param domain
The catalog domain to look for.
@param msgIdLanguage
Specifies the language of "msgid" strings in source code (i.e.
arguments to GetString(), wxGetTranslation() and the _() macro).
@return An array of language codes if any suitable matches were found,
empty array otherwise.
@since 3.1.2
*/
wxArrayString GetAcceptableTranslations(const wxString& domain,
wxLanguage msgIdLanguage);
/// @overload
wxArrayString GetAcceptableTranslations(const wxString& domain,
const wxString& msgIdLanguage = "en");
/**
Add standard wxWidgets catalogs ("wxstd" and possible port-specific
catalogs).
@ -147,9 +175,10 @@ public:
bool AddStdCatalog();
/**
Add a catalog for use with the current locale.
Add a catalog for the preferred UI language. In case of multiple
preferences, add catalog for each language, if available.
By default, it is searched for in standard places (see
By default, the catalog is searched for in standard places (see
wxFileTranslationsLoader), but you may also prepend additional
directories to the search path with
wxFileTranslationsLoader::AddCatalogLookupPathPrefix().
@ -173,8 +202,9 @@ public:
code are used instead.
@return
@true if catalog was successfully loaded, @false otherwise (which might
mean that the catalog is not found or that it isn't in the correct format).
@true if catalog in the most preferred language was successfully loaded,
@false otherwise (which might mean that the catalog is not found or that
it isn't in the correct format).
*/
bool AddCatalog(const wxString& domain,
wxLanguage msgIdLanguage = wxLANGUAGE_ENGLISH_US);
@ -200,8 +230,9 @@ public:
in case they use 8-bit characters (e.g. German or French strings).
@return
@true if catalog was successfully loaded, @false otherwise (which might
mean that the catalog is not found or that it isn't in the correct format).
@true if catalog in the most preferred language was successfully loaded,
@false otherwise (which might mean that the catalog is not found or that
it isn't in the correct format).
*/
bool AddCatalog(const wxString& domain,
wxLanguage msgIdLanguage,

View File

@ -127,7 +127,7 @@ wxString GetPreferredUILanguageFallback(const wxArrayString& WXUNUSED(available)
#ifdef __WINDOWS__
wxString GetPreferredUILanguage(const wxArrayString& available)
wxString GetPreferredUILanguage(const wxArrayString& available, wxArrayString& allPreferred)
{
typedef BOOL (WINAPI *GetUserPreferredUILanguages_t)(DWORD, PULONG, PWSTR, PULONG);
static GetUserPreferredUILanguages_t s_pfnGetUserPreferredUILanguages = NULL;
@ -172,18 +172,20 @@ wxString GetPreferredUILanguage(const wxArrayString& available)
wxString lang(*j);
lang.Replace("-", "_");
if ( available.Index(lang, /*bCase=*/false) != wxNOT_FOUND )
return lang;
allPreferred.Add(lang);
size_t pos = lang.find('_');
if ( pos != wxString::npos )
{
lang = lang.substr(0, pos);
if ( available.Index(lang, /*bCase=*/false) != wxNOT_FOUND )
return lang;
allPreferred.Add(lang);
}
}
}
}
}
if ( !allPreferred.empty() )
return allPreferred[0];
return GetPreferredUILanguageFallback(available);
}
@ -207,7 +209,7 @@ void LogTraceArray(const char *prefix, CFArrayRef arr)
#endif // wxUSE_LOG_TRACE
wxString GetPreferredUILanguage(const wxArrayString& available)
wxString GetPreferredUILanguage(const wxArrayString& available, wxArrayString &allPreferred)
{
wxStringToStringHashMap availableNormalized;
wxCFRef<CFMutableArrayRef> availableArr(
@ -231,17 +233,19 @@ wxString GetPreferredUILanguage(const wxArrayString& available)
LogTraceArray(" - system preferred languages", prefArr);
unsigned prefArrLength = CFArrayGetCount(prefArr);
if ( prefArrLength > 0 )
for ( size_t x = 0; x < prefArrLength; ++x )
{
// Lookup the name in 'available' by index -- we need to get the
// original value corresponding to the normalized one chosen.
wxString lang(wxCFStringRef::AsString((CFStringRef)CFArrayGetValueAtIndex(prefArr, 0)));
wxString lang(wxCFStringRef::AsString((CFStringRef)CFArrayGetValueAtIndex(prefArr, x)));
wxStringToStringHashMap::const_iterator i = availableNormalized.find(lang);
if ( i == availableNormalized.end() )
return lang;
allPreferred.push_back(lang);
else
return i->second;
allPreferred.push_back(i->second);
}
if ( allPreferred.empty() == false )
return allPreferred[0];
return GetPreferredUILanguageFallback(available);
}
@ -255,7 +259,7 @@ wxString GetPreferredUILanguage(const wxArrayString& available)
// The LANGUAGE variable may contain a colon separated list of language
// codes in the order of preference.
// http://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html
wxString GetPreferredUILanguage(const wxArrayString& available)
wxString GetPreferredUILanguage(const wxArrayString& available, wxArrayString &allPreferred)
{
wxString languageFromEnv;
wxArrayString preferred;
@ -283,15 +287,17 @@ wxString GetPreferredUILanguage(const wxArrayString& available)
{
wxString lang(*j);
if ( available.Index(lang) != wxNOT_FOUND )
return lang;
allPreferred.Add(lang);
size_t pos = lang.find('_');
if ( pos != wxString::npos )
{
lang = lang.substr(0, pos);
if ( available.Index(lang) != wxNOT_FOUND )
return lang;
allPreferred.Add(lang);
}
}
if ( allPreferred.empty() == false )
return allPreferred[0];
return GetPreferredUILanguageFallback(available);
}
@ -1549,9 +1555,9 @@ bool wxTranslations::AddCatalog(const wxString& domain,
wxLanguage msgIdLanguage)
{
const wxString msgIdLang = wxLocale::GetLanguageCanonicalName(msgIdLanguage);
const wxString domain_lang = GetBestTranslation(domain, msgIdLang);
const wxArrayString domain_langs = GetAcceptableTranslations(domain, msgIdLanguage);
if ( domain_lang.empty() )
if ( domain_langs.empty() )
{
wxLogTrace(TRACE_I18N,
wxS("no suitable translation for domain '%s' found"),
@ -1559,11 +1565,30 @@ bool wxTranslations::AddCatalog(const wxString& domain,
return false;
}
wxLogTrace(TRACE_I18N,
wxS("adding '%s' translation for domain '%s' (msgid language '%s')"),
domain_lang, domain, msgIdLang);
bool success = false;
for ( wxArrayString::const_iterator lang = domain_langs.begin();
lang != domain_langs.end();
++lang )
{
wxLogTrace(TRACE_I18N,
wxS("adding '%s' translation for domain '%s' (msgid language '%s')"),
*lang, domain, msgIdLang);
return LoadCatalog(domain, domain_lang, msgIdLang);
// No use loading languages that are less preferred than the
// msgid language, as by definition it contains all the strings
// in the msgid language.
if ( msgIdLang == *lang )
break;
// We determine success by the success of loading/failing to load
// the most preferred (i.e. the first one) language's catalog:
if ( lang == domain_langs.begin() )
success = LoadCatalog(domain, *lang, msgIdLang);
else
LoadCatalog(domain, *lang, msgIdLang);
}
return success;
}
@ -1650,20 +1675,37 @@ wxString wxTranslations::GetBestTranslation(const wxString& domain,
wxString wxTranslations::GetBestTranslation(const wxString& domain,
const wxString& msgIdLanguage)
{
// explicitly set language should always be respected
if ( !m_lang.empty() )
return m_lang;
const wxArrayString allGoodOnes = GetAcceptableTranslations(domain, msgIdLanguage);
wxLogTrace(TRACE_I18N, " => using language '%s'", allGoodOnes[0]);
return allGoodOnes[0];
}
wxArrayString wxTranslations::GetAcceptableTranslations(const wxString& domain,
wxLanguage msgIdLanguage)
{
const wxString lang = wxLocale::GetLanguageCanonicalName(msgIdLanguage);
return GetAcceptableTranslations(domain, lang);
}
wxArrayString wxTranslations::GetAcceptableTranslations(const wxString& domain,
const wxString& msgIdLanguage)
{
wxArrayString available(GetAvailableTranslations(domain));
// it's OK to have duplicates, so just add msgid language
available.push_back(msgIdLanguage);
available.push_back(msgIdLanguage.BeforeFirst('_'));
wxLogTrace(TRACE_I18N, "choosing best language for domain '%s'", domain);
wxLogTrace(TRACE_I18N, "choosing best languages for domain '%s'", domain);
LogTraceArray(" - available translations", available);
const wxString lang = GetPreferredUILanguage(available);
wxLogTrace(TRACE_I18N, " => using language '%s'", lang);
return lang;
wxArrayString allPreferred;
GetPreferredUILanguage(available, allPreferred);
// explicitly set language should always be preferred the most
if ( !m_lang.empty() )
allPreferred.insert(allPreferred.begin(), m_lang);
return allPreferred;
}