New "dynamic" i18n menu.
authorBastien Montagne <montagne29@wanadoo.fr>
Mon, 22 Oct 2012 14:04:40 +0000 (14:04 +0000)
committerBastien Montagne <montagne29@wanadoo.fr>
Mon, 22 Oct 2012 14:04:40 +0000 (14:04 +0000)
Now both UI translation menu (in userprefs) and isocodes are defined in a text file (release/datafiles/locale/languages), parsed at lunchtime. This way:
* No more need to edit Blender code each time we want to add an new language or reorganize the existing menu;
* Translators can easily add a new language for testing, by just editing the text file, so no more need to ask to change Blender code and wait for a new build to see your new translation work in Blender!

Remaining todo:
* Commit i18n py tools
* Update wiki doc!

source/blender/blenfont/BLF_translation.h
source/blender/blenfont/CMakeLists.txt
source/blender/blenfont/SConscript
source/blender/blenfont/intern/blf_lang.c
source/blender/makesrna/intern/rna_define.c
source/blender/makesrna/intern/rna_userdef.c
source/blender/windowmanager/intern/wm_init_exit.c
source/gameengine/GamePlayer/ghost/GPG_ghost.cpp

index 278c45dac52f3f0802f9de015d20d1280bc48593..b01ce93cb65948b6d340f9699250851cd4204908 100644 (file)
@@ -49,9 +49,13 @@ const char *BLF_pgettext(const char *context, const char *message);
 
 /* Search the path directory to the locale files, this try all
  * the case for Linux, Win and Mac.
+ * Also dynamically builds locales and locales' menu from "languages" text file.
  */
 void BLF_lang_init(void);
 
+/* Free languages and locales_menu arrays created by BLF_lang_init. */
+void BLF_lang_free(void);
+
 /* Set the current locale. */
 void BLF_lang_set(const char *);
 /* Get the current locale (short code, e.g. es_ES). */
@@ -60,6 +64,9 @@ const char *BLF_lang_get(void);
 /* Set the current encoding name. */
 void BLF_lang_encoding(const char *str);
 
+/* Get EnumPropertyItem's for translations menu. */
+struct EnumPropertyItem *BLF_RNA_lang_enum_properties(void);
+
 /* translation */
 int BLF_translate_iface(void);
 int BLF_translate_tooltips(void);
index da60710a137f92b5746aa2b12276309884b53e71..022dfd282b07aa7f962e75d17a6321c8d369ef65 100644 (file)
@@ -25,8 +25,10 @@ set(INC
        .
        ../blenkernel
        ../blenlib
+       ../blenloader
        ../editors/include
        ../makesdna
+       ../makesrna
        ../imbuf
        ../../../intern/guardedalloc
 )
index d3c7b1c2fcc7e49166cde07964950246092e7c13..075da58b116a6737f2343042c1649e37afaecc43 100644 (file)
@@ -4,7 +4,8 @@ Import ('env')
 
 sources = env.Glob('intern/*.c')
 
-incs = '. intern  #/intern/guardedalloc ../blenkernel ../blenlib ../makesdna ../imbuf ../editors/include'
+incs = '. intern  #/intern/guardedalloc ../blenkernel ../blenlib ../blenloader'
+incs += ' ../makesdna ../makesrna ../imbuf ../editors/include'
 incs += ' #/extern/glew/include'
 incs += ' ' + env['BF_FREETYPE_INC']
 incs += ' ' + env['BF_GETTEXT_INC']
index e46e6be399e1b845c9af9e72869e734ac666023f..5b9ec60fc11fc32359e26249a660a2e650a58936 100644 (file)
@@ -35,7 +35,6 @@
 #include "BKE_global.h"
 
 #include "BLF_api.h"
-
 #include "BLF_translation.h" /* own include */
 
 #ifdef WITH_INTERNATIONAL
 
 #include "DNA_userdef_types.h"
 
-#include "DNA_listBase.h"
-#include "DNA_vec_types.h"
+#include "RNA_types.h"
 
 #include "MEM_guardedalloc.h"
 
-#include "BLI_linklist.h" /* linknode */
 #include "BLI_string.h"
 #include "BLI_utildefines.h"
 #include "BLI_path_util.h"
