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:
+135
-151
@@ -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
|
||||
CS- und EN-Übersetzungen schrittweise in dieselbe Datei geschrieben werden.
|
||||
|
||||
Nur ohne --translate wird die flache Struktur verwendet:
|
||||
- "translations": [...]
|
||||
Nur ohne --translate (kein translation_languages) wird die flache Legacy-Struktur
|
||||
erzeugt – sie dient als reiner Rohextrakt ohne Sprachzuordnung:
|
||||
- "translations": [] (immer leer, da keine Sprache bekannt)
|
||||
- "untranslated": [...]
|
||||
- "ignored": [...]
|
||||
Diese Dateien können anschliessend mit --retranslate-json + --translate LANG
|
||||
in das Multilang-Format konvertiert und übersetzt werden.
|
||||
|
||||
Args:
|
||||
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)
|
||||
|
||||
|
||||
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,
|
||||
translation_dicts_all: dict[str, tuple],
|
||||
translation_languages: list[str]):
|
||||
"""
|
||||
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.
|
||||
|
||||
Unterstützt beide Strukturen:
|
||||
- Alte Struktur: "translations": [{"source": "...", "target": "..."}, ...]
|
||||
- Hierarchische Struktur: "[LANG]": {"translations": [...], ...}
|
||||
Erkennt die Dateistruktur automatisch und delegiert die eigentliche
|
||||
Übersetzungsarbeit an _retranslate_language_block():
|
||||
- Alte Struktur: "translations"/ "untranslated" auf oberster Ebene
|
||||
- Hierarchische Struktur: "[CS]": {"translations": ..., "untranslated": ...}
|
||||
|
||||
Args:
|
||||
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
|
||||
"""
|
||||
try:
|
||||
# Lade bestehende JSON-Datei
|
||||
with open(json_path, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
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)
|
||||
|
||||
# Erkenne Struktur
|
||||
structure_type, existing_languages = detect_json_structure(data)
|
||||
|
||||
updated_count = 0
|
||||
unchanged_count = 0
|
||||
new_count = 0
|
||||
total_updated = 0
|
||||
total_unchanged = 0
|
||||
total_newly = 0
|
||||
new_block_count = 0
|
||||
|
||||
if structure_type == "legacy":
|
||||
# Alte Struktur: ein Sprach-Block
|
||||
if 'translations' not in data:
|
||||
print(f"FEHLER: JSON-Datei enthält keinen 'translations' Block")
|
||||
if 'translations' not in data or not isinstance(data['translations'], list):
|
||||
print(f"FEHLER: JSON-Datei enthält keinen gültigen 'translations' Block")
|
||||
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:
|
||||
print(f"FEHLER: Keine Übersetzungs-Config für Re-Übersetzung verfügbar")
|
||||
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
|
||||
for entry in translations_list[:]: # Kopie für sicheres Entfernen
|
||||
if not isinstance(entry, dict) or 'source' not in entry:
|
||||
continue
|
||||
lang = translation_languages[0]
|
||||
multi, trigramme, bigramme, single = translation_dicts_all[lang]
|
||||
|
||||
source = entry['source']
|
||||
old_target = entry.get('target', '')
|
||||
updated, unchanged, newly = _retranslate_language_block(
|
||||
data['translations'], data['untranslated'],
|
||||
multi, trigramme, bigramme, single,
|
||||
f"[{lang}]"
|
||||
)
|
||||
total_updated, total_unchanged, total_newly = updated, unchanged, newly
|
||||
|
||||
# Neue Übersetzung berechnen
|
||||
new_target = translate_text(source, multi, trigramme, bigramme, single)
|
||||
print(f"\nRe-Übersetzung abgeschlossen (alte Struktur → wird zu multilang konvertiert, Sprache: {lang}):")
|
||||
print(f" Aktualisiert (translations): {updated}")
|
||||
print(f" Unverändert: {unchanged}")
|
||||
if newly:
|
||||
print(f" Neu aus 'untranslated' übersetzt/verschoben: {newly}")
|
||||
|
||||
if new_target:
|
||||
if new_target != old_target:
|
||||
entry['target'] = new_target
|
||||
updated_count += 1
|
||||
else:
|
||||
unchanged_count += 1
|
||||
else:
|
||||
# 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}")
|
||||
# Legacy → Multilang konvertieren: flache Struktur in [LANG]-Block einpacken
|
||||
data = {
|
||||
f"[{lang}]": {
|
||||
"translations": data['translations'],
|
||||
"untranslated": data['untranslated'],
|
||||
},
|
||||
"ignored": data.get('ignored', []),
|
||||
}
|
||||
|
||||
elif structure_type == "multilang":
|
||||
# Hierarchische mehrsprachige Struktur
|
||||
print(f"Erkannte mehrsprachige Struktur mit Sprachen: {', '.join(existing_languages)}")
|
||||
|
||||
# Re-übersetze für alle angegebenen Sprachen
|
||||
for lang in translation_languages:
|
||||
lang_key = f"[{lang}]"
|
||||
|
||||
# Wenn Sprache nicht in JSON existiert, erstelle neuen Block
|
||||
if lang_key not in data:
|
||||
print(f" Erstelle neuen Sprach-Block: [{lang}]")
|
||||
data[lang_key] = {
|
||||
"translations": [],
|
||||
"untranslated": []
|
||||
}
|
||||
new_count += 1
|
||||
data[lang_key] = {"translations": [], "untranslated": []}
|
||||
new_block_count += 1
|
||||
|
||||
lang_data = data[lang_key]
|
||||
if not isinstance(lang_data, dict):
|
||||
print(f"WARNUNG: [{lang}] Block ist kein Object, überspringe")
|
||||
continue
|
||||
|
||||
# Stelle sicher dass translations Block existiert
|
||||
if 'translations' not in lang_data:
|
||||
lang_data['translations'] = []
|
||||
if 'untranslated' not in lang_data:
|
||||
lang_data['untranslated'] = []
|
||||
|
||||
translations_list = lang_data['translations']
|
||||
if not isinstance(translations_list, list):
|
||||
if not isinstance(lang_data['translations'], list):
|
||||
print(f"WARNUNG: [{lang}][translations] ist kein Array, überspringe")
|
||||
continue
|
||||
|
||||
# Hole Übersetzungs-Config für diese Sprache
|
||||
if lang not in translation_dicts_all:
|
||||
print(f"WARNUNG: Keine Übersetzungs-Config für [{lang}] verfügbar, überspringe")
|
||||
continue
|
||||
|
||||
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
|
||||
lang_unchanged = 0
|
||||
|
||||
# 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(f" [{lang}] Aktualisiert: {updated}, Unverändert: {unchanged}", end="")
|
||||
if newly:
|
||||
print(f", Neu aus 'untranslated': {newly}", end="")
|
||||
print()
|
||||
|
||||
print(f"\nRe-Übersetzung abgeschlossen (mehrsprachige Struktur):")
|
||||
print(f" Gesamt aktualisiert: {updated_count}")
|
||||
print(f" Gesamt unverändert: {unchanged_count}")
|
||||
if new_count > 0:
|
||||
print(f" Neue Sprach-Blöcke erstellt: {new_count}")
|
||||
print(f" Gesamt aktualisiert: {total_updated}")
|
||||
print(f" Gesamt unverändert: {total_unchanged}")
|
||||
if new_block_count:
|
||||
print(f" Neue Sprach-Blöcke erstellt: {new_block_count}")
|
||||
|
||||
else:
|
||||
print(f"FEHLER: JSON-Datei hat unbekannte Struktur")
|
||||
sys.exit(1)
|
||||
|
||||
# Schreibe aktualisierte JSON-Datei zurück
|
||||
with open(json_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user