UI translation from inside Blender UI: first part.

This commit reshapes a bit runtime button info getter, by adding a new uiButGetStrInfo() which accepts a variable number of uiStringInfo parameters, and tries to fill them with the requested strings, for the given button (label, tip, context, RNA identifier, keymap, etc.). Currently used mostly by existing ui_tooltip_create(), and new UI_OT_edittranslation_init operator.

It also adds a few getters (to get RNA i18n context, and current language iso code).

Finally, it adds to C operators needed for the py ui_translation addon:
*UI_OT_edittranslation_init, which gathers requested data and launch the py operator.
*UI_OT_reloadtranslation, which forces a full reload of the whole UI translation (including rechecking the directory containing mo files).

For the first operator to work, it also adds a new user preferences path: i18n_branches_directory, to point to the /branch part of a bf-translation checkout.
This commit is contained in:
Bastien Montagne 2012-07-09 14:25:35 +00:00
parent facb1512c0
commit 0dafa97ea3
12 changed files with 436 additions and 17 deletions

@ -787,6 +787,7 @@ class USERPREF_PT_file(Panel):
sub.label(text="Scripts:")
sub.label(text="Sounds:")
sub.label(text="Temp:")
sub.label(text="I18n Branches:")
sub.label(text="Image Editor:")
sub.label(text="Animation Player:")
@ -797,6 +798,7 @@ class USERPREF_PT_file(Panel):
sub.prop(paths, "script_directory", text="")
sub.prop(paths, "sound_directory", text="")
sub.prop(paths, "temporary_directory", text="")
sub.prop(paths, "i18n_branches_directory", text="")
sub.prop(paths, "image_editor", text="")
subsplit = sub.split(percentage=0.3)
subsplit.prop(paths, "animation_player_preset", text="")

@ -54,6 +54,8 @@ void BLF_lang_init(void);
/* Set the current locale. */
void BLF_lang_set(const char *);
/* Get the current locale (short code, e.g. es_ES). */
const char *BLF_lang_get(void);
/* Set the current encoding name. */
void BLF_lang_encoding(const char *str);