+#include "BLI_fileops.h"
+#include "BLI_linklist.h"
+#include "BLI_string.h"
 
 #define SYSTEM_ENCODING_DEFAULT "UTF-8"
 #define FONT_SIZE_DEFAULT 12
@@ -64,50 +64,120 @@ static char global_messagepath[1024];
 static char global_language[32];
 static char global_encoding_name[32];
 
-/* Map from the rna_userdef.c:rna_def_userdef_system(BlenderRNA *brna):language_items */
-static const char *locales[] = {
-       "", "",
-       "english", "en_US",
-       "japanese", "ja_JP",
-       "dutch", "nl_NL",
-       "italian", "it_IT",
-       "german", "de_DE",
-       "finnish", "fi_FI",
-       "swedish", "sv_SE",
-       "french", "fr_FR",
-       "spanish", "es",
-       "catalan", "ca_AD",
-       "czech", "cs_CZ",
-       "portuguese", "pt_PT",
-#if defined(_WIN32) && !defined(FREE_WINDOWS)
-       "Chinese (Simplified)_China.1252", "zh_CN",
-       "Chinese (Traditional)_China.1252", "zh_TW",
-#else
-       "chs", "zh_CN",
-       "cht", "zh_TW",
-#endif
-       "russian", "ru_RU",
-       "croatian", "hr_HR",
-       "serbian", "sr_RS",
-       "ukrainian", "uk_UA",
-       "polish", "pl_PL",
-       "romanian", "ro_RO",
-       "arabic", "ar_EG",
-       "bulgarian", "bg_BG",
-       "greek", "el_GR",
-       "korean", "ko_KR",
-       "nepali", "ne_NP",
-       "persian", "fa_IR",
-       "indonesian", "id_ID",
-       "serbian (latin)", "sr_RS@latin",
-       "kyrgyz", "ky_KG",
-       "turkish", "tr_TR",
-       "hungarian", "hu_HU",
-       "portuguese-brazilian", "pt_BR",
-       "hebrew", "he_IL",
-       "estonian", "et_EE",
-       "esperanto", "eo", /* No country code for esperanto! ;) */
-};
+static const char **locales = NULL;
+static int num_locales = 0;
+static EnumPropertyItem *locales_menu = NULL;
+static int num_locales_menu = 0;
+
+#define ULANGUAGE ((U.language >= 0 && U.language < num_locales) ? U.language : 0)
+#define LOCALE(_id) (locales ? locales[_id] : "")
+
+static void free_locales(void)
+{
+       if (locales) {
+               int idx = num_locales_menu - 1; /* Last item does not need to be freed! */
+               while (idx--) {
+                       printf("freeing %s\n", locales_menu[idx].identifier);
+                       MEM_freeN((void*)locales_menu[idx].identifier);
+                       printf("freeing %s\n", locales_menu[idx].name);
+                       MEM_freeN((void*)locales_menu[idx].name);
+                       printf("freeing %s\n", locales_menu[idx].description);
+                       MEM_freeN((void*)locales_menu[idx].description); /* Also frees locales's relevant value! */
+               }
+               MEM_freeN(locales);
+               MEM_freeN(locales_menu);
+       }
+       num_locales = num_locales_menu = 0;
+}
+
+static void fill_locales(void)
+{
+       char *languages_path = BLI_get_folder(BLENDER_DATAFILES, "locale");
+       LinkNode *lines = NULL, *line;
+       char *str;
+       int idx = 0;
+
+       free_locales();
+
+       BLI_join_dirfile(languages_path, FILE_MAX, languages_path, "languages");
+       line = lines = BLI_file_read_as_lines(languages_path);
+
+       /* This whole "parsing" code is a bit weak, in that it expects strictly formated input file...
+        * Should not be a problem, though, as this file is script-generated! */
+
+       /* First loop to find highest locale ID */
+       while (line) {
+               int t;
+               str = (char*) line->link;
+               if (str[0] == '#' || str[0] == '\0') {
+                       line = line->next;
+                       continue; /* Comment or void... */
+               }
+               printf("%s\n", str);
+               t = atoi(str);
+               if (t >= num_locales)
+                       num_locales = t + 1;
+               num_locales_menu++;
+               line = line->next;
+       }
+       num_locales_menu++; /* The "closing" void item... */
+       printf("num_locales_menu: %d\n", num_locales_menu);
+
+       /* And now, buil locales and locale_menu! */
+       locales = MEM_callocN(num_locales * sizeof(char*), __func__);
+       locales_menu = MEM_callocN(num_locales_menu * sizeof(EnumPropertyItem), __func__);
+       line = lines;
+       while (line) {
+               int id;
+               char *loc, *sep1, *sep2;
+
+               str = (char*) line->link;
+               if (str[0] == '#' || str[0] == '\0') {
+                       line = line->next;
+                       continue;
+               }
+
+               id = atoi(str);
+               printf("%s\n", str);
+               sep1 = strchr(str, ':');
+               if (sep1) {
+                       sep1++;
+                       sep2 = strchr(sep1, ':');
+                       if (sep2) {
+
+                                       locales_menu[idx].value = id;
+                                       locales_menu[idx].icon = 0;
+                                       locales_menu[idx].name = BLI_strdupn(sep1, sep2 - sep1);
+                                       locales_menu[idx].identifier = loc = BLI_strdup(sep2 + 1);
+                                       if (id == 0) {
+                                               /* The DEFAULT item... */
+                                               if (BLI_strnlen(loc, 2))
+                                                       locales[id] = locales_menu[idx].description = BLI_strdup("");
+                                               /* Menu "label", not to be stored in locales! */
+                                               else
+                                                       locales_menu[idx].description = BLI_strdup("");
+                                       }
+                                       else
+                                               locales[id] = locales_menu[idx].description = BLI_strdup(loc);
+                                       idx++;
+                               
+                       }
+               }
+
+               line = line->next;
+       }
+
+       /* Add closing item to menu! */
+       locales_menu[idx].value = locales_menu[idx].icon = 0;
+       locales_menu[idx].identifier = locales_menu[idx].name = locales_menu[idx].description = "";
+
+       BLI_file_free_lines(lines);
+}
+
+EnumPropertyItem *BLF_RNA_lang_enum_properties(void)
+{
+       return locales_menu;
+}
 
 void BLF_lang_init(void)
 {
@@ -117,6 +187,7 @@ void BLF_lang_init(void)
 
        if (messagepath) {
                BLI_strncpy(global_messagepath, messagepath, sizeof(global_messagepath));
+               fill_locales();
        }
        else {
                printf("%s: 'locale' data path for translations not found, continuing\n", __func__);
@@ -124,6 +195,11 @@ void BLF_lang_init(void)
        }
 }
 
