message.hpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. //
  2. // Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // https://www.boost.org/LICENSE_1_0.txt
  6. #ifndef BOOST_LOCALE_MESSAGE_HPP_INCLUDED
  7. #define BOOST_LOCALE_MESSAGE_HPP_INCLUDED
  8. #include <boost/locale/formatting.hpp>
  9. #include <boost/locale/util/string.hpp>
  10. #include <locale>
  11. #include <memory>
  12. #include <set>
  13. #include <string>
  14. #ifdef BOOST_MSVC
  15. # pragma warning(push)
  16. # pragma warning(disable : 4275 4251 4231 4660)
  17. #endif
  18. // glibc < 2.3.4 declares those as macros if compiled with optimization turned on
  19. #ifdef gettext
  20. # undef gettext
  21. # undef ngettext
  22. # undef dgettext
  23. # undef dngettext
  24. #endif
  25. namespace boost { namespace locale {
  26. ///
  27. /// \defgroup message Message Formatting (translation)
  28. ///
  29. /// This module provides message translation functionality, i.e. allow your application to speak native language
  30. ///
  31. /// @{
  32. ///
  33. /// \cond INTERNAL
  34. template<typename CharType>
  35. struct base_message_format;
  36. /// \endcond
  37. /// \brief This facet provides message formatting abilities
  38. template<typename CharType>
  39. class BOOST_SYMBOL_VISIBLE message_format : public base_message_format<CharType> {
  40. public:
  41. /// Character type
  42. typedef CharType char_type;
  43. /// String type
  44. typedef std::basic_string<CharType> string_type;
  45. /// Default constructor
  46. message_format(size_t refs = 0) : base_message_format<CharType>(refs) {}
  47. /// This function returns a pointer to the string for a message defined by a \a context
  48. /// and identification string \a id. Both create a single key for message lookup in
  49. /// a domain defined by \a domain_id.
  50. ///
  51. /// If \a context is NULL it is not considered to be a part of the key
  52. ///
  53. /// If a translated string is found, it is returned, otherwise NULL is returned
  54. virtual const char_type* get(int domain_id, const char_type* context, const char_type* id) const = 0;
  55. /// This function returns a pointer to the string for a plural message defined by a \a context
  56. /// and identification string \a single_id.
  57. ///
  58. /// If \a context is NULL it is not considered to be a part of the key
  59. ///
  60. /// Both create a single key for message lookup in
  61. /// a domain defined \a domain_id. \a n is used to pick the correct translation string for a specific
  62. /// number.
  63. ///
  64. /// If a translated string is found, it is returned, otherwise NULL is returned
  65. virtual const char_type*
  66. get(int domain_id, const char_type* context, const char_type* single_id, int n) const = 0;
  67. /// Convert a string that defines \a domain to the integer id used by \a get functions
  68. virtual int domain(const std::string& domain) const = 0;
  69. /// Convert the string \a msg to target locale's encoding. If \a msg is already
  70. /// in target encoding it would be returned otherwise the converted
  71. /// string is stored in temporary \a buffer and buffer.c_str() is returned.
  72. ///
  73. /// Note: for char_type that is char16_t, char32_t and wchar_t it is no-op, returns
  74. /// msg
  75. virtual const char_type* convert(const char_type* msg, string_type& buffer) const = 0;
  76. protected:
  77. virtual ~message_format() = default;
  78. };
  79. /// \cond INTERNAL
  80. namespace detail {
  81. inline bool is_us_ascii_char(char c)
  82. {
  83. // works for null terminated strings regardless char "signedness"
  84. return 0 < c && c < 0x7F;
  85. }
  86. inline bool is_us_ascii_string(const char* msg)
  87. {
  88. while(*msg) {
  89. if(!is_us_ascii_char(*msg++))
  90. return false;
  91. }
  92. return true;
  93. }
  94. template<typename CharType>
  95. struct string_cast_traits {
  96. static const CharType* cast(const CharType* msg, std::basic_string<CharType>& /*unused*/) { return msg; }
  97. };
  98. template<>
  99. struct string_cast_traits<char> {
  100. static const char* cast(const char* msg, std::string& buffer)
  101. {
  102. if(is_us_ascii_string(msg))
  103. return msg;
  104. buffer.reserve(strlen(msg));
  105. char c;
  106. while((c = *msg++) != 0) {
  107. if(is_us_ascii_char(c))
  108. buffer += c;
  109. }
  110. return buffer.c_str();
  111. }
  112. };
  113. } // namespace detail
  114. /// \endcond
  115. /// \brief This class represents a message that can be converted to a specific locale message
  116. ///
  117. /// It holds the original ASCII string that is queried in the dictionary when converting to the output string.
  118. /// The created string may be UTF-8, UTF-16, UTF-32 or other 8-bit encoded string according to the target
  119. /// character type and locale encoding.
  120. template<typename CharType>
  121. class basic_message {
  122. public:
  123. typedef CharType char_type; ///< The character this message object is used with
  124. typedef std::basic_string<char_type> string_type; ///< The string type this object can be used with
  125. typedef message_format<char_type> facet_type; ///< The type of the facet the messages are fetched with
  126. /// Create default empty message
  127. basic_message() : n_(0), c_id_(0), c_context_(0), c_plural_(0) {}
  128. /// Create a simple message from 0 terminated string. The string should exist
  129. /// until the message is destroyed. Generally useful with static constant strings
  130. explicit basic_message(const char_type* id) : n_(0), c_id_(id), c_context_(0), c_plural_(0) {}
  131. /// Create a simple plural form message from 0 terminated strings. The strings should exist
  132. /// until the message is destroyed. Generally useful with static constant strings.
  133. ///
  134. /// \a n is the number, \a single and \a plural are singular and plural forms of the message
  135. explicit basic_message(const char_type* single, const char_type* plural, int n) :
  136. n_(n), c_id_(single), c_context_(0), c_plural_(plural)
  137. {}
  138. /// Create a simple message from 0 terminated strings, with context
  139. /// information. The string should exist
  140. /// until the message is destroyed. Generally useful with static constant strings
  141. explicit basic_message(const char_type* context, const char_type* id) :
  142. n_(0), c_id_(id), c_context_(context), c_plural_(0)
  143. {}
  144. /// Create a simple plural form message from 0 terminated strings, with context. The strings should exist
  145. /// until the message is destroyed. Generally useful with static constant strings.
  146. ///
  147. /// \a n is the number, \a single and \a plural are singular and plural forms of the message
  148. explicit basic_message(const char_type* context, const char_type* single, const char_type* plural, int n) :
  149. n_(n), c_id_(single), c_context_(context), c_plural_(plural)
  150. {}
  151. /// Create a simple message from a string.
  152. explicit basic_message(const string_type& id) : n_(0), c_id_(0), c_context_(0), c_plural_(0), id_(id) {}
  153. /// Create a simple plural form message from strings.
  154. ///
  155. /// \a n is the number, \a single and \a plural are single and plural forms of the message
  156. explicit basic_message(const string_type& single, const string_type& plural, int number) :
  157. n_(number), c_id_(0), c_context_(0), c_plural_(0), id_(single), plural_(plural)
  158. {}
  159. /// Create a simple message from a string with context.
  160. explicit basic_message(const string_type& context, const string_type& id) :
  161. n_(0), c_id_(0), c_context_(0), c_plural_(0), id_(id), context_(context)
  162. {}
  163. /// Create a simple plural form message from strings.
  164. ///
  165. /// \a n is the number, \a single and \a plural are single and plural forms of the message
  166. explicit basic_message(const string_type& context,
  167. const string_type& single,
  168. const string_type& plural,
  169. int number) :
  170. n_(number),
  171. c_id_(0), c_context_(0), c_plural_(0), id_(single), context_(context), plural_(plural)
  172. {}
  173. /// Copy an object
  174. basic_message(const basic_message&) = default;
  175. basic_message(basic_message&&) = default;
  176. /// Assign other message object to this one
  177. basic_message& operator=(const basic_message&) = default;
  178. basic_message& operator=(basic_message&&) = default;
  179. /// Swap two message objects
  180. void swap(basic_message& other)
  181. {
  182. std::swap(n_, other.n_);
  183. std::swap(c_id_, other.c_id_);
  184. std::swap(c_context_, other.c_context_);
  185. std::swap(c_plural_, other.c_plural_);
  186. id_.swap(other.id_);
  187. context_.swap(other.context_);
  188. plural_.swap(other.plural_);
  189. }
  190. /// Message class can be explicitly converted to string class
  191. operator string_type() const { return str(); }
  192. /// Translate message to a string in the default global locale, using default domain
  193. string_type str() const
  194. {
  195. std::locale loc;
  196. return str(loc, 0);
  197. }
  198. /// Translate message to a string in the locale \a locale, using default domain
  199. string_type str(const std::locale& locale) const { return str(locale, 0); }
  200. /// Translate message to a string using locale \a locale and message domain \a domain_id
  201. string_type str(const std::locale& locale, const std::string& domain_id) const
  202. {
  203. int id = 0;
  204. if(std::has_facet<facet_type>(locale))
  205. id = std::use_facet<facet_type>(locale).domain(domain_id);
  206. return str(locale, id);
  207. }
  208. /// Translate message to a string using the default locale and message domain \a domain_id
  209. string_type str(const std::string& domain_id) const
  210. {
  211. int id = 0;
  212. std::locale loc;
  213. if(std::has_facet<facet_type>(loc))
  214. id = std::use_facet<facet_type>(loc).domain(domain_id);
  215. return str(loc, id);
  216. }
  217. /// Translate message to a string using locale \a loc and message domain index \a id
  218. string_type str(const std::locale& loc, int id) const
  219. {
  220. string_type buffer;
  221. const char_type* ptr = write(loc, id, buffer);
  222. if(ptr == buffer.c_str())
  223. return buffer;
  224. else
  225. buffer = ptr;
  226. return buffer;
  227. }
  228. /// Translate message and write to stream \a out, using imbued locale and domain set to the
  229. /// stream
  230. void write(std::basic_ostream<char_type>& out) const
  231. {
  232. const std::locale& loc = out.getloc();
  233. int id = ios_info::get(out).domain_id();
  234. string_type buffer;
  235. out << write(loc, id, buffer);
  236. }
  237. private:
  238. const char_type* plural() const
  239. {
  240. if(c_plural_)
  241. return c_plural_;
  242. if(plural_.empty())
  243. return 0;
  244. return plural_.c_str();
  245. }
  246. const char_type* context() const
  247. {
  248. if(c_context_)
  249. return c_context_;
  250. if(context_.empty())
  251. return 0;
  252. return context_.c_str();
  253. }
  254. const char_type* id() const { return c_id_ ? c_id_ : id_.c_str(); }
  255. const char_type* write(const std::locale& loc, int domain_id, string_type& buffer) const
  256. {
  257. const char_type* translated = 0;
  258. static const char_type empty_string[1] = {0};
  259. const char_type* id = this->id();
  260. const char_type* context = this->context();
  261. const char_type* plural = this->plural();
  262. if(*id == 0)
  263. return empty_string;
  264. const facet_type* facet = 0;
  265. if(std::has_facet<facet_type>(loc))
  266. facet = &std::use_facet<facet_type>(loc);
  267. if(facet) {
  268. if(!plural) {
  269. translated = facet->get(domain_id, context, id);
  270. } else {
  271. translated = facet->get(domain_id, context, id, n_);
  272. }
  273. }
  274. if(!translated) {
  275. const char_type* msg = plural ? (n_ == 1 ? id : plural) : id;
  276. if(facet) {
  277. translated = facet->convert(msg, buffer);
  278. } else {
  279. translated = detail::string_cast_traits<char_type>::cast(msg, buffer);
  280. }
  281. }
  282. return translated;
  283. }
  284. /// members
  285. int n_;
  286. const char_type* c_id_;
  287. const char_type* c_context_;
  288. const char_type* c_plural_;
  289. string_type id_;
  290. string_type context_;
  291. string_type plural_;
  292. };
  293. /// Convenience typedef for char
  294. typedef basic_message<char> message;
  295. /// Convenience typedef for wchar_t
  296. typedef basic_message<wchar_t> wmessage;
  297. #ifdef BOOST_LOCALE_ENABLE_CHAR16_T
  298. /// Convenience typedef for char16_t
  299. typedef basic_message<char16_t> u16message;
  300. #endif
  301. #ifdef BOOST_LOCALE_ENABLE_CHAR32_T
  302. /// Convenience typedef for char32_t
  303. typedef basic_message<char32_t> u32message;
  304. #endif
  305. /// Translate message \a msg and write it to stream
  306. template<typename CharType>
  307. std::basic_ostream<CharType>& operator<<(std::basic_ostream<CharType>& out, const basic_message<CharType>& msg)
  308. {
  309. msg.write(out);
  310. return out;
  311. }
  312. /// \anchor boost_locale_translate_family \name Indirect message translation function family
  313. /// @{
  314. /// \brief Translate a message, \a msg is not copied
  315. template<typename CharType>
  316. inline basic_message<CharType> translate(const CharType* msg)
  317. {
  318. return basic_message<CharType>(msg);
  319. }
  320. /// \brief Translate a message in context, \a msg and \a context are not copied
  321. template<typename CharType>
  322. inline basic_message<CharType> translate(const CharType* context, const CharType* msg)
  323. {
  324. return basic_message<CharType>(context, msg);
  325. }
  326. /// \brief Translate a plural message form, \a single and \a plural are not copied
  327. template<typename CharType>
  328. inline basic_message<CharType> translate(const CharType* single, const CharType* plural, int n)
  329. {
  330. return basic_message<CharType>(single, plural, n);
  331. }
  332. /// \brief Translate a plural message from in constext, \a context, \a single and \a plural are not copied
  333. template<typename CharType>
  334. inline basic_message<CharType>
  335. translate(const CharType* context, const CharType* single, const CharType* plural, int n)
  336. {
  337. return basic_message<CharType>(context, single, plural, n);
  338. }
  339. /// \brief Translate a message, \a msg is copied
  340. template<typename CharType>
  341. inline basic_message<CharType> translate(const std::basic_string<CharType>& msg)
  342. {
  343. return basic_message<CharType>(msg);
  344. }
  345. /// \brief Translate a message in context,\a context and \a msg is copied
  346. template<typename CharType>
  347. inline basic_message<CharType> translate(const std::basic_string<CharType>& context,
  348. const std::basic_string<CharType>& msg)
  349. {
  350. return basic_message<CharType>(context, msg);
  351. }
  352. /// \brief Translate a plural message form in constext, \a context, \a single and \a plural are copied
  353. template<typename CharType>
  354. inline basic_message<CharType> translate(const std::basic_string<CharType>& context,
  355. const std::basic_string<CharType>& single,
  356. const std::basic_string<CharType>& plural,
  357. int n)
  358. {
  359. return basic_message<CharType>(context, single, plural, n);
  360. }
  361. /// \brief Translate a plural message form, \a single and \a plural are copied
  362. template<typename CharType>
  363. inline basic_message<CharType>
  364. translate(const std::basic_string<CharType>& single, const std::basic_string<CharType>& plural, int n)
  365. {
  366. return basic_message<CharType>(single, plural, n);
  367. }
  368. /// @}
  369. /// \anchor boost_locale_gettext_family \name Direct message translation functions family
  370. /// Translate message \a id according to locale \a loc
  371. template<typename CharType>
  372. std::basic_string<CharType> gettext(const CharType* id, const std::locale& loc = std::locale())
  373. {
  374. return basic_message<CharType>(id).str(loc);
  375. }
  376. /// Translate plural form according to locale \a loc
  377. template<typename CharType>
  378. std::basic_string<CharType>
  379. ngettext(const CharType* s, const CharType* p, int n, const std::locale& loc = std::locale())
  380. {
  381. return basic_message<CharType>(s, p, n).str(loc);
  382. }
  383. /// Translate message \a id according to locale \a loc in domain \a domain
  384. template<typename CharType>
  385. std::basic_string<CharType> dgettext(const char* domain, const CharType* id, const std::locale& loc = std::locale())
  386. {
  387. return basic_message<CharType>(id).str(loc, domain);
  388. }
  389. /// Translate plural form according to locale \a loc in domain \a domain
  390. template<typename CharType>
  391. std::basic_string<CharType>
  392. dngettext(const char* domain, const CharType* s, const CharType* p, int n, const std::locale& loc = std::locale())
  393. {
  394. return basic_message<CharType>(s, p, n).str(loc, domain);
  395. }
  396. /// Translate message \a id according to locale \a loc in context \a context
  397. template<typename CharType>
  398. std::basic_string<CharType>
  399. pgettext(const CharType* context, const CharType* id, const std::locale& loc = std::locale())
  400. {
  401. return basic_message<CharType>(context, id).str(loc);
  402. }
  403. /// Translate plural form according to locale \a loc in context \a context
  404. template<typename CharType>
  405. std::basic_string<CharType> npgettext(const CharType* context,
  406. const CharType* s,
  407. const CharType* p,
  408. int n,
  409. const std::locale& loc = std::locale())
  410. {
  411. return basic_message<CharType>(context, s, p, n).str(loc);
  412. }
  413. /// Translate message \a id according to locale \a loc in domain \a domain in context \a context
  414. template<typename CharType>
  415. std::basic_string<CharType>
  416. dpgettext(const char* domain, const CharType* context, const CharType* id, const std::locale& loc = std::locale())
  417. {
  418. return basic_message<CharType>(context, id).str(loc, domain);
  419. }
  420. /// Translate plural form according to locale \a loc in domain \a domain in context \a context
  421. template<typename CharType>
  422. std::basic_string<CharType> dnpgettext(const char* domain,
  423. const CharType* context,
  424. const CharType* s,
  425. const CharType* p,
  426. int n,
  427. const std::locale& loc = std::locale())
  428. {
  429. return basic_message<CharType>(context, s, p, n).str(loc, domain);
  430. }
  431. /// \cond INTERNAL
  432. template<>
  433. struct BOOST_LOCALE_DECL base_message_format<char> : public std::locale::facet {
  434. base_message_format(size_t refs = 0) : std::locale::facet(refs) {}
  435. ~base_message_format();
  436. static std::locale::id id;
  437. };
  438. template<>
  439. struct BOOST_LOCALE_DECL base_message_format<wchar_t> : public std::locale::facet {
  440. base_message_format(size_t refs = 0) : std::locale::facet(refs) {}
  441. ~base_message_format();
  442. static std::locale::id id;
  443. };
  444. #ifdef BOOST_LOCALE_ENABLE_CHAR16_T
  445. template<>
  446. struct BOOST_LOCALE_DECL base_message_format<char16_t> : public std::locale::facet {
  447. base_message_format(size_t refs = 0) : std::locale::facet(refs) {}
  448. ~base_message_format();
  449. static std::locale::id id;
  450. };
  451. #endif
  452. #ifdef BOOST_LOCALE_ENABLE_CHAR32_T
  453. template<>
  454. struct BOOST_LOCALE_DECL base_message_format<char32_t> : public std::locale::facet {
  455. base_message_format(size_t refs = 0) : std::locale::facet(refs) {}
  456. ~base_message_format();
  457. static std::locale::id id;
  458. };
  459. #endif
  460. /// \endcond
  461. /// @}
  462. namespace as {
  463. /// \cond INTERNAL
  464. namespace detail {
  465. struct set_domain {
  466. std::string domain_id;
  467. };
  468. template<typename CharType>
  469. std::basic_ostream<CharType>& operator<<(std::basic_ostream<CharType>& out, const set_domain& dom)
  470. {
  471. int id = std::use_facet<message_format<CharType>>(out.getloc()).domain(dom.domain_id);
  472. ios_info::get(out).domain_id(id);
  473. return out;
  474. }
  475. } // namespace detail
  476. /// \endcond
  477. /// \addtogroup manipulators
  478. ///
  479. /// @{
  480. /// Manipulator for switching message domain in ostream,
  481. ///
  482. /// \note The returned object throws std::bad_cast if the I/O stream does not have \ref message_format facet
  483. /// installed
  484. inline
  485. #ifdef BOOST_LOCALE_DOXYGEN
  486. unspecified_type
  487. #else
  488. detail::set_domain
  489. #endif
  490. domain(const std::string& id)
  491. {
  492. detail::set_domain tmp = {id};
  493. return tmp;
  494. }
  495. /// @}
  496. } // namespace as
  497. }} // namespace boost::locale
  498. #ifdef BOOST_MSVC
  499. # pragma warning(pop)
  500. #endif
  501. #endif