File size: 6,764 Bytes
bb12dec 01723a1 bb12dec 01723a1 bb12dec 01723a1 bb12dec 01723a1 ff7fea2 01723a1 bb12dec 01723a1 bb12dec |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
<!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 }
.error { color:#b00 }
.ok { color:#0a0 }
</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>
<div id="libStatus" class="error"></div>
<!-- Loader that tries multiple CDNs, then local file -->
<script>
(function loadTransformersUMD() {
const tries = [
"https://cdn.jsdelivr.net/npm/@xenova/transformers@3.0.0/dist/transformers.min.js",
"https://unpkg.com/@xenova/transformers@3.0.0/dist/transformers.min.js",
"https://huggingface.co/datasets/Xenova/transformers.js/resolve/main/3.0.0/transformers.min.js",
"./transformers.min.js" // <- upload this file as a final fallback
];
const statusEl = document.getElementById("libStatus");
function tryNext(i) {
if (i >= tries.length) {
statusEl.textContent = "❌ Failed to load Transformers.js from all sources.";
return;
}
const src = tries[i];
const s = document.createElement("script");
s.src = src;
s.async = true;
s.onload = () => {
if (window.transformers) {
statusEl.className = "ok";
statusEl.textContent = "✅ Transformers.js loaded from: " + src;
initApp();
} else {
statusEl.textContent = "⚠️ Loaded but window.transformers missing: " + src;
tryNext(i + 1);
}
};
s.onerror = () => {
statusEl.textContent = "⚠️ Failed: " + src + " — trying next…";
tryNext(i + 1);
};
document.head.appendChild(s);
}
tryNext(0);
// initApp runs only after the library is present
function initApp() {
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");
btn.disabled = false;
btn.value = "Translate";
const tf = window.transformers;
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;
// If WASM fallback happens, more threads help a bit
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
});
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.";
} finally {
btn.disabled = false; btn.value = prev;
}
});
}
})();
</script>
</body>
</html> |