@ -112,6 +112,7 @@ static const char *locales[] = {
void BLF_lang_init(void)
{
char *messagepath = BLI_get_folder(BLENDER_DATAFILES, "locale");
/* printf("%s\n", messagepath);*/
BLI_strncpy(global_encoding_name, SYSTEM_ENCODING_DEFAULT, sizeof(global_encoding_name));
@ -276,6 +277,11 @@ void BLF_lang_set(const char *str)
bind_textdomain_codeset(TEXT_DOMAIN_NAME, global_encoding_name);
}
const char *BLF_lang_get(void)
{
return locales[2 * U.language + 1];
}
void BLF_lang_encoding(const char *str)
{
BLI_strncpy(global_encoding_name, str, sizeof(global_encoding_name));
@ -301,4 +307,9 @@ void BLF_lang_set(const char *str)
return;
}
const char *BLF_lang_get(void)
{
return "";
}
#endif /* WITH_INTERNATIONAL */

@ -515,6 +515,34 @@ struct PointerRNA *uiButGetOperatorPtrRNA(uiBut *but);
void uiButSetUnitType(uiBut *but, const int unit_type);
int uiButGetUnitType(uiBut *but);
enum {
BUT_GET_RNAPROP_IDENTIFIER = 1,
BUT_GET_RNASTRUCT_IDENTIFIER,
BUT_GET_RNAENUM_IDENTIFIER,
BUT_GET_LABEL,
BUT_GET_RNA_LABEL,
BUT_GET_RNAENUM_LABEL,
BUT_GET_RNA_LABEL_CONTEXT, /* Context specified in CTX_XXX_ macros are just unreachable! */
BUT_GET_TIP,
BUT_GET_RNA_TIP,
BUT_GET_RNAENUM_TIP,
BUT_GET_OP_KEYMAP,
};
typedef struct uiStringInfo {
int type;
char *strinfo;
} uiStringInfo;
/* Note: Expects pointers to uiStringInfo structs as parameters.
* Will fill them with translated strings, when possible.
* Strings in uiStringInfo must be MEM_freeN'ed by caller. */
void uiButGetStrInfo(struct bContext *C, uiBut *but, const int nbr, ...);
/* Edit i18n stuff. */
/* Name of the main py op from i18n addon. */
#define EDTSRC_I18N_OP_NAME "UI_OT_edittranslation"
/* Special Buttons
*
* Buttons with a more specific purpose:

@ -3691,6 +3691,134 @@ void uiButSetFocusOnEnter(wmWindow *win, uiBut *but)
wm_event_add(win, &event);
}
void uiButGetStrInfo(bContext *C, uiBut *but, int nbr, ...)
{
va_list args;
EnumPropertyItem *items = NULL, *item = NULL;
int totitems, free_items = FALSE;
va_start(args, nbr);
while (nbr--) {
uiStringInfo *si = (uiStringInfo*) va_arg(args, void*);
int type = si->type;
char *tmp = NULL;
if (type == BUT_GET_LABEL) {
if (but->str) {
/* Menu labels can have some complex formating stuff marked by pipes, we don't want those here! */
char *tc = strchr(but->str, '|');
if (tc)
tmp = BLI_strdupn(but->str, tc - but->str);
else
tmp = BLI_strdup(but->str);
}
else
type = BUT_GET_RNA_LABEL; /* Fail-safe solution... */
}
else if (type == BUT_GET_TIP) {
if (but->tip && but->tip[0])
tmp = BLI_strdup(but->tip);
else
type = BUT_GET_RNA_TIP; /* Fail-safe solution... */
}
if (type == BUT_GET_RNAPROP_IDENTIFIER) {
if (but->rnaprop)
tmp = BLI_strdup(RNA_property_identifier(but->rnaprop));
}
else if (type == BUT_GET_RNASTRUCT_IDENTIFIER) {
if (but->rnaprop)
tmp = BLI_strdup(RNA_struct_identifier(but->rnapoin.type));
else if (but->optype)
tmp = BLI_strdup(but->optype->idname);
else if (ELEM(but->type, MENU, PULLDOWN)) {
MenuType *mt = uiButGetMenuType(but);
if (mt)
tmp = BLI_strdup(mt->idname);
}
}
else if (ELEM(type, BUT_GET_RNA_LABEL, BUT_GET_RNA_TIP)) {
if (but->rnaprop) {
if (type == BUT_GET_RNA_LABEL)
tmp = BLI_strdup(RNA_property_ui_name(but->rnaprop));
else {
const char *t = RNA_property_ui_description(but->rnaprop);
if (t && t[0])
tmp = BLI_strdup(t);
}
}
else if (but->optype) {
if (type == BUT_GET_RNA_LABEL)
tmp = BLI_strdup(RNA_struct_ui_name(but->optype->srna));
else {
const char *t = RNA_struct_ui_description(but->optype->srna);
if (t && t[0])
tmp = BLI_strdup(t);
}
}
else if (ELEM(but->type, MENU, PULLDOWN)) {
MenuType *mt = uiButGetMenuType(but);
if (mt) {
if (type == BUT_GET_RNA_LABEL)
tmp = BLI_strdup(RNA_struct_ui_name(mt->ext.srna));
else {
const char *t = RNA_struct_ui_description(mt->ext.srna);
if (t && t[0])
tmp = BLI_strdup(t);
}
}
}
}
else if (type == BUT_GET_RNA_LABEL_CONTEXT) {
if (but->rnaprop)
tmp = BLI_strdup(RNA_property_translation_context(but->rnaprop));
else if (but->optype)
tmp = BLI_strdup(RNA_struct_translation_context(but->optype->srna));
else if (ELEM(but->type, MENU, PULLDOWN)) {
MenuType *mt = uiButGetMenuType(but);
if (mt)
tmp = BLI_strdup(RNA_struct_translation_context(mt->ext.srna));
}
}
else if (ELEM3(type, BUT_GET_RNAENUM_IDENTIFIER, BUT_GET_RNAENUM_LABEL, BUT_GET_RNAENUM_TIP)) {
if (but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM) {
if (!item) {
int i;
int value = (but->type == ROW) ? (int)but->hardmax : (int)ui_get_but_val(but);
RNA_property_enum_items_gettexted(C, &but->rnapoin, but->rnaprop, &items, &totitems, &free_items);
for (i = 0, item = items; i < totitems; i++, item++) {
if (item->identifier[0] && item->value == value)
break;
}
}
if (type == BUT_GET_RNAENUM_IDENTIFIER)
tmp = BLI_strdup(item->identifier);
else if (type == BUT_GET_RNAENUM_LABEL)
tmp = BLI_strdup(item->name);
else if (item->description && item->description[0])
tmp = BLI_strdup(item->description);
}
}
else if (type == BUT_GET_OP_KEYMAP) {
if (but->optype && !(but->block->flag & UI_BLOCK_LOOP)) {
/* operator keymap (not menus, they already have it) */
IDProperty *prop = (but->opptr) ? but->opptr->data : NULL;
char buf[512];
if (WM_key_event_operator_string(C, but->optype->idname, but->opcontext, prop, TRUE,
buf, sizeof(buf)))
tmp = BLI_strdup(buf);
}
}
si->strinfo = tmp;
}
if (free_items && items)
MEM_freeN(items);
}
/* Program Init/Exit */
void UI_init(void)

