Electroneum
i18n.cpp File Reference
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <string>
#include <map>
#include "file_io_utils.h"
#include "common/i18n.h"
#include "translation_files.h"
#include <algorithm>
Include dependency graph for i18n.cpp:

Go to the source code of this file.

Macros

#define ELECTRONEUM_DEFAULT_LOG_CATEGORY   "i18n"
 
#define MAX_LANGUAGE_SIZE   16
 
#define i18n_log(x)   ((void)0)
 

Functions

std::string i18n_get_language ()
 
int i18n_set_language (const char *directory, const char *base, std::string language)
 
const char * i18n_translate (const char *s, const std::string &context)
 

Macro Definition Documentation

◆ ELECTRONEUM_DEFAULT_LOG_CATEGORY

#define ELECTRONEUM_DEFAULT_LOG_CATEGORY   "i18n"

Definition at line 41 of file i18n.cpp.

◆ i18n_log

#define i18n_log (   x)    ((void)0)

Definition at line 52 of file i18n.cpp.

◆ MAX_LANGUAGE_SIZE

#define MAX_LANGUAGE_SIZE   16

Definition at line 43 of file i18n.cpp.

Function Documentation

◆ i18n_get_language()

std::string i18n_get_language ( )

Definition at line 54 of file i18n.cpp.

55 {
56  const char *e;
57 
58  e = getenv("LANG");
59  i18n_log("LANG=" << e);
60  if (!e || !*e) {
61  e = getenv("LC_ALL");
62  i18n_log("LC_ALL=" << e);
63  }
64  if (!e || !*e)
65  e = "en";
66 
67  std::string language = e;
68  language = language.substr(0, language.find("."));
69  language = language.substr(0, language.find("@"));
70 
71  // check valid values
72  for (char c: language)
73  if (!strchr("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-.@", c))
74  return "en";
75 
76  std::transform(language.begin(), language.end(), language.begin(), tolower);
77  if (language.size() > MAX_LANGUAGE_SIZE)
78  {
79  i18n_log("Language from LANG/LC_ALL suspiciously long, defaulting to en");
80  return "en";
81  }
82  return language;
83 }
::std::string string
Definition: gtest-port.h:1097
#define MAX_LANGUAGE_SIZE
Definition: i18n.cpp:43
#define i18n_log(x)
Definition: i18n.cpp:52
Here is the caller graph for this function:

◆ i18n_set_language()

int i18n_set_language ( const char *  directory,
const char *  base,
std::string  language 
)

Definition at line 133 of file i18n.cpp.

