Translate dialects. Define any script. Read raw manga.
A translator, a multilingual dictionary and a manga reader, built entirely on free sources. No API keys, no accounts, no paid tier, no trial.
Translate between roughly a hundred languages, then refine the result for a
specific region. Ask for Mexican Spanish and ordenador becomes computadora.
Ask for Punjabi in Shahmukhi and the Gurmukhi is transliterated into the
Perso-Arabic script. Every substitution is listed underneath the translation
with a confidence level and a reason, so nothing is changed invisibly.
Define a word in any of 107 languages. Definitions, IPA, recordings by real human speakers, synonyms, antonyms and example sentences, merged from several sources into one entry with the licences attributed in the margin.
Read a raw Japanese, Korean or Chinese page. Speech bubbles are detected, the text is recognised and translated, and the result is drawn back into the bubbles it came from. Recognition runs in your browser, so the page is never uploaded anywhere. A browser extension does the same thing in place on any raw site you are already reading.
git clone https://github.com/Abudora-0/Wakaru.git
cd Wakaru
npm install
npm run devThat is the whole setup. There is nothing to configure and no key to obtain, because every provider works anonymously. Open http://localhost:3000.
npm run dev # the web app on port 3000
npm test # unit tests, no network, no quota spent
npm run test:live # hits the real providers, run this deliberately
npm run e2e # end to end, against a production build
npm run check # house style, types and tests
npm run ext:build # build the browser extension
npm run brand:build # regenerate every icon from brand/*.svgEvery source below was probed live before it was wired in. This table records what was actually true, including the parts that are inconvenient.
| Source | Status | Provides | Limit |
|---|---|---|---|
| MyMemory | live, keyless | translation, about 100 pairs | 5,000 chars a day, 50,000 with an email |
| LibreTranslate | self hosted | translation | none, it is your machine |
| dictionaryapi.dev | live, keyless | English definitions, IPA, audio | fair use |
| Wiktionary REST | live, keyless | definitions in ~180 languages | fair use |
| Lingua Libre via Wiktionary | live, keyless | human pronunciation recordings | fair use |
| Datamuse | live, keyless | English synonyms and antonyms | 100k a day |
| Tesseract.js | in your browser | OCR for jpn, jpn_vert, kor, chi, eng | none, it is local |
Two of these deserve a note rather than a footnote:
The public LibreTranslate instance now requires an API key. It is only free if you run it yourself, which is one command and is genuinely worth doing:
docker compose -f docker/libretranslate.yml up -d
echo "LIBRETRANSLATE_URL=http://localhost:5000" >> .env.localOnce that is set the provider chain puts it first automatically. No quota, no third party, nothing leaving your network.
The undocumented Google endpoint is included but disabled. It is keyless
and by far the highest quality option, and it is also undocumented, rate
limited by IP address and not covered by any published terms for this use.
Turning it on is a deliberate decision for whoever runs the deployment, not a
default, so it sits behind WAKARU_ENABLE_GTX.
No free provider is reliable on its own. Providers are tried in priority order behind a circuit breaker: three consecutive failures bench a provider for a minute, and a quota refusal benches it for fifteen. Results are cached in an LRU in front of CDN cache headers, which is what makes a 5,000 character daily budget survive contact with more than one visitor. Every response says which provider served it and what it fell back from, and the interface shows that rather than hiding it.
107 languages, 38 curated dialects.
No free API exposes dialects. Not one. So the dialect layer is not an API call, it is a hand written, reviewable dataset in this repository, applied in three passes after translation:
- Locale routing. Pass
pt-BRupstream where a provider understands it, and fall back toptwhere it does not. - Lexicon overlay. Substitution maps with a confidence level and a note.
cogerbecomestomarfor Mexico, flagged high confidence, becausecogeris vulgar there.raparigabecomesgarotafor Brazil for the same reason. - Script transliteration. Serbian Cyrillic and Latin map one to one and are marked lossless. Gurmukhi to Shahmukhi and Devanagari to Latin are approximations and are marked lossy, with the reason shown to the reader.
Devanagari romanisation is done with syllabic rules rather than a lookup table,
because Indic consonants carry an inherent vowel that is never written. A
character map turns नमस्ते into nmste. Wakaru returns namaste, and returns
kamal rather than kamala for कमल because Hindi drops the final inherent
vowel.
Adding a dialect means appending one object to
packages/core/src/dialects/data.ts.
No code changes. See CONTRIBUTING.md.
A website cannot read another website's page, so the in place reader is a browser extension. The site itself takes a page you give it.
The easy way: download the pre-built extension from
the latest release,
unzip it, and load it unpacked. Chrome, Edge and other Chromium browsers use
chrome://extensions with developer mode on; Firefox uses
about:debugging#/runtime/this-firefox and Load Temporary Add-on, which
lasts until Firefox restarts since the build is not signed. The release notes
walk through both.
Building it yourself, which is the only way to change the default translator endpoint before install rather than after:
npm run ext:build # Chrome, Edge and other Chromium browsers
npm run ext:build:firefox # FirefoxThen load apps/extension/.output/chrome-mv3 (or firefox-mv2) the same way
as above. A seal appears on every large image; press it to read that page.
Once you have read one page on a site by hand, every page after it reads
itself automatically as it loads, unless turned off in the popup.
Three details in there are worth knowing about, because they are the parts that usually break:
- Cross origin images taint a canvas, and a tainted canvas cannot be read back, which would defeat OCR on essentially every real site. The extension fetches the image bytes in the background service worker instead and hands them on as a data URL.
- A service worker has no DOM, so Tesseract cannot run in it. Recognition happens in an MV3 offscreen document, which is kept alive between pages so the language model downloads once rather than on every page turn.
- Site access is requested per site, on first use, rather than at install. An extension like this asking for every URL up front is normal and is the wrong default.
Bubble detection is plain canvas work rather than OpenCV, which would add about eight megabytes to a page that already downloads a language model. A bubble is an enclosed light region that does not touch the page border, is roughly convex inside its own bounding box, and contains ink at a density that reads as text rather than as artwork. Tall narrow regions are read with the vertical Japanese model. Pages with no bubbles at all, which is most webtoons, fall back to whole page recognition.
Wakaru is source agnostic and works on local files. Respecting the copyright on whatever you point it at is your responsibility.
The interface is called Sumi Press. It is built on ink, paper and print rather than on gradients and rounded cards, and every control is drawn from scratch:
- Scrollbars are square, with a screentone track and a vermilion thumb.
- Buttons carry a solid offset shadow with zero blur and travel into it when pressed, so a button stamps rather than lifts.
- The translate control is a seal, and pressing it rotates as it stamps.
- The source field is genkō yōshi, Japanese manuscript paper, one square per character.
- The language picker is a real ARIA combobox, never a native select, with a sample of each language's own script instead of a flag. A flag names a country, not a language, and choosing one for Arabic or Spanish would be a political statement.
- Loading is a screentone shimmer, never a spinner.
- Ctrl and K opens a command palette that jumps to any page, finds any of the 107 languages, or defines whatever you type. It is a real modal dialog: it takes focus, traps it, and hands it back to whatever opened it.
- Radius never exceeds 4px. The seal is the only round object in the system.
Two themes: Paper and Night Ink. Both are explicit, so a chosen theme beats the operating system in either direction.
Accessibility is treated as functional, not decorative: every foreign string
carries its own lang and dir, right to left renders correctly in Arabic,
Urdu and Hebrew, the combobox is fully keyboard operable, focus rings are loud,
and prefers-reduced-motion disables the stamps and speed lines.
There is no CSS framework. The design system is hand written, so a utility framework on top of it would have contributed nothing but a dependency.
flowchart TB
subgraph clients [" "]
web["apps/web<br/>Next.js 16"]
ext["apps/extension<br/>MV3"]
end
subgraph shared ["shared packages"]
core["@wakaru/core<br/>providers, languages, dialects"]
ocr["@wakaru/ocr<br/>bubbles, Tesseract"]
tok["@wakaru/tokens<br/>Sumi Press"]
end
subgraph free ["free sources"]
mm["MyMemory"]
lt["LibreTranslate<br/>self hosted"]
wikt["Wiktionary"]
dapi["dictionaryapi.dev"]
dm["Datamuse"]
end
web --> core & ocr & tok
ext --> core & ocr & tok
core -->|chain with fallback| mm & lt
core -->|merged into one entry| wikt & dapi & dm
ocr -->|runs in the browser| ocr
packages/core imports no framework, so the web app and the extension run the
same provider logic, the same language registry and the same dialect rules
rather than two copies that drift apart.
wakaru/
apps/
web/ Next.js 16, route handlers, pages
extension/ MV3, content script, offscreen OCR, popup
packages/
core/ providers, language registry, dialect engine
ocr/ bubble detection, preprocessing, Tesseract
tokens/ design tokens and control primitives
brand/ the four logo sources, everything else is generated
docker/ self hosted LibreTranslate
scripts/ house style guard, brand build
Speech takes three routes, tried in order, and the interface says which one answered:
- A voice from your operating system. Instant, nothing to download. This is Web Speech, so coverage depends entirely on the machine.
- A neural voice that runs in your browser. Piper models are fetched once, about 60 MB, cached, and then run locally on the CPU. No key, no endpoint that can rate limit or disappear, and it works with a privacy blocker that hides the system voice list.
- Nothing, said plainly rather than played as silence.
Nothing downloads until a language actually needs it, so a reader whose machine already speaks Spanish never fetches a Spanish model.
33 languages have a downloadable voice, including Spanish, French, German, Chinese, Arabic, Russian, Portuguese, Italian, Dutch, Polish, Turkish, Ukrainian, Vietnamese, Persian and the Nordic languages. Mexican Spanish, British English, European Portuguese and Flemish have voices of their own rather than borrowing the base language's.
Japanese and Korean do not. They exist in Piper upstream but not in the voice mirror this runtime is pinned to, so they fall back to a system voice. Both are widely installed, so most readers still hear them, but the site says so rather than pretending.
Neither does South Asia. Hindi, Urdu, Punjabi, Bengali, Tamil, Telugu, Malayalam, Kannada, Gujarati and Marathi have no voice in the mirror this runtime is pinned to at all, checked directly against its own voice list rather than assumed. Hindi is common enough on device that most readers hear it anyway; the rest are honestly labelled "if installed" rather than promised, and read as "no voice" outright on a machine that has none, which is the correct answer rather than a silent failure.
The languages page marks every entry: a downloadable voice reads as a promise, a system voice reads as a prediction, and neither reads as "no voice".
Wakaru needs no environment variables to run, so a deploy is genuinely just an
import. The only thing to get right is the root directory, because this is an
npm workspace monorepo and the site lives in apps/web.
Importing by hand instead:
- vercel.com/new, then import
Abudora-0/Wakaru. - Set Root Directory to
apps/web. Vercel still installs from the workspace root, so the shared packages resolve. - Framework preset should read Next.js. Leave the build and output settings alone.
- Deploy. Nothing else is required: every provider works anonymously.
None of these are needed. See .env.example.
| Variable | Effect |
|---|---|
MYMEMORY_EMAIL |
Lifts the MyMemory daily budget from 5,000 to 50,000 characters |
LIBRETRANSLATE_URL |
Points at your own LibreTranslate, which removes the quota entirely and takes priority over everything else |
LIBRETRANSLATE_API_KEY |
Only for a hosted LibreTranslate instance |
WAKARU_ENABLE_GTX |
Turns on the undocumented Google endpoint. Read the note above before you do |
The extension needs to know where your deployment lives. It ships pointed at
https://wakaruu.vercel.app. To use your own, set it in the extension popup under Translator
endpoint, or change the default in
apps/extension/lib/messages.ts and rebuild.
Pick the hostname carefully. wakaru.vercel.app, the obvious choice, is
already registered to an unrelated JavaScript decompiler, and an extension
pointed there would post recognised text from people's pages to a stranger's
server.
No em dashes. Not in code, not in UI copy, not in documentation. This is enforced mechanically rather than by review:
npm run check:emdashIt runs in the pre-commit hook and in CI and fails the build, because a style rule nobody can enforce is not a rule.
Commits use Conventional Commits and carry no AI or assistant attribution:
no Co-Authored-By trailer, no generated-with footer.
Tests never touch the network. npm test runs against recorded fixtures so
it cannot fail because a free provider is down and cannot spend a daily quota.
The end to end suite stubs the provider routes for the same reason. Anything
that genuinely needs a live endpoint is quarantined: *.live.test.ts behind
npm run test:live, and specs tagged @live behind npm run e2e:live.
- Firefox build of the extension, using a direct background page path since Firefox has no offscreen API and does not need one
- More dialects, particularly South Asian and African varieties, 38 and counting
- Furigana readings on Japanese dictionary entries
- Saved word lists and export
- Whole volume reading in the extension rather than page by page
- A better Japanese, Korean and Chinese OCR model. No free, ONNX exportable model currently beats Tesseract for manga specifically, so this waits on one existing rather than on effort here
- Piper voices for Hindi, Urdu, Punjabi and the rest of South Asia, and for Japanese and Korean. Blocked on the voice mirror this runtime is pinned to adding them upstream, not something this project can fix alone
See CONTRIBUTING.md. Adding a dialect is a one file change and is the most useful thing anyone can contribute.
Wakaru is MIT licensed. The data it displays is not, and the interface carries the attribution the licences require rather than burying it:
- Wiktionary definitions and examples, CC BY-SA 4.0
- dictionaryapi.dev, sourced from Wiktionary, CC BY-SA 3.0
- Lingua Libre and Wikimedia Commons recordings, CC BY-SA 4.0
- MyMemory, free tier, attribution requested
- Datamuse, free for public use
- LibreTranslate, AGPL-3.0, run separately
- Tesseract.js, Apache-2.0
Every dictionary entry stamps its sources in the margin. That is a licence obligation, and it was designed to look deliberate rather than apologetic.
translator dictionary dialects manga manhwa ocr tesseract
wiktionary pronunciation ipa text-to-speech nextjs typescript
browser-extension chrome-extension manifest-v3 i18n transliteration
no-api-key self-hosted





