diff --git a/docs/changes.txt b/docs/changes.txt index 6deef2d39b..17d8b0766d 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -76,6 +76,7 @@ All: - Add wxSecretStore for storing passwords using the OS-provided facilities. - Add support for compiling application code with wxNO_UNSAFE_WXSTRING_CONV. +- Add support for translating strings in different contexts (RickS). - Add support for the micro version (third component) to OS and toolkit version functions. See wxGetOsVersion(), wxPlatformInfo, and wxAppTraits. - wxLogInfo() now logs messages if the log level is high enough, even without diff --git a/include/wx/translation.h b/include/wx/translation.h index a85c2a1612..3d5d5b0b83 100644 --- a/include/wx/translation.h +++ b/include/wx/translation.h @@ -35,10 +35,19 @@ // --keyword="_" --keyword="wxPLURAL:1,2" options // to extract the strings from the sources) #ifndef WXINTL_NO_GETTEXT_MACRO - #define _(s) wxGetTranslation((s)) - #define wxPLURAL(sing, plur, n) wxGetTranslation((sing), (plur), n) + #define _(s) wxGetTranslation((s)) + #define wxPLURAL(sing, plur, n) wxGetTranslation((sing), (plur), n) #endif +// wx-specific macro for translating strings in the given context: if you use +// them, you need to also add +// --keyword="wxGETTEXT_IN_CONTEXT:1c,2" --keyword="wxGETTEXT_IN_CONTEXT_PLURAL:1c,2,3" +// options to xgettext invocation. +#define wxGETTEXT_IN_CONTEXT(c, s) \ + wxGetTranslation((s), wxString(), c) +#define wxGETTEXT_IN_CONTEXT_PLURAL(c, sing, plur, n) \ + wxGetTranslation((sing), (plur), n, wxString(), c) + // another one which just marks the strings for extraction, but doesn't // perform the translation (use -kwxTRANSLATE with xgettext!) #define wxTRANSLATE(str) str @@ -79,7 +88,7 @@ public: wxString GetDomain() const { return m_domain; } // get the translated string: returns NULL if not found - const wxString *GetString(const wxString& sz, unsigned n = UINT_MAX) const; + const wxString *GetString(const wxString& sz, unsigned n = UINT_MAX, const wxString& ct = wxEmptyString) const; protected: wxMsgCatalog(const wxString& domain) @@ -154,10 +163,12 @@ public: // access to translations const wxString *GetTranslatedString(const wxString& origString, - const wxString& domain = wxEmptyString) const; + const wxString& domain = wxEmptyString, + const wxString& context = wxEmptyString) const; const wxString *GetTranslatedString(const wxString& origString, unsigned n, - const wxString& domain = wxEmptyString) const; + const wxString& domain = wxEmptyString, + const wxString& context = wxEmptyString) const; wxString GetHeaderValue(const wxString& header, const wxString& domain = wxEmptyString) const; @@ -247,10 +258,11 @@ protected: // get the translation of the string in the current locale inline const wxString& wxGetTranslation(const wxString& str, - const wxString& domain = wxString()) + const wxString& domain = wxString(), + const wxString& context = wxString()) { wxTranslations *trans = wxTranslations::Get(); - const wxString *transStr = trans ? trans->GetTranslatedString(str, domain) + const wxString *transStr = trans ? trans->GetTranslatedString(str, domain, context) : NULL; if ( transStr ) return *transStr; @@ -263,10 +275,11 @@ inline const wxString& wxGetTranslation(const wxString& str, inline const wxString& wxGetTranslation(const wxString& str1, const wxString& str2, unsigned n, - const wxString& domain = wxString()) + const wxString& domain = wxString(), + const wxString& context = wxString()) { wxTranslations *trans = wxTranslations::Get(); - const wxString *transStr = trans ? trans->GetTranslatedString(str1, n, domain) + const wxString *transStr = trans ? trans->GetTranslatedString(str1, n, domain, context) : NULL; if ( transStr ) return *transStr; @@ -287,6 +300,8 @@ inline const wxString& wxGetTranslation(const wxString& str1, #define _(s) (s) #endif #define wxPLURAL(sing, plur, n) ((n) == 1 ? (sing) : (plur)) + #define wxGETTEXT_IN_CONTEXT(c, s) (s) + #define wxGETTEXT_IN_CONTEXT_PLURAL(c, sing, plur, n) wxPLURAL(sing, plur, n) #endif #define wxTRANSLATE(str) str @@ -304,6 +319,10 @@ template inline TString wxGetTranslation(TString str, TDomain WXUNUSED(domain)) { return str; } +template +inline TString wxGetTranslation(TString str, TDomain WXUNUSED(domain), TContext WXUNUSED(context)) + { return str; } + template inline TString wxGetTranslation(TString str1, TString str2, size_t n) { return n == 1 ? str1 : str2; } @@ -313,6 +332,12 @@ inline TString wxGetTranslation(TString str1, TString str2, size_t n, TDomain WXUNUSED(domain)) { return n == 1 ? str1 : str2; } +template +inline TString wxGetTranslation(TString str1, TString str2, size_t n, + TDomain WXUNUSED(domain), + TContext WXUNUSED(context)) + { return n == 1 ? str1 : str2; } + #endif // wxUSE_INTL/!wxUSE_INTL // define this one just in case it occurs somewhere (instead of preferred diff --git a/interface/wx/translation.h b/interface/wx/translation.h index ad7146cd14..eed6355d0b 100644 --- a/interface/wx/translation.h +++ b/interface/wx/translation.h @@ -454,6 +454,28 @@ public: */ #define wxPLURAL(string, plural, n) +/** + Similar to _() but translates the string in the given context. + + See the description of @c context argument of wxGetTranslation(). + + @see wxGETTEXT_IN_CONTEXT_PLURAL() + + @since 3.1.1 + */ +#define wxGETTEXT_IN_CONTEXT(context, string) + +/** + Similar to wxPLURAL() but translates the string in the given context. + + See the description of @c context argument of wxGetTranslation(). + + @see wxGETTEXT_IN_CONTEXT() + + @since 3.1.1 + */ +#define wxGETTEXT_IN_CONTEXT_PLURAL(context, string, plural, n) + /** This macro doesn't do anything in the program code -- it simply expands to the value of its argument. @@ -515,6 +537,16 @@ public: also common in Unix world) syntax is provided: the _() macro is defined to do the same thing as wxGetTranslation(). + If @a context is not empty (notice that this argument is only available + starting from wxWidgets 3.1.1), item translation is looked up in the + specified context. This allows to have different translations for the same + string appearing in different contexts, e.g. it may be necessary to + translate the same English "Open" verb differently depending on the object + it applies to. To do this, you need to use @c msgctxt in the source message + catalog and specify different contexts for the different occurrences of the + string and then use the same contexts in the calls to this function (or + wxGETTEXT_IN_CONTEXT() or wxGETTEXT_IN_CONTEXT_PLURAL() macros). + This function is thread-safe. @note This function is not suitable for literal strings using wxT() macro @@ -527,7 +559,8 @@ public: @header{wx/intl.h} */ const wxString& wxGetTranslation(const wxString& string, - const wxString& domain = wxEmptyString); + const wxString& domain = wxEmptyString, + const wxString& context = wxEmptyString); /** This is an overloaded version of @@ -553,7 +586,8 @@ const wxString& wxGetTranslation(const wxString& string, */ const wxString& wxGetTranslation(const wxString& string, const wxString& plural, unsigned n, - const wxString& domain = wxEmptyString); + const wxString& domain = wxEmptyString, + const wxString& context = wxEmptyString); /** Macro to be used around all literal strings that should be translated. diff --git a/samples/internat/fr/internat.mo b/samples/internat/fr/internat.mo index ec2d79a202..f65c6b7003 100644 Binary files a/samples/internat/fr/internat.mo and b/samples/internat/fr/internat.mo differ diff --git a/samples/internat/fr/internat.po b/samples/internat/fr/internat.po index 734ab79fad..fcd9a5cafe 100644 --- a/samples/internat/fr/internat.po +++ b/samples/internat/fr/internat.po @@ -2,17 +2,47 @@ # Copyright (C) 1999 wxWindows development team # Vadim Zeitlin # -#: internat.cpp:146 -#, fuzzy msgid "" msgstr "" "Project-Id-Version: wxWindows 2.0 i18n sample\n" "POT-Creation-Date: 1999-01-13 18:19+0100\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"PO-Revision-Date: 2016-09-20 19:38+0200\n" "Last-Translator: Vadim Zeitlin \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" +"Language-Team: \n" +"Language: fr\n" +"X-Generator: Poedit 1.8.7\n" + +msgid "item" +msgstr "Item without context" + +msgctxt "context_1" +msgid "item" +msgstr "Item in context 1" + +msgctxt "context_2" +msgid "item" +msgstr "Item in context 2" + +msgid "sing" +msgid_plural "plur" +msgstr[0] "Singular form without context" +msgstr[1] "Plural form without context" + +msgctxt "context_1" +msgid "sing" +msgid_plural "plur" +msgstr[0] "Singular form in context 1" +msgstr[1] "Plural form in context 1" + +msgctxt "context_2" +msgid "sing" +msgid_plural "plur" +msgstr[0] "Singular form in context 2" +msgstr[1] "Plural form in context 2" #: internat.cpp:98 msgid "International wxWindows App" @@ -43,9 +73,11 @@ msgid "&Test" msgstr "&Test" #: internat.cpp:138 -msgid "I18n sample\n" +msgid "" +"I18n sample\n" "© 1998, 1999 Vadim Zeitlin and Julian Smart" -msgstr "Exemple d'i18n\n" +msgstr "" +"Exemple d'i18n\n" "© 1998, 1999 Vadim Zeitlin et Julian Smart" #: internat.cpp:139 diff --git a/samples/internat/internat.cpp b/samples/internat/internat.cpp index 5f2646f9fe..abc4f0c6a8 100644 --- a/samples/internat/internat.cpp +++ b/samples/internat/internat.cpp @@ -94,7 +94,16 @@ enum INTERNAT_TEST_1, INTERNAT_TEST_2, INTERNAT_TEST_3, - INTERNAT_TEST_MSGBOX + INTERNAT_TEST_MSGBOX, + INTERNAT_MACRO_1, + INTERNAT_MACRO_2, + INTERNAT_MACRO_3, + INTERNAT_MACRO_4, + INTERNAT_MACRO_5, + INTERNAT_MACRO_6, + INTERNAT_MACRO_7, + INTERNAT_MACRO_8, + INTERNAT_MACRO_9 }; // language data @@ -308,9 +317,23 @@ MyFrame::MyFrame(wxLocale& locale) test_menu->Append(INTERNAT_TEST_MSGBOX, _("&Message box test"), _("Tests message box buttons labels translation")); + // Note that all these strings are currently "translated" only in French + // catalog, so you need to use French locale to see them in action. + wxMenu *macro_menu = new wxMenu; + macro_menu->Append(INTERNAT_MACRO_1, _("item")); + macro_menu->Append(INTERNAT_MACRO_2, wxGETTEXT_IN_CONTEXT("context_1", "item")); + macro_menu->Append(INTERNAT_MACRO_3, wxGETTEXT_IN_CONTEXT("context_2", "item")); + macro_menu->Append(INTERNAT_MACRO_4, wxPLURAL("sing", "plur", 1)); + macro_menu->Append(INTERNAT_MACRO_5, wxPLURAL("sing", "plur", 2)); + macro_menu->Append(INTERNAT_MACRO_6, wxGETTEXT_IN_CONTEXT_PLURAL("context_1", "sing", "plur", 1)); + macro_menu->Append(INTERNAT_MACRO_7, wxGETTEXT_IN_CONTEXT_PLURAL("context_1", "sing", "plur", 2)); + macro_menu->Append(INTERNAT_MACRO_8, wxGETTEXT_IN_CONTEXT_PLURAL("context_2", "sing", "plur", 1)); + macro_menu->Append(INTERNAT_MACRO_9, wxGETTEXT_IN_CONTEXT_PLURAL("context_2", "sing", "plur", 2)); + wxMenuBar *menu_bar = new wxMenuBar; menu_bar->Append(file_menu, _("&File")); menu_bar->Append(test_menu, _("&Test")); + menu_bar->Append(macro_menu, _("&Macro")); SetMenuBar(menu_bar); // this demonstrates RTL support in wxStatusBar: diff --git a/src/common/translation.cpp b/src/common/translation.cpp index 96df133112..21dc0daf50 100644 --- a/src/common/translation.cpp +++ b/src/common/translation.cpp @@ -1352,7 +1352,7 @@ wxMsgCatalog *wxMsgCatalog::CreateFromData(const wxScopedCharBuffer& data, return cat.release(); } -const wxString *wxMsgCatalog::GetString(const wxString& str, unsigned n) const +const wxString *wxMsgCatalog::GetString(const wxString& str, unsigned n, const wxString& context) const { int index = 0; if (n != UINT_MAX) @@ -1362,11 +1362,17 @@ const wxString *wxMsgCatalog::GetString(const wxString& str, unsigned n) const wxStringToStringHashMap::const_iterator i; if (index != 0) { - i = m_messages.find(wxString(str) + wxChar(index)); // plural + if (context.IsEmpty()) + i = m_messages.find(wxString(str) + wxChar(index)); // plural, no context + else + i = m_messages.find(wxString(context) + wxString('\x04') + wxString(str) + wxChar(index)); // plural, context } else { - i = m_messages.find(str); + if (context.IsEmpty()) + i = m_messages.find(str); // no context + else + i = m_messages.find(wxString(context) + wxString('\x04') + wxString(str)); // context } if ( i != m_messages.end() ) @@ -1631,14 +1637,16 @@ const wxString& wxTranslations::GetUntranslatedString(const wxString& str) const wxString *wxTranslations::GetTranslatedString(const wxString& origString, - const wxString& domain) const + const wxString& domain, + const wxString& context) const { - return GetTranslatedString(origString, UINT_MAX, domain); + return GetTranslatedString(origString, UINT_MAX, domain, context); } const wxString *wxTranslations::GetTranslatedString(const wxString& origString, unsigned n, - const wxString& domain) const + const wxString& domain, + const wxString& context) const { if ( origString.empty() ) return NULL; @@ -1652,14 +1660,14 @@ const wxString *wxTranslations::GetTranslatedString(const wxString& origString, // does the catalog exist? if ( pMsgCat != NULL ) - trans = pMsgCat->GetString(origString, n); + trans = pMsgCat->GetString(origString, n, context); } else { // search in all domains for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext ) { - trans = pMsgCat->GetString(origString, n); + trans = pMsgCat->GetString(origString, n, context); if ( trans != NULL ) // take the first found break; } @@ -1670,10 +1678,11 @@ const wxString *wxTranslations::GetTranslatedString(const wxString& origString, wxLogTrace ( TRACE_I18N, - "string \"%s\"%s not found in %slocale '%s'.", + "string \"%s\"%s not found in %s%slocale '%s'.", origString, (n != UINT_MAX ? wxString::Format("[%ld]", (long)n) : wxString()), (!domain.empty() ? wxString::Format("domain '%s' ", domain) : wxString()), + (!context.empty() ? wxString::Format("context '%s' ", context) : wxString()), m_lang ); } @@ -1681,7 +1690,6 @@ const wxString *wxTranslations::GetTranslatedString(const wxString& origString, return trans; } - wxString wxTranslations::GetHeaderValue(const wxString& header, const wxString& domain) const {