/********************************************************************** Audacity: A Digital Audio Editor @file TranslatableString.cpp Paul Licameli split from Internat.cpp **********************************************************************/ #include "TranslatableString.h" #include "Identifier.h" #include const wxChar *const TranslatableString::NullContextName = wxT("*"); Identifier TranslatableString::MSGID() const { return Identifier{ mMsgid }; } const TranslatableString::Formatter TranslatableString::NullContextFormatter { [](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 DoGetContext( mFormatter ) == NullContextName; } TranslatableString &TranslatableString::Strip( unsigned codes ) & { auto prevFormatter = mFormatter; mFormatter = [prevFormatter, codes] ( const wxString & str, TranslatableString::Request request ) -> wxString { switch ( request ) { case Request::Context: return TranslatableString::DoGetContext( prevFormatter ); case Request::Format: case Request::DebugFormat: default: { bool debug = request == Request::DebugFormat; auto result = TranslatableString::DoSubstitute( prevFormatter, str, TranslatableString::DoGetContext( prevFormatter ), debug ); if ( codes & MenuCodes ) { // Don't use this, it's in wxCore // result = wxStripMenuCodes( result ); decltype( result ) temp; temp.swap(result); for ( auto iter = temp.begin(), end = temp.end(); iter != end; ++iter ) { // Stop at trailing hot key name if ( *iter == '\t' ) break; // Strip & (unless escaped by another preceding &) if ( *iter == '&' && ++iter == end ) break; result.append( 1, *iter ); } } if ( codes & Ellipses ) { if (result.EndsWith(wxT("..."))) result = result.Left( result.length() - 3 ); // Also check for the single-character Unicode ellipsis else if (result.EndsWith(wxT("\u2026"))) result = result.Left( result.length() - 1 ); } return result; } } }; return *this; } wxString TranslatableString::DoGetContext( const Formatter &formatter ) { return formatter ? formatter( {}, Request::Context ) : wxString{}; } wxString TranslatableString::DoSubstitute( const Formatter &formatter, const wxString &format, const wxString &context, 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 #if HAS_I18N_CONTEXTS , wxString{}, context #endif ) ); } 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 #if HAS_I18N_CONTEXTS , wxString{} // domain , context #endif ); } TranslatableString &TranslatableString::Join( const TranslatableString arg, const wxString &separator ) & { auto prevFormatter = mFormatter; mFormatter = [prevFormatter, arg /* = std::move( arg ) */, separator](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 TranslatableString::DoSubstitute( prevFormatter, str, TranslatableString::DoGetContext( prevFormatter ), debug ) + separator + arg.DoFormat( debug ); } } }; return *this; } const TranslatableString TranslatableString::Inaudible{ wxT("\a") };