+void BLF_lang_free(void)
+{
+       free_locales();
+}
+
 /* Get LANG/LANGUAGE environment variable. */
 static void get_language_variable(const char *varname, char *var, const size_t maxlen)
 {
@@ -166,7 +242,8 @@ void BLF_lang_set(const char *str)
 {
        char *locreturn;
        const char *short_locale;
-       int ok = 1;
+       int ok = TRUE;
+       int ulang = ULANGUAGE;
 
        if ((U.transopts & USER_DOTRANSLATE) == 0)
                return;
@@ -174,14 +251,14 @@ void BLF_lang_set(const char *str)
        if (str)
                short_locale = str;
        else
-               short_locale = locales[2 * U.language + 1];
+               short_locale = LOCALE(ulang);
 
 #if defined(_WIN32) && !defined(FREE_WINDOWS)
        {
                if (short_locale) {
                        char *envStr;
 
-                       if (U.language == 0) /* Use system setting. */
+                       if (ulang) /* Use system setting. */
                                envStr = BLI_sprintfN("LANG=%s", getenv("LANG"));
                        else
                                envStr = BLI_sprintfN("LANG=%s", short_locale);
@@ -196,7 +273,7 @@ void BLF_lang_set(const char *str)
                        if (G.debug & G_DEBUG)
                                printf("Could not change locale to %s\n", short_locale);
 
-                       ok = 0;
+                       ok = FALSE;
                }
        }
 #else
@@ -265,7 +342,7 @@ void BLF_lang_set(const char *str)
 
                        locreturn = setlocale(LC_ALL, "");
 
-                       ok = 0;
+                       ok = FALSE;
                }
        }
 #endif
