argument --retranslate-txt impl. Neue Routinen _parse_txt_sections _run_retranslate_txt
This commit is contained in:
@@ -64,6 +64,8 @@ KR_code = KR-M*
|
|||||||
#h1875ff = ^\\H1.875x;.*$
|
#h1875ff = ^\\H1.875x;.*$
|
||||||
#millimeter = ^\d+\s*mm$
|
#millimeter = ^\d+\s*mm$
|
||||||
#m_numbers = ^M\d{3}$
|
#m_numbers = ^M\d{3}$
|
||||||
|
ET200_UC =\+UC\d{4}\\P.*3 x ET200 SP DI16 \d{3}\.\d - \d{3}\.\d
|
||||||
|
MTEXT_UZ =\\H1\.875x.*\+UZ\d{4}
|
||||||
|
|
||||||
# Koordinaten mit Pfeilen
|
# Koordinaten mit Pfeilen
|
||||||
coord_arrows = ^\+\d{1,3}(?:[.,]\d{3})\s*(?:<<<|>>>)\s*\+\d{1,3}(?:[.,]\d{3})$
|
coord_arrows = ^\+\d{1,3}(?:[.,]\d{3})\s*(?:<<<|>>>)\s*\+\d{1,3}(?:[.,]\d{3})$
|
||||||
|
|||||||
+209
-1
@@ -32,9 +32,10 @@ SCHRITT 1 – Texte aus DXF extrahieren und Roh-JSON erzeugen
|
|||||||
Optional: zusätzliche Ausgabe als Excel oder Text (-t excel,json,text).
|
Optional: zusätzliche Ausgabe als Excel oder Text (-t excel,json,text).
|
||||||
|
|
||||||
───────────────────────────────────────────────────────────────────────────────
|
───────────────────────────────────────────────────────────────────────────────
|
||||||
SCHRITT 2a – JSON auto-übersetzen (Drop auf tr2dxf_CS.bat / tr2dxf_EN.bat)
|
SCHRITT 2a – JSON/TXT auto-übersetzen (Drop auf tr2dxf_CS.bat / tr2dxf_EN.bat)
|
||||||
───────────────────────────────────────────────────────────────────────────────
|
───────────────────────────────────────────────────────────────────────────────
|
||||||
--retranslate-json myfile_texts.json --translate CS[,EN,...]
|
--retranslate-json myfile_texts.json --translate CS[,EN,...]
|
||||||
|
--retranslate-txt myfile_texts.txt --translate CS
|
||||||
|
|
||||||
Liest eine bestehende JSON-Datei (Legacy-Flat oder Multilang), übersetzt
|
Liest eine bestehende JSON-Datei (Legacy-Flat oder Multilang), übersetzt
|
||||||
alle Texte aus 'untranslated' automatisch und verschiebt Treffer nach
|
alle Texte aus 'untranslated' automatisch und verschiebt Treffer nach
|
||||||
@@ -1937,6 +1938,201 @@ def resolve_input_file(name: str, work_dir: Path, label: str = "Datei") -> Path:
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_txt_sections(filename: Path) -> tuple[list[tuple[str, str]], list[str], list[str]]:
|
||||||
|
"""
|
||||||
|
Parst eine Textdatei im Format von write_texts_to_text.
|
||||||
|
|
||||||
|
Erkannte Abschnitte:
|
||||||
|
- "--- translations:" → Zeilen im Format "source -> target"
|
||||||
|
- "--- untranslated:" → einfache Texte (eine pro Zeile)
|
||||||
|
- "--- ignored:" → einfache Texte (eine pro Zeile)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filename: Pfad zur Textdatei
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple (translations, untranslated, ignored):
|
||||||
|
- translations: Liste von (source, target) Tupeln
|
||||||
|
- untranslated: Liste von source-Texten ohne Übersetzung
|
||||||
|
- ignored: Liste von ignorierten Texten
|
||||||
|
"""
|
||||||
|
translations: list[tuple[str, str]] = []
|
||||||
|
untranslated: list[str] = []
|
||||||
|
ignored: list[str] = []
|
||||||
|
current_section: str | None = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(filename, 'r', encoding='utf-8') as f:
|
||||||
|
for line in f:
|
||||||
|
stripped = line.rstrip('\n\r').strip()
|
||||||
|
|
||||||
|
if stripped.startswith('---'):
|
||||||
|
low = stripped.lower()
|
||||||
|
if 'translations' in low:
|
||||||
|
current_section = 'translations'
|
||||||
|
elif 'untranslated' in low:
|
||||||
|
current_section = 'untranslated'
|
||||||
|
elif 'ignored' in low:
|
||||||
|
current_section = 'ignored'
|
||||||
|
else:
|
||||||
|
current_section = None
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not stripped or current_section is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if current_section == 'translations':
|
||||||
|
if ' -> ' in stripped:
|
||||||
|
source, target = stripped.split(' -> ', 1)
|
||||||
|
translations.append((source, target))
|
||||||
|
else:
|
||||||
|
# Eintrag ohne Ziel → als untranslated behandeln
|
||||||
|
untranslated.append(stripped)
|
||||||
|
elif current_section == 'untranslated':
|
||||||
|
untranslated.append(stripped)
|
||||||
|
elif current_section == 'ignored':
|
||||||
|
ignored.append(stripped)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Fehler beim Lesen der Textdatei: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
return translations, untranslated, ignored
|
||||||
|
|
||||||
|
|
||||||
|
def retranslate_txt_file(txt_path: Path,
|
||||||
|
translation_dicts_all: dict[str, tuple],
|
||||||
|
translation_languages: list[str],
|
||||||
|
wildcard_patterns: list[str] | None = None,
|
||||||
|
regex_patterns: list[str] | None = None) -> None:
|
||||||
|
"""
|
||||||
|
Re-übersetzt eine Textdatei im Format von write_texts_to_text.
|
||||||
|
Überschreibt die ursprüngliche Datei mit aktualisierten Inhalten.
|
||||||
|
|
||||||
|
Verarbeitungsreihenfolge:
|
||||||
|
1. Bestehende translations werden mit aktueller Config neu übersetzt.
|
||||||
|
Texte für die keine Übersetzung mehr gefunden wird, wandern nach untranslated.
|
||||||
|
2. Einträge aus untranslated werden gegen die Ignore-Pattern geprüft:
|
||||||
|
- Treffer → ignored (neu erkannte Pattern seit dem letzten Extrakt)
|
||||||
|
- Kein Treffer → auto-übersetzen; Erfolg → translations, sonst bleibt in untranslated.
|
||||||
|
|
||||||
|
Arbeitet immer mit der ersten Sprache aus translation_languages.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
txt_path: Pfad zur Textdatei (wird in-place überschrieben)
|
||||||
|
translation_dicts_all: Dict mit {LANG: (multi, trigramme, bigramme, single), ...}
|
||||||
|
translation_languages: Liste der Sprachen (nur die erste wird verwendet)
|
||||||
|
wildcard_patterns: Wildcard-Muster zum Filtern (aus translator.cfg)
|
||||||
|
regex_patterns: Regex-Muster zum Filtern (aus translator.cfg)
|
||||||
|
"""
|
||||||
|
if not translation_languages or translation_languages[0] not in translation_dicts_all:
|
||||||
|
print(f"FEHLER: Keine Übersetzungs-Config für Re-Übersetzung verfügbar")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
wildcard_patterns = wildcard_patterns or []
|
||||||
|
regex_patterns = regex_patterns or []
|
||||||
|
|
||||||
|
lang = translation_languages[0]
|
||||||
|
multi, trigramme, bigramme, single = translation_dicts_all[lang]
|
||||||
|
|
||||||
|
existing_translations, untranslated, ignored = _parse_txt_sections(txt_path)
|
||||||
|
|
||||||
|
print(f" Vorhandene Übersetzungen: {len(existing_translations)}")
|
||||||
|
print(f" Nicht übersetzt: {len(untranslated)}")
|
||||||
|
print(f" Ignoriert: {len(ignored)}")
|
||||||
|
|
||||||
|
new_translations: list[tuple[str, str]] = []
|
||||||
|
updated = 0
|
||||||
|
unchanged = 0
|
||||||
|
moved_to_untranslated: list[str] = []
|
||||||
|
|
||||||
|
# Schritt 1: Bestehende Übersetzungen neu übersetzen
|
||||||
|
for source, old_target in existing_translations:
|
||||||
|
new_target = translate_text(source, multi, trigramme, bigramme, single)
|
||||||
|
if new_target:
|
||||||
|
new_translations.append((source, new_target))
|
||||||
|
if new_target != old_target:
|
||||||
|
updated += 1
|
||||||
|
else:
|
||||||
|
unchanged += 1
|
||||||
|
else:
|
||||||
|
moved_to_untranslated.append(source)
|
||||||
|
print(f" WARNUNG: Übersetzung nicht mehr verfügbar für '{source}', "
|
||||||
|
f"verschoben zu 'untranslated'")
|
||||||
|
|
||||||
|
# Schritt 2: Untranslated Texte prüfen und auto-übersetzen
|
||||||
|
still_untranslated: list[str] = list(moved_to_untranslated)
|
||||||
|
newly = 0
|
||||||
|
newly_ignored = 0
|
||||||
|
for text in untranslated:
|
||||||
|
# Neu hinzugefügte Ignore-Pattern prüfen
|
||||||
|
if should_ignore_text(text, wildcard_patterns, regex_patterns):
|
||||||
|
ignored.append(text)
|
||||||
|
newly_ignored += 1
|
||||||
|
continue
|
||||||
|
new_target = translate_text(text, multi, trigramme, bigramme, single)
|
||||||
|
if new_target:
|
||||||
|
new_translations.append((text, new_target))
|
||||||
|
newly += 1
|
||||||
|
else:
|
||||||
|
still_untranslated.append(text)
|
||||||
|
|
||||||
|
# Ergebnis zurückschreiben
|
||||||
|
try:
|
||||||
|
with open(txt_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write("--- translations:\n\n")
|
||||||
|
for source, target in new_translations:
|
||||||
|
f.write(f"{source} -> {target}\n")
|
||||||
|
f.write("\n--- untranslated:\n\n")
|
||||||
|
for text in still_untranslated:
|
||||||
|
f.write(f"{text}\n")
|
||||||
|
f.write("\n--- ignored:\n\n")
|
||||||
|
for text in ignored:
|
||||||
|
f.write(f"{text}\n")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Fehler beim Schreiben der Textdatei: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f"\nRe-Übersetzung abgeschlossen (Sprache: {lang}):")
|
||||||
|
print(f" Aktualisiert: {updated}")
|
||||||
|
print(f" Unverändert: {unchanged}")
|
||||||
|
if newly:
|
||||||
|
print(f" Neu übersetzt: {newly}")
|
||||||
|
if newly_ignored:
|
||||||
|
print(f" Neu ignoriert (Pattern): {newly_ignored}")
|
||||||
|
if moved_to_untranslated:
|
||||||
|
print(f" Zurück zu untranslated: {len(moved_to_untranslated)}")
|
||||||
|
print(f" Noch nicht übersetzt: {len(still_untranslated)}")
|
||||||
|
print(f"\nDatei aktualisiert: {txt_path}")
|
||||||
|
|
||||||
|
|
||||||
|
def _run_retranslate_txt(
|
||||||
|
args,
|
||||||
|
work_dir: Path,
|
||||||
|
translation_dicts_all: dict,
|
||||||
|
translation_languages: list,
|
||||||
|
wildcard_patterns: list,
|
||||||
|
regex_patterns: list) -> None:
|
||||||
|
"""Workflow: Re-Übersetzung einer bestehenden TXT-Datei (--retranslate-txt)."""
|
||||||
|
print("=== Re-Übersetzung TXT-Datei ===\n")
|
||||||
|
|
||||||
|
input_filename = args.retranslate_txt
|
||||||
|
if not input_filename.lower().endswith('.txt'):
|
||||||
|
print("Mit --retranslate-txt nur .txt Dateien erlaubt")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
txt_file = resolve_input_file(input_filename, work_dir, "TXT-Datei")
|
||||||
|
|
||||||
|
print(f"TXT-Datei: {txt_file}")
|
||||||
|
print(f"Sprache: {translation_languages[0]}")
|
||||||
|
|
||||||
|
retranslate_txt_file(txt_file, translation_dicts_all, translation_languages,
|
||||||
|
wildcard_patterns, regex_patterns)
|
||||||
|
|
||||||
|
print("\nVerarbeitung erfolgreich abgeschlossen.")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
def _build_arg_parser() -> argparse.ArgumentParser:
|
def _build_arg_parser() -> argparse.ArgumentParser:
|
||||||
"""Erstellt und konfiguriert den Argument-Parser für translate.py."""
|
"""Erstellt und konfiguriert den Argument-Parser für translate.py."""
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
@@ -1969,6 +2165,12 @@ def _build_arg_parser() -> argparse.ArgumentParser:
|
|||||||
metavar='FILE.json',
|
metavar='FILE.json',
|
||||||
help='Re-übersetzt translations Block in JSON-Datei mit aktueller Sprachkonfig (benötigt --translate)'
|
help='Re-übersetzt translations Block in JSON-Datei mit aktueller Sprachkonfig (benötigt --translate)'
|
||||||
)
|
)
|
||||||
|
input_group.add_argument(
|
||||||
|
'--retranslate-txt',
|
||||||
|
action='store',
|
||||||
|
metavar='FILE.txt',
|
||||||
|
help='Re-übersetzt translations/untranslated Blöcke in TXT-Datei mit aktueller Sprachkonfig (benötigt --translate)'
|
||||||
|
)
|
||||||
input_group.add_argument(
|
input_group.add_argument(
|
||||||
'--todxf',
|
'--todxf',
|
||||||
action='store',
|
action='store',
|
||||||
@@ -2208,6 +2410,8 @@ def main() -> None:
|
|||||||
parser.error("--fromjson benötigt --translate (Übersetzung ist erforderlich)")
|
parser.error("--fromjson benötigt --translate (Übersetzung ist erforderlich)")
|
||||||
if args.retranslate_json and not args.translate:
|
if args.retranslate_json and not args.translate:
|
||||||
parser.error("--retranslate-json benötigt --translate (Übersetzung ist erforderlich)")
|
parser.error("--retranslate-json benötigt --translate (Übersetzung ist erforderlich)")
|
||||||
|
if args.retranslate_txt and not args.translate:
|
||||||
|
parser.error("--retranslate-txt benötigt --translate (Übersetzung ist erforderlich)")
|
||||||
if args.todxf and not args.filename:
|
if args.todxf and not args.filename:
|
||||||
parser.error("--todxf benötigt --filename (Quell-DXF muss angegeben werden)")
|
parser.error("--todxf benötigt --filename (Quell-DXF muss angegeben werden)")
|
||||||
if args.translation_json and not args.todxf:
|
if args.translation_json and not args.todxf:
|
||||||
@@ -2262,6 +2466,10 @@ def main() -> None:
|
|||||||
if args.retranslate_json:
|
if args.retranslate_json:
|
||||||
_run_retranslate(args, work_dir, translation_dicts_all, translation_languages)
|
_run_retranslate(args, work_dir, translation_dicts_all, translation_languages)
|
||||||
|
|
||||||
|
if args.retranslate_txt:
|
||||||
|
_run_retranslate_txt(args, work_dir, translation_dicts_all, translation_languages,
|
||||||
|
wildcard_patterns, regex_patterns)
|
||||||
|
|
||||||
if args.todxf:
|
if args.todxf:
|
||||||
_run_todxf(args, work_dir, translation_languages, wildcard_patterns, regex_patterns,
|
_run_todxf(args, work_dir, translation_languages, wildcard_patterns, regex_patterns,
|
||||||
source_attr_name, target_attr_name, target_layer_name)
|
source_attr_name, target_attr_name, target_layer_name)
|
||||||
|
|||||||
Reference in New Issue
Block a user