From 34a90ba3afabb20283657ecf2ca09d22888b48a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Slavi=CC=81k?= Date: Fri, 9 Feb 2024 18:37:01 +0100 Subject: [PATCH 1/7] Fix GetBestAvailableTranslation to return no match GetBestTranslation() and GetBestAvailableTranslation() are documented to return an empty string if they find no match, but actually didn't do that due to the way GetPreferredUILanguage() was implemented to always fall back to the locale and return it without additional checks. --- src/common/translation.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/common/translation.cpp b/src/common/translation.cpp index 0d58caf3a5..7ced54853d 100644 --- a/src/common/translation.cpp +++ b/src/common/translation.cpp @@ -116,14 +116,6 @@ void LogTraceLargeArray(const wxString& prefix, const wxArrayString& arr) #endif // wxUSE_LOG_TRACE/!wxUSE_LOG_TRACE -// Use locale-based detection as a fallback -wxString GetPreferredUILanguageFallback(const wxArrayString& WXUNUSED(available)) -{ - const wxString lang = wxUILocale::GetLanguageCanonicalName(wxUILocale::GetSystemLocale()); - wxLogTrace(TRACE_I18N, " - obtained best language from locale: %s", lang); - return lang; -} - wxString GetPreferredUILanguage(const wxArrayString& available) { wxVector preferred = wxUILocale::GetPreferredUILanguages(); @@ -168,7 +160,7 @@ wxString GetPreferredUILanguage(const wxArrayString& available) if (!langNoMatchRegion.empty()) return langNoMatchRegion; - return GetPreferredUILanguageFallback(available); + return wxString(); } } // anonymous namespace From 2af36e9560c694a750b6aa835d32d12a0393fb83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Slavi=CC=81k?= Date: Fri, 9 Feb 2024 18:33:22 +0100 Subject: [PATCH 2/7] Partially revert "Add AddAvailableCatalog() and use it in AddStdCatalog()" This commit partially reverts 94b1a17aeb12a1ec723a255089be16cd31a268a2. The above commit changed translation lookup logic to differentiate between msgid and "available" translations (the latter being loaded from a file or resource). It first attempted to use available translations and only felt back to msgid language as the last resort. Unfortunately, doing that broke lookup in cases when the user had msgid language as not-last preference in their UI languages list, or even when a similar translation was available. For example: - msgid=en, user preference [cs,en,fr], only fr.po available: fr would be used instead of en - msgid=en_US or en, user preference [en], but en_GB.po available: British English would be used even for US users This commit fixes this by restoring the previous behavior where msgid language was treated as a first-class citizen in lookup, while preserving the API that allows distinguishing between the two cases. It also reimplements AddStdCatalog() in a more straightforward way that doesn't rely on AddAvailableCatalog(). --- include/wx/translation.h | 2 + src/common/translation.cpp | 97 +++++++++++++++++++------------------- 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/include/wx/translation.h b/include/wx/translation.h index ca97649a4b..0f38931d2f 100644 --- a/include/wx/translation.h +++ b/include/wx/translation.h @@ -203,6 +203,8 @@ private: static void SetNonOwned(wxTranslations *t); friend class wxLocale; + wxString DoGetBestAvailableTranslation(const wxString& domain, const wxString& additionalAvailableLanguage); + private: wxString m_lang; wxTranslationsLoader *m_loader; diff --git a/src/common/translation.cpp b/src/common/translation.cpp index 7ced54853d..6ed764df5b 100644 --- a/src/common/translation.cpp +++ b/src/common/translation.cpp @@ -1308,13 +1308,11 @@ bool wxTranslations::AddStdCatalog() // the name without the version if it's not found, as message catalogs // typically won't have the version in their names under non-Unix platforms // (i.e. where they're not installed by our own "make install"). - if ( AddAvailableCatalog("wxstd-" wxSTRINGIZE(wxMAJOR_VERSION) "." wxSTRINGIZE(wxMINOR_VERSION)) ) - return true; + wxString domain("wxstd-" wxSTRINGIZE(wxMAJOR_VERSION) "." wxSTRINGIZE(wxMINOR_VERSION)); + if ( GetBestAvailableTranslation(domain).empty() ) + domain = wxS("wxstd"); - if ( AddCatalog(wxS("wxstd")) ) - return true; - - return false; + return AddCatalog(domain); } bool wxTranslations::AddAvailableCatalog(const wxString& domain) @@ -1334,39 +1332,30 @@ bool wxTranslations::AddAvailableCatalog(const wxString& domain) bool wxTranslations::AddCatalog(const wxString& domain, wxLanguage msgIdLanguage) { - if ( AddAvailableCatalog(domain) ) - return true; - const wxString msgIdLang = wxUILocale::GetLanguageCanonicalName(msgIdLanguage); - - // Check if the original strings can be used directly. - bool canUseUntranslated = false; - if ( m_lang.empty() ) - { - // If we are using the default language, check if the message ID - // language is acceptable for this system. - const wxString domain_lang = GetBestTranslation(domain, msgIdLang); - - if ( msgIdLang == domain_lang ) - canUseUntranslated = true; - } - else // But if we have a fixed language, we should just check it instead. - { - // Consider message IDs for another region using the same language - // acceptable. - if ( msgIdLang.BeforeFirst('_') == m_lang.BeforeFirst('_') ) - canUseUntranslated = true; - } - - if ( canUseUntranslated ) + const wxString domain_lang = GetBestTranslation(domain, msgIdLang); + if ( domain_lang.empty() ) { wxLogTrace(TRACE_I18N, - wxS("not using translations for domain '%s' with msgid language '%s'"), - domain, msgIdLang); + wxS("no suitable translation for domain '%s' found"), + domain); + return false; + } + + if ( LoadCatalog(domain, domain_lang) ) + { + wxLogTrace(TRACE_I18N, + wxS("adding '%s' translation for domain '%s' (msgid language '%s')"), + domain_lang, domain, msgIdLang); return true; } - return false; + // LoadCatalog() failed, but GetBestTranslation() returned non-empty language. + // That must mean that msgIdLanguage was used. + wxLogTrace(TRACE_I18N, + wxS("not using translations for domain '%s' with msgid language '%s'"), + domain, msgIdLang); + return true; } @@ -1444,23 +1433,19 @@ wxString wxTranslations::GetBestTranslation(const wxString& domain, wxString wxTranslations::GetBestTranslation(const wxString& domain, const wxString& msgIdLanguage) { - wxString lang = GetBestAvailableTranslation(domain); + // Determine the best language, including the msgId language, which is always + // available because it is present in the code: + wxString lang = DoGetBestAvailableTranslation(domain, msgIdLanguage); + if ( lang.empty() ) { - wxArrayString available; - available.push_back(msgIdLanguage); - available.push_back(msgIdLanguage.BeforeFirst('_')); - lang = GetPreferredUILanguage(available); - if ( lang.empty() ) - { - wxLogTrace(TRACE_I18N, - "no available language for domain '%s'", domain); - } - else - { - wxLogTrace(TRACE_I18N, - "using message ID language '%s' for domain '%s'", lang); - } + wxLogTrace(TRACE_I18N, + "no available language for domain '%s'", domain); + } + else if ( lang == msgIdLanguage || lang == msgIdLanguage.BeforeFirst('_') ) + { + wxLogTrace(TRACE_I18N, + "using message ID language '%s' for domain '%s'", lang, domain); } return lang; @@ -1468,7 +1453,21 @@ wxString wxTranslations::GetBestTranslation(const wxString& domain, wxString wxTranslations::GetBestAvailableTranslation(const wxString& domain) { - const wxArrayString available(GetAvailableTranslations(domain)); + // Determine the best language from the ones with actual translation file: + // As this function never considers the language of the original messages as being + // available, pass empty string as message ID language to the helper function. + return DoGetBestAvailableTranslation(domain, wxString()); +} + +wxString wxTranslations::DoGetBestAvailableTranslation(const wxString& domain, const wxString& additionalAvailableLanguage) +{ + wxArrayString available(GetAvailableTranslations(domain)); + if ( !additionalAvailableLanguage.empty() ) + { + available.push_back(additionalAvailableLanguage); + available.push_back(additionalAvailableLanguage.BeforeFirst('_')); + } + if ( !m_lang.empty() ) { wxLogTrace(TRACE_I18N, From 1030147aff1fc3ec7f49ed581c976018e82f2cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Slavi=CC=81k?= Date: Sat, 10 Feb 2024 11:46:13 +0100 Subject: [PATCH 3/7] Fix AddAvailableCatalog() API Knowing msgIdLanguage is necessary for correct evaluation of preferred languages order; without it, it would be possible to load undesirable translation file. See 2af36e9560c694a750b6aa835d32d12a0393fb83 for details. --- include/wx/translation.h | 11 ++++++++++- interface/wx/translation.h | 15 +++++++++++++-- src/common/translation.cpp | 28 ++++++++++++---------------- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/include/wx/translation.h b/include/wx/translation.h index 0f38931d2f..aaa55571d4 100644 --- a/include/wx/translation.h +++ b/include/wx/translation.h @@ -160,7 +160,7 @@ public: // add catalog for the given domain returning true if it could be found by // wxTranslationsLoader - bool AddAvailableCatalog(const wxString& domain); + bool AddAvailableCatalog(const wxString& domain, wxLanguage msgIdLanguage = wxLANGUAGE_ENGLISH_US); // add standard wxWidgets catalog ("wxstd") bool AddStdCatalog(); @@ -193,6 +193,15 @@ public: static const wxString& GetUntranslatedString(const wxString& str); private: + enum class Translations + { + NotNeeded = -1, + NotFound = 0, + Found = 1 + }; + + Translations DoAddCatalog(const wxString& domain, wxLanguage msgIdLanguage); + // perform loading of the catalog via m_loader bool LoadCatalog(const wxString& domain, const wxString& lang); diff --git a/interface/wx/translation.h b/interface/wx/translation.h index 401a421bdd..cba3f1fdad 100644 --- a/interface/wx/translation.h +++ b/interface/wx/translation.h @@ -158,6 +158,16 @@ public: All loaded catalogs will be used for message lookup by GetString() for the current locale. + @param domain + The catalog domain to add. + + @param msgIdLanguage + Specifies the language of "msgid" strings in source code + (i.e. arguments to GetString(), wxGetTranslation() and the _() macro). + It is used if AddCatalog() cannot find any catalog for current language: + if the language is same as source code language, then strings from source + code are used instead. + @return @true if catalog was successfully loaded, @false otherwise, usually because it wasn't found. Note that unlike AddCatalog() this @@ -167,9 +177,10 @@ public: selected or system-default languages, but is not necessarily an error if no translations are needed in the first place. - @since 3.3.0 + @since 3.2.5 */ - bool AddAvailableCatalog(const wxString& domain); + bool AddAvailableCatalog(const wxString& domain, + wxLanguage msgIdLanguage = wxLANGUAGE_ENGLISH_US); /** Add a catalog for use with the current locale or fall back to the diff --git a/src/common/translation.cpp b/src/common/translation.cpp index 6ed764df5b..152d1786ef 100644 --- a/src/common/translation.cpp +++ b/src/common/translation.cpp @@ -1315,22 +1315,18 @@ bool wxTranslations::AddStdCatalog() return AddCatalog(domain); } -bool wxTranslations::AddAvailableCatalog(const wxString& domain) +bool wxTranslations::AddAvailableCatalog(const wxString& domain, wxLanguage msgIdLanguage) { - const wxString domain_lang = GetBestAvailableTranslation(domain); - if ( domain_lang.empty() ) - { - wxLogTrace(TRACE_I18N, - wxS("no suitable translation for domain '%s' found"), - domain); - return false; - } - - return LoadCatalog(domain, domain_lang); + return DoAddCatalog(domain, msgIdLanguage) == Translations::Found; } -bool wxTranslations::AddCatalog(const wxString& domain, - wxLanguage msgIdLanguage) +bool wxTranslations::AddCatalog(const wxString& domain, wxLanguage msgIdLanguage) +{ + return DoAddCatalog(domain, msgIdLanguage) != Translations::NotFound; +} + +wxTranslations::Translations wxTranslations::DoAddCatalog(const wxString& domain, + wxLanguage msgIdLanguage) { const wxString msgIdLang = wxUILocale::GetLanguageCanonicalName(msgIdLanguage); const wxString domain_lang = GetBestTranslation(domain, msgIdLang); @@ -1339,7 +1335,7 @@ bool wxTranslations::AddCatalog(const wxString& domain, wxLogTrace(TRACE_I18N, wxS("no suitable translation for domain '%s' found"), domain); - return false; + return Translations::NotFound; } if ( LoadCatalog(domain, domain_lang) ) @@ -1347,7 +1343,7 @@ bool wxTranslations::AddCatalog(const wxString& domain, wxLogTrace(TRACE_I18N, wxS("adding '%s' translation for domain '%s' (msgid language '%s')"), domain_lang, domain, msgIdLang); - return true; + return Translations::Found; } // LoadCatalog() failed, but GetBestTranslation() returned non-empty language. @@ -1355,7 +1351,7 @@ bool wxTranslations::AddCatalog(const wxString& domain, wxLogTrace(TRACE_I18N, wxS("not using translations for domain '%s' with msgid language '%s'"), domain, msgIdLang); - return true; + return Translations::NotNeeded; } From f0ed987010f9f6e91d3954beab67d73fa51ed2aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Slavi=CC=81k?= Date: Mon, 12 Feb 2024 14:31:16 +0100 Subject: [PATCH 4/7] Warn about a GetBestAvailableTranslation() quirk --- interface/wx/translation.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/interface/wx/translation.h b/interface/wx/translation.h index cba3f1fdad..bfecf3d24f 100644 --- a/interface/wx/translation.h +++ b/interface/wx/translation.h @@ -97,6 +97,12 @@ public: it simply returns the language set with SetLanguage() if it's available or empty string otherwise. + @warning + This function does not consider messages ID language (typically + English) and can return inappropriate language if it is anywhere in + user's preferred languages list. Use GetBestTranslation() instead + unless you have very specific needs. + @since 3.3.0 */ wxString GetBestAvailableTranslation(const wxString& domain); From f248f0593c7e5a839bda94c823d0dcd6c7bbd5df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Slavi=CC=81k?= Date: Mon, 12 Feb 2024 14:30:48 +0100 Subject: [PATCH 5/7] Clarify GetAvailableTranslations() return value Add a note explaining that the msgid language is not included. --- interface/wx/translation.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/wx/translation.h b/interface/wx/translation.h index bfecf3d24f..d90a896d0e 100644 --- a/interface/wx/translation.h +++ b/interface/wx/translation.h @@ -85,6 +85,11 @@ public: translations offered to the user. To do this, pass the app's main catalog as @a domain. + @note + The returned list does not include messages ID language, i.e. the + language (typically English) included in the source code. In the use + case described above, that language needs to be added manually. + @see GetBestTranslation() */ wxArrayString GetAvailableTranslations(const wxString& domain) const; From 1f658ff72921207392e512df2fbe8cea66b5ceb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Slavi=CC=81k?= Date: Tue, 13 Feb 2024 13:38:37 +0100 Subject: [PATCH 6/7] Override GetPreferredUILanguages with WXLANGUAGE Add support for overriding GetPreferredUILanguages() return value by setting the WXLANGUAGE environment variable to a colon-separated list of desired languages (same as GNU's LANGUAGE variable). Primarily used for unit tests, but may be more generally useful. --- docs/doxygen/overviews/envvars.h | 6 ++++++ src/common/uilocale.cpp | 30 +++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/docs/doxygen/overviews/envvars.h b/docs/doxygen/overviews/envvars.h index 7b6386d8e4..21079cc25a 100644 --- a/docs/doxygen/overviews/envvars.h +++ b/docs/doxygen/overviews/envvars.h @@ -51,6 +51,12 @@ wxWidgets programs. default value if it's not a number, so that e.g. setting it to "yes" suppresses all GTK diagnostics while setting it to 16 only suppresses GTK warning messages.} +@itemdef{WXLANGUAGE, + This variable can be set to override OS setting of preferred languages + and make wxUILocale::GetPreferredUILanguages() return the set list + instead. The format is same as GNU's LANGUAGE + variable: a colon-separated list of language codes.} */ @see wxSystemOptions diff --git a/src/common/uilocale.cpp b/src/common/uilocale.cpp index 014d32b678..e52fa2d833 100644 --- a/src/common/uilocale.cpp +++ b/src/common/uilocale.cpp @@ -24,6 +24,9 @@ #include "wx/arrstr.h" #include "wx/intl.h" +#include "wx/log.h" +#include "wx/tokenzr.h" +#include "wx/utils.h" #ifndef __WINDOWS__ #include "wx/language.h" @@ -31,6 +34,8 @@ #include "wx/private/uilocale.h" +#define TRACE_I18N wxS("i18n") + // ---------------------------------------------------------------------------- // helper functions // ---------------------------------------------------------------------------- @@ -678,7 +683,7 @@ int wxUILocale::GetSystemLanguage() { const wxLanguageInfos& languagesDB = wxGetLanguageInfos(); size_t count = languagesDB.size(); - wxVector preferred = wxUILocaleImpl::GetPreferredUILanguages(); + wxVector preferred = wxUILocale::GetPreferredUILanguages(); for (wxVector::const_iterator j = preferred.begin(); j != preferred.end(); @@ -743,6 +748,29 @@ int wxUILocale::GetSystemLocale() /* static */ wxVector wxUILocale::GetPreferredUILanguages() { + // The WXLANGUAGE variable may contain a colon separated list of language + // codes in the order of preference. It is modelled after GNU's LANGUAGE: + // http://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html + wxString languageFromEnv; + if (wxGetEnv("WXLANGUAGE", &languageFromEnv) && !languageFromEnv.empty()) + { + wxVector preferred; + wxStringTokenizer tknzr(languageFromEnv, ":"); + while (tknzr.HasMoreTokens()) + { + const wxString tok = tknzr.GetNextToken(); + if (const wxLanguageInfo* li = wxUILocale::FindLanguageInfo(tok)) + { + preferred.push_back(li->CanonicalName); + } + } + if (!preferred.empty()) + { + wxLogTrace(TRACE_I18N, " - using languages override from WXLANGUAGE: '%s'", languageFromEnv); + return preferred; + } + } + return wxUILocaleImpl::GetPreferredUILanguages(); } From 6d48acb5f39215dfd000ffa38f02a2b29870eb50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=CC=81clav=20Slavi=CC=81k?= Date: Tue, 13 Feb 2024 16:08:04 +0100 Subject: [PATCH 7/7] Add unit tests for wxTranslations language lookup --- build/cmake/tests/base/CMakeLists.txt | 2 + tests/Makefile.in | 16 +++++- tests/intl/en_GB/internat.mo | Bin 0 -> 302 bytes tests/intl/en_GB/internat.po | 18 +++++++ tests/intl/intltest.cpp | 67 ++++++++++++++++++++++++-- tests/makefile.gcc | 8 ++- tests/makefile.vc | 6 ++- tests/test.bkl | 1 + 8 files changed, 108 insertions(+), 10 deletions(-) create mode 100644 tests/intl/en_GB/internat.mo create mode 100644 tests/intl/en_GB/internat.po diff --git a/build/cmake/tests/base/CMakeLists.txt b/build/cmake/tests/base/CMakeLists.txt index 71ca50e243..9fb576c1d6 100644 --- a/build/cmake/tests/base/CMakeLists.txt +++ b/build/cmake/tests/base/CMakeLists.txt @@ -106,6 +106,8 @@ if(wxUSE_XML) endif() set(TEST_DATA + intl/en_GB/internat.mo + intl/en_GB/internat.po intl/fr/internat.mo intl/fr/internat.po intl/ja/internat.mo diff --git a/tests/Makefile.in b/tests/Makefile.in index 760d178820..f9c14e47b6 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -463,7 +463,7 @@ COND_MONOLITHIC_1___WXLIB_MONO_p = \ ### Targets: ### -all: test$(EXEEXT) $(__test_drawing___depname) $(__test_drawingplugin___depname) $(__test_gui___depname) $(__test_gui_bundle___depname) $(__test_allheaders___depname) $(__test_allheaders_bundle___depname) data data-image-sample data-images fr ja +all: test$(EXEEXT) $(__test_drawing___depname) $(__test_drawingplugin___depname) $(__test_gui___depname) $(__test_gui_bundle___depname) $(__test_allheaders___depname) $(__test_allheaders_bundle___depname) data data-image-sample data-images en_GB fr ja install: @@ -602,6 +602,18 @@ data-images: esac; \ done +en_GB: + @mkdir -p ./intl/en_GB + @for f in internat.po internat.mo; do \ + if test ! -f ./intl/en_GB/$$f -a ! -d ./intl/en_GB/$$f ; \ + then x=yep ; \ + else x=`find $(srcdir)/intl/en_GB/$$f -newer ./intl/en_GB/$$f -print` ; \ + fi; \ + case "$$x" in ?*) \ + cp -pRf $(srcdir)/intl/en_GB/$$f ./intl/en_GB ;; \ + esac; \ + done + fr: @mkdir -p ./intl/fr @for f in internat.po internat.mo; do \ @@ -1278,4 +1290,4 @@ failtest_allheaders: @IF_GNU_MAKE@-include ./.deps/*.d .PHONY: all install uninstall clean distclean test_gui_bundle \ - test_allheaders_bundle data data-image-sample data-images fr ja + test_allheaders_bundle data data-image-sample data-images en_GB fr ja diff --git a/tests/intl/en_GB/internat.mo b/tests/intl/en_GB/internat.mo new file mode 100644 index 0000000000000000000000000000000000000000..2089093322d5880fd80311f590b8192489c7e705 GIT binary patch literal 302 zcmYj~K}*9x5QQsx+M{O=D|k?JbhA~68*))wDYO_QdX+UDV*rq3!-y(3Z>TmheQu&C+NHNG bjGbQuBdJ^2kn2pY&-{?&elC-vObmVjAQn;R literal 0 HcmV?d00001 diff --git a/tests/intl/en_GB/internat.po b/tests/intl/en_GB/internat.po new file mode 100644 index 0000000000..1bdced1767 --- /dev/null +++ b/tests/intl/en_GB/internat.po @@ -0,0 +1,18 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: \n" +"POT-Creation-Date: 2003-10-04 23:10+0200\n" +"PO-Revision-Date: 2024-02-13 13:25+0100\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: en_GB\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" diff --git a/tests/intl/intltest.cpp b/tests/intl/intltest.cpp index 9b14175fce..a504a9edaa 100644 --- a/tests/intl/intltest.cpp +++ b/tests/intl/intltest.cpp @@ -19,6 +19,7 @@ #include "wx/intl.h" #include "wx/uilocale.h" +#include "wx/scopeguard.h" #include "wx/private/glibc.h" @@ -241,8 +242,9 @@ void IntlTestCase::IsAvailable() TEST_CASE("wxTranslations::AddCatalog", "[translations]") { - // We currently have translations for French and Japanese in this test - // directory, check that loading those succeeds but loading others doesn't. + // We currently have translations for British English, French and Japanese + // in this test directory, check that loading those succeeds but loading + // others doesn't. wxFileTranslationsLoader::AddCatalogLookupPathPrefix("./intl"); const wxString domain("internat"); @@ -252,11 +254,12 @@ TEST_CASE("wxTranslations::AddCatalog", "[translations]") SECTION("All") { auto available = trans.GetAvailableTranslations(domain); - REQUIRE( available.size() == 2 ); + REQUIRE( available.size() == 3 ); available.Sort(); - CHECK( available[0] == "fr" ); - CHECK( available[1] == "ja" ); + CHECK( available[0] == "en_GB" ); + CHECK( available[1] == "fr" ); + CHECK( available[2] == "ja" ); } SECTION("French") @@ -287,6 +290,60 @@ TEST_CASE("wxTranslations::AddCatalog", "[translations]") } } +TEST_CASE("wxTranslations::GetBestTranslation", "[translations]") +{ + wxFileTranslationsLoader::AddCatalogLookupPathPrefix("./intl"); + + const wxString domain("internat"); + + wxTranslations trans; + wxON_BLOCK_EXIT1( wxUnsetEnv, "WXLANGUAGE" ); + + SECTION("ChooseLanguage") + { + // Simple case. + wxSetEnv("WXLANGUAGE", "fr:en"); + CHECK( trans.GetBestTranslation(domain) == "fr" ); + CHECK( trans.GetBestAvailableTranslation(domain) == "fr" ); + + // Choose 2nd language _and_ its base form. + wxSetEnv("WXLANGUAGE", "cs:fr_CA:en"); + CHECK( trans.GetBestTranslation(domain) == "fr" ); + CHECK( trans.GetBestAvailableTranslation(domain) == "fr" ); + } + + SECTION("EnglishHandling") + { + // Check that existing en_GB file isn't used for msgid language. + wxSetEnv("WXLANGUAGE", "en_US"); + + CHECK( trans.GetBestTranslation(domain) == "en" ); + // GetBestAvailableTranslation() will wrongly return "en_GB", don't test that. + + wxSetEnv("WXLANGUAGE", "es:en"); + CHECK( trans.GetBestTranslation(domain) == "en" ); + // GetBestAvailableTranslation() will wrongly return "en_GB", don't test that. + + // And that it is used when it should be + wxSetEnv("WXLANGUAGE", "en_GB"); + CHECK( trans.GetBestTranslation(domain) == "en_GB" ); + CHECK( trans.GetBestAvailableTranslation(domain) == "en_GB" ); + + } + + SECTION("DontSkipMsgidLanguage") + { + // Check that msgid language will be used if it's the best match. + wxSetEnv("WXLANGUAGE", "cs:en:fr"); + CHECK( trans.GetBestTranslation(domain) == "en" ); + + // ...But won't be used if there's a suitable translation file. + wxSetEnv("WXLANGUAGE", "fr:en:cs"); + CHECK( trans.GetBestTranslation(domain) == "fr" ); + CHECK( trans.GetBestAvailableTranslation(domain) == "fr" ); + } +} + TEST_CASE("wxLocale::Default", "[locale]") { const int langDef = wxUILocale::GetSystemLanguage(); diff --git a/tests/makefile.gcc b/tests/makefile.gcc index 048f3fc3ed..8cbaed0a1e 100644 --- a/tests/makefile.gcc +++ b/tests/makefile.gcc @@ -512,7 +512,7 @@ $(OBJS): ### Targets: ### -all: $(OBJS)\test.exe $(__test_drawing___depname) $(__test_drawingplugin___depname) $(__test_gui___depname) $(__test_allheaders___depname) data data-image-sample data-images fr ja +all: $(OBJS)\test.exe $(__test_drawing___depname) $(__test_drawingplugin___depname) $(__test_gui___depname) $(__test_allheaders___depname) data data-image-sample data-images en_GB fr ja clean: -if exist $(OBJS)\*.o del $(OBJS)\*.o @@ -572,6 +572,10 @@ data-images: if not exist image mkdir image for %%f in (bitfields.bmp bitfields-alpha.bmp 8bpp-colorsused-large.bmp 8bpp-colorsused-negative.bmp rle4-delta-320x240.bmp rle8-delta-320x240.bmp rle8-delta-320x240-expected.bmp horse_grey.bmp horse_grey_flipped.bmp horse_rle4.bmp horse_rle4_flipped.bmp horse_rle8.bmp horse_rle8_flipped.bmp horse_bicubic_50x50.png horse_bicubic_100x100.png horse_bicubic_150x150.png horse_bicubic_300x300.png horse_bilinear_50x50.png horse_bilinear_100x100.png horse_bilinear_150x150.png horse_bilinear_300x300.png horse_box_average_50x50.png horse_box_average_100x100.png horse_box_average_150x150.png horse_box_average_300x300.png cross_bicubic_256x256.png cross_bilinear_256x256.png cross_box_average_256x256.png cross_nearest_neighb_256x256.png paste_input_background.png paste_input_black.png paste_input_overlay_transparent_border_opaque_square.png paste_input_overlay_transparent_border_semitransparent_circle.png paste_input_overlay_transparent_border_semitransparent_square.png paste_result_background_plus_circle_plus_square.png paste_result_background_plus_overlay_transparent_border_opaque_square.png paste_result_background_plus_overlay_transparent_border_semitransparent_square.png paste_result_no_background_square_over_circle.png wx.png toucan.png toucan_hue_0.538.png toucan_sat_-0.41.png toucan_bright_-0.259.png toucan_hsv_0.538_-0.41_-0.259.png toucan_light_46.png toucan_dis_240.png toucan_grey.png toucan_mono_255_255_255.png width-times-height-overflow.bmp width_height_32_bit_overflow.pgm bad_truncated.gif) do if not exist image\%%f copy .\image\%%f image +en_GB: + if not exist $(OBJS)\intl\en_GB mkdir $(OBJS)\intl\en_GB + for %%f in (internat.po internat.mo) do if not exist $(OBJS)\intl\en_GB\%%f copy .\intl\en_GB\%%f $(OBJS)\intl\en_GB + fr: if not exist $(OBJS)\intl\fr mkdir $(OBJS)\intl\fr for %%f in (internat.po internat.mo) do if not exist $(OBJS)\intl\fr\%%f copy .\intl\fr\%%f $(OBJS)\intl\fr @@ -1201,7 +1205,7 @@ $(OBJS)\test_allheaders_allheaders.o: ./allheaders.cpp $(OBJS)\test_allheaders_testableframe.o: ./testableframe.cpp $(CXX) -c -o $@ $(TEST_ALLHEADERS_CXXFLAGS) $(CPPDEPS) $< -.PHONY: all clean data data-image-sample data-images fr ja +.PHONY: all clean data data-image-sample data-images en_GB fr ja SHELL := $(COMSPEC) diff --git a/tests/makefile.vc b/tests/makefile.vc index 41c1452831..2ee5ebb7d3 100644 --- a/tests/makefile.vc +++ b/tests/makefile.vc @@ -792,7 +792,7 @@ $(OBJS): ### Targets: ### -all: $(OBJS)\test.exe $(__test_drawing___depname) $(__test_drawingplugin___depname) $(__test_gui___depname) $(__test_allheaders___depname) data data-image-sample data-images fr ja +all: $(OBJS)\test.exe $(__test_drawing___depname) $(__test_drawingplugin___depname) $(__test_gui___depname) $(__test_allheaders___depname) data data-image-sample data-images en_GB fr ja clean: -if exist $(OBJS)\*.obj del $(OBJS)\*.obj @@ -863,6 +863,10 @@ fr: if not exist $(OBJS)\intl\fr mkdir $(OBJS)\intl\fr for %f in (internat.po internat.mo) do if not exist $(OBJS)\intl\fr\%f copy .\intl\fr\%f $(OBJS)\intl\fr +en_GB: + if not exist $(OBJS)\intl\en_GB mkdir $(OBJS)\intl\en_GB + for %f in (internat.po internat.mo) do if not exist $(OBJS)\intl\en_GB\%f copy .\intl\en_GB\%f $(OBJS)\intl\en_GB + ja: if not exist $(OBJS)\intl\ja mkdir $(OBJS)\intl\ja for %f in (internat.po internat.mo) do if not exist $(OBJS)\intl\ja\%f copy .\intl\ja\%f $(OBJS)\intl\ja diff --git a/tests/test.bkl b/tests/test.bkl index 67ce2c10c1..323ce14c9d 100644 --- a/tests/test.bkl +++ b/tests/test.bkl @@ -429,6 +429,7 @@ internat.po internat.mo +