Compare commits

..

6 Commits

Author SHA1 Message Date
Alex
32b88aa9b8 lancement depuis le shel 2026-01-05 12:28:01 +01:00
Alex
dacd665535 update 2026-01-05 12:00:23 +01:00
Alex
a37b962a05 meilleur LLM de traduction 2026-01-05 12:00:03 +01:00
Alex
ca862aa9e3 Ajouts des fonctions manquantes 2026-01-05 11:59:35 +01:00
Alex
71786d27c6 reprise du programme en cas d'erreur ou plantage 2026-01-05 11:04:51 +01:00
Alex
d9fae5f658 traduction de 7 en 7 paragraphes 2026-01-05 09:48:22 +01:00
4 changed files with 141 additions and 94 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
output_temp.txt
checkpoint.json

View File

@ -1,10 +1,14 @@
FROM lauchacarro/qwen2.5-translator
PARAMETER temperature 0.2
PARAMETER num_ctx 65536
FROM zongwei/gemma3-translator:4b
PARAMETER temperature 0.9
PARAMETER num_ctx 131072
SYSTEM """
Tu es un traducteur professionnel spécialisé dans la traduction de textes historiques ukrainiens en français.
Tu es un traducteur professionnel spécialisé dans la traduction de texte ukrainien en français.
Tu est spécialisé dans la rédaction des textes auto-biographiques et historiques et parles un français impécable.
Tu dois toujours répondre en français et uniquement dans cette langue.
Tu prends soins des congugaisons et de la tournure de phases.
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.
N'inclus jamais la phrase "Voici le texte traduit" dans la réponse.
Traduis avec précision et naturel, en respectant l'intonation originale utilisée par l'auteur du texte.
Tu dois toujours répondre en français et uniquement en français.
Tu ne dois pas interpréter les pensées ou les réflexions de l'auteur.
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.
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.
Le texte que tu traduis est un texte historique, tu ne dois pas le changer.
"""

171
main.py
View File

