1 /**************************************************************************/
2 /*  Klavaro - a flexible touch typing tutor                               */
3 /*  Copyright (C) from 2005 until 2008 Felipe Castro                      */
4 /*  Copyright (C) from 2009 until 2019 The Free Software Foundation       */
5 /*                                                                        */
6 /*  This program is free software, licensed under the terms of the GNU    */
7 /*  General Public License as published by the Free Software Foundation,  */
8 /*  either version 3 of the License, or (at your option) any later        */
9 /*  version. You should have received a copy of the GNU General Public    */
10 /*  License along with this program. If not,                              */
11 /*  see <https://www.gnu.org/licenses/>.                                  */
12 /**************************************************************************/
13 
14 /*
15  * Set of functions to deal with internationalization (translation).
16  */
17 #include <stdio.h>
18 #include <string.h>
19 #include <locale.h>
20 #include <glib.h>
21 #include <glib/gstdio.h>
22 #include <gtk/gtk.h>
23 
24 #include "auxiliar.h"
25 #include "main.h"
26 #include "callbacks.h"
27 #include "keyboard.h"
28 #include "velocity.h"
29 #include "fluidness.h"
30 #include "translation.h"
31 
32 /**********************************************************************
33  * Variables
34  */
35 static Lang_Name_Code *lang;
36 static gint lang_num = 0;
37 
38 /**********************************************************************
39  * Get country name from its "ISO code" (eo and xx are just languages...)
40  */
41 const gchar *
trans_code_to_country(gchar * code)42 trans_code_to_country (gchar *code)
43 {
44 #define COUNTRY_N 50
45 	gsize i;
46 	gchar *dummy = NULL;
47 	static gchar map[COUNTRY_N][3][64] = {
48 		{"xx","Esperantio"},
49 		{"ad","Andorra"},
50 		{"ar","العالم العربي"},
51 		{"be","België"},
52 		{"bg","България"},
53  		{"bo","ཧི་མ་ལ་ཡ།"},
54 		{"br","Brasil"},
55 		{"ca","Canada"},
56 		{"ch","Schweiz / Suisse"},
57 		/* 10 */
58 		{"cn","中华人民共和国"},
59 		{"cz","Česká republika"},
60 		{"dk","Danmark"},
61 		{"de","Deutschland"},
62 		{"eo","Esperantujo"},
63 		{"es","España"},
64 		{"eu","Euskal Herria"},
65 		{"fi","Suomi"},
66 		{"fr","France"},
67 		{"gr","Ελλάδα"},
68 		/* 20 */
69 		{"il","ישראל"},
70 		{"hr","Hrvatska"},
71 		{"hu","Magyarország"},
72 		{"in","India"},
73 		{"it","Italia"},
74 		{"jp","日本 (Nippon)"},
75 		{"kk","Қазақстан"},
76 		{"kr","대한민국"}, /* Korea */
77 		{"no","Norge"},
78 		{"pl","Polska"},
79 		/* 30 */
80 		{"pk","پاکستان"},
81 		{"pt","Portugal"},
82 		{"rs","Србија"}, /* Serbia */
83 		{"ru","Россия"},
84 		{"se","Sverige"},
85 		{"si","Slovenija"},
86 		{"sk","Slovensko"},
87 		{"tr","Türkiye"},
88 		{"ua","Україна"},
89 		{"uk","United Kingdom"},
90 		/* 40 */
91 		{"us","USA"},
92 		{"",""},
93 		{"",""},
94 		{"",""},
95 		{"",""},
96 		{"",""},
97 		{"",""},
98 		{"",""},
99 		{"",""},
100 		{"",""},
101 		/* 50 */
102 		{"",""},
103 	};
104 
105 	for (i = 0; i < COUNTRY_N; i++)
106 		if (g_str_equal (code, map[i][0]))
107 			return (map[i][1]);
108 	dummy = g_strdup_printf ("(%s)", code);
109 	return (dummy);
110 }
111 
112 /**********************************************************************
113  * Get a 'reazonable' value for keyboard, that is, QWERTY... :-(
114  */
115 gchar *
trans_get_default_keyboard()116 trans_get_default_keyboard ()
117 {
118 	gint i;
119 	gchar *tmp;
120 
121 	if (lang_num == 0)
122 	{
123 		g_warning ("Internal error: trying to use language data not initialized!");
124 		return (NULL);
125 	}
126 
127 	tmp = main_preferences_get_string ("interface", "language");
128 	for (i = 0; i < lang_num; i++)
129 		if (g_str_equal (lang[i].code, tmp))
130 		{
131 			g_free (tmp);
132 			return (lang[i].kbd);
133 		}
134 
135 	g_free (tmp);
136 	return (NULL);
137 }
138 
139 /**********************************************************************
140  * Initialize 'lang', 'code' and 'kbd' accordingly to 'language_set',
141  * which was defined in "languages.h".
142  */
143 void
trans_init_lang_name_code()144 trans_init_lang_name_code ()
145 {
146 	static gboolean init = FALSE;
147 	gint i;
148 	gchar *tmp;
149 	gchar **temp_lang = NULL;
150 	const gchar languages_set[] = LANG_SET;
151 
152 	if (init)
153 	{
154 		g_warning ("Not initializing again the language data");
155 		return;
156 	}
157 	init = TRUE;
158 
159 	/* Number of configured languages
160 	 */
161 	temp_lang = g_strsplit (languages_set, "\n", -1);
162 	i = 0;
163 	while (temp_lang[i] != NULL)
164 		i++;
165 	g_assert (i > 0);
166 	lang = g_new (Lang_Name_Code, i);
167 	lang_num = i;
168 
169 	for (i = 0; i < lang_num; i++)
170 	{
171 		gchar *end;
172 		gchar *begin;
173 
174 		tmp = g_strdup (temp_lang[i]);
175 		/* Initialize 'lang'
176 		 */
177 		end = strchr (tmp, '(');
178 		if (end)
179 		       	end -= ( ((end - tmp) > 1) ? 1 : 0 );
180 		else
181 			g_error ("Internal lang error: found nothing like '(LL...'");
182 		lang[i].name = g_strndup (tmp, end - tmp);
183 
184 		/* Initialize 'code'
185 		 */
186 		begin = strchr (end, '(') + 1;
187 		end = strchr (begin, ')');
188 		if (end == NULL)
189 			g_error ("Internal lang error: found nothing like '(LL)'");
190 		lang[i].code = g_strndup (begin, end - begin);
191 
192 		/* Initialize 'cd'
193 		 */
194 		if (lang[i].code[0] == 'C')
195 			strcpy (lang[i].cd, "en");
196 		else
197 			strncpy (lang[i].cd, lang[i].code, 2);
198 		lang[i].cd[2] = '\0';
199 
200 		/* Initialize 'kbd'
201 		 */
202 		begin = strchr (end, '[') + 1;
203 		end = strchr (begin, ']');
204 		if (end == NULL)
205 			g_error ("Internal lang error: found nothing like '[yy_zz]'");
206 		lang[i].kbd = g_strndup (begin, end - begin);
207 
208 		//g_printf ("%s : %s : %s : %s\n", lang[i].name, lang[i].code, lang[i].cd, lang[i].kbd);
209 	}
210 	g_strfreev (temp_lang);
211 }
212 
213 gchar *
trans_get_code(gint i)214 trans_get_code (gint i)
215 {
216 	if (i >= lang_num || i < 0)
217 		return (NULL);
218 	return (lang[i].cd);
219 }
220 
221 gboolean
trans_lang_is_available(gchar * langcode)222 trans_lang_is_available (gchar * langcode)
223 {
224 	gint i;
225 
226 	for (i = 0; i < lang_num; i++)
227 		if (g_str_equal (lang[i].code, langcode))
228 			break;
229 	return (i == lang_num ? FALSE : TRUE);
230 }
231 
232 /**********************************************************************
233  * Define if we may put a stop mark at the end of "phrases".
234  */
235 gboolean
trans_lang_has_stopmark()236 trans_lang_has_stopmark ()
237 {
238 	gboolean stopmark;
239 	gchar *hlp;
240 
241 	hlp = main_preferences_get_string ("interface", "language");
242 	stopmark = g_str_has_prefix (hlp, "ur") ||
243 		   g_str_has_prefix (hlp, "ar") ||
244 		   g_str_has_prefix (hlp, "bn") ||
245  		   g_str_has_prefix (hlp, "bo") ||
246 		   g_str_has_prefix (hlp, "pa");
247 	g_free (hlp);
248 
249 	return (!stopmark);
250 }
251 
252 /**********************************************************************
253  * Private auxiliar function
254  */
255 static gboolean
trans_lang_get_similar(gchar * test)256 trans_lang_get_similar (gchar * test)
257 {
258 	gint i;
259 	gchar aux_code_2[3];
260 
261 	/* Prefer C over en_GB for English variants other than en_GB. (Debian patch 02) */
262 	if (g_str_has_prefix (test, "en"))
263 	{
264 		g_free (test);
265 		test = g_strdup ("C");
266 		return (TRUE);
267 	}
268 
269 	if (g_str_equal (test, "C"))
270 		return TRUE;
271 
272 	strncpy (aux_code_2, test, 2);
273 	aux_code_2[2] = '\0';
274 
275 	for (i = 0; i < lang_num; i++)
276 	{
277 		if (strstr (lang[i].code, aux_code_2))
278 		{
279 			g_free (test);
280 			test = g_strdup (lang[i].code);
281 			break;
282 		}
283 	}
284 	if (i == lang_num && g_str_has_prefix (test, "en"))
285 	{
286 		g_free (test);
287 		test = g_strdup ("C");
288 		return (TRUE);
289 	}
290 	return (i == lang_num ? FALSE : TRUE);
291 }
292 
293 /**********************************************************************
294  * Get the current locale and change it if necessary
295  */
296 void
trans_init_language_env()297 trans_init_language_env ()
298 {
299 	gchar *tmp_code;
300 	gboolean lang_ok;
301 	gint i;
302 
303 	/*
304 	 * If the language is already set in preferences, just use it
305 	 */
306 	lang_ok = FALSE;
307 	if (main_preferences_exist ("interface", "language"))
308 	{
309 		lang_ok = TRUE;
310 		tmp_code = main_preferences_get_string ("interface", "language");
311 		if (trans_lang_is_available (tmp_code) == FALSE)
312 		{
313 			tmp_code[2] = '\0';
314 			if (trans_lang_is_available (tmp_code) == FALSE)
315 			{
316 				lang_ok = FALSE;
317 				main_preferences_remove ("interface", "language");
318 			}
319 			else
320 				main_preferences_set_string ("interface", "language", tmp_code);
321 		}
322 	}
323 
324 	if (lang_ok == FALSE)
325 	{
326 		/*
327 		 * Read the current locale
328 		 */
329 #ifdef G_OS_UNIX
330 		i = 0;
331 		while ((tmp_code = g_strdup (g_get_language_names ()[i])))
332 		{
333 			if (tmp_code[0] == 'C')
334 			{
335 				lang_ok = (i == 0 ? TRUE : FALSE);
336 				break;
337 			}
338 			lang_ok = trans_lang_is_available (tmp_code);
339 			if (lang_ok == TRUE)
340 				break;
341 			g_free (tmp_code);
342 			lang_ok = FALSE;
343 			i++;
344 		}
345 		if (lang_ok == FALSE)
346 		{
347 			i = 0;
348 			while ((tmp_code = g_strdup (g_get_language_names ()[i])))
349 			{
350 				if (tmp_code[0] == 'C')
351 				{
352 					lang_ok = (i == 0 ? TRUE : FALSE);
353 					break;
354 				}
355 				lang_ok = trans_lang_get_similar (tmp_code);
356 				if (lang_ok == TRUE)
357 					break;
358 				g_free (tmp_code);
359 				lang_ok = FALSE;
360 				i++;
361 			}
362 		}
363 #else
364 		tmp_code = g_win32_getlocale ();
365 		lang_ok = trans_lang_is_available (tmp_code);
366 		if (lang_ok == FALSE)
367 			lang_ok = trans_lang_get_similar (tmp_code);
368 #endif
369 	}
370 	if (tmp_code == NULL)
371 		tmp_code = g_strdup ("xx");
372 
373 	/* If even a similar is not available...
374 	 */
375 	if (lang_ok == FALSE)
376 	{
377 		g_message ("as your locale (%s) isn't available, "
378 			       "we are using \"C\"", tmp_code);
379 		g_free (tmp_code);
380 		tmp_code = g_strdup ("C");
381 	}
382 
383 #ifdef G_OS_WIN32
384 	g_setenv ("LANGUAGE", tmp_code, TRUE);
385 #endif
386 	main_preferences_set_string ("interface", "language", tmp_code);
387 	g_free (tmp_code);
388 }
389 
390 /**********************************************************************
391  * Inserts the list of available languages in the 'combo_language'.
392  */
393 void
trans_set_combo_language()394 trans_set_combo_language ()
395 {
396 	static gboolean recur = FALSE;
397 	gint i;
398 	gint i_env;
399 	gchar *tmp_code;
400 	gchar *langcode;
401 	GtkComboBox *cmb;
402 
403 	callbacks_shield_set (TRUE);
404 
405 	if (recur)
406 		cmb = GTK_COMBO_BOX (get_wg ("combobox_top10_language"));
407 	else
408 		cmb = GTK_COMBO_BOX (get_wg ("combobox_language"));
409 
410 	tmp_code = main_preferences_get_string ("interface", "language");
411 	if (tmp_code == NULL || g_str_equal ("en_US", tmp_code))
412 	{
413 		g_message ("Using \"C\" as language code.");
414 		main_preferences_set_string ("interface", "language", "C");
415 		if (tmp_code)
416 			g_free (tmp_code);
417 		tmp_code = g_strdup ("C");
418 	}
419 	gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (cmb), 0);
420 	for (i = 0, i_env = -1; i < lang_num; i++)
421 	{
422 		langcode = g_strdup_printf ("%s (%s)", lang[i].name, lang[i].code);
423 		gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (cmb), langcode);
424 		if (g_str_equal (lang[i].code, tmp_code))
425 			i_env = i;
426 		else if (g_str_has_prefix (tmp_code, lang[i].code))
427 		{
428 			i_env = i;
429 			main_preferences_set_string ("interface", "language", lang[i].code);
430 		}
431 		g_free (langcode);
432 	}
433 	if (i_env == -1)
434 	{
435 		g_warning ("set_combo_language() ==> the locale \"%s\" is not available!", tmp_code);
436 		g_message ("Using \"C\" as language code.");
437 		g_free (tmp_code);
438 		tmp_code = g_strdup ("C");
439 		main_preferences_set_string ("interface", "language", "C");
440 		gtk_combo_box_set_active (cmb, 8);
441 	}
442 	else
443 		gtk_combo_box_set_active (cmb, i_env);
444 
445 	callbacks_shield_set (FALSE);
446 
447 	if (! recur)
448 	{
449 		recur = TRUE;
450 		trans_set_combo_language ();
451 		return;
452 	}
453 	recur = FALSE;
454 
455 	if (g_str_has_prefix (tmp_code, "en") || g_str_equal (tmp_code, "C"))
456 		gtk_widget_show (get_wg ("checkbutton_speech"));
457 	else
458 		gtk_widget_hide (get_wg ("checkbutton_speech"));
459 
460 	g_free (tmp_code);
461 }
462 
463 /**********************************************************************
464  * Get the current language name, mapped from the preference's key code
465  */
466 gchar *
trans_get_current_language()467 trans_get_current_language ()
468 {
469 	gchar *tmp_code;
470 	gint i;
471 
472 	if (lang_num == 0)
473 	{
474 		g_warning ("Internal error: trying to use language data not initialized!");
475 		return (NULL);
476 	}
477 
478 	tmp_code = main_preferences_get_string ("interface", "language");
479 	for (i = 0; i < lang_num; i++)
480 		if (g_str_equal (lang[i].code, tmp_code))
481 		{
482 			g_free (tmp_code);
483 			return (lang[i].name);
484 		}
485 	g_free (tmp_code);
486 	return ("??");
487 }
488 
489 /**********************************************************************
490  * Update the current language used accordingly to that selected in the
491  * 'combo_language'
492  */
493 void
trans_change_language(gchar * language)494 trans_change_language (gchar *language)
495 {
496 	gint i;
497 	gchar *tmp_code;
498 
499 	/* Keep decreasing order of scanning, for not missing "English UK", for instance. */
500 	for (i = lang_num-1; i >= 0; i--)
501 		if (g_str_has_prefix (language, lang[i].name))
502 			break;
503 
504 	if (i == lang_num)
505 	{
506 		g_warning ("change_language() --> couldn't find the language: %s", language);
507 		return;
508 	}
509 
510 	main_preferences_set_string ("interface", "language", lang[i].code);
511 
512 	velo_reset_dict ();
513 	fluid_reset_paragraph ();
514 
515 	/* Check if the interface language is the same of the selected in the combo,
516 	 * so that it may be spoken
517 	 */
518 	if (lang[i].code[0] == 'C')
519 		tmp_code = g_strdup ("en");
520 	else
521 		tmp_code = g_strdup (lang[i].code);
522 	if (tmp_code[0] == _("en")[0] && tmp_code[1] == _("en")[1])
523 		gtk_widget_show (get_wg ("checkbutton_speech"));
524 	else
525 		gtk_widget_hide (get_wg ("checkbutton_speech"));
526 	g_free (tmp_code);
527 }
528 
529 /**********************************************************************
530  * Find a file whose language prefix is similar to the current one
531  */
532 FILE *
trans_lang_get_similar_file(const gchar * file_end)533 trans_lang_get_similar_file (const gchar * file_end)
534 {
535 	gint i;
536 	gchar *tmp_code;
537 	gchar *tmp_path = NULL;
538 	FILE *fh = NULL;
539 
540 	tmp_code = main_preferences_get_string ("interface", "language");
541 	for (i = 0; i < lang_num && fh == NULL; i++)
542 	{
543 		if (g_str_equal (lang[i].code, tmp_code))
544 			continue;
545 		if (lang[i].code[0] == tmp_code[0] && lang[i].code[1] == tmp_code[1])
546 		{
547 			g_free (tmp_path);
548 			tmp_path =
549 				g_strconcat (main_path_data (), G_DIR_SEPARATOR_S, lang[i].code, file_end, NULL);
550 			fh = (FILE *) g_fopen (tmp_path, "r");
551 		}
552 	}
553 	g_free (tmp_code);
554 	g_free (tmp_path);
555 	i--;
556 	if (fh)
557 		g_message ("applying similar file: %s%s", lang[i].code, file_end);
558 	return (fh);
559 }
560 
561 /**********************************************************************
562  * Find a file whose language prefix is similar to the current one, returnig
563  * its name
564  */
565 gchar *
trans_lang_get_similar_file_name(const gchar * file_end)566 trans_lang_get_similar_file_name (const gchar * file_end)
567 {
568 	gint i;
569 	gchar *tmp_code;
570 	gchar *tmp_path = NULL;
571 
572 	tmp_code = main_preferences_get_string ("interface", "language");
573 	for (i = 0; i < lang_num; i++)
574 	{
575 		if (g_str_equal (lang[i].code, tmp_code))
576 			continue;
577 		if (lang[i].code[0] == tmp_code[0] && lang[i].code[1] == tmp_code[1])
578 		{
579 			g_free (tmp_path);
580 			tmp_path =
581 				g_strconcat (main_path_data (), G_DIR_SEPARATOR_S, lang[i].code, file_end, NULL);
582 			if (g_file_test (tmp_path, G_FILE_TEST_IS_REGULAR))
583 				break;
584 		}
585 	}
586 	if (tmp_path == NULL)
587 		tmp_path = g_strconcat (main_path_data (), G_DIR_SEPARATOR_S, "C", file_end, NULL);
588 	g_free (tmp_code);
589 	return (tmp_path);
590 }
591 
592 /**********************************************************************
593  * Reads the text of a file to be presented at some dialog,
594  * accordingly to the environment language.
595  * 'text': new buffer where the text is copied into.
596  * 'file_end': how the file name ends
597  */
598 
599 gchar *
trans_read_text(const gchar * file_end)600 trans_read_text (const gchar * file_end)
601 {
602 	gchar *buf;
603 	gchar *tmp_name = NULL;
604 	gchar *tmp_path = NULL;
605 	gchar *tmp_code;
606 	FILE *fh;
607 
608 	gchar *basic1 = _(
609 "The basic course focuses on having you read the characters presented to you on screen "
610 "and typing the corresponding keys. Remember to keep your hands correctly oriented "
611 "on the home row of the keyboard at all times (see introduction on main menu).");
612 	gchar *basic2 = _(
613 "The key set used in each series will be shown in the above message line. "
614 "The [Space], [Shift] and [Enter] keys may not show up there but are used very often.");
615 	gchar *basic3 = _(
616 "The message line below follows and echoes your key presses. "
617 "If required, it changes and displays instructions for actions required from you.");
618 
619 	gchar *adapt1 = _(
620 "Here you may practice and improve your memorization of all keys. "
621 "There will be sentences presented with nonsense words which mix some numbers and symbols.");
622 	gchar *adapt2 = _(
623 "In order to keep the lesson contents language and keyboard independent, "
624 "accented letter combinations will probably not appear. For real word sentences, "
625 "please use the fourth option of the main menu (about fluidness).");
626 	gchar *adapt3 = _(
627 "After each exercise there will be a brief statistics panel "
628 "reviewing your performance along with some relevant comments.");
629 
630 	gchar *velo1 = _(
631 "This exercise is very similar to the second one, for adaptability. "
632 "The difference is that here you'll type real words.");
633 	gchar *velo2 = _(
634 "The default language is the actual one of the interface. "
635 "But you may select any other texts with words you would like to use. "
636 "Press the 'Other' option above and add files containing those texts.");
637 	gchar *velo3 = _(
638 "With this exercise the focus is on speed. "
639 "So, you are supposed to type really fast and I will only flatter you when you deserve it!");
640 
641 	gchar *fluid1 = _(
642 "We will now use complete sentences and paragraphs which make logical sense. "
643 "This may distract you while you type if you try to understand what you are entering. "
644 "The previous exercises were aimed at getting you to type without interpreting and analyzing the content.");
645 	gchar *fluid2 = _(
646 "We do not mean to imply that the typists must behave like a robot, without understanding what they type. "
647 "We do aim to develop the skill of typing, making it an automatic reflex akin to the acts of walking, talking, etc. "
648 "After reaching this goal, the act of typing will become automatic and require little concentration. "
649 "Then you will be able to pay attention to the real meaning of the text.");
650 	gchar *fluid3 = _("These exercises are longer. Each exercise consists of three paragraphs and "
651 "the emphasis is placed on correctness and rhythm, with a minimum speed requirement. "
652 "Here you will be required to use the backspace key to correct any mistakes. "
653 "In other words, only input without error will be accepted.");
654 
655 	if (g_str_equal (file_end, "_basic_intro.txt"))
656 		return (g_strdup_printf ("%s\n%s\n%s", basic1, basic2, basic3));
657 
658 	if (g_str_equal (file_end, "_adapt_intro.txt"))
659 		return (g_strdup_printf ("%s\n%s\n%s", adapt1, adapt2, adapt3));
660 
661 	if (g_str_equal (file_end, "_velo_intro.txt"))
662 		return (g_strdup_printf ("%s\n%s\n%s", velo1, velo2, velo3));
663 
664 	if (g_str_equal (file_end, "_fluid_intro.txt"))
665 		return (g_strdup_printf ("%s\n%s\n%s", fluid1, fluid2, fluid3));
666 
667 	/* Use text files
668 	 */
669 	tmp_code = main_preferences_get_string ("interface", "language");
670 	tmp_name = g_strconcat (tmp_code, file_end, NULL);
671 
672 	/* Try at HOME
673 	 */
674 	tmp_path = g_build_filename (main_path_user (), tmp_name, NULL);
675 	fh = (FILE *) g_fopen (tmp_path, "r");
676 
677 	/* Try at PACKAGE_DATA
678 	 */
679 	if (fh == NULL)
680 	{
681 		g_free (tmp_path);
682 		tmp_path = g_build_filename (main_path_data (), tmp_name, NULL);
683 		fh = (FILE *) g_fopen (tmp_path, "r");
684 	}
685 
686 	/*
687 	 * Try other "flavors" of the same language
688 	 */
689 	if (fh == NULL && strlen (tmp_code) > 1)
690 		fh = trans_lang_get_similar_file (file_end);
691 
692 	/*
693 	 * Default to C
694 	 */
695 	if (fh == NULL && ! g_str_equal (tmp_code, "C"))
696 	{
697 		g_message ("trans_read_text() --> couldn't open the data file: %s\n"
698 			 " So, we have to apply the default one: C%s", tmp_name, file_end);
699 		main_preferences_set_string ("interface", "language", "C");
700 		buf = trans_read_text (file_end);
701 		main_preferences_set_string ("interface", "language", tmp_code);
702 		g_free (tmp_code);
703 		g_free (tmp_path);
704 		g_free (tmp_name);
705 		return buf;
706 	}
707 
708 	if (fh == NULL)
709 		g_error ("trans_read_text() --> couldn't open the data file:\n %s", tmp_name);
710 
711 	g_free (tmp_code);
712 	g_free (tmp_path);
713 	g_free (tmp_name);
714 
715 	/*
716 	 * Process the file
717 	 */
718 	gsize bufsize = 16;
719 	gsize pos = 0;
720 	buf = g_malloc (bufsize);
721 	while (1)
722 	{
723 		gsize max = bufsize - pos - 1; // -1 for terminating zero
724 		gsize ct = fread (buf + pos, 1, max, fh);
725 		if (ct == 0)
726 			break;
727 		pos += ct;
728 		if (ct == max)
729 		{
730 			bufsize *= 2;
731 			buf = g_realloc (buf, bufsize);
732 		}
733 	}
734 	buf[pos] = 0;
735 	fclose (fh);
736 	return buf;
737 }
738