/ src / util / translation.h
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