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#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
347#endif
348#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
351#endif
352
354 template<typename CharType>
355 std::basic_ostream<CharType>& operator<<(std::basic_ostream<CharType>& out, const basic_message<CharType>& msg)
356 {
357 msg.write(out);
358 return out;
359 }
360
363
365 template<typename CharType>
366 inline basic_message<CharType> translate(const CharType* msg)
367 {
368 return basic_message<CharType>(msg);
369 }
370
372 template<typename CharType>
373 inline basic_message<CharType> translate(const CharType* context, const CharType* msg)
374 {
375 return basic_message<CharType>(context, msg);
376 }
377
379 template<typename CharType>
380 inline basic_message<CharType> translate(const CharType* single, const CharType* plural, count_type n)
381 {
382 return basic_message<CharType>(single, plural, n);
383 }
384
386 template<typename CharType>
387 inline basic_message<CharType>
388 translate(const CharType* context, const CharType* single, const CharType* plural, count_type n)
389 {
390 return basic_message<CharType>(context, single, plural, n);
391 }
392
394 template<typename CharType>
395 inline basic_message<CharType> translate(const std::basic_string<CharType>& msg)
396 {
397 return basic_message<CharType>(msg);
398 }
399
401 template<typename CharType>
402 inline basic_message<CharType> translate(const std::basic_string<CharType>& context,
403 const std::basic_string<CharType>& msg)
404 {
405 return basic_message<CharType>(context, msg);
406 }
407
409 template<typename CharType>
410 inline basic_message<CharType> translate(const std::basic_string<CharType>& context,
411 const std::basic_string<CharType>& single,
412 const std::basic_string<CharType>& plural,
413 count_type n)
414 {
415 return basic_message<CharType>(context, single, plural, n);
416 }
417
419 template<typename CharType>
420 inline basic_message<CharType>
421 translate(const std::basic_string<CharType>& single, const std::basic_string<CharType>& plural, count_type n)
422 {
423 return basic_message<CharType>(single, plural, n);
424 }
425
427
429
431 template<typename CharType>
432 std::basic_string<CharType> gettext(const CharType* id, const std::locale& loc = std::locale())
433 {
434 return basic_message<CharType>(id).str(loc);
435 }
437 template<typename CharType>
438 std::basic_string<CharType>
439 ngettext(const CharType* s, const CharType* p, count_type n, const std::locale& loc = std::locale())
440 {
441 return basic_message<CharType>(s, p, n).str(loc);
442 }
443
445 template<typename CharType>
446 std::basic_string<CharType> dgettext(const char* domain, const CharType* id, const std::locale& loc = std::locale())
447 {
448 return basic_message<CharType>(id).str(loc, domain);
449 }
450
452 template<typename CharType>
453 std::basic_string<CharType> dngettext(const char* domain,
454 const CharType* s,
455 const CharType* p,
456 count_type n,
457 const std::locale& loc = std::locale())
458 {
459 return basic_message<CharType>(s, p, n).str(loc, domain);
460 }
461
463 template<typename CharType>
464 std::basic_string<CharType>
465 pgettext(const CharType* context, const CharType* id, const std::locale& loc = std::locale())
466 {
467 return basic_message<CharType>(context, id).str(loc);
468 }
469
471 template<typename CharType>
472 std::basic_string<CharType> npgettext(const CharType* context,
473 const CharType* s,
474 const CharType* p,
475 count_type n,
476 const std::locale& loc = std::locale())
477 {
478 return basic_message<CharType>(context, s, p, n).str(loc);
479 }
480
482 template<typename CharType>
483 std::basic_string<CharType>
484 dpgettext(const char* domain, const CharType* context, const CharType* id, const std::locale& loc = std::locale())
485 {
486 return basic_message<CharType>(context, id).str(loc, domain);
487 }
488
490 template<typename CharType>
491 std::basic_string<CharType> dnpgettext(const char* domain,
492 const CharType* context,
493 const CharType* s,
494 const CharType* p,
495 count_type n,
496 const std::locale& loc = std::locale())
497 {
498 return basic_message<CharType>(context, s, p, n).str(loc, domain);
499 }
500
502
503 namespace as {
505 namespace detail {
506 struct set_domain {
507 std::string domain_id;
508 };
509 template<typename CharType>
510 std::basic_ostream<CharType>& operator<<(std::basic_ostream<CharType>& out, const set_domain& dom)
511 {
512 int id = std::use_facet<message_format<CharType>>(out.getloc()).domain(dom.domain_id);
513 ios_info::get(out).domain_id(id);
514 return out;
515 }
516 } // namespace detail
518
522
527 inline
528#ifdef BOOST_LOCALE_DOXYGEN
529 unspecified_type
530#else
531 detail::set_domain
532#endif
533 domain(const std::string& id)
534 {
535 detail::set_domain tmp = {id};
536 return tmp;
537 }
539 } // namespace as
540}} // namespace boost::locale
541
542#ifdef BOOST_MSVC
543# pragma warning(pop)
544#endif
545
546#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:722
unspecified_type domain(const std::string &id)
Definition: message.hpp:533
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()
Create default empty message.
Definition: message.hpp:149
basic_message< char16_t > u16message
Convenience typedef for char16_t.
Definition: message.hpp:346
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:350
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:465
std::basic_string< CharType > gettext(const CharType *id, const std::locale &loc=std::locale())
Translate message id according to locale loc.
Definition: message.hpp:432
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:491
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:484
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:472
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:366
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:439
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:453
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:446
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