1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-10-20 17:41:13 +02:00

Rewrites of TranslatableString and reimplementation of wxPLURAL...

... including move-construction of the base string, debug string formatting,
and contexts (not fully implemented)
This commit is contained in:
Paul Licameli
2019-12-07 11:05:45 -05:00
parent 9d05fc0c7d
commit 2e3ba2204f
6 changed files with 254 additions and 95 deletions

View File

@@ -291,51 +291,80 @@ std::vector< Identifier > Identifier::split( wxChar separator ) const
return { strings.begin(), strings.end() };
}
static const wxChar *const NullContextName = wxT("*");
const wxChar *const TranslatableString::NullContextName = wxT("*");
const TranslatableString::Formatter
TranslatableString::NullContextFormatter {
[](const wxString & str) -> wxString {
if (str.empty())
return NullContextName;
else
return str;
[](const wxString & str, TranslatableString::Request request) -> wxString {
switch ( request ) {
case Request::Context:
return NullContextName;
case Request::Format:
case Request::DebugFormat:
default:
return str;
}
}
};
bool TranslatableString::IsVerbatim() const
{
return mFormatter && mFormatter({}) == NullContextName;
return DoGetContext( mFormatter ) == NullContextName;
}
wxString TranslatableString::Translation() const
wxString TranslatableString::DoGetContext( const Formatter &formatter )
{
wxString context;
if ( mFormatter )
context = mFormatter({});
wxString result = (context == NullContextName)
? *this
: wxGetTranslation( *this
// , wxString{}, context
);
if ( mFormatter )
result = mFormatter( result );
return result;
return formatter ? formatter( {}, Request::Context ) : wxString{};
}
TranslatableString &TranslatableString::operator +=(
const TranslatableString &arg )
wxString TranslatableString::DoFormat(
const Formatter &formatter, const wxString &format, bool debug )
{
return formatter
? formatter( format, debug ? Request::DebugFormat : Request::Format )
: // come here for most translatable strings, which have no formatting
( debug ? format : wxGetTranslation( format ) );
}
wxString TranslatableString::DoChooseFormat(
const Formatter &formatter,
const wxString &singular, const wxString &plural, unsigned nn, bool debug )
{
// come here for translatable strings that choose among forms by number;
// if not debugging, then two keys are passed to an overload of
// wxGetTranslation, and also a number.
// Some languages might choose among more or fewer than two forms
// (e.g. Arabic has duals and Russian has complicated declension rules)
wxString context;
return ( debug || NullContextName == (context = DoGetContext(formatter)) )
? ( nn == 1 ? singular : plural )
: wxGetTranslation(
singular, plural, nn
// , wxString{}
// , context
);
}
TranslatableString &&TranslatableString::Join(
const TranslatableString &arg, const wxString &separator ) &&
{
auto prevFormatter = mFormatter;
mFormatter = [prevFormatter, arg](const wxString &str){
if (str.empty())
return prevFormatter ? prevFormatter({}) : wxString{};
else
return (prevFormatter ? prevFormatter(str) : str)
+ arg.Translation();
mFormatter =
[prevFormatter, arg, separator](const wxString &str, Request request)
-> wxString {
switch ( request ) {
case Request::Context:
return DoGetContext( prevFormatter );
case Request::Format:
case Request::DebugFormat:
default: {
bool debug = request == Request::DebugFormat;
return
DoFormat( prevFormatter, str, debug )
+ separator
+ arg.DoFormat( debug );
}
}
};
return *this;
return std::move( *this );
}

View File

@@ -78,7 +78,12 @@ extern AUDACITY_DLL_API const wxString& GetCustomSubstitution(const wxString& st
//
// Your i18n-comment should therefore say something like,
// "In the string after this one, ..."
#define wxPLURAL(sing, plur, n) wxGetTranslation( wxT(sing), wxT(plur), n)
//
// The macro call is then followed by a sequence of format arguments in
// parentheses. The third argument of the macro call is the zero-based index
// of the format argument that selects singular or plural
#define wxPLURAL(sing, plur, n) \
TranslatableString{ wxT(sing), {} }.Plural<(n)>( wxT(plur) )
#endif

View File

@@ -1899,7 +1899,11 @@ bool PluginManager::DropFile(const wxString &fileName)
// Ask whether to enable the plug-ins
if (auto nIds = ids.size()) {
auto message = wxPLURAL( "Enable this plug-in?", "Enable these plug-ins?", nIds );
auto message = wxPLURAL(
"Enable this plug-in?",
"Enable these plug-ins?",
0
)( nIds ).Translation();
message += wxT("\n");
for (const auto &name : names)
message += name + wxT("\n");

View File

@@ -1041,10 +1041,9 @@ wxString ProjectManager::GetHoursMinsString(int iMinutes)
int iHours = iMinutes / 60;
int iMins = iMinutes % 60;
auto sHours =
wxString::Format( wxPLURAL("%d hour", "%d hours", iHours), iHours );
auto sMins =
wxString::Format( wxPLURAL("%d minute", "%d minutes", iMins), iMins );
auto sHours = wxPLURAL( "%d hour", "%d hours", 0 )( iHours ).Translation();
auto sMins = wxPLURAL( "%d minute", "%d minutes", 0 )( iMins ).Translation();
/* i18n-hint: A time in hours and minutes. Only translate the "and". */
sFormatted.Printf( _("%s and %s."), sHours, sMins);

View File

@@ -327,17 +327,16 @@ wxString ClipBoundaryMessage(const std::vector<FoundClipBoundary>& results)
clips.
*/
_("dummyStringClipBoundaryMessage");
auto format = wxPLURAL(
auto str = wxPLURAL(
"%s %d of %d clip %s",
"%s %d of %d clips %s",
nClips
);
str = wxString::Format(format,
2
)(
result.clipStart1 ? _("start") : _("end"),
result.index1 + 1,
nClips,
longName
);
).Translation();
}
else {
/* i18n-hint: in the string after this one,
@@ -350,19 +349,18 @@ wxString ClipBoundaryMessage(const std::vector<FoundClipBoundary>& results)
clips.
*/
_("dummyStringClipBoundaryMessageLong");
auto format = wxPLURAL(
auto str = wxPLURAL(
"%s %d and %s %d of %d clip %s",
"%s %d and %s %d of %d clips %s",
nClips
);
str = wxString::Format(format,
4
)(
result.clipStart1 ? _("start") : _("end"),
result.index1 + 1,
result.clipStart2 ? _("start") : _("end"),
result.index2 + 1,
nClips,
longName
);
).Translation();
}
if (message.empty())
@@ -587,13 +585,15 @@ void DoSelectClip(AudacityProject &project, bool next)
last number counts the clips,
string names a track */
_("dummyStringOnSelectClip");
auto format = wxPLURAL(
auto str = wxPLURAL(
"%d of %d clip %s",
"%d of %d clips %s",
nClips
);
auto str =
wxString::Format( format, result.index + 1, nClips, longName );
1
)(
result.index + 1,
nClips,
longName
).Translation();
if (message.empty())
message = str;