unitrad / index.html
Bruno2023's picture
Add 2 files
3d3e3ca verified
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Traducteur Universel</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
dark: {
100: '#E5E7EB',
200: '#D1D5DB',
300: '#9CA3AF',
400: '#6B7280',
500: '#4B5563',
600: '#374151',
700: '#1F2937',
800: '#111827',
900: '#0F172A',
}
}
}
}
}
</script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.language-flag {
width: 24px;
height: 16px;
margin-right: 8px;
border-radius: 2px;
}
.text-input {
min-height: 150px;
resize: none;
}
.swap-btn {
transition: all 0.3s ease;
}
.swap-btn:hover {
transform: rotate(180deg);
}
.pulse {
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
body {
transition: background-color 0.3s ease;
}
</style>
</head>
<body class="bg-gradient-to-br from-dark-800 to-dark-900 min-h-screen text-dark-100">
<div class="container mx-auto px-4 py-12 max-w-4xl">
<!-- Theme Toggle -->
<div class="flex justify-end mb-4">
<button id="themeToggle" class="p-2 rounded-full bg-dark-700 hover:bg-dark-600 transition">
<i class="fas fa-moon" id="themeIcon"></i>
</button>
</div>
<div class="text-center mb-10">
<h1 class="text-4xl font-bold text-indigo-400 mb-2">Traducteur Universel</h1>
<p class="text-lg text-indigo-300">Traduisez instantanément entre toutes les langues</p>
</div>
<div class="bg-dark-700 rounded-xl shadow-xl overflow-hidden border border-dark-600">
<div class="flex flex-col md:flex-row">
<!-- Source Language -->
<div class="w-full md:w-1/2 p-6 border-b md:border-b-0 md:border-r border-dark-600">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center">
<div class="relative w-full">
<select id="sourceLanguage" class="w-full p-3 border border-dark-500 rounded-lg appearance-none bg-dark-800 text-dark-100 pr-10 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
<option value="auto">Détection automatique</option>
<option value="fr">Français</option>
<option value="en">Anglais</option>
<option value="es">Espagnol</option>
<option value="de">Allemand</option>
<option value="it">Italien</option>
<option value="pt">Portugais</option>
<option value="ru">Russe</option>
<option value="zh">Chinois</option>
<option value="ja">Japonais</option>
<option value="ar">Arabe</option>
</select>
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
<i class="fas fa-chevron-down text-dark-400"></i>
</div>
</div>
</div>
</div>
<textarea id="sourceText" class="w-full text-input p-4 border border-dark-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 bg-dark-800 text-dark-100 placeholder-dark-400" placeholder="Entrez votre texte ici..."></textarea>
<div class="flex justify-between items-center mt-3">
<div class="text-sm text-dark-400" id="sourceCharCount">0/5000</div>
<button id="clearSource" class="text-dark-400 hover:text-indigo-400">
<i class="fas fa-times"></i> Effacer
</button>
</div>
</div>
<!-- Target Language -->
<div class="w-full md:w-1/2 p-6">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center">
<div class="relative w-full">
<select id="targetLanguage" class="w-full p-3 border border-dark-500 rounded-lg appearance-none bg-dark-800 text-dark-100 pr-10 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
<option value="fr">Français</option>
<option value="en" selected>Anglais</option>
<option value="es">Espagnol</option>
<option value="de">Allemand</option>
<option value="it">Italien</option>
<option value="pt">Portugais</option>
<option value="ru">Russe</option>
<option value="zh">Chinois</option>
<option value="ja">Japonais</option>
<option value="ar">Arabe</option>
</select>
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
<i class="fas fa-chevron-down text-dark-400"></i>
</div>
</div>
</div>
</div>
<div id="targetText" class="w-full text-input p-4 border border-dark-600 rounded-lg bg-dark-800 min-h-[150px] text-dark-100">
<div class="text-dark-400 italic">La traduction apparaîtra ici...</div>
</div>
<div class="flex justify-between items-center mt-3">
<div class="text-sm text-dark-400" id="targetCharCount">0/5000</div>
<div class="flex space-x-2">
<button id="copyTranslation" class="text-dark-400 hover:text-indigo-400" title="Copier">
<i class="far fa-copy"></i>
</button>
<button id="speakTranslation" class="text-dark-400 hover:text-indigo-400" title="Écouter">
<i class="fas fa-volume-up"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Controls -->
<div class="bg-dark-800 px-6 py-4 border-t border-dark-600 flex justify-between items-center">
<div class="flex items-center space-x-2">
<button id="swapLanguages" class="swap-btn bg-indigo-600 text-white p-3 rounded-full hover:bg-indigo-500">
<i class="fas fa-exchange-alt"></i>
</button>
<span class="text-sm text-dark-300">Échanger les langues</span>
</div>
<button id="translateBtn" class="bg-indigo-600 hover:bg-indigo-500 text-white font-medium py-3 px-6 rounded-lg flex items-center">
<i class="fas fa-language mr-2"></i> Traduire
</button>
</div>
</div>
<!-- Translation History -->
<div class="mt-10 bg-dark-700 rounded-xl shadow-lg overflow-hidden border border-dark-600">
<div class="p-6">
<h2 class="text-xl font-semibold text-indigo-400 mb-4 flex items-center">
<i class="fas fa-history mr-2"></i> Historique des traductions
</h2>
<div id="historyList" class="space-y-3 max-h-60 overflow-y-auto">
<!-- Les éléments d'historique seront ajoutés ici dynamiquement -->
<div class="text-center text-dark-400 py-4 italic">Aucun historique pour le moment</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Éléments du DOM
const sourceText = document.getElementById('sourceText');
const targetText = document.getElementById('targetText');
const sourceLanguage = document.getElementById('sourceLanguage');
const targetLanguage = document.getElementById('targetLanguage');
const translateBtn = document.getElementById('translateBtn');
const swapLanguages = document.getElementById('swapLanguages');
const clearSource = document.getElementById('clearSource');
const copyTranslation = document.getElementById('copyTranslation');
const speakTranslation = document.getElementById('speakTranslation');
const sourceCharCount = document.getElementById('sourceCharCount');
const targetCharCount = document.getElementById('targetCharCount');
const historyList = document.getElementById('historyList');
const themeToggle = document.getElementById('themeToggle');
const themeIcon = document.getElementById('themeIcon');
let isDarkMode = true;
// Gestion du thème sombre/clair
themeToggle.addEventListener('click', function() {
isDarkMode = !isDarkMode;
if (isDarkMode) {
document.documentElement.classList.add('dark');
themeIcon.classList.remove('fa-sun');
themeIcon.classList.add('fa-moon');
} else {
document.documentElement.classList.remove('dark');
themeIcon.classList.remove('fa-moon');
themeIcon.classList.add('fa-sun');
}
});
// Compteur de caractères
sourceText.addEventListener('input', function() {
const count = sourceText.value.length;
sourceCharCount.textContent = `${count}/5000`;
if (count > 4500) {
sourceCharCount.classList.add('text-red-400');
} else {
sourceCharCount.classList.remove('text-red-400');
}
});
// Effacer le texte source
clearSource.addEventListener('click', function() {
sourceText.value = '';
sourceCharCount.textContent = '0/5000';
targetText.innerHTML = '<div class="text-dark-400 italic">La traduction apparaîtra ici...</div>';
targetCharCount.textContent = '0/5000';
});
// Échanger les langues
swapLanguages.addEventListener('click', function() {
const tempLang = sourceLanguage.value;
sourceLanguage.value = targetLanguage.value;
targetLanguage.value = tempLang;
// Si le texte source n'est pas vide, on traduit automatiquement
if (sourceText.value.trim()) {
translateText();
}
});
// Copier la traduction
copyTranslation.addEventListener('click', function() {
if (targetText.textContent.trim() && !targetText.querySelector('.italic')) {
navigator.clipboard.writeText(targetText.textContent)
.then(() => {
const originalIcon = copyTranslation.innerHTML;
copyTranslation.innerHTML = '<i class="fas fa-check"></i>';
setTimeout(() => {
copyTranslation.innerHTML = originalIcon;
}, 2000);
});
}
});
// Lire la traduction à voix haute
speakTranslation.addEventListener('click', function() {
if (targetText.textContent.trim() && !targetText.querySelector('.italic')) {
const utterance = new SpeechSynthesisUtterance(targetText.textContent);
utterance.lang = targetLanguage.value;
speechSynthesis.speak(utterance);
}
});
// Traduction automatique lors de la saisie (avec délai)
let translateTimeout;
sourceText.addEventListener('input', function() {
clearTimeout(translateTimeout);
if (sourceText.value.trim()) {
translateTimeout = setTimeout(translateText, 1000);
}
});
// Traduction lors du clic sur le bouton
translateBtn.addEventListener('click', translateText);
// Fonction de traduction
function translateText() {
const text = sourceText.value.trim();
if (!text) return;
// Afficher l'indicateur de chargement
targetText.innerHTML = '<div class="flex justify-center items-center h-full"><div class="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-indigo-500"></div></div>';
translateBtn.disabled = true;
translateBtn.classList.add('opacity-75', 'cursor-not-allowed');
// Simuler une traduction (dans un vrai projet, vous utiliseriez une API de traduction)
setTimeout(() => {
// Ici, nous simulons simplement une traduction en inversant le texte
// Dans un vrai projet, vous utiliseriez une API comme Google Translate
const translatedText = `[Traduction simulée de ${sourceLanguage.value} vers ${targetLanguage.value}]\n${text.split('').reverse().join('')}`;
targetText.innerHTML = translatedText;
targetCharCount.textContent = `${translatedText.length}/5000`;
// Ajouter à l'historique
addToHistory(text, translatedText, sourceLanguage.value, targetLanguage.value);
translateBtn.disabled = false;
translateBtn.classList.remove('opacity-75', 'cursor-not-allowed');
}, 1500);
}
// Ajouter une traduction à l'historique
function addToHistory(source, translation, fromLang, toLang) {
// Vérifier si l'historique est vide (avec le message par défaut)
if (historyList.querySelector('.italic')) {
historyList.innerHTML = '';
}
const historyItem = document.createElement('div');
historyItem.className = 'bg-dark-800 p-3 rounded-lg border border-dark-600 hover:bg-dark-600 transition cursor-pointer';
historyItem.innerHTML = `
<div class="flex justify-between items-start">
<div class="flex-1">
<div class="text-sm font-medium text-dark-100 mb-1">${source.substring(0, 50)}${source.length > 50 ? '...' : ''}</div>
<div class="text-sm text-indigo-400">${translation.substring(0, 50)}${translation.length > 50 ? '...' : ''}</div>
</div>
<div class="text-xs text-dark-400 ml-2">${fromLang}${toLang}</div>
</div>
`;
// Ajouter au début de la liste
historyList.insertBefore(historyItem, historyList.firstChild);
// Limiter l'historique à 10 éléments
if (historyList.children.length > 10) {
historyList.removeChild(historyList.lastChild);
}
// Gestion du clic sur un élément d'historique
historyItem.addEventListener('click', function() {
sourceText.value = source;
sourceCharCount.textContent = `${source.length}/5000`;
targetText.innerHTML = translation;
targetCharCount.textContent = `${translation.length}/5000`;
sourceLanguage.value = fromLang;
targetLanguage.value = toLang;
});
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Bruno2023/unitrad" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>