Compare commits

..

5 Commits

Author SHA1 Message Date
Alex
defcc38435 meilleur traducteur 2026-01-06 20:54:24 +01:00
Alex
e9a090ad9d typo 2026-01-05 18:42:00 +01:00
Alex
156220e62b gestion des reprises de traductions 2026-01-05 18:40:52 +01:00
Alex
89c2197a42 affichage des paragraphes en mode debug 2026-01-05 17:13:40 +01:00
Alex
aa2ab7dbec la meilleur traduction 2026-01-05 12:59:46 +01:00
3 changed files with 103 additions and 46 deletions

View File

@ -1,14 +1,16 @@
FROM zongwei/gemma3-translator:4b FROM qwen2.5:14b
PARAMETER temperature 0.9 PARAMETER temperature 0.2
PARAMETER num_ctx 131072 PARAMETER num_ctx 8192
SYSTEM """ SYSTEM """
Tu es un traducteur professionnel spécialisé dans la traduction de texte ukrainien en français. Tu es un traducteur spécialisé dans les mémoires ukrainiennes des années 1910.
Tu est spécialisé dans la rédaction des textes auto-biographiques et historiques et parles un français impécable. - Utilise le glossaire fourni pour les noms de lieux et termes historiques.
Tu dois toujours répondre en français et uniquement dans cette langue. - Garde le style narratif et les tournures orales de l'auteur.
Tu prends soins des congugaisons et de la tournure de phases. Règles strictes :
N'ajoutes aucun texte sous quelle forme que ce soit avant ou après le texte traduit. Ne réponds qu'avec le texte traduit. 1. **Conserve tous les noms de lieux** dans leur forme originale (ex. : Львів → Lviv, mais ajoute une note si nécessaire : "[Lemberg en 1910]").
N'inclus jamais la phrase "Voici le texte traduit" dans la réponse. 2. **Respecte le style narratif** : garde les tournures orales et les expressions propres à lauteur.
Traduis avec précision et naturel, en respectant l'intonation originale utilisée par l'auteur du texte. 3. **Pour les termes historiques** (ex. : "powiat"), utilise le terme français standard ou ajoute une note explicative.
Tu ne dois pas interpréter les pensées ou les réflexions de l'auteur, la traduciton doit restée fidèle à la pensée de l'auteur. 4. **Ne traduis pas** les mots en russe/allemand/polonais intégrés au texte (ex. : citations, noms officiels).
Le texte que tu traduis est un texte historique, tu ne dois pas le changer. 5. **Structure** : Garde les sauts de ligne et la mise en page originale.
6. **Notes du traducteur** : Ajoute entre crochets [ ] les explications contextuelles (ex. : "[Note : ville alors sous domination autrichienne]").
""" """

121
main.py
View File

