log dir dazu gemacht. Ausgabe der Übersetzuung als json möglich
This commit is contained in:
+197
-37
@@ -1,4 +1,5 @@
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
@@ -15,7 +16,8 @@ Dieses Programm:
|
||||
- liest eine DXF Datei und sammelt alle TEXT und MTEXT Objekte
|
||||
ODER liest Texte aus einer TXT Datei (ein Text pro Zeile)
|
||||
- filtert die Texte basierend auf Mustern in translator.cfg
|
||||
- schreibt die Ergebnisse in eine Excel- oder Textdatei
|
||||
- übersetzt optional Texte mit Trigrammen, Bigrammen und Einzelwörtern aus translation/<LANG>.cfg
|
||||
- schreibt die Ergebnisse in eine Excel- oder JSON-Datei
|
||||
- speichert die Ausgabedatei im PROJECT_WORK Verzeichnis
|
||||
"""
|
||||
|
||||
@@ -30,6 +32,69 @@ def check_environment_var(env_str: str) -> Path:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def load_translation_config(translation_lang: str, translation_dir: Path) -> tuple[dict[str, str], dict[str, str], dict[str, str]]:
|
||||
"""
|
||||
Lädt eine Übersetzungs-Config-Datei (z.B. CSK.cfg, EN.cfg, FR.cfg).
|
||||
|
||||
Args:
|
||||
translation_lang: Sprach-Code (z.B. "CSK", "EN", "FR")
|
||||
translation_dir: Verzeichnis mit Übersetzungs-Configs (aus PROJECT_TRANSLATION)
|
||||
|
||||
Returns:
|
||||
Tuple mit (Trigramme-Dict, Bigramme-Dict, Single-Dict)
|
||||
"""
|
||||
config_file = translation_dir / f"{translation_lang}.cfg"
|
||||
|
||||
if not config_file.exists():
|
||||
print(f"FEHLER: Übersetzungs-Konfiguration nicht gefunden: {config_file}")
|
||||
sys.exit(1)
|
||||
|
||||
# RawConfigParser mit case-sensitive Keys
|
||||
config = RawConfigParser()
|
||||
# Überschreibe optionxform um Groß-/Kleinschreibung zu erhalten
|
||||
config.optionxform = str
|
||||
try:
|
||||
config.read(config_file, encoding='utf-8')
|
||||
except Exception as e:
|
||||
print(f"Fehler beim Lesen der Übersetzungs-Konfiguration: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
trigramme = {}
|
||||
bigramme = {}
|
||||
single = {}
|
||||
|
||||
# Lade Trigramme
|
||||
if config.has_section('trigramme'):
|
||||
for key in config['trigramme']:
|
||||
original = key
|
||||
translation = config['trigramme'][key]
|
||||
if translation and not translation.startswith('#'):
|
||||
trigramme[original] = translation
|
||||
|
||||
# Lade Bigramme
|
||||
if config.has_section('bigramme'):
|
||||
for key in config['bigramme']:
|
||||
original = key
|
||||
translation = config['bigramme'][key]
|
||||
if translation and not translation.startswith('#'):
|
||||
bigramme[original] = translation
|
||||
|
||||
# Lade Single
|
||||
if config.has_section('single'):
|
||||
for key in config['single']:
|
||||
original = key
|
||||
translation = config['single'][key]
|
||||
if translation and not translation.startswith('#'):
|
||||
single[original] = translation
|
||||
|
||||
print(f"\nÜbersetzungs-Config geladen ({translation_lang}):")
|
||||
print(f" Trigramme: {len(trigramme)}")
|
||||
print(f" Bigramme: {len(bigramme)}")
|
||||
print(f" Einzelwörter: {len(single)}")
|
||||
|
||||
return trigramme, bigramme, single
|
||||
|
||||
|
||||
def load_ignore_patterns(cfg_path: Path) -> tuple[list[str], list[str]]:
|
||||
"""
|
||||
Liest die ignore_pattern aus der translator.cfg Datei.
|
||||
@@ -50,6 +115,8 @@ def load_ignore_patterns(cfg_path: Path) -> tuple[list[str], list[str]]:
|
||||
|
||||
# RawConfigParser verhindert dass Backslashes in Regex-Mustern escaped werden
|
||||
config = RawConfigParser()
|
||||
# Überschreibe optionxform um Groß-/Kleinschreibung zu erhalten
|
||||
config.optionxform = str
|
||||
try:
|
||||
config.read(config_file, encoding='utf-8')
|
||||
except Exception as e:
|
||||
@@ -84,6 +151,50 @@ def load_ignore_patterns(cfg_path: Path) -> tuple[list[str], list[str]]:
|
||||
return wildcard_patterns, regex_patterns
|
||||
|
||||
|
||||
def translate_text(text: str, trigramme: dict[str, str], bigramme: dict[str, str], single: dict[str, str]) -> str:
|
||||
"""
|
||||
Übersetzt einen Text durch Ersetzen von Trigrammen, Bigrammen und Einzelwörtern.
|
||||
Ersetzungen erfolgen in dieser Reihenfolge: Trigramme → Bigramme → Einzelwörter.
|
||||
|
||||
Args:
|
||||
text: Zu übersetzender Text
|
||||
trigramme: Dictionary mit Drei-Wort-Phrasen
|
||||
bigramme: Dictionary mit Zwei-Wort-Phrasen
|
||||
single: Dictionary mit Einzelwörtern
|
||||
|
||||
Returns:
|
||||
Übersetzter Text (leer wenn keine Übersetzung gefunden wurde)
|
||||
"""
|
||||
translated_text = text
|
||||
has_translation = False
|
||||
|
||||
# 1. Trigramme ersetzen (längste Phrasen zuerst)
|
||||
for original, translation in trigramme.items():
|
||||
if original in translated_text:
|
||||
translated_text = translated_text.replace(original, translation)
|
||||
has_translation = True
|
||||
|
||||
# 2. Bigramme ersetzen
|
||||
for original, translation in bigramme.items():
|
||||
if original in translated_text:
|
||||
translated_text = translated_text.replace(original, translation)
|
||||
has_translation = True
|
||||
|
||||
# 3. Einzelwörter ersetzen
|
||||
words = translated_text.split()
|
||||
translated_words = []
|
||||
for word in words:
|
||||
if word in single:
|
||||
translated_words.append(single[word])
|
||||
has_translation = True
|
||||
else:
|
||||
translated_words.append(word)
|
||||
translated_text = ' '.join(translated_words)
|
||||
|
||||
# Gib nur übersetzten Text zurück wenn tatsächlich etwas übersetzt wurde
|
||||
return translated_text if has_translation else ""
|
||||
|
||||
|
||||
def should_ignore_text(text: str, wildcard_patterns: list[str], regex_patterns: list[str], debug: bool = False) -> bool:
|
||||
"""
|
||||
Prüft ob ein Text ignoriert werden soll basierend auf den Mustern.
|
||||
@@ -245,13 +356,16 @@ def read_texts_from_file(filename: Path) -> list[str]:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def write_texts_to_excel(texts: list[str], output_path: Path):
|
||||
def write_texts_to_excel(texts: list[str], output_path: Path,
|
||||
translation_dicts: tuple[dict[str, str], dict[str, str], dict[str, str]] = None):
|
||||
"""
|
||||
Schreibt die Texte in die erste Spalte einer Excel-Datei.
|
||||
Optional: Schreibt Übersetzungen in die zweite Spalte.
|
||||
|
||||
Args:
|
||||
texts: Liste der zu schreibenden Texte
|
||||
output_path: Pfad zur Ausgabe-Excel-Datei
|
||||
translation_dicts: Optional: Tuple mit (Trigramme, Bigramme, Single) für Übersetzungen
|
||||
"""
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
@@ -259,61 +373,95 @@ def write_texts_to_excel(texts: list[str], output_path: Path):
|
||||
|
||||
# Kopfzeile
|
||||
ws['A1'] = "Text"
|
||||
if translation_dicts:
|
||||
ws['B1'] = "Übersetzung"
|
||||
|
||||
# Texte in die erste Spalte schreiben
|
||||
# Texte schreiben
|
||||
for idx, text in enumerate(texts, start=2):
|
||||
ws[f'A{idx}'] = text
|
||||
|
||||
# Spaltenbreite anpassen
|
||||
# Optional: Übersetzung in zweite Spalte
|
||||
if translation_dicts:
|
||||
trigramme, bigramme, single = translation_dicts
|
||||
translated = translate_text(text, trigramme, bigramme, single)
|
||||
if translated: # Nur wenn Übersetzung gefunden wurde
|
||||
ws[f'B{idx}'] = translated
|
||||
# Sonst bleibt die Zelle leer
|
||||
|
||||
# Spaltenbreiten anpassen
|
||||
ws.column_dimensions['A'].width = 50
|
||||
if translation_dicts:
|
||||
ws.column_dimensions['B'].width = 50
|
||||
|
||||
try:
|
||||
wb.save(output_path)
|
||||
print(f"Excel-Datei erfolgreich erstellt: {output_path}")
|
||||
if translation_dicts:
|
||||
print(f" Mit Übersetzungen in Spalte B")
|
||||
except Exception as e:
|
||||
print(f"Fehler beim Schreiben der Excel-Datei: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def write_texts_to_textfile(texts: list[str], ignored_texts: list[str], output_path: Path):
|
||||
def write_texts_to_json(texts: list[str], ignored_texts: list[str], output_path: Path,
|
||||
translation_dicts: tuple[dict[str, str], dict[str, str], dict[str, str]] = None):
|
||||
"""
|
||||
Schreibt die Texte in eine UTF-8 Textdatei.
|
||||
Jeder Text wird in einer eigenen Zeile geschrieben (auch Multiline-Texte).
|
||||
Die Datei enthält zwei Teile: akzeptierte und ignorierte Texte.
|
||||
Schreibt die Texte in eine JSON-Datei mit drei Blöcken.
|
||||
|
||||
Args:
|
||||
texts: Liste der akzeptierten Texte
|
||||
ignored_texts: Liste der ignorierten Texte
|
||||
output_path: Pfad zur Ausgabe-Textdatei
|
||||
output_path: Pfad zur Ausgabe-JSON-Datei
|
||||
translation_dicts: Optional: Tuple mit (Trigramme, Bigramme, Single) für Übersetzungen
|
||||
"""
|
||||
output_data = {}
|
||||
|
||||
# Block 1: Übersetzte Texte (als Liste von Dicts mit source/target)
|
||||
# Block 2: Nicht übersetzte Texte (nur akzeptierte Texte ohne Übersetzung)
|
||||
if translation_dicts:
|
||||
trigramme, bigramme, single = translation_dicts
|
||||
translations = []
|
||||
untranslated_texts = []
|
||||
|
||||
for text in texts:
|
||||
translated = translate_text(text, trigramme, bigramme, single)
|
||||
if translated: # Übersetzung vorhanden
|
||||
translations.append({
|
||||
"source": text,
|
||||
"target": translated
|
||||
})
|
||||
else: # Keine Übersetzung vorhanden
|
||||
untranslated_texts.append(text)
|
||||
|
||||
output_data["translations"] = translations
|
||||
output_data["texts"] = untranslated_texts
|
||||
else:
|
||||
# Ohne Übersetzungs-Config: alle akzeptierten Texte sind nicht übersetzt
|
||||
output_data["translations"] = []
|
||||
output_data["texts"] = texts
|
||||
|
||||
# Block 3: Ignorierte Texte (als Liste)
|
||||
output_data["ignored"] = ignored_texts
|
||||
|
||||
try:
|
||||
with open(output_path, 'w', encoding='utf-8') as f:
|
||||
# Schreibe akzeptierte Texte
|
||||
for text in texts:
|
||||
# Multiline-Texte werden in einer Zeile geschrieben (Zeilenumbrüche ersetzen)
|
||||
text_single_line = text.replace('\n', '\\n').replace('\r', '\\r')
|
||||
f.write(text_single_line + '\n')
|
||||
json.dump(output_data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
# Trennzeile
|
||||
f.write('\n-- durch translator.cfg ausgeschlossen --\n\n')
|
||||
|
||||
# Schreibe ignorierte Texte
|
||||
for text in ignored_texts:
|
||||
# Multiline-Texte werden in einer Zeile geschrieben (Zeilenumbrüche ersetzen)
|
||||
text_single_line = text.replace('\n', '\\n').replace('\r', '\\r')
|
||||
f.write(text_single_line + '\n')
|
||||
|
||||
print(f"Textdatei erfolgreich erstellt: {output_path}")
|
||||
print(f" Akzeptierte Texte: {len(texts)}")
|
||||
print(f"JSON-Datei erfolgreich erstellt: {output_path}")
|
||||
if translation_dicts:
|
||||
print(f" Übersetzungen: {len(output_data['translations'])}")
|
||||
print(f" Nicht übersetzte Texte: {len(output_data['texts'])}")
|
||||
else:
|
||||
print(f" Akzeptierte Texte: {len(output_data['texts'])}")
|
||||
print(f" Ignorierte Texte: {len(ignored_texts)}")
|
||||
except Exception as e:
|
||||
print(f"Fehler beim Schreiben der Textdatei: {e}")
|
||||
print(f"Fehler beim Schreiben der JSON-Datei: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Extrahiert TEXT und MTEXT Objekte aus einer DXF Datei oder filtert Texte aus einer TXT Datei',
|
||||
description='Extrahiert TEXT und MTEXT Objekte aus einer DXF Datei oder filtert Texte aus einer TXT Datei. Optional mit Übersetzungsfunktion.',
|
||||
prog='translate'
|
||||
)
|
||||
|
||||
@@ -352,8 +500,14 @@ if __name__ == '__main__':
|
||||
'-t', '--export-type',
|
||||
action='store',
|
||||
default='excel',
|
||||
choices=['excel', 'text'],
|
||||
help='Exportformat: excel (Standard) oder text (UTF-8 Textdatei)'
|
||||
choices=['excel', 'json'],
|
||||
help='Exportformat: excel (Standard) oder json (JSON-Datei)'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--translate',
|
||||
action='store',
|
||||
metavar='LANG',
|
||||
help='Übersetzt Texte mit Config aus translation/<LANG>.cfg (z.B. CSK, EN, FR)'
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
@@ -373,6 +527,12 @@ if __name__ == '__main__':
|
||||
# Lade Ignore-Muster aus Konfigurationsdatei
|
||||
wildcard_patterns, regex_patterns = load_ignore_patterns(cfg_dir)
|
||||
|
||||
# Optional: Lade Übersetzungs-Config
|
||||
translation_dicts = None
|
||||
if args.translate:
|
||||
translation_dir = check_environment_var('PROJECT_TRANSLATION')
|
||||
translation_dicts = load_translation_config(args.translate, translation_dir)
|
||||
|
||||
if args.debug:
|
||||
print("DEBUG-Modus aktiviert")
|
||||
|
||||
@@ -433,22 +593,22 @@ if __name__ == '__main__':
|
||||
if args.export_type == 'excel':
|
||||
if not output_filename.endswith('.xlsx'):
|
||||
output_filename += '.xlsx'
|
||||
else: # text
|
||||
if not output_filename.endswith('.txt'):
|
||||
output_filename += '.txt'
|
||||
else: # json
|
||||
if not output_filename.endswith('.json'):
|
||||
output_filename += '.json'
|
||||
else:
|
||||
# Standardname basierend auf Eingabedatei
|
||||
if args.export_type == 'excel':
|
||||
output_filename = filename.stem + "_texts.xlsx"
|
||||
else: # text
|
||||
output_filename = filename.stem + "_texts.txt"
|
||||
else: # json
|
||||
output_filename = filename.stem + "_texts.json"
|
||||
|
||||
output_path = work_dir / output_filename
|
||||
|
||||
# Schreibe Texte in gewähltem Format
|
||||
if args.export_type == 'excel':
|
||||
write_texts_to_excel(texts, output_path)
|
||||
else: # text
|
||||
write_texts_to_textfile(texts, ignored_texts, output_path)
|
||||
write_texts_to_excel(texts, output_path, translation_dicts)
|
||||
else: # json
|
||||
write_texts_to_json(texts, ignored_texts, output_path, translation_dicts)
|
||||
|
||||
print("Verarbeitung erfolgreich abgeschlossen.")
|
||||
|
||||
Reference in New Issue
Block a user