From d57d76f23df98d29dc1a6c32a9d0b31a0abe94a2 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 10 Oct 2025 11:25:48 +0200 Subject: [PATCH 1/2] I18n: Refactor: Introduce class for message regexes, includes ctxt This new class for C++ message extraction regexes includes an optional translation context override. --- .../bl_i18n_utils/bl_extract_messages.py | 13 ++-- scripts/modules/bl_i18n_utils/settings.py | 65 ++++++++++++------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/scripts/modules/bl_i18n_utils/bl_extract_messages.py b/scripts/modules/bl_i18n_utils/bl_extract_messages.py index 9a07914c7c6..1fdc74927be 100644 --- a/scripts/modules/bl_i18n_utils/bl_extract_messages.py +++ b/scripts/modules/bl_i18n_utils/bl_extract_messages.py @@ -820,10 +820,6 @@ def dump_src_messages(msgs, reports, settings): return {k: getattr(bpy.app.translations.contexts, n) for k, n in bpy.app.translations.contexts_C_to_py.items()} contexts = get_contexts() - - # Build regexes to extract messages (with optional contexts) from C source. - pygettexts = tuple(re.compile(r).search for r in settings.PYGETTEXT_KEYWORDS) - _clean_str = re.compile(settings.str_clean_re).finditer def clean_str(s): @@ -867,8 +863,9 @@ def dump_src_messages(msgs, reports, settings): data = "" with open(path, encoding="utf8") as f: data = f.read() - for srch in pygettexts: - m = srch(data) + + for keyword in settings.PYGETTEXT_KEYWORDS: + m = keyword.search(data) line = pos = 0 while m: d = m.groupdict() @@ -887,14 +884,14 @@ def dump_src_messages(msgs, reports, settings): process_msg(msgs, msgctxt, msgid, msgsrc, reports, check_ctxt_src, settings) reports["src_messages"].append((msgctxt, msgid, msgsrc)) else: - _msgctxt = d.get("ctxt_raw") + _msgctxt = d.get("ctxt_raw", keyword.context_override) msgctxt, msgid = process_entry(_msgctxt, _msgid) process_msg(msgs, msgctxt, msgid, msgsrc, reports, check_ctxt_src, settings) reports["src_messages"].append((msgctxt, msgid, msgsrc)) pos = m.end() line += data[m.start():pos].count('\n') - m = srch(data, pos) + m = keyword.search(data, pos) forbidden = set() forced = set() diff --git a/scripts/modules/bl_i18n_utils/settings.py b/scripts/modules/bl_i18n_utils/settings.py index 693c8b43254..99f46d18d11 100644 --- a/scripts/modules/bl_i18n_utils/settings.py +++ b/scripts/modules/bl_i18n_utils/settings.py @@ -11,6 +11,7 @@ import json import os +import re import sys import types @@ -247,77 +248,91 @@ _ctxt_re_gen = lambda uid: ( ) _ctxt_re = _ctxt_re_gen("") _msg_re = r"(?P" + _str_whole_re.format(_="_msg") + r")" -PYGETTEXT_KEYWORDS = (() + - tuple((r"{}\(\s*" + _msg_re + r"\s*\)").format(it) - for it in ("IFACE_", "TIP_", "RPT_", "DATA_", "N_")) + - tuple((r"{}\(\s*" + _ctxt_re + r"\s*,\s*" + _msg_re + r"\s*\)").format(it) +class PyGettextKeyword: + def __init__(self, re_expr, context_override=None): + self.re_expr = re_expr + self.context_override = context_override + + self.re = re.compile(re_expr) + self.search = self.re.search + +PYGETTEXT_KEYWORDS = (() + + tuple(PyGettextKeyword((r"{}\(\s*" + _msg_re + r"\s*\)").format(it)) + for it in ("IFACE_", "TIP_", "RPT_", "DATA_", "N_")) + + + tuple(PyGettextKeyword((r"{}\(\s*" + _ctxt_re + r"\s*,\s*" + _msg_re + r"\s*\)").format(it)) for it in ("CTX_IFACE_", "CTX_TIP_", "CTX_RPT_", "CTX_DATA_", "CTX_N_")) + - tuple(("{}\\((?:[^\"',]+,){{1,2}}\\s*" + _msg_re + r"\s*(?:\)|,)").format(it) + tuple(PyGettextKeyword(("{}\\((?:[^\"',]+,){{1,2}}\\s*" + _msg_re + r"\s*(?:\)|,)").format(it)) for it in ("BKE_report", "BKE_reportf", "BKE_reports_prepend", "BKE_reports_prependf", "CTX_wm_operator_poll_msg_set", "WM_global_report", "WM_global_reportf", "UI_but_disable")) + # bmesh operator errors - tuple(("{}\\((?:[^\"',]+,){{3}}\\s*" + _msg_re + r"\s*\)").format(it) + tuple(PyGettextKeyword(("{}\\((?:[^\"',]+,){{3}}\\s*" + _msg_re + r"\s*\)").format(it)) for it in ("BMO_error_raise",)) + # Modifier errors - tuple(("{}\\((?:[^\"',]+,){{2}}\\s*" + _msg_re + r"\s*(?:\)|,)").format(it) + tuple(PyGettextKeyword(("{}\\((?:[^\"',]+,){{2}}\\s*" + _msg_re + r"\s*(?:\)|,)").format(it)) for it in ("BKE_modifier_set_error",)) + # Window manager job names. - tuple(("{}\\((?:[^\"',]+,){{3}}\\s*" + _msg_re + r"\s*,").format(it) + tuple(PyGettextKeyword(("{}\\((?:[^\"',]+,){{3}}\\s*" + _msg_re + r"\s*,").format(it)) for it in ("WM_jobs_get",)) + # Compositor and EEVEE messages. # Ends either with `)` (function call close), or `,` when there are extra formatting parameters. - tuple((r"{}\(\s*" + _msg_re + r"\s*(?:\)|,)").format(it) + tuple(PyGettextKeyword((r"{}\(\s*" + _msg_re + r"\s*(?:\)|,)").format(it)) for it in ("set_info_message", "info_append_i18n")) + # This one is a tad more risky, but in practice would not expect a name/uid string parameter # (the second one in those functions) to ever have a comma in it, so think this is fine. - tuple(("{}\\((?:[^,]+,){{2}}\\s*" + _msg_re + r"\s*(?:\)|,)").format(it) + tuple(PyGettextKeyword(("{}\\((?:[^,]+,){{2}}\\s*" + _msg_re + r"\s*(?:\)|,)").format(it)) for it in ("modifier_subpanel_register", "gpencil_modifier_subpanel_register")) + # Node socket declarations: context-less names. - tuple((r"\.{}(?:\(|[^,]+,)\s*" + _msg_re + r"(?:,[^),]+)*\s*\)" - r"(?![^;]*\.translation_context\()").format(it) + tuple(PyGettextKeyword((r"\.{}(?:\(|[^,]+,)\s*" + _msg_re + r"(?:,[^),]+)*\s*\)" + r"(?![^;]*\.translation_context\()").format(it)) for it in ("add_input", "add_output")) + # Node socket declarations: names with contexts - tuple((r"\.{}(?:\(|[^,]+,)\s*" + _msg_re + - r"[^;]*\.translation_context\(\s*" + _ctxt_re + r"\s*\)").format(it) + tuple(PyGettextKeyword((r"\.{}(?:\(|[^,]+,)\s*" + _msg_re + + r"[^;]*\.translation_context\(\s*" + _ctxt_re + r"\s*\)").format(it)) for it in ("add_input", "add_output")) + # Node socket declarations: description and error messages - tuple((r"\.{}\(\s*" + _msg_re + r"\s*\)").format(it) + tuple(PyGettextKeyword((r"\.{}\(\s*" + _msg_re + r"\s*\)").format(it)) for it in ("description", "error_message_add")) + # Node socket panels and labels from declarations: context-less names - tuple((r"\.{}\(\s*" + _msg_re + - r"\s*\)(?![^;]*\.translation_context\()[^;]*;").format(it) + tuple(PyGettextKeyword((r"\.{}\(\s*" + _msg_re + + r"\s*\)(?![^;]*\.translation_context\()[^;]*;").format(it)) for it in ("short_label", "add_panel",)) + # Node socket panels and labels from declarations: names with contexts - tuple((r"\.{}\(\s*" + _msg_re + r"[^;]*\.translation_context\(\s*" + - _ctxt_re + r"\s*\)").format(it) + tuple(PyGettextKeyword((r"\.{}\(\s*" + _msg_re + r"[^;]*\.translation_context\(\s*" + + _ctxt_re + r"\s*\)").format(it)) for it in ("short_label", "add_panel",)) + # Dynamic node socket labels - tuple((r"{}\(\s*[^,]+,\s*" + _msg_re + r"\s*\)").format(it) + tuple(PyGettextKeyword((r"{}\(\s*[^,]+,\s*" + _msg_re + r"\s*\)").format(it)) for it in ("node_sock_label",)) + # Geometry Nodes field inputs - ((r"FieldInput\(CPPType::get<.*?>\(\),\s*" + _msg_re + r"\s*\)"),) + + (PyGettextKeyword(r"FieldInput\(CPPType::get<.*?>\(\),\s*" + _msg_re + r"\s*\)"),) + # bUnitDef unit names - ((r"/\*name_display\*/\s*" + _msg_re + r"\s*,"),) + + (PyGettextKeyword(r"/\*name_display\*/\s*" + _msg_re + r"\s*,"),) + - tuple((r"{}\(\s*" + _msg_re + r"\s*,\s*(?:" + - r"\s*,\s*)?(?:".join(_ctxt_re_gen(i) for i in range(PYGETTEXT_MAX_MULTI_CTXT)) + r")?\s*,?\s*\)").format(it) - for it in ("BLT_I18N_MSGID_MULTI_CTXT",)) + tuple(PyGettextKeyword( + (r"{}\(\s*" + + _msg_re + + r"\s*,\s*(?:" + + r"\s*,\s*)?(?:".join(_ctxt_re_gen(i) for i in range(PYGETTEXT_MAX_MULTI_CTXT)) + + r")?\s*,?\s*\)" + ).format(it) + ) for it in ("BLT_I18N_MSGID_MULTI_CTXT",)) ) # autopep8: on From 84fc90bb43f501b67d1a92336bb4fa09631bdadb Mon Sep 17 00:00:00 2001 From: Damien Picard Date: Tue, 7 Oct 2025 19:58:24 +0200 Subject: [PATCH 2/2] I18n: Extract many messages from ED_push_undo This function is used to add undo steps. It includes a message, for use in the Undo History menu. This label uses the "Operator" context for translation. This commit adds automatic extraction of two functions `ED_undo_push` and `ED_undo_grouped_push`, as well as manual extraction for many strings not declared in the function calls. In order to extract those messages using the proper context, the extraction logic had to be changed so that a custom context could be specified. The regexes can now be either a pattern, or a (ctxt_override, pattern) tuple. Pull Request: https://projects.blender.org/blender/blender/pulls/147581 --- scripts/modules/bl_i18n_utils/settings.py | 5 ++++ .../interface_template_color_ramp.cc | 6 +++- .../templates/interface_template_id.cc | 14 ++++----- .../editors/space_outliner/outliner_draw.cc | 29 ++++++++++--------- .../editors/space_outliner/outliner_tools.cc | 10 ++++--- 5 files changed, 38 insertions(+), 26 deletions(-) diff --git a/scripts/modules/bl_i18n_utils/settings.py b/scripts/modules/bl_i18n_utils/settings.py index 99f46d18d11..02e52a0a862 100644 --- a/scripts/modules/bl_i18n_utils/settings.py +++ b/scripts/modules/bl_i18n_utils/settings.py @@ -269,6 +269,11 @@ PYGETTEXT_KEYWORDS = (() + "CTX_wm_operator_poll_msg_set", "WM_global_report", "WM_global_reportf", "UI_but_disable")) + + # ED_undo_push() is used in Undo History menu and has the "Operator" context, same as operators. + tuple(PyGettextKeyword(("{}\\((?:[^\"',]+,)\\s*" + _msg_re + r"\s*(?:\))").format(it), + context_override='BLT_I18NCONTEXT_OPERATOR_DEFAULT') + for it in ("ED_undo_push", "ED_undo_grouped_push")) + + # bmesh operator errors tuple(PyGettextKeyword(("{}\\((?:[^\"',]+,){{3}}\\s*" + _msg_re + r"\s*\)").format(it)) for it in ("BMO_error_raise",)) + diff --git a/source/blender/editors/interface/templates/interface_template_color_ramp.cc b/source/blender/editors/interface/templates/interface_template_color_ramp.cc index 8210a026eb1..73ab95f27b9 100644 --- a/source/blender/editors/interface/templates/interface_template_color_ramp.cc +++ b/source/blender/editors/interface/templates/interface_template_color_ramp.cc @@ -58,7 +58,11 @@ static void colorband_distribute(bContext *C, ColorBand *coba, bool evenly) coba->data[a].pos = pos; pos += gap; } - ED_undo_push(C, evenly ? "Distribute Stops Evenly" : "Distribute Stops from Left"); + const char *undo_str = evenly ? CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, + "Distribute Stops Evenly") : + CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, + "Distribute Stops from Left"); + ED_undo_push(C, undo_str); } } diff --git a/source/blender/editors/interface/templates/interface_template_id.cc b/source/blender/editors/interface/templates/interface_template_id.cc index 4c6309f6120..0040ba74482 100644 --- a/source/blender/editors/interface/templates/interface_template_id.cc +++ b/source/blender/editors/interface/templates/interface_template_id.cc @@ -678,7 +678,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) break; case UI_ID_RENAME: /* Only for the undo push. */ - undo_push_label = "Rename Data-Block"; + undo_push_label = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Rename Data-Block"); break; case UI_ID_BROWSE: case UI_ID_PIN: @@ -698,10 +698,10 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) id_us_clear_real(id); id_fake_user_clear(id); id->us = 0; - undo_push_label = "Delete Data-Block"; + undo_push_label = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Delete Data-Block"); } else { - undo_push_label = "Unlink Data-Block"; + undo_push_label = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Unlink Data-Block"); } break; @@ -713,7 +713,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) else { id_us_min(id); } - undo_push_label = "Fake User"; + undo_push_label = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Fake User"); } else { return; @@ -731,7 +731,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) /* Reassign to get proper updates/notifiers. */ idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); - undo_push_label = "Make Local"; + undo_push_label = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Make Local"); } } if (undo_push_label != nullptr) { @@ -752,7 +752,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, nullptr); RNA_property_update(C, &template_ui->ptr, template_ui->prop); - undo_push_label = "Make Local"; + undo_push_label = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Make Local"); } } break; @@ -776,7 +776,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) DEG_relations_tag_update(bmain); } BKE_main_ensure_invariants(*CTX_data_main(C)); - undo_push_label = "Make Single User"; + undo_push_label = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Make Single User"); } break; #if 0 diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index 9e2c170e8fc..4397e0d9764 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -730,7 +730,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) if (ELEM(tselem->type, TSE_SOME_ID, TSE_LINKED_NODE_TREE)) { if (id_rename_helper()) { - undo_str = "Rename Data-Block"; + undo_str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Rename Data-Block"); } WM_msg_publish_rna_prop(mbus, tselem->id, tselem->id, ID, name); @@ -787,13 +787,13 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) BKE_object_defgroup_unique_name(vg, ob); WM_msg_publish_rna_prop(mbus, &ob->id, vg, VertexGroup, name); DEG_id_tag_update(tselem->id, ID_RECALC_SYNC_TO_EVAL); - undo_str = "Rename Vertex Group"; + undo_str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Rename Vertex Group"); break; } case TSE_NLA_ACTION: { /* The #tselem->id is a #bAction. */ if (id_rename_helper()) { - undo_str = "Rename Data-Block"; + undo_str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Rename Data-Block"); } WM_msg_publish_rna_prop(mbus, tselem->id, tselem->id, ID, name); DEG_id_tag_update(tselem->id, ID_RECALC_SYNC_TO_EVAL); @@ -801,13 +801,14 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) } case TSE_NLA_TRACK: { WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_RENAME, nullptr); - undo_str = "Rename NLA Track"; + undo_str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Rename NLA Track"); break; } case TSE_MODIFIER: { WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER | NA_RENAME, nullptr); DEG_relations_tag_update(bmain); - undo_str = "Rename Modifier"; + undo_str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Rename Modifier"); + break; } case TSE_EBONE: { @@ -823,7 +824,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) WM_msg_publish_rna_prop(mbus, &arm->id, ebone, EditBone, name); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); DEG_id_tag_update(tselem->id, ID_RECALC_SYNC_TO_EVAL); - undo_str = "Rename Edit Bone"; + undo_str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Rename Edit Bone"); } break; } @@ -846,7 +847,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) WM_msg_publish_rna_prop(mbus, &arm->id, bone, Bone, name); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); DEG_id_tag_update(tselem->id, ID_RECALC_SYNC_TO_EVAL); - undo_str = "Rename Bone"; + undo_str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Rename Bone"); break; } case TSE_POSE_CHANNEL: { @@ -871,7 +872,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); DEG_id_tag_update(tselem->id, ID_RECALC_SYNC_TO_EVAL); DEG_id_tag_update(&arm->id, ID_RECALC_SYNC_TO_EVAL); - undo_str = "Rename Pose Bone"; + undo_str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Rename Pose Bone"); break; } case TSE_GP_LAYER: { @@ -889,7 +890,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, gpd); DEG_id_tag_update(tselem->id, ID_RECALC_SYNC_TO_EVAL); - undo_str = "Rename Annotation Layer"; + undo_str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Rename Annotation Layer"); break; } case TSE_GREASE_PENCIL_NODE: { @@ -905,7 +906,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) grease_pencil.rename_node(*bmain, node, new_name); DEG_id_tag_update(&grease_pencil.id, ID_RECALC_SYNC_TO_EVAL); WM_event_add_notifier(C, NC_ID | NA_RENAME, nullptr); - undo_str = "Rename Grease Pencil Drawing"; + undo_str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Rename Grease Pencil Drawing"); break; } case TSE_R_LAYER: { @@ -922,13 +923,13 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, ViewLayer, name); WM_event_add_notifier(C, NC_ID | NA_RENAME, nullptr); DEG_id_tag_update(tselem->id, ID_RECALC_SYNC_TO_EVAL); - undo_str = "Rename View Layer"; + undo_str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Rename View Layer"); break; } case TSE_LAYER_COLLECTION: { /* The #tselem->id is a #Collection, not a #LayerCollection */ if (id_rename_helper()) { - undo_str = "Rename Data-Block"; + undo_str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Rename Data-Block"); } WM_msg_publish_rna_prop(mbus, tselem->id, tselem->id, ID, name); WM_event_add_notifier(C, NC_ID | NA_RENAME, nullptr); @@ -944,13 +945,13 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) WM_msg_publish_rna_prop(mbus, &arm->id, bcoll, BoneCollection, name); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_COLLECTION, arm); DEG_id_tag_update(&arm->id, ID_RECALC_SYNC_TO_EVAL); - undo_str = "Rename Bone Collection"; + undo_str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Rename Bone Collection"); break; } case TSE_ACTION_SLOT: { WM_event_add_notifier(C, NC_ID | NA_RENAME, nullptr); - undo_str = "Rename Action Slot"; + undo_str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Rename Action Slot"); break; } } diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 36fcbd1cc79..b5a534304fb 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -36,6 +36,8 @@ #include "BLI_utildefines.h" #include "BLI_vector.hh" +#include "BLT_translation.hh" + #include "BKE_anim_data.hh" #include "BKE_animsys.h" #include "BKE_armature.hh" @@ -2513,7 +2515,7 @@ static wmOperatorStatus outliner_object_operation_exec(bContext *C, wmOperator * WM_window_set_active_scene(bmain, C, win, sce); } - str = "Select Objects"; + str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Select Objects"); selection_changed = true; break; } @@ -2531,14 +2533,14 @@ static wmOperatorStatus outliner_object_operation_exec(bContext *C, wmOperator * if (scene != sce) { WM_window_set_active_scene(bmain, C, win, sce); } - str = "Select Object Hierarchy"; + str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Select Object Hierarchy"); selection_changed = true; break; } case OL_OP_DESELECT: outliner_do_object_operation( C, op->reports, scene, space_outliner, &space_outliner->tree, object_deselect_fn); - str = "Deselect Objects"; + str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Deselect Objects"); selection_changed = true; break; case OL_OP_REMAP: @@ -2549,7 +2551,7 @@ static wmOperatorStatus outliner_object_operation_exec(bContext *C, wmOperator * case OL_OP_RENAME: outliner_do_object_operation( C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn); - str = "Rename Object"; + str = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Rename Object"); break; default: BLI_assert_unreachable();