Internationalization and localization¶
Blender's ultimate goal is to adapt to the different languages and regional differences of its many users. However, its current state offers many language translations but does not perform other localization.
Default Locale¶
Although a Dutch project, Blender's default locale is "English (United States" (en-US). Even when the display language is changed, all other locale-specific preferences are assumed to be in this locale. One exception is system of measurement, which is a separate user setting.
- Capitalization is simple Latin text-only
- Numerals are Western Arabic
- Collation (text sorting) is ASCII
- Interface flow is left-to-right
- Decimal separator is a baseline dot.
- Digit grouping (thousands separator) is the comma.
- Date format is MDY
- Time format is 12-hour
- Keyboard shortcut mnemonics in English (ctrl-S for Save)
- Western color symbolism
Many of the above could be addressed and improved to become locale-specific or user preferences. Exceptions to this are numeral set, decimal separator, and numerical grouping character as these would complicate compatibility between Blender and Python and between users (script sharing for example).
The following sections are all related to language translation.
Automated Translation¶
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)!
Translation Categories¶
Translations are subdivided into a few categories. This makes it possible to, for example, have the interface in English (to follow along with English tutorials) but have tooltips translated to another language.
- Interface:
- Main interface elements, such as titles, labels, buttons, etc.
- Tooltips:
- Tooltips and other longer / more complex explanations for which a translation to native is desirable.
- Reports:
- Info / warning / error messages.
- Data:
- For data-block names like "Object", "Action", etc. These are only translated when a new data-block of that type is created, and the translated string is stored as its name. After that, the name should be shown as-is, and not translated any more.
Translatable Strings in C++ code¶
There are various macros to work with translatable strings. These all act as markers for xgettext,
and most also call the appropriate function to do the actual translation. These macros are defined
in blentranslation/BLT_translation.hh
.
- IFACE_
- For translations in the "Interface" category. Calls
UI_translate_do_iface()
, which will translate the message if the User Preferences’use_translate_interface
is set. - TIP_
- For translations in the "Tooltip" category. Calls
UI_translate_do_tooltip()
, which will translate the message if the User Preferences’use_translate_tooltips
is set. - DATA_
-
For translations in the "Data" category. Calls
UI_translate_do_new_dataname()
, which will translate the message if the User Preferences’use_translate_new_dataname
is set.WARNING: This macro should only be used when naming a new data-block. After that, the name should never be translated, and just shown as-is.
- RPT_
- For translations in the "Report" category. Usually this is not necessary to call directly, as
BKE_report()
already uses this macro internally. - N_
- Marker-only macro. In other words, it does strictly nothing in the code. It’s just here to tag a
string as translatable, so it will be extracted by the
xgettext
tool, but it does not actually replace the string with a translated version. For that, it is assumed the code receiving the string handles the translation. 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") : N_("This is a bar"));
, wheredraw_my_message()
calls the translation system to do the actual string replacement).
Additionally, you may have to edit CMakeLists.txt
to add ../blentranslation
to the include
paths, and to add the -DWITH_INTERNATIONAL
definition.
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…)!
Contexts¶
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
"*"
.
No other context should use that char as first
one! The conversion is handled internally, and you should
anyway never have to use default context yourself.
Defining multiple contexts for a single message¶
The typical use case is the "New" message, which is used for objects, textures, materials, particles, 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 Translation API¶
Blender’s translations API is also exposed in
Python
(bpy.app.translations
), to be used by UI scripts and addons!
No 'f-string'
There should be no 'f-string' type of formatting used in translatable
Python strings. These are not actual strings, and cannot be processed
neither by the i18n messages extraction tools, nor the gettext
backend
used to find a valid translation.
Python translations
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 text_ctxt
parameter.
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 (_()
, iface_()
, tip_()
, and 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!
See Also¶
- The translator process.
- The Python API (« Application Translations » section).