diff --git a/src/banman.cpp b/src/banman.cpp index 2dbfc76df2dc9..2964a37de0173 100644 --- a/src/banman.cpp +++ b/src/banman.cpp @@ -30,7 +30,7 @@ void BanMan::LoadBanlist() { LOCK(m_banned_mutex); - if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist…").translated); + if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist…")); const auto start{SteadyClock::now()}; if (m_ban_db.Read(m_banned)) { diff --git a/src/clientversion.cpp b/src/clientversion.cpp index af58c3023f570..10e9472b84ba6 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -95,7 +95,7 @@ std::string LicenseInfo() strprintf(_("The source code is available from %s."), URL_SOURCE_CODE).translated + "\n" + "\n" + - _("This is experimental software.").translated + "\n" + + _("This is experimental software.") + "\n" + strprintf(_("Distributed under the MIT software license, see the accompanying file %s or %s"), "COPYING", "").translated + "\n"; } diff --git a/src/init.cpp b/src/init.cpp index cb4ff733a5009..9887c071653f6 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1244,8 +1244,8 @@ static ChainstateLoadResult InitAndLoadChainstate( _("Error reading from database, shutting down."), "", CClientUIInterface::MSG_ERROR); }; - uiInterface.InitMessage(_("Loading block index…").translated); - auto catch_exceptions = [](auto&& f) { + uiInterface.InitMessage(_("Loading block index…")); + auto catch_exceptions = [](auto&& f) -> ChainstateLoadResult { try { return f(); } catch (const std::exception& e) { @@ -1255,7 +1255,7 @@ static ChainstateLoadResult InitAndLoadChainstate( }; auto [status, error] = catch_exceptions([&] { return LoadChainstate(chainman, cache_sizes, options); }); if (status == node::ChainstateLoadStatus::SUCCESS) { - uiInterface.InitMessage(_("Verifying blocks…").translated); + uiInterface.InitMessage(_("Verifying blocks…")); if (chainman.m_blockman.m_have_pruned && options.check_blocks > MIN_BLOCKS_TO_KEEP) { LogWarning("pruned datadir may not have more than %d blocks; only checking available blocks\n", MIN_BLOCKS_TO_KEEP); @@ -1418,7 +1418,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // Initialize addrman assert(!node.addrman); - uiInterface.InitMessage(_("Loading P2P addresses…").translated); + uiInterface.InitMessage(_("Loading P2P addresses…")); auto addrman{LoadAddrman(*node.netgroupman, args)}; if (!addrman) return InitError(util::ErrorString(addrman)); node.addrman = std::move(*addrman); @@ -1703,7 +1703,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) if (chainman.m_blockman.m_blockfiles_indexed) { LOCK(cs_main); for (Chainstate* chainstate : chainman.GetAll()) { - uiInterface.InitMessage(_("Pruning blockstore…").translated); + uiInterface.InitMessage(_("Pruning blockstore…")); chainstate->PruneAndFlush(); } } @@ -1999,7 +1999,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // ChainstateManager's active tip. SetRPCWarmupFinished(); - uiInterface.InitMessage(_("Done loading").translated); + uiInterface.InitMessage(_("Done loading")); for (const auto& client : node.chain_clients) { client->start(scheduler); diff --git a/src/net.cpp b/src/net.cpp index 8ea7f6ce44508..47ef2d22d7d3d 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -3296,7 +3296,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) } if (m_client_interface) { - m_client_interface->InitMessage(_("Starting network threads…").translated); + m_client_interface->InitMessage(_("Starting network threads…")); } fAddressesInitialized = true; diff --git a/src/util/translation.h b/src/util/translation.h index 7c734a176631f..c58f7c26619e9 100644 --- a/src/util/translation.h +++ b/src/util/translation.h @@ -6,12 +6,15 @@ #define BITCOIN_UTIL_TRANSLATION_H #include +#include +#include #include #include /** Translate a message to the native language of the user. */ -const extern std::function G_TRANSLATION_FUN; +using TranslateFn = std::function; +const extern TranslateFn G_TRANSLATION_FUN; /** * Bilingual messages: @@ -47,6 +50,27 @@ inline bilingual_str operator+(bilingual_str lhs, const bilingual_str& rhs) return lhs; } +namespace util { +//! Compile-time literal string that can be translated with an optional translation function. +struct TranslatedLiteral { + const char* const original; + const TranslateFn* translate_fn; + + consteval TranslatedLiteral(const char* str, const TranslateFn* fn = &G_TRANSLATION_FUN) : original{str}, translate_fn{fn} { assert(original); } + operator std::string() const { return translate_fn && *translate_fn ? (*translate_fn)(original) : original; } + operator bilingual_str() const { return {original, std::string{*this}}; } +}; + +// TranslatedLiteral operators for formatting and adding to strings. +inline std::ostream& operator<<(std::ostream& os, const TranslatedLiteral& lit) { return os << std::string{lit}; } +template +T operator+(const T& lhs, const TranslatedLiteral& rhs) { return lhs + static_cast(rhs); } +template +T operator+(const TranslatedLiteral& lhs, const T& rhs) { return static_cast(lhs) + rhs; } +} // namespace util + +consteval auto _(util::TranslatedLiteral str) { return str; } + /** Mark a bilingual_str as untranslated */ inline bilingual_str Untranslated(std::string original) { return {original, original}; } @@ -67,19 +91,4 @@ bilingual_str format(const bilingual_str& fmt, const Args&... args) } } // namespace tinyformat -struct ConstevalStringLiteral { - const char* const lit; - consteval ConstevalStringLiteral(const char* str) : lit{str} {} - consteval ConstevalStringLiteral(std::nullptr_t) = delete; -}; - -/** - * Translation function. - * If no translation function is set, simply return the input. - */ -inline bilingual_str _(ConstevalStringLiteral str) -{ - return bilingual_str{str.lit, G_TRANSLATION_FUN ? (G_TRANSLATION_FUN)(str.lit) : str.lit}; -} - #endif // BITCOIN_UTIL_TRANSLATION_H diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp index 2b5c021cda451..e77999b1115fb 100644 --- a/src/wallet/load.cpp +++ b/src/wallet/load.cpp @@ -52,7 +52,7 @@ bool VerifyWallets(WalletContext& context) LogPrintf("Using wallet directory %s\n", fs::PathToString(GetWalletDir())); - chain.initMessage(_("Verifying wallet(s)…").translated); + chain.initMessage(_("Verifying wallet(s)…")); // For backwards compatibility if an unnamed top level wallet exists in the // wallets directory, include it in the default list of wallets to load. @@ -135,7 +135,7 @@ bool LoadWallets(WalletContext& context) if (!database && status == DatabaseStatus::FAILED_NOT_FOUND) { continue; } - chain.initMessage(_("Loading wallet…").translated); + chain.initMessage(_("Loading wallet…")); std::shared_ptr pwallet = database ? CWallet::Create(context, name, std::move(database), options.create_flags, error, warnings) : nullptr; if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n"))); if (!pwallet) { diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp index bfd7249c046df..79f1a40ec5a02 100644 --- a/src/wallet/rpc/backup.cpp +++ b/src/wallet/rpc/backup.cpp @@ -534,7 +534,7 @@ RPCHelpMan importwallet() // Use uiInterface.ShowProgress instead of pwallet.ShowProgress because pwallet.ShowProgress has a cancel button tied to AbortRescan which // we don't want for this progress bar showing the import progress. uiInterface.ShowProgress does not have a cancel button. - pwallet->chain().showProgress(strprintf("%s %s", pwallet->GetDisplayName(), _("Importing…").translated), 0, false); // show progress dialog in GUI + pwallet->chain().showProgress(strprintf("%s %s", pwallet->GetDisplayName(), _("Importing…")), 0, false); // show progress dialog in GUI std::vector> keys; std::vector> scripts; while (file.good()) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e7e06aec01a23..ea59f1a9d57c5 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -280,7 +280,7 @@ std::shared_ptr LoadWalletInternal(WalletContext& context, const std::s return nullptr; } - context.chain->initMessage(_("Loading wallet…").translated); + context.chain->initMessage(_("Loading wallet…")); std::shared_ptr wallet = CWallet::Create(context, name, std::move(database), options.create_flags, error, warnings); if (!wallet) { error = Untranslated("Wallet loading failed.") + Untranslated(" ") + error; @@ -430,7 +430,7 @@ std::shared_ptr CreateWallet(WalletContext& context, const std::string& } // Make the wallet - context.chain->initMessage(_("Loading wallet…").translated); + context.chain->initMessage(_("Loading wallet…")); std::shared_ptr wallet = CWallet::Create(context, name, std::move(database), wallet_creation_flags, error, warnings); if (!wallet) { error = Untranslated("Wallet creation failed.") + Untranslated(" ") + error; @@ -1903,7 +1903,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc fast_rescan_filter ? "fast variant using block filters" : "slow variant inspecting all blocks"); fAbortRescan = false; - ShowProgress(strprintf("%s %s", GetDisplayName(), _("Rescanning…").translated), 0); // show rescan progress in GUI as dialog or on splashscreen, if rescan required on startup (e.g. due to corruption) + ShowProgress(strprintf("%s %s", GetDisplayName(), _("Rescanning…")), 0); // show rescan progress in GUI as dialog or on splashscreen, if rescan required on startup (e.g. due to corruption) uint256 tip_hash = WITH_LOCK(cs_wallet, return GetLastBlockHash()); uint256 end_hash = tip_hash; if (max_height) chain().findAncestorByHeight(tip_hash, *max_height, FoundBlock().hash(end_hash)); @@ -1918,7 +1918,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc m_scanning_progress = 0; } if (block_height % 100 == 0 && progress_end - progress_begin > 0.0) { - ShowProgress(strprintf("%s %s", GetDisplayName(), _("Rescanning…").translated), std::max(1, std::min(99, (int)(m_scanning_progress * 100)))); + ShowProgress(strprintf("%s %s", GetDisplayName(), _("Rescanning…")), std::max(1, std::min(99, (int)(m_scanning_progress * 100)))); } bool next_interval = reserver.now() >= current_time + INTERVAL_TIME; @@ -2015,7 +2015,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc WalletLogPrintf("Scanning current mempool transactions.\n"); WITH_LOCK(cs_wallet, chain().requestMempoolTransactions(*this)); } - ShowProgress(strprintf("%s %s", GetDisplayName(), _("Rescanning…").translated), 100); // hide progress dialog in GUI + ShowProgress(strprintf("%s %s", GetDisplayName(), _("Rescanning…")), 100); // hide progress dialog in GUI if (block_height && fAbortRescan) { WalletLogPrintf("Rescan aborted at block %d. Progress=%f\n", block_height, progress_current); result.status = ScanResult::USER_ABORT; @@ -3353,7 +3353,7 @@ bool CWallet::AttachChain(const std::shared_ptr& walletInstance, interf } } - chain.initMessage(_("Rescanning…").translated); + chain.initMessage(_("Rescanning…")); walletInstance->WalletLogPrintf("Rescanning last %i blocks (from block %i)...\n", *tip_height - rescan_height, rescan_height); {