translation.h
1 // Copyright (c) 2019-present The Bitcoin Core developers 2 // Distributed under the MIT software license, see the accompanying 3 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 5 #ifndef BITCOIN_UTIL_TRANSLATION_H 6 #define BITCOIN_UTIL_TRANSLATION_H 7 8 #include <tinyformat.h> 9 #include <util/check.h> 10 #include <util/string.h> 11 12 #include <functional> 13 #include <string> 14 15 /** Translate a message to the native language of the user. */ 16 using TranslateFn = std::function<std::string(const char*)>; 17 const extern TranslateFn G_TRANSLATION_FUN; 18 19 /** 20 * Bilingual messages: 21 * - in GUI: user's native language + untranslated (i.e. English) 22 * - in log and stderr: untranslated only 23 */ 24 struct bilingual_str { 25 std::string original; 26 std::string translated; 27 28 bilingual_str& operator+=(const bilingual_str& rhs) 29 { 30 original += rhs.original; 31 translated += rhs.translated; 32 return *this; 33 } 34 35 bool empty() const 36 { 37 return original.empty(); 38 } 39 40 void clear() 41 { 42 original.clear(); 43 translated.clear(); 44 } 45 }; 46 47 inline bilingual_str operator+(bilingual_str lhs, const bilingual_str& rhs) 48 { 49 lhs += rhs; 50 return lhs; 51 } 52 53 namespace util { 54 //! Compile-time literal string that can be translated with an optional translation function. 55 struct TranslatedLiteral { 56 const char* const original; 57 const TranslateFn* translate_fn; 58 59 consteval TranslatedLiteral(const char* str, const TranslateFn* fn = &G_TRANSLATION_FUN) : original{str}, translate_fn{fn} { assert(original); } 60 operator std::string() const { return translate_fn && *translate_fn ? (*translate_fn)(original) : original; } 61 operator bilingual_str() const { return {original, std::string{*this}}; } 62 }; 63 64 // TranslatedLiteral operators for formatting and adding to strings. 65 inline std::ostream& operator<<(std::ostream& os, const TranslatedLiteral& lit) { return os << std::string{lit}; } 66 template<typename T> 67 T operator+(const T& lhs, const TranslatedLiteral& rhs) { return lhs + static_cast<T>(rhs); } 68 template<typename T> 69 T operator+(const TranslatedLiteral& lhs, const T& rhs) { return static_cast<T>(lhs) + rhs; } 70 71 template <unsigned num_params> 72 struct BilingualFmt { 73 const ConstevalFormatString<num_params> original; 74 TranslatedLiteral lit; 75 consteval BilingualFmt(TranslatedLiteral l) : original{l.original}, lit{l} {} 76 }; 77 } // namespace util 78 79 consteval auto _(util::TranslatedLiteral str) { return str; } 80 81 /** Mark a bilingual_str as untranslated */ 82 inline bilingual_str Untranslated(std::string original) { return {original, original}; } 83 84 // Provide an overload of tinyformat::format for BilingualFmt format strings and bilingual_str or TranslatedLiteral args. 85 namespace tinyformat { 86 template <typename... Args> 87 bilingual_str format(util::BilingualFmt<sizeof...(Args)> fmt, const Args&... args) 88 { 89 const auto original_arg{[](const auto& arg) -> const auto& { 90 if constexpr (std::is_same_v<decltype(arg), const bilingual_str&>) { 91 return arg.original; 92 } else if constexpr (std::is_same_v<decltype(arg), const util::TranslatedLiteral&>) { 93 return arg.original; 94 } else { 95 return arg; 96 } 97 }}; 98 const auto translated_arg{[](const auto& arg) -> const auto& { 99 if constexpr (std::is_same_v<decltype(arg), const bilingual_str&>) { 100 return arg.translated; 101 } else { 102 return arg; 103 } 104 }}; 105 return bilingual_str{tfm::format(fmt.original, original_arg(args)...), 106 tfm::format(RuntimeFormat{std::string{fmt.lit}}, translated_arg(args)...)}; 107 } 108 } // namespace tinyformat 109 110 #endif // BITCOIN_UTIL_TRANSLATION_H