Cleanup: add trailing commas
[blender.git] / source / blender / python / intern / bpy_app_translations.c
index dffa8f20fa3141e0e4e7c42514bed6a30867f85f..acb44f86f75dae66b5b3359a11571fd925e7ab0a 100644 (file)
 /* XXX Why bloody hell isn't that included in Python.h???? */
 #include <structmember.h>
 
+#include "BLI_utildefines.h"
 #include "BLI_string.h"
 #include "BLI_ghash.h"
-#include "BLI_utildefines.h"
 
 #include "BPY_extern.h"
 #include "bpy_app_translations.h"
 
 #include "MEM_guardedalloc.h"
 
-#include "BLF_translation.h"
+#include "BLT_translation.h"
+#include "BLT_lang.h"
 
 #include "RNA_types.h"
-#include "RNA_access.h"
 
+#include "../generic/python_utildefines.h"
 
-typedef struct
-{
+typedef struct {
        PyObject_HEAD
        /* The string used to separate context from actual message in PY_TRANSLATE RNA props. */
        const char *context_separator;
@@ -74,7 +74,7 @@ typedef struct GHashKey {
 static GHashKey *_ghashutil_keyalloc(const void *msgctxt, const void *msgid)
 {
        GHashKey *key = MEM_mallocN(sizeof(GHashKey), "Py i18n GHashKey");
-       key->msgctxt = BLI_strdup(msgctxt ? msgctxt : BLF_I18NCONTEXT_DEFAULT_BPY_INTERN);
+       key->msgctxt = BLI_strdup(BLT_is_default_context(msgctxt) ? BLT_I18NCONTEXT_DEFAULT_BPYRNA : msgctxt);
        key->msgid = BLI_strdup(msgid);
        return key;
 }
@@ -86,16 +86,15 @@ static unsigned int _ghashutil_keyhash(const void *ptr)
        return hash ^ BLI_ghashutil_strhash(key->msgid);
 }
 
-static int _ghashutil_keycmp(const void *a, const void *b)
+static bool _ghashutil_keycmp(const void *a, const void *b)
 {
        const GHashKey *A = a;
        const GHashKey *B = b;
 
        /* Note: comparing msgid first, most of the time it will be enough! */
-       int cmp = BLI_ghashutil_strcmp(A->msgid, B->msgid);
-       if (cmp == 0)
+       if (BLI_ghashutil_strcmp(A->msgid, B->msgid) == false)
                return BLI_ghashutil_strcmp(A->msgctxt, B->msgctxt);
-       return cmp;
+       return true;  /* true means they are not equal! */
 }
 
 static void _ghashutil_keyfree(void *ptr)
@@ -108,10 +107,7 @@ static void _ghashutil_keyfree(void *ptr)
        MEM_freeN((void *)key);
 }
 
-static void _ghashutil_valfree(void *ptr)
-{
-       MEM_freeN(ptr);
-}
+#define _ghashutil_valfree MEM_freeN
 
 /***** Python's messages cache *****/
 
@@ -138,7 +134,7 @@ static void _build_translations_cache(PyObject *py_messages, const char *locale)
 
        /* For each py dict, we'll search for full locale, then language+country, then language+variant,
         * then only language keys... */
-       BLF_locale_explode(locale, &language, NULL, NULL, &language_country, &language_variant);
+       BLT_lang_locale_explode(locale, &language, NULL, NULL, &language_country, &language_variant);
 
        /* Clear the cached ghash if needed, and create a new one. */
        _clear_translations_cache();
@@ -185,7 +181,6 @@ static void _build_translations_cache(PyObject *py_messages, const char *locale)
 
                        /* Iterate over all translations of the found language dict, and populate our ghash cache. */
                        while (PyDict_Next(lang_dict, &ppos, &pykey, &trans)) {
-                               GHashKey *key;
                                const char *msgctxt = NULL, *msgid = NULL;
                                bool invalid_key = false;
 
@@ -195,7 +190,7 @@ static void _build_translations_cache(PyObject *py_messages, const char *locale)
                                else {
                                        PyObject *tmp = PyTuple_GET_ITEM(pykey, 0);
                                        if (tmp == Py_None) {
-                                               msgctxt = BLF_I18NCONTEXT_DEFAULT;
+                                               msgctxt = BLT_I18NCONTEXT_DEFAULT_BPYRNA;
                                        }
                                        else if (PyUnicode_Check(tmp)) {
                                                msgctxt = _PyUnicode_AsString(tmp);
@@ -233,32 +228,26 @@ static void _build_translations_cache(PyObject *py_messages, const char *locale)
                                        continue;
                                }
 
-                               key = _ghashutil_keyalloc(msgctxt, msgid);
-
                                /* Do not overwrite existing keys! */
-                               if (BLI_ghash_lookup(_translations_cache, (void *)key)) {
-                                       continue;
+                               if (BPY_app_translations_py_pgettext(msgctxt, msgid) == msgid) {
+                                       GHashKey *key = _ghashutil_keyalloc(msgctxt, msgid);
+                                       BLI_ghash_insert(_translations_cache, key, BLI_strdup(_PyUnicode_AsString(trans)));
                                }
-
-                               BLI_ghash_insert(_translations_cache, key, BLI_strdup(_PyUnicode_AsString(trans)));
                        }
                }
        }
 
        /* Clean up! */
-       if (language)
-               MEM_freeN(language);
-       if (language_country)
-               MEM_freeN(language_country);
-       if (language_variant)
-               MEM_freeN(language_variant);
+       MEM_SAFE_FREE(language);
+       MEM_SAFE_FREE(language_country);
+       MEM_SAFE_FREE(language_variant);
 }
 
 const char *BPY_app_translations_py_pgettext(const char *msgctxt, const char *msgid)
 {
 #define STATIC_LOCALE_SIZE 32  /* Should be more than enough! */
 
-       GHashKey *key;
+       GHashKey key;
        static char locale[STATIC_LOCALE_SIZE] = "";
        const char *tmp;
 
@@ -266,8 +255,8 @@ const char *BPY_app_translations_py_pgettext(const char *msgctxt, const char *ms
        if (!_translations)
                return msgid;
 
-       tmp = BLF_lang_get();
-       if (strcmp(tmp, locale) || !_translations_cache) {
+       tmp = BLT_lang_get();
+       if (!STREQ(tmp, locale) || !_translations_cache) {
                PyGILState_STATE _py_state;
 
                BLI_strncpy(locale, tmp, STATIC_LOCALE_SIZE);
@@ -282,15 +271,12 @@ const char *BPY_app_translations_py_pgettext(const char *msgctxt, const char *ms
        }
 
        /* And now, simply create the key (context, messageid) and find it in the cached dict! */
-       key = _ghashutil_keyalloc(msgctxt, msgid);
-
-       tmp = BLI_ghash_lookup(_translations_cache, key);
+       key.msgctxt = BLT_is_default_context(msgctxt) ? BLT_I18NCONTEXT_DEFAULT_BPYRNA : msgctxt;
+       key.msgid = msgid;
 
-       _ghashutil_keyfree((void *)key);
+       tmp = BLI_ghash_lookup(_translations_cache, &key);
 
-       if (tmp)
-               return tmp;
-       return msgid;
+       return tmp ? tmp : msgid;
 
 #undef STATIC_LOCALE_SIZE
 }
@@ -302,12 +288,13 @@ PyDoc_STRVAR(app_translations_py_messages_register_doc,
 "\n"
 "   Registers an addon's UI translations.\n"
 "\n"
-"   Note: Does nothing when Blender is built without internationalization support.\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"
+"      ``{locale: {msg_key: msg_translation, ...}, ...}``\n"
 "   :type translations_dict: dict\n"
 "\n"
 );
@@ -349,7 +336,8 @@ PyDoc_STRVAR(app_translations_py_messages_unregister_doc,
 "\n"
 "   Unregisters an addon's UI translations.\n"
 "\n"
-"   Note: Does nothing when Blender is built without internationalization support.\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"
@@ -387,25 +375,25 @@ static PyObject *app_translations_py_messages_unregister(BlenderAppTranslations
 
 static PyTypeObject BlenderAppTranslationsContextsType;
 
-static BLF_i18n_contexts_descriptor _contexts[] = BLF_I18NCONTEXTS_DESC;
+static BLT_i18n_contexts_descriptor _contexts[] = BLT_I18NCONTEXTS_DESC;
 
 /* These fields are just empty placeholders, actual values get set in app_translations_struct().
- * This allows us to avoid many handwriting, and above all, to keep all context definition stuff in BLF_translation.h!
+ * This allows us to avoid many handwriting, and above all, to keep all context definition stuff in BLT_translation.h!
  */
 static PyStructSequence_Field
-app_translations_contexts_fields[sizeof(_contexts) / sizeof(BLF_i18n_contexts_descriptor)] = {{NULL}};
+app_translations_contexts_fields[ARRAY_SIZE(_contexts)] = {{NULL}};
 
 static PyStructSequence_Desc app_translations_contexts_desc = {
        (char *)"bpy.app.translations.contexts",     /* name */
        (char *)"This named tuple contains all pre-defined translation contexts",    /* doc */
        app_translations_contexts_fields,    /* fields */
-       (sizeof(app_translations_contexts_fields) / sizeof(PyStructSequence_Field)) - 1
+       ARRAY_SIZE(app_translations_contexts_fields) - 1
 };
 
 static PyObject *app_translations_contexts_make(void)
 {
        PyObject *translations_contexts;
-       BLF_i18n_contexts_descriptor *ctxt;
+       BLT_i18n_contexts_descriptor *ctxt;
        int pos = 0;
 
        translations_contexts = PyStructSequence_New(&BlenderAppTranslationsContextsType);
@@ -414,7 +402,7 @@ static PyObject *app_translations_contexts_make(void)
        }
 
 #define SetObjString(item) PyStructSequence_SET_ITEM(translations_contexts, pos++, PyUnicode_FromString((item)))
-#define SetObjNone() Py_INCREF(Py_None); PyStructSequence_SET_ITEM(translations_contexts, pos++, Py_None)
+#define SetObjNone() PyStructSequence_SET_ITEM(translations_contexts, pos++, Py_INCREF_RET(Py_None))
 
        for (ctxt = _contexts; ctxt->c_id; ctxt++) {
                if (ctxt->value) {
@@ -434,16 +422,18 @@ static PyObject *app_translations_contexts_make(void)
 /***** Main BlenderAppTranslations Py object definition *****/
 
 PyDoc_STRVAR(app_translations_contexts_doc,
-       "A named tuple containing all pre-defined translation contexts.\n"
-       "WARNING: do not use the \"" BLF_I18NCONTEXT_DEFAULT_BPY_INTERN "\" context, it is internally assimilated as the "
-       "default one!\n"
+"A named tuple containing all pre-defined translation contexts.\n"
+"\n"
+".. warning::\n"
+"   Never use a (new) context starting with \"" BLT_I18NCONTEXT_DEFAULT_BPYRNA "\", it would be internally\n"
+"   assimilated as the default one!\n"
 );
 
 PyDoc_STRVAR(app_translations_contexts_C_to_py_doc,
        "A readonly dict mapping contexts' C-identifiers to their py-identifiers."
 );
 
-PyMemberDef app_translations_members[] = {
+static PyMemberDef app_translations_members[] = {
        {(char *)"contexts", T_OBJECT_EX, offsetof(BlenderAppTranslations, contexts), READONLY,
                             app_translations_contexts_doc},
        {(char *)"contexts_C_to_py", T_OBJECT_EX, offsetof(BlenderAppTranslations, contexts_C_to_py), READONLY,
@@ -452,12 +442,12 @@ PyMemberDef app_translations_members[] = {
 };
 
 PyDoc_STRVAR(app_translations_locale_doc,
-       "The actual locale currently in use (will always return a void string when Blender is built without "
-       "internationalization support)."
+"The actual locale currently in use (will always return a void string when Blender is built without "
+"internationalization support)."
 );
 static PyObject *app_translations_locale_get(PyObject *UNUSED(self), void *UNUSED(userdata))
 {
-       return PyUnicode_FromString(BLF_lang_get());
+       return PyUnicode_FromString(BLT_lang_get());
 }
 
 /* Note: defining as getter, as (even if quite unlikely), this *may* change during runtime... */
@@ -465,7 +455,7 @@ PyDoc_STRVAR(app_translations_locales_doc, "All locales currently known by Blend
 static PyObject *app_translations_locales_get(PyObject *UNUSED(self), void *UNUSED(userdata))
 {
        PyObject *ret;
-       EnumPropertyItem *it, *items = BLF_RNA_lang_enum_properties();
+       EnumPropertyItem *it, *items = BLT_lang_RNA_enum_properties();
        int num_locales = 0, pos = 0;
 
        if (items) {
@@ -488,53 +478,131 @@ static PyObject *app_translations_locales_get(PyObject *UNUSED(self), void *UNUS
        return ret;
 }
 
-PyGetSetDef app_translations_getseters[] = {
+static PyGetSetDef app_translations_getseters[] = {
        /* {name, getter, setter, doc, userdata} */
        {(char *)"locale", (getter)app_translations_locale_get, NULL, app_translations_locale_doc, NULL},
        {(char *)"locales", (getter)app_translations_locales_get, NULL, app_translations_locales_doc, NULL},
        {NULL}
 };
 
+/* pgettext helper. */
+static PyObject *_py_pgettext(PyObject *args, PyObject *kw, const char *(*_pgettext)(const char *, const char *))
+{
+       static const char *kwlist[] = {"msgid", "msgctxt", NULL};
+
+#ifdef WITH_INTERNATIONAL
+       char *msgid, *msgctxt = NULL;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw,
+                                        "s|z:bpy.app.translations.pgettext",
+                                        (char **)kwlist, &msgid, &msgctxt))
+       {
+               return NULL;
+       }
+
+       return PyUnicode_FromString((*_pgettext)(msgctxt ? msgctxt : BLT_I18NCONTEXT_DEFAULT, msgid));
+#else
+       PyObject *msgid, *msgctxt;
+       (void)_pgettext;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kw,
+                                        "O|O:bpy.app.translations.pgettext",
+                                        (char **)kwlist, &msgid, &msgctxt))
+       {
+               return NULL;
+       }
+
+       return Py_INCREF_RET(msgid);
+#endif
+}
+
 PyDoc_STRVAR(app_translations_pgettext_doc,
 ".. method:: pgettext(msgid, msgctxt)\n"
 "\n"
 "   Try to translate the given msgid (with optional msgctxt).\n"
-"   NOTE: The (msgid, msgctxt) parameter orders has been switched compared to gettext function, to allow\n"
-"         single-parameter calls (context then defaults to BLF_I18NCONTEXT_DEFAULT).\n"
-"   NOTE: You should really rarely need to use this function in regular addon code, as all translation should be\n"
-"         handled by Blender internal code.\n"
-"   Note: Does nothing when Blender is built without internationalization support (hence always returns msgid).\n"
+"\n"
+"   .. note::\n"
+"      The ``(msgid, msgctxt)`` parameters order has been switched compared to gettext function, to allow\n"
+"      single-parameter calls (context then defaults to BLT_I18NCONTEXT_DEFAULT).\n"
+"\n"
+"   .. note::\n"
+"      You should really rarely need to use this function in regular addon code, as all translation should be\n"
+"      handled by Blender internal code. The only exception are string containing formatting (like \"File: %r\"),\n"
+"      but you should rather use :func:`pgettext_iface`/:func:`pgettext_tip` in those cases!\n"
+"\n"
+"   .. note::\n"
+"      Does nothing when Blender is built without internationalization support (hence always returns ``msgid``).\n"
 "\n"
 "   :arg msgid: The string to translate.\n"
 "   :type msgid: string\n"
-"   :arg msgctxt: The translation context.\n"
+"   :arg msgctxt: The translation context (defaults to BLT_I18NCONTEXT_DEFAULT).\n"
 "   :type msgctxt: string or None\n"
-"   :default msgctxt: BLF_I18NCONTEXT_DEFAULT value.\n"
 "   :return: The translated string (or msgid if no translation was found).\n"
 "\n"
 );
 static PyObject *app_translations_pgettext(BlenderAppTranslations *UNUSED(self), PyObject *args, PyObject *kw)
 {
-       /* Note we could optimize this a bit when WITH_INTERNATIONAL is not defined, but don't think "code complexity" would
-        * be worth it, as this func should not often be used!
-        */
-       /* XXX This code fails with scons when WITH_INTERNATIONAL is not defined, at link time, stating that BLF_pgettext
-        * is undefined... So using #ifdef after all, rather than removing scons from blender trunk!
-        */
-       static const char *kwlist[] = {"msgid", "msgctxt", NULL};
-       char *msgid, *msgctxt = NULL;
+       return _py_pgettext(args, kw, BLT_pgettext);
+}
 
-       if (!PyArg_ParseTupleAndKeywords(args, kw, "s|z:bpy.app.translations.pgettext", (char **)kwlist,
-                                        &msgid, &msgctxt))
-       {
-               return NULL;
-       }
+PyDoc_STRVAR(app_translations_pgettext_iface_doc,
+".. method:: pgettext_iface(msgid, msgctxt)\n"
+"\n"
+"   Try to translate the given msgid (with optional msgctxt), if labels' translation is enabled.\n"
+"\n"
+"   .. note::\n"
+"      See :func:`pgettext` notes.\n"
+"\n"
+"   :arg msgid: The string to translate.\n"
+"   :type msgid: string\n"
+"   :arg msgctxt: The translation context (defaults to BLT_I18NCONTEXT_DEFAULT).\n"
+"   :type msgctxt: string or None\n"
+"   :return: The translated string (or msgid if no translation was found).\n"
+"\n"
+);
+static PyObject *app_translations_pgettext_iface(BlenderAppTranslations *UNUSED(self), PyObject *args, PyObject *kw)
+{
+       return _py_pgettext(args, kw, BLT_translate_do_iface);
+}
 
-#ifdef WITH_INTERNATIONAL
-       return PyUnicode_FromString(BLF_pgettext(msgctxt ? msgctxt : BLF_I18NCONTEXT_DEFAULT, msgid));
-#else
-       return PyUnicode_FromString(msgid);
-#endif
+PyDoc_STRVAR(app_translations_pgettext_tip_doc,
+".. method:: pgettext_tip(msgid, msgctxt)\n"
+"\n"
+"   Try to translate the given msgid (with optional msgctxt), if tooltips' translation is enabled.\n"
+"\n"
+"   .. note::\n"
+"      See :func:`pgettext` notes.\n"
+"\n"
+"   :arg msgid: The string to translate.\n"
+"   :type msgid: string\n"
+"   :arg msgctxt: The translation context (defaults to BLT_I18NCONTEXT_DEFAULT).\n"
+"   :type msgctxt: string or None\n"
+"   :return: The translated string (or msgid if no translation was found).\n"
+"\n"
+);
+static PyObject *app_translations_pgettext_tip(BlenderAppTranslations *UNUSED(self), PyObject *args, PyObject *kw)
+{
+       return _py_pgettext(args, kw, BLT_translate_do_tooltip);
+}
+
+PyDoc_STRVAR(app_translations_pgettext_data_doc,
+".. method:: pgettext_data(msgid, msgctxt)\n"
+"\n"
+"   Try to translate the given msgid (with optional msgctxt), if new data name's translation is enabled.\n"
+"\n"
+"   .. note::\n"
+"      See :func:`pgettext` notes.\n"
+"\n"
+"   :arg msgid: The string to translate.\n"
+"   :type msgid: string\n"
+"   :arg msgctxt: The translation context (defaults to BLT_I18NCONTEXT_DEFAULT).\n"
+"   :type msgctxt: string or None\n"
+"   :return: The translated string (or ``msgid`` if no translation was found).\n"
+"\n"
+);
+static PyObject *app_translations_pgettext_data(BlenderAppTranslations *UNUSED(self), PyObject *args, PyObject *kw)
+{
+       return _py_pgettext(args, kw, BLT_translate_do_new_dataname);
 }
 
 PyDoc_STRVAR(app_translations_locale_explode_doc,
@@ -549,11 +617,12 @@ PyDoc_STRVAR(app_translations_locale_explode_doc,
 "\n"
 "   :arg locale: The ISO locale string to explode.\n"
 "   :type msgid: string\n"
-"   :return: A tuple (language, country, variant, language_country, language@variant).\n"
+"   :return: A tuple ``(language, country, variant, language_country, language@variant)``.\n"
 "\n"
 );
 static PyObject *app_translations_locale_explode(BlenderAppTranslations *UNUSED(self), PyObject *args, PyObject *kw)
 {
+       PyObject *ret_tuple;
        static const char *kwlist[] = {"locale", NULL};
        const char *locale;
        char *language, *country, *variant, *language_country, *language_variant;
@@ -562,21 +631,35 @@ static PyObject *app_translations_locale_explode(BlenderAppTranslations *UNUSED(
                return NULL;
        }
 
-       BLF_locale_explode(locale, &language, &country, &variant, &language_country, &language_variant);
+       BLT_lang_locale_explode(locale, &language, &country, &variant, &language_country, &language_variant);
+
+       ret_tuple = Py_BuildValue("sssss", language, country, variant, language_country, language_variant);
+
+       MEM_SAFE_FREE(language);
+       MEM_SAFE_FREE(country);
+       MEM_SAFE_FREE(variant);
+       MEM_SAFE_FREE(language_country);
+       MEM_SAFE_FREE(language_variant);
 
-       return Py_BuildValue("sssss", language, country, variant, language_country, language_variant);
+       return ret_tuple;
 }
 
-PyMethodDef app_translations_methods[] = {
+static PyMethodDef app_translations_methods[] = {
        /* Can't use METH_KEYWORDS alone, see http://bugs.python.org/issue11587 */
-       {(char *)"register", (PyCFunction)app_translations_py_messages_register, METH_VARARGS | METH_KEYWORDS,
-                            app_translations_py_messages_register_doc},
-       {(char *)"unregister", (PyCFunction)app_translations_py_messages_unregister, METH_VARARGS | METH_KEYWORDS,
-                              app_translations_py_messages_unregister_doc},
-       {(char *)"pgettext", (PyCFunction)app_translations_pgettext, METH_VARARGS | METH_KEYWORDS | METH_STATIC,
-                            app_translations_pgettext_doc},
-       {(char *)"locale_explode", (PyCFunction)app_translations_locale_explode, METH_VARARGS | METH_KEYWORDS | METH_STATIC,
-                                  app_translations_locale_explode_doc},
+       {"register", (PyCFunction)app_translations_py_messages_register, METH_VARARGS | METH_KEYWORDS,
+                     app_translations_py_messages_register_doc},
+       {"unregister", (PyCFunction)app_translations_py_messages_unregister, METH_VARARGS | METH_KEYWORDS,
+                       app_translations_py_messages_unregister_doc},
+       {"pgettext", (PyCFunction)app_translations_pgettext, METH_VARARGS | METH_KEYWORDS | METH_STATIC,
+                     app_translations_pgettext_doc},
+       {"pgettext_iface", (PyCFunction)app_translations_pgettext_iface, METH_VARARGS | METH_KEYWORDS | METH_STATIC,
+                           app_translations_pgettext_iface_doc},
+       {"pgettext_tip", (PyCFunction)app_translations_pgettext_tip, METH_VARARGS | METH_KEYWORDS | METH_STATIC,
+                         app_translations_pgettext_tip_doc},
+       {"pgettext_data", (PyCFunction)app_translations_pgettext_data, METH_VARARGS | METH_KEYWORDS | METH_STATIC,
+                          app_translations_pgettext_data_doc},
+       {"locale_explode", (PyCFunction)app_translations_locale_explode, METH_VARARGS | METH_KEYWORDS | METH_STATIC,
+                           app_translations_locale_explode_doc},
        {NULL}
 };
 
@@ -588,11 +671,11 @@ static PyObject *app_translations_new(PyTypeObject *type, PyObject *UNUSED(args)
                _translations = (BlenderAppTranslations *)type->tp_alloc(type, 0);
                if (_translations) {
                        PyObject *py_ctxts;
-                       BLF_i18n_contexts_descriptor *ctxt;
+                       BLT_i18n_contexts_descriptor *ctxt;
 
                        _translations->contexts = app_translations_contexts_make();
 
-                       py_ctxts = PyDict_New();
+                       py_ctxts = _PyDict_NewPresized(ARRAY_SIZE(_contexts));
                        for (ctxt = _contexts; ctxt->c_id; ctxt++) {
                                PyObject *val = PyUnicode_FromString(ctxt->py_id);
                                PyDict_SetItemString(py_ctxts, ctxt->c_id, val);
@@ -608,33 +691,23 @@ static PyObject *app_translations_new(PyTypeObject *type, PyObject *UNUSED(args)
        return (PyObject *)_translations;
 }
 
+static void app_translations_free(void *obj)
+{
+       PyObject_Del(obj);
+#ifdef WITH_INTERNATIONAL
+       _clear_translations_cache();
+#endif
+}
+
 PyDoc_STRVAR(app_translations_doc,
-"   This object contains some data/methods regarding internationalization in Blender, and allows every py script\n"
-"   to feature translations for its own UI messages.\n"
-"\n"
-"   WARNING: Most of this object should only be useful if you actually manipulate i18n stuff from Python.\n"
-"            If you are a regular addon, you should only bother about :contexts: and :context_sep: members, and the \n"
-"            :register:/:unregister: functions!"
-"\n"
-"   To add translations to your python script, you must define a dictionary formatted like that:\n"
-"       {locale: {msg_key: msg_translation, ...}, ...}\n"
-"       where:\n"
-"           locale is either a lang iso code (e.g. 'fr'), a lang+country code (e.g. 'pt_BR'),\n"
-"                  a lang+variant code (e.g. 'sr@latin'), or a full code (e.g. 'uz_UZ@cyrilic').\n"
-"           msg_key is a tuple (context, org message) - use, as much as possible, the predefined :contexts:.\n"
-"           msg_translation is the translated message in given language!"
-"   Then, call bpy.app.translations.register(__name__, your_dict) in your register() function, and \n"
-"   bpy.app.translations.unregister(__name__) in your unregister() one.\n"
-"\n"
-"   bl_i18n_utils module has several functions to help you collect strings to translate, and generate the needed\n"
-"   python code (the translation dictionary), as well as optional intermediary po files if you want some...\n"
-"   See its documentation for more details.\n"
+"This object contains some data/methods regarding internationalization in Blender, and allows every py script\n"
+"to feature translations for its own UI messages.\n"
 "\n"
 );
 static PyTypeObject BlenderAppTranslationsType = {
        PyVarObject_HEAD_INIT(NULL, 0)
                                    /* tp_name */
-       (char *)"bpy.app._translations_type",
+       "bpy.app._translations_type",
                                    /* tp_basicsize */
        sizeof(BlenderAppTranslations),
        0,                          /* tp_itemsize */
@@ -700,7 +773,7 @@ static PyTypeObject BlenderAppTranslationsType = {
                                    /* newfunc tp_new; */
        (newfunc)app_translations_new,
        /*  Low-level free-memory routine */
-       NULL,                       /* freefunc tp_free;  */
+       app_translations_free,      /* freefunc tp_free;  */
        /* For PyObject_IS_GC */
        NULL,                       /* inquiry tp_is_gc;  */
        NULL,                       /* PyObject *tp_bases; */
@@ -709,7 +782,7 @@ static PyTypeObject BlenderAppTranslationsType = {
        NULL,                       /* PyObject *tp_cache; */
        NULL,                       /* PyObject *tp_subclasses; */
        NULL,                       /* PyObject *tp_weaklist; */
-       NULL
+       NULL,
 };
 
 PyObject *BPY_app_translations_struct(void)
@@ -718,7 +791,7 @@ PyObject *BPY_app_translations_struct(void)
 
        /* Let's finalize our contexts structseq definition! */
        {
-               BLF_i18n_contexts_descriptor *ctxt;
+               BLT_i18n_contexts_descriptor *ctxt;
                PyStructSequence_Field *desc;
 
                /* We really populate the contexts' fields here! */
@@ -743,3 +816,11 @@ PyObject *BPY_app_translations_struct(void)
 
        return ret;
 }
+
+void BPY_app_translations_end(void)
+{
+       /* Incase the object remains in a module's namespace, see T44127. */
+#ifdef WITH_INTERNATIONAL
+       _clear_translations_cache();
+#endif
+}