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#include <boost/locale/encoding.hpp>
9#include <boost/locale/generator.hpp>
10#include <boost/locale/gnu_gettext.hpp>
11#include <boost/locale/localization_backend.hpp>
12#include <boost/locale/message.hpp>
13#include "boostLocale/test/tools.hpp"
14#include "boostLocale/test/unit_test.hpp"
15#include <fstream>
16#include <iostream>
17#include <limits>
18#include <type_traits>
19#include <vector>
20
21namespace bl = boost::locale;
22
23void test_messages_info()
24{
25 using string_vec = std::vector<std::string>;
26 {
27 bl::gnu_gettext::messages_info info;
28 info.locale_category = "LC";
29 TEST_EQ(info.get_catalog_paths(), string_vec{});
30 info.paths.push_back(x: ".");
31 TEST_EQ(info.get_catalog_paths(), string_vec{"./C/LC"});
32 info.language = "en";
33 TEST_EQ(info.get_catalog_paths(), string_vec{"./en/LC"});
34 info.country = "US";
35 TEST_EQ(info.get_catalog_paths(), (string_vec{"./en_US/LC", "./en/LC"}));
36 info.country.clear();
37 info.variant = "euro";
38 TEST_EQ(info.get_catalog_paths(), (string_vec{"./en@euro/LC", "./en/LC"}));
39 info.country = "US";
40 TEST_EQ(info.get_catalog_paths(), (string_vec{"./en_US@euro/LC", "./en@euro/LC", "./en_US/LC", "./en/LC"}));
41
42 info.paths = string_vec{"/1", "/2"};
43 TEST_EQ(info.get_catalog_paths(),
44 (string_vec{"/1/en_US@euro/LC",
45 "/2/en_US@euro/LC",
46 "/1/en@euro/LC",
47 "/2/en@euro/LC",
48 "/1/en_US/LC",
49 "/2/en_US/LC",
50 "/1/en/LC",
51 "/2/en/LC"}));
52 }
53}
54
55std::string backend;
56bool file_loader_is_actually_called = false;
57
58struct file_loader {
59 std::vector<char> operator()(const std::string& name, const std::string& /*encoding*/) const
60 {
61 std::vector<char> buffer;
62 std::ifstream f(name.c_str(), std::ifstream::binary);
63 if(!f)
64 return buffer;
65 f.seekg(0, std::ifstream::end);
66 const auto len = f.tellg();
67 f.seekg(0);
68 if(len > 0) {
69 buffer.resize(new_size: static_cast<size_t>(len));
70 f.read(s: buffer.data(), n: buffer.size());
71 }
72 file_loader_is_actually_called = true;
73 return buffer;
74 }
75};
76
77std::string same_s(std::string s)
78{
79 return s;
80}
81
82std::wstring same_w(std::wstring s)
83{
84 return s;
85}
86
87#ifndef BOOST_LOCALE_NO_CXX20_STRING8
88std::basic_string<char8_t> same_u8(std::basic_string<char8_t> s)
89{
90 return s;
91}
92#endif
93
94#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
95std::u16string same_u16(std::u16string s)
96{
97 return s;
98}
99#endif
100
101#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
102std::u32string same_u32(std::u32string s)
103{
104 return s;
105}
106#endif
107
108namespace impl {
109
110template<class T, std::size_t = sizeof(T)>
111std::true_type is_complete_impl(T*);
112std::false_type is_complete_impl(...);
113
114template<class T>
115using has_ctype = decltype(is_complete_impl(std::declval<std::ctype<T>*>()));
116
117template<typename Char, typename... Ts>
118typename std::enable_if<has_ctype<Char>::value, bool>::type stream_translate(std::basic_ostream<Char>& ss, Ts&&... args)
119{
120 ss << bl::translate(args...);
121 return true;
122}
123
124// Required for char types not fully supported by the standard library
125// e.g.: error: implicit instantiation of undefined template 'std::ctype<char8_t>'
126template<typename Char, typename... Ts>
127typename std::enable_if<!has_ctype<Char>::value, bool>::type stream_translate(std::basic_ostream<Char>&, Ts&&...)
128{
129 return false; // LCOV_EXCL_LINE
130}
131
132template<typename Char>
133void test_cntranslate(const std::string& sContext,
134 const std::string& sSingular,
135 const std::string& sPlural,
136 long long n,
137 const std::string& sExpected,
138 const std::locale& l,
139 const std::string& domain)
140{
141 typedef std::basic_string<Char> string_type;
142 const string_type expected = to_correct_string<Char>(sExpected, l);
143
144 const string_type c = to<Char>(sContext);
145 const string_type s = to<Char>(sSingular);
146 const string_type p = to<Char>(sPlural);
147
148 if(domain == "default") {
149 TEST_EQ(bl::translate(c, s, p, n).str(l), expected);
150 TEST_EQ(bl::translate(c.c_str(), s.c_str(), p.c_str(), n).str(l), expected);
151 std::locale tmp_locale;
152 std::locale::global(loc: l);
153 string_type tmp = bl::translate(c, s, p, n);
154 TEST_EQ(tmp, expected);
155 tmp = bl::translate(c, s, p, n).str();
156 TEST_EQ(tmp, expected);
157 std::locale::global(loc: tmp_locale);
158
159 std::basic_ostringstream<Char> ss;
160 ss.imbue(l);
161 if(stream_translate(ss, c, s, p, n))
162 TEST_EQ(ss.str(), expected);
163
164 // Copyable & movable
165 const string_type s2 = ascii_to<Char>("missing Singular");
166 const string_type p2 = ascii_to<Char>("missing Plural");
167 const string_type& expected2 = (n == 1) ? s2 : p2;
168 auto translation1 = bl::translate(c, s, p, n);
169 auto translation2 = bl::translate(c, s2, p2, n);
170 TEST_EQ(translation1.str(l), expected);
171 TEST_EQ(translation2.str(l), expected2);
172 // Copy
173 translation1 = translation2;
174 TEST_EQ(translation1.str(l), expected2);
175 {
176 bl::basic_message<Char> t3(translation2);
177 TEST_EQ(t3.str(l), expected2);
178 }
179 // Move
180 translation1 = bl::translate(c, s, p, n);
181 translation2 = std::move(translation1);
182 TEST_EQ(translation2.str(l), expected);
183 {
184 bl::basic_message<Char> t3(std::move(translation2));
185 TEST_EQ(t3.str(l), expected);
186 }
187 // Swap
188 translation1 = bl::translate(c, s, p, n);
189 translation2 = bl::translate(c, s2, p2, n);
190 TEST_EQ(translation1.str(l), expected);
191 TEST_EQ(translation2.str(l), expected2);
192 using std::swap;
193 swap(translation1, translation2);
194 TEST_EQ(translation1.str(l), expected2);
195 TEST_EQ(translation2.str(l), expected);
196 translation1 = bl::translate(c, s, p, n);
197 translation2 = bl::translate(c, s2, p2, 1); // n==1!
198 swap(translation1, translation2);
199 TEST_EQ(translation1.str(l), s2);
200 TEST_EQ(translation2.str(l), expected);
201 }
202 TEST_EQ(bl::translate(c, s, p, n).str(l, domain), expected);
203 std::locale tmp_locale;
204 std::locale::global(loc: l);
205 TEST_EQ(bl::translate(c, s, p, n).str(domain), expected);
206 std::locale::global(loc: tmp_locale);
207 {
208 std::basic_ostringstream<Char> ss;
209 ss.imbue(l);
210 if(stream_translate(ss << bl::as::domain(id: domain), c, s, p, n))
211 TEST_EQ(ss.str(), expected);
212 }
213 {
214 std::basic_ostringstream<Char> ss;
215 ss.imbue(l);
216 if(stream_translate(ss << bl::as::domain(id: domain), c.c_str(), s.c_str(), p.c_str(), n))
217 TEST_EQ(ss.str(), expected);
218 }
219 // Missing facet -> No translation
220 {
221 const string_type nonAscii = ((string_type() + Char('\x7F')) + Char('\x82')) + Char('\xF0');
222 const string_type p2 = nonAscii + p + nonAscii;
223 // For char the non-ASCII chars are removed -> original p
224 const string_type expected2 = (n == 1) ? s : (std::is_same<Char, char>::value ? p : p2);
225 TEST_EQ(bl::translate(c, s, p2, n).str(std::locale::classic()), expected2);
226 }
227}
228
229template<typename Char>
230void test_ntranslate(const std::string& sSingular,
231 const std::string& sPlural,
232 long long n,
233 const std::string& sExpected,
234 const std::locale& l,
235 const std::string& domain)
236{
237 typedef std::basic_string<Char> string_type;
238 const string_type expected = to_correct_string<Char>(sExpected, l);
239 const string_type s = to<Char>(sSingular);
240 const string_type p = to<Char>(sPlural);
241 if(domain == "default") {
242 TEST_EQ(bl::translate(s, p, n).str(l), expected);
243 TEST_EQ(bl::translate(s.c_str(), p.c_str(), n).str(l), expected);
244 std::locale tmp_locale;
245 std::locale::global(loc: l);
246 string_type tmp = bl::translate(s, p, n);
247 TEST_EQ(tmp, expected);
248 tmp = bl::translate(s, p, n).str();
249 TEST_EQ(tmp, expected);
250 std::locale::global(loc: tmp_locale);
251
252 std::basic_ostringstream<Char> ss;
253 ss.imbue(l);
254 if(stream_translate(ss, s, p, n))
255 TEST_EQ(ss.str(), expected);
256 }
257 TEST_EQ(bl::translate(s, p, n).str(l, domain), expected);
258 std::locale tmp_locale;
259 std::locale::global(loc: l);
260 TEST_EQ(bl::translate(s, p, n).str(domain), expected);
261 std::locale::global(loc: tmp_locale);
262 {
263 std::basic_ostringstream<Char> ss;
264 ss.imbue(l);
265 if(stream_translate(ss << bl::as::domain(id: domain), s, p, n))
266 TEST_EQ(ss.str(), expected);
267 }
268 {
269 std::basic_ostringstream<Char> ss;
270 ss.imbue(l);
271 if(stream_translate(ss << bl::as::domain(id: domain), s.c_str(), p.c_str(), n))
272 TEST_EQ(ss.str(), expected);
273 }
274}
275
276template<typename Char>
277void test_ctranslate(const std::string& sContext,
278 const std::string& sOriginal,
279 const std::string& sExpected,
280 const std::locale& l,
281 const std::string& domain)
282{
283 typedef std::basic_string<Char> string_type;
284 const string_type expected = to_correct_string<Char>(sExpected, l);
285 const string_type original = to<Char>(sOriginal);
286 const string_type c = to<Char>(sContext);
287 if(domain == "default") {
288 TEST_EQ(bl::translate(c, original).str(l), expected);
289 TEST_EQ(bl::translate(c.c_str(), original.c_str()).str(l), expected);
290 std::locale tmp_locale;
291 std::locale::global(loc: l);
292 string_type tmp = bl::translate(c, original);
293 TEST_EQ(tmp, expected);
294 tmp = bl::translate(c, original).str();
295 TEST_EQ(tmp, expected);
296 std::locale::global(loc: tmp_locale);
297
298 std::basic_ostringstream<Char> ss;
299 ss.imbue(l);
300 if(stream_translate(ss, c, original))
301 TEST_EQ(ss.str(), expected);
302 }
303 TEST_EQ(bl::translate(c, original).str(l, domain), expected);
304 std::locale tmp_locale;
305 std::locale::global(loc: l);
306 TEST_EQ(bl::translate(c, original).str(domain), expected);
307 std::locale::global(loc: tmp_locale);
308 {
309 std::basic_ostringstream<Char> ss;
310 ss.imbue(l);
311 if(stream_translate(ss << bl::as::domain(id: domain), c, original))
312 TEST_EQ(ss.str(), expected);
313 }
314 {
315 std::basic_ostringstream<Char> ss;
316 ss.imbue(l);
317 if(stream_translate(ss << bl::as::domain(id: domain), c.c_str(), original.c_str()))
318 TEST_EQ(ss.str(), expected);
319 }
320}
321
322template<typename Char>
323void test_translate(const std::string& sOriginal,
324 const std::string& sExpected,
325 const std::locale& l,
326 const std::string& domain)
327{
328 typedef std::basic_string<Char> string_type;
329 const string_type expected = to_correct_string<Char>(sExpected, l);
330 const string_type original = to<Char>(sOriginal);
331 if(domain == "default") {
332 TEST_EQ(bl::translate(original).str(l), expected);
333 TEST_EQ(bl::translate(original.c_str()).str(l), expected);
334 std::locale tmp_locale;
335 std::locale::global(loc: l);
336 string_type tmp = bl::translate(original);
337 TEST_EQ(tmp, expected);
338 tmp = bl::translate(original).str();
339 TEST_EQ(tmp, expected);
340 std::locale::global(loc: tmp_locale);
341
342 std::basic_ostringstream<Char> ss;
343 ss.imbue(l);
344 if(stream_translate(ss, original))
345 TEST_EQ(ss.str(), expected);
346 }
347 TEST_EQ(bl::translate(original).str(l, domain), expected);
348 std::locale tmp_locale;
349 std::locale::global(loc: l);
350 TEST_EQ(bl::translate(original).str(domain), expected);
351 std::locale::global(loc: tmp_locale);
352 {
353 std::basic_ostringstream<Char> ss;
354 ss.imbue(l);
355 if(stream_translate(ss << bl::as::domain(id: domain), original))
356 TEST_EQ(ss.str(), expected);
357 }
358 {
359 std::basic_ostringstream<Char> ss;
360 ss.imbue(l);
361 if(stream_translate(ss << bl::as::domain(id: domain), original.c_str()))
362 TEST_EQ(ss.str(), expected);
363 }
364}
365} // namespace impl
366
367void test_cntranslate(const std::string& c,
368 const std::string& s,
369 const std::string& p,
370 long long n,
371 const std::string& expected,
372 const std::locale& l,
373 const std::string& domain)
374{
375 std::cout << " char" << std::endl;
376 impl::test_cntranslate<char>(sContext: c, sSingular: s, sPlural: p, n, sExpected: expected, l, domain);
377 std::cout << " wchar_t" << std::endl;
378 impl::test_cntranslate<wchar_t>(sContext: c, sSingular: s, sPlural: p, n, sExpected: expected, l, domain);
379#ifndef BOOST_LOCALE_NO_CXX20_STRING8
380 std::cout << " char8_t" << std::endl;
381 impl::test_cntranslate<char8_t>(c, s, p, n, expected, l, domain);
382#endif
383#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
384 std::cout << " char16_t" << std::endl;
385 impl::test_cntranslate<char16_t>(c, s, p, n, expected, l, domain);
386#endif
387#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
388 std::cout << " char32_t" << std::endl;
389 impl::test_cntranslate<char32_t>(c, s, p, n, expected, l, domain);
390#endif
391}
392
393void test_ntranslate(const std::string& s,
394 const std::string& p,
395 long long n,
396 const std::string& expected,
397 const std::locale& l,
398 const std::string& domain)
399{
400 std::cout << " char" << std::endl;
401 impl::test_ntranslate<char>(sSingular: s, sPlural: p, n, sExpected: expected, l, domain);
402 std::cout << " wchar_t" << std::endl;
403 impl::test_ntranslate<wchar_t>(sSingular: s, sPlural: p, n, sExpected: expected, l, domain);
404#ifndef BOOST_LOCALE_NO_CXX20_STRING8
405 std::cout << " char8_t" << std::endl;
406 impl::test_ntranslate<char8_t>(s, p, n, expected, l, domain);
407#endif
408#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
409 std::cout << " char16_t" << std::endl;
410 impl::test_ntranslate<char16_t>(s, p, n, expected, l, domain);
411#endif
412#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
413 std::cout << " char32_t" << std::endl;
414 impl::test_ntranslate<char32_t>(s, p, n, expected, l, domain);
415#endif
416}
417
418void test_ctranslate(const std::string& c,
419 const std::string& original,
420 const std::string& expected,
421 const std::locale& l,
422 const std::string& domain)
423{
424 std::cout << " char" << std::endl;
425 impl::test_ctranslate<char>(sContext: c, sOriginal: original, sExpected: expected, l, domain);
426 std::cout << " wchar_t" << std::endl;
427 impl::test_ctranslate<wchar_t>(sContext: c, sOriginal: original, sExpected: expected, l, domain);
428#ifndef BOOST_LOCALE_NO_CXX20_STRING8
429 std::cout << " char8_t" << std::endl;
430 impl::test_ctranslate<char8_t>(c, original, expected, l, domain);
431#endif
432#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
433 std::cout << " char16_t" << std::endl;
434 impl::test_ctranslate<char16_t>(c, original, expected, l, domain);
435#endif
436#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
437 std::cout << " char32_t" << std::endl;
438 impl::test_ctranslate<char32_t>(c, original, expected, l, domain);
439#endif
440}
441
442void test_translate(const std::string& original,
443 const std::string& expected,
444 const std::locale& l,
445 const std::string& domain)
446{
447 std::cout << " char" << std::endl;
448 impl::test_translate<char>(sOriginal: original, sExpected: expected, l, domain);
449 std::cout << " wchar_t" << std::endl;
450 impl::test_translate<wchar_t>(sOriginal: original, sExpected: expected, l, domain);
451#ifndef BOOST_LOCALE_NO_CXX20_STRING8
452 std::cout << " char8_t" << std::endl;
453 impl::test_translate<char8_t>(original, expected, l, domain);
454#endif
455#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
456 std::cout << " char16_t" << std::endl;
457 impl::test_translate<char16_t>(original, expected, l, domain);
458#endif
459#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
460 std::cout << " char32_t" << std::endl;
461 impl::test_translate<char32_t>(original, expected, l, domain);
462#endif
463}
464
465bool iso_8859_8_supported = true;
466
467void test_main(int argc, char** argv)
468{
469 test_messages_info();
470
471 const std::string message_path = (argc == 2) ? argv[1] : ".";
472
473 for(const std::string& backend_name : boost::locale::localization_backend_manager::global().get_all_backends()) {
474 std::cout << "Testing for backend --------- " << backend_name << std::endl;
475 boost::locale::localization_backend_manager tmp_backend = boost::locale::localization_backend_manager::global();
476 tmp_backend.select(backend_name);
477 boost::locale::localization_backend_manager::global(tmp_backend);
478
479 backend = backend_name;
480
481 boost::locale::generator g;
482 g.add_messages_domain(domain: "simple");
483 g.add_messages_domain(domain: "full");
484 // Fallback using only ASCII keys, choose a specific encoding != UTF-8, here: Latin1
485 g.add_messages_domain(domain: "fall/ISO-8859-1");
486 g.add_messages_path(path: message_path);
487 g.set_default_messages_domain("default");
488
489 for(const std::string locale_name : {"he_IL.UTF-8", "he_IL.ISO8859-8"}) {
490 std::locale l;
491
492 if(locale_name.find(s: ".ISO") != std::string::npos) {
493 try {
494 l = g(locale_name);
495 } catch(const boost::locale::conv::invalid_charset_error&) { // LCOV_EXCL_LINE
496 std::cout << "Looks like ISO-8859-8 is not supported! skipping" << std::endl; // LCOV_EXCL_LINE
497 iso_8859_8_supported = false; // LCOV_EXCL_LINE
498 continue; // LCOV_EXCL_LINE
499 }
500 } else
501 l = g(locale_name);
502
503 std::cout << " Testing " << locale_name << std::endl;
504 std::cout << " single forms" << std::endl;
505
506 test_translate(original: "hello", expected: "שלום", l, domain: "default");
507 test_translate(original: "hello", expected: "היי", l, domain: "simple");
508 test_translate(original: "hello", expected: "hello", l, domain: "undefined");
509 test_translate(original: "untranslated", expected: "untranslated", l, domain: "default");
510 // Check removal of old "context" information
511 test_translate(original: "#untranslated", expected: "#untranslated", l, domain: "default");
512 test_translate(original: "##untranslated", expected: "##untranslated", l, domain: "default");
513 test_ctranslate(c: "context", original: "hello", expected: "שלום בהקשר אחר", l, domain: "default");
514 test_translate(original: "#hello", expected: "#שלום", l, domain: "default");
515
516 std::cout << " plural forms" << std::endl;
517
518 {
519 test_ntranslate(s: "x day", p: "x days", n: 0, expected: "x ימים", l, domain: "default");
520 test_ntranslate(s: "x day", p: "x days", n: 1, expected: "יום x", l, domain: "default");
521 test_ntranslate(s: "x day", p: "x days", n: 2, expected: "יומיים", l, domain: "default");
522 test_ntranslate(s: "x day", p: "x days", n: 3, expected: "x ימים", l, domain: "default");
523 test_ntranslate(s: "x day", p: "x days", n: 20, expected: "x יום", l, domain: "default");
524 test_ntranslate(s: "x day", p: "x days", n: 0, expected: "x days", l, domain: "undefined");
525 test_ntranslate(s: "x day", p: "x days", n: 1, expected: "x day", l, domain: "undefined");
526 test_ntranslate(s: "x day", p: "x days", n: 2, expected: "x days", l, domain: "undefined");
527 test_ntranslate(s: "x day", p: "x days", n: 20, expected: "x days", l, domain: "undefined");
528 // Ensure no truncation occurs
529 test_ntranslate(s: "x day", p: "x days", n: std::numeric_limits<long long>::min(), expected: "x days", l, domain: "undefined");
530 test_ntranslate(s: "x day", p: "x days", n: std::numeric_limits<long long>::max(), expected: "x days", l, domain: "undefined");
531 for(unsigned bit = 1; bit < std::numeric_limits<long long>::digits; ++bit) {
532 // Set each individual bit possible and add 1.
533 // If the value is truncated the 1 will remain leading to singular form
534 const auto num = static_cast<long long>(static_cast<unsigned long long>(1) << bit);
535 test_ntranslate(s: "x day", p: "x days", n: num + 1, expected: "x days", l, domain: "undefined");
536 }
537 }
538 std::cout << " plural forms with context" << std::endl;
539 {
540 std::string inp = "context";
541 std::string out = "בהקשר ";
542
543 test_cntranslate(c: inp, s: "x day", p: "x days", n: 0, expected: out + "x ימים", l, domain: "default");
544 test_cntranslate(c: inp, s: "x day", p: "x days", n: 1, expected: out + "יום x", l, domain: "default");
545 test_cntranslate(c: inp, s: "x day", p: "x days", n: 2, expected: out + "יומיים", l, domain: "default");
546 test_cntranslate(c: inp, s: "x day", p: "x days", n: 3, expected: out + "x ימים", l, domain: "default");
547 test_cntranslate(c: inp, s: "x day", p: "x days", n: 20, expected: out + "x יום", l, domain: "default");
548
549 test_cntranslate(c: inp, s: "x day", p: "x days", n: 0, expected: "x days", l, domain: "undefined");
550 test_cntranslate(c: inp, s: "x day", p: "x days", n: 1, expected: "x day", l, domain: "undefined");
551 test_cntranslate(c: inp, s: "x day", p: "x days", n: 2, expected: "x days", l, domain: "undefined");
552 test_cntranslate(c: inp, s: "x day", p: "x days", n: 20, expected: "x days", l, domain: "undefined");
553 }
554 }
555 std::cout << " Testing fallbacks" << std::endl;
556 {
557 const std::locale l = g("he_IL.UTF-8");
558 test_translate(original: "test", expected: "he_IL", l, domain: "full");
559 test_translate(original: "test", expected: "he", l, domain: "fall");
560 for(int n = -1; n < 5; ++n) {
561 // No plural forms -> Use english logic
562 // Singular is translated, plural is not
563 test_ntranslate(s: "test", p: "tests", n, expected: (n == 1) ? "he" : "tests", l, domain: "fall");
564 }
565 }
566
567 std::cout << " Testing automatic conversions " << std::endl;
568 std::locale::global(loc: g("he_IL.UTF-8"));
569
570 TEST_EQ(same_s(bl::translate("hello")), "שלום");
571 TEST_EQ(same_w(bl::translate(to<wchar_t>("hello"))), to<wchar_t>("שלום"));
572
573#ifndef BOOST_LOCALE_NO_CXX20_STRING8
574 TEST_EQ(same_u8(bl::translate(to<char8_t>("hello"))), to<char8_t>("שלום"));
575#endif
576#ifdef BOOST_LOCALE_ENABLE_CHAR16_T
577 if(backend == "icu" || backend == "std")
578 TEST_EQ(same_u16(bl::translate(to<char16_t>("hello"))), to<char16_t>("שלום"));
579#endif
580#ifdef BOOST_LOCALE_ENABLE_CHAR32_T
581 if(backend == "icu" || backend == "std")
582 TEST_EQ(same_u32(bl::translate(to<char32_t>("hello"))), to<char32_t>("שלום"));
583#endif
584 }
585
586 std::cout << "Testing custom file system support" << std::endl;
587 {
588 boost::locale::gnu_gettext::messages_info info;
589 info.language = "he";
590 info.country = "IL";
591 info.encoding = "UTF-8";
592 info.paths.push_back(x: message_path);
593
594 info.domains.push_back(x: bl::gnu_gettext::messages_info::domain("default"));
595 info.callback = file_loader();
596
597 file_loader_is_actually_called = false;
598 std::locale l(std::locale::classic(), boost::locale::gnu_gettext::create_messages_facet<char>(info));
599 TEST(file_loader_is_actually_called);
600 TEST_EQ(bl::translate("hello").str(l), "שלום");
601 }
602 if(iso_8859_8_supported) {
603 std::cout << "Testing non-US-ASCII keys" << std::endl;
604 std::cout << " UTF-8 keys" << std::endl;
605 {
606 boost::locale::generator g;
607 g.add_messages_domain(domain: "default");
608 g.add_messages_path(path: message_path);
609
610 std::locale l = g("he_IL.UTF-8");
611
612 // narrow
613 TEST_EQ(bl::gettext("בדיקה", l), "test");
614 TEST_EQ(bl::gettext("לא קיים", l), "לא קיים");
615
616 // wide
617 std::wstring wtest = bl::conv::utf_to_utf<wchar_t>(str: "בדיקה");
618 std::wstring wmiss = bl::conv::utf_to_utf<wchar_t>(str: "לא קיים");
619 TEST_EQ(bl::gettext(wtest.c_str(), l), L"test");
620 TEST_EQ(bl::gettext(wmiss.c_str(), l), wmiss);
621
622 l = g("he_IL.ISO-8859-8");
623
624 // conversion with substitution
625 TEST_EQ(bl::gettext("test-あにま-בדיקה", l), bl::conv::from_utf("test--בדיקה", "ISO-8859-8"));
626 }
627
628 std::cout << " ANSI keys" << std::endl;
629
630 {
631 boost::locale::generator g;
632 g.add_messages_domain(domain: "default/ISO-8859-8");
633 g.add_messages_path(path: message_path);
634
635 std::locale l = g("he_IL.UTF-8");
636
637 // narrow non-UTF-8 keys
638 // match
639 TEST_EQ(bl::gettext(bl::conv::from_utf("בדיקה", "ISO-8859-8").c_str(), l), "test");
640 // conversion
641 TEST_EQ(bl::gettext(bl::conv::from_utf("לא קיים", "ISO-8859-8").c_str(), l), "לא קיים");
642 }
643 }
644 // Test compiles
645 {
646 bl::gettext(id: "");
647 bl::gettext(id: L"");
648 bl::dgettext(domain: "", id: "");
649 bl::dgettext(domain: "", id: L"");
650
651 bl::pgettext(context: "", id: "");
652 bl::pgettext(context: L"", id: L"");
653 bl::dpgettext(domain: "", context: "", id: "");
654 bl::dpgettext(domain: "", context: L"", id: L"");
655
656 bl::ngettext(s: "", p: "", n: 1);
657 bl::ngettext(s: L"", p: L"", n: 1);
658 bl::dngettext(domain: "", s: "", p: "", n: 1);
659 bl::dngettext(domain: "", s: L"", p: L"", n: 1);
660
661 bl::npgettext(context: "", s: "", p: "", n: 1);
662 bl::npgettext(context: L"", s: L"", p: L"", n: 1);
663 bl::dnpgettext(domain: "", context: "", s: "", p: "", n: 1);
664 bl::dnpgettext(domain: "", context: L"", s: L"", p: L"", n: 1);
665 }
666}
667
668// boostinspect:noascii
669

source code of boost/libs/locale/test/test_message.cpp