@ -12,18 +12,14 @@ import os
from datetime import datetime
# Configuration
PDF_PATH = "TaniaBorecMemoir(Ukr).pdf" # Fichier original
PDF_PATH = "TaniaBorecMemoir(Ukr).pdf"
OLLAMA_MODEL = "traductionUkrainienVersFrancais:latest"
OLLAMA_URL = "http://localhost:11434/api/generate" # URL par défaut d'Ollama
TARGET_LANGUAGE = "français" # Langue cible (ex: "français", "anglais", "allemand", "espagnol", etc.)
# Récupère la date et l'heure actuelles au format AAAMMJJ-HHMM
current_datetime = datetime.now().strftime("%Y%m%d-%H%M")
# Ajoute la date et la langue cible au nom du fichier PDF de sortie
OUTPUT_PDF_PATH = PDF_PATH.replace(
".pdf",
f" ({TARGET_LANGUAGE.upper()[:2]})_{current_datetime}.pdf"
)
OLLAMA_URL = "http://localhost:11434/api/generate"
TARGET_LANGUAGE = "français"
CHECKPOINT_FILE = "checkpoint.json"
TEMP_OUTPUT_TXT = "output_temp.txt"
FINAL_OUTPUT_PDF = PDF_PATH.replace(".pdf",f" ({TARGET_LANGUAGE.upper()[:2]})_V2.pdf")
FINAL_OUTPUT_TXT = PDF_PATH.replace(".pdf",f" ({TARGET_LANGUAGE.upper()[:2]})_V2.txt")
def extract_parameters_from_template(template_str):
"""Extrait les paramètres du modèle à partir du template."""
@ -147,58 +143,6 @@ def display_llm_info():
else:
return "Informations du modèle non disponibles"
def extract_text_from_pdf(pdf_path):
"""Extrait le texte page par page d'un PDF sans les numéros de pages."""
import re
text_by_page = []
with open(pdf_path, "rb") as file:
reader = PyPDF2.PdfReader(file)
for page in reader.pages:
text = page.extract_text()
# Supprime les numéros de pages (nombres seuls en début/fin de ligne)
text = re.sub(r'^\d+\s*\n', '', text, flags=re.MULTILINE)
text = re.sub(r'\n\s*\d+\s*$', '', text, flags=re.MULTILINE)
text_by_page.append(text)
return text_by_page
def split_pages_in_paragraphs(pages_text):
"""
Divise le texte en paragraphes en détectant un point suivi d'un saut de ligne ou d'un retour à la ligne.
Conserve les sauts de ligne à l'intérieur des paragraphes.
"""
import re
# Concatène tout le texte
full_text = "\n".join(pages_text)
# Remplace les sauts de ligne à l'intérieur des paragraphes par des espaces
# (pour éviter les sauts de ligne intempestifs dans un même paragraphe)
full_text = re.sub(r'(?<![.!?])\n+(?![.!?])', ' ', full_text)
# Divise le texte en paragraphes : un point suivi d'un saut de ligne
paragraphs = re.split(r'(?<=[.!?])\s*\n+', full_text.strip())
# Nettoie chaque paragraphe : remplace les sauts de ligne restants par des espaces
paragraphs = [re.sub(r'\s+', ' ', p).strip() for p in paragraphs if p.strip()]
return paragraphs
def send_to_ollama(text, target_lang=TARGET_LANGUAGE, model=OLLAMA_MODEL, context_size=128000):
"""Envoie une requête à Ollama et retourne la réponse traduite."""
# Construit le prompt avec les instructions système et la demande de traduction
full_prompt = f"\n\nTraduis le texte suivant de l'ukrainien vers le {target_lang} :\n{text}"
payload = {
"model": model,
"prompt": full_prompt,
"stream": False,
"options": {"num_ctx": context_size}
}
response = requests.post(OLLAMA_URL, data=json.dumps(payload))
if response.status_code == 200:
return response.json()["response"]
else:
raise Exception(f"Erreur Ollama: {response.text}")
def register_unicode_font():
"""Enregistre une police TrueType qui supporte le cyrilique."""
# Recherche une police système qui supporte le cyrilique
@ -220,6 +164,54 @@ def register_unicode_font():
print("Aucune police Unicode trouvée, utilisation d'Helvetica")
return 'Helvetica'
# Charge ou initialise le checkpoint
def load_checkpoint():
if os.path.exists(CHECKPOINT_FILE):
with open(CHECKPOINT_FILE, "r") as f:
return json.load(f)
return {"last_processed_index": -1, "results": {}}
# Sauvegarde le checkpoint
def save_checkpoint(last_index, results):
with open(CHECKPOINT_FILE, "w") as f:
json.dump({"last_processed_index": last_index, "results": results}, f)
# Sauvegarde les résultats temporaires dans un fichier TXT
def save_temp_results(results):
with open(TEMP_OUTPUT_TXT, "w", encoding="utf-8") as f:
for idx, translation in results.items():
f.write(f"Paragraphe {idx}:\n{translation}\n\n")
# Extraction du texte du PDF (inchangée)
def extract_text_from_pdf(pdf_path):
text_by_page = []
with open(pdf_path, "rb") as file:
reader = PyPDF2.PdfReader(file)
for page in reader.pages:
text = page.extract_text()
text_by_page.append(text)
return text_by_page
# Découpage en paragraphes (inchangé)
def split_pages_in_paragraphs(pages_text):
import re
full_text = "\n".join(pages_text)
full_text = re.sub(r'(?<![.!?])\n+(?![.!?])', ' ', full_text)
paragraphs = re.split(r'(?<=[.!?])\s*\n+', full_text.strip())
paragraphs = [re.sub(r'\s+', ' ', p).strip() for p in paragraphs if p.strip()]
return paragraphs
# Envoi à Ollama (inchangé)
def send_to_ollama(text, target_lang=TARGET_LANGUAGE, model=OLLAMA_MODEL):
full_prompt = f"\n\nTraduis le texte suivant de l'ukrainien vers le {target_lang} :\n{text}"
payload = {"model": model, "prompt": full_prompt, "stream": False}
response = requests.post(OLLAMA_URL, data=json.dumps(payload))
if response.status_code == 200:
return response.json()["response"]
else:
raise Exception(f"Erreur Ollama: {response.text}")
# Création du PDF final (inchangée)
def create_pdf_from_results(results, output_path):
"""Crée un PDF à partir des résultats de traduction."""
doc = SimpleDocTemplate(output_path, pagesize=letter, topMargin=inch, bottomMargin=inch)
@ -297,35 +289,40 @@ def create_txt_from_results(results, output_path):
txt_file.write("\n")
txt_file.write(display_llm_info() + "\n")
# Fonction principale
def main():
# Affiche les informations du modèle LLM
display_llm_info()
# Extraction du texte page par page
pages = extract_text_from_pdf(PDF_PATH)
print(f"Nombre de pages extraites : {len(pages)}")
# Fusion des paragraphes qui s'étendent sur plusieurs pages
paragraphs = split_pages_in_paragraphs(pages)
print(f"Nombre de paragraphes complets extraits : {len(paragraphs)}")
# Charge le checkpoint
checkpoint = load_checkpoint()
last_index = checkpoint["last_processed_index"]
results = checkpoint["results"]
# Extraction des paragraphes
pages = extract_text_from_pdf(PDF_PATH)
paragraphs = split_pages_in_paragraphs(pages)
# Traitement des paragraphes
batch_size = 3
for i in range(last_index + 1, len(paragraphs), batch_size):
batch = paragraphs[i:i + batch_size]
paragraph_cumul = "\n".join(batch)
print(f"{15 * '-'} Traduction des paragraphes {i+1} à {min(i + batch_size, len(paragraphs))} / {len(paragraphs)}")
# Dictionnaire pour stocker les résultats
results = {}
# Traitement des paragraphes complets
for i, paragraph_text in enumerate(paragraphs, start=1):
print(f"{15 * '-'} Traduction du paragraphe {i}/{len(paragraphs)}...")
try:
result = send_to_ollama(paragraph_text, target_lang=TARGET_LANGUAGE)
print(f"{result}.")
result = send_to_ollama(paragraph_cumul)
print(f"{result}")
results[i] = result
save_checkpoint(i, results) # Sauvegarde le checkpoint
save_temp_results(results) # Sauvegarde les résultats temporaires
except Exception as e:
print(f"Erreur lors du traitement du paragraphe {i} : {e}")
results[i] = f"Erreur lors du traitement du paragraphe {i} : {e}"
# Création du PDF avec tous les résultats
create_pdf_from_results(results, OUTPUT_PDF_PATH)
create_txt_from_results(results, OUTPUT_PDF_PATH)
print(f"Erreur : {e}")
continue
# Génération des fichiers finaux
save_temp_results(results)
create_pdf_from_results(results, FINAL_OUTPUT_PDF)
create_txt_from_results(results, FINAL_OUTPUT_TXT)
print("Traduction terminée !")
if __name__ == "__main__":
main()

