13 #include <structmember.h>
27 #include "../generic/python_utildefines.h"
29 #ifdef WITH_INTERNATIONAL
62 #ifdef WITH_INTERNATIONAL
64 typedef struct GHashKey {
69 static GHashKey *_ghashutil_keyalloc(
const void *msgctxt,
const void *msgid)
71 GHashKey *key =
MEM_mallocN(
sizeof(GHashKey),
"Py i18n GHashKey");
78 static uint _ghashutil_keyhash(
const void *
ptr)
80 const GHashKey *key =
ptr;
85 static bool _ghashutil_keycmp(
const void *
a,
const void *
b)
87 const GHashKey *
A =
a;
88 const GHashKey *
B =
b;
97 static void _ghashutil_keyfree(
void *
ptr)
99 const GHashKey *key =
ptr;
107 # define _ghashutil_valfree MEM_freeN
120 static GHash *_translations_cache =
NULL;
122 static void _clear_translations_cache(
void)
124 if (_translations_cache) {
125 BLI_ghash_free(_translations_cache, _ghashutil_keyfree, _ghashutil_valfree);
127 _translations_cache =
NULL;
130 static void _build_translations_cache(PyObject *py_messages,
const char *locale)
132 PyObject *uuid, *uuid_dict;
134 char *language =
NULL, *language_country =
NULL, *language_variant =
NULL;
141 _clear_translations_cache();
142 _translations_cache =
BLI_ghash_new(_ghashutil_keyhash, _ghashutil_keycmp, __func__);
145 while (PyDict_Next(py_messages, &
pos, &uuid, &uuid_dict)) {
149 PyObject_Print(uuid_dict, stdout, 0);
155 lang_dict = PyDict_GetItemString(uuid_dict, locale);
156 if (!lang_dict && language_country) {
157 lang_dict = PyDict_GetItemString(uuid_dict, language_country);
158 locale = language_country;
160 if (!lang_dict && language_variant) {
161 lang_dict = PyDict_GetItemString(uuid_dict, language_variant);
162 locale = language_variant;
164 if (!lang_dict && language) {
165 lang_dict = PyDict_GetItemString(uuid_dict, language);
170 PyObject *pykey, *trans;
173 if (!PyDict_Check(lang_dict)) {
174 printf(
"WARNING! In translations' dict of \"");
175 PyObject_Print(uuid, stdout, Py_PRINT_RAW);
178 " Each language key must have a dictionary as value, \"%s\" is not valid, "
181 PyObject_Print(lang_dict, stdout, Py_PRINT_RAW);
187 while (PyDict_Next(lang_dict, &ppos, &pykey, &trans)) {
188 const char *msgctxt =
NULL, *msgid =
NULL;
189 bool invalid_key =
false;
191 if ((PyTuple_CheckExact(pykey) ==
false) || (PyTuple_GET_SIZE(pykey) != 2)) {
195 PyObject *tmp = PyTuple_GET_ITEM(pykey, 0);
196 if (tmp == Py_None) {
199 else if (PyUnicode_Check(tmp)) {
200 msgctxt = PyUnicode_AsUTF8(tmp);
206 tmp = PyTuple_GET_ITEM(pykey, 1);
207 if (PyUnicode_Check(tmp)) {
208 msgid = PyUnicode_AsUTF8(tmp);
216 printf(
"WARNING! In translations' dict of \"");
217 PyObject_Print(uuid, stdout, Py_PRINT_RAW);
218 printf(
"\", %s language:\n", locale);
220 " Keys must be tuples of (msgctxt [string or None], msgid [string]), "
221 "this one is not valid, skipping: ");
222 PyObject_Print(pykey, stdout, Py_PRINT_RAW);
226 if (PyUnicode_Check(trans) ==
false) {
227 printf(
"WARNING! In translations' dict of \"");
228 PyObject_Print(uuid, stdout, Py_PRINT_RAW);
230 printf(
" Values must be strings, this one is not valid, skipping: ");
231 PyObject_Print(trans, stdout, Py_PRINT_RAW);
237 if (BPY_app_translations_py_pgettext(msgctxt, msgid) == msgid) {
238 GHashKey *key = _ghashutil_keyalloc(msgctxt, msgid);
251 const char *BPY_app_translations_py_pgettext(
const char *msgctxt,
const char *msgid)
253 # define STATIC_LOCALE_SIZE 32
256 static char locale[STATIC_LOCALE_SIZE] =
"";
265 if (!
STREQ(tmp, locale) || !_translations_cache) {
266 PyGILState_STATE _py_state;
272 _py_state = PyGILState_Ensure();
276 PyGILState_Release(_py_state);
285 return tmp ? tmp : msgid;
287 # undef STATIC_LOCALE_SIZE
293 ".. method:: register(module_name, translations_dict)\n"
295 " Registers an addon's UI translations.\n"
298 " Does nothing when Blender is built without internationalization support.\n"
300 " :arg module_name: The name identifying the addon.\n"
301 " :type module_name: string\n"
302 " :arg translations_dict: A dictionary built like that:\n"
303 " ``{locale: {msg_key: msg_translation, ...}, ...}``\n"
304 " :type translations_dict: dict\n"
310 #ifdef WITH_INTERNATIONAL
311 static const char *kwlist[] = {
"module_name",
"translations_dict",
NULL};
312 PyObject *module_name, *uuid_dict;
314 if (!PyArg_ParseTupleAndKeywords(args,
316 "O!O!:bpy.app.translations.register",
325 if (PyDict_Contains(
self->py_messages, module_name)) {
328 "bpy.app.translations.register: translations message cache already contains some data for "
330 (
const char *)PyUnicode_AsUTF8(module_name));
334 PyDict_SetItem(
self->py_messages, module_name, uuid_dict);
337 _clear_translations_cache();
349 ".. method:: unregister(module_name)\n"
351 " Unregisters an addon's UI translations.\n"
354 " Does nothing when Blender is built without internationalization support.\n"
356 " :arg module_name: The name identifying the addon.\n"
357 " :type module_name: string\n"
363 #ifdef WITH_INTERNATIONAL
364 static const char *kwlist[] = {
"module_name",
NULL};
365 PyObject *module_name;
367 if (!PyArg_ParseTupleAndKeywords(args,
369 "O!:bpy.app.translations.unregister",
376 if (PyDict_Contains(
self->py_messages, module_name)) {
377 PyDict_DelItem(
self->py_messages, module_name);
379 _clear_translations_cache();
409 "bpy.app.translations.contexts",
410 "This named tuple contains all predefined translation contexts",
417 PyObject *translations_contexts;
422 if (translations_contexts ==
NULL) {
426 #define SetObjString(item) \
427 PyStructSequence_SET_ITEM(translations_contexts, pos++, PyUnicode_FromString((item)))
428 #define SetObjNone() \
429 PyStructSequence_SET_ITEM(translations_contexts, pos++, Py_INCREF_RET(Py_None))
443 return translations_contexts;
453 "A named tuple containing all predefined translation contexts.\n"
457 "\", it would be internally\n"
458 " assimilated as the default one!\n");
461 "A readonly dict mapping contexts' C-identifiers to their py-identifiers.");
468 app_translations_contexts_doc},
473 app_translations_contexts_C_to_py_doc},
478 "The actual locale currently in use (will always return a void string when Blender "
480 "internationalization support).");
488 "All locales currently known by Blender (i.e. available as translations).");
493 int num_locales = 0,
pos = 0;
504 ret = PyTuple_New(num_locales);
527 const char *(*_pgettext)(
const char *,
const char *))
529 static const char *kwlist[] = {
"msgid",
"msgctxt",
NULL};
531 #ifdef WITH_INTERNATIONAL
532 char *msgid, *msgctxt =
NULL;
534 if (!PyArg_ParseTupleAndKeywords(
535 args, kw,
"s|z:bpy.app.translations.pgettext", (
char **)kwlist, &msgid, &msgctxt)) {
541 PyObject *msgid, *msgctxt;
544 if (!PyArg_ParseTupleAndKeywords(
545 args, kw,
"O|O:bpy.app.translations.pgettext", (
char **)kwlist, &msgid, &msgctxt)) {
549 return Py_INCREF_RET(msgid);
554 app_translations_pgettext_doc,
555 ".. method:: pgettext(msgid, msgctxt=None)\n"
557 " Try to translate the given msgid (with optional msgctxt).\n"
560 " The ``(msgid, msgctxt)`` parameters order has been switched compared to gettext "
561 "function, to allow\n"
562 " single-parameter calls (context then defaults to BLT_I18NCONTEXT_DEFAULT).\n"
565 " You should really rarely need to use this function in regular addon code, as all "
566 "translation should be\n"
567 " handled by Blender internal code. The only exception are string containing formatting "
568 "(like \"File: %r\"),\n"
569 " but you should rather use :func:`pgettext_iface`/:func:`pgettext_tip` in those cases!\n"
572 " Does nothing when Blender is built without internationalization support (hence always "
573 "returns ``msgid``).\n"
575 " :arg msgid: The string to translate.\n"
576 " :type msgid: string\n"
577 " :arg msgctxt: The translation context (defaults to BLT_I18NCONTEXT_DEFAULT).\n"
578 " :type msgctxt: string or None\n"
579 " :return: The translated string (or msgid if no translation was found).\n"
589 ".. method:: pgettext_iface(msgid, msgctxt=None)\n"
591 " Try to translate the given msgid (with optional msgctxt), if labels' translation "
595 " See :func:`pgettext` notes.\n"
597 " :arg msgid: The string to translate.\n"
598 " :type msgid: string\n"
599 " :arg msgctxt: The translation context (defaults to BLT_I18NCONTEXT_DEFAULT).\n"
600 " :type msgctxt: string or None\n"
601 " :return: The translated string (or msgid if no translation was found).\n"
611 ".. method:: pgettext_tip(msgid, msgctxt=None)\n"
613 " Try to translate the given msgid (with optional msgctxt), if tooltips' "
614 "translation is enabled.\n"
617 " See :func:`pgettext` notes.\n"
619 " :arg msgid: The string to translate.\n"
620 " :type msgid: string\n"
621 " :arg msgctxt: The translation context (defaults to BLT_I18NCONTEXT_DEFAULT).\n"
622 " :type msgctxt: string or None\n"
623 " :return: The translated string (or msgid if no translation was found).\n"
633 ".. method:: pgettext_data(msgid, msgctxt=None)\n"
635 " Try to translate the given msgid (with optional msgctxt), if new data name's "
636 "translation is enabled.\n"
639 " See :func:`pgettext` notes.\n"
641 " :arg msgid: The string to translate.\n"
642 " :type msgid: string\n"
643 " :arg msgctxt: The translation context (defaults to BLT_I18NCONTEXT_DEFAULT).\n"
644 " :type msgctxt: string or None\n"
645 " :return: The translated string (or ``msgid`` if no translation was found).\n"
655 app_translations_locale_explode_doc,
656 ".. method:: locale_explode(locale)\n"
658 " Return all components and their combinations of the given ISO locale string.\n"
660 " >>> bpy.app.translations.locale_explode(\"sr_RS@latin\")\n"
661 " (\"sr\", \"RS\", \"latin\", \"sr_RS\", \"sr@latin\")\n"
663 " For non-complete locales, missing elements will be None.\n"
665 " :arg locale: The ISO locale string to explode.\n"
666 " :type msgid: string\n"
667 " :return: A tuple ``(language, country, variant, language_country, language@variant)``.\n"
674 static const char *kwlist[] = {
"locale",
NULL};
676 char *language, *country, *variant, *language_country, *language_variant;
678 if (!PyArg_ParseTupleAndKeywords(
679 args, kw,
"s:bpy.app.translations.locale_explode", (
char **)kwlist, &locale)) {
684 locale, &language, &country, &variant, &language_country, &language_variant);
686 ret_tuple = Py_BuildValue(
687 "sssss", language, country, variant, language_country, language_variant);
702 METH_VARARGS | METH_KEYWORDS,
703 app_translations_py_messages_register_doc},
706 METH_VARARGS | METH_KEYWORDS,
707 app_translations_py_messages_unregister_doc},
710 METH_VARARGS | METH_KEYWORDS | METH_STATIC,
711 app_translations_pgettext_doc},
714 METH_VARARGS | METH_KEYWORDS | METH_STATIC,
715 app_translations_pgettext_iface_doc},
718 METH_VARARGS | METH_KEYWORDS | METH_STATIC,
719 app_translations_pgettext_tip_doc},
722 METH_VARARGS | METH_KEYWORDS | METH_STATIC,
723 app_translations_pgettext_data_doc},
726 METH_VARARGS | METH_KEYWORDS | METH_STATIC,
727 app_translations_locale_explode_doc},
747 PyObject *val = PyUnicode_FromString(ctxt->
py_id);
748 PyDict_SetItemString(py_ctxts, ctxt->
c_id, val);
764 #ifdef WITH_INTERNATIONAL
765 _clear_translations_cache();
770 "This object contains some data/methods regarding internationalization in Blender, "
771 "and allows every py script\n"
772 "to feature translations for its own UI messages.\n"
775 PyVarObject_HEAD_INIT(
NULL, 0)
777 "bpy.app._translations_type",
809 app_translations_doc,
863 PyStructSequence_Field *desc;
868 desc->name = ctxt->
py_id;
871 desc->name = desc->doc =
NULL;
894 #ifdef WITH_INTERNATIONAL
895 _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_lookup(const GHash *gh, const void *key) 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)
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, 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))
SyclQueue void void size_t num_bytes void
void(* MEM_freeN)(void *vmemh)
void *(* MEM_mallocN)(size_t len, const char *str)
static const pxr::TfToken b("b", pxr::TfToken::Immortal)
PyObject_HEAD const char * context_separator
PyObject * contexts_C_to_py