Javedalam's picture
Update index.html
bb12dec verified
raw
history blame
5.59 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>NLLB Translation (WebGPU/WASM) + Progress</title>
<style>
body { font: 14px/1.5 system-ui, -apple-system, Segoe UI, Roboto, sans-serif; margin: 16px; }
h1 { margin-top:0; }
label { display:block; margin: 8px 0 4px; }
textarea { width: 100%; min-height: 120px; }
select, input[type="submit"] { padding: 8px; font: inherit; }
#row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
#progress-wrap { margin: 10px 0; display:none; }
#footer { opacity:.7; margin-top:12px; font-size:12px; }
.muted { opacity:.8 }
</style>
</head>
<body>
<h1>NLLB translation in browser</h1>
<div id="row">
<div>
<label for="src_lang">Source language</label>
<select id="src_lang">
<option value="eng_Latn" selected>English (eng_Latn)</option>
<option value="spa_Latn">Spanish (spa_Latn)</option>
<option value="fra_Latn">French (fra_Latn)</option>
<option value="hin_Deva">Hindi (hin_Deva)</option>
</select>
</div>
<div>
<label for="tgt_lang">Target language</label>
<select id="tgt_lang">
<option value="spa_Latn" selected>Spanish (spa_Latn)</option>
<option value="eng_Latn">English (eng_Latn)</option>
<option value="fra_Latn">French (fra_Latn)</option>
<option value="hin_Deva">Hindi (hin_Deva)</option>
</select>
</div>
</div>
<label for="from">Input text</label>
<textarea id="from" placeholder="Type text to translate"></textarea>
<!-- Progress UI -->
<div id="progress-wrap">
<progress id="loadProgress" value="0" max="1" style="width:100%;"></progress>
<div id="progressText" class="muted">Initializing…</div>
</div>
<input type="submit" id="submit" value="Loading..." disabled />
<label for="to" style="margin-top:12px">Output</label>
<textarea id="to" readonly>Output will be here...</textarea>
<div id="footer" class="muted">
Tip: first load downloads the model; later loads are much faster thanks to browser cache.
</div>
<!-- Load UMD bundle (no ESM/dynamic import).
If jsDelivr is blocked, try unpkg:
https://unpkg.com/@xenova/transformers@3.0.0/dist/transformers.min.js
-->
<script src="https://cdn.jsdelivr.net/npm/@xenova/transformers@3.0.0/dist/transformers.min.js"></script>
<script>
// Grab DOM elements
const btn = document.getElementById("submit");
const out = document.getElementById("to");
const fromEl = document.getElementById("from");
const srcEl = document.getElementById("src_lang");
const tgtEl = document.getElementById("tgt_lang");
const wrap = document.getElementById("progress-wrap");
const bar = document.getElementById("loadProgress");
const txt = document.getElementById("progressText");
// Show UI immediately
btn.disabled = false;
btn.value = "Translate";
// Transformers.js UMD exposes window.transformers
const tf = window.transformers;
if (!tf) {
document.body.insertAdjacentHTML('beforeend',
'<p style="color:#b00">Failed to load Transformers.js. Check CDN/network.</p>');
}
let translator = null;
function showProgressUI(show, message) {
wrap.style.display = show ? "" : "none";
if (message) txt.textContent = message;
bar.value = 0; bar.max = 1;
}
function progressCallback(p) {
if (p.status) txt.textContent = p.status;
if (typeof p.loaded === "number" && typeof p.total === "number" && p.total > 0) {
bar.max = p.total;
bar.value = p.loaded;
const pct = Math.round((p.loaded / p.total) * 100);
txt.textContent = `Downloading ${p.file || "model"}${pct}%`;
}
}
async function getTranslator() {
if (translator) return translator;
showProgressUI(true, "Loading model…");
try {
const { pipeline, env } = tf;
env.useBrowserCache = true;
env.allowLocalModels = false;
// WASM fallback uses threads; harmless if WebGPU available
env.backends.onnx.wasm.numThreads = Math.max(4, Math.min(8, navigator.hardwareConcurrency || 4));
translator = await pipeline("translation", "Xenova/nllb-200-distilled-600M", {
device: (navigator.gpu ? "webgpu" : "wasm"),
progress_callback: progressCallback
// If you hit issues with quant hints, you can add:
// dtype: { encoder_model: "q8", decoder_model_merged: "q8" },
});
txt.textContent = "Model ready ✔";
bar.max = 1; bar.value = 1;
return translator;
} catch (e) {
console.error(e);
txt.textContent = "Error loading model — see console.";
throw e;
}
}
btn.addEventListener("click", async () => {
const prev = btn.value;
btn.disabled = true; btn.value = "Working…";
try {
const t = await getTranslator();
const text = (fromEl.value || "").trim();
if (!text) { out.value = "Please type some text."; return; }
const res = await t(text, { src_lang: srcEl.value, tgt_lang: tgtEl.value, max_length: 128 });
out.value = res?.[0]?.translation_text || "(no output)";
} catch (e) {
out.value = "Translation failed. See console for details.";
} finally {
btn.disabled = false; btn.value = prev;
}
});
</script>
</body>
</html>