Update html/translate.ejs

This commit is contained in:
ashley 2025-04-30 17:47:21 +00:00
parent b4d7b3da11
commit f05f3e5b3a

View File

@ -16,14 +16,14 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see https://www.gnu.org/licenses/. along with this program. If not, see https://www.gnu.org/licenses/.
--> -->
<!doctype html>
<% const languageOptions = [ <% const languageOptions = [
{ code: 'autodetect', name: 'Autodetect' }, { code: 'autodetect', name: 'Autodetect' },
{ code: 'af', name: 'Afrikaans' }, { code: 'af', name: 'Afrikaans' },
{ code: 'sq', name: 'Albanian' }, { code: 'sq', name: 'Albanian' },
{ code: 'am', name: 'Amharic' }, { code: 'am', name: 'Amharic' },
{ code: 'ar', name: 'Arabic' }, { code: 'ar', name: 'Arabic' },
{ code: 'hy', name: 'Armenian' }, { code: 'hy', name: 'Armenian' },
{ code: 'as', name: 'Assamese' }, { code: 'as', name: 'Assamese' },
{ code: 'ay', name: 'Aymara' }, { code: 'ay', name: 'Aymara' },
{ code: 'az', name: 'Azerbaijani' }, { code: 'az', name: 'Azerbaijani' },
@ -117,7 +117,7 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<title>PokeTranslate</title> <title>PokeTranslate</title>
<link rel="icon" href="/static/yt-ukraine.svg"> <link rel="icon" href="/static/yt-ukraine.svg">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta content="PokeTranslate" property=og:title> <meta content="PokeTranslate" property=og:title>
@ -130,7 +130,7 @@
<link rel="manifest" href="/manifest.json"> <link rel="manifest" href="/manifest.json">
<link href="https://fonts.bunny.net/css?family=poppins:400,500,600" rel="stylesheet"> <link href="https://fonts.bunny.net/css?family=poppins:400,500,600" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<style> <style>
:root { :root {
--accent-1: #ff6b6b; --accent-1: #ff6b6b;
@ -146,7 +146,7 @@
body { body {
font-family:'Poppins',sans-serif; font-family:'Poppins',sans-serif;
background: var(--bg-light); background: var(--bg-light);
color: #fff; /* all text white */ color: #fff;
display:flex; align-items:center; justify-content:center; display:flex; align-items:center; justify-content:center;
min-height:100vh; padding:20px; min-height:100vh; padding:20px;
transition: background var(--transition), color var(--transition); transition: background var(--transition), color var(--transition);
@ -172,7 +172,6 @@
} }
} }
/* Blobs */
.blob { .blob {
position:absolute; width:300px; height:300px; position:absolute; width:300px; height:300px;
background: linear-gradient(45deg,var(--accent-1),var(--accent-2)); background: linear-gradient(45deg,var(--accent-1),var(--accent-2));
@ -202,12 +201,10 @@
@media (prefers-reduced-motion: no-preference) { @media (prefers-reduced-motion: no-preference) {
header h1 { header h1 {
font-size:2.4rem; font-weight:600; letter-spacing:1.5px; font-size:2.4rem; font-weight:600; letter-spacing:1.5px;
background: conic-gradient( background: conic-gradient(red,orange,yellow,green,blue,indigo,violet,red);
red, orange, yellow, green, blue, indigo, violet, red background-size:300%;
); -webkit-background-clip:text;
background-size: 300%; -webkit-text-fill-color:transparent;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: rainbow 4s linear infinite; animation: rainbow 4s linear infinite;
} }
@keyframes rainbow { @keyframes rainbow {
@ -216,30 +213,23 @@
100% { background-position: 0% 50%; } 100% { background-position: 0% 50%; }
} }
} }
/* Static white title when reduced-motion is set */
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
header h1 { header h1 {
font-size:2.4rem; font-weight:600; letter-spacing:1.5px; font-size:2.4rem; font-weight:600; letter-spacing:1.5px;
color: #fff; color:#fff;
} }
} }
form { position:relative; z-index:2; } form { position:relative; z-index:2; }
.language-bar { .language-bar { display:flex; align-items:center; gap:12px; padding:16px 24px; }
display:flex; align-items:center; gap:12px; padding:16px 24px;
}
.language-select { flex:1; } .language-select { flex:1; }
.language-select select { .language-select select {
width:100%; padding:14px 18px; border-radius:var(--radius); width:100%; padding:14px 18px; border-radius:var(--radius);
border:1px solid rgba(255,255,255,0.5); background:transparent; border:1px solid rgba(255,255,255,0.5); background:transparent;
font-size:1rem; appearance:none; cursor:pointer; font-size:1rem; appearance:none; cursor:pointer; color:#fff;
color:#fff;
transition:border-color var(--transition); transition:border-color var(--transition);
} }
.language-select select:focus { .language-select select:focus { border-color:var(--accent-1); outline:none; }
border-color: var(--accent-1); outline:none;
}
.swap-button { .swap-button {
font-size:2rem; color:var(--accent-1); text-decoration:none; font-size:2rem; color:var(--accent-1); text-decoration:none;
transition:transform var(--transition),color var(--transition); transition:transform var(--transition),color var(--transition);
@ -264,39 +254,28 @@
line-height:1.5; background:rgba(255,255,255,0.2); color:#fff; line-height:1.5; background:rgba(255,255,255,0.2); color:#fff;
transition:border-color var(--transition),box-shadow var(--transition); transition:border-color var(--transition),box-shadow var(--transition);
} }
.panel textarea::placeholder { .panel textarea::placeholder { color:rgba(255,255,255,0.7); }
color: rgba(255,255,255,0.7);
}
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
.panel textarea { .panel textarea { background:rgba(20,20,25,0.5); }
background:rgba(20,20,25,0.5);
}
} }
.panel textarea:focus { .panel textarea:focus {
border-color: var(--accent-1); border-color:var(--accent-1);
box-shadow: 0 0 8px var(--accent-1); box-shadow:0 0 8px var(--accent-1);
outline: none; outline:none;
} }
.actions { .actions { display:flex; justify-content:center; padding-bottom:24px; }
display:flex; justify-content:center; padding-bottom:24px;
}
.actions button { .actions button {
padding:16px 36px; font-size:1.1rem; font-weight:600; border:none; padding:16px 36px; font-size:1.1rem; font-weight:600; border:none;
border-radius:var(--radius); cursor:pointer; border-radius:var(--radius); cursor:pointer;
background:linear-gradient(60deg,var(--accent-1),var(--accent-2)); background:linear-gradient(60deg,var(--accent-1),var(--accent-2));
background-size:200% 200%; animation:gradientAnimation 6s ease infinite; background-size:200% 200%; animation:gradientAnimation 6s ease infinite;
color: #fff; color:#fff; box-shadow:0 4px 14px rgba(0,0,0,0.2);
box-shadow:0 4px 14px rgba(0,0,0,0.2);
transition:transform var(--transition),opacity var(--transition); transition:transform var(--transition),opacity var(--transition);
} }
.actions button:hover { .actions button:hover { transform:scale(1.03); opacity:0.9; }
transform:scale(1.03); opacity:0.9;
}
@media (max-width:768px) { @media (max-width:768px) { .panels { grid-template-columns:1fr; } }
.panels { grid-template-columns:1fr; }
}
</style> </style>
</head> </head>
<body> <body>
@ -353,30 +332,65 @@
</div> </div>
<script> <script>
(function(){ (function(){
const swapBtn = document.getElementById('swapBtn'); // swap button
if(swapBtn){ const swapBtn = document.getElementById('swapBtn');
swapBtn.addEventListener('click', function(e){ if(swapBtn){
e.preventDefault(); swapBtn.addEventListener('click', function(e){
const from = document.getElementById('from_language'); e.preventDefault();
const to = document.getElementById('to_language'); const from = document.getElementById('from_language');
const inp = document.getElementById('input'); const to = document.getElementById('to_language');
const out = document.getElementById('output'); const inp = document.getElementById('input');
[from.value, to.value] = [to.value, from.value]; const out = document.getElementById('output');
[inp.value, out.value] = [out.value, inp.value]; [from.value, to.value] = [to.value, from.value];
}); [inp.value, out.value] = [out.value, inp.value];
}
document.querySelectorAll('textarea').forEach(el=>{
const resize = ()=>{ el.style.height='auto'; el.style.height=el.scrollHeight+'px'; };
el.addEventListener('input', resize);
resize();
}); });
})(); }
// auto-resize all textareas
document.querySelectorAll('textarea').forEach(el=>{
const resize = ()=>{ el.style.height='auto'; el.style.height=el.scrollHeight+'px'; };
el.addEventListener('input', resize);
resize();
});
// live-translate if JS available
if(window.fetch){
const from = document.getElementById('from_language');
const to = document.getElementById('to_language');
const input = document.getElementById('input');
const output= document.getElementById('output');
let timer;
function translateNow(){
const q = new URLSearchParams({
from_language: from.value,
to_language: to.value,
input: input.value
});
fetch(`/translate?${q}`)
.then(r => r.text())
.then(html => {
const doc = new DOMParser().parseFromString(html, 'text/html');
const newOut = doc.getElementById('output');
if(newOut){
output.value = newOut.value;
output.dispatchEvent(new Event('input'));
}
})
.catch(() => {/* ignore */});
}
// debounce on typing
input.addEventListener('input', ()=>{
clearTimeout(timer);
timer = setTimeout(translateNow, 500);
});
// re-translate on language change
from.addEventListener('change', translateNow);
to.addEventListener('change', translateNow);
}
})();
</script> </script>
</body> </body>
</html> </html>