Compare commits

..

No commits in common. "32b88aa9b86aaf62cc2835004cdecc7b7d4264ee" and "2f5fed58a7ee8271758259c142ad52b9265b92ad" have entirely different histories.

4 changed files with 92 additions and 139 deletions

2
.gitignore vendored
View File

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

View File

@ -1,14 +1,10 @@
FROM zongwei/gemma3-translator:4b FROM lauchacarro/qwen2.5-translator
PARAMETER temperature 0.9 PARAMETER temperature 0.2
PARAMETER num_ctx 131072 PARAMETER num_ctx 65536
SYSTEM """ SYSTEM """
Tu es un traducteur professionnel spécialisé dans la traduction de texte ukrainien en français. Tu es un traducteur professionnel spécialisé dans la traduction de textes historiques ukrainiens 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. Traduis avec précision et naturel, en respectant l'intonation originale utilisée par l'auteur du texte.
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. Tu dois toujours répondre en français et uniquement en français.
Le texte que tu traduis est un texte historique, tu ne dois pas le changer. 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.
""" """

167
main.py
View File

@ -12,14 +12,18 @@ import os
from datetime import datetime from datetime import datetime
# Configuration # Configuration
PDF_PATH = "TaniaBorecMemoir(Ukr).pdf" PDF_PATH = "TaniaBorecMemoir(Ukr).pdf" # Fichier original
OLLAMA_MODEL = "traductionUkrainienVersFrancais:latest" OLLAMA_MODEL = "traductionUkrainienVersFrancais:latest"
OLLAMA_URL = "http://localhost:11434/api/generate" OLLAMA_URL = "http://localhost:11434/api/generate" # URL par défaut d'Ollama
TARGET_LANGUAGE = "français" TARGET_LANGUAGE = "français" # Langue cible (ex: "français", "anglais", "allemand", "espagnol", etc.)
CHECKPOINT_FILE = "checkpoint.json"
TEMP_OUTPUT_TXT = "output_temp.txt" # Récupère la date et l'heure actuelles au format AAAMMJJ-HHMM
FINAL_OUTPUT_PDF = PDF_PATH.replace(".pdf",f" ({TARGET_LANGUAGE.upper()[:2]})_V2.pdf") current_datetime = datetime.now().strftime("%Y%m%d-%H%M")
FINAL_OUTPUT_TXT = PDF_PATH.replace(".pdf",f" ({TARGET_LANGUAGE.upper()[:2]})_V2.txt") # 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"
)
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."""
@ -143,6 +147,58 @@ def display_llm_info():
else: else:
return "Informations du modèle non disponibles" 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(): def register_unicode_font():
"""Enregistre une police TrueType qui supporte le cyrilique.""" """Enregistre une police TrueType qui supporte le cyrilique."""
# Recherche une police système qui supporte le cyrilique # Recherche une police système qui supporte le cyrilique
@ -164,54 +220,6 @@ def register_unicode_font():
print("Aucune police Unicode trouvée, utilisation d'Helvetica") print("Aucune police Unicode trouvée, utilisation d'Helvetica")
return '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): 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."""
doc = SimpleDocTemplate(output_path, pagesize=letter, topMargin=inch, bottomMargin=inch) doc = SimpleDocTemplate(output_path, pagesize=letter, topMargin=inch, bottomMargin=inch)
@ -289,40 +297,35 @@ def create_txt_from_results(results, output_path):
txt_file.write("\n") txt_file.write("\n")
txt_file.write(display_llm_info() + "\n") txt_file.write(display_llm_info() + "\n")
# Fonction principale
def main(): def main():
# Charge le checkpoint # Affiche les informations du modèle LLM
checkpoint = load_checkpoint() display_llm_info()
last_index = checkpoint["last_processed_index"]
results = checkpoint["results"] # Extraction du texte page par page
# Extraction des paragraphes
pages = extract_text_from_pdf(PDF_PATH) 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) paragraphs = split_pages_in_paragraphs(pages)
print(f"Nombre de paragraphes complets extraits : {len(paragraphs)}")
# Traitement des paragraphes # Dictionnaire pour stocker les résultats
batch_size = 3 results = {}
for i in range(last_index + 1, len(paragraphs), batch_size):
batch = paragraphs[i:i + batch_size] # Traitement des paragraphes complets
paragraph_cumul = "\n".join(batch) for i, paragraph_text in enumerate(paragraphs, start=1):
print(f"{15 * '-'} Traduction du paragraphe {i}/{len(paragraphs)}...")
print(f"{15 * '-'} Traduction des paragraphes {i+1} à {min(i + batch_size, len(paragraphs))} / {len(paragraphs)}")
try: try:
result = send_to_ollama(paragraph_cumul) result = send_to_ollama(paragraph_text, target_lang=TARGET_LANGUAGE)
print(f"{result}") print(f"{result}.")
results[i] = result results[i] = result
save_checkpoint(i, results) # Sauvegarde le checkpoint
save_temp_results(results) # Sauvegarde les résultats temporaires
except Exception as e: except Exception as e:
print(f"Erreur : {e}") print(f"Erreur lors du traitement du paragraphe {i} : {e}")
continue results[i] = f"Erreur lors du traitement du paragraphe {i} : {e}"
# Génération des fichiers finaux # Création du PDF avec tous les résultats
save_temp_results(results) create_pdf_from_results(results, OUTPUT_PDF_PATH)
create_pdf_from_results(results, FINAL_OUTPUT_PDF) create_txt_from_results(results, OUTPUT_PDF_PATH)
create_txt_from_results(results, FINAL_OUTPUT_TXT)
print("Traduction terminée !")
if __name__ == "__main__": if __name__ == "__main__":
main() main()

44
run.bat
View File

@ -1,44 +0,0 @@
@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