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
|
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)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user