Fix #76101: I18n: add new preference to translate reports

Translation of the UI is currently split into 3 preferences:
interface, tooltips, and new data. The distinction between interface
and tooltips is currently unclear as tooltips also include a lot of
messages not displayed in the actual tooltips on mouse hover.

These include reports to the Info Editor, information in editor
headers and footers, and statuses in panels.

In order to limit the use of `TIP_()` to actual tooltips, this commit
introduces a new preference for this extra information: "Reports".

New translation macros are introduced: `RPT_()` and `CTX_RPT_()`, as
well as their equivalent for the Python API, `pgettext_rpt_()`, to be
imported as `rpt_()`.

Pull Request: https://projects.blender.org/blender/blender/pulls/116804
This commit is contained in:
Damien Picard 2024-01-11 19:49:03 +01:00 committed by Bastien Montagne
parent ff38836c93
commit 5db82be74f
10 changed files with 77 additions and 8 deletions

View File

@ -573,6 +573,7 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
("pgettext", ("_",)),
("pgettext_iface", ("iface_",)),
("pgettext_tip", ("tip_",)),
("pgettext_rpt", ("rpt_",)),
("pgettext_data", ("data_",)),
)
pgettext_variants_args = {"msgid": (0, {"msgctxt": 1})}

View File

@ -243,10 +243,10 @@ _ctxt_re = _ctxt_re_gen("")
_msg_re = r"(?P<msg_raw>" + _str_whole_re.format(_="_msg") + r")"
PYGETTEXT_KEYWORDS = (() +
tuple((r"{}\(\s*" + _msg_re + r"\s*\)").format(it)
for it in ("IFACE_", "TIP_", "DATA_", "N_")) +
for it in ("IFACE_", "TIP_", "RPT_", "DATA_", "N_")) +
tuple((r"{}\(\s*" + _ctxt_re + r"\s*,\s*" + _msg_re + r"\s*\)").format(it)
for it in ("CTX_IFACE_", "CTX_TIP_", "CTX_DATA_", "CTX_N_")) +
for it in ("CTX_IFACE_", "CTX_TIP_", "CTX_RPT_", "CTX_DATA_", "CTX_N_")) +
tuple(("{}\\((?:[^\"',]+,){{1,2}}\\s*" + _msg_re + r"\s*(?:\)|,)").format(it)
for it in ("BKE_report", "BKE_reportf", "BKE_reports_prepend", "BKE_reports_prependf",

View File

@ -260,6 +260,7 @@ class USERPREF_PT_interface_translation(InterfacePanel, CenterAlignMixIn, Panel)
col.active = (bpy.app.translations.locale != "en_US")
col.prop(view, "use_translate_tooltips", text="Tooltips")
col.prop(view, "use_translate_interface", text="Interface")
col.prop(view, "use_translate_reports", text="Reports")
col.prop(view, "use_translate_new_dataname", text="New Data")

View File

@ -494,8 +494,8 @@ void blo_do_versions_userdef(UserDef *userdef)
USER_FLAG_UNUSED_6 | USER_FLAG_UNUSED_7 | USER_FLAG_UNUSED_9 |
USER_DEVELOPER_UI);
userdef->uiflag &= ~(USER_HEADER_BOTTOM);
userdef->transopts &= ~(USER_TR_UNUSED_2 | USER_TR_UNUSED_3 | USER_TR_UNUSED_4 |
USER_TR_UNUSED_6 | USER_TR_UNUSED_7);
userdef->transopts &= ~(USER_TR_UNUSED_3 | USER_TR_UNUSED_4 | USER_TR_UNUSED_6 |
USER_TR_UNUSED_7);
userdef->uiflag |= USER_LOCK_CURSOR_ADJUST;
}

View File

