diff --git a/lib/translate.py b/lib/translate.py index fde8c3b..a9a1aac 100644 --- a/lib/translate.py +++ b/lib/translate.py @@ -1279,28 +1279,59 @@ def write_texts_to_json(texts: list[str], ignored_texts: list[str], output_path: """ Schreibt die Texte in eine JSON-Datei. - Bei einer Sprache (alte Struktur): - - "translations": Liste von {source, target} für übersetzte Texte - - "untranslated": Liste von {source, target: ""} für nicht übersetzte Texte + Immer wenn Übersetzungssprachen angegeben sind (auch nur eine), wird die + hierarchische Struktur verwendet: + - "[CS]": {"translations": [...], "untranslated": [...]} + - "[EN]": {"translations": [...], "untranslated": [...]} - "ignored": Liste von Strings für gefilterte/ignorierte Texte - Bei mehreren Sprachen (hierarchische Struktur): - - "[EN]": {"translations": [...], "untranslated": [...]} - - "[FR]": {"translations": [...], "untranslated": [...]} - - "ignored": Liste von Strings für gefilterte/ignorierte Texte + Existiert die Ausgabedatei bereits und enthält sie Sprachblöcke, werden + 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": [...] + - "untranslated": [...] + - "ignored": [...] Args: texts: Liste der akzeptierten Texte ignored_texts: Liste der ignorierten Texte output_path: Pfad zur Ausgabe-JSON-Datei - translation_dicts: Optional: Tuple mit (Multi, Trigramme, Bigramme, Single) für eine Sprache (backwards compatibility) - translation_dicts_all: Optional: Dict mit {LANG: (multi, trigramme, bigramme, single)} für mehrere Sprachen + translation_dicts: Optional: Tuple für eine Sprache (für Excel/Text-Ausgabe, hier ignoriert) + translation_dicts_all: Optional: Dict mit {LANG: (multi, trigramme, bigramme, single)} translation_languages: Optional: Liste der Sprachen in der Reihenfolge der Verarbeitung """ output_data = {} - # Mehrsprachige hierarchische Struktur - if translation_dicts_all and translation_languages and len(translation_languages) > 1: + # Hierarchische Struktur: immer wenn mindestens eine Übersetzungssprache angegeben + if translation_dicts_all and translation_languages: + + # Lade bestehende Sprachblöcke aus vorhandener Datei (Merge-Unterstützung) + existing_lang_blocks = {} + if output_path.exists(): + try: + with open(output_path, 'r', encoding='utf-8') as f: + existing = json.load(f) + if isinstance(existing, dict): + for key, value in existing.items(): + if key.startswith('[') and key.endswith(']') and len(key) > 2: + existing_lang_blocks[key] = value + if existing_lang_blocks: + preserved = [k for k in existing_lang_blocks + if k not in {f"[{lang}]" for lang in translation_languages}] + if preserved: + print(f" Bestehende Sprachblöcke übernommen: {', '.join(preserved)}") + except Exception: + pass # Fehler beim Laden → einfach neu schreiben + + # Übernehme Sprachblöcke, die NICHT neu generiert werden + new_lang_keys = {f"[{lang}]" for lang in translation_languages} + for key, value in existing_lang_blocks.items(): + if key not in new_lang_keys: + output_data[key] = value + + # Generiere neue/aktualisierte Sprachblöcke for lang in translation_languages: if lang not in translation_dicts_all: continue @@ -1311,18 +1342,11 @@ def write_texts_to_json(texts: list[str], ignored_texts: list[str], output_path: for text in texts: translated = translate_text(text, multi, trigramme, bigramme, single) - if translated: # Übersetzung vorhanden - translations.append({ - "source": text, - "target": translated - }) - else: # Keine Übersetzung vorhanden - untranslated.append({ - "source": text, - "target": "" - }) + if translated: + translations.append({"source": text, "target": translated}) + else: + untranslated.append({"source": text, "target": ""}) - # Hierarchische Struktur: [LANG] als Schlüssel output_data[f"[{lang}]"] = { "translations": translations, "untranslated": untranslated @@ -1331,10 +1355,8 @@ def write_texts_to_json(texts: list[str], ignored_texts: list[str], output_path: # Ignorierte Texte am Ende (ohne Sprachzuordnung) output_data["ignored"] = ignored_texts - # Alte Struktur (eine Sprache oder keine Übersetzung) + # Flache Struktur: ohne Übersetzungssprachen else: - # Block 1: Übersetzte Texte (als Liste von Dicts mit source/target) - # Block 2: Nicht übersetzte Texte (als Liste von Dicts mit source und leerem target) if translation_dicts: multi, trigramme, bigramme, single = translation_dicts translations = [] @@ -1342,16 +1364,10 @@ def write_texts_to_json(texts: list[str], ignored_texts: list[str], output_path: for text in texts: translated = translate_text(text, multi, trigramme, bigramme, single) - if translated: # Übersetzung vorhanden - translations.append({ - "source": text, - "target": translated - }) - else: # Keine Übersetzung vorhanden - untranslated.append({ - "source": text, - "target": "" - }) + if translated: + translations.append({"source": text, "target": translated}) + else: + untranslated.append({"source": text, "target": ""}) output_data["translations"] = translations output_data["untranslated"] = untranslated @@ -1360,7 +1376,6 @@ def write_texts_to_json(texts: list[str], ignored_texts: list[str], output_path: output_data["translations"] = [] output_data["untranslated"] = [{"source": text, "target": ""} for text in texts] - # Block 3: Ignorierte Texte (als Liste) output_data["ignored"] = ignored_texts try: @@ -1370,14 +1385,16 @@ def write_texts_to_json(texts: list[str], ignored_texts: list[str], output_path: print(f"JSON-Datei erfolgreich erstellt: {output_path}") # Ausgabe-Statistiken - if translation_dicts_all and translation_languages and len(translation_languages) > 1: - # Mehrsprachige Struktur - for lang in translation_languages: - if f"[{lang}]" in output_data: - lang_data = output_data[f"[{lang}]"] - print(f" [{lang}] Übersetzungen: {len(lang_data['translations'])}, Nicht übersetzt: {len(lang_data['untranslated'])}") + if translation_dicts_all and translation_languages: + # Alle Sprachblöcke in der Ausgabedatei anzeigen (inkl. übernommene) + for key in output_data: + if key.startswith('[') and key.endswith(']') and len(key) > 2: + lang_data = output_data[key] + lang = key[1:-1] + marker = "" if lang in translation_languages else " (übernommen)" + print(f" [{lang}]{marker} Übersetzungen: {len(lang_data['translations'])}, " + f"Nicht übersetzt: {len(lang_data['untranslated'])}") else: - # Alte Struktur if translation_dicts: print(f" Übersetzungen: {len(output_data['translations'])}") print(f" Nicht übersetzte Texte: {len(output_data['untranslated'])}") @@ -1520,8 +1537,8 @@ def retranslate_json_file(json_path: Path, multi, trigramme, bigramme, single = translation_dicts_all[translation_languages[0]] - # Re-übersetze alle Einträge - for entry in translations_list: + # 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 @@ -1548,9 +1565,32 @@ def retranslate_json_file(json_path: Path, 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: {updated_count}") + 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": # Hierarchische mehrsprachige Struktur @@ -1595,7 +1635,7 @@ def retranslate_json_file(json_path: Path, lang_updated = 0 lang_unchanged = 0 - # Re-übersetze alle Einträge für diese Sprache + # 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 @@ -1621,10 +1661,34 @@ def retranslate_json_file(json_path: Path, 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}, Unverändert: {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(f"\nRe-Übersetzung abgeschlossen (mehrsprachige Struktur):") print(f" Gesamt aktualisiert: {updated_count}")