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