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 #define BOOST_LOCALE_SOURCE
9 #include <boost/config.hpp>
10 #include <boost/locale/message.hpp>
11 #include <boost/locale/gnu_gettext.hpp>
12 #include <boost/shared_ptr.hpp>
13 #include <boost/locale/encoding.hpp>
14 #ifdef BOOST_MSVC
15 #  pragma warning(disable : 4996)
16 #endif
17 
18 
19 #if BOOST_VERSION >= 103600
20 #define BOOST_LOCALE_UNORDERED_CATALOG
21 #endif
22 
23 #ifdef BOOST_LOCALE_UNORDERED_CATALOG
24 #include <boost/unordered_map.hpp>
25 #else
26 #include <map>
27 #endif
28 
29 #include <iostream>
30 
31 
32 #include "mo_hash.hpp"
33 #include "mo_lambda.hpp"
34 
35 #include <stdio.h>
36 
37 #include <string.h>
38 
39 namespace boost {
40     namespace locale {
41         namespace gnu_gettext {
42 
43             class c_file {
44                 c_file(c_file const &);
45                 void operator=(c_file const &);
46             public:
47 
48                 FILE *file;
49 
c_file()50                 c_file() :
51                     file(0)
52                 {
53                 }
~c_file()54                 ~c_file()
55                 {
56                     close();
57                 }
58 
close()59                 void close()
60                 {
61                     if(file) {
62                         fclose(file);
63                         file=0;
64                     }
65                 }
66 
67                 #if defined(BOOST_WINDOWS)
68 
open(std::string const & file_name,std::string const & encoding)69                 bool open(std::string const &file_name,std::string const &encoding)
70                 {
71                     close();
72 
73                     //
74                     // Under windows we have to use "_wfopen" to get
75                     // access to path's with Unicode in them
76                     //
77                     // As not all standard C++ libraries support nonstandard std::istream::open(wchar_t const *)
78                     // we would use old and good stdio and _wfopen CRTL functions
79                     //
80 
81                     std::wstring wfile_name = conv::to_utf<wchar_t>(file_name,encoding);
82                     file = _wfopen(wfile_name.c_str(),L"rb");
83 
84                     return file!=0;
85                 }
86 
87                 #else // POSIX systems do not have all this Wide API crap, as native codepages are UTF-8
88 
89                 // We do not use encoding as we use native file name encoding
90 
open(std::string const & file_name,std::string const &)91                 bool open(std::string const &file_name,std::string const &/* encoding */)
92                 {
93                     close();
94 
95                     file = fopen(file_name.c_str(),"rb");
96 
97                     return file!=0;
98                 }
99 
100                 #endif
101 
102             };
103 
104             class mo_file {
105             public:
106                 typedef std::pair<char const *,char const *> pair_type;
107 
mo_file(std::vector<char> & file)108                 mo_file(std::vector<char> &file) :
109                     native_byteorder_(true),
110                     size_(0)
111                 {
112                     load_file(file);
113                     init();
114                 }
115 
mo_file(FILE * file)116                 mo_file(FILE *file) :
117                     native_byteorder_(true),
118                     size_(0)
119                 {
120                     load_file(file);
121                     init();
122                 }
123 
find(char const * context_in,char const * key_in) const124                 pair_type find(char const *context_in,char const *key_in) const
125                 {
126                     pair_type null_pair((char const *)0,(char const *)0);
127                     if(hash_size_==0)
128                         return null_pair;
129                     uint32_t hkey = 0;
130                     if(context_in == 0)
131                         hkey = pj_winberger_hash_function(key_in);
132                     else {
133                         pj_winberger_hash::state_type st = pj_winberger_hash::initial_state;
134                         st = pj_winberger_hash::update_state(st,context_in);
135                         st = pj_winberger_hash::update_state(st,'\4'); // EOT
136                         st = pj_winberger_hash::update_state(st,key_in);
137                         hkey = st;
138                     }
139                     uint32_t incr = 1 + hkey % (hash_size_-2);
140                     hkey %= hash_size_;
141                     uint32_t orig=hkey;
142 
143 
144                     do {
145                         uint32_t idx = get(hash_offset_ + 4*hkey);
146                         /// Not found
147                         if(idx == 0)
148                             return null_pair;
149                         /// If equal values return translation
150                         if(key_equals(key(idx-1),context_in,key_in))
151                             return value(idx-1);
152                         /// Rehash
153                         hkey=(hkey + incr) % hash_size_;
154                     } while(hkey!=orig);
155                     return null_pair;
156                 }
157 
key_equals(char const * real_key,char const * cntx,char const * key)158                 static bool key_equals(char const *real_key,char const *cntx,char const *key)
159                 {
160                     if(cntx == 0)
161                         return strcmp(real_key,key) == 0;
162                     else {
163                         size_t real_len = strlen(real_key);
164                         size_t cntx_len = strlen(cntx);
165                         size_t key_len = strlen(key);
166                         if(cntx_len + 1 + key_len != real_len)
167                             return false;
168                         return
169                             memcmp(real_key,cntx,cntx_len) == 0
170                             && real_key[cntx_len] == '\4'
171                             && memcmp(real_key + cntx_len + 1 ,key,key_len) == 0;
172                     }
173                 }
174 
key(int id) const175                 char const *key(int id) const
176                 {
177                     uint32_t off = get(keys_offset_ + id*8 + 4);
178                     return data_ + off;
179                 }
180 
value(int id) const181                 pair_type value(int id) const
182                 {
183                     uint32_t len = get(translations_offset_ + id*8);
184                     uint32_t off = get(translations_offset_ + id*8 + 4);
185                     if(off >= file_size_ || off + len >= file_size_)
186                         throw std::runtime_error("Bad mo-file format");
187                     return pair_type(&data_[off],&data_[off]+len);
188                 }
189 
has_hash() const190                 bool has_hash() const
191                 {
192                     return hash_size_ != 0;
193                 }
194 
size() const195                 size_t size() const
196                 {
197                     return size_;
198                 }
199 
empty()200                 bool empty()
201                 {
202                     return size_ == 0;
203                 }
204 
205             private:
init()206                 void init()
207                 {
208                     // Read all format sizes
209                     size_=get(8);
210                     keys_offset_=get(12);
211                     translations_offset_=get(16);
212                     hash_size_=get(20);
213                     hash_offset_=get(24);
214                 }
215 
load_file(std::vector<char> & data)216                 void load_file(std::vector<char> &data)
217                 {
218                     vdata_.swap(data);
219                     file_size_ = vdata_.size();
220                     data_ = &vdata_[0];
221                     if(file_size_ < 4 )
222                         throw std::runtime_error("invalid 'mo' file format - the file is too short");
223                     uint32_t magic=0;
224                     memcpy(&magic,data_,4);
225                     if(magic == 0x950412de)
226                         native_byteorder_ = true;
227                     else if(magic == 0xde120495)
228                         native_byteorder_ = false;
229                     else
230                         throw std::runtime_error("Invalid file format - invalid magic number");
231                 }
232 
load_file(FILE * file)233                 void load_file(FILE *file)
234                 {
235                     uint32_t magic=0;
236                     // if the size is wrong magic would be wrong
237                     // ok to ingnore fread result
238                     size_t four_bytes = fread(&magic,4,1,file);
239                     (void)four_bytes; // shut GCC
240 
241                     if(magic == 0x950412de)
242                         native_byteorder_ = true;
243                     else if(magic == 0xde120495)
244                         native_byteorder_ = false;
245                     else
246                         throw std::runtime_error("Invalid file format");
247 
248                     fseek(file,0,SEEK_END);
249                     long len=ftell(file);
250                     if(len < 0) {
251                         throw std::runtime_error("Wrong file object");
252                     }
253                     fseek(file,0,SEEK_SET);
254                     vdata_.resize(len+1,0); // +1 to make sure the vector is not empty
255                     if(fread(&vdata_.front(),1,len,file)!=unsigned(len))
256                         throw std::runtime_error("Failed to read file");
257                     data_ = &vdata_[0];
258                     file_size_ = len;
259                 }
260 
get(unsigned offset) const261                 uint32_t get(unsigned offset) const
262                 {
263                     uint32_t tmp;
264                     if(offset > file_size_ - 4) {
265                         throw std::runtime_error("Bad mo-file format");
266                     }
267                     memcpy(&tmp,data_ + offset,4);
268                     convert(tmp);
269                     return tmp;
270                 }
271 
convert(uint32_t & v) const272                 void convert(uint32_t &v) const
273                 {
274                     if(native_byteorder_)
275                         return;
276                     v =   ((v & 0xFF) << 24)
277                         | ((v & 0xFF00) << 8)
278                         | ((v & 0xFF0000) >> 8)
279                         | ((v & 0xFF000000) >> 24);
280                 }
281 
282                 uint32_t keys_offset_;
283                 uint32_t translations_offset_;
284                 uint32_t hash_size_;
285                 uint32_t hash_offset_;
286 
287                 char const *data_;
288                 size_t file_size_;
289                 std::vector<char> vdata_;
290                 bool native_byteorder_;
291                 size_t size_;
292             };
293 
294             template<typename CharType>
295             struct mo_file_use_traits {
296                 static const bool in_use = false;
297                 typedef CharType char_type;
298                 typedef std::pair<char_type const *,char_type const *> pair_type;
useboost::locale::gnu_gettext::mo_file_use_traits299                 static pair_type use(mo_file const &/*mo*/,char_type const * /*context*/,char_type const * /*key*/)
300                 {
301                     return pair_type((char_type const *)(0),(char_type const *)(0));
302                 }
303             };
304 
305             template<>
306             struct mo_file_use_traits<char> {
307                 static const bool in_use = true;
308                 typedef char char_type;
309                 typedef std::pair<char_type const *,char_type const *> pair_type;
useboost::locale::gnu_gettext::mo_file_use_traits310                 static pair_type use(mo_file const &mo,char const *context,char const *key)
311                 {
312                     return mo.find(context,key);
313                 }
314             };
315 
316             template<typename CharType>
317             class converter {
318             public:
converter(std::string,std::string in_enc)319                 converter(std::string /*out_enc*/,std::string in_enc) :
320                     in_(in_enc)
321                 {
322                 }
323 
operator ()(char const * begin,char const * end)324                 std::basic_string<CharType> operator()(char const *begin,char const *end)
325                 {
326                     return conv::to_utf<CharType>(begin,end,in_,conv::stop);
327                 }
328 
329             private:
330                 std::string in_;
331             };
332 
333             template<>
334             class converter<char> {
335             public:
converter(std::string out_enc,std::string in_enc)336                 converter(std::string out_enc,std::string in_enc) :
337                     out_(out_enc),
338                     in_(in_enc)
339                 {
340                 }
341 
operator ()(char const * begin,char const * end)342                 std::string operator()(char const *begin,char const *end)
343                 {
344                     return conv::between(begin,end,out_,in_,conv::stop);
345                 }
346 
347             private:
348                 std::string out_,in_;
349             };
350 
351             template<typename CharType>
352             struct message_key {
353                 typedef CharType char_type;
354                 typedef std::basic_string<char_type> string_type;
355 
356 
message_keyboost::locale::gnu_gettext::message_key357                 message_key(string_type const &c = string_type()) :
358                     c_context_(0),
359                     c_key_(0)
360                 {
361                     size_t pos = c.find(char_type(4));
362                     if(pos == string_type::npos) {
363                         key_ = c;
364                     }
365                     else {
366                         context_ = c.substr(0,pos);
367                         key_ = c.substr(pos+1);
368                     }
369                 }
message_keyboost::locale::gnu_gettext::message_key370                 message_key(char_type const *c,char_type const *k) :
371                     c_key_(k)
372                 {
373                     static const char_type empty = 0;
374                     if(c!=0)
375                         c_context_ = c;
376                     else
377                         c_context_ = &empty;
378                 }
operator <boost::locale::gnu_gettext::message_key379                 bool operator < (message_key const &other) const
380                 {
381                     int cc = compare(context(),other.context());
382                     if(cc != 0)
383                         return cc < 0;
384                     return compare(key(),other.key()) < 0;
385                 }
operator ==boost::locale::gnu_gettext::message_key386                 bool operator==(message_key const &other) const
387                 {
388                     return compare(context(),other.context()) == 0
389                             && compare(key(),other.key())==0;
390                 }
operator !=boost::locale::gnu_gettext::message_key391                 bool operator!=(message_key const &other) const
392                 {
393                     return !(*this==other);
394                 }
contextboost::locale::gnu_gettext::message_key395                 char_type const *context() const
396                 {
397                     if(c_context_)
398                         return c_context_;
399                     return context_.c_str();
400                 }
keyboost::locale::gnu_gettext::message_key401                 char_type const *key() const
402                 {
403                     if(c_key_)
404                         return c_key_;
405                     return key_.c_str();
406                 }
407             private:
compareboost::locale::gnu_gettext::message_key408                 static int compare(char_type const *l,char_type const *r)
409                 {
410                     typedef std::char_traits<char_type> traits_type;
411                     for(;;) {
412                         char_type cl = *l++;
413                         char_type cr = *r++;
414                         if(cl == 0 && cr == 0)
415                             return 0;
416                         if(traits_type::lt(cl,cr))
417                             return -1;
418                         if(traits_type::lt(cr,cl))
419                             return 1;
420                     }
421                 }
422                 string_type context_;
423                 string_type key_;
424                 char_type const *c_context_;
425                 char_type const *c_key_;
426             };
427 
428             template<typename CharType>
429             struct hash_function {
operator ()boost::locale::gnu_gettext::hash_function430                 size_t operator()(message_key<CharType> const &msg) const
431                 {
432                     pj_winberger_hash::state_type state = pj_winberger_hash::initial_state;
433                     CharType const *p = msg.context();
434                     if(*p != 0) {
435                         CharType const *e = p;
436                         while(*e)
437                             e++;
438                         state = pj_winberger_hash::update_state(state,
439                                     static_cast<char const *>(p),
440                                     static_cast<char const *>(e));
441                         state = pj_winberger_hash::update_state(state,'\4');
442                     }
443                     p = msg.key();
444                     CharType const *e = p;
445                     while(*e)
446                         e++;
447                     state = pj_winberger_hash::update_state(state,
448                                 static_cast<char const *>(p),
449                                 static_cast<char const *>(e));
450                     return state;
451                 }
452             };
453 
454 
455             // By default for wide types the conversion is not requiredyy
456             template<typename CharType>
runtime_conversion(CharType const * msg,std::basic_string<CharType> &,bool,std::string const &,std::string const &)457             CharType const *runtime_conversion(CharType const *msg,
458                                                 std::basic_string<CharType> &/*buffer*/,
459                                                 bool /*do_conversion*/,
460                                                 std::string const &/*locale_encoding*/,
461                                                 std::string const &/*key_encoding*/)
462             {
463                 return msg;
464             }
465 
466             // But still need to specialize for char
467             template<>
runtime_conversion(char const * msg,std::string & buffer,bool do_conversion,std::string const & locale_encoding,std::string const & key_encoding)468             char const *runtime_conversion( char const *msg,
469                                             std::string &buffer,
470                                             bool do_conversion,
471                                             std::string const &locale_encoding,
472                                             std::string const &key_encoding)
473             {
474                 if(!do_conversion)
475                     return msg;
476                 if(details::is_us_ascii_string(msg))
477                     return msg;
478                 std::string tmp = conv::between(msg,locale_encoding,key_encoding,conv::skip);
479                 buffer.swap(tmp);
480                 return buffer.c_str();
481             }
482 
483             template<typename CharType>
484             class mo_message : public message_format<CharType> {
485 
486                 typedef CharType char_type;
487                 typedef std::basic_string<CharType> string_type;
488                 typedef message_key<CharType> key_type;
489                 #ifdef BOOST_LOCALE_UNORDERED_CATALOG
490                 typedef boost::unordered_map<key_type,string_type,hash_function<CharType> > catalog_type;
491                 #else
492                 typedef std::map<key_type,string_type> catalog_type;
493                 #endif
494                 typedef std::vector<catalog_type> catalogs_set_type;
495                 typedef std::map<std::string,int> domains_map_type;
496             public:
497 
498                 typedef std::pair<CharType const *,CharType const *> pair_type;
499 
get(int domain_id,char_type const * context,char_type const * id) const500                 virtual char_type const *get(int domain_id,char_type const *context,char_type const *id) const
501                 {
502                     return get_string(domain_id,context,id).first;
503                 }
504 
get(int domain_id,char_type const * context,char_type const * single_id,int n) const505                 virtual char_type const *get(int domain_id,char_type const *context,char_type const *single_id,int n) const
506                 {
507                     pair_type ptr = get_string(domain_id,context,single_id);
508                     if(!ptr.first)
509                         return 0;
510                     int form=0;
511                     if(plural_forms_.at(domain_id))
512                         form = (*plural_forms_[domain_id])(n);
513                     else
514                         form = n == 1 ? 0 : 1; // Fallback to english plural form
515 
516                     CharType const *p=ptr.first;
517                     for(int i=0;p < ptr.second && i<form;i++) {
518                         p=std::find(p,ptr.second,0);
519                         if(p==ptr.second)
520                             return 0;
521                         ++p;
522                     }
523                     if(p>=ptr.second)
524                         return 0;
525                     return p;
526                 }
527 
domain(std::string const & domain) const528                 virtual int domain(std::string const &domain) const
529                 {
530                     domains_map_type::const_iterator p=domains_.find(domain);
531                     if(p==domains_.end())
532                         return -1;
533                     return p->second;
534                 }
535 
mo_message(messages_info const & inf)536                 mo_message(messages_info const &inf)
537                 {
538                     std::string language = inf.language;
539                     std::string variant = inf.variant;
540                     std::string country = inf.country;
541                     std::string encoding = inf.encoding;
542                     std::string lc_cat = inf.locale_category;
543                     std::vector<messages_info::domain> const &domains = inf.domains;
544                     std::vector<std::string> const &search_paths = inf.paths;
545 
546                     //
547                     // List of fallbacks: en_US@euro, en@euro, en_US, en.
548                     //
549                     std::vector<std::string> paths;
550 
551 
552                     if(!variant.empty() && !country.empty())
553                         paths.push_back(language + "_" + country + "@" + variant);
554 
555                     if(!variant.empty())
556                         paths.push_back(language + "@" + variant);
557 
558                     if(!country.empty())
559                         paths.push_back(language + "_" + country);
560 
561                     paths.push_back(language);
562 
563                     catalogs_.resize(domains.size());
564                     mo_catalogs_.resize(domains.size());
565                     plural_forms_.resize(domains.size());
566 
567 
568                     for(unsigned id=0;id<domains.size();id++) {
569                         std::string domain=domains[id].name;
570                         std::string key_encoding = domains[id].encoding;
571                         domains_[domain]=id;
572 
573 
574                         bool found=false;
575                         for(unsigned j=0;!found && j<paths.size();j++) {
576                             for(unsigned i=0;!found && i<search_paths.size();i++) {
577                                 std::string full_path = search_paths[i]+"/"+paths[j]+"/" + lc_cat + "/"+domain+".mo";
578                                 found = load_file(full_path,encoding,key_encoding,id,inf.callback);
579                             }
580                         }
581                     }
582                 }
583 
convert(char_type const * msg,string_type & buffer) const584                 char_type const *convert(char_type const *msg,string_type &buffer) const
585                 {
586                     return runtime_conversion<char_type>(msg,buffer,key_conversion_required_,locale_encoding_,key_encoding_);
587                 }
588 
~mo_message()589                 virtual ~mo_message()
590                 {
591                 }
592 
593             private:
compare_encodings(std::string const & left,std::string const & right)594                 int compare_encodings(std::string const &left,std::string const &right)
595                 {
596                     return convert_encoding_name(left).compare(convert_encoding_name(right));
597                 }
598 
convert_encoding_name(std::string const & in)599                 std::string convert_encoding_name(std::string const &in)
600                 {
601                     std::string result;
602                     for(unsigned i=0;i<in.size();i++) {
603                         char c=in[i];
604                         if('A' <= c && c<='Z')
605                             c=c-'A' + 'a';
606                         else if(('a' <= c && c<='z') || ('0' <= c && c<='9'))
607                             ;
608                         else
609                             continue;
610                         result+=c;
611                     }
612                     return result;
613                 }
614 
615 
load_file(std::string const & file_name,std::string const & locale_encoding,std::string const & key_encoding,int id,messages_info::callback_type const & callback)616                 bool load_file( std::string const &file_name,
617                                 std::string const &locale_encoding,
618                                 std::string const &key_encoding,
619                                 int id,
620                                 messages_info::callback_type const &callback)
621                 {
622                     locale_encoding_ = locale_encoding;
623                     key_encoding_ = key_encoding;
624 
625                     key_conversion_required_ =  sizeof(CharType) == 1
626                                                 && compare_encodings(locale_encoding,key_encoding)!=0;
627 
628                     std::auto_ptr<mo_file> mo;
629 
630                     if(callback) {
631                         std::vector<char> vfile = callback(file_name,locale_encoding);
632                         if(vfile.empty())
633                             return false;
634                         mo.reset(new mo_file(vfile));
635                     }
636                     else {
637                         c_file the_file;
638                         the_file.open(file_name,locale_encoding);
639                         if(!the_file.file)
640                             return false;
641                         mo.reset(new mo_file(the_file.file));
642                     }
643 
644                     std::string plural = extract(mo->value(0).first,"plural=","\r\n;");
645 
646                     std::string mo_encoding = extract(mo->value(0).first,"charset="," \r\n;");
647 
648                     if(mo_encoding.empty())
649                         throw std::runtime_error("Invalid mo-format, encoding is not specified");
650 
651                     if(!plural.empty()) {
652                         std::auto_ptr<lambda::plural> ptr=lambda::compile(plural.c_str());
653                         plural_forms_[id] = ptr;
654                     }
655 
656                     if( mo_useable_directly(mo_encoding,*mo) )
657                     {
658                         mo_catalogs_[id]=mo;
659                     }
660                     else {
661                         converter<CharType> cvt_value(locale_encoding,mo_encoding);
662                         converter<CharType> cvt_key(key_encoding,mo_encoding);
663                         for(unsigned i=0;i<mo->size();i++) {
664                             char const *ckey = mo->key(i);
665                             string_type skey = cvt_key(ckey,ckey+strlen(ckey));
666                             key_type key(skey);
667 
668                             mo_file::pair_type tmp = mo->value(i);
669                             string_type value = cvt_value(tmp.first,tmp.second);
670                             catalogs_[id][key].swap(value);
671                         }
672                     }
673                     return true;
674 
675                 }
676 
677                 // Check if the mo file as-is is useful
678                 // 1. It is char and not wide character
679                 // 2. The locale encoding and mo encoding is same
680                 // 3. The source strings encoding and mo encoding is same or all
681                 //    mo key strings are US-ASCII
mo_useable_directly(std::string const & mo_encoding,mo_file const & mo)682                 bool mo_useable_directly(   std::string const &mo_encoding,
683                                             mo_file const &mo)
684                 {
685                     if(sizeof(CharType) != 1)
686                         return false;
687                     if(!mo.has_hash())
688                         return false;
689                     if(compare_encodings(mo_encoding.c_str(),locale_encoding_.c_str())!=0)
690                         return false;
691                     if(compare_encodings(mo_encoding.c_str(),key_encoding_.c_str())==0) {
692                         return true;
693                     }
694                     for(unsigned i=0;i<mo.size();i++) {
695                         if(!details::is_us_ascii_string(mo.key(i))) {
696                             return false;
697                         }
698                     }
699                     return true;
700                 }
701 
702 
703 
extract(std::string const & meta,std::string const & key,char const * separator)704                 static std::string extract(std::string const &meta,std::string const &key,char const *separator)
705                 {
706                     size_t pos=meta.find(key);
707                     if(pos == std::string::npos)
708                         return "";
709                     pos+=key.size(); /// size of charset=
710                     size_t end_pos = meta.find_first_of(separator,pos);
711                     return meta.substr(pos,end_pos - pos);
712                 }
713 
714 
715 
716 
get_string(int domain_id,char_type const * context,char_type const * in_id) const717                 pair_type get_string(int domain_id,char_type const *context,char_type const *in_id) const
718                 {
719                     pair_type null_pair((CharType const *)0,(CharType const *)0);
720                     if(domain_id < 0 || size_t(domain_id) >= catalogs_.size())
721                         return null_pair;
722                     if(mo_file_use_traits<char_type>::in_use && mo_catalogs_[domain_id]) {
723                         return mo_file_use_traits<char_type>::use(*mo_catalogs_[domain_id],context,in_id);
724                     }
725                     else {
726                         key_type key(context,in_id);
727                         catalog_type const &cat = catalogs_[domain_id];
728                         typename catalog_type::const_iterator p = cat.find(key);
729                         if(p==cat.end()) {
730                             return null_pair;
731                         }
732                         return pair_type(p->second.data(),p->second.data()+p->second.size());
733                     }
734                 }
735 
736                 catalogs_set_type catalogs_;
737                 std::vector<boost::shared_ptr<mo_file> > mo_catalogs_;
738                 std::vector<boost::shared_ptr<lambda::plural> > plural_forms_;
739                 domains_map_type domains_;
740 
741                 std::string locale_encoding_;
742                 std::string key_encoding_;
743                 bool key_conversion_required_;
744             };
745 
746             template<>
create_messages_facet(messages_info const & info)747             message_format<char> *create_messages_facet(messages_info const &info)
748             {
749                 return new mo_message<char>(info);
750             }
751 
752             template<>
create_messages_facet(messages_info const & info)753             message_format<wchar_t> *create_messages_facet(messages_info const &info)
754             {
755                 return new mo_message<wchar_t>(info);
756             }
757 
758             #ifdef BOOST_HAS_CHAR16_T
759 
760             template<>
create_messages_facet(messages_info const & info)761             message_format<char16_t> *create_messages_facet(messages_info const &info)
762             {
763                 return new mo_message<char16_t>(info);
764             }
765             #endif
766 
767             #ifdef BOOST_HAS_CHAR32_T
768 
769             template<>
create_messages_facet(messages_info const & info)770             message_format<char32_t> *create_messages_facet(messages_info const &info)
771             {
772                 return new mo_message<char32_t>(info);
773             }
774             #endif
775 
776 
777         } /// gnu_gettext
778 
779     } // locale
780 } // boost
781 // vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
782 
783