traductor / index.html
javarribas's picture
Add 1 files
cc4cddb verified
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Traductor Funcional</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.gradient-bg {
background: linear-gradient(135deg, #6b73ff 0%, #000dff 100%);
}
.text-area {
min-height: 150px;
resize: none;
}
.swap-btn:hover {
transform: rotate(180deg);
transition: transform 0.3s ease;
}
.pulse {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
</style>
</head>
<body class="gradient-bg min-h-screen flex items-center justify-center p-4">
<div class="w-full max-w-4xl bg-white rounded-xl shadow-2xl overflow-hidden">
<!-- Header -->
<div class="bg-indigo-700 text-white p-6">
<h1 class="text-3xl font-bold flex items-center">
<i class="fas fa-language mr-3"></i>
Traductor Funcional
</h1>
<p class="text-indigo-200 mt-1">Traduce texto usando la API de Google Translate</p>
</div>
<!-- Main Content -->
<div class="p-6">
<!-- Language Selection -->
<div class="flex items-center justify-between mb-6">
<!-- Source Language -->
<div class="flex-1 mr-4">
<div class="relative">
<select id="sourceLanguage" class="w-full p-3 border border-gray-300 rounded-lg appearance-none bg-white cursor-pointer">
<option value="auto">Detectar idioma</option>
<option value="es">Español</option>
<option value="en">Inglés</option>
<option value="fr">Francés</option>
<option value="de">Alemán</option>
<option value="it">Italiano</option>
<option value="pt">Portugués</option>
<option value="ru">Ruso</option>
<option value="ja">Japonés</option>
<option value="zh">Chino</option>
</select>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
<i class="fas fa-chevron-down"></i>
</div>
</div>
</div>
<!-- Swap Button -->
<button id="swapLanguages" class="swap-btn bg-indigo-600 text-white p-3 rounded-full hover:bg-indigo-700 transition-colors duration-300">
<i class="fas fa-exchange-alt"></i>
</button>
<!-- Target Language -->
<div class="flex-1 ml-4">
<div class="relative">
<select id="targetLanguage" class="w-full p-3 border border-gray-300 rounded-lg appearance-none bg-white cursor-pointer">
<option value="es">Español</option>
<option value="en" selected>Inglés</option>
<option value="fr">Francés</option>
<option value="de">Alemán</option>
<option value="it">Italiano</option>
<option value="pt">Portugués</option>
<option value="ru">Ruso</option>
<option value="ja">Japonés</option>
<option value="zh">Chino</option>
</select>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
<i class="fas fa-chevron-down"></i>
</div>
</div>
</div>
</div>
<!-- Text Areas -->
<div class="flex flex-col md:flex-row gap-6">
<!-- Source Text -->
<div class="flex-1">
<div class="relative">
<textarea id="sourceText" class="text-area w-full p-4 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent" placeholder="Escribe o pega el texto aquí..."></textarea>
<div class="absolute bottom-3 right-3 flex space-x-2">
<button id="clearSource" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
<button id="listenSource" class="text-indigo-600 hover:text-indigo-800">
<i class="fas fa-volume-up"></i>
</button>
</div>
</div>
<div class="mt-2 text-sm text-gray-500 flex justify-between">
<span id="charCount">0 caracteres</span>
</div>
</div>
<!-- Target Text -->
<div class="flex-1">
<div class="relative">
<textarea id="targetText" readonly class="text-area w-full p-4 border border-gray-300 rounded-lg bg-gray-50" placeholder="Traducción aparecerá aquí..."></textarea>
<div class="absolute bottom-3 right-3 flex space-x-2">
<button id="copyTranslation" class="text-indigo-600 hover:text-indigo-800" title="Copiar traducción">
<i class="far fa-copy"></i>
</button>
<button id="listenTranslation" class="text-indigo-600 hover:text-indigo-800">
<i class="fas fa-volume-up"></i>
</button>
</div>
</div>
<div class="mt-2 text-sm text-gray-500">
<span id="translationStatus">Listo para traducir</span>
</div>
</div>
</div>
<!-- Translate Button -->
<div class="mt-6 text-center">
<button id="translateBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-3 px-8 rounded-full shadow-lg transition-all duration-300 transform hover:scale-105">
<i class="fas fa-exchange-alt mr-2"></i> Traducir
</button>
</div>
</div>
<!-- Footer -->
<div class="bg-gray-100 p-4 text-center text-gray-600 text-sm">
<p>Traductor Funcional © 2023 | Usa la API de Google Translate</p>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Elements
const sourceLanguage = document.getElementById('sourceLanguage');
const targetLanguage = document.getElementById('targetLanguage');
const sourceText = document.getElementById('sourceText');
const targetText = document.getElementById('targetText');
const translateBtn = document.getElementById('translateBtn');
const swapLanguages = document.getElementById('swapLanguages');
const clearSource = document.getElementById('clearSource');
const copyTranslation = document.getElementById('copyTranslation');
const listenSource = document.getElementById('listenSource');
const listenTranslation = document.getElementById('listenTranslation');
const charCount = document.getElementById('charCount');
const translationStatus = document.getElementById('translationStatus');
// Character counter
sourceText.addEventListener('input', function() {
const count = sourceText.value.length;
charCount.textContent = `${count} caracter${count !== 1 ? 'es' : ''}`;
if (count > 5000) {
charCount.classList.add('text-red-500');
charCount.classList.remove('text-gray-500');
} else {
charCount.classList.add('text-gray-500');
charCount.classList.remove('text-red-500');
}
});
// Clear source text
clearSource.addEventListener('click', function() {
sourceText.value = '';
charCount.textContent = '0 caracteres';
charCount.classList.add('text-gray-500');
charCount.classList.remove('text-red-500');
});
// Copy translation
copyTranslation.addEventListener('click', function() {
if (targetText.value) {
navigator.clipboard.writeText(targetText.value)
.then(() => {
const originalIcon = copyTranslation.innerHTML;
copyTranslation.innerHTML = '<i class="fas fa-check"></i>';
setTimeout(() => {
copyTranslation.innerHTML = originalIcon;
}, 2000);
});
}
});
// Swap languages
swapLanguages.addEventListener('click', function() {
const tempLang = sourceLanguage.value;
sourceLanguage.value = targetLanguage.value === 'auto' ? 'es' : targetLanguage.value;
targetLanguage.value = tempLang === 'auto' ? 'en' : tempLang;
if (sourceText.value && targetText.value) {
const tempText = sourceText.value;
sourceText.value = targetText.value;
targetText.value = tempText;
}
});
// Text-to-speech
listenSource.addEventListener('click', function() {
if (sourceText.value.trim()) {
speak(sourceText.value, sourceLanguage.value === 'auto' ? 'es' : sourceLanguage.value);
}
});
listenTranslation.addEventListener('click', function() {
if (targetText.value.trim()) {
speak(targetText.value, targetLanguage.value);
}
});
// Translate button
translateBtn.addEventListener('click', function() {
if (!sourceText.value.trim()) {
showError("Por favor ingresa texto para traducir");
return;
}
if (sourceText.value.length > 5000) {
showError("El texto es demasiado largo (máx. 5000 caracteres)");
return;
}
translationStatus.textContent = "Traduciendo...";
translateBtn.disabled = true;
translateBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Traduciendo';
// Usamos la API de Google Translate (método no oficial)
translateText(
sourceText.value,
sourceLanguage.value,
targetLanguage.value
).then(translatedText => {
targetText.value = translatedText;
translationStatus.textContent = "Traducción completada";
translateBtn.disabled = false;
translateBtn.innerHTML = '<i class="fas fa-exchange-alt mr-2"></i> Traducir';
}).catch(error => {
showError("Error al traducir: " + error.message);
translateBtn.disabled = false;
translateBtn.innerHTML = '<i class="fas fa-exchange-alt mr-2"></i> Traducir';
});
});
// Helper functions
function showError(message) {
translationStatus.textContent = message;
translationStatus.classList.add('text-red-500');
setTimeout(() => {
translationStatus.textContent = "Listo para traducir";
translationStatus.classList.remove('text-red-500');
}, 3000);
}
function speak(text, lang) {
if ('speechSynthesis' in window) {
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = lang;
window.speechSynthesis.speak(utterance);
} else {
alert("Tu navegador no soporta la función de texto a voz.");
}
}
// Función principal de traducción usando Google Translate API
async function translateText(text, sourceLang, targetLang) {
// Si es "auto", no especificamos el idioma origen
const sourceParam = sourceLang === 'auto' ? '' : `&sl=${sourceLang}`;
const url = `https://translate.googleapis.com/translate_a/single?client=gtx&dt=t${sourceParam}&tl=${targetLang}&q=${encodeURIComponent(text)}`;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Error en la API de traducción');
}
const data = await response.json();
// La respuesta viene en un formato complejo, extraemos la traducción
if (data && Array.isArray(data[0])) {
return data[0].map(item => item[0]).join('');
}
throw new Error('Formato de respuesta no reconocido');
} catch (error) {
console.error('Error al traducir:', error);
throw error;
}
}
});
</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=javarribas/traductor" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>