The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
gettext_boost.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2016 by David White <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "global.hpp"
16 #include "gettext.hpp"
17 #include "log.hpp"
18 
19 #include <iostream>
20 #include <locale>
21 #include <boost/locale.hpp>
22 // including boost/thread fixes linking of boost locale for msvc on boost 1.60
23 #include <boost/thread.hpp>
24 #include <set>
25 
26 #define DBG_G LOG_STREAM(debug, lg::general())
27 #define LOG_G LOG_STREAM(info, lg::general())
28 #define WRN_G LOG_STREAM(warn, lg::general())
29 #define ERR_G LOG_STREAM(err, lg::general())
30 
31 namespace bl = boost::locale;
32 namespace
33 {
34  class default_utf8_locale_name
35  {
36  public:
37  static const std::string& name()
38  {
39  //Use pointers becasue we don't want it to be destructed at programm end.
40  static default_utf8_locale_name* lname = new default_utf8_locale_name();
41  return lname->name_;
42  }
43  private:
44  default_utf8_locale_name()
45  : name_()
46  {
47  LOG_G << "Generating default locale\n";
48  try
49  {
50  //NOTE: the default_locale objects needs to live as least as long as the locale_info object. Otherwise the programm will segfault.
51  std::locale default_locale = bl::generator().generate("");
52  const boost::locale::info& locale_info = std::use_facet< boost::locale::info >(default_locale);
53  name_ += locale_info.language();
54  if(!locale_info.country().empty())
55  name_ += "_" + locale_info.country();
56  name_ += ".UTF-8";
57  if(!locale_info.variant().empty())
58  name_ += "@" + locale_info.variant();
59  }
60  catch(const std::exception& e)
61  {
62  ERR_G << "Failed to generate default locale string. message:" << e.what() << std::endl;
63  }
64  LOG_G << "Finished generating default locale, default is now '" << name_ << "'\n";
65  }
66 
67  std::string name_;
68  };
69  struct translation_manager
70  {
71  translation_manager()
72  : loaded_paths_()
73  , loaded_domains_()
74  , current_language_(default_utf8_locale_name::name())
75  , generator_()
76  , current_locale_()
77  , is_dirty_(true)
78  {
79  const bl::localization_backend_manager& g_mgr = bl::localization_backend_manager::global();
80  for(const std::string& name : g_mgr.get_all_backends())
81  {
82  LOG_G << "Found boost locale backend: '" << name << "'\n";
83  }
84 
85  generator_.use_ansi_encoding(false);
86  generator_.categories(bl::message_facet | bl::information_facet | bl::collation_facet);
87  generator_.characters(bl::char_facet);
88  //we cannot have current_locale_ beeing a non boost gerenerated locale since it might not suppy
89  //the boost::locale::info facet. as soon as we add message paths update_locale_internal might fail
90  //for example becasue of invalid .mo files. So make sure we call it at least once before adding paths/domains
91  update_locale_internal();
92  }
93 
94  void add_messages_domain(const std::string& domain)
95  {
96  if(loaded_domains_.find(domain) != loaded_domains_.end())
97  {
98  return;
99  }
100 
101  if(domain.find('/') != std::string::npos)
102  {
103  // Forward slash has a specific meaning in Boost.Locale domain
104  // names, specifying the encoding. We use UTF-8 for everything
105  // so we can't possibly support that, and odds are it's a user
106  // mistake (as in bug #23839).
107  ERR_G << "illegal textdomain name '" << domain
108  << "', skipping textdomain\n";
109  return;
110  }
111 
112  generator_.add_messages_domain(domain);
113  loaded_domains_.insert(domain);
114  }
115 
116  void add_messages_path(const std::string& path)
117  {
118  if(loaded_paths_.find(path) != loaded_paths_.end())
119  {
120  return;
121  }
122  generator_.add_messages_path(path);
123  loaded_paths_.insert(path);
124  }
125 
126  void set_default_messages_domain(const std::string& domain)
127  {
128  generator_.set_default_messages_domain(domain);
129  update_locale();
130  }
131 
132  void set_language(const std::string& language)
133  {
134  std::string::size_type at_pos = language.rfind('@');
135  if(language.empty())
136  {
137  current_language_ = default_utf8_locale_name::name();
138  }
139  else if(at_pos != std::string::npos)
140  {
141  current_language_ = language.substr(0, at_pos) + ".UTF-8" + language.substr(at_pos);
142  }
143  else
144  {
145  current_language_ = language + ".UTF-8";
146  }
147  update_locale();
148  }
149 
150  void update_locale()
151  {
152  is_dirty_ = true;
153  }
154 
155  void update_locale_internal()
156  {
157  try
158  {
159  LOG_G << "attempting to generate locale by name '" << current_language_ << "'\n";
160  current_locale_ = generator_.generate(current_language_);
161  const boost::locale::info& info = std::use_facet< boost::locale::info >(current_locale_);
162  LOG_G << "updated locale to '" << current_language_ << "' locale is now '" << current_locale_.name() << "' ( "
163  << "name='" << info.name()
164  << "' country='" << info.country()
165  << "' language='" << info.language()
166  << "' encoding='" << info.encoding()
167  << "' variant='" << info.variant() << "')\n";
168  }
169  catch(const boost::locale::conv::conversion_error&)
170  {
171  assert(std::has_facet<boost::locale::info>(current_locale_));
172  const boost::locale::info& info = std::use_facet< boost::locale::info >(current_locale_);
173  ERR_G << "Failed to update locale due to conversion error, locale is now: "
174  << "name='" << info.name()
175  << "' country='" << info.country()
176  << "' language='" << info.language()
177  << "' encoding='" << info.encoding()
178  << "' variant='" << info.variant()
179  << "'" << std::endl;
180  }
181  is_dirty_ = false;
182  }
183 
184  const std::locale& get_locale()
185  {
186  if(is_dirty_)
187  {
188  update_locale_internal();
189  }
190  return current_locale_;
191  }
192 
193  private:
194  std::set<std::string> loaded_paths_;
195  std::set<std::string> loaded_domains_;
196  std::string current_language_;
197  boost::locale::generator generator_;
198  std::locale current_locale_;
199  bool is_dirty_;
200  };
201 
202  translation_manager& get_manager()
203  {
204  static translation_manager* mng = new translation_manager();
205  return *mng;
206  }
207 
208 }
209 
210 namespace translation
211 {
212 
213 std::string dgettext(const char* domain, const char* msgid)
214 {
215  return boost::locale::dgettext(domain, msgid, get_manager().get_locale());
216 }
217 std::string egettext(char const *msgid)
218 {
219  return msgid[0] == '\0' ? msgid : boost::locale::gettext(msgid, get_manager().get_locale());
220 }
221 
222 std::string dsgettext (const char * domainname, const char *msgid)
223 {
224  std::string msgval = dgettext (domainname, msgid);
225  if (msgval == msgid) {
226  const char* firsthat = std::strrchr (msgid, '^');
227  if (firsthat == nullptr)
228  msgval = msgid;
229  else
230  msgval = firsthat + 1;
231  }
232  return msgval;
233 }
234 
235 std::string dsngettext (const char * domainname, const char *singular, const char *plural, int n)
236 {
237  std::string msgval = boost::locale::dngettext(domainname, singular, plural, n, get_manager().get_locale());
238  if (msgval == singular) {
239  const char* firsthat = std::strrchr (singular, '^');
240  if (firsthat == nullptr)
241  msgval = singular;
242  else
243  msgval = firsthat + 1;
244  }
245  return msgval;
246 }
247 
248 void bind_textdomain(const char* domain, const char* directory, const char* /*encoding*/)
249 {
250  LOG_G << "adding textdomain '" << domain << "' in directory '" << directory << "'\n";
251  get_manager().add_messages_domain(domain);
252  get_manager().add_messages_path(directory);
253  get_manager().update_locale();
254 }
255 
256 void set_default_textdomain(const char* domain)
257 {
258  LOG_G << "set_default_textdomain: '" << domain << "'\n";
259  get_manager().set_default_messages_domain(domain);
260 }
261 
262 
263 void set_language(const std::string& language, const std::vector<std::string>* /*alternates*/)
264 {
265  // why shoudl we need alternates? which languages we support shoudl only be related
266  // to which languages we ship with and not which the os supports
267  LOG_G << "setting language to '" << language << "' \n";
268  get_manager().set_language(language);
269 }
270 int compare(const std::string& s1, const std::string& s2)
271 {
272  return std::use_facet<std::collate<char> >(get_manager().get_locale()).compare(s1.c_str(), s1.c_str() + s1.size(), s2.c_str(), s2.c_str() + s2.size());
273 }
274 
275 void init()
276 {
277 
278 }
279 }
rng * generator
This generator is automatically synced during synced context.
Definition: random_new.cpp:52
logger & info()
Definition: log.cpp:91
int compare(const std::string &s1, const std::string &s2)
const language_def & get_locale()
Definition: language.cpp:253
std::string dsngettext(const char *domainname, const char *singular, const char *plural, int n)
Definition: gettext.cpp:91
std::string dgettext(const char *domain, const char *msgid)
Definition: gettext.cpp:39
void init()
Definition: gettext.cpp:189
void set_language(const std::string &slocale, const std::vector< std::string > *alternates)
Definition: gettext.cpp:126
std::string dsgettext(const char *domainname, const char *msgid)
Definition: gettext.cpp:48
void bind_textdomain(const char *domain, const char *directory, const char *encoding)
Definition: gettext.cpp:105
#define ERR_G
GLsizei const char ** path
Definition: glew.h:4654
void set_default_textdomain(const char *domain)
Definition: gettext.cpp:121
#define LOG_G
static UNUSEDNOWARN std::string gettext(const char *str)
Definition: gettext.hpp:64
std::string egettext(char const *msgid)
Definition: gettext.cpp:43
std::string language()
GLuint const GLchar * name
Definition: glew.h:1782
GLclampd n
Definition: glew.h:5903
Standard logging facilities (interface).
#define e
GLsizei const GLcharARB ** string
Definition: glew.h:4503