@@ -284,7 +361,8 @@ void BLF_lang_set(const char *str)
 
 const char *BLF_lang_get(void)
 {
-       return locales[2 * U.language + 1];
+       int uilang = ULANGUAGE;
+       return LOCALE(uilang);
 }
 
 void BLF_lang_encoding(const char *str)
@@ -293,6 +371,9 @@ void BLF_lang_encoding(const char *str)
        /* bind_textdomain_codeset(TEXT_DOMAIN_NAME, encoding_name); */
 }
 
+#undef LOCALE
+#undef ULANGUAGE
+
 #else /* ! WITH_INTERNATIONAL */
 
 void BLF_lang_init(void)
@@ -300,6 +381,11 @@ void BLF_lang_init(void)
        return;
 }
 
+void BLF_lang_free(void)
+{
+       return;
+}
+
 void BLF_lang_encoding(const char *str)
 {
        (void)str;
index d4e0ba68d9d7a846870a6566606c40f9d634c4dd..8ad0cf963aa628abb81233c6a9d0be7e9c71c70c 100644 (file)
@@ -42,6 +42,8 @@
 #include "BLI_listbase.h"
 #include "BLI_ghash.h"
 
+#include "BLF_translation.h"
+
 #include "RNA_define.h"
 
 #include "rna_internal.h"
index baa892ecdc8cd41d7cc584515bb319e29b7d788e..24f856e67d445caf31c0452d5e05890e9766b66d 100644 (file)
@@ -410,6 +410,13 @@ static EnumPropertyItem *rna_userdef_compute_device_itemf(bContext *UNUSED(C), P
 }
 #endif
 
+static EnumPropertyItem *rna_lang_enum_properties_itemf(bContext *UNUSED(C), PointerRNA *UNUSED(ptr),
+                                                        PropertyRNA *UNUSED(prop), int *free)
+{
+       *free = 0; /* These items are handled by BLF code! */
+       return BLF_RNA_lang_enum_properties();
+}
+
 #else
 
 static void rna_def_userdef_theme_ui_font_style(BlenderRNA *brna)
@@ -2983,6 +2990,7 @@ static void rna_def_userdef_system(BlenderRNA *brna)
                {0, NULL, 0, NULL, NULL}
        };
        
+#if 0
        /* hardcoded here, could become dynamic somehow */
        /* locale according to http://www.roseindia.net/tutorials/I18N/locales-list.shtml */
        /* if you edit here, please also edit the source/blender/blenfont/intern/blf_lang.c 's locales */
@@ -3032,6 +3040,12 @@ static void rna_def_userdef_system(BlenderRNA *brna)
                {30, "TURKISH", 0, "Turkish (Türkçe)", "tr_TR"},
                { 0, NULL, 0, NULL, NULL}
        };
+#else
+       static EnumPropertyItem language_items[] = {
+               { 0, "DEFAULT", 0, "Default (Default)", ""},
+               { 0, NULL, 0, NULL, NULL}
+       };
+#endif
 
 #ifdef WITH_CYCLES
        static EnumPropertyItem compute_device_items[] = {
@@ -3074,6 +3088,7 @@ static void rna_def_userdef_system(BlenderRNA *brna)
 
        prop = RNA_def_property(srna, "language", PROP_ENUM, PROP_NONE);
        RNA_def_property_enum_items(prop, language_items);
+       RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_lang_enum_properties_itemf");
        RNA_def_property_ui_text(prop, "Language", "Language used for translation");
        RNA_def_property_update(prop, NC_WINDOW, "rna_userdef_language_update");
 
index 658a13a8918dc1d0f256eced025d77fc611a5a56..89fa92b7373c43ad3bea4425af32b1a599b5ab3d 100644 (file)
@@ -423,6 +423,7 @@ void WM_exit_ext(bContext *C, const short do_python)
 
 #ifdef WITH_INTERNATIONAL
        BLF_free_unifont();
+       BLF_lang_free();
 #endif
        
        ANIM_keyingset_infos_exit();
index 7500a55a059fe737e85509728f0e33b3f48602fb..9006a629791d87183966d86e76be2694d81712d9 100644 (file)
@@ -1064,6 +1064,7 @@ int main(int argc, char** argv)
 
 #ifdef WITH_INTERNATIONAL
        BLF_free_unifont();
+       BLF_lang_free();
 #endif
 
        IMB_exit();