Log In     Register    

Language Files
Download or post language files
<<  Back To Forum

Python script to merge old translation with new one

by Guest on 2025/12/07 09:38:37 PM    
For those who want to retrieve their old translation and update to the new template, here is a python script!

use it with this command:  

python merge_tixati_lang.py --new tixati_language_template.txt --old tixati_langue_française_v3.31.txt --out tixati_langue_fusionnée.txt

merge_tixati_lang.py
=========code=======
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Fusionne un template Tixati (anglais ou anglais+français partiel) avec
une ou plusieurs anciennes traductions (anglais+français).

Fonctions :
- Ne PAS écraser les lignes françaises déjà présentes dans le fichier --new.
- Appliquer les traductions des fichiers --old dans l'ordre où ils sont passés :
   --old v1.0beta.txt  --old v3.31.txt
 => v1.0β prioritaire, v3.31 utilisée seulement pour les clés manquantes.
- Produire deux rapports :
   1) doublons : lignes anglaises identiques dans le fichier final
      avec leurs numéros de ligne ;
   2) nouvelles clés : pour chaque fichier --old après le premier,
      les clés anglaises absentes du tout premier --old.

Usage :
   python merge_tixati_lang.py --new tixati_language_template.txt --old tixati_langue_française_v3.31.txt --out tixati_langue_fusionnée.txt
