37 #include "translation_files.h"
39 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
40 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "i18n"
42 #define MAX_LANGUAGE_SIZE 16
44 static const unsigned char qm_magic[16] = {0x3c, 0xb8, 0x64, 0x18, 0xca, 0xef, 0x9c, 0x95, 0xcd, 0x21, 0x1c, 0xbf, 0x60, 0xa1, 0xbd, 0xdd};
46 static std::map<std::string,std::string> i18n_entries;
51 #define i18n_log(x) ((void)0)
67 language = language.substr(0, language.find(
"."));
68 language = language.substr(0, language.find(
"@"));
71 for (
char c: language)
72 if (!strchr(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-.@", c))
75 std::transform(language.begin(), language.end(), language.begin(), tolower);
78 i18n_log(
"Language from LANG/LC_ALL suspiciously long, defaulting to en");
84 static uint32_t be32(
const unsigned char *data)
86 return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
93 uint32_t code = (data[0] << 8) | data[1];
96 if (code >= 0xd800 && code <= 0xdbfff && len >= 2) {
97 uint32_t next = (data[0] << 8) | data[1];
98 if (next >= 0xdc00 && next <= 0xdfff) {
99 code = (code << 10) + next - 0x35dfc00;
107 else if (code <= 0x7ff) {
108 s += 0xc0 | (code >> 6);
109 s += 0x80 | (code & 0x3f);
111 else if (code <= 0xffff) {
112 s += 0xe0 | (code >> 12);
113 s += 0x80 | ((code >> 6) & 0x3f);
114 s += 0x80 | (code & 0x3f);
117 s += 0xf0 | (code >> 18);
118 s += 0x80 | ((code >> 12) & 0x3f);
119 s += 0x80 | ((code >> 6) & 0x3f);
120 s += 0x80 | (code & 0x3f);
135 const unsigned char *data;
138 unsigned char chunk_type;
145 i18n_log(
"i18n_set_language(" << directory <<
"," << base <<
")");
146 if (!directory || !base)
149 if (language.empty())
151 filename =
std::string(directory) +
"/" + base +
"_" + language +
".qm";
152 i18n_log(
"Loading translations for language " << language);
154 boost::system::error_code ignored_ec;
155 if (boost::filesystem::exists(filename, ignored_ec)) {
157 i18n_log(
"Failed to load translations file: " << filename);
161 i18n_log(
"Translations file not found: " << filename);
162 filename =
std::string(base) +
"_" + language +
".qm";
163 if (!find_embedded_file(filename, contents)) {
164 i18n_log(
"Embedded translations file not found: " << filename);
165 const char *underscore = strchr(language.c_str(),
'_');
168 filename =
std::string(directory) +
"/" + base +
"_" + fallback_language +
".qm";
169 i18n_log(
"Loading translations for language " << fallback_language);
170 if (boost::filesystem::exists(filename, ignored_ec)) {
172 i18n_log(
"Failed to load translations file: " << filename);
176 i18n_log(
"Translations file not found: " << filename);
177 filename =
std::string(base) +
"_" + fallback_language +
".qm";
178 if (!find_embedded_file(filename, contents)) {
179 i18n_log(
"Embedded translations file not found: " << filename);
189 data = (
const unsigned char*)contents.c_str();
190 datalen = contents.size();
192 i18n_log(
"Translations file size: " << datalen);
219 if (datalen <
sizeof(qm_magic) || memcmp(data, qm_magic,
sizeof(qm_magic))) {
220 i18n_log(
"Bad translations file format: " << filename);
223 idx +=
sizeof(qm_magic);
225 while (idx < datalen) {
226 if (idx + 5 > datalen) {
227 i18n_log(
"Bad translations file format: " << filename);
230 chunk_type = data[idx++];
231 chunk_size = be32(data+idx);
234 i18n_log(
"Found " << chunk_type <<
" of " << chunk_size <<
" bytes");
235 if (chunk_size >= datalen || idx > datalen - chunk_size) {
236 i18n_log(
"Bad translations file format: " << filename);
240 switch (chunk_type) {
242 i18n_log(
"Found offsets at " << idx);
245 num_messages = chunk_size / 8;
248 i18n_log(
"Found messages at " << idx);
252 i18n_log(
"Found unsupported chunk type: " << chunk_type);
264 i18n_log(
"No messages chunk found");
268 for (
uint32_t m = 0; m < num_messages; ++m) {
269 be32(data+offsets_idx+m*8);
270 idx = be32(data+offsets_idx+m*8+4);
273 if (idx > datalen || idx + 1 > datalen) {
274 i18n_log(
"Bad translations file format: " << filename);
279 if (idx + 5 > datalen) {
280 i18n_log(
"Bad translations file format: " << filename);
283 chunk_type = data[idx++];
285 if (chunk_type == 0x01) {
293 chunk_size = be32(data+idx);
295 i18n_log(
"Found " << chunk_type <<
" of " << chunk_size <<
" bytes");
296 if (chunk_size >= datalen || idx > datalen - chunk_size) {
297 i18n_log(
"Bad translations file format: " << filename);
300 switch (chunk_type) {
302 translation = utf16(data+idx, chunk_size);
303 i18n_log(
"Found translation: " << translation);
306 source = utf8(data+idx, chunk_size);
310 context = utf8(data+idx, chunk_size);
325 std::map<std::string,std::string>::const_iterator i = i18n_entries.find(
key);
326 if (i == i18n_entries.end())
328 return (*i).second.c_str();
#define MAX_LANGUAGE_SIZE
const char * i18n_translate(const char *s, const std::string &context)
std::string i18n_get_language()
int i18n_set_language(const char *directory, const char *base, std::string language)
bool load_file_to_string(const std::string &path_to_file, std::string &target_str, size_t max_size=1000000000)
std::unique_ptr< void, terminate > context
Unique ZMQ context handle, calls zmq_term on destruction.
const CharType(& source)[N]