Spaces:
Running
Running
| <html lang="fr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>AI Document Translator Pro</title> | |
| <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> | |
| <style> | |
| :root { | |
| --primary-color: #4361ee; | |
| --secondary-color: #3f37c9; | |
| --accent-color: #4cc9f0; | |
| --dark-color: #1a1a2e; | |
| --light-color: #f8f9fa; | |
| --success-color: #4bb543; | |
| } | |
| body { | |
| background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); | |
| color: var(--dark-color); | |
| min-height: 100vh; | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| } | |
| .main-container { | |
| max-width: 800px; | |
| background: white; | |
| padding: 2rem; | |
| border-radius: 15px; | |
| box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); | |
| margin: 2rem auto; | |
| } | |
| .header { | |
| text-align: center; | |
| margin-bottom: 2rem; | |
| position: relative; | |
| } | |
| .logo { | |
| font-size: 2.5rem; | |
| color: var(--primary-color); | |
| margin-bottom: 0.5rem; | |
| } | |
| .title { | |
| font-weight: 700; | |
| color: var(--dark-color); | |
| margin-bottom: 0.5rem; | |
| } | |
| .subtitle { | |
| color: #6c757d; | |
| font-size: 1rem; | |
| } | |
| .upload-section { | |
| background: rgba(67, 97, 238, 0.05); | |
| border: 2px dashed rgba(67, 97, 238, 0.3); | |
| border-radius: 10px; | |
| padding: 2rem; | |
| margin-bottom: 2rem; | |
| transition: all 0.3s ease; | |
| } | |
| .upload-section:hover { | |
| border-color: var(--primary-color); | |
| background: rgba(67, 97, 238, 0.1); | |
| } | |
| .file-info { | |
| background: var(--light-color); | |
| padding: 0.5rem; | |
| border-radius: 5px; | |
| font-size: 0.9rem; | |
| margin-top: 1rem; | |
| } | |
| .form-control, .form-select { | |
| border-radius: 8px; | |
| padding: 0.75rem 1rem; | |
| border: 1px solid #ced4da; | |
| transition: all 0.3s ease; | |
| } | |
| .form-control:focus, .form-select:focus { | |
| border-color: var(--primary-color); | |
| box-shadow: 0 0 0 0.25rem rgba(67, 97, 238, 0.25); | |
| } | |
| .btn-primary { | |
| background-color: var(--primary-color); | |
| border-color: var(--primary-color); | |
| border-radius: 8px; | |
| padding: 0.75rem 1.5rem; | |
| font-weight: 600; | |
| transition: all 0.3s ease; | |
| } | |
| .btn-primary:hover { | |
| background-color: var(--secondary-color); | |
| border-color: var(--secondary-color); | |
| transform: translateY(-2px); | |
| } | |
| .result-container { | |
| margin-top: 2rem; | |
| } | |
| .result-box { | |
| background: white; | |
| border: 1px solid #e9ecef; | |
| border-radius: 10px; | |
| padding: 1.5rem; | |
| min-height: 200px; | |
| max-height: 400px; | |
| overflow-y: auto; | |
| box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.05); | |
| } | |
| .result-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 1rem; | |
| padding-bottom: 0.5rem; | |
| border-bottom: 1px solid #e9ecef; | |
| } | |
| .action-buttons { | |
| display: flex; | |
| gap: 0.5rem; | |
| } | |
| .action-btn { | |
| background: var(--light-color); | |
| border: none; | |
| width: 36px; | |
| height: 36px; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: var(--dark-color); | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| } | |
| .action-btn:hover { | |
| background: var(--primary-color); | |
| color: white; | |
| transform: scale(1.1); | |
| } | |
| .file-preview { | |
| margin-top: 1rem; | |
| padding: 1rem; | |
| background: var(--light-color); | |
| border-radius: 8px; | |
| max-height: 200px; | |
| overflow-y: auto; | |
| } | |
| .language-selector { | |
| display: flex; | |
| gap: 1rem; | |
| margin-bottom: 1.5rem; | |
| } | |
| .language-box { | |
| flex: 1; | |
| background: white; | |
| border-radius: 10px; | |
| padding: 1rem; | |
| box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05); | |
| } | |
| .language-label { | |
| font-size: 0.9rem; | |
| color: #6c757d; | |
| margin-bottom: 0.5rem; | |
| display: block; | |
| } | |
| .progress-container { | |
| margin-top: 1rem; | |
| display: none; | |
| } | |
| .progress-bar { | |
| background-color: var(--primary-color); | |
| border-radius: 5px; | |
| height: 8px; | |
| transition: width 0.3s ease; | |
| } | |
| @media (max-width: 768px) { | |
| .main-container { | |
| padding: 1rem; | |
| margin: 1rem; | |
| } | |
| .language-selector { | |
| flex-direction: column; | |
| gap: 0.5rem; | |
| } | |
| } | |
| /* Animation */ | |
| @keyframes pulse { | |
| 0% { transform: scale(1); } | |
| 50% { transform: scale(1.05); } | |
| 100% { transform: scale(1); } | |
| } | |
| .pulse { | |
| animation: pulse 2s infinite; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="main-container"> | |
| <div class="header"> | |
| <div class="logo"> | |
| <i class="fas fa-language"></i> | |
| </div> | |
| <h1 class="title">AI Document Translator Pro</h1> | |
| <p class="subtitle">Traduisez vos documents en un clic avec une intelligence artificielle avancée</p> | |
| </div> | |
| <div class="upload-section text-center"> | |
| <form id="uploadForm"> | |
| <div class="mb-3"> | |
| <label for="fileInput" class="form-label"> | |
| <i class="fas fa-cloud-upload-alt fa-3x mb-3" style="color: #4361ee;"></i> | |
| <p class="text-muted"> cliquez pour parcourir vos fichiers</p> | |
| </label> | |
| <input type="file" id="fileInput" name="file" class="form-control d-none" required> | |
| <div class="file-info"> | |
| Formats supportés: <b>TXT, PDF, DOCX, PPTX, XLSX</b> (Max 10MB) | |
| </div> | |
| </div> | |
| <div class="language-selector"> | |
| <div class="language-box"> | |
| <span class="language-label">Langue source</span> | |
| <select id="src_lang" name="src_lang" class="form-select"> | |
| <option >Francais</option> | |
| <option >Anglais</option> | |
| <option>Espagnol</option> | |
| <option >Arabe</option> | |
| </select> | |
| </div> | |
| <div class="language-box"> | |
| <span class="language-label">Langue cible</span> | |
| <select id="tgt_lang" name="tgt_lang" class="form-select"> | |
| <option >Francais</option> | |
| <option >Anglais</option> | |
| <option>Espagnol</option> | |
| <option >Arabe</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="progress-container" id="progressContainer"> | |
| <div class="d-flex justify-content-between mb-1"> | |
| <small>Progression</small> | |
| <small id="progressPercent">0%</small> | |
| </div> | |
| <div class="progress" style="height: 8px;"> | |
| <div class="progress-bar" id="progressBar" role="progressbar" style="width: 0%"></div> | |
| </div> | |
| </div> | |
| <button type="submit" class="btn btn-primary mt-3 pulse"> | |
| <i class="fas fa-exchange-alt me-2"></i> Traduire le document | |
| </button> | |
| </form> | |
| </div> | |
| <div class="result-container"> | |
| <div class="result-header"> | |
| <h5><i class="fas fa-file-alt me-2"></i> Résultat de la traduction</h5> | |
| <div class="action-buttons"> | |
| <button class="action-btn" id="openFile" title="Ouvrir le fichier"> | |
| <i class="fas fa-folder-open"></i> | |
| </button> | |
| <button class="action-btn" id="copyText" title="Copier le texte"> | |
| <i class="fas fa-copy"></i> | |
| </button> | |
| <button class="action-btn" id="speakText" title="Lire à voix haute"> | |
| <i class="fas fa-volume-up"></i> | |
| </button> | |
| <button class="action-btn" id="stopSpeaking" title="Arrêter la lecture" style="display: none;"> | |
| <i class="fas fa-stop"></i> | |
| </button> | |
| <button class="action-btn" id="downloadTxt" title="Télécharger en TXT"> | |
| <i class="fas fa-file-download"></i> | |
| </button> | |
| <button class="action-btn" id="downloadPdf" title="Télécharger en PDF"> | |
| <i class="fas fa-file-pdf"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="result-box" id="result"> | |
| <div class="text-center text-muted py-5"> | |
| <i class="fas fa-language fa-3x mb-3"></i> | |
| <p>Aucun résultat pour le moment.<br>Votre traduction apparaîtra ici.</p> | |
| </div> | |
| </div> | |
| <div class="file-preview" id="filePreview" style="display: none;"> | |
| <h6><i class="fas fa-file me-2"></i> Aperçu du fichier</h6> | |
| <div id="previewContent"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| $(document).ready(function() { | |
| // Afficher le sélecteur de fichier lorsque l'utilisateur clique sur la zone de dépôt | |
| $('.upload-section').click(function() { | |
| $('#fileInput').click(); | |
| }); | |
| // Afficher le nom du fichier sélectionné | |
| $('#fileInput').change(function() { | |
| const file = this.files[0]; | |
| if (file) { | |
| $('.file-info').html(`<i class="fas fa-file me-1"></i> ${file.name} (${formatFileSize(file.size)})`); | |
| // Afficher l'aperçu du fichier si c'est un fichier texte | |
| if (file.type === "text/plain" || file.name.endsWith('.txt')) { | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| $('#previewContent').text(e.target.result.substring(0, 1000)); | |
| $('#filePreview').show(); | |
| }; | |
| reader.readAsText(file); | |
| } else { | |
| $('#filePreview').hide(); | |
| } | |
| } | |
| }); | |
| // Soumission du formulaire avec gestion de la progression | |
| $('#uploadForm').submit(async function(event) { | |
| event.preventDefault(); | |
| const formData = new FormData(this); | |
| const file = $('#fileInput')[0].files[0]; | |
| if (!file) { | |
| Swal.fire({ | |
| icon: 'error', | |
| title: 'Erreur', | |
| text: 'Veuillez sélectionner un fichier à traduire', | |
| confirmButtonColor: '#4361ee' | |
| }); | |
| return; | |
| } | |
| // Afficher la barre de progression | |
| // $('#progressContainer').show(); | |
| // Configuration de SweetAlert avec barre de progression | |
| let timerInterval; | |
| Swal.fire({ | |
| title: 'Traduction en cours', | |
| html: 'Votre document est en cours de traitement...<br><div class="progress mt-3" style="height: 8px;"><div class="progress-bar progress-bar-striped progress-bar-animated" style="width: 100%"></div></div>', | |
| allowOutsideClick: false, | |
| didOpen: () => { | |
| Swal.showLoading(); | |
| }, | |
| willClose: () => { | |
| clearInterval(timerInterval); | |
| } | |
| }); | |
| try { | |
| // Simulation de progression (à remplacer par votre vrai appel API) | |
| simulateProgress(); | |
| // Envoi réel à l'API | |
| let response = await fetch('https://rayhane123-ai-traduction.hf.space/upload/', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| let result = await response.json(); | |
| Swal.close(); | |
| if (result.translated_text) { | |
| $('#result').html(`<div class="translated-content">${formatTranslatedText(result.translated_text)}</div>`); | |
| Swal.fire({ | |
| icon: 'success', | |
| title: 'Traduction terminée!', | |
| text: 'Votre document a été traduit avec succès', | |
| confirmButtonColor: '#4361ee' | |
| }); | |
| } else { | |
| $('#result').html(`<div class="alert alert-danger">❌ Erreur: ${result.detail || 'Une erreur est survenue lors de la traduction'}</div>`); | |
| Swal.fire({ | |
| icon: 'error', | |
| title: 'Erreur', | |
| text: result.detail || 'Une erreur est survenue lors de la traduction', | |
| confirmButtonColor: '#4361ee' | |
| }); | |
| } | |
| } catch (error) { | |
| console.error('Error:', error); | |
| Swal.fire({ | |
| icon: 'error', | |
| title: 'Erreur', | |
| text: 'Une erreur est survenue lors de la connexion au serveur', | |
| confirmButtonColor: '#4361ee' | |
| }); | |
| $('#result').html(`<div class="alert alert-danger">❌ Erreur de connexion au serveur</div>`); | |
| } finally { | |
| $('#progressContainer').hide(); | |
| } | |
| }); | |
| // Fonction pour simuler la progression (à supprimer dans la version finale) | |
| function simulateProgress() { | |
| let progress = 0; | |
| const interval = setInterval(() => { | |
| progress += Math.random() * 10; | |
| if (progress > 100) { | |
| progress = 100; | |
| clearInterval(interval); | |
| } | |
| $('#progressBar').css('width', `${progress}%`); | |
| $('#progressPercent').text(`${Math.round(progress)}%`); | |
| }, 300); | |
| } | |
| // Formatage du texte traduit avec mise en page améliorée | |
| function formatTranslatedText(text) { | |
| // Ajoute des paragraphes et une meilleure mise en forme | |
| return text.split('\n').map(paragraph => { | |
| return paragraph.trim() ? `<p>${paragraph}</p>` : ''; | |
| }).join(''); | |
| } | |
| // Formatage de la taille du fichier | |
| function formatFileSize(bytes) { | |
| if (bytes === 0) return '0 Bytes'; | |
| const k = 1024; | |
| const sizes = ['Bytes', 'KB', 'MB', 'GB']; | |
| const i = Math.floor(Math.log(bytes) / Math.log(k)); | |
| return parseFloat((bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i]); | |
| } | |
| // Ouvrir le fichier sélectionné | |
| $('#openFile').click(function() { | |
| const fileInput = $('#fileInput')[0]; | |
| if (fileInput.files.length === 0) { | |
| Swal.fire({ | |
| icon: 'warning', | |
| title: 'Aucun fichier', | |
| text: 'Veuillez sélectionner un fichier d\'abord', | |
| confirmButtonColor: '#4361ee' | |
| }); | |
| return; | |
| } | |
| const file = fileInput.files[0]; | |
| const fileURL = URL.createObjectURL(file); | |
| window.open(fileURL, '_blank'); | |
| }); | |
| // 📋 Copier le texte traduit | |
| $('#copyText').click(function() { | |
| let text = $('#result .translated-content').text().trim(); | |
| if (!text) { | |
| Swal.fire({ | |
| icon: 'warning', | |
| title: 'Aucun texte', | |
| text: 'Aucun texte à copier pour le moment', | |
| confirmButtonColor: '#4361ee' | |
| }); | |
| return; | |
| } | |
| navigator.clipboard.writeText(text).then(() => { | |
| Swal.fire({ | |
| icon: 'success', | |
| title: 'Copié!', | |
| text: 'Le texte a été copié dans le presse-papiers', | |
| showConfirmButton: false, | |
| timer: 1500 | |
| }); | |
| // Animation de feedback | |
| $(this).html('<i class="fas fa-check"></i>'); | |
| setTimeout(() => { | |
| $(this).html('<i class="fas fa-copy"></i>'); | |
| }, 2000); | |
| }); | |
| }); | |
| // 🔊 Écouter le texte traduit | |
| $('#speakText').click(function() { | |
| let text = $('#result .translated-content').text().trim(); | |
| if (!text) { | |
| Swal.fire({ | |
| icon: 'warning', | |
| title: 'Aucun texte', | |
| text: 'Aucun texte à lire pour le moment', | |
| confirmButtonColor: '#4361ee' | |
| }); | |
| return; | |
| } | |
| // Arrêter toute lecture en cours | |
| window.speechSynthesis.cancel(); | |
| let langMap = { | |
| "fr": "fr-FR", | |
| "en": "en-US", | |
| "ar": "ar-SA", | |
| "es": "es-ES", | |
| }; | |
| let lang = $('#tgt_lang').val(); | |
| let utterance = new SpeechSynthesisUtterance(text); | |
| utterance.lang = langMap[lang] || "fr-FR"; | |
| utterance.rate = 0.9; | |
| utterance.pitch = 1; | |
| // Feedback visuel pendant la lecture | |
| $(this).hide(); | |
| $('#stopSpeaking').show(); | |
| utterance.onend = function() { | |
| $('#speakText').show(); | |
| $('#stopSpeaking').hide(); | |
| }; | |
| speechSynthesis.speak(utterance); | |
| }); | |
| // Arrêter la lecture | |
| $('#stopSpeaking').click(function() { | |
| window.speechSynthesis.cancel(); | |
| $('#speakText').show(); | |
| $(this).hide(); | |
| }); | |
| // 📄 Télécharger en TXT | |
| $('#downloadTxt').click(function() { | |
| let text = $('#result .translated-content').text().trim(); | |
| if (!text) { | |
| Swal.fire({ | |
| icon: 'warning', | |
| title: 'Aucun texte', | |
| text: 'Aucun texte à télécharger pour le moment', | |
| confirmButtonColor: '#4361ee' | |
| }); | |
| return; | |
| } | |
| let blob = new Blob([text], { type: "text/plain;charset=utf-8" }); | |
| let link = document.createElement("a"); | |
| link.href = URL.createObjectURL(blob); | |
| link.download = `traduction_${$('#src_lang').val()}_to_${$('#tgt_lang').val()}.txt`; | |
| document.body.appendChild(link); | |
| link.click(); | |
| document.body.removeChild(link); | |
| // Feedback | |
| Swal.fire({ | |
| icon: 'success', | |
| title: 'Téléchargement réussi', | |
| text: 'Le fichier TXT a été téléchargé', | |
| showConfirmButton: false, | |
| timer: 1500 | |
| }); | |
| }); | |
| // 📄 Télécharger en PDF | |
| $('#downloadPdf').click(function() { | |
| let text = $('#result .translated-content').text().trim(); | |
| if (!text) { | |
| Swal.fire({ | |
| icon: 'warning', | |
| title: 'Aucun texte', | |
| text: 'Aucun texte à télécharger pour le moment', | |
| confirmButtonColor: '#4361ee' | |
| }); | |
| return; | |
| } | |
| try { | |
| const { jsPDF } = window.jspdf; | |
| let doc = new jsPDF(); | |
| // Ajout d'un en-tête professionnel | |
| doc.setFontSize(18); | |
| doc.setTextColor(40, 40, 40); | |
| doc.text('Traduction de document', 105, 20, { align: 'center' }); | |
| doc.setFontSize(12); | |
| doc.text(`De: ${$('#src_lang option:selected').text()}`, 20, 30); | |
| doc.text(`À: ${$('#tgt_lang option:selected').text()}`, 160, 30); | |
| // Ajout de la date | |
| const today = new Date(); | |
| doc.text(`Date: ${today.toLocaleDateString()}`, 105, 40, { align: 'center' }); | |
| // Ajout d'une ligne de séparation | |
| doc.setDrawColor(67, 97, 238); | |
| doc.setLineWidth(0.5); | |
| doc.line(20, 45, 190, 45); | |
| // Ajout du contenu | |
| doc.setFontSize(11); | |
| doc.setTextColor(20, 20, 20); | |
| const lines = doc.splitTextToSize(text, 170); | |
| doc.text(lines, 20, 55); | |
| // Sauvegarde du PDF | |
| doc.save(`traduction_${$('#src_lang').val()}_to_${$('#tgt_lang').val()}.pdf`); | |
| // Feedback | |
| Swal.fire({ | |
| icon: 'success', | |
| title: 'Téléchargement réussi', | |
| text: 'Le fichier PDF a été téléchargé', | |
| showConfirmButton: false, | |
| timer: 1500 | |
| }); | |
| } catch (error) { | |
| console.error('PDF generation error:', error); | |
| Swal.fire({ | |
| icon: 'error', | |
| title: 'Erreur', | |
| text: 'Une erreur est survenue lors de la génération du PDF', | |
| confirmButtonColor: '#4361ee' | |
| }); | |
| } | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> |