Boost.Locale
message.hpp
1//
2// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
3// Copyright (c) 2021-2023 Alexander Grund
4//
5// Distributed under the Boost Software License, Version 1.0.
6// https://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/detail/facet_id.hpp>
12#include <boost/locale/detail/is_supported_char.hpp>
13#include <boost/locale/formatting.hpp>
14#include <boost/locale/util/string.hpp>
15#include <locale>
16#include <memory>
17#include <set>
18#include <string>
19#include <type_traits>
20
21#ifdef BOOST_MSVC
22# pragma warning(push)
23# pragma warning(disable : 4275 4251 4231 4660)
24#endif
25
26// glibc < 2.3.4 declares those as macros if compiled with optimization turned on
27#ifdef gettext
28# undef gettext
29# undef ngettext
30# undef dgettext
31# undef dngettext
32#endif
33
34namespace boost { namespace locale {
42
44 using count_type = long long;
45
47 template<typename CharType>
48 class BOOST_SYMBOL_VISIBLE message_format : public std::locale::facet,
49 public detail::facet_id<message_format<CharType>> {
50 BOOST_LOCALE_ASSERT_IS_SUPPORTED(CharType);
51
52 public:
54 typedef CharType char_type;
56 typedef std::basic_string<CharType> string_type;
57
59 message_format(size_t refs = 0) : std::locale::facet(refs) {}
60
68 virtual const char_type* get(int domain_id, const char_type* context, const char_type* id) const = 0;
69
80 virtual const char_type*
81 get(int domain_id, const char_type* context, const char_type* single_id, count_type n) const = 0;
82
84 virtual int domain(const std::string& domain) const = 0;
85
92 virtual const char_type* convert(const char_type* msg, string_type& buffer) const = 0;
93 };
94
96
97 namespace detail {
98 inline bool is_us_ascii_char(char c)
99 {
100 // works for null terminated strings regardless char "signedness"
101 return 0 < c && c < 0x7F;
102 }
103 inline bool is_us_ascii_string(const char* msg)
104 {
105 while(*msg) {
106 if(!is_us_ascii_char(*msg++))
107 return false;
108 }
109 return true;
110 }
111
112 template<typename CharType>
113 struct string_cast_traits {
114 static const CharType* cast(const CharType* msg, std::basic_string<CharType>& /*unused*/) { return msg; }
115 };
116
117 template<>
118 struct string_cast_traits<char> {
119 static const char* cast(const char* msg, std::string& buffer)
120 {
121 if(is_us_ascii_string(msg))
122 return msg;
123 buffer.reserve(strlen(msg));
124 char c;
125 while((c = *msg++) != 0) {
126 if(is_us_ascii_char(c))
127 buffer += c;
128 }
129 return buffer.c_str();
130 }
131 };
132 } // namespace detail
133
135
141 template<typename CharType>
143 public:
144 typedef CharType char_type;
145 typedef std::basic_string<char_type> string_type;
147
149 basic_message() : n_(0), c_id_(nullptr), c_context_(nullptr), c_plural_(nullptr) {}
150
153 explicit basic_message(const char_type* id) : n_(0), c_id_(id), c_context_(nullptr), c_plural_(nullptr) {}
154
159 explicit basic_message(const char_type* single, const char_type* plural, count_type n) :
160 n_(n), c_id_(single), c_context_(nullptr), c_plural_(plural)
161 {}
162
166 explicit basic_message(const char_type* context, const char_type* id) :
167 n_(0), c_id_(id), c_context_(context), c_plural_(nullptr)
168 {}
169
174 explicit basic_message(const char_type* context,
175 const char_type* single,
176 const char_type* plural,
177 count_type n) :
178 n_(n),
179 c_id_(single), c_context_(context), c_plural_(plural)
180 {}
181
183 explicit basic_message(const string_type& id) :
184 n_(0), c_id_(nullptr), c_context_(nullptr), c_plural_(nullptr), id_(id)
185 {}
186
190 explicit basic_message(const string_type& single, const string_type& plural, count_type number) :
191 n_(number), c_id_(nullptr), c_context_(nullptr), c_plural_(nullptr), id_(single), plural_(plural)
192 {}
193
195 explicit basic_message(const string_type& context, const string_type& id) :
196 n_(0), c_id_(nullptr), c_context_(nullptr), c_plural_(nullptr), id_(id), context_(context)
197 {}
198
202 explicit basic_message(const string_type& context,
203 const string_type& single,
204 const string_type& plural,
205 count_type number) :
206 n_(number),
207 c_id_(nullptr), c_context_(nullptr), c_plural_(nullptr), id_(single), context_(context), plural_(plural)
208 {}
209
211 basic_message(const basic_message&) = default;
212 basic_message(basic_message&&) noexcept = default;
213
215 basic_message& operator=(const basic_message&) = default;
217 operator=(basic_message&&) noexcept(std::is_nothrow_move_assignable<string_type>::value) = default;
218
220 void
221 swap(basic_message& other) noexcept(noexcept(std::declval<string_type&>().swap(std::declval<string_type&>())))
222 {
223 using std::swap;
224 swap(n_, other.n_);
225 swap(c_id_, other.c_id_);
226 swap(c_context_, other.c_context_);
227 swap(c_plural_, other.c_plural_);
228 swap(id_, other.id_);
229 swap(context_, other.context_);
230 swap(plural_, other.plural_);
231 }
232 friend void swap(basic_message& x, basic_message& y) noexcept(noexcept(x.swap(y))) { x.swap(y); }
233
235 operator string_type() const { return str(); }
236
238 string_type str() const { return str(std::locale()); }
239
241 string_type str(const std::locale& locale) const { return str(locale, 0); }
242
244 string_type str(const std::locale& locale, const std::string& domain_id) const
245 {
246 int id = 0;
247 if(std::has_facet<facet_type>(locale))
248 id = std::use_facet<facet_type>(locale).domain(domain_id);
249 return str(locale, id);
250 }
251
253 string_type str(const std::string& domain_id) const { return str(std::locale(), domain_id); }
254
256 string_type str(const std::locale& loc, int id) const
257 {
258 string_type buffer;
259 const char_type* ptr = write(loc, id, buffer);
260 if(ptr != buffer.c_str())
261 buffer = ptr;
262 return buffer;
263 }
264
267 void write(std::basic_ostream<char_type>& out) const
268 {
269 const std::locale& loc = out.getloc();
270 int id = ios_info::get(out).domain_id();
271 string_type buffer;
272 out << write(loc, id, buffer);
273 }
274
275 private:
276 const char_type* plural() const
277 {
278 if(c_plural_)
279 return c_plural_;
280 if(plural_.empty())
281 return nullptr;
282 return plural_.c_str();
283 }
284 const char_type* context() const
285 {
286 if(c_context_)
287 return c_context_;
288 if(context_.empty())
289 return nullptr;
290 return context_.c_str();
291 }
292
293 const char_type* id() const { return c_id_ ? c_id_ : id_.c_str(); }
294
295 const char_type* write(const std::locale& loc, int domain_id, string_type& buffer) const
296 {
297 static const char_type empty_string[1] = {0};
298
299 const char_type* id = this->id();
300 const char_type* context = this->context();
301 const char_type* plural = this->plural();
302
303 if(*id == 0)
304 return empty_string;
305
306 const facet_type* facet = nullptr;
307 if(std::has_facet<facet_type>(loc))
308 facet = &std::use_facet<facet_type>(loc);
309
310 const char_type* translated = nullptr;
311 if(facet) {
312 if(!plural)
313 translated = facet->get(domain_id, context, id);
314 else
315 translated = facet->get(domain_id, context, id, n_);
316 }
317
318 if(!translated) {
319 const char_type* msg = plural ? (n_ == 1 ? id : plural) : id;
320
321 if(facet)
322 translated = facet->convert(msg, buffer);
323 else
324 translated = detail::string_cast_traits<char_type>::cast(msg, buffer);
325 }
326 return translated;
327 }
328
330
331 count_type n_;
332 const char_type* c_id_;
333 const char_type* c_context_;
334 const char_type* c_plural_;
335 string_type id_;
336 string_type context_;
337 string_type plural_;
338 };
339
344#ifndef BOOST_LOCALE_NO_CXX20_STRING8
347#endif
348#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
351#endif
352#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
355#endif
356
358 template<typename CharType>
359 std::basic_ostream<CharType>& operator<<(std::basic_ostream<CharType>& out, const basic_message<CharType>& msg)
360 {
361 msg.write(out);
362 return out;
363 }
364
367
369 template<typename CharType>
370 inline basic_message<CharType> translate(const CharType* msg)
371 {
372 return basic_message<CharType>(msg);
373 }
374
376 template<typename CharType>
377 inline basic_message<CharType> translate(const CharType* context, const CharType* msg)
378 {
379 return basic_message<CharType>(context, msg);
380 }
381
383 template<typename CharType>
384 inline basic_message<CharType> translate(const CharType* single, const CharType* plural, count_type n)
385 {
386 return basic_message<CharType>(single, plural, n);
387 }
388
390 template<typename CharType>
391 inline basic_message<CharType>
392 translate(const CharType* context, const CharType* single, const CharType* plural, count_type n)
393 {
394 return basic_message<CharType>(context, single, plural, n);
395 }
396
398 template<typename CharType>
399 inline basic_message<CharType> translate(const std::basic_string<CharType>& msg)
400 {
401 return basic_message<CharType>(msg);
402 }
403
405 template<typename CharType>
406 inline basic_message<CharType> translate(const std::basic_string<CharType>& context,
407 const std::basic_string<CharType>& msg)
408 {
409 return basic_message<CharType>(context, msg);
410 }
411
413 template<typename CharType>
414 inline basic_message<CharType> translate(const std::basic_string<CharType>& context,
415 const std::basic_string<CharType>& single,
416 const std::basic_string<CharType>& plural,
417 count_type n)
418 {
419 return basic_message<CharType>(context, single, plural, n);
420 }
421
423 template<typename CharType>
424 inline basic_message<CharType>
425 translate(const std::basic_string<CharType>& single, const std::basic_string<CharType>& plural, count_type n)
426 {
427 return basic_message<CharType>(single, plural, n);
428 }
429
431
433
435 template<typename CharType>
436 std::basic_string<CharType> gettext(const CharType* id, const std::locale& loc = std::locale())
437 {
438 return basic_message<CharType>(id).str(loc);
439 }
441 template<typename CharType>
442 std::basic_string<CharType>
443 ngettext(const CharType* s, const CharType* p, count_type n, const std::locale& loc = std::locale())
444 {
445 return basic_message<CharType>(s, p, n).str(loc);
446 }
447
449 template<typename CharType>
450 std::basic_string<CharType> dgettext(const char* domain, const CharType* id, const std::locale& loc = std::locale())
451 {
452 return basic_message<CharType>(id).str(loc, domain);
453 }
454
456 template<typename CharType>
457 std::basic_string<CharType> dngettext(const char* domain,
458 const CharType* s,
459 const CharType* p,
460 count_type n,
461 const std::locale& loc = std::locale())
462 {
463 return basic_message<CharType>(s, p, n).str(loc, domain);
464 }
465
467 template<typename CharType>
468 std::basic_string<CharType>
469 pgettext(const CharType* context, const CharType* id, const std::locale& loc = std::locale())
470 {
471 return basic_message<CharType>(context, id).str(loc);
472 }
473
475 template<typename CharType>
476 std::basic_string<CharType> npgettext(const CharType* context,
477 const CharType* s,
478 const CharType* p,
479 count_type n,
480 const std::locale& loc = std::locale())
481 {
482 return basic_message<CharType>(context, s, p, n).str(loc);
483 }
484
486 template<typename CharType>
487 std::basic_string<CharType>
488 dpgettext(const char* domain, const CharType* context, const CharType* id, const std::locale& loc = std::locale())
489 {
490 return basic_message<CharType>(context, id).str(loc, domain);
491 }
492
494 template<typename CharType>
495 std::basic_string<CharType> dnpgettext(const char* domain,
496 const CharType* context,
497 const CharType* s,
498 const CharType* p,
499 count_type n,
500 const std::locale& loc = std::locale())
501 {
502 return basic_message<CharType>(context, s, p, n).str(loc, domain);
503 }
504
506
507 namespace as {
509 namespace detail {
510 struct set_domain {
511 std::string domain_id;
512 };
513 template<typename CharType>
514 std::basic_ostream<CharType>& operator<<(std::basic_ostream<CharType>& out, const set_domain& dom)
515 {
516 int id = std::use_facet<message_format<CharType>>(out.getloc()).domain(dom.domain_id);
517 ios_info::get(out).domain_id(id);
518 return out;
519 }
520 } // namespace detail
522
526
531 inline
532#ifdef BOOST_LOCALE_DOXYGEN
533 unspecified_type
534#else
535 detail::set_domain
536#endif
537 domain(const std::string& id)
538 {
539 detail::set_domain tmp = {id};
540 return tmp;
541 }
543 } // namespace as
544}} // namespace boost::locale
545
546#ifdef BOOST_MSVC
547# pragma warning(pop)
548#endif
549
550#endif
This class represents a message that can be converted to a specific locale message.
Definition: message.hpp:142
void domain_id(int)
Set special message domain identification.
static ios_info & get(std::ios_base &ios)
Get ios_info instance for specific stream object.
This facet provides message formatting abilities.
Definition: message.hpp:49
std::basic_ostream< CharType > & operator<<(std::basic_ostream< CharType > &out, const date_time &t)
Definition: date_time.hpp:724
unspecified_type domain(const std::string &id)
Definition: message.hpp:537
string_type str(const std::locale &loc, int id) const
Translate message to a string using locale loc and message domain index id.
Definition: message.hpp:256
basic_message(const char_type *context, const char_type *id)
Definition: message.hpp:166
basic_message< char8_t > u8message
Convenience typedef for char8_t.
Definition: message.hpp:346
basic_message()
Create default empty message.
Definition: message.hpp:149
basic_message< char16_t > u16message
Convenience typedef for char16_t.
Definition: message.hpp:350
std::basic_string< char_type > string_type
The string type this object can be used with.
Definition: message.hpp:145
basic_message< char > message
Convenience typedef for char.
Definition: message.hpp:341
virtual const char_type * get(int domain_id, const char_type *context, const char_type *id) const =0
basic_message(const string_type &single, const string_type &plural, count_type number)
Definition: message.hpp:190
string_type str(const std::locale &locale, const std::string &domain_id) const
Translate message to a string using locale locale and message domain domain_id.
Definition: message.hpp:244
basic_message(const basic_message &)=default
Copy an object.
basic_message< char32_t > u32message
Convenience typedef for char32_t.
Definition: message.hpp:354
string_type str(const std::string &domain_id) const
Translate message to a string using the default locale and message domain domain_id.
Definition: message.hpp:253
long long count_type
Type used for the count/n argument to the translation functions choosing between singular and plural ...
Definition: message.hpp:44
void write(std::basic_ostream< char_type > &out) const
Definition: message.hpp:267
std::basic_string< CharType > pgettext(const CharType *context, const CharType *id, const std::locale &loc=std::locale())
Translate message id according to locale loc in context context.
Definition: message.hpp:469
std::basic_string< CharType > gettext(const CharType *id, const std::locale &loc=std::locale())
Translate message id according to locale loc.
Definition: message.hpp:436
CharType char_type
Character type.
Definition: message.hpp:54
std::basic_string< CharType > dnpgettext(const char *domain, const CharType *context, const CharType *s, const CharType *p, count_type n, const std::locale &loc=std::locale())
Translate plural form according to locale loc in domain domain in context context.
Definition: message.hpp:495
void swap(basic_message &other) noexcept(noexcept(std::declval< string_type & >().swap(std::declval< string_type & >())))
Swap two message objects.
Definition: message.hpp:221
basic_message(const string_type &context, const string_type &id)
Create a simple message from a string with context.
Definition: message.hpp:195
basic_message(const char_type *single, const char_type *plural, count_type n)
Definition: message.hpp:159
basic_message(const string_type &id)
Create a simple message from a string.
Definition: message.hpp:183
CharType char_type
The character this message object is used with.
Definition: message.hpp:144
message_format< char_type > facet_type
The type of the facet the messages are fetched with.
Definition: message.hpp:146
std::basic_string< CharType > string_type
String type.
Definition: message.hpp:56
basic_message(const char_type *context, const char_type *single, const char_type *plural, count_type n)
Definition: message.hpp:174
std::basic_string< CharType > dpgettext(const char *domain, const CharType *context, const CharType *id, const std::locale &loc=std::locale())
Translate message id according to locale loc in domain domain in context context.
Definition: message.hpp:488
string_type str() const
Translate message to a string in the default global locale, using default domain.
Definition: message.hpp:238
std::basic_string< CharType > npgettext(const CharType *context, const CharType *s, const CharType *p, count_type n, const std::locale &loc=std::locale())
Translate plural form according to locale loc in context context.
Definition: message.hpp:476
virtual const char_type * convert(const char_type *msg, string_type &buffer) const =0
basic_message< CharType > translate(const CharType *msg)
Translate a message, msg is not copied.
Definition: message.hpp:370
string_type str(const std::locale &locale) const
Translate message to a string in the locale locale, using default domain.
Definition: message.hpp:241
virtual const char_type * get(int domain_id, const char_type *context, const char_type *single_id, count_type n) const =0
std::basic_string< CharType > ngettext(const CharType *s, const CharType *p, count_type n, const std::locale &loc=std::locale())
Translate plural form according to locale loc.
Definition: message.hpp:443
std::basic_string< CharType > dngettext(const char *domain, const CharType *s, const CharType *p, count_type n, const std::locale &loc=std::locale())
Translate plural form according to locale loc in domain domain.
Definition: message.hpp:457
basic_message(const char_type *id)
Definition: message.hpp:153
virtual int domain(const std::string &domain) const =0
Convert a string that defines domain to the integer id used by get functions.
basic_message(const string_type &context, const string_type &single, const string_type &plural, count_type number)
Definition: message.hpp:202
std::basic_string< CharType > dgettext(const char *domain, const CharType *id, const std::locale &loc=std::locale())
Translate message id according to locale loc in domain domain.
Definition: message.hpp:450
message_format(size_t refs=0)
Standard constructor.
Definition: message.hpp:59
basic_message< wchar_t > wmessage
Convenience typedef for wchar_t.
Definition: message.hpp:343