@ -4399,23 +4399,32 @@ static int ui_but_menu(bContext *C, uiBut *but)
uiPopupMenu *pup;
uiLayout *layout;
int length;
const char *name;
char *name;
uiStringInfo label = {BUT_GET_LABEL, NULL};
if ((but->rnapoin.data && but->rnaprop) == 0 && but->optype == NULL)
return 0;
/* if ((but->rnapoin.data && but->rnaprop) == 0 && but->optype == NULL)*/
/* return 0;*/
button_timers_tooltip_remove(C, but);
#if 0
if (but->rnaprop)
name = RNA_property_ui_name(but->rnaprop);
else if (but->optype && but->optype->srna)
name = RNA_struct_ui_name(but->optype->srna);
else
name = IFACE_("<needs_name>"); // XXX - should never happen.
#else
uiButGetStrInfo(C, but, 1, &label);
name = label.strinfo;
#endif
pup = uiPupMenuBegin(C, name, ICON_NONE);
layout = uiPupMenuLayout(pup);
if (label.strinfo)
MEM_freeN(label.strinfo);
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
if (but->rnapoin.data && but->rnaprop) {
@ -4642,8 +4651,8 @@ static int ui_but_menu(bContext *C, uiBut *but)
}
/* perhaps we should move this into (G.debug & G_DEBUG) - campbell */
uiItemFullO(layout, "UI_OT_editsource", CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Edit Source"),
ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0);
uiItemFullO(layout, "UI_OT_editsource", NULL, ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0);
uiItemFullO(layout, "UI_OT_edittranslation_init", NULL, ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0);
uiPupMenuEnd(C, pup);

