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 
9 #include <boost/locale/generator.hpp>
10 #include <boost/locale/localization_backend.hpp>
11 #include <boost/locale/message.hpp>
12 #include <boost/locale/gnu_gettext.hpp>
13 #include <boost/locale/encoding.hpp>
14 #include "test_locale.hpp"
15 #include "test_locale_tools.hpp"
16 #include <fstream>
17 
18 namespace bl = boost::locale;
19 
20 std::string backend;
21 
22 bool file_loader_is_actually_called = false;
23 
24 struct file_loader {
operator ()file_loader25     std::vector<char> operator()(std::string const &name,std::string const &/*encoding*/) const
26     {
27         std::vector<char> buffer;
28         std::ifstream f(name.c_str(),std::ifstream::binary);
29         if(!f)
30             return buffer;
31         f.seekg(0,std::ifstream::end);
32         size_t len = f.tellg();
33         if(len == 0)
34             return buffer;
35         f.seekg(0);
36         buffer.resize(len,'\0');
37         f.read(&buffer[0],len);
38         file_loader_is_actually_called = true;
39         return buffer;
40     }
41 };
42 
43 
same_s(std::string s)44 std::string same_s(std::string s)
45 {
46     return s;
47 }
48 
same_w(std::wstring s)49 std::wstring same_w(std::wstring s)
50 {
51     return s;
52 }
53 
54 #ifdef BOOST_HAS_CHAR16_T
same_u16(std::u16string s)55 std::u16string same_u16(std::u16string s)
56 {
57     return s;
58 }
59 #endif
60 
61 #ifdef BOOST_HAS_CHAR32_T
same_u32(std::u32string s)62 std::u32string same_u32(std::u32string s)
63 {
64     return s;
65 }
66 #endif
67 
68 template<typename Char>
strings_equal(std::string n_c,std::string n_s,std::string n_p,int n,std::string iexpected,std::locale const & l,std::string domain)69 void strings_equal(std::string n_c,std::string n_s,std::string n_p,int n,std::string iexpected,std::locale const &l,std::string domain)
70 {
71     typedef std::basic_string<Char> string_type;
72     string_type expected=to_correct_string<Char>(iexpected,l);
73 
74     string_type c = to<Char>(n_c.c_str());
75     string_type s = to<Char>(n_s.c_str());
76     string_type p = to<Char>(n_p.c_str());
77 
78     if(domain=="default") {
79         TEST(bl::translate(c,s,p,n).str(l)==expected);
80         Char const *c_c_str = c.c_str(),*s_c_str=s.c_str(), *p_c_str=p.c_str(); // workaround gcc-3.4 bug
81         TEST(bl::translate(c_c_str,s_c_str,p_c_str,n).str(l)==expected);
82         std::locale tmp_locale=std::locale();
83         std::locale::global(l);
84         string_type tmp=bl::translate(c,s,p,n);
85         TEST(tmp==expected);
86         tmp=bl::translate(c,s,p,n).str();
87         TEST(tmp==expected);
88         std::locale::global(tmp_locale);
89 
90         std::basic_ostringstream<Char> ss;
91         ss.imbue(l);
92         ss << bl::translate(c,s,p,n);
93         TEST(ss.str()==expected);
94     }
95     TEST( bl::translate(c,s,p,n).str(l,domain)==expected );
96     std::locale tmp_locale=std::locale();
97     std::locale::global(l);
98     TEST(bl::translate(c,s,p,n).str(domain)==expected);
99     std::locale::global(tmp_locale);
100     {
101         std::basic_ostringstream<Char> ss;
102         ss.imbue(l);
103         ss << bl::as::domain(domain) << bl::translate(c,s,p,n);
104         TEST(ss.str()==expected);
105     }
106     {
107         std::basic_ostringstream<Char> ss;
108         ss.imbue(l);
109         ss << bl::as::domain(domain) << bl::translate(c.c_str(),s.c_str(),p.c_str(),n);
110         TEST(ss.str()==expected);
111     }
112 }
113 
114 
115 
116 
117 template<typename Char>
strings_equal(std::string n_s,std::string n_p,int n,std::string iexpected,std::locale const & l,std::string domain)118 void strings_equal(std::string n_s,std::string n_p,int n,std::string iexpected,std::locale const &l,std::string domain)
119 {
120     typedef std::basic_string<Char> string_type;
121     string_type expected=to_correct_string<Char>(iexpected,l);
122     string_type s = to<Char>(n_s.c_str());
123     string_type p = to<Char>(n_p.c_str());
124     if(domain=="default") {
125         TEST(bl::translate(s,p,n).str(l)==expected);
126         Char const *s_c_str=s.c_str(), *p_c_str=p.c_str(); // workaround gcc-3.4 bug
127         TEST(bl::translate(s_c_str,p_c_str,n).str(l)==expected);
128         std::locale tmp_locale=std::locale();
129         std::locale::global(l);
130         string_type tmp=bl::translate(s,p,n);
131         TEST(tmp==expected);
132         tmp=bl::translate(s,p,n).str();
133         TEST(tmp==expected);
134         std::locale::global(tmp_locale);
135 
136         std::basic_ostringstream<Char> ss;
137         ss.imbue(l);
138         ss << bl::translate(s,p,n);
139         TEST(ss.str()==expected);
140     }
141     TEST(bl::translate(s,p,n).str(l,domain)==expected);
142     std::locale tmp_locale=std::locale();
143     std::locale::global(l);
144     TEST(bl::translate(s,p,n).str(domain)==expected);
145     std::locale::global(tmp_locale);
146     {
147         std::basic_ostringstream<Char> ss;
148         ss.imbue(l);
149         ss << bl::as::domain(domain) << bl::translate(s,p,n);
150         TEST(ss.str()==expected);
151     }
152     {
153         std::basic_ostringstream<Char> ss;
154         ss.imbue(l);
155         ss << bl::as::domain(domain) << bl::translate(s.c_str(),p.c_str(),n);
156         TEST(ss.str()==expected);
157     }
158 }
159 
160 
161 template<typename Char>
strings_equal(std::string n_c,std::string n_original,std::string iexpected,std::locale const & l,std::string domain)162 void strings_equal(std::string n_c,std::string n_original,std::string iexpected,std::locale const &l,std::string domain)
163 {
164     typedef std::basic_string<Char> string_type;
165     string_type expected=to_correct_string<Char>(iexpected,l);
166     string_type original = to<Char>(n_original.c_str());
167     string_type c = to<Char>(n_c.c_str());
168     if(domain=="default") {
169         TEST(bl::translate(c,original).str(l)==expected);
170         Char const *original_c_str=original.c_str(); // workaround gcc-3.4 bug
171         Char const *context_c_str = c.c_str();
172         TEST(bl::translate(context_c_str,original_c_str).str(l)==expected);
173         std::locale tmp_locale=std::locale();
174         std::locale::global(l);
175         string_type tmp=bl::translate(c,original);
176         TEST(tmp==expected);
177         tmp=bl::translate(c,original).str();
178         TEST(tmp==expected);
179         std::locale::global(tmp_locale);
180 
181         std::basic_ostringstream<Char> ss;
182         ss.imbue(l);
183         ss << bl::translate(c,original);
184         TEST(ss.str()==expected);
185     }
186     TEST(bl::translate(c,original).str(l,domain)==expected);
187     std::locale tmp_locale=std::locale();
188     std::locale::global(l);
189     TEST(bl::translate(c,original).str(domain)==expected);
190     std::locale::global(tmp_locale);
191     {
192         std::basic_ostringstream<Char> ss;
193         ss.imbue(l);
194         ss << bl::as::domain(domain) << bl::translate(c,original);
195         TEST(ss.str()==expected);
196     }
197     {
198         std::basic_ostringstream<Char> ss;
199         ss.imbue(l);
200         ss << bl::as::domain(domain) << bl::translate(c.c_str(),original.c_str());
201         TEST(ss.str()==expected);
202     }
203 }
204 
205 
206 
207 
208 template<typename Char>
strings_equal(std::string n_original,std::string iexpected,std::locale const & l,std::string domain)209 void strings_equal(std::string n_original,std::string iexpected,std::locale const &l,std::string domain)
210 {
211     typedef std::basic_string<Char> string_type;
212     string_type expected=to_correct_string<Char>(iexpected,l);
213     string_type original = to<Char>(n_original.c_str());
214     if(domain=="default") {
215         TEST(bl::translate(original).str(l)==expected);
216         Char const *original_c_str=original.c_str(); // workaround gcc-3.4 bug
217         TEST(bl::translate(original_c_str).str(l)==expected);
218         std::locale tmp_locale=std::locale();
219         std::locale::global(l);
220         string_type tmp=bl::translate(original);
221         TEST(tmp==expected);
222         tmp=bl::translate(original).str();
223         TEST(tmp==expected);
224         std::locale::global(tmp_locale);
225 
226         std::basic_ostringstream<Char> ss;
227         ss.imbue(l);
228         ss << bl::translate(original);
229         TEST(ss.str()==expected);
230     }
231     TEST(bl::translate(original).str(l,domain)==expected);
232     std::locale tmp_locale=std::locale();
233     std::locale::global(l);
234     TEST(bl::translate(original).str(domain)==expected);
235     std::locale::global(tmp_locale);
236     {
237         std::basic_ostringstream<Char> ss;
238         ss.imbue(l);
239         ss << bl::as::domain(domain) << bl::translate(original);
240         TEST(ss.str()==expected);
241     }
242     {
243         std::basic_ostringstream<Char> ss;
244         ss.imbue(l);
245         ss << bl::as::domain(domain) << bl::translate(original.c_str());
246         TEST(ss.str()==expected);
247     }
248 }
249 
test_cntranslate(std::string c,std::string s,std::string p,int n,std::string expected,std::locale const & l,std::string domain)250 void test_cntranslate(std::string c,std::string s,std::string p,int n,std::string expected,std::locale const &l,std::string domain)
251 {
252     strings_equal<char>(c,s,p,n,expected,l,domain);
253     strings_equal<wchar_t>(c,s,p,n,expected,l,domain);
254     #ifdef BOOST_HAS_CHAR16_T
255     if(backend=="icu" || backend=="std")
256         strings_equal<char16_t>(c,s,p,n,expected,l,domain);
257     #endif
258     #ifdef BOOST_HAS_CHAR32_T
259     if(backend=="icu" || backend=="std")
260         strings_equal<char32_t>(c,s,p,n,expected,l,domain);
261     #endif
262 }
263 
264 
test_ntranslate(std::string s,std::string p,int n,std::string expected,std::locale const & l,std::string domain)265 void test_ntranslate(std::string s,std::string p,int n,std::string expected,std::locale const &l,std::string domain)
266 {
267     strings_equal<char>(s,p,n,expected,l,domain);
268     strings_equal<wchar_t>(s,p,n,expected,l,domain);
269     #ifdef BOOST_HAS_CHAR16_T
270     if(backend=="icu" || backend=="std")
271         strings_equal<char16_t>(s,p,n,expected,l,domain);
272     #endif
273     #ifdef BOOST_HAS_CHAR32_T
274     if(backend=="icu" || backend=="std")
275         strings_equal<char32_t>(s,p,n,expected,l,domain);
276     #endif
277 }
278 
test_ctranslate(std::string c,std::string original,std::string expected,std::locale const & l,std::string domain)279 void test_ctranslate(std::string c,std::string original,std::string expected,std::locale const &l,std::string domain)
280 {
281     strings_equal<char>(c,original,expected,l,domain);
282     strings_equal<wchar_t>(c,original,expected,l,domain);
283     #ifdef BOOST_HAS_CHAR16_T
284     if(backend=="icu" || backend=="std")
285         strings_equal<char16_t>(c,original,expected,l,domain);
286     #endif
287     #ifdef BOOST_HAS_CHAR32_T
288     if(backend=="icu" || backend=="std")
289         strings_equal<char32_t>(c,original,expected,l,domain);
290     #endif
291 }
292 
293 
294 
test_translate(std::string original,std::string expected,std::locale const & l,std::string domain)295 void test_translate(std::string original,std::string expected,std::locale const &l,std::string domain)
296 {
297     strings_equal<char>(original,expected,l,domain);
298     strings_equal<wchar_t>(original,expected,l,domain);
299     #ifdef BOOST_HAS_CHAR16_T
300     if(backend=="icu" || backend=="std")
301         strings_equal<char16_t>(original,expected,l,domain);
302     #endif
303     #ifdef BOOST_HAS_CHAR32_T
304     if(backend=="icu" || backend=="std")
305         strings_equal<char32_t>(original,expected,l,domain);
306     #endif
307 }
308 
309 bool iso_8859_8_not_supported = false;
310 
311 
main(int argc,char ** argv)312 int main(int argc,char **argv)
313 {
314     try {
315         std::string def[] = {
316         #ifdef BOOST_LOCALE_WITH_ICU
317             "icu" ,
318         #endif
319         #ifndef BOOST_LOCALE_NO_STD_BACKEND
320             "std" ,
321         #endif
322         #ifndef BOOST_LOCALE_NO_POSIX_BACKEND
323             "posix",
324         #endif
325         #ifndef BOOST_LOCALE_NO_WINAPI_BACKEND
326             "winapi",
327         #endif
328         };
329         for(int type = 0 ; type < int(sizeof(def)/sizeof(def[0])) ; type ++ ) {
330             boost::locale::localization_backend_manager tmp_backend = boost::locale::localization_backend_manager::global();
331             tmp_backend.select(def[type]);
332             boost::locale::localization_backend_manager::global(tmp_backend);
333 
334             backend = def[type];
335 
336             std::cout << "Testing for backend --------- " << def[type] << std::endl;
337 
338             boost::locale::generator g;
339             g.add_messages_domain("default");
340             g.add_messages_domain("simple");
341             g.add_messages_domain("full");
342             g.add_messages_domain("fall");
343             if(argc==2)
344                 g.add_messages_path(argv[1]);
345             else
346                 g.add_messages_path("./");
347 
348 
349             std::string locales[] = { "he_IL.UTF-8", "he_IL.ISO8859-8" };
350 
351             for(unsigned i=0;i<sizeof(locales)/sizeof(locales[0]);i++){
352                 std::locale l;
353 
354                 if(i==1) {
355                     try {
356                         l = g(locales[i]);
357                     }
358                     catch(boost::locale::conv::invalid_charset_error const &e) {
359                         std::cout << "Looks like ISO-8859-8 is not supported! skipping" << std::endl;
360                         iso_8859_8_not_supported = true;
361                         continue;
362                     }
363                 }
364                 else {
365                         l = g(locales[i]);
366                 }
367 
368                 std::cout << "  Testing "<<locales[i]<<std::endl;
369                 std::cout << "    single forms" << std::endl;
370 
371                 test_translate("hello","שלום",l,"default");
372                 test_translate("hello","היי",l,"simple");
373                 test_translate("hello","hello",l,"undefined");
374                 test_translate("untranslated","untranslated",l,"default");
375                 // Check removal of old "context" information
376                 test_translate("#untranslated","#untranslated",l,"default");
377                 test_translate("##untranslated","##untranslated",l,"default");
378                 test_ctranslate("context","hello","שלום בהקשר אחר",l,"default");
379                 test_translate("#hello","#שלום",l,"default");
380 
381                 std::cout << "    plural forms" << std::endl;
382 
383                 {
384                     test_ntranslate("x day","x days",0,"x ימים",l,"default");
385                     test_ntranslate("x day","x days",1,"יום x",l,"default");
386                     test_ntranslate("x day","x days",2,"יומיים",l,"default");
387                     test_ntranslate("x day","x days",3,"x ימים",l,"default");
388                     test_ntranslate("x day","x days",20,"x יום",l,"default");
389 
390                     test_ntranslate("x day","x days",0,"x days",l,"undefined");
391                     test_ntranslate("x day","x days",1,"x day",l,"undefined");
392                     test_ntranslate("x day","x days",2,"x days",l,"undefined");
393                     test_ntranslate("x day","x days",20,"x days",l,"undefined");
394                 }
395                 std::cout << "    plural forms with context" << std::endl;
396                 {
397                     std::string inp = "context";
398                     std::string out = "בהקשר ";
399 
400                     test_cntranslate(inp,"x day","x days",0,out+"x ימים",l,"default");
401                     test_cntranslate(inp,"x day","x days",1,out+"יום x",l,"default");
402                     test_cntranslate(inp,"x day","x days",2,out+"יומיים",l,"default");
403                     test_cntranslate(inp,"x day","x days",3,out+"x ימים",l,"default");
404                     test_cntranslate(inp,"x day","x days",20,out+"x יום",l,"default");
405 
406                     test_cntranslate(inp,"x day","x days",0,"x days",l,"undefined");
407                     test_cntranslate(inp,"x day","x days",1,"x day",l,"undefined");
408                     test_cntranslate(inp,"x day","x days",2,"x days",l,"undefined");
409                     test_cntranslate(inp,"x day","x days",20,"x days",l,"undefined");
410                 }
411             }
412             std::cout << "  Testing fallbacks" <<std::endl;
413             test_translate("test","he_IL",g("he_IL.UTF-8"),"full");
414             test_translate("test","he",g("he_IL.UTF-8"),"fall");
415 
416             std::cout << "  Testing automatic conversions " << std::endl;
417             std::locale::global(g("he_IL.UTF-8"));
418 
419 
420             TEST(same_s(bl::translate("hello"))=="שלום");
421             TEST(same_w(bl::translate(to<wchar_t>("hello")))==to<wchar_t>("שלום"));
422 
423             #ifdef BOOST_HAS_CHAR16_T
424             if(backend=="icu" || backend=="std")
425                 TEST(same_u16(bl::translate(to<char16_t>("hello")))==to<char16_t>("שלום"));
426             #endif
427 
428             #ifdef BOOST_HAS_CHAR32_T
429             if(backend=="icu" || backend=="std")
430                 TEST(same_u32(bl::translate(to<char32_t>("hello")))==to<char32_t>("שלום"));
431             #endif
432 
433         }
434 
435         std::cout << "Testing custom file system support" << std::endl;
436         {
437             boost::locale::gnu_gettext::messages_info info;
438             info.language = "he";
439             info.country = "IL";
440             info.encoding="UTF-8";
441             if(argc==2)
442                 info.paths.push_back(argv[1]);
443             else
444                 info.paths.push_back("./");
445 
446             info.domains.push_back(bl::gnu_gettext::messages_info::domain("default"));
447             info.callback = file_loader();
448 
449             std::locale l(std::locale::classic(),boost::locale::gnu_gettext::create_messages_facet<char>(info));
450             TEST(file_loader_is_actually_called);
451             TEST(bl::translate("hello").str(l)=="שלום");
452         }
453         if(iso_8859_8_not_supported)
454         {
455             std::cout << "ISO 8859-8 not supported so skipping non-US-ASCII keys" << std::endl;
456         }
457         else
458         {
459             std::cout << "Testing non-US-ASCII keys" << std::endl;
460             std::cout << "  UTF-8 keys" << std::endl;
461             {
462                 boost::locale::generator g;
463                 g.add_messages_domain("default");
464                 if(argc==2)
465                     g.add_messages_path(argv[1]);
466                 else
467                     g.add_messages_path("./");
468 
469                 std::locale l = g("he_IL.UTF-8");
470 
471                 // narrow
472                 TEST(bl::gettext("בדיקה",l)=="test");
473                 TEST(bl::gettext("לא קיים",l)=="לא קיים");
474 
475                 // wide
476                 std::wstring wtest = bl::conv::to_utf<wchar_t>("בדיקה","UTF-8");
477                 std::wstring wmiss = bl::conv::to_utf<wchar_t>("לא קיים","UTF-8");
478                 TEST(bl::gettext(wtest.c_str(),l)==L"test");
479                 TEST(bl::gettext(wmiss.c_str(),l)==wmiss);
480 
481                 l=g("he_IL.ISO-8859-8");
482 
483                 // conversion with substitution
484                 TEST(bl::gettext("test-あにま-בדיקה",l)==bl::conv::from_utf("test--בדיקה","ISO-8859-8"));
485             }
486 
487             std::cout << "  `ANSI' keys" << std::endl;
488 
489             {
490                 boost::locale::generator g;
491                 g.add_messages_domain("default/ISO-8859-8");
492                 if(argc==2)
493                     g.add_messages_path(argv[1]);
494                 else
495                     g.add_messages_path("./");
496 
497                 std::locale l = g("he_IL.UTF-8");
498 
499                 // narrow non-UTF-8 keys
500                 // match
501                 TEST(bl::gettext(bl::conv::from_utf("בדיקה","ISO-8859-8").c_str(),l)=="test");
502                 // conversion
503                 TEST(bl::gettext(bl::conv::from_utf("לא קיים","ISO-8859-8").c_str(),l)=="לא קיים");
504             }
505         }
506         // Test compiles
507         {
508             bl::gettext("");
509             bl::gettext(L"");
510             bl::dgettext("","");
511             bl::dgettext("",L"");
512             bl::pgettext("","");
513             bl::pgettext(L"",L"");
514             bl::dpgettext("","","");
515             bl::dpgettext("",L"",L"");
516             bl::ngettext("","",1);
517             bl::ngettext(L"",L"",1);
518             bl::dngettext("","","",1);
519             bl::dngettext("",L"",L"",1);
520             bl::npgettext("","","",1);
521             bl::npgettext(L"",L"",L"",1);
522             bl::dnpgettext("","","","",1);
523             bl::dnpgettext("",L"",L"",L"",1);
524         }
525 
526     }
527     catch(std::exception const &e) {
528         std::cerr << "Failed " << e.what() << std::endl;
529         return EXIT_FAILURE;
530     }
531     FINALIZE();
532 }
533 
534 // vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
535 // boostinspect:noascii
536