// 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);
}