37 #include "translation_files.h" 40 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY 41 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "i18n" 43 #define MAX_LANGUAGE_SIZE 16 45 static const unsigned char qm_magic[16] = {0x3c, 0xb8, 0x64, 0x18, 0xca, 0xef, 0x9c, 0x95, 0xcd, 0x21, 0x1c, 0xbf, 0x60, 0xa1, 0xbd, 0xdd};
47 static std::map<std::string,std::string> i18n_entries;
52 #define i18n_log(x) ((void)0) 68 language = language.substr(0, language.find(
"."));
69 language = language.substr(0, language.find(
"@"));
72 for (
char c: language)
73 if (!strchr(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-.@", c))
76 std::transform(language.begin(), language.end(), language.begin(), tolower);
79 i18n_log(
"Language from LANG/LC_ALL suspiciously long, defaulting to en");
85 static uint32_t be32(
const unsigned char *data)
87 return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
94 uint32_t code = (data[0] << 8) | data[1];
97 if (code >= 0xd800 && code <= 0xdbfff && len >= 2) {
98 uint32_t next = (data[0] << 8) | data[1];
99 if (next >= 0xdc00 && next <= 0xdfff) {
100 code = (code << 10) + next - 0x35dfc00;
108 else if (code <= 0x7ff) {
109 s += 0xc0 | (code >> 6);
110 s += 0x80 | (code & 0x3f);
112 else if (code <= 0xffff) {
113 s += 0xe0 | (code >> 12);
114 s += 0x80 | ((code >> 6) & 0x3f);
115 s += 0x80 | (code & 0x3f);
118 s += 0xf0 | (code >> 18);
119 s += 0x80 | ((code >> 12) & 0x3f);
120 s += 0x80 | ((code >> 6) & 0x3f);
121 s += 0x80 | (code & 0x3f);
136 const unsigned char *data;
139 unsigned char chunk_type;
146 i18n_log(
"i18n_set_language(" << directory <<
"," << base <<
")");
147 if (!directory || !base)
150 if (language.empty())
152 filename =
std::string(directory) +
"/" + base +
"_" + language +
".qm";
153 i18n_log(
"Loading translations for language " << language);
155 boost::system::error_code ignored_ec;
156 if (boost::filesystem::exists(filename, ignored_ec)) {
158 i18n_log(
"Failed to load translations file: " << filename);
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(),
'_');
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)) {
173 i18n_log(
"Failed to load translations file: " << filename);
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);
190 data = (
const unsigned char*)contents.c_str();
191 datalen = contents.size();
193 i18n_log(
"Translations file size: " << datalen);
220 if (datalen <
sizeof(qm_magic) || memcmp(data, qm_magic,
sizeof(qm_magic))) {
221 i18n_log(
"Bad translations file format: " << filename);
224 idx +=
sizeof(qm_magic);
226 while (idx < datalen) {
227 if (idx + 5 > datalen) {
228 i18n_log(
"Bad translations file format: " << filename);
231 chunk_type = data[idx++];
232 chunk_size = be32(data+idx);
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);
241 switch (chunk_type) {
243 i18n_log(
"Found offsets at " << idx);
246 num_messages = chunk_size / 8;
249 i18n_log(
"Found messages at " << idx);
253 i18n_log(
"Found unsupported chunk type: " << chunk_type);
265 i18n_log(
"No messages chunk found");
269 for (
uint32_t m = 0; m < num_messages; ++m) {
270 be32(data+offsets_idx+m*8);
271 idx = be32(data+offsets_idx+m*8+4);
274 if (idx > datalen || idx + 1 > datalen) {
275 i18n_log(
"Bad translations file format: " << filename);
280 if (idx + 5 > datalen) {
281 i18n_log(
"Bad translations file format: " << filename);
284 chunk_type = data[idx++];
286 if (chunk_type == 0x01) {
294 chunk_size = be32(data+idx);
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);
301 switch (chunk_type) {
303 translation = utf16(data+idx, chunk_size);
304 i18n_log(
"Found translation: " << translation);
307 source = utf8(data+idx, chunk_size);
311 context = utf8(data+idx, chunk_size);
326 std::map<std::string,std::string>::const_iterator i = i18n_entries.find(
key);
327 if (i == i18n_entries.end())
329 return (*i).second.c_str();
const CharType(& source)[N]
#define MAX_LANGUAGE_SIZE
std::string i18n_get_language()
bool load_file_to_string(const std::string &path_to_file, std::string &target_str, size_t max_size=1000000000)
const char * i18n_translate(const char *s, const std::string &context)
int i18n_set_language(const char *directory, const char *base, std::string language)
std::unique_ptr< void, terminate > context
Unique ZMQ context handle, calls zmq_term on destruction.