134 {
135  std::string filename, contents;
136  const unsigned char *data;
137  size_t datalen;
138  size_t idx;
139  unsigned char chunk_type;
140  uint32_t chunk_size;
141  uint32_t num_messages = (uint32_t)-1;
142  uint32_t messages_idx = (uint32_t)-1;
143  uint32_t offsets_idx = (uint32_t)-1;
144  std::string translation, source, context;
145 
146  i18n_log("i18n_set_language(" << directory << "," << base << ")");
147  if (!directory || !base)
148  return -1;
149 
150  if (language.empty())
151  language = i18n_get_language();
152  filename = std::string(directory) + "/" + base + "_" + language + ".qm";
153  i18n_log("Loading translations for language " << language);
154 
155  boost::system::error_code ignored_ec;
156  if (boost::filesystem::exists(filename, ignored_ec)) {
157  if (!epee::file_io_utils::load_file_to_string(filename, contents)) {
158  i18n_log("Failed to load translations file: " << filename);
159  return -1;
160  }
161  } else {
162  i18n_log("Translations file not found: " << filename);
163  filename = std::string(base) + "_" + language + ".qm";
164  if (!find_embedded_file(filename, contents)) {
165  i18n_log("Embedded translations file not found: " << filename);
166  const char *underscore = strchr(language.c_str(), '_');
167  if (underscore) {
168  std::string fallback_language = std::string(language, 0, underscore - language.c_str());
169  filename = std::string(directory) + "/" + base + "_" + fallback_language + ".qm";
170  i18n_log("Loading translations for language " << fallback_language);
171  if (boost::filesystem::exists(filename, ignored_ec)) {
172  if (!epee::file_io_utils::load_file_to_string(filename, contents)) {
173  i18n_log("Failed to load translations file: " << filename);
174  return -1;
175  }
176  } else {
177  i18n_log("Translations file not found: " << filename);
178  filename = std::string(base) + "_" + fallback_language + ".qm";
179  if (!find_embedded_file(filename, contents)) {
180  i18n_log("Embedded translations file not found: " << filename);
181  return -1;
182  }
183  }
184  } else {
185  return -1;
186  }
187  }
188  }
189 
190  data = (const unsigned char*)contents.c_str();
191  datalen = contents.size();
192  idx = 0;
193  i18n_log("Translations file size: " << datalen);
194 
195  /* Format of the QM file (AFAICT):
196  * 16 bytes magic
197  * chunk list: N instances of chunks:
198  * 1 byte: chunk type (0x42: offsets, 0x69: messages)
199  * 4 bytes: chunk length, big endian
200  * D bytes: "chunk length" bytes of data
201  *
202  * 0x42 chunk: N instances of subchunks:
203  * 1 byte: subchunk type
204  * 0x01: end, no data
205  * 0x02: unsupported
206  * 0x03: translation
207  * 4 bytes: string length, big endian
208  * N bytes: string data, UTF-16 (or UCS2-BE ?)
209  * 0x04: unsupported
210  * 0x05: obsolete, unsupported
211  * 0x06: source text
212  * 0x07: context
213  * 0x08: obsolete, unsupported
214  * other: unsupported
215  * 4 bytes: subchunk length, big endian - except for 0x01, which has none
216  * S bytes: "chunk length" bytes of data
217  * 0x69 chunk:
218  * string data indexed by the 0x42 chunk data
219  */
220  if (datalen < sizeof(qm_magic) || memcmp(data, qm_magic, sizeof(qm_magic))) {
221  i18n_log("Bad translations file format: " << filename);
222  return -1;
223  }
224  idx += sizeof(qm_magic);
225 
226  while (idx < datalen) {
227  if (idx + 5 > datalen) {
228  i18n_log("Bad translations file format: " << filename);
229  return -1;
230  }
231  chunk_type = data[idx++];
232  chunk_size = be32(data+idx);
233  idx += 4;
234 
235  i18n_log("Found " << chunk_type << " of " << chunk_size << " bytes");
236  if (chunk_size >= datalen || idx > datalen - chunk_size) {
237  i18n_log("Bad translations file format: " << filename);
238  return -1;
239  }
240 
241  switch (chunk_type) {
242  case 0x42:
243  i18n_log("Found offsets at " << idx);
244  /* two 32 bit integers, and possible padding */
245  offsets_idx = idx;
246  num_messages = chunk_size / 8;
247  break;
248  case 0x69:
249  i18n_log("Found messages at " << idx);
250  messages_idx = idx;
251  break;
252  default:
253  i18n_log("Found unsupported chunk type: " << chunk_type);
254  break;
255  }
256 
257  idx += chunk_size;
258  }
259 
260  if (offsets_idx == (uint32_t)-1) {
261  i18n_log("No offsets chunk found");
262  return -1;
263  }
264  if (messages_idx == (uint32_t)-1) {
265  i18n_log("No messages chunk found");
266  return -1;
267  }
268 
269  for (uint32_t m = 0; m < num_messages; ++m) {
270  be32(data+offsets_idx+m*8); // unused
271  idx = be32(data+offsets_idx+m*8+4);
272  idx += messages_idx;
273 
274  if (idx > datalen || idx + 1 > datalen) {
275  i18n_log("Bad translations file format: " << filename);
276  return -1;
277  }
278 
279  while (1) {
280  if (idx + 5 > datalen) {
281  i18n_log("Bad translations file format: " << filename);
282  return -1;
283  }
284  chunk_type = data[idx++];
285  chunk_size = 0;
286  if (chunk_type == 0x01) {
287  i18n_entries[context + std::string("",1) + source] = translation;
288  context = std::string();
289  source = std::string();
290  translation = std::string();
291  break;
292  }
293 
294  chunk_size = be32(data+idx);
295  idx += 4;
296  i18n_log("Found " << chunk_type << " of " << chunk_size << " bytes");
297  if (chunk_size >= datalen || idx > datalen - chunk_size) {
298  i18n_log("Bad translations file format: " << filename);
299  return -1;
300  }
301  switch (chunk_type) {
302  case 0x03: // translation, UTF-16
303  translation = utf16(data+idx, chunk_size);
304  i18n_log("Found translation: " << translation);
305  break;
306  case 0x06: // source, UTF-8
307  source = utf8(data+idx, chunk_size);
308  i18n_log("Found source: " << source);
309  break;
310  case 0x07: // context, UTF-8
311  context = utf8(data+idx, chunk_size);
312  i18n_log("Found context: " << context);
313  break;
314  }
315  idx += chunk_size;
316  }
317  }
318 
319  return 0;
320 }
const CharType(& source)[N]
Definition: pointer.h:1147
::std::string string
Definition: gtest-port.h:1097
std::string i18n_get_language()
Definition: i18n.cpp:54
bool load_file_to_string(const std::string &path_to_file, std::string &target_str, size_t max_size=1000000000)
unsigned int uint32_t
Definition: stdint.h:126
std::unique_ptr< void, terminate > context
Unique ZMQ context handle, calls zmq_term on destruction.
Definition: zmq.h:98
#define i18n_log(x)
Definition: i18n.cpp:52
Here is the call graph for this function:
Here is the caller graph for this function:

◆ i18n_translate()

const char* i18n_translate ( const char *  s,
const std::string &  context 
)

Definition at line 323 of file i18n.cpp.

324 {
325  const std::string key = context + std::string("", 1) + s;
326  std::map<std::string,std::string>::const_iterator i = i18n_entries.find(key);
327  if (i == i18n_entries.end())
328  return s;
329  return (*i).second.c_str();
330 }
::std::string string
Definition: gtest-port.h:1097
const char * key
Definition: hmac_keccak.cpp:39
std::unique_ptr< void, terminate > context
Unique ZMQ context handle, calls zmq_term on destruction.
Definition: zmq.h:98
Here is the caller graph for this function: