eigene Routine für die Ergänzung der Blockattribute für TEXT-CS geschrieben. Der Layer der Symbole für die jeweilige Zielsprache ins in translate.cfg einstellbar

This commit is contained in:
2025-12-04 12:38:16 +01:00
parent b10bd78b79
commit bf45f70e7c
2 changed files with 186 additions and 44 deletions
+175 -44
View File
@@ -36,10 +36,10 @@ def check_environment_var(env_str: str) -> Path:
def load_translation_config(translation_lang: str, translation_dir: Path) -> tuple[dict[str, str], dict[str, str], dict[str, str], dict[str, str]]:
"""
Lädt eine Übersetzungs-Config-Datei (z.B. CSK.cfg, EN.cfg, FR.cfg).
Lädt eine Übersetzungs-Config-Datei (z.B. CS.cfg, EN.cfg, FR.cfg).
Args:
translation_lang: Sprach-Code (z.B. "CSK", "EN", "FR")
translation_lang: Sprach-Code (z.B. "CS", "EN", "FR")
translation_dir: Verzeichnis mit Übersetzungs-Configs (aus PROJECT_TRANSLATION)
Returns:
@@ -107,6 +107,49 @@ def load_translation_config(translation_lang: str, translation_dir: Path) -> tup
return multi, trigramme, bigramme, single
def load_symbol_attribute_mapping(cfg_path: Path) -> dict[str, str]:
"""
Liest die Zuordnung von Sprachcodes zu Symbol-Attributnamen aus der translator.cfg Datei.
Args:
cfg_path: Pfad zum Konfigurationsverzeichnis (PROJECT_CFG)
Returns:
Dictionary mit Sprachcode -> Attributname Mapping (z.B. "DE" -> "TEXT-D", "EN" -> "TEXT-EN")
"""
config_file = cfg_path / "translator.cfg"
if not config_file.exists():
print(f"FEHLER: Konfigurationsdatei nicht gefunden: {config_file}")
print(f"Bitte erstellen Sie die Datei 'translator.cfg' im Verzeichnis: {cfg_path}")
sys.exit(1)
# 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:
print(f"Fehler beim Lesen der Konfigurationsdatei: {e}")
sys.exit(1)
symbol_mapping = {}
# Lade Symbol-Attribut-Zuordnung
if config.has_section('symbolattribute'):
for key in config['symbolattribute']:
attr_name = config['symbolattribute'][key].strip()
if attr_name and not attr_name.startswith('#'):
symbol_mapping[key] = attr_name
print(f"\nGeladene Symbol-Attribut-Zuordnungen: {len(symbol_mapping)}")
for lang, attr in symbol_mapping.items():
print(f" {lang} -> {attr}")
return symbol_mapping
def load_ignore_patterns(cfg_path: Path) -> tuple[list[str], list[str]]:
"""
Liest die ignore_pattern aus der translator.cfg Datei.
@@ -272,14 +315,15 @@ def should_ignore_text(text: str, wildcard_patterns: list[str], regex_patterns:
return False
def extract_text_from_dxf(filename: Path) -> list[str]:
def extract_text_from_dxf(filename: Path, text_attr_name: str = 'TEXT-D') -> list[str]:
"""
Extrahiert alle TEXT und MTEXT Objekte sowie TEXT-D Attribute aus Blöcken einer DXF Datei.
Extrahiert alle TEXT und MTEXT Objekte sowie TEXT-{attr} Attribute aus Blöcken einer DXF Datei.
Verwendet ein Set, um Duplikate zu vermeiden.
Filtert leere Texte heraus.
Args:
filename: Pfad zur DXF Datei
text_attr_name: Name des Text-Attributs in Blöcken (z.B. "TEXT-D", "TEXT-EN", "TEXT-CS"). Standard: "TEXT-D"
Returns:
Alphabetisch sortierte Liste aller gefundenen Texte (ohne Duplikate, ohne leere Texte)
@@ -290,8 +334,8 @@ def extract_text_from_dxf(filename: Path) -> list[str]:
block_count = 0
empty_count = 0
# Attribute die ein Block haben muss, damit TEXT-D extrahiert wird
required_attributes = {'IO', 'ID', 'VERW', 'BEZEICHNUNG', 'KENNZEICHNUNG', 'TEXT-D'}
# Attribute die ein Block haben muss, damit das Text-Attribut extrahiert wird
required_attributes = {'IO', 'ID', 'VERW', 'BEZEICHNUNG', 'KENNZEICHNUNG', text_attr_name}
try:
doc = ezdxf.readfile(filename)
@@ -332,7 +376,7 @@ def extract_text_from_dxf(filename: Path) -> list[str]:
for attrib in entity.attribs:
block_attributes.add(attrib.dxf.tag)
if attrib.dxf.tag == 'TEXT-D':
if attrib.dxf.tag == text_attr_name:
text_d_value = attrib.dxf.text.strip() if attrib.dxf.text else None
# Prüfe ob Block alle erforderlichen Attribute hat
@@ -351,7 +395,7 @@ def extract_text_from_dxf(filename: Path) -> list[str]:
print(f"Anzahl gefundener TEXT Objekte: {text_count}")
print(f"Anzahl gefundener MTEXT Objekte: {mtext_count}")
print(f"Anzahl gefundener TEXT-D in Blöcken: {block_count}")
print(f"Anzahl gefundener {text_attr_name} in Blöcken: {block_count}")
print(f"Leere Texte ignoriert: {empty_count}")
total_count = text_count + mtext_count + block_count
print(f"Duplikate entfernt: {total_count - empty_count - len(texts)}")
@@ -543,10 +587,93 @@ def load_translations_from_json(filename: Path) -> dict[str, str]:
sys.exit(1)
def translate_dxf_texts(input_dxf: Path, output_dxf: Path, translations: dict[str, str],
wildcard_patterns: list[str], regex_patterns: list[str]):
def translate_block_attribute(entity, translations: dict[str, str],
wildcard_patterns: list[str], regex_patterns: list[str],
required_attributes: set[str], text_source_attr: str, text_target_attr: str) -> tuple[int, int, int]:
"""
Übersetzt alle TEXT, MTEXT Objekte und TEXT-D Attribute in Blöcken einer DXF-Datei.
Übersetzt Text-Attribute eines einzelnen Block-Inserts (INSERT entity).
Args:
entity: INSERT entity aus der DXF-Datei
translations: Dictionary mit source -> target Mappings
wildcard_patterns: Wildcard-Muster zum Filtern
regex_patterns: Regex-Muster zum Filtern
required_attributes: Set mit erforderlichen Attributnamen für den Block
text_source_attr: Quell-Attributname (z.B. "TEXT-D", "TEXT-EN")
text_target_attr: Ziel-Attributname (z.B. "TEXT-EN", "TEXT-CS")
Returns:
Tuple mit (translated_count, skipped_count, ignored_count) für diesen Block
"""
if not entity.has_attrib:
return 0, 0, 0
# Sammle alle vorhandenen Attribute des Blocks
block_attributes = {}
text_source_attrib = None
text_target_attrib = None
for attrib in entity.attribs:
block_attributes[attrib.dxf.tag] = attrib
if attrib.dxf.tag == text_source_attr:
text_source_attrib = attrib
elif attrib.dxf.tag == text_target_attr:
text_target_attrib = attrib
# Prüfe ob Block alle erforderlichen Attribute hat
if not (required_attributes.issubset(block_attributes.keys()) and text_source_attrib):
return 0, 0, 0
original_text = text_source_attrib.dxf.text.strip() if text_source_attrib.dxf.text else None
if not original_text:
return 0, 0, 0
# Prüfe ob Text ignoriert werden soll
if should_ignore_text(original_text, wildcard_patterns, regex_patterns):
return 0, 0, 1
# Suche Übersetzung
if original_text not in translations:
return 0, 1, 0
# Schreibe Übersetzung in Ziel-Attribut
if text_target_attrib:
# Ziel-Attribut existiert bereits, aktualisiere es
text_target_attrib.dxf.text = translations[original_text]
else:
# Ziel-Attribut existiert nicht, erstelle es basierend auf Quell-Attribut
# Kopiere alle Eigenschaften vom Quell-Attribut
new_attrib = entity.add_attrib(
tag=text_target_attr,
text=translations[original_text],
insert=text_source_attrib.dxf.insert
)
# Kopiere weitere Eigenschaften
if text_source_attrib.dxf.hasattr('height'):
new_attrib.dxf.height = text_source_attrib.dxf.height
if text_source_attrib.dxf.hasattr('style'):
new_attrib.dxf.style = text_source_attrib.dxf.style
if text_source_attrib.dxf.hasattr('rotation'):
new_attrib.dxf.rotation = text_source_attrib.dxf.rotation
if text_source_attrib.dxf.hasattr('width'):
new_attrib.dxf.width = text_source_attrib.dxf.width
if text_source_attrib.dxf.hasattr('layer'):
new_attrib.dxf.layer = text_source_attrib.dxf.layer
return 1, 0, 0
def translate_dxf_texts(input_dxf: Path, output_dxf: Path, translations: dict[str, str],
wildcard_patterns: list[str], regex_patterns: list[str],
text_source_attr: str = 'TEXT-D', text_target_attr: str = 'TEXT-CS'):
"""
Übersetzt alle TEXT, MTEXT Objekte und Text-Attribute in Blöcken einer DXF-Datei.
Für Block-Attribute:
- Liest die Übersetzung aus dem Quell-Attribut (z.B. TEXT-D)
- Schreibt die Übersetzung in das Ziel-Attribut (z.B. TEXT-EN, TEXT-CS) (erstellt das Attribut falls nicht vorhanden)
- Das Quell-Attribut bleibt unverändert (Original wird bewahrt)
Args:
input_dxf: Pfad zur Quell-DXF
@@ -554,9 +681,11 @@ def translate_dxf_texts(input_dxf: Path, output_dxf: Path, translations: dict[st
translations: Dictionary mit source -> target Mappings
wildcard_patterns: Wildcard-Muster zum Filtern
regex_patterns: Regex-Muster zum Filtern
text_source_attr: Quell-Attributname für Texte in Blöcken (z.B. "TEXT-D", "TEXT-EN"). Standard: "TEXT-D"
text_target_attr: Ziel-Attributname für Übersetzungen in Blöcken (z.B. "TEXT-EN", "TEXT-CS"). Standard: "TEXT-CS"
"""
# Attribute die ein Block haben muss, damit TEXT-D übersetzt wird
required_attributes = {'IO', 'ID', 'VERW', 'BEZEICHNUNG', 'KENNZEICHNUNG', 'TEXT-D'}
# Attribute die ein Block haben muss, damit das Text-Attribut übersetzt wird
required_attributes = {'IO', 'ID', 'VERW', 'BEZEICHNUNG', 'KENNZEICHNUNG', text_source_attr}
try:
doc = ezdxf.readfile(input_dxf)
@@ -606,34 +735,15 @@ def translate_dxf_texts(input_dxf: Path, output_dxf: Path, translations: dict[st
else:
mtext_skipped += 1
# Übersetze TEXT-D in Blöcken
# Übersetze Text-Attribute in Blöcken
for entity in msp.query('INSERT'):
if entity.has_attrib:
# Sammle alle vorhandenen Attribute des Blocks
block_attributes = set()
text_d_attrib = None
for attrib in entity.attribs:
block_attributes.add(attrib.dxf.tag)
if attrib.dxf.tag == 'TEXT-D':
text_d_attrib = attrib
# Prüfe ob Block alle erforderlichen Attribute hat
if required_attributes.issubset(block_attributes) and text_d_attrib:
original_text = text_d_attrib.dxf.text.strip() if text_d_attrib.dxf.text else None
if original_text:
# Prüfe ob Text ignoriert werden soll
if should_ignore_text(original_text, wildcard_patterns, regex_patterns):
block_ignored += 1
continue
# Suche Übersetzung
if original_text in translations:
text_d_attrib.dxf.text = translations[original_text]
block_translated += 1
else:
block_skipped += 1
translated, skipped, ignored = translate_block_attribute(
entity, translations, wildcard_patterns, regex_patterns,
required_attributes, text_source_attr, text_target_attr
)
block_translated += translated
block_skipped += skipped
block_ignored += ignored
# Speichere neue DXF
doc.saveas(output_dxf)
@@ -645,8 +755,9 @@ def translate_dxf_texts(input_dxf: Path, output_dxf: Path, translations: dict[st
print(f"\nDXF-Übersetzung abgeschlossen:")
print(f" TEXT übersetzt: {text_translated}, übersprungen: {text_skipped}, ignoriert: {text_ignored}")
print(f" MTEXT übersetzt: {mtext_translated}, übersprungen: {mtext_skipped}, ignoriert: {mtext_ignored}")
print(f" TEXT-D (Blöcke) übersetzt: {block_translated}, übersprungen: {block_skipped}, ignoriert: {block_ignored}")
print(f" {text_target_attr} (Blöcke) erstellt/aktualisiert: {block_translated}, übersprungen: {block_skipped}, ignoriert: {block_ignored}")
print(f" Gesamt: {translated_count} übersetzt, {skipped_count} übersprungen, {ignored_count} ignoriert")
print(f" Hinweis: Übersetzungen in Blöcken wurden in {text_target_attr} geschrieben, TEXT-D bleibt unverändert")
print(f" Ausgabe-Datei: {output_dxf}")
except Exception as e:
@@ -904,7 +1015,14 @@ if __name__ == '__main__':
'--translate',
action='store',
metavar='LANG',
help='Übersetzt Texte mit Config aus translation/<LANG>.cfg (z.B. CSK, EN, FR)'
help='Übersetzt Texte mit Config aus translation/<LANG>.cfg (z.B. CS, EN, FR)'
)
parser.add_argument(
'--lang_source',
action='store',
default='DE',
metavar='LANG',
help='Quellsprache für Attribut in Blöcken (z.B. DE->TEXT-D, EN->TEXT-EN, CS->TEXT-CS). Standard: DE'
)
parser.add_argument(
'--translation-json',
@@ -942,6 +1060,18 @@ if __name__ == '__main__':
# Lade Ignore-Muster aus Konfigurationsdatei
wildcard_patterns, regex_patterns = load_ignore_patterns(cfg_dir)
# Lade Symbol-Attribut-Mapping
symbol_attr_mapping = load_symbol_attribute_mapping(cfg_dir)
# Bestimme Quell- und Ziel-Attributnamen basierend auf Sprachcodes
source_attr_name = symbol_attr_mapping.get(args.lang_source, 'TEXT-D')
target_attr_name = symbol_attr_mapping.get(args.translate, 'TEXT-CS') if args.translate else 'TEXT-CS'
print(f"\nVerwendete Attribut-Namen:")
print(f" Quell-Attribut (--lang_source {args.lang_source}): {source_attr_name}")
if args.translate:
print(f" Ziel-Attribut (--translate {args.translate}): {target_attr_name}")
# Optional: Lade Übersetzungs-Config
translation_dicts = None
if args.translate:
@@ -1004,7 +1134,8 @@ if __name__ == '__main__':
print(f"Ziel-DXF: {output_dxf}")
# Übersetze DXF
translate_dxf_texts(source_dxf, output_dxf, translations, wildcard_patterns, regex_patterns)
translate_dxf_texts(source_dxf, output_dxf, translations, wildcard_patterns, regex_patterns,
source_attr_name, target_attr_name)
print("\nVerarbeitung erfolgreich abgeschlossen.")
sys.exit(0)
@@ -1031,7 +1162,7 @@ if __name__ == '__main__':
sys.exit(1)
print(f"Lese DXF Datei: {filename}")
all_texts = extract_text_from_dxf(filename)
all_texts = extract_text_from_dxf(filename, source_attr_name)
elif args.fromtxt:
# TXT-Modus