44
run.bat Normal file
View File

@ -0,0 +1,44 @@
@echo off
setlocal
set OLLAMA_PORT=11434
set OLLAMA_HOST=localhost
REM Récupérer le répertoire courant
set CURRENT_DIR=%cd%
REM Chemin vers l'environnement virtuel Python (relatif au répertoire courant)
set VENV_PATH=%CURRENT_DIR%\venv
REM Chemin vers votre script principal (relatif au répertoire courant)
set MAIN_SCRIPT_PATH=%CURRENT_DIR%\main.py
REM Activer l'environnement virtuel Python
call %VENV_PATH%\Scripts\activate.bat
REM Lancer la compilation du modèle LLM pour Ollama
ollama create traductionUkrainienVersFrancais -f .\Modelfile
:: 1. Vérifie si le processus ollama.exe est en cours d'exécution
tasklist | find "ollama.exe" >nul
if %ERRORLEVEL% equ 0 (
echo [OK] Le processus Ollama est en cours d'exécution.
) else (
echo [ERREUR] Ollama n'est pas lancé.
pause
exit /b 1
)
:: 2. Vérifie si le port 11434 est ouvert en local
echo Verification du port %OLLAMA_PORT% sur %OLLAMA_HOST%...
powershell -Command "$tcp = New-Object System.Net.Sockets.TcpClient; $connect = $tcp.ConnectAsync('%OLLAMA_HOST%', %OLLAMA_PORT%); $connect.Wait(1000); if ($tcp.Connected) { echo 'Port %OLLAMA_PORT% ouvert en local.'; $tcp.Close() } else { echo 'Port %OLLAMA_PORT% non accessible en local.'; exit 1 }"
if %ERRORLEVEL% neq 0 (
echo [ERREUR] Le port %OLLAMA_PORT% n'est pas accessible en local.
pause
exit /b 1
)
REM Exécuter le script principal
python %MAIN_SCRIPT_PATH%
endlocal