transliteration-bangla
v0.1.4
Published
Banglish-to-Bangla transliteration in the browser with ONNX Runtime Web
Maintainers
Readme
NOTE: IT IS AN INTERNAL TOOL PUBLISHED PUBLICLY.
transliteration-bangla
Run Banglish-to-Bangla transliteration entirely in the browser with ONNX Runtime Web. Inference runs inside a dedicated Web Worker and supports WebGPU with an automatic WASM/CPU fallback.
ami tomake bhalobashi → আমি তোমাকে ভালোবাসি
vai kemon acho → ভাই কেমন আছোInstall
pnpm add transliteration-banglaInside this monorepo, add the workspace package instead:
pnpm --filter <your-app> add transliteration-bangla@workspace:*The package is browser-only and uses ESM, Web Workers, and ONNX Runtime Web.
Model assets
The npm package does not contain model weights. Host these files together in a public, CORS-enabled directory:
encoder.onnx
decoder.onnx
src_vocab.json
tgt_vocab.json
config.jsonPass that directory as modelBaseUrl. After the model successfully initializes,
newly downloaded assets are saved in IndexedDB. Later runtimes using the same
resolved asset URLs read them from IndexedDB instead of downloading them again.
Use an immutable or versioned directory URL, such as /bangla-model/v1/. To
deploy a new model, change the URL so it receives a separate cache entry.
The worker supports both the original full-prefix decoder and the faster
self-kv-cache-v1 decoder produced by this repository's export tool. Put each
precision variant in its own immutable directory and select it with
modelBaseUrl:
| Variant | ONNX graphs | Recommended backend | Notes | | ------- | ----------- | ------------------- | ---------------------------------------------------------------- | | FP32 | 26.7 MB | WASM or WebGPU | Highest precision and the default | | INT8 | 8.7 MB | WASM | Smallest download; benchmark quality and latency for your inputs | | FP16 | 13.7 MB | WebGPU | Smaller GPU-oriented variant; not usually faster on WASM |
These variants are post-training conversions. They do not retrain or otherwise change the learned model weights, although reduced precision must still be validated against representative inputs.
Basic usage
import { createTransliterator } from "transliteration-bangla";
const transliterator = await createTransliterator({
modelBaseUrl: "https://your-domain.com/transliteration-model/v2/",
});
const bangla = await transliterator.transliterate("ami tomake bhalobashi");
console.log(bangla);
// আমি তোমাকে ভালোবাসি
console.log(transliterator.device);
// "webgpu" or "wasm"
// Terminate the worker when it is no longer needed.
transliterator.dispose();The default auto mode tries WebGPU first and automatically uses WASM/CPU when
WebGPU is unavailable or cannot initialize the model.
React example with debouncing
Create the runtime once, reuse it for every request, and dispose it when the component unmounts:
import { useEffect, useRef, useState } from "react";
import {
createTransliterator,
SupersededRequestError,
type Transliterator,
} from "transliteration-bangla";
export function TransliterationInput() {
const runtime = useRef<Transliterator | undefined>(undefined);
const [input, setInput] = useState("");
const [output, setOutput] = useState("");
const [ready, setReady] = useState(false);
useEffect(() => {
let cancelled = false;
void createTransliterator({
modelBaseUrl: "https://your-domain.com/transliteration-model/v2/",
device: "auto",
}).then((instance) => {
if (cancelled) {
instance.dispose();
return;
}
runtime.current = instance;
setReady(true);
});
return () => {
cancelled = true;
runtime.current?.dispose();
runtime.current = undefined;
};
}, []);
useEffect(() => {
if (!ready || !runtime.current) return;
const timeout = window.setTimeout(async () => {
try {
const result = await runtime.current?.transliterate(input);
setOutput(result ?? "");
} catch (error) {
if (!(error instanceof SupersededRequestError)) throw error;
}
}, 350);
return () => window.clearTimeout(timeout);
}, [input, ready]);
return (
<div>
<textarea
value={input}
onChange={(event) => setInput(event.target.value)}
placeholder="Type Banglish"
/>
<p>{ready ? output : "Loading model…"}</p>
</div>
);
}Production code may also want to catch initialization and inference errors and ignore stale results when the input changes during an active request.
Choose WebGPU or WASM
const webgpuRuntime = await createTransliterator({
modelBaseUrl: "https://your-domain.com/transliteration-model/v2/",
device: "webgpu",
});
const cpuRuntime = await createTransliterator({
modelBaseUrl: "https://your-domain.com/transliteration-model/v2/",
device: "wasm",
});Supported device values:
| Value | Behavior |
| ---------- | ---------------------------------------------------------------- |
| "auto" | Prefer WebGPU and fall back to WASM/CPU |
| "webgpu" | Request WebGPU and fall back to WASM/CPU if initialization fails |
| "wasm" | Always use WASM/CPU |
Always inspect transliterator.device after initialization to determine which
backend is actually active.
WebGPU generally requires a supported browser and a secure context such as
HTTPS or localhost. The WASM backend works on a wider range of browsers.
Multithreaded WASM
Browsers only allow ONNX Runtime's WASM threads when the application is cross-origin isolated. Configure these response headers on the application:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corpCross-origin model responses must continue to satisfy CORS under this policy.
The package requests up to four threads by default, capped by available
hardware. Without isolation it safely uses one thread. Inspect
transliterator.wasmThreads and transliterator.crossOriginIsolated to see the
actual runtime configuration, or pass wasmThreads to request a different cap.
Loading progress
Use onProgress to update a loading indicator while the model is downloaded
and initialized:
const transliterator = await createTransliterator({
modelBaseUrl: "https://your-domain.com/transliteration-model/v2/",
requestTimeoutMs: 120_000,
onProgress({ progress, message }) {
console.log(`${Math.round(progress * 100)}%`, message);
},
onFatalError(error) {
console.error(error);
},
});Detailed inference result
transliterateDetailed() includes the worker-side inference duration:
const result = await transliterator.transliterateDetailed("Bangladesh amader desh");
console.log(result.text);
// বাংলাদেশ আমাদের দেশ
console.log(result.durationMs);
// Inference duration in millisecondsAPI
createTransliterator(options)
Creates the Web Worker, downloads the model files, initializes ONNX Runtime, and resolves when the model is ready.
| Option | Type | Required | Description |
| ------------------- | ------------------------------ | -------- | ---------------------------------------------------------------------------- |
| modelBaseUrl | string | Yes | CORS-enabled directory containing the five model files |
| device | "auto" \| "webgpu" \| "wasm" | No | Backend preference; defaults to "auto" |
| wasmBaseUrl | string | No | Custom base URL for ONNX Runtime WASM files |
| wasmThreads | number | No | Requested WASM thread cap; defaults to 4 and requires cross-origin isolation |
| maxOutputLength | number | No | Positive output-token limit, capped by the model context |
| requestScheduling | "latest" \| "fifo" | No | Concurrent request policy; defaults to "latest" |
| requestTimeoutMs | number | No | Per-request timeout; defaults to 120 seconds; use 0 to disable |
| onProgress | (event) => void | No | Receives initialization progress and messages |
| onFatalError | (error) => void | No | Reports terminal worker or WebGPU device failures |
With the default "latest" scheduling, a newer call rejects older outstanding
calls with SupersededRequestError, and the worker discards obsolete queued
work. Use "fifo" when every concurrent request must complete.
Inputs longer than the model context are handled automatically inside the
worker. Each chunk fills as much of the context as possible, splits at
whitespace, and preserves the boundary whitespace in the combined result. Text
is split inside a word only when that individual word exceeds the model
context. maxOutputLength applies independently to each generated chunk.
Only ASCII English letters (A-Z and a-z) are sent to the model. ASCII
digits (0-9) bypass inference and are mapped directly to Bangla digits
(০-৯). Ordinary English phrases remain grouped across single spaces for
contextual accuracy. Bangla text, other scripts, punctuation, symbols, emoji,
and unusual whitespace bypass the model and are copied to the result exactly.
Periods between numeric digits remain decimal points, while sentence-ending
periods become Bangla dari (।). Leading decimals such as .5 are supported,
and repeated dots are preserved. URLs, email addresses, domains, IP addresses,
and explicit version strings bypass all conversion so identifiers remain valid.
The returned Transliterator provides:
| Member | Description |
| ----------------------------- | ------------------------------------------------------ |
| device | The active backend: "webgpu" or "wasm" |
| wasmThreads | Actual configured WASM thread count |
| crossOriginIsolated | Whether the worker is cross-origin isolated |
| decoder | "kv-cache" or legacy "full-prefix" |
| transliterate(text) | Resolves to the Bangla output string |
| transliterateDetailed(text) | Resolves to { text, durationMs } |
| dispose() | Terminates the worker and rejects outstanding requests |
Generate optimized assets
From the source repository, generate and validate every variant with:
python tools/export_model.py \
--source /path/to/model/checkpoint-directory \
--output /path/to/deployment-directory \
--variants allThe source directory must contain best_model.pt, both vocabulary files,
config.json, and the adjacent infer.py model definition expected by the
exporter. Output validation runs real CPU inference for FP32, INT8, and FP16 and
fails if any known transliteration changes.
Notes
- The first initialization downloads and compiles the model, so it is slower than subsequent inference calls.
- Initialize during application startup or idle time and reuse one runtime. Recreating it avoids downloads after the first load but still rebuilds the ONNX sessions.
- Model assets are cached in IndexedDB by their complete resolved URLs after a successful initialization.
- Always call
dispose()when the runtime is no longer needed. It owns a dedicated worker and two ONNX sessions that intentionally remain alive for reuse until the worker is terminated. - A request timeout or fatal worker/WebGPU error terminates that runtime. Create a new runtime to continue.
- Inputs longer than the model's 256-token context are truncated.
- User text stays on the device; the runtime does not call a translation API.
- The model host must allow cross-origin requests when it is on a different origin from the application.
- Confirm that you have permission to host and redistribute whichever model weights you provide.
