Spaces:
Running
Running
// 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 = ` | |
<i class="bi bi-file-earmark-text me-1"></i> | |
<strong>${file.name}</strong> • ${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 = ` | |
<i class="bi bi-gear-fill animate-spin me-2"></i> | |
${message} | |
`; | |
} else { | |
loadingElement.style.display = 'none'; | |
} | |
} | |
function updateProgress(percent, message) { | |
progressBar.style.width = `${percent}%`; | |
progressPercent.textContent = `${percent}%`; | |
if (message) { | |
document.getElementById('loadingText').innerHTML = ` | |
<i class="bi bi-gear-fill animate-spin me-2"></i> | |
${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 = ` | |
<i class="bi ${style.icon} me-2"></i> | |
${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); | |
} |