@ -3,23 +3,26 @@ import requests
import json import json
from reportlab.lib.pagesizes import letter from reportlab.lib.pagesizes import letter
from reportlab.lib.units import inch from reportlab.lib.units import inch
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Flowable
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_JUSTIFY from reportlab.lib.enums import TA_JUSTIFY
from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont from reportlab.pdfbase.ttfonts import TTFont
import os import os
from datetime import datetime
# Configuration # Configuration
DEBUG = True
PDF_PATH = "TaniaBorecMemoir(Ukr).pdf" PDF_PATH = "TaniaBorecMemoir(Ukr).pdf"
OLLAMA_MODEL = "traductionUkrainienVersFrancais:latest" OLLAMA_MODEL = "traductionUkrainienVersFrancais:latest"
OLLAMA_URL = "http://localhost:11434/api/generate" OLLAMA_URL = "http://localhost:11434/api/generate"
TARGET_LANGUAGE = "français" TARGET_LANGUAGE = "français"
CHECKPOINT_FILE = "checkpoint.json" CHECKPOINT_FILE = "checkpoint.json"
TEMP_OUTPUT_TXT = "output_temp.txt" TEMP_OUTPUT_TXT = "output_temp.txt"
FINAL_OUTPUT_PDF = PDF_PATH.replace(".pdf",f" ({TARGET_LANGUAGE.upper()[:2]})_V2.pdf") FINAL_OUTPUT_PDF = PDF_PATH.replace(".pdf",f"({TARGET_LANGUAGE.upper()[:2]})_V6.pdf")
FINAL_OUTPUT_TXT = PDF_PATH.replace(".pdf",f" ({TARGET_LANGUAGE.upper()[:2]})_V2.txt") FINAL_OUTPUT_TXT = PDF_PATH.replace(".pdf",f"({TARGET_LANGUAGE.upper()[:2]})_V6.txt")
DEBUG = True
def extract_parameters_from_template(template_str): def extract_parameters_from_template(template_str):
"""Extrait les paramètres du modèle à partir du template.""" """Extrait les paramètres du modèle à partir du template."""
@ -38,7 +41,7 @@ def extract_parameters_from_template(template_str):
params_section = template_str params_section = template_str
# Parse les lignes de paramètres # Parse les lignes de paramètres
# Format: "stop "<end_of_turn>"" # Format: "stop "<end_of_turn>""
# "temperature 0.1" # "temperature 0.1"
lines = params_section.split('\n') lines = params_section.split('\n')
@ -144,8 +147,8 @@ def display_llm_info():
return "Informations du modèle non disponibles" return "Informations du modèle non disponibles"
def register_unicode_font(): def register_unicode_font():
"""Enregistre une police TrueType qui supporte le cyrilique.""" """Enregistre une police TrueType qui supporte le cyrillique."""
# Recherche une police système qui supporte le cyrilique # Recherche une police système qui supporte le cyrillique
font_paths = [ font_paths = [
r"C:\Windows\Fonts\DejaVuSans.ttf", r"C:\Windows\Fonts\DejaVuSans.ttf",
r"C:\Windows\Fonts\Calibri.ttf", r"C:\Windows\Fonts\Calibri.ttf",
@ -171,10 +174,15 @@ def load_checkpoint():
return json.load(f) return json.load(f)
return {"last_processed_index": -1, "results": {}} return {"last_processed_index": -1, "results": {}}
# Sauvegarde le checkpoint
# Sauvegarde le checkpoint # Sauvegarde le checkpoint
def save_checkpoint(last_index, results): def save_checkpoint(last_index, results):
# Trier les clés du dictionnaire results
sorted_results = {key: results[key] for key in sorted(results.keys(), key=int)}
with open(CHECKPOINT_FILE, "w") as f: with open(CHECKPOINT_FILE, "w") as f:
json.dump({"last_processed_index": last_index, "results": results}, f) # Utiliser un espace d'indentation de 4 espaces pour rendre le JSON plus lisible
json.dump({"last_processed_index": last_index, "results": sorted_results}, f, indent=4)
# Sauvegarde les résultats temporaires dans un fichier TXT # Sauvegarde les résultats temporaires dans un fichier TXT
def save_temp_results(results): def save_temp_results(results):
@ -211,16 +219,13 @@ def send_to_ollama(text, target_lang=TARGET_LANGUAGE, model=OLLAMA_MODEL):
else: else:
raise Exception(f"Erreur Ollama: {response.text}") raise Exception(f"Erreur Ollama: {response.text}")
# Création du PDF final (inchangée) # Création du PDF final avec numéros de chapitres dans la marge
def create_pdf_from_results(results, output_path): def create_pdf_from_results(results, output_path):
"""Crée un PDF à partir des résultats de traduction.""" """Crée un PDF à partir des résultats de traduction, avec des notes dans la marge et un numéro de page."""
doc = SimpleDocTemplate(output_path, pagesize=letter, topMargin=inch, bottomMargin=inch)
story = [] story = []
# Enregistre une police qui supporte le cyrilique
font_name = register_unicode_font() font_name = register_unicode_font()
# Style personnalisé # Styles personnalisés
styles = getSampleStyleSheet() styles = getSampleStyleSheet()
title_style = ParagraphStyle( title_style = ParagraphStyle(
'CustomTitle', 'CustomTitle',
@ -231,7 +236,7 @@ def create_pdf_from_results(results, output_path):
alignment=TA_JUSTIFY, alignment=TA_JUSTIFY,
fontName=font_name fontName=font_name
) )
page_style = ParagraphStyle( page_style = ParagraphStyle(
'PageHeading', 'PageHeading',
parent=styles['Heading2'], parent=styles['Heading2'],
@ -241,7 +246,7 @@ def create_pdf_from_results(results, output_path):
spaceBefore=0.2*inch, spaceBefore=0.2*inch,
fontName=font_name fontName=font_name
) )
body_style = ParagraphStyle( body_style = ParagraphStyle(
'CustomBody', 'CustomBody',
parent=styles['BodyText'], parent=styles['BodyText'],
@ -250,27 +255,45 @@ def create_pdf_from_results(results, output_path):
spaceAfter=0.2*inch, spaceAfter=0.2*inch,
fontName=font_name fontName=font_name
) )
note_style = ParagraphStyle(
'CustomBody',
parent=styles['BodyText'],
fontSize=8,
alignment=TA_JUSTIFY,
spaceAfter=0,
fontName=font_name
)
# Création du document avec les callbacks pour les notes et le numéro de page
doc = SimpleDocTemplate(
output_path,
pagesize=letter,
topMargin=inch,
bottomMargin=inch,
)
# Titre avec la langue cible # Titre avec la langue cible
story.append(Paragraph(f"Traduction - Ukrainien vers {TARGET_LANGUAGE.capitalize()}", title_style)) story.append(Paragraph(f"Traduction - Ukrainien vers {TARGET_LANGUAGE.capitalize()}", title_style))
story.append(Paragraph(f"Document : {PDF_PATH}", title_style)) story.append(Paragraph(f"Document : {PDF_PATH}", title_style))
story.append(Spacer(1, 0.2*inch)) story.append(Spacer(1, 0.2*inch))
# Contenu # Contenu
for page_num, translation in results.items(): for paragraph_num, translation in results.items():
# Préserver la mise en page en convertissant les sauts de ligne
formatted_text = translation.replace("\n", "<br/>") formatted_text = translation.replace("\n", "<br/>")
if DEBUG:
# Ajoute le paragraphe avec sa note
story.append(Paragraph(paragraph_num, note_style))
story.append(Paragraph(formatted_text, body_style)) story.append(Paragraph(formatted_text, body_style))
# story.append(Spacer(1, 0.1*inch))
# Infos sur le LLM # Infos sur le LLM
story.append(Spacer(1, 0.2*inch)) story.append(Spacer(1, 0.2*inch))
story.append(Paragraph(display_llm_info(), page_style)) story.append(Paragraph(display_llm_info(), page_style))
# Construction du PDF # Construction du PDF
doc.build(story) doc.build(story)
print(f"PDF généré avec succès : {output_path}") print(f"PDF généré avec succès : {output_path}")
def create_txt_from_results(results, output_path): def create_txt_from_results(results, output_path):
"""Crée un fichier TXT à partir des résultats de traduction.""" """Crée un fichier TXT à partir des résultats de traduction."""
OUTPUT_TXT_PATH = output_path.replace(".pdf", f".txt") # Chemin du fichier TXT de sortie OUTPUT_TXT_PATH = output_path.replace(".pdf", f".txt") # Chemin du fichier TXT de sortie
@ -281,7 +304,10 @@ def create_txt_from_results(results, output_path):
txt_file.write(title_text + "\n\n") txt_file.write(title_text + "\n\n")
# Contenu # Contenu
for page_num, translation in results.items(): for paragraph_num, translation in results.items():
# Ajoute les numéro de paragraphe et chapitre
if(DEBUG): txt_file.write(f"{paragraph_num}\n")
# Préserver la mise en page en convertissant les sauts de ligne # Préserver la mise en page en convertissant les sauts de ligne
txt_file.write(translation + "\n\n") txt_file.write(translation + "\n\n")
@ -291,17 +317,46 @@ def create_txt_from_results(results, output_path):
# Fonction principale # Fonction principale
def main(): def main():
# Charge le checkpoint
checkpoint = load_checkpoint() checkpoint = load_checkpoint()
last_index = checkpoint["last_processed_index"] last_index = checkpoint["last_processed_index"]
results = checkpoint["results"] results = checkpoint["results"]
# Extraction des paragraphes
pages = extract_text_from_pdf(PDF_PATH) pages = extract_text_from_pdf(PDF_PATH)
paragraphs = split_pages_in_paragraphs(pages) paragraphs = split_pages_in_paragraphs(pages)
# Traitement des paragraphes # Liste de tous les indices de batches attendus (par pas de batch_size)
batch_size = 3 batch_size = 5
expected_batch_indices = list(range(0, len(paragraphs), batch_size))
# Liste des indices de batches déjà présents dans results
present_batch_indices = set()
for key in results.keys():
batch_start = int(int(key) // batch_size * batch_size) # Arrondit à l'indice de début de batch
present_batch_indices.add(batch_start)
# Trouve les batches manquants
missing_batches = [i for i in expected_batch_indices if i not in present_batch_indices and i <= last_index]
# Affichage des batches manquants (pour débogage)
print(f"Batches manquants détectés : {missing_batches}")
# Traduction des paragraphes manquants
for i in missing_batches:
batch = paragraphs[i:i + batch_size]
paragraph_cumul = "\n".join(batch)
print(f"{15 * '-'} Traduction des paragraphes manquants {i+1} à {min(i + batch_size, len(paragraphs))} / {len(paragraphs)}")
try:
result = send_to_ollama(paragraph_cumul)
print(f"{result}")
results[str(i)] = result
save_checkpoint(len(paragraphs), results) # Met à jour le dernier indice du batch
save_temp_results(results)
except Exception as e:
print(f"Erreur lors de la traduction du paragraphe {i}: {e}")
# Traitement des paragraphes suivants
for i in range(last_index + 1, len(paragraphs), batch_size): for i in range(last_index + 1, len(paragraphs), batch_size):
batch = paragraphs[i:i + batch_size] batch = paragraphs[i:i + batch_size]
paragraph_cumul = "\n".join(batch) paragraph_cumul = "\n".join(batch)
@ -311,18 +366,18 @@ def main():
try: try:
result = send_to_ollama(paragraph_cumul) result = send_to_ollama(paragraph_cumul)
print(f"{result}") print(f"{result}")
results[i] = result results[str(i)] = result
save_checkpoint(i, results) # Sauvegarde le checkpoint save_checkpoint(i + batch_size - 1, results)
save_temp_results(results) # Sauvegarde les résultats temporaires save_temp_results(results)
except Exception as e: except Exception as e:
print(f"Erreur : {e}") print(f"Erreur : {e}")
continue continue
# Génération des fichiers finaux
save_temp_results(results) save_temp_results(results)
create_pdf_from_results(results, FINAL_OUTPUT_PDF) create_pdf_from_results(results, FINAL_OUTPUT_PDF)
create_txt_from_results(results, FINAL_OUTPUT_TXT) create_txt_from_results(results, FINAL_OUTPUT_TXT)
print("Traduction terminée !") print("Traduction terminée !")
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -22,7 +22,7 @@ ollama create traductionUkrainienVersFrancais -f .\Modelfile
:: 1. Vérifie si le processus ollama.exe est en cours d'exécution :: 1. Vérifie si le processus ollama.exe est en cours d'exécution
tasklist | find "ollama.exe" >nul tasklist | find "ollama.exe" >nul
if %ERRORLEVEL% equ 0 ( if %ERRORLEVEL% equ 0 (
echo [OK] Le processus Ollama est en cours d'exécution. echo [OK] Le processus Ollama est en cours d'execution.
) else ( ) else (
echo [ERREUR] Ollama n'est pas lancé. echo [ERREUR] Ollama n'est pas lancé.
pause pause