1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-12-02 06:40:12 +01:00

Move TranslatableString to new files

This commit is contained in:
Paul Licameli
2021-05-23 04:38:05 -04:00
parent 4d45054f4c
commit bb14a6016f
5 changed files with 84 additions and 1024 deletions

View File

@@ -50,7 +50,7 @@
#include <wx/debug.h> // for wxASSERT
#include <wx/string.h> // type used in inline function and member variable
#include "../../../src/Identifier.h"
#include "../../../src/TranslatableString.h"
// ----------------------------------------------------------------------------
// TODO: I'd imagine this header may be replaced by other public headers. But,
@@ -58,310 +58,6 @@
// until proper public headers are created for the stuff in here.
// ----------------------------------------------------------------------------
// Holds a msgid for the translation catalog and may hold a closure that
// captures formatting arguments
//
// Different string-valued accessors for the msgid itself, and for the
// user-visible translation with substitution of captured format arguments.
// Also an accessor for format substitution into the English msgid, for debug-
// only outputs.
// The msgid should be used only in unusual cases and the translation more often
//
// Implicit conversions to and from wxString are intentionally disabled
class AUDACITY_DLL_API TranslatableString {
enum class Request;
template< size_t N > struct PluralTemp;
public:
// A special string value that will have no screen reader pronunciation
static const TranslatableString Inaudible;
// A multi-purpose function, depending on the enum argument; the string
// argument is unused in some cases
// If there is no function, defaults are empty context string, no plurals,
// and no substitutions
using Formatter = std::function< wxString(const wxString &, Request) >;
TranslatableString() {}
// Supply {} for the second argument to cause lookup of the msgid with
// empty context string (default context) rather than the null context
explicit TranslatableString( wxString str, Formatter formatter )
: mFormatter{ std::move(formatter) }
{
mMsgid.swap( str );
}
// copy and move
TranslatableString( const TranslatableString & ) = default;
TranslatableString &operator=( const TranslatableString & ) = default;
TranslatableString( TranslatableString && str )
: mFormatter( std::move( str.mFormatter ) )
{
mMsgid.swap( str.mMsgid );
}
TranslatableString &operator=( TranslatableString &&str )
{
mFormatter = std::move( str.mFormatter );
mMsgid.swap( str.mMsgid );
return *this;
}
bool empty() const { return mMsgid.empty(); }
// MSGID is the English lookup key in the message catalog, not necessarily
// for user's eyes if the locale is some other.
// The MSGID might not be all the information TranslatableString holds.
// This is a deliberately ugly-looking function name. Use with caution.
Identifier MSGID() const { return Identifier{ mMsgid }; }
wxString Translation() const { return DoFormat( false ); }
// Format as an English string for debugging logs and developers' eyes, not
// for end users
wxString Debug() const { return DoFormat( true ); }
// Warning: comparison of msgids only, which is not all of the information!
// This operator makes it easier to define a std::unordered_map on
// TranslatableStrings
friend bool operator == (
const TranslatableString &x, const TranslatableString &y)
{ return x.mMsgid == y.mMsgid; }
friend bool operator != (
const TranslatableString &x, const TranslatableString &y)
{ return !(x == y); }
// Returns true if context is NullContextFormatter
bool IsVerbatim() const;
// Capture variadic format arguments (by copy) when there is no plural.
// The substitution is computed later in a call to Translate() after msgid is
// looked up in the translation catalog.
// Any format arguments that are also of type TranslatableString will be
// translated too at substitution time, for non-debug formatting
template< typename... Args >
TranslatableString &Format( Args &&...args ) &
{
auto prevFormatter = mFormatter;
this->mFormatter = [prevFormatter, args...]
(const wxString &str, Request request) -> wxString {
switch ( request ) {
case Request::Context:
return TranslatableString::DoGetContext( prevFormatter );
case Request::Format:
case Request::DebugFormat:
default: {
bool debug = request == Request::DebugFormat;
return wxString::Format(
TranslatableString::DoSubstitute(
prevFormatter,
str, TranslatableString::DoGetContext( prevFormatter ),
debug ),
TranslatableString::TranslateArgument( args, debug )...
);
}
}
};
return *this;
}
template< typename... Args >
TranslatableString &&Format( Args &&...args ) &&
{
return std::move( Format( std::forward<Args>(args)... ) );
}
// Choose a non-default and non-null disambiguating context for lookups
// (but this is not fully implemented)
// This is meant to be the first of chain-call modifications of the
// TranslatableString object; it will destroy any previously captured
// information
TranslatableString &Context( const wxString &context ) &
{
this->mFormatter = [context]
(const wxString &str, Request request) -> wxString {
switch ( request ) {
case Request::Context:
return context;
case Request::DebugFormat:
return DoSubstitute( {}, str, context, true );
case Request::Format:
default:
return DoSubstitute( {}, str, context, false );
}
};
return *this;
}
TranslatableString &&Context( const wxString &context ) &&
{
return std::move( Context( context ) );
}
// Append another translatable string; lookup of msgids for
// this and for the argument are both delayed until Translate() is invoked
// on this, and then the formatter concatenates the translations
TranslatableString &Join(
TranslatableString arg, const wxString &separator = {} ) &;
TranslatableString &&Join(
TranslatableString arg, const wxString &separator = {} ) &&
{ return std::move( Join( std::move(arg), separator ) ); }
TranslatableString &operator +=( TranslatableString arg )
{
Join( std::move( arg ) );
return *this;
}
// Implements the XP macro, which specifies a second msgid, a list
// of format arguments, and which of those format arguments selects among
// messages; the translated strings to select among, depending on language,
// might actually be more or fewer than two. See Internat.h.
template< size_t N >
PluralTemp< N > Plural( const wxString &pluralStr ) &&
{
return PluralTemp< N >{ *this, pluralStr };
}
// Translated strings may still contain menu hot-key codes (indicated by &)
// that wxWidgets interprets, and also trailing ellipses, that should be
// removed for other uses.
enum StripOptions : unsigned {
// Values to be combined with bitwise OR
MenuCodes = 0x1,
Ellipses = 0x2,
};
TranslatableString &Strip( unsigned options = MenuCodes ) &;
TranslatableString &&Strip( unsigned options = MenuCodes ) &&
{ return std::move( Strip( options ) ); }
// non-mutating, constructs another TranslatableString object
TranslatableString Stripped( unsigned options = MenuCodes ) const
{ return TranslatableString{ *this }.Strip( options ); }
wxString StrippedTranslation() const { return Stripped().Translation(); }
private:
static const Formatter NullContextFormatter;
// Construct a TranslatableString that does no translation but passes
// str verbatim
explicit TranslatableString( wxString str )
: mFormatter{ NullContextFormatter }
{
mMsgid.swap( str );
}
friend TranslatableString Verbatim( wxString str );
enum class Request {
Context, // return a disambiguating context string
Format, // Given the msgid, format the string for end users
DebugFormat, // Given the msgid, format the string for developers
};
static const wxChar *const NullContextName;
friend std::hash< TranslatableString >;
static wxString DoGetContext( const Formatter &formatter );
static wxString DoSubstitute(
const Formatter &formatter,
const wxString &format, const wxString &context, bool debug );
wxString DoFormat( bool debug ) const
{ return DoSubstitute(
mFormatter, mMsgid, DoGetContext(mFormatter), debug ); }
static wxString DoChooseFormat(
const Formatter &formatter,
const wxString &singular, const wxString &plural, unsigned nn, bool debug );
template< typename T > static const T &TranslateArgument( const T &arg, bool )
{ return arg; }
// This allows you to wrap arguments of Format in std::cref so that they
// are captured (as if) by reference rather than by value
template< typename T > static auto TranslateArgument(
const std::reference_wrapper<T> &arg, bool debug )
-> decltype(
TranslatableString::TranslateArgument( arg.get(), debug ) )
{ return TranslatableString::TranslateArgument( arg.get(), debug ); }
static wxString TranslateArgument( const TranslatableString &arg, bool debug )
{ return arg.DoFormat( debug ); }
template< size_t N > struct PluralTemp{
TranslatableString &ts;
const wxString &pluralStr;
template< typename... Args >
TranslatableString &&operator()( Args&&... args )
{
// Pick from the pack the argument that specifies number
auto selector =
std::template get< N >( std::forward_as_tuple( args... ) );
// We need an unsigned value. Guard against negative values.
auto nn = static_cast<unsigned>(
std::max<unsigned long long>( 0, selector )
);
auto plural = this->pluralStr;
auto prevFormatter = this->ts.mFormatter;
this->ts.mFormatter = [prevFormatter, plural, nn, args...]
(const wxString &str, Request request) -> wxString {
switch ( request ) {
case Request::Context:
return TranslatableString::DoGetContext( prevFormatter );
case Request::Format:
case Request::DebugFormat:
default:
{
bool debug = request == Request::DebugFormat;
return wxString::Format(
TranslatableString::DoChooseFormat(
prevFormatter, str, plural, nn, debug ),
TranslatableString::TranslateArgument( args, debug )...
);
}
}
};
return std::move(ts);
}
};
wxString mMsgid;
Formatter mFormatter;
};
inline TranslatableString operator +(
TranslatableString x, TranslatableString y )
{
return std::move(x += std::move(y));
}
using TranslatableStrings = std::vector<TranslatableString>;
// For using std::unordered_map on TranslatableString
// Note: hashing on msgids only, which is not all of the information
namespace std
{
template<> struct hash< TranslatableString > {
size_t operator () (const TranslatableString &str) const // noexcept
{
const wxString &stdstr = str.mMsgid.ToStdWstring(); // no allocations, a cheap fetch
using Hasher = hash< wxString >;
return Hasher{}( stdstr );
}
};
}
// Allow TranslatableString to work with shift output operators
template< typename Sink >
inline Sink &operator <<( Sink &sink, const TranslatableString &str )
{
return sink << str.Translation();
}
// Require calls to the one-argument constructor to go through this
// distinct global function name. This makes it easier to locate and
// review the uses of this function, separately from the uses of the type.
inline TranslatableString Verbatim( wxString str )
{ return TranslatableString( std::move( str ) ); }
// ----------------------------------------------------------------------------
// A native 64-bit integer...used when referring to any number of samples
// ----------------------------------------------------------------------------