|
<!DOCTYPE html> |
|
<html lang="fr"> |
|
<head> |
|
<title>BOMA LANGUE</title> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
<script> |
|
tailwind.config = { |
|
theme: { |
|
extend: { |
|
colors: { |
|
primary: '#ff69b4', |
|
secondary: '#2d3748', |
|
hover: '#ff1493', |
|
'light-gray': '#e2e8f0', |
|
accent: '#9f7aea', |
|
}, |
|
keyframes: { |
|
'fade-in-down': { |
|
'0%': { |
|
opacity: '0', |
|
transform: 'translateY(-20px)' |
|
}, |
|
'100%': { |
|
opacity: '1', |
|
transform: 'translateY(0)' |
|
}, |
|
}, |
|
'float': { |
|
'0%, 100%': { |
|
transform: 'translateY(0)' |
|
}, |
|
'50%': { |
|
transform: 'translateY(-20px)' |
|
} |
|
}, |
|
'pulse-scale': { |
|
'0%, 100%': { |
|
transform: 'scale(1)', |
|
}, |
|
'50%': { |
|
transform: 'scale(1.05)', |
|
} |
|
}, |
|
'gradient-shift': { |
|
'0%': { |
|
'background-position': '0% 50%' |
|
}, |
|
'50%': { |
|
'background-position': '100% 50%' |
|
}, |
|
'100%': { |
|
'background-position': '0% 50%' |
|
} |
|
} |
|
}, |
|
animation: { |
|
'fade-in-down': 'fade-in-down 0.8s ease-out', |
|
'float': 'float 3s ease-in-out infinite', |
|
'pulse-scale': 'pulse-scale 2s ease-in-out infinite', |
|
'gradient-shift': 'gradient-shift 5s ease infinite' |
|
} |
|
} |
|
} |
|
} |
|
</script> |
|
<style> |
|
.gradient-bg { |
|
background: linear-gradient(-45deg, #ff69b4, #9f7aea, #4fd1c5, #ff69b4); |
|
background-size: 400% 400%; |
|
animation: gradient-shift 15s ease infinite; |
|
} |
|
|
|
.loading-overlay { |
|
position: fixed; |
|
top: 0; |
|
left: 0; |
|
width: 100%; |
|
height: 100%; |
|
background-color: rgba(255, 255, 255, 0.9); |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
z-index: 50; |
|
backdrop-filter: blur(5px); |
|
} |
|
|
|
.glassmorphism { |
|
background: rgba(255, 255, 255, 0.25); |
|
backdrop-filter: blur(10px); |
|
border: 1px solid rgba(255, 255, 255, 0.3); |
|
border-radius: 1rem; |
|
} |
|
|
|
.form-container { |
|
transform-style: preserve-3d; |
|
perspective: 1000px; |
|
} |
|
|
|
.floating-label { |
|
position: absolute; |
|
pointer-events: none; |
|
left: 12px; |
|
top: 8px; |
|
transition: 0.2s ease all; |
|
opacity: 0.6; |
|
} |
|
|
|
textarea:focus + .floating-label, |
|
textarea:not(:placeholder-shown) + .floating-label { |
|
transform: translateY(-20px); |
|
font-size: 0.75rem; |
|
color: #ff69b4; |
|
opacity: 1; |
|
} |
|
</style> |
|
</head> |
|
<body class="bg-gradient-to-br from-white to-pink-50 min-h-screen"> |
|
|
|
<header class="gradient-bg py-6 shadow-lg animate-fade-in-down"> |
|
<h1 class="text-center text-4xl font-bold text-white glassmorphism mx-auto w-fit px-8 py-3 animate-pulse-scale"> |
|
BOMA LANGUE |
|
</h1> |
|
</header> |
|
|
|
<main class="container mx-auto p-8 animate-fade-in-down form-container"> |
|
<form id="translation-form" class="glassmorphism p-8 max-w-4xl mx-auto shadow-2xl"> |
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8"> |
|
<div class="transform hover:scale-105 transition-transform duration-300"> |
|
<label for="source_language" class="block text-lg font-medium text-secondary mb-3">Langue source:</label> |
|
<select id="source_language" name="source_language" class="w-full p-4 border-2 border-primary/30 rounded-lg focus:ring-4 focus:ring-primary/30 focus:border-primary transition-all duration-300"> |
|
{% for lang in source_languages %} |
|
<option value="{{ lang }}">{{ lang }}</option> |
|
{% endfor %} |
|
</select> |
|
</div> |
|
<div class="transform hover:scale-105 transition-transform duration-300"> |
|
<label for="target_language" class="block text-lg font-medium text-secondary mb-3">Langue cible:</label> |
|
<select id="target_language" name="target_language" class="w-full p-4 border-2 border-primary/30 rounded-lg focus:ring-4 focus:ring-primary/30 focus:border-primary transition-all duration-300"> |
|
{% for lang in target_languages %} |
|
<option value="{{ lang }}">{{ lang }}</option> |
|
{% endfor %} |
|
</select> |
|
</div> |
|
</div> |
|
|
|
<div class="mb-8 relative transform hover:scale-102 transition-transform duration-300"> |
|
<textarea id="input_text" name="input_text" class="w-full p-4 border-2 border-primary/30 rounded-lg focus:ring-4 focus:ring-primary/30 focus:border-primary transition-all duration-300 resize-none" rows="5" placeholder=""></textarea> |
|
<span class="floating-label">Entrez le texte à traduire</span> |
|
</div> |
|
|
|
<div class="flex justify-center mb-8"> |
|
<button type="submit" class="bg-primary hover:bg-hover text-white font-bold py-4 px-8 rounded-full transition-all duration-300 transform hover:scale-110 hover:shadow-lg animate-float"> |
|
Traduire ⇄ |
|
</button> |
|
</div> |
|
|
|
<div class="relative transform hover:scale-102 transition-transform duration-300"> |
|
<textarea id="translated_text" name="translated_text" class="w-full p-4 border-2 border-primary/30 rounded-lg focus:ring-4 focus:ring-primary/30 focus:border-primary transition-all duration-300 resize-none" rows="5" placeholder="" readonly></textarea> |
|
<span class="floating-label">Traduction</span> |
|
</div> |
|
</form> |
|
</main> |
|
|
|
<div id="loading-overlay" class="loading-overlay hidden"> |
|
<div class="text-center"> |
|
<div class="flex space-x-3 mb-4"> |
|
<div class="w-5 h-5 bg-primary rounded-full animate-bounce" style="animation-delay: 0s"></div> |
|
<div class="w-5 h-5 bg-accent rounded-full animate-bounce" style="animation-delay: 0.1s"></div> |
|
<div class="w-5 h-5 bg-primary rounded-full animate-bounce" style="animation-delay: 0.2s"></div> |
|
</div> |
|
<p class="text-secondary font-medium mt-4">Traduction en cours...</p> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
document.getElementById('translation-form').addEventListener('submit', function (event) { |
|
event.preventDefault(); |
|
const form = event.target; |
|
const inputText = document.getElementById('input_text').value.trim(); |
|
const translatedTextArea = document.getElementById('translated_text'); |
|
const loadingOverlay = document.getElementById('loading-overlay'); |
|
|
|
if (inputText === '') { |
|
translatedTextArea.value = 'Veuillez entrer un texte à traduire'; |
|
return; |
|
} |
|
|
|
loadingOverlay.classList.remove('hidden'); |
|
|
|
fetch('/', { |
|
method: 'POST', |
|
body: new FormData(form) |
|
}) |
|
.then(response => response.json()) |
|
.then(data => { |
|
loadingOverlay.classList.add('hidden'); |
|
translatedTextArea.value = data.translated_text; |
|
}) |
|
.catch(error => { |
|
loadingOverlay.classList.add('hidden'); |
|
console.error('Error:', error); |
|
translatedTextArea.value = 'Une erreur est survenue lors de la traduction.'; |
|
}); |
|
}); |
|
</script> |
|
|
|
</body> |
|
</html> |