diff --git a/locale/update_po_files.sh b/locale/update_po_files.sh index 143c548bf..7ded141d0 100755 --- a/locale/update_po_files.sh +++ b/locale/update_po_files.sh @@ -5,7 +5,7 @@ sed -E 's/\.\.\///g' |\ xargs xgettext \ --default-domain=audacity \ --directory=.. \ ---keyword=_ --keyword=XO --keyword=XXO --keyword=XP:1,2 \ +--keyword=_ --keyword=XO --keyword=XC:1,2c --keyword=XXO --keyword=XP:1,2 --keyword=XPC:1,2,4c \ --add-comments=" i18n" \ --add-location=file \ --copyright-holder='Audacity Team' \ @@ -19,7 +19,7 @@ sed -E 's/\.\.\///g' |\ xargs xgettext \ --default-domain=audacity \ --directory=.. \ ---keyword=_ --keyword=ngettext:1,2 \ +--keyword=_ --keyword=_C:1,2c --keyword=ngettext:1,2 --keyword=ngettextc:1,2,4c \ --add-comments=" i18n" \ --add-location=file \ --copyright-holder='Audacity Team' \ diff --git a/src/Internat.cpp b/src/Internat.cpp index 8eb1d8cc1..b92054507 100644 --- a/src/Internat.cpp +++ b/src/Internat.cpp @@ -363,7 +363,7 @@ wxString TranslatableString::DoChooseFormat( ? ( nn == 1 ? singular : plural ) : wxGetTranslation( singular, plural, nn -#if wxCHECK_VERSION(3, 1, 3) +#if HAS_I18N_CONTEXTS , wxString{} // domain , context #endif diff --git a/src/Internat.h b/src/Internat.h index a6aa0b26a..31f529c81 100644 --- a/src/Internat.h +++ b/src/Internat.h @@ -32,7 +32,13 @@ extern AUDACITY_DLL_API const wxString& GetCustomSubstitution(const wxString& st // '&', preceding menu accelerators, should NOT occur in the argument. #define XO(s) (TranslatableString{ wxT(s), {} }) -// Marks strings for extraction only, where '&', preceding men accelerators, MAY +// Alternative taking a second context argument. A context is a string literal, +// which is not translated, but serves to disambiguate uses of the first string +// that might need differing translations, such as "Light" meaning not-heavy in +// one place but not-dark elsewhere. +#define XC(s, c) (TranslatableString{ wxT(s), {} }.Context(c)) + +// Marks strings for extraction only, where '&', preceding menu accelerators, MAY // occur. // For now, expands exactly as macro XO does, but in future there will be a // type distinction - for example XXO should be used for menu item names that @@ -86,6 +92,10 @@ extern AUDACITY_DLL_API const wxString& GetCustomSubstitution(const wxString& st #define XP(sing, plur, n) \ TranslatableString{ wxT(sing), {} }.Plural<(n)>( wxT(plur) ) +// Like XP but with an additional context argument, as for XC +#define XPC(sing, plur, n, c) \ + TranslatableString{ wxT(sing), {} }.Context(c).Plural<(n)>( wxT(plur) ) + #endif class Internat @@ -151,4 +161,9 @@ TranslatableStrings Msgids( const EnumValueSymbol strings[], size_t nStrings); TranslatableStrings Msgids( const std::vector &strings ); +// Whether disambiguationg contexts are supported +// If not, then the program builds and runs, but strings in the catalog with +// contexts will fail to translate +#define HAS_I18N_CONTEXTS wxCHECK_VERSION(3, 1, 1) + #endif diff --git a/src/PluginManager.cpp b/src/PluginManager.cpp index a05e2842d..bae6be3ec 100644 --- a/src/PluginManager.cpp +++ b/src/PluginManager.cpp @@ -1897,10 +1897,13 @@ bool PluginManager::DropFile(const wxString &fileName) // Ask whether to enable the plug-ins if (auto nIds = ids.size()) { - auto message = XP( + auto message = XPC( + /* i18n-hint A plug-in is an optional added program for a sound + effect, or generator, or analyzer */ "Enable this plug-in?\n", "Enable these plug-ins?\n", - 0 + 0, + "plug-ins" )( nIds ); for (const auto &name : names) message.Join( Verbatim( name ), wxT("\n") ); diff --git a/src/commands/ScreenshotCommand.cpp b/src/commands/ScreenshotCommand.cpp index f28b34901..ac8e98307 100644 --- a/src/commands/ScreenshotCommand.cpp +++ b/src/commands/ScreenshotCommand.cpp @@ -94,7 +94,8 @@ kBackgroundStrings[ ScreenshotCommand::nBackgrounds ] = { // These are acceptable dual purpose internal/visible names { XO("Blue") }, - { XO("White") }, + /* i18n-hint: This really means the color, not as in "white noise" */ + { XC("White", "color") }, { XO("None") }, }; diff --git a/src/effects/Noise.cpp b/src/effects/Noise.cpp index b27698e7b..fdf4ce665 100644 --- a/src/effects/Noise.cpp +++ b/src/effects/Noise.cpp @@ -41,9 +41,13 @@ enum kTypes static const EnumValueSymbol kTypeStrings[nTypes] = { // These are acceptable dual purpose internal/visible names - { XO("White") }, - { XO("Pink") }, - { XO("Brownian") } + /* i18n-hint: not a color, but "white noise" having a uniform spectrum */ + { XC("White", "noise") }, + /* i18n-hint: not a color, but "pink noise" having a spectrum with more power + in low frequencies */ + { XC("Pink", "noise") }, + /* i18n-hint: a kind of noise spectrum also known as "red" or "brown" */ + { XC("Brownian", "noise") } }; // Define keys, defaults, minimums, and maximums for the effect parameters diff --git a/src/effects/nyquist/Nyquist.cpp b/src/effects/nyquist/Nyquist.cpp index e11af3c8a..c692d9c24 100644 --- a/src/effects/nyquist/Nyquist.cpp +++ b/src/effects/nyquist/Nyquist.cpp @@ -1443,8 +1443,8 @@ bool NyquistEffect::ProcessOne() if (rval == nyx_string) { // Assume the string has already been translated within the Lisp runtime - // if necessary, by gettext or ngettext defined below, before it is - // communicated back to C++ + // if necessary, by one of the gettext functions defined below, before it + // is communicated back to C++ auto msg = Verbatim( NyquistToWxString(nyx_get_string()) ); if (!msg.empty()) { // Empty string may be used as a No-Op return value. Effect::MessageBox( msg ); @@ -3332,20 +3332,58 @@ void NyquistOutputDialog::OnOk(wxCommandEvent & /* event */) static LVAL gettext() { auto string = UTF8CTOWX(getstring(xlgastring())); +#if !HAS_I18N_CONTEXTS + // allow ignored context argument + if ( moreargs() ) + nextarg(); +#endif xllastarg(); return cvstring(GetCustomTranslation(string).mb_str(wxConvUTF8)); } +static LVAL gettextc() +{ +#if HAS_I18N_CONTEXTS + auto string = UTF8CTOWX(getstring(xlgastring())); + auto context = UTF8CTOWX(getstring(xlgastring())); + xllastarg(); + return cvstring(wxGetTranslation( string, "", 0, "", context ) + .mb_str(wxConvUTF8)); +#else + return gettext(); +#endif +} + static LVAL ngettext() { auto string1 = UTF8CTOWX(getstring(xlgastring())); auto string2 = UTF8CTOWX(getstring(xlgastring())); auto number = getfixnum(xlgafixnum()); +#if !HAS_I18N_CONTEXTS + // allow ignored context argument + if ( moreargs() ) + nextarg(); +#endif xllastarg(); return cvstring( wxGetTranslation(string1, string2, number).mb_str(wxConvUTF8)); } +static LVAL ngettextc() +{ +#if HAS_I18N_CONTEXTS + auto string1 = UTF8CTOWX(getstring(xlgastring())); + auto string2 = UTF8CTOWX(getstring(xlgastring())); + auto number = getfixnum(xlgafixnum()); + auto context = UTF8CTOWX(getstring(xlgastring())); + xllastarg(); + return cvstring(wxGetTranslation( string1, string2, number, "", context ) + .mb_str(wxConvUTF8)); +#else + return ngettext(); +#endif +} + /*--------------------Audacity Automation -------------------------*/ /* These functions may later move to their own source file. */ extern void * ExecForLisp( char * pIn ); @@ -3413,8 +3451,9 @@ static void RegisterFunctions() // All function names must be UP-CASED static const FUNDEF functions[] = { { "_", SUBR, gettext }, - // to do: more i18n functions, for contexts and plurals + { "_C", SUBR, gettextc }, { "NGETTEXT", SUBR, ngettext }, + { "NGETTEXTC", SUBR, ngettextc }, { "AUD-DO", SUBR, xlc_aud_do }, };