Using i18n in Blender code
Here is a small intro on how to internationalize your code in Blender.
Most (in fact, nearly all) translations are detected and done automatically by Blender’s RNA system.
So when you define a RNA property, the name and label of a panel, menu or operator, etc., you have nothing special to do, everything is handled automatically. Well, not the translation itself, of course, you’ll have to wait for the translating teams to do their job!
Apart from RNA, here is a list of functions that will automatically translate their string(s) (and which string can be caught by our fake py-xgettext code – please note that only regular strings are caught by this tool, so do not expect strings involving macro magic and the like to work here):
BKE_report(ReportList *reports, ReportType type, const char *message).
BKE_reportf(ReportList *reports, ReportType type, const char *format, ...).
BKE_reports_prepend(ReportList *reports, const char *prepend).
BKE_reports_prependf(ReportList *reports, const char *prepend, ...).
CTX_wm_operator_poll_msg_set(struct bContext *C, const char *msg).
BMO_error_raise(BMesh *bm, BMOperator *owner, int errcode, const char *msg).
modifier_setError(struct ModifierData *md, const char *format, ...).
Please note that “printf-like” functions only translate the “format” string, it’s up to you to manage the parameters translation if needed (see below)!
When You Have to Do Something
You have to manually use macros translating (or marking for translation) your UI messages when you are directly accessing GUI drawing code, or when you are using exotic string manipulations… In one word: every time you bybass RNA for some GUI stuff.
To do so, you have three macros available, all being at least markers for xgettext:
- Defined in
- This is the marker-only macro. In other words, it does strictly nothing, it’s just here to tag a string as translatable, for the xgettext tool. Use it e.g. when you use more than one string, based on conditional checks (
draw_my_message(is_foo ? N_("This is a foo message") : N_("This is a bar message"));).
- Defined in
- This macro calls UI_translate_do_iface, which will translate the message if the User Preferences’ use_translate_interface is set. So it’s obviously to be used for labels.
- Defined in
- This macro calls UI_translate_do_tooltip, which will translate the message if the User Preferences’ use_translate_tooltips is set. As stated by its name, it’s to be used for tooltips.
- Defined in
- This macro UI_translate_do_new_dataname, which will translate the message if the User Preferences’ use_translate_new_dataname is set. This one is used to translate new data names (e.g. objects, materials, vgroups, etc.).
- WARNING: This macro should only be used when creating a new data!
Additionally, you may have to edit sconscripts/cmake files to add
../blendfont to the include paths (and add the -DWITH_INTERNATIONAL definition), and obviously include the
Note that those macros are defined as no-op when i18n is disabled at build time.
Adding a new language in Blender
The only file to edit is the release/scripts/module/bl_i18n_utils/settings.py one, LANGUAGES var (format is quite self-explanatory). This will be used by the update_languages_menu.py script in the same dir to generate the locale/languages text file, used by Blender to know which translations are available and to generate the relevant menu…
So your main task will be to gather a team of motivated translators (currently, there are more than 19k messages, above 500k signs…)!
Gettext’s contexts are optional strings attached to a given text to translate, designed to avoid ambiguity (as some words in a language can have different translations in another, depending on… context!).
By default, all translated strings in Blender have the default NULL xgettext context (except for operators’ names, which have the "Operator" one). You should nearly never have to bother about contexts! We are trying to keep their uses as low as possible…
If you nonetheless need to use a context, you could directly give a literal string. However, this is not a good practice, for at least two reasons:
- A typo would go unnoticed, e.g. creating two contexts where only one should exists.
- It’s quite hard to know whether a same (or similar) context already exists!
- And last but not least, it would not fit into our Python i18n API!
To address this, our message searching tools support a very limited “pre-processor” feature: you can use context defines instead of literals. Here is how it works:
- In blenfont/BLF_translation.h, bottom of the file, the contexts are defined.
- In the same file, they are added to the BLF_I18NCONTEXTS_DESC macro, used by the py API (see below).
- You then can use those defines as context values everywhere in code.
You can specify a context for a given RNA structure or property with the RNA define’s
void RNA_def_struct_translation_context(StructRNA *srna, const char *context) and
void RNA_def_property_translation_context(PropertyRNA *prop, const char *context) functions. Please note that RNA context always only affects the name (or GUI label) of the element, never its description (or GUI tooltip), which is assumed detailed enough to avoid any confusion!
For non-RNA translation, you have the CTX_N_(), CTX_IFACE_(), CTX_TIP_() and CTX_DATA_() macros, which take as first parameter a context string (as a define, as explained above), and as second one the text to translate. Otherwise, they do the same thing as their context-less counterparts described above.
About default context
Default context is a bit tricky to handle, as it has to be NULL on gettext side, but RNA/Py does not handle well NULL strings, so here BLF_I18NCONTEXT_DEFAULT_BPYRNA is used, which is defined t
Defining multiple contexts for a single message
The typical use case is the "New" message, which is used for objects, textures, materials, particules, etc. It often translates as an adjective in foreign languages, and adjectives are often made agreed in gender, so we need a context for each kind of data.
To achieve this, the
BLF_I18N_MSGID_MULTI_CTXT(msgid, ...) macro was created. It does nothing, it’s only a declarative one (as e.g. the
N_(msgid) one), it just says the i18n tools that the given msgid must be defined for all the contexts given.
Warning: Due to Python regex limitations, you cannot specify more than 16 contexts in one “call” to this macro – but you can call it several times with the same msgid!
Python I18n API
Blender’s translations API is also exposed in Python (
bpy.app.translations), to be used by UI scripts and addons!
Translations of all “internal” scripts, as well as for OFFICIAL addons, is handled by the main (core) translations project (bf-translation). Other addons have to handle their translations themselves. See this page for more info.
Please note that here too, most messages are detected automatically. In particular,
text parameter of all
bpy.types.UILayout’s functions is handled as expected (most of the time, this is a complex area!). For these functions, you may also manually specify a translation context through the
As in C code, please use pre-defined contexts (accessible through the named tuple
bpy.app.translations.contexts), as much as possible.
When you have complex strings generation (formatting, etc.), you may use the
bpy.app.translations.pgettext family of functions, which mimics the matching macros described above. Note that you can import those functions as shorten names (
data_()), which are also understood by messages extraction tools. And we obviously do not have context/context-less variants in python, context parameter is just in second position and optional!