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