1 // 2 // Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) 3 // 4 // Distributed under the Boost Software License, Version 1.0. (See 5 // accompanying file LICENSE_1_0.txt or copy at 6 // http://www.boost.org/LICENSE_1_0.txt) 7 // 8 #ifndef BOOST_LOCALE_MESSAGE_HPP_INCLUDED 9 #define BOOST_LOCALE_MESSAGE_HPP_INCLUDED 10 11 #include <boost/locale/config.hpp> 12 #ifdef BOOST_MSVC 13 # pragma warning(push) 14 # pragma warning(disable : 4275 4251 4231 4660) 15 #endif 16 #include <locale> 17 #include <string> 18 #include <vector> 19 #include <set> 20 #include <memory> 21 #include <boost/locale/formatting.hpp> 22 23 // glibc < 2.3.4 declares those as macros if compiled with optimization turned on 24 #ifdef gettext 25 # undef gettext 26 # undef ngettext 27 # undef dgettext 28 # undef dngettext 29 #endif 30 31 namespace boost { 32 namespace locale { 33 /// 34 /// \defgroup message Message Formatting (translation) 35 /// 36 ///This module provides message translation functionality, i.e. allow your application to speak native language 37 /// 38 /// @{ 39 /// 40 41 /// \cond INTERNAL 42 43 template<typename CharType> 44 struct base_message_format: public std::locale::facet 45 { 46 }; 47 48 /// \endcond 49 50 /// 51 /// \brief This facet provides message formatting abilities 52 /// 53 template<typename CharType> 54 class message_format : public base_message_format<CharType> 55 { 56 public: 57 58 /// 59 /// Character type 60 /// 61 typedef CharType char_type; 62 /// 63 /// String type 64 /// 65 typedef std::basic_string<CharType> string_type; 66 67 /// 68 /// Default constructor 69 /// message_format(size_t refs=0)70 message_format(size_t refs = 0) : 71 base_message_format<CharType>(refs) 72 { 73 } 74 75 /// 76 /// This function returns a pointer to the string for a message defined by a \a context 77 /// and identification string \a id. Both create a single key for message lookup in 78 /// a domain defined by \a domain_id. 79 /// 80 /// If \a context is NULL it is not considered to be a part of the key 81 /// 82 /// If a translated string is found, it is returned, otherwise NULL is returned 83 /// 84 /// 85 virtual char_type const *get(int domain_id,char_type const *context,char_type const *id) const = 0; 86 /// 87 /// This function returns a pointer to the string for a plural message defined by a \a context 88 /// and identification string \a single_id. 89 /// 90 /// If \a context is NULL it is not considered to be a part of the key 91 /// 92 /// Both create a single key for message lookup in 93 /// a domain defined \a domain_id. \a n is used to pick the correct translation string for a specific 94 /// number. 95 /// 96 /// If a translated string is found, it is returned, otherwise NULL is returned 97 /// 98 /// 99 virtual char_type const *get(int domain_id,char_type const *context,char_type const *single_id,int n) const = 0; 100 101 /// 102 /// Convert a string that defines \a domain to the integer id used by \a get functions 103 /// 104 virtual int domain(std::string const &domain) const = 0; 105 106 /// 107 /// Convert the string \a msg to target locale's encoding. If \a msg is already 108 /// in target encoding it would be returned otherwise the converted 109 /// string is stored in temporary \a buffer and buffer.c_str() is returned. 110 /// 111 /// Note: for char_type that is char16_t, char32_t and wchar_t it is no-op, returns 112 /// msg 113 /// 114 virtual char_type const *convert(char_type const *msg,string_type &buffer) const = 0; 115 116 #if defined (__SUNPRO_CC) && defined (_RWSTD_VER) __get_id(void) const117 std::locale::id& __get_id (void) const { return id; } 118 #endif 119 protected: ~message_format()120 virtual ~message_format() 121 { 122 } 123 124 }; 125 126 /// \cond INTERNAL 127 128 namespace details { is_us_ascii_char(char c)129 inline bool is_us_ascii_char(char c) 130 { 131 // works for null terminated strings regardless char "signness" 132 return 0<c && c<0x7F; 133 } is_us_ascii_string(char const * msg)134 inline bool is_us_ascii_string(char const *msg) 135 { 136 while(*msg) { 137 if(!is_us_ascii_char(*msg++)) 138 return false; 139 } 140 return true; 141 } 142 143 template<typename CharType> 144 struct string_cast_traits { castboost::locale::details::string_cast_traits145 static CharType const *cast(CharType const *msg,std::basic_string<CharType> &/*unused*/) 146 { 147 return msg; 148 } 149 }; 150 151 template<> 152 struct string_cast_traits<char> { castboost::locale::details::string_cast_traits153 static char const *cast(char const *msg,std::string &buffer) 154 { 155 if(is_us_ascii_string(msg)) 156 return msg; 157 buffer.reserve(strlen(msg)); 158 char c; 159 while((c=*msg++)!=0) { 160 if(is_us_ascii_char(c)) 161 buffer+=c; 162 } 163 return buffer.c_str(); 164 } 165 }; 166 } // details 167 168 /// \endcond 169 170 /// 171 /// \brief This class represents a message that can be converted to a specific locale message 172 /// 173 /// It holds the original ASCII string that is queried in the dictionary when converting to the output string. 174 /// The created string may be UTF-8, UTF-16, UTF-32 or other 8-bit encoded string according to the target 175 /// character type and locale encoding. 176 /// 177 template<typename CharType> 178 class basic_message { 179 public: 180 181 typedef CharType char_type; ///< The character this message object is used with 182 typedef std::basic_string<char_type> string_type; ///< The string type this object can be used with 183 typedef message_format<char_type> facet_type; ///< The type of the facet the messages are fetched with 184 185 /// 186 /// Create default empty message 187 /// basic_message()188 basic_message() : 189 n_(0), 190 c_id_(0), 191 c_context_(0), 192 c_plural_(0) 193 { 194 } 195 196 /// 197 /// Create a simple message from 0 terminated string. The string should exist 198 /// until the message is destroyed. Generally useful with static constant strings 199 /// basic_message(char_type const * id)200 explicit basic_message(char_type const *id) : 201 n_(0), 202 c_id_(id), 203 c_context_(0), 204 c_plural_(0) 205 { 206 } 207 208 /// 209 /// Create a simple plural form message from 0 terminated strings. The strings should exist 210 /// until the message is destroyed. Generally useful with static constant strings. 211 /// 212 /// \a n is the number, \a single and \a plural are singular and plural forms of the message 213 /// basic_message(char_type const * single,char_type const * plural,int n)214 explicit basic_message(char_type const *single,char_type const *plural,int n) : 215 n_(n), 216 c_id_(single), 217 c_context_(0), 218 c_plural_(plural) 219 { 220 } 221 222 /// 223 /// Create a simple message from 0 terminated strings, with context 224 /// information. The string should exist 225 /// until the message is destroyed. Generally useful with static constant strings 226 /// basic_message(char_type const * context,char_type const * id)227 explicit basic_message(char_type const *context,char_type const *id) : 228 n_(0), 229 c_id_(id), 230 c_context_(context), 231 c_plural_(0) 232 { 233 } 234 235 /// 236 /// Create a simple plural form message from 0 terminated strings, with context. The strings should exist 237 /// until the message is destroyed. Generally useful with static constant strings. 238 /// 239 /// \a n is the number, \a single and \a plural are singular and plural forms of the message 240 /// basic_message(char_type const * context,char_type const * single,char_type const * plural,int n)241 explicit basic_message(char_type const *context,char_type const *single,char_type const *plural,int n) : 242 n_(n), 243 c_id_(single), 244 c_context_(context), 245 c_plural_(plural) 246 { 247 } 248 249 250 /// 251 /// Create a simple message from a string. 252 /// basic_message(string_type const & id)253 explicit basic_message(string_type const &id) : 254 n_(0), 255 c_id_(0), 256 c_context_(0), 257 c_plural_(0), 258 id_(id) 259 { 260 } 261 262 /// 263 /// Create a simple plural form message from strings. 264 /// 265 /// \a n is the number, \a single and \a plural are single and plural forms of the message 266 /// basic_message(string_type const & single,string_type const & plural,int number)267 explicit basic_message(string_type const &single,string_type const &plural,int number) : 268 n_(number), 269 c_id_(0), 270 c_context_(0), 271 c_plural_(0), 272 id_(single), 273 plural_(plural) 274 { 275 } 276 277 /// 278 /// Create a simple message from a string with context. 279 /// basic_message(string_type const & context,string_type const & id)280 explicit basic_message(string_type const &context,string_type const &id) : 281 n_(0), 282 c_id_(0), 283 c_context_(0), 284 c_plural_(0), 285 id_(id), 286 context_(context) 287 { 288 } 289 290 /// 291 /// Create a simple plural form message from strings. 292 /// 293 /// \a n is the number, \a single and \a plural are single and plural forms of the message 294 /// basic_message(string_type const & context,string_type const & single,string_type const & plural,int number)295 explicit basic_message(string_type const &context,string_type const &single,string_type const &plural,int number) : 296 n_(number), 297 c_id_(0), 298 c_context_(0), 299 c_plural_(0), 300 id_(single), 301 context_(context), 302 plural_(plural) 303 { 304 } 305 306 /// 307 /// Copy an object 308 /// basic_message(basic_message const & other)309 basic_message(basic_message const &other) : 310 n_(other.n_), 311 c_id_(other.c_id_), 312 c_context_(other.c_context_), 313 c_plural_(other.c_plural_), 314 id_(other.id_), 315 context_(other.context_), 316 plural_(other.plural_) 317 { 318 } 319 320 /// 321 /// Assign other message object to this one 322 /// operator =(basic_message const & other)323 basic_message const &operator=(basic_message const &other) 324 { 325 if(this==&other) { 326 return *this; 327 } 328 basic_message tmp(other); 329 swap(tmp); 330 return *this; 331 } 332 333 /// 334 /// Swap two message objects 335 /// swap(basic_message & other)336 void swap(basic_message &other) 337 { 338 std::swap(n_,other.n_); 339 std::swap(c_id_,other.c_id_); 340 std::swap(c_context_,other.c_context_); 341 std::swap(c_plural_,other.c_plural_); 342 343 id_.swap(other.id_); 344 context_.swap(other.context_); 345 plural_.swap(other.plural_); 346 } 347 348 /// 349 /// Message class can be explicitly converted to string class 350 /// 351 operator string_type() const352 operator string_type () const 353 { 354 return str(); 355 } 356 357 /// 358 /// Translate message to a string in the default global locale, using default domain 359 /// str() const360 string_type str() const 361 { 362 std::locale loc; 363 return str(loc,0); 364 } 365 366 /// 367 /// Translate message to a string in the locale \a locale, using default domain 368 /// str(std::locale const & locale) const369 string_type str(std::locale const &locale) const 370 { 371 return str(locale,0); 372 } 373 374 /// 375 /// Translate message to a string using locale \a locale and message domain \a domain_id 376 /// str(std::locale const & locale,std::string const & domain_id) const377 string_type str(std::locale const &locale,std::string const &domain_id) const 378 { 379 int id=0; 380 if(std::has_facet<facet_type>(locale)) 381 id=std::use_facet<facet_type>(locale).domain(domain_id); 382 return str(locale,id); 383 } 384 385 /// 386 /// Translate message to a string using the default locale and message domain \a domain_id 387 /// str(std::string const & domain_id) const388 string_type str(std::string const &domain_id) const 389 { 390 int id=0; 391 std::locale loc; 392 if(std::has_facet<facet_type>(loc)) 393 id=std::use_facet<facet_type>(loc).domain(domain_id); 394 return str(loc,id); 395 } 396 397 398 /// 399 /// Translate message to a string using locale \a loc and message domain index \a id 400 /// str(std::locale const & loc,int id) const401 string_type str(std::locale const &loc,int id) const 402 { 403 string_type buffer; 404 char_type const *ptr = write(loc,id,buffer); 405 if(ptr == buffer.c_str()) 406 return buffer; 407 else 408 buffer = ptr; 409 return buffer; 410 } 411 412 413 /// 414 /// Translate message and write to stream \a out, using imbued locale and domain set to the 415 /// stream 416 /// write(std::basic_ostream<char_type> & out) const417 void write(std::basic_ostream<char_type> &out) const 418 { 419 std::locale const &loc = out.getloc(); 420 int id = ios_info::get(out).domain_id(); 421 string_type buffer; 422 out << write(loc,id,buffer); 423 } 424 425 private: plural() const426 char_type const *plural() const 427 { 428 if(c_plural_) 429 return c_plural_; 430 if(plural_.empty()) 431 return 0; 432 return plural_.c_str(); 433 } context() const434 char_type const *context() const 435 { 436 if(c_context_) 437 return c_context_; 438 if(context_.empty()) 439 return 0; 440 return context_.c_str(); 441 } 442 id() const443 char_type const *id() const 444 { 445 return c_id_ ? c_id_ : id_.c_str(); 446 } 447 write(std::locale const & loc,int domain_id,string_type & buffer) const448 char_type const *write(std::locale const &loc,int domain_id,string_type &buffer) const 449 { 450 char_type const *translated = 0; 451 static const char_type empty_string[1] = {0}; 452 453 char_type const *id = this->id(); 454 char_type const *context = this->context(); 455 char_type const *plural = this->plural(); 456 457 if(*id == 0) 458 return empty_string; 459 460 facet_type const *facet = 0; 461 if(std::has_facet<facet_type>(loc)) 462 facet = &std::use_facet<facet_type>(loc); 463 464 if(facet) { 465 if(!plural) { 466 translated = facet->get(domain_id,context,id); 467 } 468 else { 469 translated = facet->get(domain_id,context,id,n_); 470 } 471 } 472 473 if(!translated) { 474 char_type const *msg = plural ? ( n_ == 1 ? id : plural) : id; 475 476 if(facet) { 477 translated = facet->convert(msg,buffer); 478 } 479 else { 480 translated = details::string_cast_traits<char_type>::cast(msg,buffer); 481 } 482 } 483 return translated; 484 } 485 486 /// members 487 488 int n_; 489 char_type const *c_id_; 490 char_type const *c_context_; 491 char_type const *c_plural_; 492 string_type id_; 493 string_type context_; 494 string_type plural_; 495 }; 496 497 498 /// 499 /// Convenience typedef for char 500 /// 501 typedef basic_message<char> message; 502 /// 503 /// Convenience typedef for wchar_t 504 /// 505 typedef basic_message<wchar_t> wmessage; 506 #ifdef BOOST_LOCALE_ENABLE_CHAR16_T 507 /// 508 /// Convenience typedef for char16_t 509 /// 510 typedef basic_message<char16_t> u16message; 511 #endif 512 #ifdef BOOST_LOCALE_ENABLE_CHAR32_T 513 /// 514 /// Convenience typedef for char32_t 515 /// 516 typedef basic_message<char32_t> u32message; 517 #endif 518 519 /// 520 /// Translate message \a msg and write it to stream 521 /// 522 template<typename CharType> operator <<(std::basic_ostream<CharType> & out,basic_message<CharType> const & msg)523 std::basic_ostream<CharType> &operator<<(std::basic_ostream<CharType> &out,basic_message<CharType> const &msg) 524 { 525 msg.write(out); 526 return out; 527 } 528 529 /// 530 /// \anchor boost_locale_translate_family \name Indirect message translation function family 531 /// @{ 532 533 /// 534 /// \brief Translate a message, \a msg is not copied 535 /// 536 template<typename CharType> translate(CharType const * msg)537 inline basic_message<CharType> translate(CharType const *msg) 538 { 539 return basic_message<CharType>(msg); 540 } 541 /// 542 /// \brief Translate a message in context, \a msg and \a context are not copied 543 /// 544 template<typename CharType> translate(CharType const * context,CharType const * msg)545 inline basic_message<CharType> translate( CharType const *context, 546 CharType const *msg) 547 { 548 return basic_message<CharType>(context,msg); 549 } 550 /// 551 /// \brief Translate a plural message form, \a single and \a plural are not copied 552 /// 553 template<typename CharType> translate(CharType const * single,CharType const * plural,int n)554 inline basic_message<CharType> translate( CharType const *single, 555 CharType const *plural, 556 int n) 557 { 558 return basic_message<CharType>(single,plural,n); 559 } 560 /// 561 /// \brief Translate a plural message from in constext, \a context, \a single and \a plural are not copied 562 /// 563 template<typename CharType> translate(CharType const * context,CharType const * single,CharType const * plural,int n)564 inline basic_message<CharType> translate( CharType const *context, 565 CharType const *single, 566 CharType const *plural, 567 int n) 568 { 569 return basic_message<CharType>(context,single,plural,n); 570 } 571 572 /// 573 /// \brief Translate a message, \a msg is copied 574 /// 575 template<typename CharType> translate(std::basic_string<CharType> const & msg)576 inline basic_message<CharType> translate(std::basic_string<CharType> const &msg) 577 { 578 return basic_message<CharType>(msg); 579 } 580 581 /// 582 /// \brief Translate a message in context,\a context and \a msg is copied 583 /// 584 template<typename CharType> translate(std::basic_string<CharType> const & context,std::basic_string<CharType> const & msg)585 inline basic_message<CharType> translate( std::basic_string<CharType> const &context, 586 std::basic_string<CharType> const &msg) 587 { 588 return basic_message<CharType>(context,msg); 589 } 590 /// 591 /// \brief Translate a plural message form in constext, \a context, \a single and \a plural are copied 592 /// 593 template<typename CharType> translate(std::basic_string<CharType> const & context,std::basic_string<CharType> const & single,std::basic_string<CharType> const & plural,int n)594 inline basic_message<CharType> translate( std::basic_string<CharType> const &context, 595 std::basic_string<CharType> const &single, 596 std::basic_string<CharType> const &plural, 597 int n) 598 { 599 return basic_message<CharType>(context,single,plural,n); 600 } 601 602 /// 603 /// \brief Translate a plural message form, \a single and \a plural are copied 604 /// 605 606 template<typename CharType> translate(std::basic_string<CharType> const & single,std::basic_string<CharType> const & plural,int n)607 inline basic_message<CharType> translate( std::basic_string<CharType> const &single, 608 std::basic_string<CharType> const &plural, 609 int n) 610 { 611 return basic_message<CharType>(single,plural,n); 612 } 613 614 /// @} 615 616 /// 617 /// \anchor boost_locale_gettext_family \name Direct message translation functions family 618 /// 619 620 /// 621 /// Translate message \a id according to locale \a loc 622 /// 623 template<typename CharType> gettext(CharType const * id,std::locale const & loc=std::locale ())624 std::basic_string<CharType> gettext(CharType const *id, 625 std::locale const &loc=std::locale()) 626 { 627 return basic_message<CharType>(id).str(loc); 628 } 629 /// 630 /// Translate plural form according to locale \a loc 631 /// 632 template<typename CharType> ngettext(CharType const * s,CharType const * p,int n,std::locale const & loc=std::locale ())633 std::basic_string<CharType> ngettext( CharType const *s, 634 CharType const *p, 635 int n, 636 std::locale const &loc=std::locale()) 637 { 638 return basic_message<CharType>(s,p,n).str(loc); 639 } 640 /// 641 /// Translate message \a id according to locale \a loc in domain \a domain 642 /// 643 template<typename CharType> dgettext(char const * domain,CharType const * id,std::locale const & loc=std::locale ())644 std::basic_string<CharType> dgettext( char const *domain, 645 CharType const *id, 646 std::locale const &loc=std::locale()) 647 { 648 return basic_message<CharType>(id).str(loc,domain); 649 } 650 651 /// 652 /// Translate plural form according to locale \a loc in domain \a domain 653 /// 654 template<typename CharType> dngettext(char const * domain,CharType const * s,CharType const * p,int n,std::locale const & loc=std::locale ())655 std::basic_string<CharType> dngettext( char const *domain, 656 CharType const *s, 657 CharType const *p, 658 int n, 659 std::locale const &loc=std::locale()) 660 { 661 return basic_message<CharType>(s,p,n).str(loc,domain); 662 } 663 /// 664 /// Translate message \a id according to locale \a loc in context \a context 665 /// 666 template<typename CharType> pgettext(CharType const * context,CharType const * id,std::locale const & loc=std::locale ())667 std::basic_string<CharType> pgettext( CharType const *context, 668 CharType const *id, 669 std::locale const &loc=std::locale()) 670 { 671 return basic_message<CharType>(context,id).str(loc); 672 } 673 /// 674 /// Translate plural form according to locale \a loc in context \a context 675 /// 676 template<typename CharType> npgettext(CharType const * context,CharType const * s,CharType const * p,int n,std::locale const & loc=std::locale ())677 std::basic_string<CharType> npgettext( CharType const *context, 678 CharType const *s, 679 CharType const *p, 680 int n, 681 std::locale const &loc=std::locale()) 682 { 683 return basic_message<CharType>(context,s,p,n).str(loc); 684 } 685 /// 686 /// Translate message \a id according to locale \a loc in domain \a domain in context \a context 687 /// 688 template<typename CharType> dpgettext(char const * domain,CharType const * context,CharType const * id,std::locale const & loc=std::locale ())689 std::basic_string<CharType> dpgettext( char const *domain, 690 CharType const *context, 691 CharType const *id, 692 std::locale const &loc=std::locale()) 693 { 694 return basic_message<CharType>(context,id).str(loc,domain); 695 } 696 /// 697 /// Translate plural form according to locale \a loc in domain \a domain in context \a context 698 /// 699 template<typename CharType> dnpgettext(char const * domain,CharType const * context,CharType const * s,CharType const * p,int n,std::locale const & loc=std::locale ())700 std::basic_string<CharType> dnpgettext(char const *domain, 701 CharType const *context, 702 CharType const *s, 703 CharType const *p, 704 int n, 705 std::locale const &loc=std::locale()) 706 { 707 return basic_message<CharType>(context,s,p,n).str(loc,domain); 708 } 709 710 /// 711 /// \cond INTERNAL 712 /// 713 714 template<> 715 struct BOOST_LOCALE_DECL base_message_format<char> : public std::locale::facet 716 { base_message_formatboost::locale::base_message_format717 base_message_format(size_t refs = 0) : std::locale::facet(refs) 718 { 719 } 720 static std::locale::id id; 721 }; 722 723 template<> 724 struct BOOST_LOCALE_DECL base_message_format<wchar_t> : public std::locale::facet 725 { base_message_formatboost::locale::base_message_format726 base_message_format(size_t refs = 0) : std::locale::facet(refs) 727 { 728 } 729 static std::locale::id id; 730 }; 731 732 #ifdef BOOST_LOCALE_ENABLE_CHAR16_T 733 734 template<> 735 struct BOOST_LOCALE_DECL base_message_format<char16_t> : public std::locale::facet 736 { base_message_formatboost::locale::base_message_format737 base_message_format(size_t refs = 0) : std::locale::facet(refs) 738 { 739 } 740 static std::locale::id id; 741 }; 742 743 #endif 744 745 #ifdef BOOST_LOCALE_ENABLE_CHAR32_T 746 747 template<> 748 struct BOOST_LOCALE_DECL base_message_format<char32_t> : public std::locale::facet 749 { base_message_formatboost::locale::base_message_format750 base_message_format(size_t refs = 0) : std::locale::facet(refs) 751 { 752 } 753 static std::locale::id id; 754 }; 755 756 #endif 757 758 /// \endcond 759 760 /// 761 /// @} 762 /// 763 764 namespace as { 765 /// \cond INTERNAL 766 namespace details { 767 struct set_domain { 768 std::string domain_id; 769 }; 770 template<typename CharType> operator <<(std::basic_ostream<CharType> & out,set_domain const & dom)771 std::basic_ostream<CharType> &operator<<(std::basic_ostream<CharType> &out, set_domain const &dom) 772 { 773 int id = std::use_facet<message_format<CharType> >(out.getloc()).domain(dom.domain_id); 774 ios_info::get(out).domain_id(id); 775 return out; 776 } 777 } // details 778 /// \endcond 779 780 /// 781 /// \addtogroup manipulators 782 /// 783 /// @{ 784 785 /// 786 /// Manipulator for switching message domain in ostream, 787 /// 788 /// \note The returned object throws std::bad_cast if the I/O stream does not have \ref message_format facet installed 789 /// 790 inline 791 #ifdef BOOST_LOCALE_DOXYGEN 792 unspecified_type 793 #else 794 details::set_domain 795 #endif domain(std::string const & id)796 domain(std::string const &id) 797 { 798 details::set_domain tmp = { id }; 799 return tmp; 800 } 801 /// @} 802 } // as 803 } // locale 804 } // boost 805 806 #ifdef BOOST_MSVC 807 #pragma warning(pop) 808 #endif 809 810 811 #endif 812 813 // vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 814 815