64 |
}; |
}; |
65 |
|
|
66 |
struct loaded_domain_s { |
struct loaded_domain_s { |
67 |
|
/* Full file name to the MO file */ |
68 |
char *file_name; |
char *file_name; |
69 |
|
/* Last modification date of the MO file */ |
70 |
time_t modify_time; |
time_t modify_time; |
71 |
|
|
72 |
char *data; |
char *data; |
73 |
int must_swap; |
int must_swap; |
74 |
DWORD nstrings; |
DWORD nstrings; |
93 |
static gettext_domain_t the_domain; |
static gettext_domain_t the_domain; |
94 |
|
|
95 |
|
|
96 |
|
/* Return TRUE if the native user language is not English |
97 |
|
and thus, gettext support is available */ |
98 |
|
static bool |
99 |
|
gettext_is_required (void) |
100 |
|
{ |
101 |
|
LANGID lang = GetUserDefaultLangID (); |
102 |
|
if (PRIMARYLANGID (lang) == LANG_ENGLISH) |
103 |
|
return false; |
104 |
|
return true; |
105 |
|
} |
106 |
|
|
107 |
|
|
108 |
static DWORD |
static DWORD |
109 |
do_swap_u32 (DWORD i) |
do_swap_u32 (DWORD i) |
110 |
{ |
{ |
135 |
} |
} |
136 |
|
|
137 |
|
|
|
/* Missing W32 functions. */ |
|
|
static char* |
|
|
w32_stpcpy (char *a, const char *b) |
|
|
{ |
|
|
while (*b) |
|
|
*a++ = *b++; |
|
|
*a = 0; |
|
|
return (char*)a; |
|
|
} |
|
|
|
|
|
|
|
138 |
static gettext_domain_t |
static gettext_domain_t |
139 |
load_domain (const char *filename) |
load_domain (const char *filename) |
140 |
{ |
{ |
167 |
size_t nb = fread (read_ptr, 1, to_read, fp); |
size_t nb = fread (read_ptr, 1, to_read, fp); |
168 |
if (nb < to_read) { |
if (nb < to_read) { |
169 |
fclose (fp); |
fclose (fp); |
170 |
free(data); |
free (data); |
171 |
return NULL; /* read error */ |
return NULL; /* read error */ |
172 |
} |
} |
173 |
read_ptr += nb; |
read_ptr += nb; |
175 |
} while (to_read > 0); |
} while (to_read > 0); |
176 |
fclose (fp); |
fclose (fp); |
177 |
|
|
178 |
/* Using the magic number we can test whether it really is a message |
/* Using the magic number we can test whether it really is a message catalog file. */ |
|
* catalog file. */ |
|
179 |
if (data->magic != MAGIC && data->magic != MAGIC_SWAPPED) { |
if (data->magic != MAGIC && data->magic != MAGIC_SWAPPED) { |
180 |
/* The magic number is wrong: not a message catalog file. */ |
/* The magic number is wrong: not a message catalog file. */ |
181 |
free (data); |
free (data); |
208 |
} |
} |
209 |
|
|
210 |
/* allocate an array to keep track of code page mappings */ |
/* allocate an array to keep track of code page mappings */ |
211 |
domain->mapped = (char *)calloc( 1, domain->nstrings ); |
domain->mapped = (char *)calloc (1, domain->nstrings); |
212 |
if (!domain->mapped) |
if (!domain->mapped) |
213 |
BUG (0); |
BUG (0); |
214 |
|
|
218 |
} |
} |
219 |
|
|
220 |
|
|
|
/* Returns a string representation of the users system language |
|
|
* if we support it, 'en' otherwise. |
|
|
*/ |
|
|
static const char* |
|
|
get_user_langid (void) |
|
|
{ |
|
|
int lang = GetUserDefaultLangID() & 511; |
|
|
|
|
|
for (int i=0; lang_list[i].id != NULL; i++) { |
|
|
if (lang == lang_list[i].langid) |
|
|
return lang_list[i].id; |
|
|
} |
|
|
return "en"; |
|
|
} |
|
|
|
|
|
|
|
221 |
/* To avoid problems that the current directory is not the |
/* To avoid problems that the current directory is not the |
222 |
* same folder the WinPT.exe is located in, we extract the |
* same folder the WinPT.exe is located in, we extract the |
223 |
* module directory. |
* module directory. |
225 |
char* |
char* |
226 |
get_module_dir (char *buf, DWORD buflen) |
get_module_dir (char *buf, DWORD buflen) |
227 |
{ |
{ |
|
int pos; |
|
|
|
|
228 |
if (buflen < 1 || !GetModuleFileName (glob_hinst, buf, buflen-1)) |
if (buflen < 1 || !GetModuleFileName (glob_hinst, buf, buflen-1)) |
229 |
return NULL; |
return NULL; |
230 |
|
|
231 |
pos = strlen (buf); |
size_t pos = strlen (buf); |
232 |
while (pos > 0 && buf[--pos] != '\\') |
while (pos > 0 && buf[--pos] != '\\') |
233 |
; |
; |
234 |
buf[pos+1] = '\0'; |
buf[pos + 1] = '\0'; |
235 |
return buf; |
return buf; |
236 |
} |
} |
237 |
|
|
238 |
|
|
239 |
/* Returns a localisation name for a specific resource (file). |
/* Returns a localisation name for a specific resource (file). |
240 |
* For instance, lang=DE and file=winpt.chm will become |
* For instance, lang=DE and file=winpt.chm will become winpt.DE.chm |
|
* winpt.DE.chm |
|
241 |
*/ |
*/ |
242 |
char* |
char* |
243 |
get_locale_name (const char *file) |
get_locale_name (const char *file) |
244 |
{ |
{ |
245 |
const char *lang = get_user_langid(); |
const char *lang = gettext_get_langid (); |
246 |
char *name, *ext; |
char buf[MAX_PATH+1]; |
|
char *p, buf[MAX_PATH+1]; |
|
247 |
|
|
248 |
p = strrchr (file, '.'); |
char *p = strrchr (file, '.'); |
249 |
if (p == NULL) { |
if (p == NULL) { |
250 |
p = new char[strlen(file)+strlen(lang)+2+1]; |
p = new char[strlen(file)+strlen(lang)+2+1]; |
251 |
sprintf (p, "%s.%s", file, lang); |
sprintf (p, "%s.%s", file, lang); |
252 |
return p; |
return p; |
253 |
} |
} |
254 |
name = substr (file, 0, p-file); |
char *name = substr (file, 0, p - file); |
255 |
ext = substr (file, p-file+1, strlen(file)); |
char *ext = substr (file, p - file + 1, strlen(file)); |
256 |
p = get_module_dir (buf, MAX_PATH); |
p = get_module_dir (buf, MAX_PATH); |
257 |
if (!p) |
if (!p) |
258 |
BUG (NULL); |
BUG (NULL); |
279 |
} |
} |
280 |
|
|
281 |
|
|
282 |
|
/* Replace and release the old domain and assign a new domain then */ |
283 |
|
static void |
284 |
|
gettext_update_domain (gettext_domain_t new_dom) |
285 |
|
{ |
286 |
|
gettext_free_current_domain (); |
287 |
|
the_domain = new_dom; |
288 |
|
} |
289 |
|
|
290 |
|
|
291 |
char* get_reg_entry_mo (void); |
char* get_reg_entry_mo (void); |
292 |
|
|
293 |
/* Fallback code which is used when no new NLS file |
/* Fallback code which is used when no new NLS file were found. |
294 |
* were found. Then we read the MODir registry key |
Then we read the MODir registry key and check if a file named |
295 |
* and check if a file named 'winpt.mo' exists in this |
'winpt.mo' exists in this directory. */ |
|
* directory. |
|
|
*/ |
|
296 |
static gettext_domain_t |
static gettext_domain_t |
297 |
load_modir_domain (void) |
load_modir_domain (void) |
298 |
{ |
{ |
|
gettext_domain_t domain = NULL; |
|
|
|
|
299 |
char *modir = get_reg_entry_mo (); |
char *modir = get_reg_entry_mo (); |
300 |
if (!modir) |
if (!modir) |
301 |
return NULL; |
return NULL; |
308 |
s += "\\"; |
s += "\\"; |
309 |
s += "winpt.mo"; |
s += "winpt.mo"; |
310 |
|
|
311 |
|
gettext_domain_t domain = NULL; |
312 |
const char *fname = s.getBuffer(); |
const char *fname = s.getBuffer(); |
313 |
if (!file_exist_check (fname)) |
if (!file_exist_check (fname)) |
314 |
domain = load_domain (fname); |
domain = load_domain (fname); |
317 |
} |
} |
318 |
|
|
319 |
|
|
320 |
int |
/* Try to load the user domain, either from |
321 |
|
the WinPT.exe directory or from the registry */ |
322 |
|
void |
323 |
gettext_set_user_domain (void) |
gettext_set_user_domain (void) |
324 |
{ |
{ |
325 |
|
if (!gettext_is_required ()) |
326 |
|
return; |
327 |
|
|
328 |
gettext_domain_t domain = NULL; |
gettext_domain_t domain = NULL; |
329 |
char *file = get_locale_name ("winpt.mo"); |
char *file = get_locale_name ("winpt.mo"); |
330 |
if (file && !file_exist_check (file)) |
if (file && !file_exist_check (file)) |
332 |
else |
else |
333 |
domain = load_modir_domain (); |
domain = load_modir_domain (); |
334 |
free_if_alloc (file); |
free_if_alloc (file); |
335 |
gettext_free_current_domain (); |
gettext_update_domain (domain); |
|
the_domain = domain; |
|
|
return 0; |
|
336 |
} |
} |
337 |
|
|
338 |
|
|
351 |
return 0; |
return 0; |
352 |
} |
} |
353 |
|
|
|
/* Set the file used for translations. Pass a NULL to disable translation. |
|
|
A new filename may be set at anytime. */ |
|
|
int |
|
|
gettext_set_file (const char *filename, const char *nls_dir) |
|
|
{ |
|
|
gettext_domain_t domain = NULL; |
|
|
|
|
|
if (filename && *filename) { |
|
|
if (filename[1] == ':' && filename[2] == '\\') |
|
|
domain = load_domain (filename); /* absolute path - use it as is */ |
|
|
else { /* relative path - append ".mo" and get dir from the environment */ |
|
|
char *buf, *dir; |
|
|
|
|
|
dir = strdup (nls_dir); |
|
|
if (!dir) |
|
|
BUG (0); |
|
|
buf= (char *)malloc (strlen (dir) + strlen (filename)+1+3+1); |
|
|
strcpy (w32_stpcpy (w32_stpcpy ( |
|
|
w32_stpcpy (buf, dir), "\\"), filename),".mo"); |
|
|
domain = load_domain(buf); |
|
|
free (buf); |
|
|
free (dir); |
|
|
} |
|
|
if (!domain) |
|
|
return -1; |
|
|
} |
|
|
|
|
|
gettext_free_current_domain (); |
|
|
the_domain = domain; |
|
|
return 0; |
|
|
} |
|
|
|
|
|
|
|
354 |
static const char* |
static const char* |
355 |
get_string (gettext_domain_t domain, DWORD idx) |
get_string (gettext_domain_t domain, DWORD idx) |
356 |
{ |
{ |
358 |
domain->trans_tab[idx].offset); |
domain->trans_tab[idx].offset); |
359 |
if (!domain->mapped[idx]) |
if (!domain->mapped[idx]) |
360 |
domain->mapped[idx] = 1; |
domain->mapped[idx] = 1; |
361 |
|
/* FIXME: all text is in UTF-8 and need to be converted to |
362 |
|
the locale charset. But the caller does not free the data and |
363 |
|
thus we will get a leak */ |
364 |
return (const char *)p; |
return (const char *)p; |
365 |
} |
} |
366 |
|
|
433 |
} |
} |
434 |
|
|
435 |
|
|
436 |
|
|
437 |
/* Map the user specific language ID to a gettext conform language string. |
/* Map the user specific language ID to a gettext conform language string. |
438 |
|
English "en" is defined as the fallback. |
439 |
Example: LANG_GERMAN -> "de" */ |
Example: LANG_GERMAN -> "de" */ |
440 |
const char* |
const char* |
441 |
gettext_get_langid (void) |
gettext_get_langid (void) |
442 |
{ |
{ |
443 |
LANGID lang = GetUserDefaultLangID (); |
LANGID lang = GetUserDefaultLangID (); |
444 |
if (PRIMARYLANGID (lang) == LANG_ENGLISH) |
|
|
return NULL; |
|
|
|
|
445 |
for (int i=0; lang_list[i].id; i++) { |
for (int i=0; lang_list[i].id; i++) { |
446 |
if (PRIMARYLANGID (lang) == lang_list[i].langid) |
if (PRIMARYLANGID (lang) == lang_list[i].langid) |
447 |
return lang_list[i].id; |
return lang_list[i].id; |
448 |
} |
} |
449 |
return NULL; |
|
450 |
|
return "en"; |
451 |
} |
} |
452 |
|
|
453 |
|
|