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:
@@ -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 );
|
||||
}
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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");
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user