/*****************************************************************************/ /* Klavaro - a flexible touch typing tutor */ /* Copyright (C) 2005, 2006, 2007, 2008 Felipe Castro */ /* Copyright (C) 2009, 2010, 2011 Free Software Foundation */ /* */ /* This program is free software, licensed under the terms of the GNU */ /* General Public License as published by the Free Software Foundation, */ /* either version 3 of the License, or (at your option) any later version. */ /* You should have received a copy of the GNU General Public License */ /* along with this program. If not, see . */ /*****************************************************************************/ /* * Set of functions to deal with internationalization (translation). */ #include #include #include #include #include #include #include "auxiliar.h" #include "main.h" #include "callbacks.h" #include "keyboard.h" #include "velocity.h" #include "fluidness.h" #include "translation.h" /********************************************************************** * Variables */ static Lang_Name_Code *lang; static gint lang_num = 0; /********************************************************************** * Get country name from its "ISO code" (some are just languages...) */ const gchar * trans_code_to_country (gchar *code) { # define COUNTRY_N 40 gsize i; gchar *dummy = NULL; static gchar map[COUNTRY_N][2][64] = { {"xx","Esperantio"}, {"ar","العالم العربي"},// {"be","België"},// {"br","Brasil"},// {"ca","Canada"},// {"cz","Česká republika"},// {"dk","Danmark"},// {"de","Deutschland"},// {"eo","Esperantujo"},// {"es","España"},// {"eu","Euskal Herria"}, {"fr","France"},// {"gr","Ελλάδα"},// {"il","ישראל"},// {"hu","Magyarország"},// {"it","Italia"},// {"jp","日本 (Nippon)"},// {"kk","Қазақстан"},// {"no","Norge"},// {"pl","Polska"},// {"pk","پاکستان"},// {"pt","Portugal"},// {"ru","Россия"},// {"se","Sverige"},// {"tr","Türkiye"},// {"ua","Україна"},// {"uk","United Kingdom"},// {"us","USA"}, // {"cn","中华人民共和国"}, {"",""}, {"",""}, {"",""}, {"",""}, {"",""}, {"",""}, {"",""}, {"",""}, {"",""}, {"",""}, {"",""} }; for (i = 0; i < COUNTRY_N; i++) if (g_str_equal (code, map[i][0])) return (map[i][1]); dummy = g_strdup_printf ("(%s)", code); return (dummy); } /********************************************************************** * Get a 'reazonable' value for keyboard, that is, QWERTY... :-( */ gchar * trans_get_default_keyboard () { gint i; gchar *tmp; if (lang_num == 0) { g_warning ("Internal error: trying to use language data not initialized!"); return (NULL); } tmp = main_preferences_get_string ("interface", "language"); for (i = 0; i < lang_num; i++) if (g_str_equal (lang[i].code, tmp)) { g_free (tmp); return (lang[i].kbd); } g_free (tmp); return (NULL); } /********************************************************************** * Initialize 'lang', 'code' and 'kbd' accordingly to 'language_set', * which was defined in "languages.h". */ void trans_init_lang_name_code () { static gboolean init = FALSE; gint i; gchar *tmp; gchar **temp_lang = NULL; const gchar languages_set[] = LANG_SET; if (init) { g_warning ("Not initializing again the language data"); return; } init = TRUE; /* Number of configured languages */ temp_lang = g_strsplit (languages_set, "\n", -1); i = 0; while (temp_lang[i] != NULL) i++; g_assert (i > 0); lang = g_new (Lang_Name_Code, i); lang_num = i; for (i = 0; i < lang_num; i++) { gchar *end; gchar *begin; tmp = g_strdup (temp_lang[i]); /* Initialize 'lang' */ end = strchr (tmp, '('); if (end) end -= ( ((end - tmp) > 1) ? 1 : 0 ); else g_error ("Internal lang error: found nothing like '(LL...'"); lang[i].name = g_strndup (tmp, end - tmp); /* Initialize 'code' */ begin = strchr (end, '(') + 1; end = strchr (begin, ')'); if (end == NULL) g_error ("Internal lang error: found nothing like '(LL)'"); lang[i].code = g_strndup (begin, end - begin); /* Initialize 'cd' */ if (lang[i].code[0] == 'C') strcpy (lang[i].cd, "en"); else strncpy (lang[i].cd, lang[i].code, 2); lang[i].cd[2] = '\0'; /* Initialize 'kbd' */ begin = strchr (end, '[') + 1; end = strchr (begin, ']'); if (end == NULL) g_error ("Internal lang error: found nothing like '[yy_zz]'"); lang[i].kbd = g_strndup (begin, end - begin); //g_printf ("%s : %s : %s : %s\n", lang[i].name, lang[i].code, lang[i].cd, lang[i].kbd); } g_strfreev (temp_lang); } gchar * trans_get_code (gint i) { if (i >= lang_num || i < 0) return (NULL); return (lang[i].cd); } gboolean trans_lang_is_available (gchar * langcode) { gint i; for (i = 0; i < lang_num; i++) if (g_str_equal (lang[i].code, langcode)) break; return (i == lang_num ? FALSE : TRUE); } /********************************************************************** * Define if we may put a stop mark at the end of "phrases". */ gboolean trans_lang_has_stopmark () { gboolean stopmark; gchar *hlp; hlp = main_preferences_get_string ("interface", "language"); stopmark = g_str_has_prefix (hlp, "ur") || g_str_has_prefix (hlp, "bn"); g_free (hlp); return (!stopmark); } /********************************************************************** * Private auxiliar function */ static gboolean trans_lang_get_similar (gchar * test) { gint i; gchar aux_code_2[3]; if (g_str_equal (test, "C")) return TRUE; strncpy (aux_code_2, test, 2); aux_code_2[2] = '\0'; for (i = 0; i < lang_num; i++) { if (strstr (lang[i].code, aux_code_2)) { g_free (test); test = g_strdup (lang[i].code); break; } } if (i == lang_num && g_str_has_prefix (test, "en")) { g_free (test); test = g_strdup ("C"); return (TRUE); } return (i == lang_num ? FALSE : TRUE); } /********************************************************************** * Get the current locale and change it if necessary */ void trans_init_language_env () { gchar *tmp_code; gboolean lang_ok; gint i; /* * If the language is already set in preferences, just use it */ lang_ok = FALSE; if (main_preferences_exist ("interface", "language")) { lang_ok = TRUE; tmp_code = main_preferences_get_string ("interface", "language"); if (trans_lang_is_available (tmp_code) == FALSE) { tmp_code[2] = '\0'; if (trans_lang_is_available (tmp_code) == FALSE) { lang_ok = FALSE; main_preferences_remove ("interface", "language"); } else main_preferences_set_string ("interface", "language", tmp_code); } } if (lang_ok == FALSE) { /* * Read the current locale */ i = 0; #ifdef G_OS_UNIX while ((tmp_code = g_strdup (g_get_language_names ()[i]))) { if (tmp_code[0] == 'C') { lang_ok = (i == 0 ? TRUE : FALSE); break; } lang_ok = trans_lang_is_available (tmp_code); if (lang_ok == TRUE) break; g_free (tmp_code); lang_ok = FALSE; i++; } if (lang_ok == FALSE) { i = 0; while ((tmp_code = g_strdup (g_get_language_names ()[i]))) { if (tmp_code[0] == 'C') { lang_ok = (i == 0 ? TRUE : FALSE); break; } lang_ok = trans_lang_get_similar (tmp_code); if (lang_ok == TRUE) break; g_free (tmp_code); lang_ok = FALSE; i++; } } #else tmp_code = g_win32_getlocale (); lang_ok = trans_lang_is_available (tmp_code); if (lang_ok == FALSE) lang_ok = trans_lang_get_similar (tmp_code); #endif } if (tmp_code == NULL) tmp_code = g_strdup ("xx"); /* If even a similar is not available... */ if (lang_ok == FALSE) { g_free (tmp_code); //tmp_code = g_strdup ("eo"); tmp_code = g_strdup ("C"); g_message ("as your prefered locales aren't available, " "we are using this neutral one: \"%s\"", tmp_code); } main_preferences_set_string ("interface", "language", tmp_code); g_free (tmp_code); } /********************************************************************** * Inserts the list of available languages in the 'combo_language'. */ void trans_set_combo_language () { static gboolean recur = FALSE; gint i; gint i_env; gchar *tmp_code; gchar *langcode; GtkComboBox *cmb; callbacks_shield_set (TRUE); if (recur) cmb = GTK_COMBO_BOX (get_wg ("combobox_top10_language")); else cmb = GTK_COMBO_BOX (get_wg ("combobox_language")); tmp_code = main_preferences_get_string ("interface", "language"); if (tmp_code == NULL) { g_message ("NULL language code detected. So, using \"C\"."); main_preferences_set_string ("interface", "language", "C"); tmp_code = g_strdup ("C"); } gtk_combo_box_remove_text (cmb, 0); for (i = 0, i_env = -1; i < lang_num; i++) { langcode = g_strdup_printf ("%s (%s)", lang[i].name, lang[i].code); gtk_combo_box_append_text (cmb, langcode); if (g_str_equal (lang[i].code, tmp_code)) i_env = i; else if (g_str_has_prefix (tmp_code, lang[i].code)) { i_env = i; main_preferences_set_string ("interface", "language", lang[i].code); } g_free (langcode); } if (i_env == -1) g_error ("set_combo_language() ==> the locale \"%s\" is not available!", tmp_code); else gtk_combo_box_set_active (cmb, i_env); callbacks_shield_set (FALSE); if (! recur) { recur = TRUE; trans_set_combo_language (); return; } recur = FALSE; if (tmp_code[0] == 'C') { g_free (tmp_code); tmp_code = g_strdup ("en"); } if (tmp_code[0] == _("en")[0] && tmp_code[1] == _("en")[1]) gtk_widget_show (get_wg ("checkbutton_speech")); else gtk_widget_hide (get_wg ("checkbutton_speech")); g_free (tmp_code); } /********************************************************************** * Get the current language name, mapped from the preference's key code */ gchar * trans_get_current_language () { gchar *tmp_code; gint i; if (lang_num == 0) { g_warning ("Internal error: trying to use language data not initialized!"); return (NULL); } tmp_code = main_preferences_get_string ("interface", "language"); for (i = 0; i < lang_num; i++) if (g_str_equal (lang[i].code, tmp_code)) { g_free (tmp_code); return (lang[i].name); } g_free (tmp_code); return ("??"); } /********************************************************************** * Update the current language used accordingly to that selected in the * 'combo_language' */ void trans_change_language (gchar *language) { gint i; gchar *tmp_code; for (i = 0; i < lang_num; i++) if (g_str_has_prefix (language, lang[i].name)) break; if (i == lang_num) { g_warning ("change_language() --> couldn't find the language: %s", language); return; } main_preferences_set_string ("interface", "language", lang[i].code); velo_reset_dict (); fluid_reset_paragraph (); /* Check if the interface language is the same of the selected in the combo, * so that it may be spoken */ if (lang[i].code[0] == 'C') tmp_code = g_strdup ("en"); else tmp_code = g_strdup (lang[i].code); if (tmp_code[0] == _("en")[0] && tmp_code[1] == _("en")[1]) gtk_widget_show (get_wg ("checkbutton_speech")); else gtk_widget_hide (get_wg ("checkbutton_speech")); g_free (tmp_code); } /********************************************************************** * Find a file whose language prefix is similar to the current one */ FILE * trans_lang_get_similar_file (const gchar * file_end) { gint i; gchar *tmp_code; gchar *tmp_path = NULL; FILE *fh = NULL; tmp_code = main_preferences_get_string ("interface", "language"); for (i = 0; i < lang_num && fh == NULL; i++) { if (g_str_equal (lang[i].code, tmp_code)) continue; if (lang[i].code[0] == tmp_code[0] && lang[i].code[1] == tmp_code[1]) { g_free (tmp_path); tmp_path = g_strconcat (main_path_data (), G_DIR_SEPARATOR_S, lang[i].code, file_end, NULL); fh = (FILE *) g_fopen (tmp_path, "r"); } } g_free (tmp_code); g_free (tmp_path); i--; if (fh) g_message ("applying similar file: %s%s", lang[i].code, file_end); return (fh); } /********************************************************************** * Find a file whose language prefix is similar to the current one, returnig * its name */ gchar * trans_lang_get_similar_file_name (const gchar * file_end) { gint i; gchar *tmp_code; gchar *tmp_path = NULL; tmp_code = main_preferences_get_string ("interface", "language"); for (i = 0; i < lang_num; i++) { if (g_str_equal (lang[i].code, tmp_code)) continue; if (lang[i].code[0] == tmp_code[0] && lang[i].code[1] == tmp_code[1]) { g_free (tmp_path); tmp_path = g_strconcat (main_path_data (), G_DIR_SEPARATOR_S, lang[i].code, file_end, NULL); if (g_file_test (tmp_path, G_FILE_TEST_IS_REGULAR)) break; } } if (tmp_path == NULL) tmp_path = g_strconcat (main_path_data (), G_DIR_SEPARATOR_S, "C", file_end, NULL); g_free (tmp_code); return (tmp_path); } /********************************************************************** * Reads the text of a file to be presented at some dialog, * accordingly to the environment language. * 'text': new buffer where the text is copied into. * 'file_end': how the file name ends */ gchar * trans_read_text (const gchar * file_end) { gchar *buf; gchar *tmp_name = NULL; gchar *tmp_path = NULL; gchar *tmp_code; FILE *fh; gchar *basic1 = _( "The basic course focuses on having you read the characters presented to you on screen " "and typing the corresponding keys. Remember to keep your hands correctly oriented " "on the home row of the keyboard at all times (see introduction on main menu)."); gchar *basic2 = _( "The key set used in each series will be shown in the above message line. " "The [Space], [Shift] and [Enter] keys may not show up there but are used very often."); gchar *basic3 = _( "The message line below follows and echoes your key presses. " "If required, it changes and displays instructions for actions required from you."); gchar *adapt1 = _( "Here you may practice and improve your memorization of all keys. " "There will be sentences presented with nonsense words which mix some numbers and symbols."); gchar *adapt2 = _( "In order to keep the lesson contents language and keyboard independent, " "accented letter combinations will probably not appear. For real word sentences, " "please use the fourth option of the main menu (about fluidness)."); gchar *adapt3 = _( "After each exercise there will be a brief statistics panel " "reviewing your performance along with some relevant comments."); gchar *velo1 = _( "This exercise is very similar to the second one, for adaptability. " "The difference is that here you'll type real words."); gchar *velo2 = _( "The default language is the actual one of the interface. " "But you may select any other texts with words you would like to use. " "Press the 'Other' option above and add files containing those texts."); gchar *velo3 = _( "With this exercise the focus is on speed. " "So, you are supposed to type really fast and I will only flatter you when you deserve it!"); gchar *fluid1 = _( "We will now use complete sentences and paragraphs which make logical sense. " "This may distract you while you type if you try to understand what you are entering. " "The previous exercises were aimed at getting you to type without interpreting and analyzing the content."); gchar *fluid2 = _( "We do not mean to imply that the typists must behave like a robot, without understanding what they type. " "We do aim to develop the skill of typing, making it an automatic reflex akin to the acts of walking, talking, etc. " "After reaching this goal, the act of typing will become automatic and require little concentration. " "Then you will be able to pay attention to the real meaning of the text."); /* English typo: minimun -> minimum */ gchar *fluid3 = _("These exercises are longer. Each exercise consists of three paragraphs and " "the emphasis is placed on correctness and rhythm, with a minimum speed requirement. " "Here you will be required to use the backspace key to correct any mistakes. " "In other words, only input without error will be accepted."); if (g_str_equal (file_end, "_basic_intro.txt")) return (g_strdup_printf ("%s\n%s\n%s", basic1, basic2, basic3)); if (g_str_equal (file_end, "_adapt_intro.txt")) return (g_strdup_printf ("%s\n%s\n%s", adapt1, adapt2, adapt3)); if (g_str_equal (file_end, "_velo_intro.txt")) return (g_strdup_printf ("%s\n%s\n%s", velo1, velo2, velo3)); if (g_str_equal (file_end, "_fluid_intro.txt")) return (g_strdup_printf ("%s\n%s\n%s", fluid1, fluid2, fluid3)); /* Use text files */ tmp_code = main_preferences_get_string ("interface", "language"); tmp_name = g_strconcat (tmp_code, file_end, NULL); /* Try at HOME */ tmp_path = g_build_filename (main_path_user (), tmp_name, NULL); fh = (FILE *) g_fopen (tmp_path, "r"); /* Try at PACKAGE_DATA */ if (fh == NULL) { g_free (tmp_path); tmp_path = g_build_filename (main_path_data (), tmp_name, NULL); fh = (FILE *) g_fopen (tmp_path, "r"); } /* * Try other "flavors" of the same language */ if (fh == NULL && strlen (tmp_code) > 1) fh = trans_lang_get_similar_file (file_end); /* * Default to C */ if (fh == NULL && ! g_str_equal (tmp_code, "C")) { g_message ("trans_read_text() --> couldn't open the data file: %s\n" " So, we have to apply the default one: C%s", tmp_name, file_end); main_preferences_set_string ("interface", "language", "C"); buf = trans_read_text (file_end); main_preferences_set_string ("interface", "language", tmp_code); g_free (tmp_code); g_free (tmp_path); g_free (tmp_name); return buf; } if (fh == NULL) g_error ("trans_read_text() --> couldn't open the data file:\n %s", tmp_name); g_free (tmp_code); g_free (tmp_path); g_free (tmp_name); /* * Process the file */ gsize bufsize = 16; gsize pos = 0; buf = g_malloc (bufsize); while (1) { gsize max = bufsize - pos - 1; // -1 for terminating zero gsize ct = fread (buf + pos, 1, max, fh); if (ct == 0) break; pos += ct; if (ct == max) { bufsize *= 2; buf = g_realloc (buf, bufsize); } } buf[pos] = 0; return buf; }