@ -19,14 +19,21 @@ extern "C" {
bool BLT_is_default_context(const char *msgctxt);
const char *BLT_pgettext(const char *msgctxt, const char *msgid);
/* translation */
/* Translation */
/* - iface includes buttons in the user interface: short labels displayed in windows, panels,
* menus.
* - tooltips only include the popup tooltips when hovering a button.
* - report is for longer, additional information displayed in the UI, such as error messages.
* - new_dataname is the actual user-created data such as objects, meshes, etc. */
bool BLT_translate(void);
bool BLT_translate_iface(void);
bool BLT_translate_tooltips(void);
bool BLT_translate_reports(void);
bool BLT_translate_new_dataname(void);
const char *BLT_translate_do(const char *msgctxt, const char *msgid);
const char *BLT_translate_do_iface(const char *msgctxt, const char *msgid);
const char *BLT_translate_do_tooltip(const char *msgctxt, const char *msgid);
const char *BLT_translate_do_report(const char *msgctxt, const char *msgid);
const char *BLT_translate_do_new_dataname(const char *msgctxt, const char *msgid);
/* The "translation-marker" macro. */
@ -37,9 +44,11 @@ const char *BLT_translate_do_new_dataname(const char *msgctxt, const char *msgid
/*# define _(msgid) BLT_gettext(msgid) */
#define IFACE_(msgid) BLT_translate_do_iface(NULL, msgid)
#define TIP_(msgid) BLT_translate_do_tooltip(NULL, msgid)
#define RPT_(msgid) BLT_translate_do_report(NULL, msgid)
#define DATA_(msgid) BLT_translate_do_new_dataname(NULL, msgid)
#define CTX_IFACE_(context, msgid) BLT_translate_do_iface(context, msgid)
#define CTX_TIP_(context, msgid) BLT_translate_do_tooltip(context, msgid)
#define CTX_RPT_(context, msgid) BLT_translate_do_report(context, msgid)
#define CTX_DATA_(context, msgid) BLT_translate_do_new_dataname(context, msgid)
/* Helper macro, when we want to define a same msgid for multiple msgctxt...

View File

@ -90,6 +90,15 @@ bool BLT_translate_tooltips()
#endif
}
bool BLT_translate_reports()
{
#ifdef WITH_INTERNATIONAL
return BLT_translate() && (U.transopts & USER_TR_REPORTS);
#else
return false;
#endif
}
bool BLT_translate_new_dataname()
{
#ifdef WITH_INTERNATIONAL
@ -144,6 +153,21 @@ const char *BLT_translate_do_tooltip(const char *msgctxt, const char *msgid)
#endif
}
const char *BLT_translate_do_report(const char *msgctxt, const char *msgid)
{
#ifdef WITH_INTERNATIONAL
if (BLT_translate_reports()) {
return BLT_pgettext(msgctxt, msgid);
}
return msgid;
#else
(void)msgctxt;
return msgid;
#endif
}
const char *BLT_translate_do_new_dataname(const char *msgctxt, const char *msgid)
{
#ifdef WITH_INTERNATIONAL

View File

@ -1323,7 +1323,7 @@ typedef enum eUserpref_Anim_Flags {
typedef enum eUserpref_Translation_Flags {
USER_TR_TOOLTIPS = (1 << 0),
USER_TR_IFACE = (1 << 1),
USER_TR_UNUSED_2 = (1 << 2), /* cleared */
USER_TR_REPORTS = (1 << 2),
USER_TR_UNUSED_3 = (1 << 3), /* cleared */
USER_TR_UNUSED_4 = (1 << 4), /* cleared */
USER_DOTRANSLATE_DEPRECATED = (1 << 5), /* Deprecated in 2.83. */

View File

@ -330,10 +330,10 @@ static void rna_userdef_language_update(Main * /*bmain*/, Scene * /*scene*/, Poi
const char *uilng = BLT_lang_get();
if (STREQ(uilng, "en_US")) {
U.transopts &= ~(USER_TR_IFACE | USER_TR_TOOLTIPS | USER_TR_NEWDATANAME);
U.transopts &= ~(USER_TR_IFACE | USER_TR_TOOLTIPS | USER_TR_REPORTS | USER_TR_NEWDATANAME);
}
else {
U.transopts |= (USER_TR_IFACE | USER_TR_TOOLTIPS | USER_TR_NEWDATANAME);
U.transopts |= (USER_TR_IFACE | USER_TR_TOOLTIPS | USER_TR_REPORTS | USER_TR_NEWDATANAME);
}
USERDEF_TAG_DIRTY;
@ -5196,6 +5196,12 @@ static void rna_def_userdef_view(BlenderRNA *brna)
"(note that this might make it hard to follow tutorials or the manual)");
RNA_def_property_update(prop, 0, "rna_userdef_update");
prop = RNA_def_property(srna, "use_translate_reports", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "transopts", USER_TR_REPORTS);
RNA_def_property_ui_text(
prop, "Translate Reports", "Translate additional information, such as error messages");
RNA_def_property_update(prop, 0, "rna_userdef_update");
prop = RNA_def_property(srna, "use_translate_new_dataname", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "transopts", USER_TR_NEWDATANAME);
RNA_def_property_ui_text(prop,

View File

@ -646,6 +646,29 @@ static PyObject *app_translations_pgettext_tip(BlenderAppTranslations * /*self*/
return _py_pgettext(args, kw, BLT_translate_do_tooltip);
}
PyDoc_STRVAR(
app_translations_pgettext_rpt_doc,
".. method:: pgettext_rpt(msgid, msgctxt=None)\n"
"\n"
" Try to translate the given msgid (with optional msgctxt), if reports' 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_rpt(BlenderAppTranslations * /*self*/,
PyObject *args,
PyObject *kw)
{
return _py_pgettext(args, kw, BLT_translate_do_report);
}
PyDoc_STRVAR(app_translations_pgettext_data_doc,
".. method:: pgettext_data(msgid, msgctxt=None)\n"
"\n"
@ -740,6 +763,10 @@ static PyMethodDef app_translations_methods[] = {
(PyCFunction)app_translations_pgettext_tip,
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
app_translations_pgettext_tip_doc},
{"pgettext_rpt",
(PyCFunction)app_translations_pgettext_rpt,
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
app_translations_pgettext_rpt_doc},
{"pgettext_data",
(PyCFunction)app_translations_pgettext_data,
METH_VARARGS | METH_KEYWORDS | METH_STATIC,

View File

@ -322,6 +322,7 @@ def fake_main():
bpy.app.translations = module_add("bpy.app.translations")
bpy.app.translations.pgettext_iface = lambda s, context="": s
bpy.app.translations.pgettext_data = lambda s: s
bpy.app.translations.pgettext_report = lambda s: s
bpy.app.translations.pgettext_tip = lambda s: s
# id's are chosen at random here...
bpy.app.translations.contexts = module_add("bpy.app.translations.contexts")