@ -43,6 +43,9 @@
#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
#include "BLF_api.h"
#include "BLF_translation.h"
#include "BKE_context.h"
#include "BKE_screen.h"
#include "BKE_global.h"
@ -732,8 +735,6 @@ void UI_editsource_active_but_test(uiBut *but)
BLI_ghash_insert(ui_editsource_info->hash, but, but_store);
}
/* editsource operator component */
static int editsource_text_edit(bContext *C, wmOperator *op,
char filepath[FILE_MAX], int line)
{
@ -843,16 +844,170 @@ static int editsource_exec(bContext *C, wmOperator *op)
static void UI_OT_editsource(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Reports to Text Block";
ot->name = "Edit Source";
ot->idname = "UI_OT_editsource";
ot->description = "Edit source code for a button";
ot->description = "Edit UI source code of the active button";
/* callbacks */
ot->exec = editsource_exec;
}
/* ------------------------------------------------------------------------- */
/* EditTranslation utility funcs and operator,
* Note: this includes utility functions and button matching checks.
* this only works in conjunction with a py operator! */
void edittranslation_find_po_file(const char *root, const char *uilng, char *path, const size_t maxlen)
{
char t[32]; /* Should be more than enough! */
/* First, full lang code. */
sprintf(t, "%s.po", uilng);
BLI_join_dirfile(path, maxlen, root, uilng);
BLI_join_dirfile(path, maxlen, path, t);
if (BLI_is_file(path))
return;
/* Now try without the second iso code part (_ES in es_ES). */
strncpy(t, uilng, 2);
strcpy(t + 2, uilng + 5); /* Because of some codes like sr_SR@latin... */
BLI_join_dirfile(path, maxlen, root, t);
sprintf(t, "%s.po", t);
BLI_join_dirfile(path, maxlen, path, t);
if (BLI_is_file(path))
return;
path[0] = '\0';
}
static int edittranslation_exec(bContext *C, wmOperator *op)
{
uiBut *but = uiContextActiveButton(C);
int ret = OPERATOR_CANCELLED;
if (but) {
PointerRNA ptr;
char popath[FILE_MAX];
const char *root = U.i18ndir;
const char *uilng = BLF_lang_get();
const int bufs_nbr = 10;
uiStringInfo but_label = {BUT_GET_LABEL, NULL};
uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL};
uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL};
uiStringInfo but_tip = {BUT_GET_TIP, NULL};
uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL};
uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL};
uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL};
uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL};
uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL};
uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL};
if (!BLI_is_dir(root)) {
BKE_report(op->reports, RPT_ERROR, "Please set your User Preferences' \"Translation Branches "
"Directory\" path to a valid directory.");
return OPERATOR_CANCELLED;
}
if (!WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0)) {
BKE_reportf(op->reports, RPT_ERROR, "Could not find operator \"%s\"! Please enable ui_translate addon "
"in the User Preferences.", EDTSRC_I18N_OP_NAME);
return OPERATOR_CANCELLED;
}
/* Try to find a valid po file for current language... */
edittranslation_find_po_file(root, uilng, popath, FILE_MAX);
printf("po path: %s\n", popath);
if (popath[0] == '\0') {
BKE_reportf(op->reports, RPT_ERROR, "No valid po found for language '%s' under %s.", uilng, root);
return OPERATOR_CANCELLED;
}
uiButGetStrInfo(C, but, bufs_nbr, &but_label, &rna_label, &enum_label, &but_tip, &rna_tip, &enum_tip,
&rna_struct, &rna_prop, &rna_enum, &rna_ctxt);
WM_operator_properties_create(&ptr, EDTSRC_I18N_OP_NAME);
RNA_string_set(&ptr, "lang", uilng);
RNA_string_set(&ptr, "po_file", popath);
RNA_string_set(&ptr, "but_label", but_label.strinfo);
RNA_string_set(&ptr, "rna_label", rna_label.strinfo);
RNA_string_set(&ptr, "enum_label", enum_label.strinfo);
RNA_string_set(&ptr, "but_tip", but_tip.strinfo);
RNA_string_set(&ptr, "rna_tip", rna_tip.strinfo);
RNA_string_set(&ptr, "enum_tip", enum_tip.strinfo);
RNA_string_set(&ptr, "rna_struct", rna_struct.strinfo);
RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo);
RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo);
RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo);
ret = WM_operator_name_call(C, EDTSRC_I18N_OP_NAME, WM_OP_INVOKE_DEFAULT, &ptr);
/* Clean up */
if (but_label.strinfo)
MEM_freeN(but_label.strinfo);
if (rna_label.strinfo)
MEM_freeN(rna_label.strinfo);
if (enum_label.strinfo)
MEM_freeN(enum_label.strinfo);
if (but_tip.strinfo)
MEM_freeN(but_tip.strinfo);
if (rna_tip.strinfo)
MEM_freeN(rna_tip.strinfo);
if (enum_tip.strinfo)
MEM_freeN(enum_tip.strinfo);
if (rna_struct.strinfo)
MEM_freeN(rna_struct.strinfo);
if (rna_prop.strinfo)
MEM_freeN(rna_prop.strinfo);
if (rna_enum.strinfo)
MEM_freeN(rna_enum.strinfo);
if (rna_ctxt.strinfo)
MEM_freeN(rna_ctxt.strinfo);
return ret;
}
else {
BKE_report(op->reports, RPT_ERROR, "Active button not found");
return OPERATOR_CANCELLED;
}
}
#if 0
static int edittranslation_poll(bContext *UNUSED(C))
{
/* We need the i18n py addon to be enabled! */
return WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0) ? TRUE : FALSE;
}
#endif
static void UI_OT_edittranslation_init(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Edit Translation";
ot->idname = "UI_OT_edittranslation_init";
ot->description = "Edit i18n in current language for the active button";
/* callbacks */
ot->exec = edittranslation_exec;
/* ot->poll = edittranslation_poll;*/
}
#endif /* WITH_PYTHON */
static int reloadtranslation_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
{
BLF_lang_init();
BLF_cache_clear();
BLF_lang_set(NULL);
UI_reinit_font();
return OPERATOR_FINISHED;
}
static void UI_OT_reloadtranslation(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Reload Translation";
ot->idname = "UI_OT_reloadtranslation";
ot->description = "Force a full reload of UI translation";
/* callbacks */
ot->exec = reloadtranslation_exec;
}
/* ********************************************************* */
/* Registration */
@ -867,6 +1022,8 @@ void UI_buttons_operatortypes(void)
#ifdef WITH_PYTHON
WM_operatortype_append(UI_OT_editsource);
WM_operatortype_append(UI_OT_edittranslation_init);
#endif
WM_operatortype_append(UI_OT_reloadtranslation);
}

