From cef730d969b35d7cc25006acded05c361602b319 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Sun, 20 Jan 2013 17:29:07 +0000 Subject: Python i18n API. Many thanks to Campbell and Brecht for the reviews and suggestions! This commit adds: * A new bpy.app.translations module giving some info about locales/translation stuff (current active locale, all locales currently known by blender, all translation contexts currently defined, etc.). * The ability for addons to feature translations, using the (un)register functions of above module. * Also cleans up "translate py string when storing into RNA prop" by removing "PROP_TRANSLATE" string's subtype, and adding a PROP_STRING_PY_TRANSLATE flag instead (this way it is no more exposed to python...). Addon translations work with py dictionaries: each addon features a dict {lang: {(context, message): translation, ...}, ...}, which is registered when the addon is enabled (and unregistered when disabled). Then, when a key (context, message) is not found in regular mo catalog, a cache dict for current locale is built from all registered addon translations, and key is searched in it. Note: currently addons writers have to do all the work by hand, will add something (probably extend "edit translation" addon) to automate messages extraction from addons soon(ish)! To get a look to expected behavior from addons, have a look at render_copy_settings/__init__.py and render_copy_settings/translations.py (rather stupid example currently, but...). Once we have a complete process, I'll also update relevant wiki pages. --- source/blender/blenfont/BLF_translation.h | 59 +++++++++++++++++++- source/blender/blenfont/CMakeLists.txt | 1 + source/blender/blenfont/SConscript | 2 +- source/blender/blenfont/intern/blf_lang.c | 71 ++++++++++++++++++++++-- source/blender/blenfont/intern/blf_translation.c | 12 +++- 5 files changed, 138 insertions(+), 7 deletions(-) (limited to 'source/blender/blenfont') diff --git a/source/blender/blenfont/BLF_translation.h b/source/blender/blenfont/BLF_translation.h index 0832907d1d9..59f66cae727 100644 --- a/source/blender/blenfont/BLF_translation.h +++ b/source/blender/blenfont/BLF_translation.h @@ -51,6 +51,13 @@ void BLF_lang_set(const char *); /* Get the current locale (short code, e.g. es_ES). */ const char *BLF_lang_get(void); +/* Get locale's elements (if relevant pointer is not NULL and element actually exists, e.g. if there is no variant, + * *variant and *language_variant will always be NULL). + * Non-null elements are always MEM_mallocN'ed, it's the caller's responsibility to free them. + */ +void BLF_locale_explode(const char *locale, char **language, char **country, char **variant, + char **language_country, char **language_variant); + /* Get EnumPropertyItem's for translations menu. */ struct EnumPropertyItem *BLF_RNA_lang_enum_properties(void); @@ -143,4 +150,54 @@ const char *BLF_translate_do_tooltip(const char *msgctxt, const char *msgid); #define BLF_I18NCONTEXT_ID_MOVIECLIP "MovieClip" #define BLF_I18NCONTEXT_ID_MASK "Mask" -#endif /* __BLF_TRANSLATION_H__ */ +/* Helper for bpy.app.i18n object... */ +typedef struct +{ + const char *c_id; + const char *py_id; + const char *value; +} BLF_i18n_contexts_descriptor; + +#define BLF_I18NCONTEXTS_ITEM(ctxt_id, py_id) {#ctxt_id, py_id, ctxt_id} + +#define BLF_I18NCONTEXTS_DESC { \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_DEFAULT, "default"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "operator_default"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_ACTION, "id_action"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_ARMATURE, "id_armature"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_BRUSH, "id_brush"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_CAMERA, "id_camera"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_CURVE, "id_curve"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_GPENCIL, "id_gpencil"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_GROUP, "id_group"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_ID, "id_id"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_IMAGE, "id_image"), \ + /*BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_IPO, "id_ipo"),*/ \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_SHAPEKEY, "id_shapekey"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_LAMP, "id_lamp"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_LIBRARY, "id_library"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_LATTICE, "id_lattice"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_MATERIAL, "id_material"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_METABALL, "id_metaball"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_MESH, "id_mesh"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_NODETREE, "id_nodetree"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_OBJECT, "id_object"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_PARTICLESETTINGS, "id_particlesettings"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_SCENE, "id_scene"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_SCREEN, "id_screen"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_SEQUENCE, "id_sequence"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_SPEAKER, "id_speaker"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_SOUND, "id_sound"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_TEXTURE, "id_texture"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_TEXT, "id_text"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_VFONT, "id_vfont"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_WORLD, "id_world"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_WINDOWMANAGER, "id_windowmanager"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_MOVIECLIP, "id_movieclip"), \ + BLF_I18NCONTEXTS_ITEM(BLF_I18NCONTEXT_ID_MASK, "id_mask"), \ + {NULL, NULL, NULL} \ +} + +//#undef _BLF_I18NCONTEXTS_ITEM + +#endif /* __BLF_TRANSLATION_H__ */ \ No newline at end of file diff --git a/source/blender/blenfont/CMakeLists.txt b/source/blender/blenfont/CMakeLists.txt index 90baef14a74..7bb80c34323 100644 --- a/source/blender/blenfont/CMakeLists.txt +++ b/source/blender/blenfont/CMakeLists.txt @@ -29,6 +29,7 @@ set(INC ../editors/include ../makesdna ../makesrna + ../python ../imbuf ../../../intern/guardedalloc ../../../intern/locale diff --git a/source/blender/blenfont/SConscript b/source/blender/blenfont/SConscript index a6ea724a51f..d1ef181f75c 100644 --- a/source/blender/blenfont/SConscript +++ b/source/blender/blenfont/SConscript @@ -31,7 +31,7 @@ Import ('env') sources = env.Glob('intern/*.c') incs = '. intern #/intern/guardedalloc #/intern/locale ../blenkernel ../blenlib ../blenloader' -incs += ' ../makesdna ../makesrna ../imbuf ../editors/include' +incs += ' ../makesdna ../makesrna ../python ../imbuf ../editors/include' incs += ' #/extern/glew/include' incs += ' ' + env['BF_FREETYPE_INC'] diff --git a/source/blender/blenfont/intern/blf_lang.c b/source/blender/blenfont/intern/blf_lang.c index 9086799f984..ab29fae1a58 100644 --- a/source/blender/blenfont/intern/blf_lang.c +++ b/source/blender/blenfont/intern/blf_lang.c @@ -60,7 +60,7 @@ 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] : "") +#define LOCALE(_id) (locales ? locales[(_id)] : "") static void free_locales(void) { @@ -113,7 +113,7 @@ static void fill_locales(void) } num_locales_menu++; /* The "closing" void item... */ - /* And now, buil locales and locale_menu! */ + /* And now, build locales and locale_menu! */ locales_menu = MEM_callocN(num_locales_menu * sizeof(EnumPropertyItem), __func__); line = lines; /* Do not allocate locales with zero-sized mem, as LOCALE macro uses NULL locales as invalid marker! */ @@ -234,10 +234,15 @@ void BLF_lang_set(const char *str) } } +/* Get the current locale (short code, e.g. es_ES). */ const char *BLF_lang_get(void) { - int uilang = ULANGUAGE; - return LOCALE(uilang); + const char *locale = LOCALE(ULANGUAGE); + if (locale[0] == '\0') { + /* Default locale, we have to find which one we are actually using! */ + locale = bl_locale_get(); + } + return locale; } #undef LOCALE @@ -245,6 +250,11 @@ const char *BLF_lang_get(void) #else /* ! WITH_INTERNATIONAL */ +EnumPropertyItem *BLF_RNA_lang_enum_properties(void) +{ + return NULL; +} + void BLF_lang_init(void) { return; @@ -266,3 +276,56 @@ const char *BLF_lang_get(void) } #endif /* WITH_INTERNATIONAL */ + + +/* Get locale's elements (if relevant pointer is not NULL and element actually exists, e.g. if there is no variant, + * *variant and *language_variant will always be NULL). + * Non-null elements are always MEM_mallocN'ed, it's the caller's responsibility to free them. + * NOTE: Keep that one always available, you never know, may become useful even in no-WITH_INTERNATIONAL context... + */ +void BLF_locale_explode(const char *locale, char **language, char **country, char **variant, + char **language_country, char **language_variant) +{ + char *m1, *m2, *_t = NULL; + + m1 = strchr(locale, '_'); + m2 = strchr(locale, '@'); + + if (language || language_variant) { + if (m1 || m2) { + _t = m1 ? BLI_strdupn(locale, m1 - locale) : BLI_strdupn(locale, m2 - locale); + if (language) + *language = _t; + } + else if (language) { + *language = NULL; + } + } + if (country) { + if (m1) + *country = m2 ? BLI_strdupn(m1 + 1, m2 - (m1 + 1)) : BLI_strdup(m1 + 1); + else + *country = NULL; + } + if (variant) { + if (m2) + *variant = BLI_strdup(m2 + 1); + else + *variant = NULL; + } + if (language_country) { + if (m2) + *language_country = BLI_strdupn(locale, m2 - locale); + else + *language_country = NULL; + } + if (language_variant) { + if (m2) + *language_variant = m1 ? BLI_strdupcat(_t, m2 + 1) : BLI_strdup(locale); + else + *language_variant = NULL; + } + if (_t && !language) { + MEM_freeN(_t); + } +} \ No newline at end of file diff --git a/source/blender/blenfont/intern/blf_translation.c b/source/blender/blenfont/intern/blf_translation.c index 5d4b631688a..3aaa61d55f2 100644 --- a/source/blender/blenfont/intern/blf_translation.c +++ b/source/blender/blenfont/intern/blf_translation.c @@ -47,6 +47,8 @@ #include "DNA_userdef_types.h" /* For user settings. */ +#include "BPY_extern.h" + static const char unifont_filename[] = "droidsans.ttf.gz"; static unsigned char *unifont_ttf = NULL; static int unifont_size = 0; @@ -84,7 +86,15 @@ const char *BLF_pgettext(const char *msgctxt, const char *msgid) { #ifdef WITH_INTERNATIONAL if (msgid && msgid[0]) { - return bl_locale_pgettext(msgctxt, msgid); + const char *ret = bl_locale_pgettext(msgctxt, msgid); + /* We assume if the returned string is the same (memory level) as the msgid, no translation was found, + * and we can try py scripts' ones! + */ + if (ret == msgid) { + ret = BPY_app_translations_py_pgettext(msgctxt, msgid); + } + + return ret; } return ""; #else -- cgit v1.2.3