27 #include <structmember.h>
41 #include "../generic/python_utildefines.h"
43 #ifdef WITH_INTERNATIONAL
72 #ifdef WITH_INTERNATIONAL
78 typedef struct GHashKey {
83 static GHashKey *_ghashutil_keyalloc(
const void *msgctxt,
const void *msgid)
85 GHashKey *key =
MEM_mallocN(
sizeof(GHashKey),
"Py i18n GHashKey");
92 static uint _ghashutil_keyhash(
const void *
ptr)
94 const GHashKey *key =
ptr;
99 static bool _ghashutil_keycmp(
const void *
a,
const void *b)
101 const GHashKey *
A =
a;
102 const GHashKey *
B = b;
111 static void _ghashutil_keyfree(
void *
ptr)
113 const GHashKey *key =
ptr;
121 # define _ghashutil_valfree MEM_freeN
134 static GHash *_translations_cache =
NULL;
136 static void _clear_translations_cache(
void)
138 if (_translations_cache) {
139 BLI_ghash_free(_translations_cache, _ghashutil_keyfree, _ghashutil_valfree);
141 _translations_cache =
NULL;
144 static void _build_translations_cache(PyObject *py_messages,
const char *locale)
146 PyObject *uuid, *uuid_dict;
148 char *language =
NULL, *language_country =
NULL, *language_variant =
NULL;
155 _clear_translations_cache();
156 _translations_cache =
BLI_ghash_new(_ghashutil_keyhash, _ghashutil_keycmp, __func__);
159 while (PyDict_Next(py_messages, &
pos, &uuid, &uuid_dict)) {
163 PyObject_Print(uuid_dict, stdout, 0);
169 lang_dict = PyDict_GetItemString(uuid_dict, locale);
170 if (!lang_dict && language_country) {
171 lang_dict = PyDict_GetItemString(uuid_dict, language_country);
172 locale = language_country;
174 if (!lang_dict && language_variant) {
175 lang_dict = PyDict_GetItemString(uuid_dict, language_variant);
176 locale = language_variant;
178 if (!lang_dict && language) {
179 lang_dict = PyDict_GetItemString(uuid_dict, language);
184 PyObject *pykey, *trans;
187 if (!PyDict_Check(lang_dict)) {
188 printf(
"WARNING! In translations' dict of \"");
189 PyObject_Print(uuid, stdout, Py_PRINT_RAW);
192 " Each language key must have a dictionary as value, \"%s\" is not valid, "
195 PyObject_Print(lang_dict, stdout, Py_PRINT_RAW);
201 while (PyDict_Next(lang_dict, &ppos, &pykey, &trans)) {
202 const char *msgctxt =
NULL, *msgid =
NULL;
203 bool invalid_key =
false;
205 if ((PyTuple_CheckExact(pykey) ==
false) || (PyTuple_GET_SIZE(pykey) != 2)) {
209 PyObject *tmp = PyTuple_GET_ITEM(pykey, 0);
210 if (tmp == Py_None) {
213 else if (PyUnicode_Check(tmp)) {
214 msgctxt = PyUnicode_AsUTF8(tmp);
220 tmp = PyTuple_GET_ITEM(pykey, 1);
221 if (PyUnicode_Check(tmp)) {
222 msgid = PyUnicode_AsUTF8(tmp);
230 printf(
"WARNING! In translations' dict of \"");
231 PyObject_Print(uuid, stdout, Py_PRINT_RAW);
232 printf(
"\", %s language:\n", locale);
234 " Keys must be tuples of (msgctxt [string or None], msgid [string]), "
235 "this one is not valid, skipping: ");
236 PyObject_Print(pykey, stdout, Py_PRINT_RAW);
240 if (PyUnicode_Check(trans) ==
false) {
241 printf(
"WARNING! In translations' dict of \"");
242 PyObject_Print(uuid, stdout, Py_PRINT_RAW);
244 printf(
" Values must be strings, this one is not valid, skipping: ");
245 PyObject_Print(trans, stdout, Py_PRINT_RAW);
251 if (BPY_app_translations_py_pgettext(msgctxt, msgid) == msgid) {
252 GHashKey *key = _ghashutil_keyalloc(msgctxt, msgid);
265 const char *BPY_app_translations_py_pgettext(
const char *msgctxt,
const char *msgid)
267 # define STATIC_LOCALE_SIZE 32
270 static char locale[STATIC_LOCALE_SIZE] =
"";
279 if (!
STREQ(tmp, locale) || !_translations_cache) {
280 PyGILState_STATE _py_state;
286 _py_state = PyGILState_Ensure();
290 PyGILState_Release(_py_state);
299 return tmp ? tmp : msgid;
301 # undef STATIC_LOCALE_SIZE
307 ".. method:: register(module_name, translations_dict)\n"
309 " Registers an addon's UI translations.\n"
312 " Does nothing when Blender is built without internationalization support.\n"
314 " :arg module_name: The name identifying the addon.\n"
315 " :type module_name: string\n"
316 " :arg translations_dict: A dictionary built like that:\n"
317 " ``{locale: {msg_key: msg_translation, ...}, ...}``\n"
318 " :type translations_dict: dict\n"
324 #ifdef WITH_INTERNATIONAL
325 static const char *kwlist[] = {
"module_name",
"translations_dict",
NULL};
326 PyObject *module_name, *uuid_dict;
328 if (!PyArg_ParseTupleAndKeywords(args,
330 "O!O!:bpy.app.translations.register",
339 if (PyDict_Contains(
self->py_messages, module_name)) {
342 "bpy.app.translations.register: translations message cache already contains some data for "
344 (
const char *)PyUnicode_AsUTF8(module_name));
348 PyDict_SetItem(
self->py_messages, module_name, uuid_dict);
351 _clear_translations_cache();
363 ".. method:: unregister(module_name)\n"
365 " Unregisters an addon's UI translations.\n"
368 " Does nothing when Blender is built without internationalization support.\n"
370 " :arg module_name: The name identifying the addon.\n"
371 " :type module_name: string\n"
377 #ifdef WITH_INTERNATIONAL
378 static const char *kwlist[] = {
"module_name",
NULL};
379 PyObject *module_name;
381 if (!PyArg_ParseTupleAndKeywords(args,
383 "O!:bpy.app.translations.unregister",
390 if (PyDict_Contains(
self->py_messages, module_name)) {
391 PyDict_DelItem(
self->py_messages, module_name);
393 _clear_translations_cache();
423 "bpy.app.translations.contexts",
424 "This named tuple contains all predefined translation contexts",
431 PyObject *translations_contexts;
436 if (translations_contexts ==
NULL) {
440 #define SetObjString(item) \
441 PyStructSequence_SET_ITEM(translations_contexts, pos++, PyUnicode_FromString((item)))
442 #define SetObjNone() \
443 PyStructSequence_SET_ITEM(translations_contexts, pos++, Py_INCREF_RET(Py_None))
457 return translations_contexts;
467 "A named tuple containing all predefined translation contexts.\n"
471 "\", it would be internally\n"
472 " assimilated as the default one!\n");
475 "A readonly dict mapping contexts' C-identifiers to their py-identifiers.");
482 app_translations_contexts_doc},
487 app_translations_contexts_C_to_py_doc},
492 "The actual locale currently in use (will always return a void string when Blender "
494 "internationalization support).");
502 "All locales currently known by Blender (i.e. available as translations).");
507 int num_locales = 0,
pos = 0;
518 ret = PyTuple_New(num_locales);
541 const char *(*_pgettext)(
const char *,
const char *))
543 static const char *kwlist[] = {
"msgid",
"msgctxt",
NULL};
545 #ifdef WITH_INTERNATIONAL
546 char *msgid, *msgctxt =
NULL;
548 if (!PyArg_ParseTupleAndKeywords(
549 args, kw,
"s|z:bpy.app.translations.pgettext", (
char **)kwlist, &msgid, &msgctxt)) {
555 PyObject *msgid, *msgctxt;
558 if (!PyArg_ParseTupleAndKeywords(
559 args, kw,
"O|O:bpy.app.translations.pgettext", (
char **)kwlist, &msgid, &msgctxt)) {
563 return Py_INCREF_RET(msgid);
568 app_translations_pgettext_doc,
569 ".. method:: pgettext(msgid, msgctxt=None)\n"
571 " Try to translate the given msgid (with optional msgctxt).\n"
574 " The ``(msgid, msgctxt)`` parameters order has been switched compared to gettext "
575 "function, to allow\n"
576 " single-parameter calls (context then defaults to BLT_I18NCONTEXT_DEFAULT).\n"
579 " You should really rarely need to use this function in regular addon code, as all "
580 "translation should be\n"
581 " handled by Blender internal code. The only exception are string containing formatting "
582 "(like \"File: %r\"),\n"
583 " but you should rather use :func:`pgettext_iface`/:func:`pgettext_tip` in those cases!\n"
586 " Does nothing when Blender is built without internationalization support (hence always "
587 "returns ``msgid``).\n"
589 " :arg msgid: The string to translate.\n"
590 " :type msgid: string\n"
591 " :arg msgctxt: The translation context (defaults to BLT_I18NCONTEXT_DEFAULT).\n"
592 " :type msgctxt: string or None\n"
593 " :return: The translated string (or msgid if no translation was found).\n"
603 ".. method:: pgettext_iface(msgid, msgctxt=None)\n"
605 " Try to translate the given msgid (with optional msgctxt), if labels' translation "
609 " See :func:`pgettext` notes.\n"
611 " :arg msgid: The string to translate.\n"
612 " :type msgid: string\n"
613 " :arg msgctxt: The translation context (defaults to BLT_I18NCONTEXT_DEFAULT).\n"
614 " :type msgctxt: string or None\n"
615 " :return: The translated string (or msgid if no translation was found).\n"
625 ".. method:: pgettext_tip(msgid, msgctxt=None)\n"
627 " Try to translate the given msgid (with optional msgctxt), if tooltips' "
628 "translation is enabled.\n"
631 " See :func:`pgettext` notes.\n"
633 " :arg msgid: The string to translate.\n"
634 " :type msgid: string\n"
635 " :arg msgctxt: The translation context (defaults to BLT_I18NCONTEXT_DEFAULT).\n"
636 " :type msgctxt: string or None\n"
637 " :return: The translated string (or msgid if no translation was found).\n"
647 ".. method:: pgettext_data(msgid, msgctxt=None)\n"
649 " Try to translate the given msgid (with optional msgctxt), if new data name's "
650 "translation is enabled.\n"
653 " See :func:`pgettext` notes.\n"
655 " :arg msgid: The string to translate.\n"
656 " :type msgid: string\n"
657 " :arg msgctxt: The translation context (defaults to BLT_I18NCONTEXT_DEFAULT).\n"
658 " :type msgctxt: string or None\n"
659 " :return: The translated string (or ``msgid`` if no translation was found).\n"
669 app_translations_locale_explode_doc,
670 ".. method:: locale_explode(locale)\n"
672 " Return all components and their combinations of the given ISO locale string.\n"
674 " >>> bpy.app.translations.locale_explode(\"sr_RS@latin\")\n"
675 " (\"sr\", \"RS\", \"latin\", \"sr_RS\", \"sr@latin\")\n"
677 " For non-complete locales, missing elements will be None.\n"
679 " :arg locale: The ISO locale string to explode.\n"
680 " :type msgid: string\n"
681 " :return: A tuple ``(language, country, variant, language_country, language@variant)``.\n"
688 static const char *kwlist[] = {
"locale",
NULL};
690 char *language, *country, *variant, *language_country, *language_variant;
692 if (!PyArg_ParseTupleAndKeywords(
693 args, kw,
"s:bpy.app.translations.locale_explode", (
char **)kwlist, &locale)) {
698 locale, &language, &country, &variant, &language_country, &language_variant);
700 ret_tuple = Py_BuildValue(
701 "sssss", language, country, variant, language_country, language_variant);
716 METH_VARARGS | METH_KEYWORDS,
717 app_translations_py_messages_register_doc},
720 METH_VARARGS | METH_KEYWORDS,
721 app_translations_py_messages_unregister_doc},
724 METH_VARARGS | METH_KEYWORDS | METH_STATIC,
725 app_translations_pgettext_doc},
728 METH_VARARGS | METH_KEYWORDS | METH_STATIC,
729 app_translations_pgettext_iface_doc},
732 METH_VARARGS | METH_KEYWORDS | METH_STATIC,
733 app_translations_pgettext_tip_doc},
736 METH_VARARGS | METH_KEYWORDS | METH_STATIC,
737 app_translations_pgettext_data_doc},
740 METH_VARARGS | METH_KEYWORDS | METH_STATIC,
741 app_translations_locale_explode_doc},
761 PyObject *val = PyUnicode_FromString(ctxt->
py_id);
762 PyDict_SetItemString(py_ctxts, ctxt->
c_id, val);
778 #ifdef WITH_INTERNATIONAL
779 _clear_translations_cache();
784 "This object contains some data/methods regarding internationalization in Blender, "
785 "and allows every py script\n"
786 "to feature translations for its own UI messages.\n"
789 PyVarObject_HEAD_INIT(
NULL, 0)
791 "bpy.app._translations_type",
823 app_translations_doc,
877 PyStructSequence_Field *desc;
882 desc->name = ctxt->
py_id;
885 desc->name = desc->doc =
NULL;
908 #ifdef WITH_INTERNATIONAL
909 _clear_translations_cache();
bool BLI_ghashutil_strcmp(const void *a, const void *b)
GHash * BLI_ghash_new(GHashHashFP hashfp, GHashCmpFP cmpfp, const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void BLI_ghash_insert(GHash *gh, void *key, void *val)
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
#define BLI_ghashutil_strhash(key)
void * BLI_ghash_lookup(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy) ATTR_NONNULL()
void BLT_lang_locale_explode(const char *locale, char **language, char **country, char **variant, char **language_country, char **language_variant)
const char * BLT_lang_get(void)
struct EnumPropertyItem * BLT_lang_RNA_enum_properties(void)
#define BLT_I18NCONTEXTS_DESC
const char * BLT_translate_do_new_dataname(const char *msgctxt, const char *msgid)
#define BLT_I18NCONTEXT_DEFAULT
const char * BLT_pgettext(const char *msgctxt, const char *msgid)
bool BLT_is_default_context(const char *msgctxt)
#define BLT_I18NCONTEXT_DEFAULT_BPYRNA
const char * BLT_translate_do_tooltip(const char *msgctxt, const char *msgid)
const char * BLT_translate_do_iface(const char *msgctxt, const char *msgid)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
Read Guarded memory(de)allocation.
PyDoc_STRVAR(app_translations_py_messages_register_doc, ".. method:: register(module_name, translations_dict)\n" "\n" " Registers an addon's UI translations.\n" "\n" " .. note::\n" " Does nothing when Blender is built without internationalization support.\n" "\n" " :arg module_name: The name identifying the addon.\n" " :type module_name: string\n" " :arg translations_dict: A dictionary built like that:\n" " ``{locale: {msg_key: msg_translation, ...}, ...}``\n" " :type translations_dict: dict\n" "\n")
static PyGetSetDef app_translations_getseters[]
static PyObject * app_translations_locale_explode(BlenderAppTranslations *UNUSED(self), PyObject *args, PyObject *kw)
#define SetObjString(item)
static PyObject * app_translations_pgettext(BlenderAppTranslations *UNUSED(self), PyObject *args, PyObject *kw)
static PyObject * app_translations_py_messages_register(BlenderAppTranslations *self, PyObject *args, PyObject *kw)
static PyTypeObject BlenderAppTranslationsContextsType
static void app_translations_free(void *obj)
static PyStructSequence_Field app_translations_contexts_fields[ARRAY_SIZE(_contexts)]
static BlenderAppTranslations * _translations
static PyTypeObject BlenderAppTranslationsType
PyObject * BPY_app_translations_struct(void)
static PyObject * app_translations_new(PyTypeObject *type, PyObject *UNUSED(args), PyObject *UNUSED(kw))
static PyObject * app_translations_py_messages_unregister(BlenderAppTranslations *self, PyObject *args, PyObject *kw)
static BLT_i18n_contexts_descriptor _contexts[]
static PyObject * app_translations_pgettext_iface(BlenderAppTranslations *UNUSED(self), PyObject *args, PyObject *kw)
static PyObject * app_translations_pgettext_data(BlenderAppTranslations *UNUSED(self), PyObject *args, PyObject *kw)
static PyObject * app_translations_locale_get(PyObject *UNUSED(self), void *UNUSED(userdata))
static PyObject * app_translations_contexts_make(void)
static PyMethodDef app_translations_methods[]
void BPY_app_translations_end(void)
static PyMemberDef app_translations_members[]
static PyObject * _py_pgettext(PyObject *args, PyObject *kw, const char *(*_pgettext)(const char *, const char *))
static PyStructSequence_Desc app_translations_contexts_desc
static PyObject * app_translations_pgettext_tip(BlenderAppTranslations *UNUSED(self), PyObject *args, PyObject *kw)
static PyObject * app_translations_locales_get(PyObject *UNUSED(self), void *UNUSED(userdata))
void(* MEM_freeN)(void *vmemh)
void *(* MEM_mallocN)(size_t len, const char *str)
PyObject_HEAD const char * context_separator
PyObject * contexts_C_to_py