diff --git a/include/wx/translation.h b/include/wx/translation.h index 3d5d5b0b83..9b931a088a 100644 --- a/include/wx/translation.h +++ b/include/wx/translation.h @@ -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(); diff --git a/interface/wx/translation.h b/interface/wx/translation.h index eed6355d0b..23ca5128c7 100644 --- a/interface/wx/translation.h +++ b/interface/wx/translation.h @@ -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, diff --git a/src/common/translation.cpp b/src/common/translation.cpp index bf92ed2942..7d9937cd18 100644 --- a/src/common/translation.cpp +++ b/src/common/translation.cpp @@ -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 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; }