// Configuration const MAX_FILE_SIZE_MB = 15; // Augmenté à 15 Mo const PROCESSING_TIMEOUT = 600000; // 10 min en ms (augmenté) const SUPPORTED_FORMATS = ['pdf', 'docx', 'xlsx', 'pptx']; // Formats supportés // Éléments UI const uploadForm = document.getElementById('uploadForm'); const fileInput = document.getElementById('document'); const targetLangSelect = document.getElementById('targetLang'); const loadingElement = document.getElementById('loading'); const resultArea = document.getElementById('resultArea'); const downloadBtn = document.getElementById('downloadBtn'); const sizeWarning = document.getElementById('sizeWarning'); const progressContainer = document.getElementById('progressContainer'); const progressBar = document.getElementById('progressBar'); const progressPercent = document.getElementById('progressPercent'); const copyTranslatedBtn = document.getElementById('copyTranslatedBtn'); const newTranslationBtn = document.getElementById('newTranslationBtn'); // Initialisation document.addEventListener('DOMContentLoaded', () => { initEventListeners(); checkFileSupport(); }); function initEventListeners() { uploadForm.addEventListener('submit', handleFormSubmit); fileInput.addEventListener('change', handleFileSelect); copyTranslatedBtn.addEventListener('click', copyTranslatedText); newTranslationBtn.addEventListener('click', resetForm); } function checkFileSupport() { const fileSupportInfo = document.createElement('div'); fileSupportInfo.className = 'small text-muted mt-2'; fileSupportInfo.innerHTML = `Formats supportés: ${SUPPORTED_FORMATS.join(', ').toUpperCase()}`; fileInput.parentNode.insertBefore(fileSupportInfo, fileInput.nextSibling); } async function handleFormSubmit(e) { e.preventDefault(); const file = fileInput.files[0]; const targetLang = targetLangSelect.value; if (!file) { showToast('Veuillez sélectionner un fichier', 'warning'); return; } // Validation du fichier if (!validateFile(file)) return; // UI Loading state setLoadingState(true, 'Préparation du document...'); resultArea.style.display = 'none'; sizeWarning.style.display = file.size > 5 * 1024 * 1024 ? 'block' : 'none'; try { // Traduction avec suivi de progression const data = await translateFile(file, targetLang); // Affichage des résultats displayResults(data, file); showToast('Traduction terminée avec succès!', 'success'); } catch (error) { handleTranslationError(error); } finally { setLoadingState(false); } } function validateFile(file) { const fileExtension = file.name.split('.').pop().toLowerCase(); if (!SUPPORTED_FORMATS.includes(fileExtension)) { showToast(`Format non supporté: ${fileExtension.toUpperCase()}`, 'error'); return false; } if (file.size > MAX_FILE_SIZE_MB * 1024 * 1024) { showToast(`Fichier trop volumineux (> ${MAX_FILE_SIZE_MB} Mo)`, 'error'); return false; } return true; } function handleFileSelect(e) { const file = e.target.files[0]; if (!file) return; const fileInfo = document.getElementById('fileInfo'); const sizeMB = (file.size / (1024 * 1024)).toFixed(2); fileInfo.innerHTML = ` ${file.name} • ${sizeMB} MB `; // Animation feedback document.querySelector('.card').classList.add('animate__animated', 'animate__pulse'); setTimeout(() => { document.querySelector('.card').classList.remove('animate__animated', 'animate__pulse'); }, 1000); // Avertissement pour fichiers volumineux sizeWarning.style.display = file.size > 5 * 1024 * 1024 ? 'block' : 'none'; if (file.size > 5 * 1024 * 1024) { document.getElementById('warningText').textContent = `Fichier volumineux (${sizeMB} MB) - Le traitement peut prendre plusieurs minutes`; } } async function translateFile(file, targetLang) { const formData = new FormData(); formData.append('file', file); formData.append('target_lang', targetLang); // Configuration du timeout et suivi de progression const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), PROCESSING_TIMEOUT); // Affichage de la progression progressContainer.style.display = 'block'; updateProgress(10, 'Analyse du document...'); try { const response = await fetch('/api/translate', { method: 'POST', body: formData, signal: controller.signal }); clearTimeout(timeoutId); if (!response.ok) { const error = await response.json().catch(() => ({ detail: response.statusText })); throw new Error(error.detail || 'Erreur lors de la traduction'); } updateProgress(100, 'Traduction terminée'); return await response.json(); } catch (error) { throw error; } finally { setTimeout(() => { progressContainer.style.display = 'none'; }, 1000); } } function displayResults(data, file) { document.getElementById('filename').textContent = data.filename; document.getElementById('originalText').textContent = data.original_text; document.getElementById('translatedText').textContent = data.translated_text; const sizeMB = (file.size / (1024 * 1024)).toFixed(2); document.getElementById('fileSize').textContent = `(${sizeMB} MB)`; document.getElementById('infoText').textContent = `Document traduit en ${targetLangSelect.options[targetLangSelect.selectedIndex].text}`; // Préparer le téléchargement downloadBtn.onclick = () => downloadTranslated(file, targetLangSelect.value); resultArea.style.display = 'block'; } async function downloadTranslated(file, targetLang) { setLoadingState(true, 'Préparation du téléchargement...'); try { const formData = new FormData(); formData.append('file', file); formData.append('target_lang', targetLang); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), PROCESSING_TIMEOUT); const response = await fetch('/api/download_translated', { method: 'POST', body: formData, signal: controller.signal }); clearTimeout(timeoutId); if (!response.ok) { const error = await response.json().catch(() => ({ detail: response.statusText })); throw new Error(error.detail || 'Erreur lors du téléchargement'); } const blob = await response.blob(); if (!blob.type.includes('openxmlformats-officedocument.wordprocessingml')) { throw new Error("Format de fichier invalide"); } const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `TRADUCTION_${file.name.replace(/\.[^/.]+$/, "")}.docx`; document.body.appendChild(a); a.click(); setTimeout(() => { document.body.removeChild(a); URL.revokeObjectURL(url); }, 100); showToast('Téléchargement terminé!', 'success'); } catch (error) { console.error('Erreur de téléchargement:', error); showToast(`Échec du téléchargement: ${error.message}`, 'error'); } finally { setLoadingState(false); } } function copyTranslatedText() { const translatedText = document.getElementById('translatedText').textContent; navigator.clipboard.writeText(translatedText) .then(() => showToast('Texte copié dans le presse-papiers!', 'success')) .catch(err => showToast('Échec de la copie', 'error')); } function resetForm() { uploadForm.reset(); document.getElementById('fileInfo').innerHTML = 'Aucun fichier sélectionné'; resultArea.style.display = 'none'; sizeWarning.style.display = 'none'; showToast('Prêt pour une nouvelle traduction', 'info'); } function setLoadingState(isLoading, message = '') { if (isLoading) { loadingElement.style.display = 'block'; document.getElementById('loadingText').innerHTML = ` ${message} `; } else { loadingElement.style.display = 'none'; } } function updateProgress(percent, message) { progressBar.style.width = `${percent}%`; progressPercent.textContent = `${percent}%`; if (message) { document.getElementById('loadingText').innerHTML = ` ${message} (${percent}%) `; } } function handleTranslationError(error) { console.error('Erreur:', error); let errorMessage = 'Une erreur est survenue'; if (error.name === 'AbortError') { errorMessage = `Délai dépassé (${PROCESSING_TIMEOUT/1000}s). Essayez avec un fichier plus court.`; } else { errorMessage = error.message || error.toString(); } showToast(errorMessage, 'error'); } function showToast(message, type = 'info') { const toast = document.getElementById('toastNotification'); const toastMessage = document.getElementById('toastMessage'); // Configuration du style selon le type const typeStyles = { success: { bg: '#2ecc71', icon: 'bi-check-circle-fill' }, error: { bg: '#e74c3c', icon: 'bi-exclamation-circle-fill' }, warning: { bg: '#f39c12', icon: 'bi-exclamation-triangle-fill' }, info: { bg: '#3498db', icon: 'bi-info-circle-fill' } }; const style = typeStyles[type] || typeStyles.info; toast.style.backgroundColor = style.bg; toastMessage.innerHTML = ` ${message} `; // Affichage avec animation toast.style.display = 'flex'; toast.style.animation = 'fadeIn 0.5s'; // Masquage après 5 secondes setTimeout(() => { toast.style.animation = 'fadeIn 0.5s reverse'; setTimeout(() => { toast.style.display = 'none'; }, 500); }, 5000); }