@ -417,20 +417,31 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
static ARegionType type;
ARegion *ar;
uiTooltipData *data;
IDProperty *prop;
/* IDProperty *prop;*/
char buf[512];
float fonth, fontw, aspect = but->block->aspect;
float x1f, x2f, y1f, y2f;
int x1, x2, y1, y2, winx, winy, ofsx, ofsy, w, h, a;
const int nbr_info = 6;
uiStringInfo but_tip = {BUT_GET_TIP, NULL};
uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL};
uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL};
uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, NULL};
uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL};
uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL};
if (but->flag & UI_BUT_NO_TOOLTIP)
return NULL;
/* create tooltip data */
data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData");
uiButGetStrInfo(C, but, nbr_info, &but_tip, &enum_label, &enum_tip, &op_keymap, &rna_struct, &rna_prop);
/* special case, enum rna buttons only have enum item description,
* use general enum description too before the specific one */
#if 0
if (but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM) {
const char *descr = RNA_property_description(but->rnaprop);
if (descr && descr[0]) {
@ -441,7 +452,7 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
if (ELEM(but->type, ROW, MENU)) {
EnumPropertyItem *item;
int i, totitem, free;
int totitem, free;
int value = (but->type == ROW) ? (int)but->hardmax : (int)ui_get_but_val(but);
RNA_property_enum_items_gettexted(C, &but->rnapoin, but->rnaprop, &item, &totitem, &free);
@ -469,7 +480,23 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
data->color_id[data->totline] = UI_TIP_LC_MAIN;
data->totline++;
}
#else
/* Tip */
if (but_tip.strinfo) {
BLI_strncpy(data->lines[data->totline], but_tip.strinfo, sizeof(data->lines[0]));
data->color_id[data->totline] = UI_TIP_LC_MAIN;
data->totline++;
}
/* Enum item label & tip */
if (enum_label.strinfo && enum_tip.strinfo) {
BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]),
"%s: %s", enum_label.strinfo, enum_tip.strinfo);
data->color_id[data->totline] = UI_TIP_LC_SUBMENU;
data->totline++;
}
#endif
#if 0
if (but->optype && !(but->block->flag & UI_BLOCK_LOOP)) {
/* operator keymap (not menus, they already have it) */
prop = (but->opptr) ? but->opptr->data : NULL;
@ -482,6 +509,14 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
data->totline++;
}
}
#else
/* Op shortcut */
if (op_keymap.strinfo) {
BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Shortcut: %s"), op_keymap.strinfo);
data->color_id[data->totline] = UI_TIP_LC_NORMAL;
data->totline++;
}
#endif
if (ELEM3(but->type, TEX, IDPOIN, SEARCH_MENU)) {
/* full string */
@ -515,7 +550,7 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
data->totline++;
}
}
#if 0
/* rna info */
if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) {
BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Python: %s.%s"),
@ -523,7 +558,7 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
data->color_id[data->totline] = UI_TIP_LC_PYTHON;
data->totline++;
}
#endif
if (but->rnapoin.id.data) {
ID *id = but->rnapoin.id.data;
if (id->lib && id->lib->name) {
@ -562,6 +597,7 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
}
}
}
#if 0
else if (ELEM(but->type, MENU, PULLDOWN)) {
if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) {
MenuType *mt = uiButGetMenuType(but);
@ -571,8 +607,34 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but)
data->totline++;
}
}
}
#else
if ((U.flag & USER_TOOLTIPS_PYTHON) == 0 && !but->optype && rna_struct.strinfo) {
if (rna_prop.strinfo)
/* Struct and prop */
BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]),
TIP_("Python: %s.%s"), rna_struct.strinfo, rna_prop.strinfo);
else
/* Only struct (e.g. menus) */
BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Python: %s"), rna_struct.strinfo);
data->color_id[data->totline] = UI_TIP_LC_PYTHON;
data->totline++;
}
#endif
/* Free strinfo's... */
if (but_tip.strinfo)
MEM_freeN(but_tip.strinfo);
if (enum_label.strinfo)
MEM_freeN(enum_label.strinfo);
if (enum_tip.strinfo)
MEM_freeN(enum_tip.strinfo);
if (op_keymap.strinfo)
MEM_freeN(op_keymap.strinfo);
if (rna_struct.strinfo)
MEM_freeN(rna_struct.strinfo);
if (rna_prop.strinfo)
MEM_freeN(rna_prop.strinfo);
assert(data->totline < MAX_TOOLTIP_LINES);

