Es wird in Zukunft immer ein multilang Block geschrieben. Nur ohne --translate wird die flache Hierarchie nach wie vor bedient

This commit is contained in:
2026-02-10 15:51:09 +01:00
parent 5cbd290a16
commit 46e2d8663b
+135 -151
View File
@@ -1289,10 +1289,13 @@ def write_texts_to_json(texts: list[str], ignored_texts: list[str], output_path:
diese ERHALTEN und nur die neu angegebenen Sprachen aktualisiert. So können diese ERHALTEN und nur die neu angegebenen Sprachen aktualisiert. So können
CS- und EN-Übersetzungen schrittweise in dieselbe Datei geschrieben werden. CS- und EN-Übersetzungen schrittweise in dieselbe Datei geschrieben werden.
Nur ohne --translate wird die flache Struktur verwendet: Nur ohne --translate (kein translation_languages) wird die flache Legacy-Struktur
- "translations": [...] erzeugt sie dient als reiner Rohextrakt ohne Sprachzuordnung:
- "translations": [] (immer leer, da keine Sprache bekannt)
- "untranslated": [...] - "untranslated": [...]
- "ignored": [...] - "ignored": [...]
Diese Dateien können anschliessend mit --retranslate-json + --translate LANG
in das Multilang-Format konvertiert und übersetzt werden.
Args: Args:
texts: Liste der akzeptierten Texte texts: Liste der akzeptierten Texte
@@ -1486,17 +1489,94 @@ def write_texts_to_text(texts: list[str], ignored_texts: list[str], output_path:
sys.exit(1) sys.exit(1)
def _retranslate_language_block(
translations_list: list,
untranslated_list: list,
multi: dict[str, str],
trigramme: dict[str, str],
bigramme: dict[str, str],
single: dict[str, str],
label: str) -> tuple[int, int, int]:
"""
Verarbeitet einen einzelnen Sprach-Block einer JSON-Übersetzungsdatei in-place.
Schritt 1 Re-übersetze bestehende Einträge in translations_list:
Jeder Eintrag wird mit der aktuellen Config neu übersetzt.
Wird keine Übersetzung mehr gefunden, wandert der Eintrag nach untranslated_list.
Schritt 2 Auto-übersetze Einträge aus untranslated_list:
Für jeden Eintrag mit leerem target wird auto-übersetzt;
erfolgreiche Treffer wandern nach translations_list.
Einträge mit bereits gesetztem target (manuell befüllt) werden
ebenfalls direkt nach translations_list übernommen.
Args:
translations_list: Liste der bereits übersetzten Einträge (in-place verändert)
untranslated_list: Liste der noch nicht übersetzten Einträge (in-place verändert)
multi: Multi-Phrasen-Dict
trigramme: Trigramme-Dict
bigramme: Bigramme-Dict
single: Einzelwörter-Dict
label: Bezeichnung für Warnmeldungen, z.B. "[CS]"
Returns:
Tuple (aktualisiert, unveraendert, neu_aus_untranslated)
"""
updated = 0
unchanged = 0
newly = 0
# Schritt 1: Re-übersetze bestehende translations-Einträge
for entry in translations_list[:]: # Kopie für sicheres Entfernen
if not isinstance(entry, dict) or 'source' not in entry:
continue
source = entry['source']
old_target = entry.get('target', '')
new_target = translate_text(source, multi, trigramme, bigramme, single)
if new_target:
if new_target != old_target:
entry['target'] = new_target
updated += 1
else:
unchanged += 1
else:
# Übersetzung nicht mehr verfügbar → nach untranslated verschieben
untranslated_list.append({"source": source, "target": ""})
translations_list.remove(entry)
print(f" WARNUNG: {label} Übersetzung für '{source}' nicht mehr verfügbar, "
f"verschoben zu 'untranslated'")
# Schritt 2: Auto-übersetze Einträge aus untranslated_list
for entry in untranslated_list[:]: # Kopie für sicheres Entfernen
if not isinstance(entry, dict) or 'source' not in entry:
continue
if entry.get('target', ''):
# Manuell befüllter Eintrag → direkt nach translations verschieben
translations_list.append(entry)
untranslated_list.remove(entry)
newly += 1
continue
source = entry['source']
new_target = translate_text(source, multi, trigramme, bigramme, single)
if new_target:
translations_list.append({"source": source, "target": new_target})
untranslated_list.remove(entry)
newly += 1
return updated, unchanged, newly
def retranslate_json_file(json_path: Path, def retranslate_json_file(json_path: Path,
translation_dicts_all: dict[str, tuple], translation_dicts_all: dict[str, tuple],
translation_languages: list[str]): translation_languages: list[str]):
""" """
Re-übersetzt eine JSON-Datei mit neuen Übersetzungs-Configs. Re-übersetzt eine JSON-Datei mit neuen Übersetzungs-Configs.
Liest alle "source" Texte aus dem "translations" Block und übersetzt sie neu.
Überschreibt die ursprüngliche JSON-Datei mit den aktualisierten Übersetzungen. Überschreibt die ursprüngliche JSON-Datei mit den aktualisierten Übersetzungen.
Unterstützt beide Strukturen: Erkennt die Dateistruktur automatisch und delegiert die eigentliche
- Alte Struktur: "translations": [{"source": "...", "target": "..."}, ...] Übersetzungsarbeit an _retranslate_language_block():
- Hierarchische Struktur: "[LANG]": {"translations": [...], ...} - Alte Struktur: "translations"/ "untranslated" auf oberster Ebene
- Hierarchische Struktur: "[CS]": {"translations": ..., "untranslated": ...}
Args: Args:
json_path: Pfad zur JSON-Datei json_path: Pfad zur JSON-Datei
@@ -1504,203 +1584,107 @@ def retranslate_json_file(json_path: Path,
translation_languages: Liste der Sprachen in der Reihenfolge der Verarbeitung translation_languages: Liste der Sprachen in der Reihenfolge der Verarbeitung
""" """
try: try:
# Lade bestehende JSON-Datei
with open(json_path, 'r', encoding='utf-8') as f: with open(json_path, 'r', encoding='utf-8') as f:
data = json.load(f) data = json.load(f)
if not isinstance(data, dict): if not isinstance(data, dict):
print(f"FEHLER: JSON-Datei hat ungültige Struktur (erwartet Object, bekam {type(data).__name__})") print(f"FEHLER: JSON-Datei hat ungültige Struktur (erwartet Object, "
f"bekam {type(data).__name__})")
sys.exit(1) sys.exit(1)
# Erkenne Struktur
structure_type, existing_languages = detect_json_structure(data) structure_type, existing_languages = detect_json_structure(data)
updated_count = 0 total_updated = 0
unchanged_count = 0 total_unchanged = 0
new_count = 0 total_newly = 0
new_block_count = 0
if structure_type == "legacy": if structure_type == "legacy":
# Alte Struktur: ein Sprach-Block if 'translations' not in data or not isinstance(data['translations'], list):
if 'translations' not in data: print(f"FEHLER: JSON-Datei enthält keinen gültigen 'translations' Block")
print(f"FEHLER: JSON-Datei enthält keinen 'translations' Block")
sys.exit(1) sys.exit(1)
translations_list = data['translations']
if not isinstance(translations_list, list):
print(f"FEHLER: 'translations' Block hat ungültige Struktur (erwartet Array)")
sys.exit(1)
# Verwende erste Sprache für Re-Übersetzung
if not translation_languages or translation_languages[0] not in translation_dicts_all: 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") print(f"FEHLER: Keine Übersetzungs-Config für Re-Übersetzung verfügbar")
sys.exit(1) sys.exit(1)
multi, trigramme, bigramme, single = translation_dicts_all[translation_languages[0]] if 'untranslated' not in data:
data['untranslated'] = []
# Re-übersetze alle Einträge in translations lang = translation_languages[0]
for entry in translations_list[:]: # Kopie für sicheres Entfernen multi, trigramme, bigramme, single = translation_dicts_all[lang]
if not isinstance(entry, dict) or 'source' not in entry:
continue
source = entry['source'] updated, unchanged, newly = _retranslate_language_block(
old_target = entry.get('target', '') data['translations'], data['untranslated'],
multi, trigramme, bigramme, single,
f"[{lang}]"
)
total_updated, total_unchanged, total_newly = updated, unchanged, newly
# Neue Übersetzung berechnen print(f"\nRe-Übersetzung abgeschlossen (alte Struktur → wird zu multilang konvertiert, Sprache: {lang}):")
new_target = translate_text(source, multi, trigramme, bigramme, single) print(f" Aktualisiert (translations): {updated}")
print(f" Unverändert: {unchanged}")
if newly:
print(f" Neu aus 'untranslated' übersetzt/verschoben: {newly}")
if new_target: # Legacy → Multilang konvertieren: flache Struktur in [LANG]-Block einpacken
if new_target != old_target: data = {
entry['target'] = new_target f"[{lang}]": {
updated_count += 1 "translations": data['translations'],
else: "untranslated": data['untranslated'],
unchanged_count += 1 },
else: "ignored": data.get('ignored', []),
# Keine Übersetzung mehr verfügbar -> in untranslated verschieben }
if 'untranslated' not in data:
data['untranslated'] = []
data['untranslated'].append({
"source": source,
"target": ""
})
translations_list.remove(entry)
print(f" WARNUNG: Übersetzung für '{source}' nicht mehr verfügbar, verschoben zu 'untranslated'")
# Versuche auch Einträge aus untranslated zu auto-übersetzen und nach translations verschieben
untranslated_list = data.get('untranslated', [])
newly_translated = 0
for entry in untranslated_list[:]: # Kopie für sicheres Entfernen
if not isinstance(entry, dict) or 'source' not in entry:
continue
if entry.get('target', ''):
# Manuell befüllter Eintrag bereits übersetzt, in translations verschieben
translations_list.append(entry)
untranslated_list.remove(entry)
newly_translated += 1
updated_count += 1
continue
source = entry['source']
new_target = translate_text(source, multi, trigramme, bigramme, single)
if new_target:
translations_list.append({"source": source, "target": new_target})
untranslated_list.remove(entry)
newly_translated += 1
updated_count += 1
print(f"\nRe-Übersetzung abgeschlossen (alte Struktur, Sprache: {translation_languages[0]}):")
print(f" Aktualisiert (translations): {updated_count - newly_translated}")
print(f" Unverändert: {unchanged_count}")
if newly_translated:
print(f" Neu aus 'untranslated' übersetzt/verschoben: {newly_translated}")
elif structure_type == "multilang": elif structure_type == "multilang":
# Hierarchische mehrsprachige Struktur
print(f"Erkannte mehrsprachige Struktur mit Sprachen: {', '.join(existing_languages)}") print(f"Erkannte mehrsprachige Struktur mit Sprachen: {', '.join(existing_languages)}")
# Re-übersetze für alle angegebenen Sprachen
for lang in translation_languages: for lang in translation_languages:
lang_key = f"[{lang}]" lang_key = f"[{lang}]"
# Wenn Sprache nicht in JSON existiert, erstelle neuen Block
if lang_key not in data: if lang_key not in data:
print(f" Erstelle neuen Sprach-Block: [{lang}]") print(f" Erstelle neuen Sprach-Block: [{lang}]")
data[lang_key] = { data[lang_key] = {"translations": [], "untranslated": []}
"translations": [], new_block_count += 1
"untranslated": []
}
new_count += 1
lang_data = data[lang_key] lang_data = data[lang_key]
if not isinstance(lang_data, dict): if not isinstance(lang_data, dict):
print(f"WARNUNG: [{lang}] Block ist kein Object, überspringe") print(f"WARNUNG: [{lang}] Block ist kein Object, überspringe")
continue continue
# Stelle sicher dass translations Block existiert
if 'translations' not in lang_data: if 'translations' not in lang_data:
lang_data['translations'] = [] lang_data['translations'] = []
if 'untranslated' not in lang_data: if 'untranslated' not in lang_data:
lang_data['untranslated'] = [] lang_data['untranslated'] = []
if not isinstance(lang_data['translations'], list):
translations_list = lang_data['translations']
if not isinstance(translations_list, list):
print(f"WARNUNG: [{lang}][translations] ist kein Array, überspringe") print(f"WARNUNG: [{lang}][translations] ist kein Array, überspringe")
continue continue
# Hole Übersetzungs-Config für diese Sprache
if lang not in translation_dicts_all: if lang not in translation_dicts_all:
print(f"WARNUNG: Keine Übersetzungs-Config für [{lang}] verfügbar, überspringe") print(f"WARNUNG: Keine Übersetzungs-Config für [{lang}] verfügbar, überspringe")
continue continue
multi, trigramme, bigramme, single = translation_dicts_all[lang] multi, trigramme, bigramme, single = translation_dicts_all[lang]
updated, unchanged, newly = _retranslate_language_block(
lang_data['translations'], lang_data['untranslated'],
multi, trigramme, bigramme, single,
f"[{lang}]"
)
total_updated += updated + newly
total_unchanged += unchanged
total_newly += newly
lang_updated = 0 print(f" [{lang}] Aktualisiert: {updated}, Unverändert: {unchanged}", end="")
lang_unchanged = 0 if newly:
print(f", Neu aus 'untranslated': {newly}", end="")
# Re-übersetze alle Einträge in translations
for entry in translations_list[:]: # Kopie der Liste für sicheres Entfernen
if not isinstance(entry, dict) or 'source' not in entry:
continue
source = entry['source']
old_target = entry.get('target', '')
# Neue Übersetzung berechnen
new_target = translate_text(source, multi, trigramme, bigramme, single)
if new_target:
if new_target != old_target:
entry['target'] = new_target
lang_updated += 1
else:
lang_unchanged += 1
else:
# Keine Übersetzung mehr verfügbar -> in untranslated verschieben
lang_data['untranslated'].append({
"source": source,
"target": ""
})
translations_list.remove(entry)
print(f" WARNUNG: [{lang}] Übersetzung für '{source}' nicht mehr verfügbar, verschoben zu 'untranslated'")
# Versuche auch Einträge aus untranslated zu auto-übersetzen und nach translations verschieben
untranslated_list = lang_data['untranslated']
lang_newly = 0
for entry in untranslated_list[:]: # Kopie für sicheres Entfernen
if not isinstance(entry, dict) or 'source' not in entry:
continue
if entry.get('target', ''):
# Manuell befüllter Eintrag bereits übersetzt, in translations verschieben
translations_list.append(entry)
untranslated_list.remove(entry)
lang_newly += 1
lang_updated += 1
continue
source = entry['source']
new_target = translate_text(source, multi, trigramme, bigramme, single)
if new_target:
translations_list.append({"source": source, "target": new_target})
untranslated_list.remove(entry)
lang_newly += 1
lang_updated += 1
updated_count += lang_updated
unchanged_count += lang_unchanged
print(f" [{lang}] Aktualisiert: {lang_updated - lang_newly}, Unverändert: {lang_unchanged}", end="")
if lang_newly:
print(f", Neu aus 'untranslated': {lang_newly}", end="")
print() print()
print(f"\nRe-Übersetzung abgeschlossen (mehrsprachige Struktur):") print(f"\nRe-Übersetzung abgeschlossen (mehrsprachige Struktur):")
print(f" Gesamt aktualisiert: {updated_count}") print(f" Gesamt aktualisiert: {total_updated}")
print(f" Gesamt unverändert: {unchanged_count}") print(f" Gesamt unverändert: {total_unchanged}")
if new_count > 0: if new_block_count:
print(f" Neue Sprach-Blöcke erstellt: {new_count}") print(f" Neue Sprach-Blöcke erstellt: {new_block_count}")
else: else:
print(f"FEHLER: JSON-Datei hat unbekannte Struktur") print(f"FEHLER: JSON-Datei hat unbekannte Struktur")
sys.exit(1) sys.exit(1)
# Schreibe aktualisierte JSON-Datei zurück
with open(json_path, 'w', encoding='utf-8') as f: with open(json_path, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2) json.dump(data, f, ensure_ascii=False, indent=2)