diff options
author | MarcoFalke <falke.marco@gmail.com> | 2020-05-08 12:17:47 -0400 |
---|---|---|
committer | MarcoFalke <falke.marco@gmail.com> | 2020-05-08 12:17:55 -0400 |
commit | 5b24f6084ede92d0f493ff416b4726245140b2c1 (patch) | |
tree | 64f8f9a29912e0f4a4466daa65403c37945b538a | |
parent | 3930014abcdd8f28a213e119605f68508aae1816 (diff) | |
parent | 18bd83b1fee2eb47ed4ad05c91f2d6cc311fc9ad (diff) |
Merge #16224: gui: Bilingual GUI error messages
18bd83b1fee2eb47ed4ad05c91f2d6cc311fc9ad util: Cleanup translation.h (Hennadii Stepanov)
e95e658b8ec6e02229691a1941d688e96d4df6af doc: Do not translate technical or extremely rare errors (Hennadii Stepanov)
7e923d47ba9891856b86bc9f718cf2f1f773bdf6 Make InitError bilingual (Hennadii Stepanov)
917ca93553917251e0fd59717a347c63cdfd8a14 Make ThreadSafe{MessageBox|Question} bilingual (Hennadii Stepanov)
23b9fa2e5ec0425980301d2eebad81e660a5ea39 gui: Add detailed text to BitcoinGUI::message (Hennadii Stepanov)
Pull request description:
This is an alternative to #15340 (it works with the `Chain` interface; see: https://github.com/bitcoin/bitcoin/pull/15340#issuecomment-502674004).
Refs:
- #16218 (partial fix)
- https://github.com/bitcoin/bitcoin/pull/15894#issuecomment-487947077
This PR:
- makes GUI error messages bilingual: user's native language + untranslated (i.e. English)
- insures that only untranslated messages are written to the debug log file and to `stderr` (that is not the case on master).
If a translated string is unavailable only an English string appears to a user.
Here are some **examples** (updated):
![Screenshot from 2020-04-24 17-08-37](https://user-images.githubusercontent.com/32963518/80222043-e2458780-864e-11ea-83fc-197b7121dba5.png)
![Screenshot from 2020-04-24 17-12-17](https://user-images.githubusercontent.com/32963518/80222051-e5407800-864e-11ea-92f7-dfef1144becd.png)
* `qt5ct: using qt5ct plugin` message is my local environment specific; please ignore it.
---
Note for reviewers: `InitWarning()` is out of this PR scope.
ACKs for top commit:
Sjors:
re-tACK 18bd83b1fee2eb47ed4ad05c91f2d6cc311fc9ad
MarcoFalke:
ACK 18bd83b1fee2eb47ed4ad05c91f2d6cc311fc9ad 🐦
Tree-SHA512: 3cc8ec44f84403e54b57d11714c86b0855ed90eb794b5472e432005073354b9e3f7b4e8e7bf347a4c21be47299dbc7170f2d0c4b80e308205ff09596e55a4f96
-rw-r--r-- | doc/translation_strings_policy.md | 3 | ||||
-rw-r--r-- | src/bitcoind.cpp | 14 | ||||
-rw-r--r-- | src/httprpc.cpp | 2 | ||||
-rw-r--r-- | src/httpserver.cpp | 9 | ||||
-rw-r--r-- | src/index/base.cpp | 3 | ||||
-rw-r--r-- | src/init.cpp | 125 | ||||
-rw-r--r-- | src/interfaces/chain.cpp | 2 | ||||
-rw-r--r-- | src/interfaces/chain.h | 3 | ||||
-rw-r--r-- | src/interfaces/node.cpp | 3 | ||||
-rw-r--r-- | src/interfaces/node.h | 4 | ||||
-rw-r--r-- | src/net.cpp | 27 | ||||
-rw-r--r-- | src/net.h | 5 | ||||
-rw-r--r-- | src/noui.cpp | 19 | ||||
-rw-r--r-- | src/noui.h | 6 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 17 | ||||
-rw-r--r-- | src/qt/bitcoingui.h | 13 | ||||
-rw-r--r-- | src/timedata.cpp | 4 | ||||
-rw-r--r-- | src/ui_interface.cpp | 11 | ||||
-rw-r--r-- | src/ui_interface.h | 9 | ||||
-rw-r--r-- | src/util/translation.h | 6 | ||||
-rw-r--r-- | src/validation.cpp | 5 | ||||
-rw-r--r-- | src/wallet/init.cpp | 6 | ||||
-rw-r--r-- | src/wallet/load.cpp | 14 |
23 files changed, 166 insertions, 144 deletions
diff --git a/doc/translation_strings_policy.md b/doc/translation_strings_policy.md index 634aca3559..1931302dda 100644 --- a/doc/translation_strings_policy.md +++ b/doc/translation_strings_policy.md @@ -23,7 +23,8 @@ On a high level, these strings are to be translated: ### GUI strings -Anything that appears to the user in the GUI is to be translated. This includes labels, menu items, button texts, tooltips and window titles. +Do not translate technical or extremely rare errors. +Anything else that appears to the user in the GUI is to be translated. This includes labels, menu items, button texts, tooltips and window titles. This includes messages passed to the GUI through the UI interface through `InitMessage`, `ThreadSafeMessageBox` or `ShowProgress`. General recommendations diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 2aa416ca44..43d3f3c5ac 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -56,7 +56,7 @@ static bool AppInit(int argc, char* argv[]) SetupServerArgs(node); std::string error; if (!gArgs.ParseParameters(argc, argv, error)) { - return InitError(strprintf("Error parsing command line arguments: %s\n", error)); + return InitError(Untranslated(strprintf("Error parsing command line arguments: %s\n", error))); } // Process help and version before taking care about datadir @@ -80,22 +80,22 @@ static bool AppInit(int argc, char* argv[]) try { if (!CheckDataDirOption()) { - return InitError(strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", ""))); + return InitError(Untranslated(strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "")))); } if (!gArgs.ReadConfigFiles(error, true)) { - return InitError(strprintf("Error reading configuration file: %s\n", error)); + return InitError(Untranslated(strprintf("Error reading configuration file: %s\n", error))); } // Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause) try { SelectParams(gArgs.GetChainName()); } catch (const std::exception& e) { - return InitError(strprintf("%s\n", e.what())); + return InitError(Untranslated(strprintf("%s\n", e.what()))); } // Error out when loose non-argument tokens are encountered on command line for (int i = 1; i < argc; i++) { if (!IsSwitchChar(argv[i][0])) { - return InitError(strprintf("Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i])); + return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]))); } } @@ -130,13 +130,13 @@ static bool AppInit(int argc, char* argv[]) // Daemonize if (daemon(1, 0)) { // don't chdir (1), do close FDs (0) - return InitError(strprintf("daemon() failed: %s\n", strerror(errno))); + return InitError(Untranslated(strprintf("daemon() failed: %s\n", strerror(errno)))); } #if defined(MAC_OSX) #pragma GCC diagnostic pop #endif #else - return InitError("-daemon is not supported on this operating system\n"); + return InitError(Untranslated("-daemon is not supported on this operating system\n")); #endif // HAVE_DECL_DAEMON } // Lock data directory after daemonization diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 60c4d06f12..3c3e6e5bba 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -252,7 +252,7 @@ static bool InitRPCAuthentication() LogPrintf("No rpcpassword set - using random cookie authentication.\n"); if (!GenerateAuthCookie(&strRPCUserColonPass)) { uiInterface.ThreadSafeMessageBox( - _("Error: A fatal internal error occurred, see debug.log for details").translated, // Same message as AbortNode + _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode "", CClientUIInterface::MSG_ERROR); return false; } diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 176284d103..ffe246b241 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -6,14 +6,15 @@ #include <chainparamsbase.h> #include <compat.h> -#include <util/threadnames.h> -#include <util/system.h> -#include <util/strencodings.h> #include <netbase.h> #include <rpc/protocol.h> // For HTTP status codes #include <shutdown.h> #include <sync.h> #include <ui_interface.h> +#include <util/strencodings.h> +#include <util/system.h> +#include <util/threadnames.h> +#include <util/translation.h> #include <deque> #include <memory> @@ -175,7 +176,7 @@ static bool InitHTTPAllowList() LookupSubNet(strAllow, subnet); if (!subnet.IsValid()) { uiInterface.ThreadSafeMessageBox( - strprintf("Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).", strAllow), + strprintf(Untranslated("Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24)."), strAllow), "", CClientUIInterface::MSG_ERROR); return false; } diff --git a/src/index/base.cpp b/src/index/base.cpp index 7bff463f5b..74ea421e13 100644 --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -8,6 +8,7 @@ #include <tinyformat.h> #include <ui_interface.h> #include <util/system.h> +#include <util/translation.h> #include <validation.h> #include <warnings.h> @@ -23,7 +24,7 @@ static void FatalError(const char* fmt, const Args&... args) SetMiscWarning(strMessage); LogPrintf("*** %s\n", strMessage); uiInterface.ThreadSafeMessageBox( - "Error: A fatal internal error occurred, see debug.log for details", + Untranslated("Error: A fatal internal error occurred, see debug.log for details"), "", CClientUIInterface::MSG_ERROR); StartShutdown(); } diff --git a/src/init.cpp b/src/init.cpp index 3e2ee4d425..221cab15f8 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -121,7 +121,7 @@ NODISCARD static bool CreatePidFile() #endif return true; } else { - return InitError(strprintf(_("Unable to create the PID file '%s': %s").translated, GetPidFile().string(), std::strerror(errno))); + return InitError(strprintf(_("Unable to create the PID file '%s': %s"), GetPidFile().string(), std::strerror(errno))); } } @@ -768,17 +768,15 @@ static void ThreadImport(std::vector<fs::path> vImportFiles) */ static bool InitSanityCheck() { - if(!ECC_InitSanityCheck()) { - InitError("Elliptic curve cryptography sanity check failure. Aborting."); - return false; + if (!ECC_InitSanityCheck()) { + return InitError(Untranslated("Elliptic curve cryptography sanity check failure. Aborting.")); } if (!glibc_sanity_test() || !glibcxx_sanity_test()) return false; if (!Random_SanityCheck()) { - InitError("OS cryptographic RNG sanity check failure. Aborting."); - return false; + return InitError(Untranslated("OS cryptographic RNG sanity check failure. Aborting.")); } return true; @@ -929,8 +927,9 @@ bool AppInitBasicSetup() HeapSetInformation(nullptr, HeapEnableTerminationOnCorruption, nullptr, 0); #endif - if (!SetupNetworking()) - return InitError("Initializing networking failed"); + if (!SetupNetworking()) { + return InitError(Untranslated("Initializing networking failed.")); + } #ifndef WIN32 if (!gArgs.GetBoolArg("-sysperms", false)) { @@ -967,7 +966,7 @@ bool AppInitParameterInteraction() // on the command line or in this network's section of the config file. std::string network = gArgs.GetChainName(); for (const auto& arg : gArgs.GetUnsuitableSectionOnlyArgs()) { - return InitError(strprintf(_("Config setting for %s only applied on %s network when in [%s] section.").translated, arg, network, network)); + return InitError(strprintf(_("Config setting for %s only applied on %s network when in [%s] section."), arg, network, network)); } // Warn if unrecognized section name are present in the config file. @@ -976,7 +975,7 @@ bool AppInitParameterInteraction() } if (!fs::is_directory(GetBlocksDir())) { - return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist.").translated, gArgs.GetArg("-blocksdir", ""))); + return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist."), gArgs.GetArg("-blocksdir", ""))); } // parse and validate enabled filter types @@ -988,7 +987,7 @@ bool AppInitParameterInteraction() for (const auto& name : names) { BlockFilterType filter_type; if (!BlockFilterTypeByName(name, filter_type)) { - return InitError(strprintf(_("Unknown -blockfilterindex value %s.").translated, name)); + return InitError(strprintf(_("Unknown -blockfilterindex value %s."), name)); } g_enabled_filter_types.insert(filter_type); } @@ -997,16 +996,16 @@ bool AppInitParameterInteraction() // if using block pruning, then disallow txindex if (gArgs.GetArg("-prune", 0)) { if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) - return InitError(_("Prune mode is incompatible with -txindex.").translated); + return InitError(_("Prune mode is incompatible with -txindex.")); if (!g_enabled_filter_types.empty()) { - return InitError(_("Prune mode is incompatible with -blockfilterindex.").translated); + return InitError(_("Prune mode is incompatible with -blockfilterindex.")); } } // -bind and -whitebind can't be set when not listening size_t nUserBind = gArgs.GetArgs("-bind").size() + gArgs.GetArgs("-whitebind").size(); if (nUserBind != 0 && !gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) { - return InitError("Cannot set -bind or -whitebind together with -listen=0"); + return InitError(Untranslated("Cannot set -bind or -whitebind together with -listen=0")); } // Make sure enough file descriptors are available @@ -1024,7 +1023,7 @@ bool AppInitParameterInteraction() #endif nMaxConnections = std::max(std::min<int>(nMaxConnections, fd_max - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS), 0); if (nFD < MIN_CORE_FILEDESCRIPTORS) - return InitError(_("Not enough file descriptors available.").translated); + return InitError(_("Not enough file descriptors available.")); nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS, nMaxConnections); if (nMaxConnections < nUserMaxConnections) @@ -1069,7 +1068,7 @@ bool AppInitParameterInteraction() if (gArgs.IsArgSet("-minimumchainwork")) { const std::string minChainWorkStr = gArgs.GetArg("-minimumchainwork", ""); if (!IsHexNumber(minChainWorkStr)) { - return InitError(strprintf("Invalid non-hex (%s) minimum chain work value specified", minChainWorkStr)); + return InitError(strprintf(Untranslated("Invalid non-hex (%s) minimum chain work value specified"), minChainWorkStr)); } nMinimumChainWork = UintToArith256(uint256S(minChainWorkStr)); } else { @@ -1084,21 +1083,21 @@ bool AppInitParameterInteraction() int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; int64_t nMempoolSizeMin = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40; if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin) - return InitError(strprintf(_("-maxmempool must be at least %d MB").translated, std::ceil(nMempoolSizeMin / 1000000.0))); + return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0))); // incremental relay fee sets the minimum feerate increase necessary for BIP 125 replacement in the mempool // and the amount the mempool min fee increases above the feerate of txs evicted due to mempool limiting. if (gArgs.IsArgSet("-incrementalrelayfee")) { CAmount n = 0; if (!ParseMoney(gArgs.GetArg("-incrementalrelayfee", ""), n)) - return InitError(AmountErrMsg("incrementalrelayfee", gArgs.GetArg("-incrementalrelayfee", "")).translated); + return InitError(AmountErrMsg("incrementalrelayfee", gArgs.GetArg("-incrementalrelayfee", ""))); incrementalRelayFee = CFeeRate(n); } // block pruning; get the amount of disk space (in MiB) to allot for block & undo files int64_t nPruneArg = gArgs.GetArg("-prune", 0); if (nPruneArg < 0) { - return InitError(_("Prune cannot be configured with a negative value.").translated); + return InitError(_("Prune cannot be configured with a negative value.")); } nPruneTarget = (uint64_t) nPruneArg * 1024 * 1024; if (nPruneArg == 1) { // manual pruning: -prune=1 @@ -1107,7 +1106,7 @@ bool AppInitParameterInteraction() fPruneMode = true; } else if (nPruneTarget) { if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) { - return InitError(strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number.").translated, MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); + return InitError(strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); } LogPrintf("Prune configured to target %u MiB on disk for block and undo files.\n", nPruneTarget / 1024 / 1024); fPruneMode = true; @@ -1120,13 +1119,13 @@ bool AppInitParameterInteraction() peer_connect_timeout = gArgs.GetArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT); if (peer_connect_timeout <= 0) { - return InitError("peertimeout cannot be configured with a negative value."); + return InitError(Untranslated("peertimeout cannot be configured with a negative value.")); } if (gArgs.IsArgSet("-minrelaytxfee")) { CAmount n = 0; if (!ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n)) { - return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", "")).translated); + return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", ""))); } // High fee check is done afterward in CWallet::CreateWalletFromFile() ::minRelayTxFee = CFeeRate(n); @@ -1142,7 +1141,7 @@ bool AppInitParameterInteraction() { CAmount n = 0; if (!ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n)) - return InitError(AmountErrMsg("blockmintxfee", gArgs.GetArg("-blockmintxfee", "")).translated); + return InitError(AmountErrMsg("blockmintxfee", gArgs.GetArg("-blockmintxfee", ""))); } // Feerate used to define dust. Shouldn't be changed lightly as old @@ -1151,13 +1150,13 @@ bool AppInitParameterInteraction() { CAmount n = 0; if (!ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n)) - return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", "")).translated); + return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", ""))); dustRelayFee = CFeeRate(n); } fRequireStandard = !gArgs.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard()); if (!chainparams.IsTestChain() && !fRequireStandard) { - return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString())); + return InitError(strprintf(Untranslated("acceptnonstdtxn is not currently supported for %s chain"), chainparams.NetworkIDString())); } nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp); @@ -1174,10 +1173,10 @@ bool AppInitParameterInteraction() nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM); if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0) - return InitError("rpcserialversion must be non-negative."); + return InitError(Untranslated("rpcserialversion must be non-negative.")); if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1) - return InitError("unknown rpcserialversion requested."); + return InitError(Untranslated("Unknown rpcserialversion requested.")); nMaxTipAge = gArgs.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE); @@ -1189,10 +1188,10 @@ static bool LockDataDirectory(bool probeOnly) // Make sure only a single Bitcoin process is using the data directory. fs::path datadir = GetDataDir(); if (!DirIsWritable(datadir)) { - return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions.").translated, datadir.string())); + return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions."), datadir.string())); } if (!LockDirectory(datadir, ".lock", probeOnly)) { - return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running.").translated, datadir.string(), PACKAGE_NAME)); + return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), datadir.string(), PACKAGE_NAME)); } return true; } @@ -1210,7 +1209,7 @@ bool AppInitSanityChecks() // Sanity check if (!InitSanityCheck()) - return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down.").translated, PACKAGE_NAME)); + return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), PACKAGE_NAME)); // Probe the data directory lock to give an early error message, if possible // We cannot hold the data directory lock here, as the forking for daemon() hasn't yet happened, @@ -1246,7 +1245,7 @@ bool AppInitMain(NodeContext& node) } } if (!LogInstance().StartLogging()) { - return InitError(strprintf("Could not open debug log file %s", + return InitError(strprintf(Untranslated("Could not open debug log file %s"), LogInstance().m_file_path.string())); } @@ -1346,7 +1345,7 @@ bool AppInitMain(NodeContext& node) { uiInterface.InitMessage_connect(SetRPCWarmupStatus); if (!AppInitServers()) - return InitError(_("Unable to start HTTP server. See debug log for details.").translated); + return InitError(_("Unable to start HTTP server. See debug log for details.")); } // ********************************************************* Step 5: verify wallet database integrity @@ -1378,12 +1377,12 @@ bool AppInitMain(NodeContext& node) std::vector<std::string> uacomments; for (const std::string& cmt : gArgs.GetArgs("-uacomment")) { if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)) - return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters.").translated, cmt)); + return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters."), cmt)); uacomments.push_back(cmt); } strSubVersion = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, uacomments); if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) { - return InitError(strprintf(_("Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.").translated, + return InitError(strprintf(_("Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments."), strSubVersion.size(), MAX_SUBVERSION_LENGTH)); } @@ -1392,7 +1391,7 @@ bool AppInitMain(NodeContext& node) for (const std::string& snet : gArgs.GetArgs("-onlynet")) { enum Network net = ParseNetwork(snet); if (net == NET_UNROUTABLE) - return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'").translated, snet)); + return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet)); nets.insert(net); } for (int n = 0; n < NET_MAX; n++) { @@ -1413,12 +1412,12 @@ bool AppInitMain(NodeContext& node) if (proxyArg != "" && proxyArg != "0") { CService proxyAddr; if (!Lookup(proxyArg, proxyAddr, 9050, fNameLookup)) { - return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'").translated, proxyArg)); + return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg)); } proxyType addrProxy = proxyType(proxyAddr, proxyRandomize); if (!addrProxy.IsValid()) - return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'").translated, proxyArg)); + return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg)); SetProxy(NET_IPV4, addrProxy); SetProxy(NET_IPV6, addrProxy); @@ -1437,11 +1436,11 @@ bool AppInitMain(NodeContext& node) } else { CService onionProxy; if (!Lookup(onionArg, onionProxy, 9050, fNameLookup)) { - return InitError(strprintf(_("Invalid -onion address or hostname: '%s'").translated, onionArg)); + return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg)); } proxyType addrOnion = proxyType(onionProxy, proxyRandomize); if (!addrOnion.IsValid()) - return InitError(strprintf(_("Invalid -onion address or hostname: '%s'").translated, onionArg)); + return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg)); SetProxy(NET_ONION, addrOnion); SetReachable(NET_ONION, true); } @@ -1457,7 +1456,7 @@ bool AppInitMain(NodeContext& node) if (Lookup(strAddr, addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid()) AddLocal(addrLocal, LOCAL_MANUAL); else - return InitError(ResolveErrMsg("externalip", strAddr)); + return InitError(Untranslated(ResolveErrMsg("externalip", strAddr))); } // Read asmap file if configured @@ -1470,12 +1469,12 @@ bool AppInitMain(NodeContext& node) asmap_path = GetDataDir() / asmap_path; } if (!fs::exists(asmap_path)) { - InitError(strprintf(_("Could not find asmap file %s").translated, asmap_path)); + InitError(strprintf(_("Could not find asmap file %s"), asmap_path)); return false; } std::vector<bool> asmap = CAddrMan::DecodeAsmap(asmap_path); if (asmap.size() == 0) { - InitError(strprintf(_("Could not parse asmap file %s").translated, asmap_path)); + InitError(strprintf(_("Could not parse asmap file %s"), asmap_path)); return false; } const uint256 asmap_version = SerializeHash(asmap); @@ -1542,7 +1541,7 @@ bool AppInitMain(NodeContext& node) auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { return fReset || fReindexChainState || chainstate->CoinsTip().GetBestBlock().IsNull(); }; - std::string strLoadError; + bilingual_str strLoadError; uiInterface.InitMessage(_("Loading block index...").translated); @@ -1573,7 +1572,7 @@ bool AppInitMain(NodeContext& node) // From here on out fReindex and fReset mean something different! if (!LoadBlockIndex(chainparams)) { if (ShutdownRequested()) break; - strLoadError = _("Error loading block database").translated; + strLoadError = _("Error loading block database"); break; } @@ -1581,13 +1580,13 @@ bool AppInitMain(NodeContext& node) // (we're likely using a testnet datadir, or the other way around). if (!::BlockIndex().empty() && !LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) { - return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?").translated); + return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); } // Check for changed -prune state. What we are concerned about is a user who has pruned blocks // in the past, but is now trying to run unpruned. if (fHavePruned && !fPruneMode) { - strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain").translated; + strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain"); break; } @@ -1596,7 +1595,7 @@ bool AppInitMain(NodeContext& node) // (otherwise we use the one already on disk). // This is called again in ThreadImport after the reindex completes. if (!fReindex && !LoadGenesisBlock(chainparams)) { - strLoadError = _("Error initializing block database").translated; + strLoadError = _("Error initializing block database"); break; } @@ -1614,21 +1613,21 @@ bool AppInitMain(NodeContext& node) chainstate->CoinsErrorCatcher().AddReadErrCallback([]() { uiInterface.ThreadSafeMessageBox( - _("Error reading from database, shutting down.").translated, + _("Error reading from database, shutting down."), "", CClientUIInterface::MSG_ERROR); }); // If necessary, upgrade from older database format. // This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate if (!chainstate->CoinsDB().Upgrade()) { - strLoadError = _("Error upgrading chainstate database").translated; + strLoadError = _("Error upgrading chainstate database"); failed_chainstate_init = true; break; } // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate if (!chainstate->ReplayBlocks(chainparams)) { - strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.").translated; + strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate."); failed_chainstate_init = true; break; } @@ -1640,7 +1639,7 @@ bool AppInitMain(NodeContext& node) if (!is_coinsview_empty(chainstate)) { // LoadChainTip initializes the chain based on CoinsTip()'s best block if (!chainstate->LoadChainTip(chainparams)) { - strLoadError = _("Error initializing block database").translated; + strLoadError = _("Error initializing block database"); failed_chainstate_init = true; break; // out of the per-chainstate loop } @@ -1653,7 +1652,7 @@ bool AppInitMain(NodeContext& node) } } catch (const std::exception& e) { LogPrintf("%s\n", e.what()); - strLoadError = _("Error opening block database").translated; + strLoadError = _("Error opening block database"); break; } @@ -1669,7 +1668,7 @@ bool AppInitMain(NodeContext& node) if (!chainstate->RewindBlockIndex(chainparams)) { strLoadError = _( "Unable to rewind the database to a pre-fork state. " - "You will need to redownload the blockchain").translated; + "You will need to redownload the blockchain"); failed_rewind = true; break; // out of the per-chainstate loop } @@ -1698,7 +1697,7 @@ bool AppInitMain(NodeContext& node) if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) { strLoadError = _("The block database contains a block which appears to be from the future. " "This may be due to your computer's date and time being set incorrectly. " - "Only rebuild the block database if you are sure that your computer's date and time are correct").translated; + "Only rebuild the block database if you are sure that your computer's date and time are correct"); failed_verification = true; break; } @@ -1710,7 +1709,7 @@ bool AppInitMain(NodeContext& node) chainparams, &chainstate->CoinsDB(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL), gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) { - strLoadError = _("Corrupted block database detected").translated; + strLoadError = _("Corrupted block database detected"); failed_verification = true; break; } @@ -1718,7 +1717,7 @@ bool AppInitMain(NodeContext& node) } } catch (const std::exception& e) { LogPrintf("%s\n", e.what()); - strLoadError = _("Error opening block database").translated; + strLoadError = _("Error opening block database"); failed_verification = true; break; } @@ -1733,8 +1732,8 @@ bool AppInitMain(NodeContext& node) // first suggest a reindex if (!fReset) { bool fRet = uiInterface.ThreadSafeQuestion( - strLoadError + ".\n\n" + _("Do you want to rebuild the block database now?").translated, - strLoadError + ".\nPlease restart with -reindex or -reindex-chainstate to recover.", + strLoadError + Untranslated(".\n\n") + _("Do you want to rebuild the block database now?"), + strLoadError.original + ".\nPlease restart with -reindex or -reindex-chainstate to recover.", "", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT); if (fRet) { fReindex = true; @@ -1807,11 +1806,11 @@ bool AppInitMain(NodeContext& node) // ********************************************************* Step 11: import blocks if (!CheckDiskSpace(GetDataDir())) { - InitError(strprintf(_("Error: Disk space is low for %s").translated, GetDataDir())); + InitError(strprintf(_("Error: Disk space is low for %s"), GetDataDir())); return false; } if (!CheckDiskSpace(GetBlocksDir())) { - InitError(strprintf(_("Error: Disk space is low for %s").translated, GetBlocksDir())); + InitError(strprintf(_("Error: Disk space is low for %s"), GetBlocksDir())); return false; } @@ -1896,21 +1895,21 @@ bool AppInitMain(NodeContext& node) for (const std::string& strBind : gArgs.GetArgs("-bind")) { CService addrBind; if (!Lookup(strBind, addrBind, GetListenPort(), false)) { - return InitError(ResolveErrMsg("bind", strBind)); + return InitError(Untranslated(ResolveErrMsg("bind", strBind))); } connOptions.vBinds.push_back(addrBind); } for (const std::string& strBind : gArgs.GetArgs("-whitebind")) { NetWhitebindPermissions whitebind; std::string error; - if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) return InitError(error); + if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) return InitError(Untranslated(error)); connOptions.vWhiteBinds.push_back(whitebind); } for (const auto& net : gArgs.GetArgs("-whitelist")) { NetWhitelistPermissions subnet; std::string error; - if (!NetWhitelistPermissions::TryParse(net, subnet, error)) return InitError(error); + if (!NetWhitelistPermissions::TryParse(net, subnet, error)) return InitError(Untranslated(error)); connOptions.vWhitelistedRange.push_back(subnet); } diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp index c5262e4bc0..515a2ba1b9 100644 --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -345,7 +345,7 @@ public: int64_t getAdjustedTime() override { return GetAdjustedTime(); } void initMessage(const std::string& message) override { ::uiInterface.InitMessage(message); } void initWarning(const std::string& message) override { InitWarning(message); } - void initError(const std::string& message) override { InitError(message); } + void initError(const bilingual_str& message) override { InitError(message); } void showProgress(const std::string& title, int progress, bool resume_possible) override { ::uiInterface.ShowProgress(title, progress, resume_possible); diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h index e33fe54ac8..77b315b195 100644 --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -21,6 +21,7 @@ class CScheduler; class Coin; class uint256; enum class RBFTransactionState; +struct bilingual_str; struct CBlockLocator; struct FeeCalculation; struct NodeContext; @@ -227,7 +228,7 @@ public: virtual void initWarning(const std::string& message) = 0; //! Send init error. - virtual void initError(const std::string& message) = 0; + virtual void initError(const bilingual_str& message) = 0; //! Send progress indicator. virtual void showProgress(const std::string& title, int progress, bool resume_possible) = 0; diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index 8564819e6a..9e603a12cd 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -28,6 +28,7 @@ #include <txmempool.h> #include <ui_interface.h> #include <util/system.h> +#include <util/translation.h> #include <validation.h> #include <warnings.h> @@ -54,7 +55,7 @@ namespace { class NodeImpl : public Node { public: - void initError(const std::string& message) override { InitError(message); } + void initError(const std::string& message) override { InitError(Untranslated(message)); } bool parseParameters(int argc, const char* const argv[], std::string& error) override { return gArgs.ParseParameters(argc, argv, error); diff --git a/src/interfaces/node.h b/src/interfaces/node.h index db9b42b293..aef6b19458 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -213,11 +213,11 @@ public: //! Register handler for message box messages. using MessageBoxFn = - std::function<bool(const std::string& message, const std::string& caption, unsigned int style)>; + std::function<bool(const bilingual_str& message, const std::string& caption, unsigned int style)>; virtual std::unique_ptr<Handler> handleMessageBox(MessageBoxFn fn) = 0; //! Register handler for question messages. - using QuestionFn = std::function<bool(const std::string& message, + using QuestionFn = std::function<bool(const bilingual_str& message, const std::string& non_interactive_message, const std::string& caption, unsigned int style)>; diff --git a/src/net.cpp b/src/net.cpp index dcc613ba88..97b80b50e2 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2068,9 +2068,8 @@ void CConnman::ThreadMessageHandler() -bool CConnman::BindListenPort(const CService& addrBind, std::string& strError, NetPermissionFlags permissions) +bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError, NetPermissionFlags permissions) { - strError = ""; int nOne = 1; // Create socket for listening for incoming connections @@ -2078,16 +2077,16 @@ bool CConnman::BindListenPort(const CService& addrBind, std::string& strError, N socklen_t len = sizeof(sockaddr); if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len)) { - strError = strprintf("Error: Bind address family for %s not supported", addrBind.ToString()); - LogPrintf("%s\n", strError); + strError = strprintf(Untranslated("Error: Bind address family for %s not supported"), addrBind.ToString()); + LogPrintf("%s\n", strError.original); return false; } SOCKET hListenSocket = CreateSocket(addrBind); if (hListenSocket == INVALID_SOCKET) { - strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %s)", NetworkErrorString(WSAGetLastError())); - LogPrintf("%s\n", strError); + strError = strprintf(Untranslated("Error: Couldn't open socket for incoming connections (socket returned error %s)"), NetworkErrorString(WSAGetLastError())); + LogPrintf("%s\n", strError.original); return false; } @@ -2111,10 +2110,10 @@ bool CConnman::BindListenPort(const CService& addrBind, std::string& strError, N { int nErr = WSAGetLastError(); if (nErr == WSAEADDRINUSE) - strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running.").translated, addrBind.ToString(), PACKAGE_NAME); + strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running."), addrBind.ToString(), PACKAGE_NAME); else - strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)").translated, addrBind.ToString(), NetworkErrorString(nErr)); - LogPrintf("%s\n", strError); + strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr)); + LogPrintf("%s\n", strError.original); CloseSocket(hListenSocket); return false; } @@ -2123,8 +2122,8 @@ bool CConnman::BindListenPort(const CService& addrBind, std::string& strError, N // Listen for incoming connections if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) { - strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)").translated, NetworkErrorString(WSAGetLastError())); - LogPrintf("%s\n", strError); + strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError())); + LogPrintf("%s\n", strError.original); CloseSocket(hListenSocket); return false; } @@ -2218,7 +2217,7 @@ NodeId CConnman::GetNewNodeId() bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags permissions) { if (!(flags & BF_EXPLICIT) && !IsReachable(addr)) return false; - std::string strError; + bilingual_str strError; if (!BindListenPort(addr, strError, permissions)) { if ((flags & BF_REPORT_ERROR) && clientInterface) { clientInterface->ThreadSafeMessageBox(strError, "", CClientUIInterface::MSG_ERROR); @@ -2265,7 +2264,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) if (fListen && !InitBinds(connOptions.vBinds, connOptions.vWhiteBinds)) { if (clientInterface) { clientInterface->ThreadSafeMessageBox( - _("Failed to listen on any port. Use -listen=0 if you want this.").translated, + _("Failed to listen on any port. Use -listen=0 if you want this."), "", CClientUIInterface::MSG_ERROR); } return false; @@ -2331,7 +2330,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) if (connOptions.m_use_addrman_outgoing && !connOptions.m_specified_outgoing.empty()) { if (clientInterface) { clientInterface->ThreadSafeMessageBox( - _("Cannot provide specific connections and have addrman find outgoing connections at the same.").translated, + _("Cannot provide specific connections and have addrman find outgoing connections at the same."), "", CClientUIInterface::MSG_ERROR); } return false; @@ -21,8 +21,8 @@ #include <random.h> #include <streams.h> #include <sync.h> -#include <uint256.h> #include <threadinterrupt.h> +#include <uint256.h> #include <atomic> #include <deque> @@ -39,6 +39,7 @@ class CScheduler; class CNode; class BanMan; +struct bilingual_str; /** Default for -whitelistrelay. */ static const bool DEFAULT_WHITELISTRELAY = true; @@ -334,7 +335,7 @@ private: NetPermissionFlags m_permissions; }; - bool BindListenPort(const CService& bindAddr, std::string& strError, NetPermissionFlags permissions); + bool BindListenPort(const CService& bindAddr, bilingual_str& strError, NetPermissionFlags permissions); bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions); bool InitBinds(const std::vector<CService>& binds, const std::vector<NetWhitebindPermissions>& whiteBinds); void ThreadOpenAddedConnections(); diff --git a/src/noui.cpp b/src/noui.cpp index 11cfe7f94d..ddb3a50ff7 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -5,8 +5,9 @@ #include <noui.h> +#include <logging.h> #include <ui_interface.h> -#include <util/system.h> +#include <util/translation.h> #include <string> @@ -18,7 +19,7 @@ boost::signals2::connection noui_ThreadSafeMessageBoxConn; boost::signals2::connection noui_ThreadSafeQuestionConn; boost::signals2::connection noui_InitMessageConn; -bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) +bool noui_ThreadSafeMessageBox(const bilingual_str& message, const std::string& caption, unsigned int style) { bool fSecure = style & CClientUIInterface::SECURE; style &= ~CClientUIInterface::SECURE; @@ -43,15 +44,15 @@ bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& ca } if (!fSecure) { - LogPrintf("%s%s\n", strCaption, message); + LogPrintf("%s%s\n", strCaption, message.original); } - tfm::format(std::cerr, "%s%s\n", strCaption, message); + tfm::format(std::cerr, "%s%s\n", strCaption, message.original); return false; } -bool noui_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style) +bool noui_ThreadSafeQuestion(const bilingual_str& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style) { - return noui_ThreadSafeMessageBox(message, caption, style); + return noui_ThreadSafeMessageBox(Untranslated(message), caption, style); } void noui_InitMessage(const std::string& message) @@ -66,13 +67,13 @@ void noui_connect() noui_InitMessageConn = uiInterface.InitMessage_connect(noui_InitMessage); } -bool noui_ThreadSafeMessageBoxRedirect(const std::string& message, const std::string& caption, unsigned int style) +bool noui_ThreadSafeMessageBoxRedirect(const bilingual_str& message, const std::string& caption, unsigned int style) { - LogPrintf("%s: %s\n", caption, message); + LogPrintf("%s: %s\n", caption, message.original); return false; } -bool noui_ThreadSafeQuestionRedirect(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style) +bool noui_ThreadSafeQuestionRedirect(const bilingual_str& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style) { LogPrintf("%s: %s\n", caption, message); return false; diff --git a/src/noui.h b/src/noui.h index 5e5767b453..8ec5708328 100644 --- a/src/noui.h +++ b/src/noui.h @@ -7,10 +7,12 @@ #include <string> +struct bilingual_str; + /** Non-GUI handler, which logs and prints messages. */ -bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style); +bool noui_ThreadSafeMessageBox(const bilingual_str& message, const std::string& caption, unsigned int style); /** Non-GUI handler, which logs and prints questions. */ -bool noui_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style); +bool noui_ThreadSafeQuestion(const bilingual_str& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style); /** Non-GUI handler, which only logs a message. */ void noui_InitMessage(const std::string& message); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index cf2de1a417..3a1fdc22a6 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -36,6 +36,7 @@ #include <interfaces/node.h> #include <ui_interface.h> #include <util/system.h> +#include <util/translation.h> #include <QAction> #include <QApplication> @@ -1039,7 +1040,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer progressBar->setToolTip(tooltip); } -void BitcoinGUI::message(const QString& title, QString message, unsigned int style, bool* ret) +void BitcoinGUI::message(const QString& title, QString message, unsigned int style, bool* ret, const QString& detailed_message) { // Default title. On macOS, the window title is ignored (as required by the macOS Guidelines). QString strTitle{PACKAGE_NAME}; @@ -1093,6 +1094,7 @@ void BitcoinGUI::message(const QString& title, QString message, unsigned int sty showNormalIfMinimized(); QMessageBox mBox(static_cast<QMessageBox::Icon>(nMBoxIcon), strTitle, message, buttons, this); mBox.setTextFormat(Qt::PlainText); + mBox.setDetailedText(detailed_message); int r = mBox.exec(); if (ret != nullptr) *ret = r == QMessageBox::Ok; @@ -1368,20 +1370,27 @@ void BitcoinGUI::showModalOverlay() modalOverlay->toggleVisibility(); } -static bool ThreadSafeMessageBox(BitcoinGUI* gui, const std::string& message, const std::string& caption, unsigned int style) +static bool ThreadSafeMessageBox(BitcoinGUI* gui, const bilingual_str& message, const std::string& caption, unsigned int style) { bool modal = (style & CClientUIInterface::MODAL); // The SECURE flag has no effect in the Qt GUI. // bool secure = (style & CClientUIInterface::SECURE); style &= ~CClientUIInterface::SECURE; bool ret = false; + + QString detailed_message; // This is original message, in English, for googling and referencing. + if (message.original != message.translated) { + detailed_message = BitcoinGUI::tr("Original message:") + "\n" + QString::fromStdString(message.original); + } + // In case of modal message, use blocking connection to wait for user to click a button bool invoked = QMetaObject::invokeMethod(gui, "message", modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(caption)), - Q_ARG(QString, QString::fromStdString(message)), + Q_ARG(QString, QString::fromStdString(message.translated)), Q_ARG(unsigned int, style), - Q_ARG(bool*, &ret)); + Q_ARG(bool*, &ret), + Q_ARG(QString, detailed_message)); assert(invoked); return ret; } diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 70366e12d1..dda88930db 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -216,13 +216,14 @@ public Q_SLOTS: void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers); /** Notify the user of an event from the core network or transaction handling code. - @param[in] title the message box / notification title - @param[in] message the displayed text - @param[in] style modality and style definitions (icon and used buttons - buttons only for message boxes) - @see CClientUIInterface::MessageBoxFlags - @param[in] ret pointer to a bool that will be modified to whether Ok was clicked (modal only) + @param[in] title the message box / notification title + @param[in] message the displayed text + @param[in] style modality and style definitions (icon and used buttons - buttons only for message boxes) + @see CClientUIInterface::MessageBoxFlags + @param[in] ret pointer to a bool that will be modified to whether Ok was clicked (modal only) + @param[in] detailed_message the text to be displayed in the details area */ - void message(const QString& title, QString message, unsigned int style, bool* ret = nullptr); + void message(const QString& title, QString message, unsigned int style, bool* ret = nullptr, const QString& detailed_message = QString()); #ifdef ENABLE_WALLET void setCurrentWallet(WalletModel* wallet_model); diff --git a/src/timedata.cpp b/src/timedata.cpp index 942b3cb919..dd56acf44f 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -101,8 +101,8 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample) if (!fMatch) { fDone = true; - std::string strMessage = strprintf(_("Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.").translated, PACKAGE_NAME); - SetMiscWarning(strMessage); + bilingual_str strMessage = strprintf(_("Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly."), PACKAGE_NAME); + SetMiscWarning(strMessage.translated); uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING); } } diff --git a/src/ui_interface.cpp b/src/ui_interface.cpp index 85bb746d19..9cfde9502d 100644 --- a/src/ui_interface.cpp +++ b/src/ui_interface.cpp @@ -4,6 +4,8 @@ #include <ui_interface.h> +#include <util/translation.h> + #include <boost/signals2/last_value.hpp> #include <boost/signals2/signal.hpp> @@ -40,8 +42,8 @@ ADD_SIGNALS_IMPL_WRAPPER(NotifyBlockTip); ADD_SIGNALS_IMPL_WRAPPER(NotifyHeaderTip); ADD_SIGNALS_IMPL_WRAPPER(BannedListChanged); -bool CClientUIInterface::ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) { return g_ui_signals.ThreadSafeMessageBox(message, caption, style); } -bool CClientUIInterface::ThreadSafeQuestion(const std::string& message, const std::string& non_interactive_message, const std::string& caption, unsigned int style) { return g_ui_signals.ThreadSafeQuestion(message, non_interactive_message, caption, style); } +bool CClientUIInterface::ThreadSafeMessageBox(const bilingual_str& message, const std::string& caption, unsigned int style) { return g_ui_signals.ThreadSafeMessageBox(message, caption, style); } +bool CClientUIInterface::ThreadSafeQuestion(const bilingual_str& message, const std::string& non_interactive_message, const std::string& caption, unsigned int style) { return g_ui_signals.ThreadSafeQuestion(message, non_interactive_message, caption, style); } void CClientUIInterface::InitMessage(const std::string& message) { return g_ui_signals.InitMessage(message); } void CClientUIInterface::NotifyNumConnectionsChanged(int newNumConnections) { return g_ui_signals.NotifyNumConnectionsChanged(newNumConnections); } void CClientUIInterface::NotifyNetworkActiveChanged(bool networkActive) { return g_ui_signals.NotifyNetworkActiveChanged(networkActive); } @@ -51,8 +53,7 @@ void CClientUIInterface::NotifyBlockTip(bool b, const CBlockIndex* i) { return g void CClientUIInterface::NotifyHeaderTip(bool b, const CBlockIndex* i) { return g_ui_signals.NotifyHeaderTip(b, i); } void CClientUIInterface::BannedListChanged() { return g_ui_signals.BannedListChanged(); } - -bool InitError(const std::string& str) +bool InitError(const bilingual_str& str) { uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR); return false; @@ -60,5 +61,5 @@ bool InitError(const std::string& str) void InitWarning(const std::string& str) { - uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING); + uiInterface.ThreadSafeMessageBox(Untranslated(str), "", CClientUIInterface::MSG_WARNING); } diff --git a/src/ui_interface.h b/src/ui_interface.h index b402177b85..132866cc5a 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -11,6 +11,8 @@ #include <string> class CBlockIndex; +struct bilingual_str; + namespace boost { namespace signals2 { class connection; @@ -82,10 +84,10 @@ public: boost::signals2::connection signal_name##_connect(std::function<signal_name##Sig> fn); /** Show message box. */ - ADD_SIGNALS_DECL_WRAPPER(ThreadSafeMessageBox, bool, const std::string& message, const std::string& caption, unsigned int style); + ADD_SIGNALS_DECL_WRAPPER(ThreadSafeMessageBox, bool, const bilingual_str& message, const std::string& caption, unsigned int style); /** If possible, ask the user a question. If not, falls back to ThreadSafeMessageBox(noninteractive_message, caption, style) and returns false. */ - ADD_SIGNALS_DECL_WRAPPER(ThreadSafeQuestion, bool, const std::string& message, const std::string& noninteractive_message, const std::string& caption, unsigned int style); + ADD_SIGNALS_DECL_WRAPPER(ThreadSafeQuestion, bool, const bilingual_str& message, const std::string& noninteractive_message, const std::string& caption, unsigned int style); /** Progress message during initialization. */ ADD_SIGNALS_DECL_WRAPPER(InitMessage, void, const std::string& message); @@ -118,10 +120,11 @@ public: }; /** Show warning message **/ +// TODO: InitWarning() should take a bilingual_str parameter. void InitWarning(const std::string& str); /** Show error message **/ -bool InitError(const std::string& str); +bool InitError(const bilingual_str& str); extern CClientUIInterface uiInterface; diff --git a/src/util/translation.h b/src/util/translation.h index b2d964c977..45595405e7 100644 --- a/src/util/translation.h +++ b/src/util/translation.h @@ -26,11 +26,11 @@ inline bilingual_str operator+(const bilingual_str& lhs, const bilingual_str& rh } /** Mark a bilingual_str as untranslated */ -inline static bilingual_str Untranslated(std::string original) { return {original, original}; } +inline bilingual_str Untranslated(std::string original) { return {original, original}; } /** Unary operator to return the original */ -inline static std::string OpOriginal(const bilingual_str& b) { return b.original; } +inline std::string OpOriginal(const bilingual_str& b) { return b.original; } /** Unary operator to return the translation */ -inline static std::string OpTranslated(const bilingual_str& b) { return b.translated; } +inline std::string OpTranslated(const bilingual_str& b) { return b.translated; } namespace tinyformat { template <typename... Args> diff --git a/src/validation.cpp b/src/validation.cpp index a924af90af..8a454c8d1b 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1651,14 +1651,15 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex) } /** Abort with a message */ +// TODO: AbortNode() should take bilingual_str userMessage parameter. static bool AbortNode(const std::string& strMessage, const std::string& userMessage = "", unsigned int prefix = 0) { SetMiscWarning(strMessage); LogPrintf("*** %s\n", strMessage); if (!userMessage.empty()) { - uiInterface.ThreadSafeMessageBox(userMessage, "", CClientUIInterface::MSG_ERROR | prefix); + uiInterface.ThreadSafeMessageBox(Untranslated(userMessage), "", CClientUIInterface::MSG_ERROR | prefix); } else { - uiInterface.ThreadSafeMessageBox(_("Error: A fatal internal error occurred, see debug.log for details").translated, "", CClientUIInterface::MSG_ERROR | CClientUIInterface::MSG_NOPREFIX); + uiInterface.ThreadSafeMessageBox(_("Error: A fatal internal error occurred, see debug.log for details"), "", CClientUIInterface::MSG_ERROR | CClientUIInterface::MSG_NOPREFIX); } StartShutdown(); return false; diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 8688f3df5e..6f973aab1c 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -91,7 +91,7 @@ bool WalletInit::ParameterInteraction() const if (gArgs.GetBoolArg("-salvagewallet", false)) { if (is_multiwallet) { - return InitError(strprintf("%s is only allowed with a single wallet file", "-salvagewallet")); + return InitError(strprintf(Untranslated("%s is only allowed with a single wallet file"), "-salvagewallet")); } // Rewrite just private keys: rescan to find transactions if (gArgs.SoftSetBoolArg("-rescan", true)) { @@ -108,7 +108,7 @@ bool WalletInit::ParameterInteraction() const // -zapwallettxes implies a rescan if (zapwallettxes) { if (is_multiwallet) { - return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes")); + return InitError(strprintf(Untranslated("%s is only allowed with a single wallet file"), "-zapwallettxes")); } if (gArgs.SoftSetBoolArg("-rescan", true)) { LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -rescan=1\n", __func__); @@ -116,7 +116,7 @@ bool WalletInit::ParameterInteraction() const } if (gArgs.GetBoolArg("-sysperms", false)) - return InitError("-sysperms is not allowed in combination with enabled wallet functionality"); + return InitError(Untranslated("-sysperms is not allowed in combination with enabled wallet functionality")); return true; } diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp index 217a950457..45841b2ae1 100644 --- a/src/wallet/load.cpp +++ b/src/wallet/load.cpp @@ -20,14 +20,14 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal // The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error); if (error || !fs::exists(wallet_dir)) { - chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist").translated, wallet_dir.string())); + chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string())); return false; } else if (!fs::is_directory(wallet_dir)) { - chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory").translated, wallet_dir.string())); + chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string())); return false; // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version } else if (!wallet_dir.is_absolute()) { - chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path").translated, wallet_dir.string())); + chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string())); return false; } gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string()); @@ -49,7 +49,7 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal WalletLocation location(wallet_file); if (!wallet_paths.insert(location.GetPath()).second) { - chain.initError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified.").translated, wallet_file)); + chain.initError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file)); return false; } @@ -58,7 +58,7 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal bool verify_success = CWallet::Verify(chain, location, salvage_wallet, error_string, warnings); if (!warnings.empty()) chain.initWarning(Join(warnings, "\n", OpTranslated)); if (!verify_success) { - chain.initError(error_string.translated); + chain.initError(error_string); return false; } } @@ -75,14 +75,14 @@ bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& walle std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(chain, WalletLocation(walletFile), error, warnings); if (!warnings.empty()) chain.initWarning(Join(warnings, "\n", OpTranslated)); if (!pwallet) { - chain.initError(error.translated); + chain.initError(error); return false; } AddWallet(pwallet); } return true; } catch (const std::runtime_error& e) { - chain.initError(e.what()); + chain.initError(Untranslated(e.what())); return false; } } |