@ -347,6 +347,7 @@ typedef struct UserDef {
char textudir[768];
char pythondir[768];
char sounddir[768];
char i18ndir[768];
char image_editor[1024]; /* 1024 = FILE_MAX */
char anim_player[1024]; /* 1024 = FILE_MAX */
int anim_player_preset;

@ -644,6 +644,7 @@ StructRNA *RNA_struct_find(const char *identifier);
const char *RNA_struct_identifier(StructRNA *type);
const char *RNA_struct_ui_name(StructRNA *type);
const char *RNA_struct_ui_description(StructRNA *type);
const char *RNA_struct_translation_context(StructRNA *type);
int RNA_struct_ui_icon(StructRNA *type);
PropertyRNA *RNA_struct_name_property(StructRNA *type);
@ -709,6 +710,7 @@ int RNA_property_string_maxlength(PropertyRNA *prop);
const char *RNA_property_ui_name(PropertyRNA *prop);
const char *RNA_property_ui_description(PropertyRNA *prop);
const char *RNA_property_translation_context(PropertyRNA *prop);
int RNA_property_ui_icon(PropertyRNA *prop);
/* Dynamic Property Information */

@ -527,6 +527,11 @@ const char *RNA_struct_ui_description(StructRNA *type)
return TIP_(type->description);
}
const char *RNA_struct_translation_context(StructRNA *type)
{
return type->translation_context ? type->translation_context : BLF_I18NCONTEXT_DEFAULT;
}
PropertyRNA *RNA_struct_name_property(StructRNA *type)
{
return type->nameproperty;
@ -1363,6 +1368,12 @@ const char *RNA_property_ui_description(PropertyRNA *prop)
return rna_ensure_property_description(prop);
}
const char *RNA_property_translation_context(PropertyRNA *_prop)
{
PropertyRNA *prop = rna_ensure_property(_prop);
return prop->translation_context ? prop->translation_context : BLF_I18NCONTEXT_DEFAULT;
}
int RNA_property_ui_icon(PropertyRNA *prop)
{
return rna_ensure_property(prop)->icon;

@ -3510,6 +3510,12 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna)
"startup, addons & modules (requires restart)");
/* TODO, editing should reset sys.path! */
prop = RNA_def_property(srna, "i18n_branches_directory", PROP_STRING, PROP_DIRPATH);
RNA_def_property_string_sdna(prop, NULL, "i18ndir");
RNA_def_property_ui_text(prop, "Translation Branches Directory",
"The path to the '/branches' directory of your local svn-translation copy, "
"to allow translating from the UI");
prop = RNA_def_property(srna, "sound_directory", PROP_STRING, PROP_DIRPATH);
RNA_def_property_string_sdna(prop, NULL, "sounddir");
RNA_def_property_ui_text(prop, "Sounds Directory", "The default directory to search for sounds");