Ai-traduction / static /index.html
rayhane123's picture
Update static/index.html
89617c9 verified
<!DOCTYPE html>
<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>