"""

import argparse
from pathlib import Path
from collections import defaultdict


def parse_translations_from_file(old_path: Path) -> dict:
   """
   Lit un fichier ancien (déjà traduit) et construit un dict :
       anglais -> liste de traductions françaises rencontrées

   Format supposé :
       English line
       French line
       (une ou plusieurs lignes vides)

   On mémorise toutes les variantes de traduction rencontrées pour une clé.
   """
   with old_path.open("r", encoding="utf-8") as f:
       lines = f.readlines()

   translations = defaultdict(list)
   i = 0
   n = len(lines)

   while i < n:
       line = lines.rstrip("\n")

       # Ignorer commentaires et lignes vides
       if not line.strip() or line.lstrip().startswith("//"):
           i += 1
           continue

       english = line

       # Ligne suivante potentiellement = traduction française
       if i + 1 < n:
           next_line = lines.rstrip("\n")
           if next_line.strip() and not next_line.lstrip().startswith("//"):
               french = next_line
               if french not in translations:
                   translations.append(french)
               i += 2
               continue

       i += 1

   return translations


def build_combined_translations(old_paths):
   """
   Construit :
     - une liste de dicts (un par fichier --old),
     - un dict combiné avec priorité au premier fichier.

   old_paths : liste de Path (dans l'ordre de priorité).
   """
   per_file_dicts = []
   for p in old_paths:
       per_file_dicts.append(parse_translations_from_file(p))

   combined = defaultdict(list)
   for d in per_file_dicts:
       for eng, fr_list in d.items():
           if eng not in combined:
               combined = fr_list.copy()
           else:
               # Ajouter éventuelles variantes de traduction
               for fr in fr_list:
                   if fr not in combined:
                       combined.append(fr)

   return per_file_dicts, combined


def merge_new_template(new_path: Path, translations: dict, out_path: Path):
   """
   Parcourt le fichier --new (anglais seul ou anglais+français) et :

   - recopie les lignes telles quelles,
   - pour chaque ligne anglaise (non vide, non commentée), regarde si une
     traduction existe déjà immédiatement en dessous :
       * si OUI : la laisse telle quelle (on NE l’écrase PAS),
       * si NON : insère la traduction trouvée dans `translations` si disponible.

   Retourne :
     english_positions : dict anglais -> liste des numéros de lignes (1-based)
                         de cette ligne anglaise dans le fichier de sortie.
   """
   with new_path.open("r", encoding="utf-8") as f:
       lines = f.readlines()

   out_lines = []
   english_positions = defaultdict(list)

   i = 0
   n = len(lines)
   current_out_line_number = 0  # n° de ligne dans le fichier de sortie

   while i < n:
       raw_line = lines
       line = raw_line.rstrip("\n")
       stripped = line.strip()

       # Commentaires ou lignes vides : recopie telle quelle
       if not stripped or stripped.startswith("//"):
           out_lines.append(raw_line)
           current_out_line_number += 1
           i += 1
           continue

       # On considère que c'est une ligne "clé" anglaise
       english = line
       out_lines.append(english + "\n")
       current_out_line_number += 1

       # Mémorise la position de cette ligne anglaise
       english_positions.append(current_out_line_number)

       # Vérifier s'il existe déjà une traduction sous cette ligne dans le fichier --new
       existing_french = None
       if i + 1 < n:
           next_raw = lines
           next_line = next_raw.rstrip("\n")
           next_stripped = next_line.strip()

           # Si la ligne suivante n'est pas vide et pas un commentaire,
           # on la considère comme la traduction déjà présente
           if next_stripped and not next_line.lstrip().startswith("//"):
               existing_french = next_line

       if existing_french is not None:
           # On garde la traduction existante telle quelle
           out_lines.append(existing_french + "\n")
           current_out_line_number += 1

           # Recopier les lignes vides/commentaires qui suivent
           i += 2
           while i < n:
               tmp_raw = lines
               tmp_line = tmp_raw.rstrip("\n")
               if tmp_line.strip() == "" or tmp_line.lstrip().startswith("//"):
                   out_lines.append(tmp_raw)
                   current_out_line_number += 1
                   i += 1
               else:
                   break
       else:
           # Pas de traduction dans --new, on regarde dans les dictionnaires combinés
           if english in translations and len(translations) > 0:
               # On prend la première traduction (du premier fichier --old
               # qui l'a fournie, donc priorité au premier)
               french = translations[0]
               out_lines.append(french + "\n")
               current_out_line_number += 1

               # On ajoute au moins une ligne vide
               out_lines.append("\n")
               current_out_line_number += 1

               # Sauter les lignes vides qui suivent déjà dans le template
               i += 1
               while i < n and not lines.strip():
                   i += 1
           else:
               # Aucune traduction connue : on laisse la suite telle quelle
               i += 1

   with out_path.open("w", encoding="utf-8", newline="\n") as f:
       f.writelines(out_lines)

   return english_positions


def make_duplicates_report(english_positions: dict) -> str:
   """
   Construit un rapport texte sur les lignes anglaises identiques
   apparaissant plusieurs fois dans le fichier recomposé.
   """
   lines = []
   lines.append("=== Rapport des lignes anglaises identiques (doublons) ===")

   has_duplicates = False
   for english, positions in sorted(english_positions.items(), key=lambda x: x[0]):
       if len(positions) > 1:
           has_duplicates = True
           lines.append("")
           lines.append(f"Texte anglais : {repr(english)}")
           lines.append(
               "  Présent aux lignes (fichier de sortie) : "
               + ", ".join(str(p) for p in positions)
           )

   if not has_duplicates:
       lines.append("")
       lines.append("Aucune ligne anglaise en double trouvée dans le fichier de sortie.")

   return "\n".join(lines) + "\n"


def make_new_keys_report(old_paths, per_file_dicts) -> str:
   """
   Construit un rapport listant, pour chaque fichier --old après le premier,
   les clés anglaises absentes du tout premier --old.

   Exemple typique :
     - old_paths[0] : v1.0β (référence)
     - old_paths[1] : v3.31
   => On liste les clés présentes dans v3.31 mais pas dans v1.0β.
   """
   if not per_file_dicts:
       return "Aucun fichier --old fourni.\n"

   lines = []
   base_keys = set(per_file_dicts[0].keys())

   lines.append("=== Rapport des nouvelles clés par rapport au premier fichier --old ===")
   lines.append(f"Fichier de référence (base) : {old_paths[0]}")
   lines.append("")

   if len(per_file_dicts) == 1:
       lines.append("Un seul fichier --old fourni, aucun autre pour comparer.\n")
       return "\n".join(lines)

   for idx in range(1, len(per_file_dicts)):
       keys_current = set(per_file_dicts.keys())
       new_keys = sorted(keys_current - base_keys)

       lines.append("")
       lines.append(f"--- Fichier : {old_paths} ---")
       if not new_keys:
           lines.append("Aucune nouvelle clé (toutes les clés sont déjà dans le fichier de référence).")
           continue

       lines.append(f"Nombre de nouvelles clés par rapport à {old_paths[0]} : {len(new_keys)}")
       lines.append("Liste des nouvelles clés (avec une traduction exemple) :")

       d = per_file_dicts
       for eng in new_keys:
           fr_list = d.get(eng, [])
           if fr_list:
               example_fr = fr_list[0]
               lines.append(f"- Anglais : {repr(eng)}")
               lines.append(f"  Français (exemple) : {repr(example_fr)}")
           else:
               lines.append(f"- Anglais : {repr(eng)}")
               lines.append("  (aucune traduction trouvée ?!)")

   lines.append("")
   return "\n".join(lines) + "\n"


def main():
   parser = argparse.ArgumentParser(
       description=(
           "Fusionne un template Tixati avec une ou plusieurs anciennes "
           "traductions, sans écraser les traductions existantes dans --new, "
           "et produit deux rapports (doublons et nouvelles clés)."
       )
   )
   parser.add_argument(
       "--new",
       required=True,
       help="Nouveau template (anglais ou anglais+français partiel)",
   )
   parser.add_argument(
       "--old",
       required=True,
       action="append",
       help="Ancien fichier déjà traduit (anglais+français). "
            "Peut être passé plusieurs fois, l'ordre donne la priorité.",
   )
   parser.add_argument("--out", required=True, help="Fichier de sortie fusionné")
   parser.add_argument(
       "--dup-report",
       help=(
           "Fichier de rapport des doublons (optionnel). "
           "Par défaut : même nom que --out avec suffixe "
           "'_duplicates_report.txt'"
       ),
   )
   parser.add_argument(
       "--new-keys-report",
       help=(
           "Fichier de rapport des nouvelles clés (optionnel). "
           "Par défaut : même nom que --out avec suffixe "
           "'_new_keys_report.txt'"
       ),
   )

   args = parser.parse_args()

   new_path = Path(args.new)
   old_paths =
   out_path = Path(args.out)

   if not new_path.is_file():
       raise SystemExit(f"Fichier --new introuvable : {new_path}")
   for p in old_paths:
       if not p.is_file():
           raise SystemExit(f"Fichier --old introuvable : {p}")

   dup_report_path = Path(args.dup_report) if args.dup_report else \
       out_path.with_suffix(out_path.suffix + "_duplicates_report.txt")

   new_keys_report_path = Path(args.new_keys_report) if args.new_keys_report else \
       out_path.with_suffix(out_path.suffix + "_new_keys_report.txt")

   # Construire les dictionnaires de traductions par fichier et combiné
   per_file_dicts, combined_translations = build_combined_translations(old_paths)
   print(f"Nombre de clés anglaises trouvées dans l'ensemble des fichiers --old : {len(combined_translations)}")

   # Fusion avec le template
   english_positions = merge_new_template(new_path, combined_translations, out_path)
   print(f"Fichier fusionné écrit dans : {out_path}")

   # Rapport des doublons
   dup_report_text = make_duplicates_report(english_positions)
   with dup_report_path.open("w", encoding="utf-8", newline="\n") as f:
       f.write(dup_report_text)
   print(f"Rapport des doublons écrit dans : {dup_report_path}")

   # Rapport des nouvelles clés par rapport au premier fichier --old
   new_keys_report_text = make_new_keys_report(old_paths, per_file_dicts)
   with new_keys_report_path.open("w", encoding="utf-8", newline="\n") as f:
       f.write(new_keys_report_text)
   print(f"Rapport des nouvelles clés écrit dans : {new_keys_report_path}")

   # Petit résumé console
   print()
   print(dup_report_text.splitlines()[0])
   print(new_keys_report_text.splitlines()[0])
   print("(voir les fichiers de rapport pour le détail)")


if __name__ == "__main__":
   main()
=========code=======

JimKi





This web site is powered by Super Simple Server