The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
tstring.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2004 by Philippe Plantier <[email protected]>
3  Copyright (C) 2005 - 2016 by Guillaume Melquiond <[email protected]>
4  Part of the Battle for Wesnoth Project http://www.wesnoth.org
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * Routines for translatable strings.
19  */
20 
21 #include "global.hpp"
22 
23 #include <map>
24 #include <vector>
25 #include <mutex>
26 
27 #include "tstring.hpp"
28 #include "gettext.hpp"
29 #include "log.hpp"
30 #include "utils/shared_object.hpp"
31 #include <boost/functional/hash.hpp>
32 
33 #include <boost/multi_index_container.hpp>
34 #include <boost/multi_index/hashed_index.hpp>
35 #include <boost/multi_index/member.hpp>
36 
37 static lg::log_domain log_config("config");
38 #define LOG_CF LOG_STREAM(info, log_config)
39 #define ERR_CF LOG_STREAM(err, log_config)
40 
41 static unsigned language_counter = 0;
42 
43 namespace {
44  const char TRANSLATABLE_PART = 0x01;
45  const char UNTRANSLATABLE_PART = 0x02;
46  const char TEXTDOMAIN_SEPARATOR = 0x03;
47  const char ID_TRANSLATABLE_PART = 0x04;
48 
49  std::vector<std::string> id_to_textdomain;
50  std::map<std::string, unsigned int> textdomain_to_id;
51 }
52 
53 size_t t_string_base::hash_value() const {
54  size_t seed = 0;
55  boost::hash_combine(seed, value_);
56  boost::hash_combine(seed, translatable_);
57  boost::hash_combine(seed, last_untranslatable_);
58  return seed;
59 }
60 
62  string_(string.value_),
63  begin_(0),
64  end_(string_.size()),
65  textdomain_(),
66  translatable_(false)
67 {
68  if(string.translatable_) {
69  update();
70  }
71 }
72 
74 {
75  unsigned int id;
76 
77  static std::string mark = std::string(TRANSLATABLE_PART, 1) + UNTRANSLATABLE_PART +
78  ID_TRANSLATABLE_PART;
79 
80  if(begin_ == string_.size())
81  return;
82 
83  switch(string_[begin_]) {
84  case TRANSLATABLE_PART: {
85  std::string::size_type textdomain_end =
86  string_.find(TEXTDOMAIN_SEPARATOR, begin_ + 1);
87 
88  if(textdomain_end == std::string::npos || textdomain_end >= string_.size() - 1) {
89  ERR_CF << "Error: invalid string: " << string_ << std::endl;
90  begin_ = string_.size();
91  return;
92  }
93 
94  end_ = string_.find_first_of(mark, textdomain_end + 1);
95  if(end_ == std::string::npos)
96  end_ = string_.size();
97 
98  textdomain_ = std::string(string_, begin_+1, textdomain_end - begin_ - 1);
99  translatable_ = true;
100  begin_ = textdomain_end + 1;
101 
102  break;
103  }
104  case ID_TRANSLATABLE_PART:
105  if(begin_ + 3 >= string_.size()) {
106  ERR_CF << "Error: invalid string: " << string_ << std::endl;
107  begin_ = string_.size();
108  return;
109  }
110  end_ = string_.find_first_of(mark, begin_ + 3);
111  if(end_ == std::string::npos)
112  end_ = string_.size();
113 
114  id = string_[begin_ + 1] + string_[begin_ + 2] * 256;
115  if(id >= id_to_textdomain.size()) {
116  ERR_CF << "Error: invalid string: " << string_ << std::endl;
117  begin_ = string_.size();
118  return;
119  }
120  textdomain_ = id_to_textdomain[id];
121  begin_ += 3;
122  translatable_ = true;
123 
124  break;
125 
126  case UNTRANSLATABLE_PART:
127  end_ = string_.find_first_of(mark, begin_ + 1);
128  if(end_ == std::string::npos)
129  end_ = string_.size();
130 
131  if(end_ <= begin_ + 1) {
132  ERR_CF << "Error: invalid string: " << string_ << std::endl;
133  begin_ = string_.size();
134  return;
135  }
136 
137  translatable_ = false;
138  textdomain_ = "";
139  begin_ += 1;
140  break;
141 
142  default:
143  end_ = string_.size();
144  translatable_ = false;
145  textdomain_ = "";
146  break;
147  }
148 }
149 
151  value_(),
154  translatable_(false),
155  last_untranslatable_(false)
156 {
157 }
158 
160 {
161 }
162 
164  value_(string.value_),
165  translated_value_(string.translated_value_),
166  translation_timestamp_(string.translation_timestamp_),
167  translatable_(string.translatable_),
168  last_untranslatable_(string.last_untranslatable_)
169 {
170 }
171 
173  value_(string),
174  translated_value_(),
175  translation_timestamp_(0),
176  translatable_(false),
177  last_untranslatable_(false)
178 {
179 }
180 
181 t_string_base::t_string_base(const std::string& string, const std::string& textdomain) :
182  value_(1, ID_TRANSLATABLE_PART),
183  translated_value_(),
184  translation_timestamp_(0),
185  translatable_(true),
186  last_untranslatable_(false)
187 {
188  if (string.empty()) {
189  value_.clear();
190  translatable_ = false;
191  return;
192  }
193 
194  std::map<std::string, unsigned int>::const_iterator idi = textdomain_to_id.find(textdomain);
195  unsigned int id;
196 
197  if(idi == textdomain_to_id.end()) {
198  id = id_to_textdomain.size();
199  textdomain_to_id[textdomain] = id;
200  id_to_textdomain.push_back(textdomain);
201  } else {
202  id = idi->second;
203  }
204 
205  value_ += char(id & 0xff);
206  value_ += char(id >> 8);
207  value_ += string;
208 }
209 
210 t_string_base::t_string_base(const char* string) :
211  value_(string),
212  translated_value_(),
213  translation_timestamp_(0),
214  translatable_(false),
215  last_untranslatable_(false)
216 {
217 }
218 
220 {
221  t_string_base orig(string);
222 
223  if(!string.empty() && (string[0] == TRANSLATABLE_PART || string[0] == UNTRANSLATABLE_PART)) {
224  orig.translatable_ = true;
225  } else {
226  orig.translatable_ = false;
227  }
228 
230 
231  for(walker w(orig); !w.eos(); w.next()) {
232  std::string substr(w.begin(), w.end());
233 
234  if(w.translatable()) {
235  res += t_string_base(substr, w.textdomain());
236  } else {
237  res += substr;
238  }
239  }
240 
241  return res;
242 }
243 
245 {
247  for(walker w(*this); !w.eos(); w.next()) {
248  res += std::string(w.begin(), w.end());
249  }
250  return res;
251 }
252 
254 {
256 
257  for(walker w(*this); !w.eos(); w.next()) {
258  t_string_base chunk;
259 
260  std::string substr(w.begin(), w.end());
261  if(w.translatable()) {
262  chunk.translatable_ = true;
263  chunk.last_untranslatable_ = false;
264  chunk.value_ = TRANSLATABLE_PART + w.textdomain() +
265  TEXTDOMAIN_SEPARATOR + substr;
266  } else {
267  chunk.translatable_ = false;
268  chunk.value_ = substr;
269  }
270 
271  res += chunk;
272  }
273 
274  return res.value();
275 }
276 
278 {
279  value_ = string.value_;
280  translated_value_ = string.translated_value_;
281  translation_timestamp_ = string.translation_timestamp_;
282  translatable_ = string.translatable_;
283  last_untranslatable_ = string.last_untranslatable_;
284 
285  return *this;
286 }
287 
289 {
290  value_ = string;
291  translated_value_ = "";
293  translatable_ = false;
294  last_untranslatable_ = false;
295 
296  return *this;
297 }
298 
300 {
301  value_ = string;
302  translated_value_ = "";
304  translatable_ = false;
305  last_untranslatable_ = false;
306 
307  return *this;
308 }
309 
311 {
312  t_string_base res(*this);
313  res += string;
314  return res;
315 }
316 
318 {
319  t_string_base res(*this);
320  res += string;
321  return res;
322 }
323 
324 t_string_base t_string_base::operator+(const char* string) const
325 {
326  t_string_base res(*this);
327  res += string;
328  return res;
329 }
330 
332 {
333  if (string.value_.empty())
334  return *this;
335  if (value_.empty()) {
336  *this = string;
337  return *this;
338  }
339 
340  if(translatable_ || string.translatable_) {
341  if(!translatable_) {
342  value_ = UNTRANSLATABLE_PART + value_;
343  translatable_ = true;
344  last_untranslatable_ = true;
345  } else
346  translated_value_ = "";
347  if(string.translatable_) {
348  if (last_untranslatable_ && string.value_[0] == UNTRANSLATABLE_PART)
349  value_.append(string.value_.begin() + 1, string.value_.end());
350  else
351  value_ += string.value_;
352  last_untranslatable_ = string.last_untranslatable_;
353  } else {
354  if (!last_untranslatable_) {
355  value_ += UNTRANSLATABLE_PART;
356  last_untranslatable_ = true;
357  }
358  value_ += string.value_;
359  }
360  } else {
361  value_ += string.value_;
362  }
363 
364  return *this;
365 }
366 
368 {
369  if (string.empty())
370  return *this;
371  if (value_.empty()) {
372  *this = string;
373  return *this;
374  }
375 
376  if(translatable_) {
377  if (!last_untranslatable_) {
378  value_ += UNTRANSLATABLE_PART;
379  last_untranslatable_ = true;
380  }
381  value_ += string;
382  translated_value_ = "";
383  } else {
384  value_ += string;
385  }
386 
387  return *this;
388 }
389 
391 {
392  if (string[0] == 0)
393  return *this;
394  if (value_.empty()) {
395  *this = string;
396  return *this;
397  }
398 
399  if(translatable_) {
400  if (!last_untranslatable_) {
401  value_ += UNTRANSLATABLE_PART;
402  last_untranslatable_ = true;
403  }
404  value_ += string;
405  translated_value_ = "";
406  } else {
407  value_ += string;
408  }
409 
410  return *this;
411 }
412 
414 {
415  return that.translatable_ == translatable_ && that.value_ == value_;
416 }
417 
418 bool t_string_base::operator==(const std::string &that) const
419 {
420  return !translatable_ && value_ == that;
421 }
422 
423 bool t_string_base::operator==(const char *that) const
424 {
425  return !translatable_ && value_ == that;
426 }
427 
429 {
430  return value_ < that.value_;
431 }
432 
434 {
435  if(!translatable_)
436  return value_;
437 
439  return translated_value_;
440 
441  translated_value_.clear();
442 
443  for(walker w(*this); !w.eos(); w.next()) {
444  std::string part(w.begin(), w.end());
445 
446  if(w.translatable()) {
447  translated_value_ += translation::dsgettext(w.textdomain().c_str(), part.c_str());
448  } else {
449  translated_value_ += part;
450  }
451  }
452 
454  return translated_value_;
455 }
456 
458 {
459 }
460 
462 {
463 }
464 
466 {
467 }
468 
469 t_string::t_string(const base &o) : val_(new base(o))
470 {
471 }
472 
473 t_string::t_string(const char *o) : val_(new base(o))
474 {
475 }
476 
478 {
479 }
480 
481 t_string::t_string(const std::string &o, const std::string &textdomain) : val_(new base(o, textdomain))
482 {
483 }
484 
486 {
487  val_ = o.val_;
488  return *this;
489 }
490 
492 {
493  t_string o2(o);
494  swap(o2);
495  return *this;
496 }
497 
499 {
500  LOG_CF << "Binding textdomain " << name << " to path " << path << "\n";
501 
502  // Register and (re-)bind this textdomain
503  translation::bind_textdomain(name.c_str(), path.c_str(), "UTF-8");
504 }
505 
507 {
509 }
510 
511 std::ostream& operator<<(std::ostream& stream, const t_string_base& string)
512 {
513  stream << string.str();
514  return stream;
515 }
static unsigned language_counter
Definition: tstring.cpp:41
static void reset_translations()
Definition: tstring.cpp:506
t_string()
Default implementation, but defined out-of-line for efficiency reasons.
Definition: tstring.cpp:457
std::string to_serialized() const
Definition: tstring.cpp:253
~t_string_base()
Default implementation, but defined out-of-line for efficiency reasons.
Definition: tstring.cpp:159
#define val_(o)
Definition: lobject.h:112
const std::string & str() const
Definition: tstring.cpp:433
void swap(t_string &other)
Definition: tstring.hpp:180
bool operator<(const t_string_base &string) const
Definition: tstring.cpp:428
std::string value_
Definition: tstring.hpp:104
t_string_base operator+(const t_string_base &) const
Definition: tstring.cpp:310
bool last_untranslatable_
Definition: tstring.hpp:107
std::string base_str() const
Definition: tstring.cpp:244
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
t_string_base & operator=(const t_string_base &)
Default implementation, but defined out-of-line for efficiency reasons.
Definition: tstring.cpp:277
unsigned translation_timestamp_
Definition: tstring.hpp:106
std::string translated_value_
Definition: tstring.hpp:105
boost::shared_ptr< const t_string_base > val_
Definition: tstring.hpp:183
GLuint GLuint stream
Definition: glew.h:5239
bool empty() const
Definition: tstring.hpp:90
GLsizei const char ** path
Definition: glew.h:4654
GLuint id
Definition: glew.h:1647
static void add_textdomain(const std::string &name, const std::string &path)
Definition: tstring.cpp:498
GLubyte GLubyte GLubyte GLubyte w
Definition: glew.h:1858
walker(const t_string_base &string)
Definition: tstring.cpp:61
t_string & operator=(const t_string &)
Default implementation, but defined out-of-line for efficiency reasons.
Definition: tstring.cpp:485
size_t hash_value() const
Definition: tstring.cpp:53
static lg::log_domain log_config("config")
GLuint res
Definition: glew.h:9258
const route_iterator begin_
Definition: move.cpp:291
~t_string()
Default implementation, but defined out-of-line for efficiency reasons.
Definition: tstring.cpp:461
const std::string & value() const
Definition: tstring.hpp:99
GLuint const GLchar * name
Definition: glew.h:1782
GLsizeiptr size
Definition: glew.h:1649
variant string_
Definition: formula.cpp:219
#define ERR_CF
Definition: tstring.cpp:39
Standard logging facilities (interface).
t_string_base & operator+=(const t_string_base &)
Definition: tstring.cpp:331
bool operator==(const t_string_base &) const
Definition: tstring.cpp:413
#define LOG_CF
Definition: tstring.cpp:38
bool translatable_
Definition: tstring.hpp:107
GLsizei const GLcharARB ** string
Definition: glew.h:4503
std::ostream & operator<<(std::ostream &stream, const t_string_base &string)
Definition: tstring.cpp:511
static t_string_base from_serialized(const std::string &string)
Definition: tstring.cpp:219