diff options
Diffstat (limited to 'release/scripts/modules/bl_i18n_utils/bl_extract_messages.py')
-rw-r--r-- | release/scripts/modules/bl_i18n_utils/bl_extract_messages.py | 148 |
1 files changed, 127 insertions, 21 deletions
diff --git a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py index 319fd3396a0..21ca38bff20 100644 --- a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py +++ b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py @@ -8,6 +8,7 @@ import datetime import os import re import sys +import glob # XXX Relative import does not work here when used from Blender... from bl_i18n_utils import settings as settings_i18n, utils @@ -96,7 +97,7 @@ def check(check_ctxt, msgs, key, msgsrc, settings): if key in py_in_rna[1]: py_in_rna[0].add(key) if not_capitalized is not None: - if(key[1] not in settings.WARN_MSGID_NOT_CAPITALIZED_ALLOWED and + if (key[1] not in settings.WARN_MSGID_NOT_CAPITALIZED_ALLOWED and key[1][0].isalpha() and not key[1][0].isupper()): not_capitalized.add(key) if end_point is not None: @@ -206,8 +207,6 @@ def dump_rna_messages(msgs, reports, settings, verbose=False): "Context", "Event", "Function", "UILayout", "UnknownType", "Property", "Struct", # registerable classes "Panel", "Menu", "Header", "RenderEngine", "Operator", "OperatorMacro", "Macro", "KeyingSetInfo", - # window classes - "Window", ) } @@ -259,11 +258,12 @@ def dump_rna_messages(msgs, reports, settings, verbose=False): bl_rna_base_props = set() if bl_rna_base: bl_rna_base_props |= set(bl_rna_base.properties.values()) - for cls_base in cls.__bases__: - bl_rna_base = getattr(cls_base, "bl_rna", None) - if not bl_rna_base: - continue - bl_rna_base_props |= set(bl_rna_base.properties.values()) + if hasattr(cls, "__bases__"): + for cls_base in cls.__bases__: + bl_rna_base = getattr(cls_base, "bl_rna", None) + if not bl_rna_base: + continue + bl_rna_base_props |= set(bl_rna_base.properties.values()) props = sorted(bl_rna.properties, key=lambda p: p.identifier) for prop in props: @@ -361,12 +361,25 @@ def dump_rna_messages(msgs, reports, settings, verbose=False): walk_properties(cls) + def walk_keymap_modal_events(keyconfigs, keymap_name, msgsrc_prev, km_i18n_context): + for keyconfig in keyconfigs: + keymap = keyconfig.keymaps.get(keymap_name, None) + if keymap and keymap.is_modal: + for modal_event in keymap.modal_event_values: + msgsrc = msgsrc_prev + ":'{}'".format(modal_event.identifier) + if modal_event.name: + process_msg(msgs, km_i18n_context, modal_event.name, msgsrc, reports, None, settings) + if modal_event.description: + process_msg(msgs, default_context, modal_event.description, msgsrc, reports, None, settings) + def walk_keymap_hierarchy(hier, msgsrc_prev): km_i18n_context = bpy.app.translations.contexts.id_windowmanager for lvl in hier: msgsrc = msgsrc_prev + "." + lvl[1] if isinstance(lvl[0], str): # Can be a function too, now, with tool system... - process_msg(msgs, km_i18n_context, lvl[0], msgsrc, reports, None, settings) + keymap_name = lvl[0] + process_msg(msgs, km_i18n_context, keymap_name, msgsrc, reports, None, settings) + walk_keymap_modal_events(bpy.data.window_managers[0].keyconfigs, keymap_name, msgsrc, km_i18n_context) if lvl[3]: walk_keymap_hierarchy(lvl[3], msgsrc) @@ -438,6 +451,19 @@ def dump_rna_messages(msgs, reports, settings, verbose=False): process_msg(msgs, bpy.app.translations.contexts.operator_default, cat_str, "Generated operator category", reports, check_ctxt_rna, settings) + # Parse keymap preset preferences + for preset_filename in sorted( + os.listdir(os.path.join(settings.PRESETS_DIR, "keyconfig"))): + preset_path = os.path.join(settings.PRESETS_DIR, "keyconfig", preset_filename) + if not (os.path.isfile(preset_path) and preset_filename.endswith(".py")): + continue + preset_name, _ = os.path.splitext(preset_filename) + + bpy.utils.keyconfig_set(preset_path) + preset = bpy.data.window_managers[0].keyconfigs[preset_name] + if preset.preferences is not None: + walk_properties(preset.preferences) + # And parse keymaps! from bl_keymap_utils import keymap_hierarchy walk_keymap_hierarchy(keymap_hierarchy.generate(), "KM_HIERARCHY") @@ -803,20 +829,21 @@ def dump_src_messages(msgs, reports, settings): line += data[pos:m.start()].count('\n') msgsrc = rel_path + ":" + str(line) _msgid = d.get("msg_raw") - # First, try the "multi-contexts" stuff! - _msgctxts = tuple(d.get("ctxt_raw{}".format(i)) for i in range(settings.PYGETTEXT_MAX_MULTI_CTXT)) - if _msgctxts[0]: - for _msgctxt in _msgctxts: - if not _msgctxt: - break + if _msgid not in {'""', "''"}: + # First, try the "multi-contexts" stuff! + _msgctxts = tuple(d.get("ctxt_raw{}".format(i)) for i in range(settings.PYGETTEXT_MAX_MULTI_CTXT)) + if _msgctxts[0]: + for _msgctxt in _msgctxts: + if not _msgctxt: + break + msgctxt, msgid = process_entry(_msgctxt, _msgid) + 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, msgid = process_entry(_msgctxt, _msgid) 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, 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') @@ -852,6 +879,64 @@ def dump_src_messages(msgs, reports, settings): dump_src_file(path, rel_path, msgs, reports, settings) +def dump_preset_messages(msgs, reports, settings): + files = [] + for dpath, _, fnames in os.walk(settings.PRESETS_DIR): + for fname in fnames: + if fname.startswith("_") or not fname.endswith(".py"): + continue + path = os.path.join(dpath, fname) + try: # can't always find the relative path (between drive letters on windows) + rel_path = os.path.relpath(path, settings.PRESETS_DIR) + except ValueError: + rel_path = path + files.append(rel_path) + for rel_path in sorted(files): + msgsrc, msgid = os.path.split(rel_path) + msgsrc = "Preset from " + msgsrc + msgid = bpy.path.display_name(msgid, title_case=False) + process_msg(msgs, settings.DEFAULT_CONTEXT, msgid, msgsrc, reports, None, settings) + + +def dump_template_messages(msgs, reports, settings): + bfiles = [""] # General template, no name needed. + bfiles += glob.glob(settings.TEMPLATES_DIR + "/**/*.blend", recursive=True) + + workspace_names = {} + + for bfile in bfiles: + template = os.path.dirname(bfile) + template = os.path.basename(template) + bpy.ops.wm.read_homefile(use_factory_startup=True, app_template=template) + for ws in bpy.data.workspaces: + names = workspace_names.setdefault(ws.name, []) + names.append(template or "General") + + from bpy.app.translations import contexts as i18n_contexts + msgctxt = i18n_contexts.id_workspace + for workspace_name in sorted(workspace_names): + for msgsrc in sorted(workspace_names[workspace_name]): + msgsrc = "Workspace from template " + msgsrc + process_msg(msgs, msgctxt, workspace_name, msgsrc, + reports, None, settings) + + +def dump_addon_bl_info(msgs, reports, module, settings): + for prop in ('name', 'location', 'description'): + process_msg( + msgs, + settings.DEFAULT_CONTEXT, + module.bl_info[prop], + "Add-on " + + module.bl_info['name'] + + " info: " + + prop, + reports, + None, + settings, + ) + + ##### Main functions! ##### def dump_messages(do_messages, do_checks, settings): bl_ver = "Blender " + bpy.app.version_string @@ -884,6 +969,19 @@ def dump_messages(do_messages, do_checks, settings): # Get strings from C source code. dump_src_messages(msgs, reports, settings) + # Get strings from presets. + dump_preset_messages(msgs, reports, settings) + + # Get strings from startup templates. + dump_template_messages(msgs, reports, settings) + + # Get strings from addons' bl_info. + import addon_utils + for module in addon_utils.modules(): + if module.bl_info['support'] != 'OFFICIAL': + continue + dump_addon_bl_info(msgs, reports, module, settings) + # Get strings from addons' categories. for uid, label, tip in bpy.types.WindowManager.addon_filter.keywords['items']( bpy.context.window_manager, @@ -961,7 +1059,12 @@ def dump_addon_messages(module_name, do_checks, settings): # and make the diff! for key in minus_msgs: if key != settings.PO_HEADER_KEY: - del msgs[key] + if key in msgs: + del msgs[key] + else: + # This should not happen, but some messages seem to have + # leaked on add-on unregister and register? + print(f"Key not found in msgs: {key}") if check_ctxt: _diff_check_ctxt(check_ctxt, minus_check_ctxt) @@ -975,6 +1078,9 @@ def dump_addon_messages(module_name, do_checks, settings): reports["check_ctxt"] = check_ctxt dump_py_messages(msgs, reports, {addon}, settings, addons_only=True) + # Get strings from the addon's bl_info + dump_addon_bl_info(msgs, reports, addon, settings) + pot.unescape() # Strings gathered in py/C source code may contain escaped chars... print_info(reports, pot) |