translation.h raw
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
111