The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
language.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 
17 #include "filesystem.hpp"
18 #include "gettext.hpp"
19 #include "language.hpp"
20 #include "log.hpp"
21 #include "preferences.hpp"
22 #include "serialization/parser.hpp"
24 
25 #include <stdexcept>
26 #include <clocale>
27 #include <boost/scoped_array.hpp>
28 
29 #ifdef _WIN32
30 #include <windows.h>
31 #if !defined(_MSC_VER) && !defined(__MINGW32__)
32 extern "C" int _putenv(const char*);
33 #endif
34 #endif
35 
36 #ifdef __APPLE__
37 #include <cerrno>
38 #endif
39 
40 #define DBG_G LOG_STREAM(debug, lg::general())
41 #define LOG_G LOG_STREAM(info, lg::general())
42 #define WRN_G LOG_STREAM(warn, lg::general())
43 #define ERR_G LOG_STREAM(err, lg::general())
44 
45 namespace {
46  language_def current_language;
47  std::vector<config> languages_;
48  utils::string_map strings_;
49 }
50 
52 
53 bool load_strings(bool complain);
54 
56 {
57  return get_language().rtl;
58 }
59 
61 {
62  return ((language == a.language) /* && (localename == a.localename) */ );
63 }
64 
66 
68 {
69  static bool result = true;
70  return result;
71 }
72 
74 {
75  const utils::string_map::const_iterator i = strings_.find(key);
76  if(i != strings_.end()) {
77  return i->second;
78  } else {
79  static t_string empty_string;
80  // Let's do it the painful way (untlb means untranslatABLE).
81  // It will cause problem if somebody stores more than one reference at once
82  // but I don't really care since this path is an error path and it should
83  // not have been taken in the first place. -- silene
84  empty_string = "UNTLB " + key;
85  return empty_string;
86  }
87 }
88 
89 const t_string& symbol_table::operator[](const char* key) const
90 {
91  return (*this)[std::string(key)];
92 }
93 
95 {
96  config cfg;
97  try {
99  read(cfg, *stream);
100  } catch(config::error &) {
101  return false;
102  }
103 
104  known_languages.clear();
105  known_languages.push_back(
106  language_def("", t_string(N_("System default language"), "wesnoth"), "ltr", "", "A"));
107 
108  for (const config &lang : cfg.child_range("locale"))
109  {
110  known_languages.push_back(
111  language_def(lang["locale"], lang["name"], lang["dir"],
112  lang["alternates"], lang["sort_name"]));
113  }
114 
115  return true;
116 }
117 
119 {
120  // We sort every time, the local might have changed which can modify the
121  // sort order.
122  std::sort(known_languages.begin(), known_languages.end());
123  return known_languages;
124 }
125 
126 static void wesnoth_setlocale(int category, std::string const &slocale,
127  std::vector<std::string> const *alternates)
128 {
129  std::string locale = slocale;
130  // FIXME: ideally we should check LANGUAGE and on first invocation
131  // use that value, so someone with es would get the game in Spanish
132  // instead of en_US the first time round
133  // LANGUAGE overrides other settings, so for now just get rid of it
134  // FIXME: add configure check for unsetenv
135 
136  //category is never LC_MESSAGES since that case was moved to gettext.cpp to remove the dependency to libintl.h in this file
137  //that's why code like if (category == LC_MESSAGES) is outcommented here.
138 #ifndef _WIN32
139  unsetenv ("LANGUAGE"); // void so no return value to check
140 #endif
141 #ifdef __APPLE__
142  //if (category == LC_MESSAGES && setenv("LANG", locale.c_str(), 1) == -1) {
143  // ERR_G << "setenv LANG failed: " << strerror(errno);
144  //}
145 #endif
146 
147 #ifdef _WIN32
148  std::string win_locale(locale, 0, 2);
149  #include "language_win32.ii"
150  //if(category == LC_MESSAGES) {
151  // SetEnvironmentVariableA("LANG", win_locale.c_str());
152  // std::string env = "LANGUAGE=" + locale;
153  // _putenv(env.c_str());
154  // return;
155  //}
156  locale = win_locale;
157 #endif
158 
159  char *res = nullptr;
160  std::vector<std::string>::const_iterator i;
161  if (alternates) i = alternates->begin();
162 
163  for (;;)
164  {
165  std::string lang = locale, extra;
166  std::string::size_type pos = locale.find('@');
167  if (pos != std::string::npos) {
168  lang.erase(pos);
169  extra = locale.substr(pos);
170  }
171 
172  /*
173  * The "" is the last item to work-around a problem in glibc picking
174  * the non utf8 locale instead an utf8 version if available.
175  */
176  char const *encoding[] = { ".utf-8", ".UTF-8", "" };
177  for (int j = 0; j != 3; ++j)
178  {
179  locale = lang + encoding[j] + extra;
180  res = std::setlocale(category, locale.c_str());
181  if (res) {
182  LOG_G << "Set locale to '" << locale << "' result: '" << res << "'.\n";
183  goto done;
184  }
185  }
186 
187  if (!alternates || i == alternates->end()) break;
188  locale = *i;
189  ++i;
190  }
191 
192  WRN_G << "setlocale() failed for '" << slocale << "'." << std::endl;
193 
194  if (category == LC_TIME) {
195  time_locale_correct() = false;
196  }
197 
198 #ifndef _WIN32
199  //if(category == LC_MESSAGES) {
200  // WRN_G << "Setting LANGUAGE to '" << slocale << "'." << std::endl;
201  // setenv("LANGUAGE", slocale.c_str(), 1);
202  // std::setlocale(LC_MESSAGES, "");
203  //}
204 #endif
205 
206  done:
207  DBG_G << "Numeric locale: " << std::setlocale(LC_NUMERIC, nullptr) << '\n';
208  DBG_G << "Full locale: " << std::setlocale(LC_ALL, nullptr) << '\n';
209 }
210 
211 void set_language(const language_def& locale)
212 {
213  strings_.clear();
214 
215  std::string locale_lc;
216  locale_lc.resize(locale.localename.size());
217  std::transform(locale.localename.begin(),locale.localename.end(),locale_lc.begin(),tolower);
218 
219  current_language = locale;
220  time_locale_correct() = true;
221 
222  wesnoth_setlocale(LC_COLLATE, locale.localename, &locale.alternates);
223  wesnoth_setlocale(LC_TIME, locale.localename, &locale.alternates);
225  load_strings(false);
226 }
227 
228 bool load_strings(bool complain)
229 {
230  DBG_G << "Loading strings\n";
231  config cfg;
232 
233  LOG_G << "There are " << languages_.size() << " [language] blocks\n";
234  if (complain && languages_.empty()) {
235  std::cerr << "No [language] block found\n";
236  return false;
237  }
238  for (const config &lang : languages_) {
239  DBG_G << "[language]\n";
240  for (const config::attribute &j : lang.attribute_range()) {
241  DBG_G << j.first << "=\"" << j.second << "\"\n";
242  strings_[j.first] = j.second;
243  }
244  DBG_G << "[/language]\n";
245  }
246  DBG_G << "done\n";
247 
248  return true;
249 }
250 
251 const language_def& get_language() { return current_language; }
252 
254 {
255  //TODO: Add in support for querying the locale on Windows
256 
257  assert(!known_languages.empty());
258 
259  const std::string& prefs_locale = preferences::language();
260  if(prefs_locale.empty() == false) {
261  translation::set_language(prefs_locale, nullptr);
262  for(language_list::const_iterator i = known_languages.begin();
263  i != known_languages.end(); ++i) {
264  if (prefs_locale == i->localename)
265  return *i;
266  }
267  LOG_G << "'" << prefs_locale << "' locale not found in known array; defaulting to system locale\n";
268  return known_languages[0];
269  }
270 
271 #if 0
272  const char* const locale = getenv("LANG");
273  #ifdef _WIN32
274  std::string win_locale = locale
275  #include "language_win32.ii"
276  return win_locale;
277  #endif
278  if(locale != nullptr && strlen(locale) >= 2) {
279  //we can't pass pointers into the string to the std::string
280  //constructor because some STL implementations don't support
281  //it (*cough* MSVC++6)
282  std::string res(2,'z');
283  res[0] = tolower(locale[0]);
284  res[1] = tolower(locale[1]);
285  return res;
286  }
287 #endif
288 
289  LOG_G << "locale could not be determined; defaulting to system locale\n";
290  return known_languages[0];
291 }
292 
293 void init_textdomains(const config& cfg)
294 {
295  for (const config &t : cfg.child_range("textdomain"))
296  {
297  const std::string &name = t["name"];
298  const std::string &path = t["path"];
299 
300  if(path.empty()) {
302  } else {
304 
305  if (location.empty()) {
306  //if location is empty, this causes a crash on Windows, so we
307  //disallow adding empty domains
308  WRN_G << "no location found for '" << path << "', skipping textdomain" << std::endl;
309  } else {
310  t_string::add_textdomain(name, location);
311  }
312  }
313  }
314 }
315 
316 bool init_strings(const config& cfg)
317 {
318  languages_.clear();
319  for (const config &l : cfg.child_range("language")) {
320  languages_.push_back(l);
321  }
322  return load_strings(true);
323 }
324 
325 /* vim:set encoding=utf-8: */
bool & time_locale_correct()
Definition: language.cpp:67
child_itors child_range(const std::string &key)
Definition: config.cpp:613
std::string get_binary_dir_location(const std::string &type, const std::string &filename)
Returns a complete path to the actual directory of a given type or an empty string if the directory i...
#define WRN_G
Definition: language.cpp:42
int pos
Definition: formula.cpp:800
const t_string & operator[](const std::string &key) const
Definition: language.cpp:73
attribute_map::value_type attribute
Definition: config.hpp:393
const language_def & get_locale()
Definition: language.cpp:253
GLdouble GLdouble t
Definition: glew.h:1366
bool operator==(const language_def &) const
Definition: language.cpp:60
void set_language(const std::string &slocale, const std::vector< std::string > *alternates)
Definition: gettext.cpp:126
GLdouble l
Definition: glew.h:6966
static const std::string empty_string
Definition: frame.cpp:119
GLuint GLenum GLenum transform
Definition: glew.h:11418
GLuint GLuint stream
Definition: glew.h:5239
static language_list known_languages
Definition: language.cpp:51
#define LOG_G
Definition: language.cpp:41
GLsizei const char ** path
Definition: glew.h:4654
GLuint64EXT * result
Definition: glew.h:10727
std::map< std::string, t_string > string_map
bool current_language_rtl()
Definition: language.cpp:55
static void add_textdomain(const std::string &name, const std::string &path)
Definition: tstring.cpp:498
std::string get_intl_dir()
std::string localename
Definition: language.hpp:46
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:7319
void init_textdomains(const config &cfg)
Initializes the list of textdomains from a configuration object.
Definition: language.cpp:293
Encapsulates the map of the game.
Definition: location.hpp:38
GLuint res
Definition: glew.h:9258
static void wesnoth_setlocale(int category, std::string const &slocale, std::vector< std::string > const *alternates)
Definition: language.cpp:126
std::string get_wml_location(const std::string &filename, const std::string &current_dir=std::string())
Returns a complete path to the actual WML file or directory or an empty string if the file isn't pres...
const language_def & get_language()
Definition: language.cpp:251
bool load_language_list()
Definition: language.cpp:94
std::vector< language_def > language_list
Definition: language.hpp:56
size_t i
Definition: function.cpp:1057
language_list get_languages()
Definition: language.cpp:118
std::string language()
Declarations for File-IO.
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:400
#define N_(String)
Definition: gettext.hpp:90
static int sort(lua_State *L)
Definition: ltablib.cpp:246
t_string language
Definition: language.hpp:48
GLuint const GLchar * name
Definition: glew.h:1782
void set_language(const language_def &locale)
Definition: language.cpp:211
std::istream * preprocess_file(std::string const &fname, preproc_map *defines)
#define DBG_G
Definition: language.cpp:40
symbol_table string_table
Definition: language.cpp:65
Standard logging facilities (interface).
bool init_strings(const config &cfg)
Initializes certain English strings.
Definition: language.cpp:316
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
bool load_strings(bool complain)
Definition: language.cpp:228
GLsizei const GLcharARB ** string
Definition: glew.h:4503
std::vector< std::string > alternates
Definition: language.hpp:47