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