From 28cd3c630e26104add2dca5c3dd04409a0ca2082 Mon Sep 17 00:00:00 2001 From: mistangl Date: Tue, 10 Feb 2026 17:25:07 +0100 Subject: [PATCH] main Funktion aufgeteilt --- lib/translate.py | 437 ++++++++++++++++++++++------------------------- 1 file changed, 207 insertions(+), 230 deletions(-) diff --git a/lib/translate.py b/lib/translate.py index 108a25c..ff52738 100644 --- a/lib/translate.py +++ b/lib/translate.py @@ -9,7 +9,7 @@ from fnmatch import fnmatch import ezdxf from openpyxl import Workbook -from utils import check_environment_var +from utils import check_environment_var, check_file_in_work """ @@ -1810,7 +1810,32 @@ def retranslate_json_file(json_path: Path, sys.exit(1) -if __name__ == '__main__': +def resolve_input_file(name: str, work_dir: Path, label: str = "Datei") -> Path: + """ + Sucht eine Datei zuerst im Work-Ordner, dann als vollständigen/relativen Pfad. + Gibt den aufgelösten Path zurück oder beendet das Programm mit Fehlermeldung. + + Verwendet intern check_file_in_work() aus utils für die Dateisuche. + + Args: + name: Dateiname oder Pfad (wie vom Benutzer angegeben) + work_dir: Arbeitsverzeichnis, in dem zuerst gesucht wird + label: Bezeichnung der Datei für die Fehlermeldung (z.B. "DXF-Datei") + + Returns: + Path zur gefundenen Datei + """ + path, found = check_file_in_work(work_dir, name) + if not found: + print(f"{label} nicht gefunden: {name}") + print(f" Geprüft in Work-Ordner: {work_dir / name}") + print(f" Geprüft als vollständiger Pfad: {Path(name)}") + sys.exit(1) + return path + + +def _build_arg_parser() -> argparse.ArgumentParser: + """Erstellt und konfiguriert den Argument-Parser für translate.py.""" parser = argparse.ArgumentParser( description='Extrahiert TEXT/MTEXT aus DXF, filtert Texte aus TXT, verarbeitet JSON, oder übersetzt DXF direkt. Optional mit Übersetzungsfunktion.', prog='translate' @@ -1890,69 +1915,213 @@ if __name__ == '__main__': metavar='FILE.json', help='JSON-Datei mit Übersetzungen für --todxf (Standard: _texts.json)' ) + return parser + +def _run_retranslate( + args, + work_dir: Path, + translation_dicts_all: dict, + translation_languages: list) -> None: + """Workflow: Re-Übersetzung einer bestehenden JSON-Datei (--retranslate-json).""" + print("=== Re-Übersetzung JSON-Datei ===\n") + + input_filename = args.retranslate_json + if not input_filename.lower().endswith('.json'): + print("Mit --retranslate-json nur .json Dateien erlaubt") + sys.exit(1) + + json_file = resolve_input_file(input_filename, work_dir, "JSON-Datei") + + print(f"JSON-Datei: {json_file}") + print(f"Sprachen: {', '.join(translation_languages)}") + + retranslate_json_file(json_file, translation_dicts_all, translation_languages) + + print("\nVerarbeitung erfolgreich abgeschlossen.") + sys.exit(0) + + +def _run_todxf( + args, + work_dir: Path, + translation_languages: list, + wildcard_patterns: list, + regex_patterns: list, + source_attr_name: str, + target_attr_name: str, + target_layer_name: str | None) -> None: + """Workflow: DXF-zu-DXF Übersetzung (--todxf).""" + print("=== DXF-zu-DXF Übersetzung ===\n") + + input_filename = args.filename + if not input_filename.lower().endswith(('.dxf', '.dwg')): + print("Mit --todxf nur .dxf oder .dwg Dateien als Quelle erlaubt") + sys.exit(1) + + source_dxf = resolve_input_file(input_filename, work_dir, "Quell-DXF") + print(f"Quell-DXF: {source_dxf}") + + # Bestimme JSON-Datei mit Übersetzungen + if args.translation_json: + translation_json = resolve_input_file(args.translation_json, work_dir, "Übersetzungs-JSON") + else: + # Standard: _texts.json im Work-Ordner + translation_json = work_dir / (Path(input_filename).stem + "_texts.json") + if not translation_json.exists(): + print(f"Standard-Übersetzungs-JSON nicht gefunden: {translation_json}") + print(f"Bitte erstellen Sie die Datei oder geben Sie eine alternative mit --translation-json an") + sys.exit(1) + + print(f"Übersetzungs-JSON: {translation_json}") + + target_lang = translation_languages[0] if translation_languages else None + translations = load_translations_from_json(translation_json, target_lang) + + output_dxf_name = args.todxf + if not output_dxf_name.lower().endswith(('.dxf', '.dwg')): + output_dxf_name += '.dxf' + output_dxf = work_dir / output_dxf_name + + print(f"Ziel-DXF: {output_dxf}") + + translate_dxf_texts( + source_dxf, + output_dxf, + translations, + wildcard_patterns, + regex_patterns, + source_attr_name, + target_attr_name, + target_layer_name, + ) + + print("\nVerarbeitung erfolgreich abgeschlossen.") + sys.exit(0) + + +def _run_extract_or_import( + args, + work_dir: Path, + wildcard_patterns: list, + regex_patterns: list, + translation_dicts, + translation_dicts_all: dict, + translation_languages: list, + export_types: list, + source_attr_name: str) -> None: + """Workflow: Texte extrahieren oder importieren (--extract, --fromtxt, --fromjson).""" + if args.extract: + input_filename = args.filename + if not input_filename.lower().endswith(('.dxf', '.dwg')): + print("Mit --extract nur .dxf oder .dwg Dateien erlaubt") + sys.exit(1) + filename = resolve_input_file(input_filename, work_dir, "DXF-Datei") + print(f"Lese DXF Datei: {filename}") + all_texts = extract_text_from_dxf(filename, source_attr_name) + + elif args.fromtxt: + input_filename = args.fromtxt + if not input_filename.lower().endswith('.txt'): + print("Mit --fromtxt nur .txt Dateien erlaubt") + sys.exit(1) + filename = resolve_input_file(input_filename, work_dir, "Textdatei") + print(f"Lese Textdatei: {filename}") + all_texts = read_texts_from_file(filename) + + else: # args.fromjson + input_filename = args.fromjson + if not input_filename.lower().endswith('.json'): + print("Mit --fromjson nur .json Dateien erlaubt") + sys.exit(1) + filename = resolve_input_file(input_filename, work_dir, "JSON-Datei") + print(f"Lese JSON-Datei: {filename}") + all_texts = read_texts_from_json(filename) + + texts, ignored_texts = filter_texts(all_texts, wildcard_patterns, regex_patterns, args.debug) + + extension_map = { + 'excel': '.xlsx', + 'json': '.json', + 'text': '.txt' + } + + print(f"\nExportiere in {len(export_types)} Format(e): {', '.join(export_types)}") + + for export_type in export_types: + if args.outname: + base_name = args.outname + for ext in extension_map.values(): + if base_name.endswith(ext): + base_name = base_name[:-len(ext)] + break + output_filename = f"{base_name}{extension_map[export_type]}" + else: + output_filename = f"{filename.stem}_texts{extension_map[export_type]}" + + output_path = work_dir / output_filename + + if export_type == 'excel': + write_texts_to_excel(texts, output_path, translation_dicts) + elif export_type == 'json': + write_texts_to_json(texts, ignored_texts, output_path, translation_dicts, translation_dicts_all, translation_languages) + else: # text + write_texts_to_text(texts, ignored_texts, output_path, translation_dicts) + + print(f" [OK] {export_type}: {output_path}") + + print("\nVerarbeitung erfolgreich abgeschlossen.") + + +def main() -> None: + """Einstiegspunkt für translate.py. Parst Argumente und delegiert an Workflow-Funktionen.""" + parser = _build_arg_parser() args = parser.parse_args() # Parse und validiere Export-Typen (komma-separierte Liste) valid_export_types = {'excel', 'json', 'text'} export_types = [t.strip() for t in args.export_type.split(',')] - # Validiere jeden Export-Typ for export_type in export_types: if export_type not in valid_export_types: parser.error(f"Ungültiger Export-Typ: '{export_type}'. Erlaubt sind: {', '.join(sorted(valid_export_types))}") - # Entferne Duplikate und behalte Reihenfolge - seen = set() + seen: set = set() export_types = [t for t in export_types if not (t in seen or seen.add(t))] - # Validierung: --extract benötigt --filename + # Validierung der Argument-Kombinationen if args.extract and not args.filename: parser.error("--extract benötigt --filename") - - # Validierung: --filename nur mit --extract oder --todxf if args.filename and not (args.extract or args.todxf): parser.error("--filename kann nur mit --extract oder --todxf verwendet werden") - - # Validierung: --fromjson benötigt --translate if args.fromjson and not args.translate: parser.error("--fromjson benötigt --translate (Übersetzung ist erforderlich)") - - # Validierung: --retranslate-json benötigt --translate if args.retranslate_json and not args.translate: parser.error("--retranslate-json benötigt --translate (Übersetzung ist erforderlich)") - - # Validierung: --todxf benötigt --filename if args.todxf and not args.filename: parser.error("--todxf benötigt --filename (Quell-DXF muss angegeben werden)") - - # Validierung: --translation-json nur mit --todxf if args.translation_json and not args.todxf: parser.error("--translation-json kann nur mit --todxf verwendet werden") - # Prüfe Umgebungsvariablen + # Umgebungsvariablen und gemeinsame Konfiguration laden work_dir = check_environment_var('PROJECT_WORK') cfg_dir = check_environment_var('PROJECT_CFG') - # Lade Ignore-Muster aus Konfigurationsdatei wildcard_patterns, regex_patterns = load_ignore_patterns(cfg_dir) - - # Lade Symbol-Attribut- und Layer-Mapping symbol_attr_mapping = load_symbol_attribute_mapping(cfg_dir) symbol_layer_mapping = load_symbol_layer_mapping(cfg_dir) - # Parse Sprachen aus --translate Parameter (kommaseparierte Liste) + # Zielsprachen parsen (kommasepariert, dedupliziert) translation_languages = [] if args.translate: - translation_languages = [lang.strip().upper() for lang in args.translate.split(',')] - # Entferne Duplikate und behalte Reihenfolge seen = set() - translation_languages = [lang for lang in translation_languages if lang and not (lang in seen or seen.add(lang))] + translation_languages = [ + lang for lang in (l.strip().upper() for l in args.translate.split(',')) + if lang and not (lang in seen or seen.add(lang)) + ] - # Bestimme Quell-Attributnamen basierend auf Sprachcode + # Attribut- und Layer-Namen bestimmen source_attr_name = symbol_attr_mapping.get(args.lang_source, 'TEXT-D') - - # Für --todxf: verwende nur die erste Sprache als Ziel (backwards compatibility) target_attr_name = symbol_attr_mapping.get(translation_languages[0], 'TEXT-CS') if translation_languages else 'TEXT-CS' target_layer_name = symbol_layer_mapping.get(translation_languages[0]) if translation_languages else None @@ -1966,223 +2135,31 @@ if __name__ == '__main__': else: print(f" Ziel-Sprachen (--translate): {', '.join(translation_languages)}") - # Optional: Lade Übersetzungs-Configs für alle Sprachen - # translation_dicts_all ist ein Dict: {LANG: (multi, trigramme, bigramme, single), ...} + # Übersetzungs-Configs laden: {LANG: (multi, trigramme, bigramme, single), ...} translation_dicts_all = {} if translation_languages: translation_dir = check_environment_var('PROJECT_TRANSLATION') for lang in translation_languages: translation_dicts_all[lang] = load_translation_config(lang, translation_dir) - # Für backwards compatibility: wenn nur eine Sprache, setze translation_dicts wie früher + # Für backwards compatibility: bei genau einer Sprache flaches Dict bereitstellen translation_dicts = translation_dicts_all.get(translation_languages[0]) if len(translation_languages) == 1 else None if args.debug: print("DEBUG-Modus aktiviert") - # Spezieller Workflow: Re-Übersetzung einer JSON-Datei + # Workflow delegieren if args.retranslate_json: - print("=== Re-Übersetzung JSON-Datei ===\n") + _run_retranslate(args, work_dir, translation_dicts_all, translation_languages) - # Finde JSON-Datei - input_filename = args.retranslate_json - if not input_filename.lower().endswith('.json'): - print("Mit --retranslate-json nur .json Dateien erlaubt") - sys.exit(1) - - json_file = work_dir / input_filename - if not json_file.exists(): - json_file = Path(input_filename) - if not json_file.exists(): - print(f"JSON-Datei nicht gefunden: {input_filename}") - print(f" Geprüft in Work-Ordner: {work_dir / input_filename}") - print(f" Geprüft als vollständiger Pfad: {json_file}") - sys.exit(1) - - print(f"JSON-Datei: {json_file}") - print(f"Sprachen: {', '.join(translation_languages)}") - - # Re-übersetze JSON-Datei - retranslate_json_file(json_file, translation_dicts_all, translation_languages) - - print("\nVerarbeitung erfolgreich abgeschlossen.") - sys.exit(0) - - # Spezieller Workflow: DXF-zu-DXF Übersetzung if args.todxf: - print("=== DXF-zu-DXF Übersetzung ===\n") + _run_todxf(args, work_dir, translation_languages, wildcard_patterns, regex_patterns, + source_attr_name, target_attr_name, target_layer_name) - # Finde Quell-DXF - input_filename = args.filename - if not input_filename.lower().endswith(('.dxf', '.dwg')): - print("Mit --todxf nur .dxf oder .dwg Dateien als Quelle erlaubt") - sys.exit(1) + _run_extract_or_import(args, work_dir, wildcard_patterns, regex_patterns, + translation_dicts, translation_dicts_all, translation_languages, + export_types, source_attr_name) - source_dxf = work_dir / input_filename - if not source_dxf.exists(): - source_dxf = Path(input_filename) - if not source_dxf.exists(): - print(f"Quell-DXF nicht gefunden: {input_filename}") - print(f" Geprüft in Work-Ordner: {work_dir / input_filename}") - print(f" Geprüft als vollständiger Pfad: {source_dxf}") - sys.exit(1) - print(f"Quell-DXF: {source_dxf}") - - # Bestimme JSON-Datei mit Übersetzungen - if args.translation_json: - translation_json = work_dir / args.translation_json - if not translation_json.exists(): - translation_json = Path(args.translation_json) - if not translation_json.exists(): - print(f"Übersetzungs-JSON nicht gefunden: {args.translation_json}") - print(f" Geprüft in Work-Ordner: {work_dir / args.translation_json}") - print(f" Geprüft als vollständiger Pfad: {translation_json}") - sys.exit(1) - else: - # Standard: _texts.json im Work-Ordner - translation_json = work_dir / (Path(input_filename).stem + "_texts.json") - if not translation_json.exists(): - print(f"Standard-Übersetzungs-JSON nicht gefunden: {translation_json}") - print(f"Bitte erstellen Sie die Datei oder geben Sie eine alternative mit --translation-json an") - sys.exit(1) - - print(f"Übersetzungs-JSON: {translation_json}") - - # Lade Übersetzungen (verwende erste Sprache als Ziel-Sprache) - target_lang = translation_languages[0] if translation_languages else None - translations = load_translations_from_json(translation_json, target_lang) - - # Bestimme Ausgabe-DXF - output_dxf_name = args.todxf - if not output_dxf_name.lower().endswith(('.dxf', '.dwg')): - output_dxf_name += '.dxf' - output_dxf = work_dir / output_dxf_name - - print(f"Ziel-DXF: {output_dxf}") - - # Übersetze DXF (inkl. ggf. neuer TEXT-Attribute und Layer) - translate_dxf_texts( - source_dxf, - output_dxf, - translations, - wildcard_patterns, - regex_patterns, - source_attr_name, - target_attr_name, - target_layer_name, - ) - - print("\nVerarbeitung erfolgreich abgeschlossen.") - sys.exit(0) - - # Bestimme Input-Modus und Datei - if args.extract: - # DXF/DWG-Modus - input_filename = args.filename - file_extension = input_filename.lower() - - if not file_extension.endswith(('.dxf', '.dwg')): - print("Mit --extract nur .dxf oder .dwg Dateien erlaubt") - sys.exit(1) - - # Versuche zuerst Datei im Work-Ordner zu finden - filename = work_dir / input_filename - if not filename.exists(): - # Falls nicht gefunden, prüfe ob es ein vollständiger Pfad ist - filename = Path(input_filename) - if not filename.exists(): - print(f"Datei nicht gefunden: {input_filename}") - print(f" Geprüft in Work-Ordner: {work_dir / input_filename}") - print(f" Geprüft als vollständiger Pfad: {filename}") - sys.exit(1) - - print(f"Lese DXF Datei: {filename}") - all_texts = extract_text_from_dxf(filename, source_attr_name) - - elif args.fromtxt: - # TXT-Modus - input_filename = args.fromtxt - - if not input_filename.lower().endswith('.txt'): - print("Mit --fromtxt nur .txt Dateien erlaubt") - sys.exit(1) - - # Versuche zuerst Datei im Work-Ordner zu finden - filename = work_dir / input_filename - if not filename.exists(): - # Falls nicht gefunden, prüfe ob es ein vollständiger Pfad ist - filename = Path(input_filename) - if not filename.exists(): - print(f"Datei nicht gefunden: {input_filename}") - print(f" Geprüft in Work-Ordner: {work_dir / input_filename}") - print(f" Geprüft als vollständiger Pfad: {filename}") - sys.exit(1) - - print(f"Lese Textdatei: {filename}") - all_texts = read_texts_from_file(filename) - - else: # args.fromjson - # JSON-Modus - input_filename = args.fromjson - - if not input_filename.lower().endswith('.json'): - print("Mit --fromjson nur .json Dateien erlaubt") - sys.exit(1) - - # Versuche zuerst Datei im Work-Ordner zu finden - filename = work_dir / input_filename - if not filename.exists(): - # Falls nicht gefunden, prüfe ob es ein vollständiger Pfad ist - filename = Path(input_filename) - if not filename.exists(): - print(f"Datei nicht gefunden: {input_filename}") - print(f" Geprüft in Work-Ordner: {work_dir / input_filename}") - print(f" Geprüft als vollständiger Pfad: {filename}") - sys.exit(1) - - print(f"Lese JSON-Datei: {filename}") - all_texts = read_texts_from_json(filename) - - # Schritt 2: Texte filtern - texts, ignored_texts = filter_texts(all_texts, wildcard_patterns, regex_patterns, args.debug) - - # Mapping für Dateiendungen - extension_map = { - 'excel': '.xlsx', - 'json': '.json', - 'text': '.txt' - } - - # Export für jeden angegebenen Typ durchführen - print(f"\nExportiere in {len(export_types)} Format(e): {', '.join(export_types)}") - - for export_type in export_types: - # Erstelle Output-Dateinamen basierend auf Export-Typ - if args.outname: - # Verwende benutzerdefinierten Namen - base_name = args.outname - # Entferne existierende Endung falls vorhanden - for ext in extension_map.values(): - if base_name.endswith(ext): - base_name = base_name[:-len(ext)] - break - # Füge korrekte Endung hinzu - output_filename = f"{base_name}{extension_map[export_type]}" - else: - # Standardname basierend auf Eingabedatei - output_filename = f"{filename.stem}_texts{extension_map[export_type]}" - - output_path = work_dir / output_filename - - # Schreibe Texte in gewähltem Format - if export_type == 'excel': - write_texts_to_excel(texts, output_path, translation_dicts) - elif export_type == 'json': - write_texts_to_json(texts, ignored_texts, output_path, translation_dicts, translation_dicts_all, translation_languages) - else: # text - write_texts_to_text(texts, ignored_texts, output_path, translation_dicts) - - print(f" [OK] {export_type}: {output_path}") - - print("\nVerarbeitung erfolgreich abgeschlossen.") +if __name__ == '__main__': + main()