Two Lines of JavaScript, 104 Languages: What I Found in the translate.js Project (And Whether NXplace Should Use It)
Two Lines of JavaScript, 104 Languages: What I Found in the translate.js Project (And Whether NXplace Should Use It)
A forwarded Chinese tech headline promised a lot: "two lines of JS code, one-click support for 104 languages, zero configuration, no API key, and SEO-friendly." That is the kind of claim that either saves you a quarter of i18n work or quietly wrecks your site. So I went and read the actual project — its repo, its docs, its limits — and then ran it against the only question that matters for us: should NXplace adopt it?
Here's the verdict up front: it is a genuinely clever, genuinely free tool that solves the wrong half of the problem for a content platform. Worth piloting as a reader convenience. Not worth replacing a real i18n strategy.
What the project actually is
- Name / repo: translate.js —
xnx3/translate(official sitetranslate.zvo.cn, mirrored on Gitee). ~3.1k stars, 481 forks, 1,100+ commits. - Author: xnx3 (雷鸣云 / leimingyun), a Chinese dev shop.
- License: MIT on the GitHub repo — free for permanent commercial use. The author explicitly allows you to repackage and sell it.
- The pitch in code:
<script src="https://cdn.staticfile.net/translate.js/3.5.1/translate.js"></script>
<script>
translate.language.setLocal('chinese_simplified'); // source language
translate.service.use('client.edge'); // translation channel
translate.execute(); // go
</script>
Drop it before </html>, and a language <select> appears at the bottom of the page. That's it. No JSON language files, no per-string calls, no signup, no API key.
How it works: it walks the DOM at runtime, detects the page's dominant language, sends the text nodes to a machine-translation backend, swaps the text in place, and caches the result. A MutationObserver (translate.listener.start()) watches for dynamically rendered text, so Vue/React/Angular SPAs get translated too.
The claims, checked
| Claim | Verdict | Reality |
|---|---|---|
| "Two lines of JS" | ✅ True | Literally the three-call block above. |
| "No language config file, no API key" | ✅ True | The whole point of the design. |
| "104 languages" | ⚠️ Half-true | Depends entirely on the channel: client.edge = 73, translate.service = 112–450, v2 = 120+. The "104" is somewhere in the middle of the marketing. |
| "Zero configuration" | ⚠️ Half-true | Zero config to try. Real deployments need a decision on channel, caching, cache-busting, and what not to translate. |
| "SEO-friendly" | 🚩 The important one | See below. |
| "Free, MIT" | ✅ True (with asterisks) | Free tier exists; heavy use pushes you toward the paid/LLM channel. |
The SEO claim deserves its own section
The tagline says "SEO-friendly" and the docs say the crawler "sees your source unchanged." That is technically true — and it's a double-edged sword.
What it means: because translation happens in the browser after load, Googlebot (and any crawler that doesn't run your JavaScript, and there are still plenty) receives your original HTML. Your original-language SEO is untouched — no duplicate-content mess, no cloaking.
But the flip side is the one nobody advertises: your translated content never exists in the HTML source, so it cannot be indexed as translated pages. No /es/, /ja/, no hreflang, no ranking for Spanish or Japanese keywords. You get a nicer experience for a human who lands on your page; you do not get a single extra organic visit from a Spanish search.
For real international SEO the author ships a separate TCDN product — a server-side mirror that pre-translates your site, binds per-language domains, and serves fully translated HTML that is crawlable. That's the honest SEO answer, and it's a different, paid-ish product.
Limits, channels, and the fine print
| Channel | Backend | Languages | Daily limit | Cost |
|---|---|---|---|---|
client.edge (recommended) |
Microsoft | 73 | None | Free |
translate.service (default) |
Community, self-hostable | 112–450 | ~2M chars/day | Free (occasionally unstable) |
giteeAI LLM channel |
Qwen3 / Hunyuan-MT | ~100+ | 3M chars/day with invite (1M without) | Free tier; ¥10 top-up for pay-as-you-go; qwen3-8B free |
| Enterprise cluster | Multi-region | 112 | 2M+/day, negotiable | Paid |
Things you should know before trusting it:
- Your text leaves your server. On
client.edgethe data goes to Microsoft; on the community channel, through the author's nodes; on GiteeAI, to China's GiteeAI platform. The docs are unusually candid about this: "data will flow overseas — if you have data-security requirements, use private deployment." - JS is mandatory. No JS, no translation. Crawlers, accessibility tools and email previews see the source only.
- Flash of untranslated content. Open a translated page and you see the original for a moment before the translation lands — there's a config option to hide text until translation completes, which trades one problem for another.
- Machine translation ≠ brand voice. Auto-MT on editorial content, product names, and legal pages is where this tool quietly embarrasses people. It has a terminology overrides API (
translate.nomenclature.append) and ignore lists (by id/class/tag/text) — use them, or your brand name gets translated. - Third-party dependency. You're loading a script from someone else's CDN. The docs even tell you to self-host the JS file and consider private-deploying the backend API.
- Private deployment is real and cheap: it ships a distilled translation model (
translate100, from m2m100/small100) that runs on 1 core / 2 GB RAM at ~43 tokens/sec on a plain CPU — no GPU, no internet. That's a genuinely notable engineering detail.
Who already uses it: Discuz, Layui, DzzOffice, wangmarket CMS, Z-Blog, several Chinese CMS platforms, plus universities and state-linked enterprises. The community backend reportedly handled 1B+ translation requests per day in late 2024 — which is exactly why the free public channel wobbles.
Should NXplace add it?
I'd say yes as a feature, no as a strategy. Here's the split.
Upside
- Days of work, not weeks. Two lines + a language picker. No i18n file plumbing, no per-key translation, no build step.
- It works on the hard cases. SPA rendering, dynamically loaded comments, user-generated content — all handled by the DOM listener. That's the part that normally makes i18n expensive.
- Free and MIT. No license risk, no per-seat cost.
- Content-heavy site, mixed languages. NXplace already publishes in English, Chinese, Cantonese — a "read this in my language" toggle is a genuine reader gift, especially for cross-language browsing.
- Built-in caching (browser + server memory + file) means the common paths are near-instant after first view.
Downside — and for us, some are serious
- No translated-content SEO at all. Every post on NXplace lives at one URL with one
languagefield. translate.js adds zero indexable language variants. If international search traffic is a goal, this tool doesn't deliver it — TCDN or real i18n does. - Data flows to third parties. NXplace hosts third-party contributors' content. Quietly shipping every page's text to Microsoft/Google/GiteeAI is a privacy and compliance decision that deserves an explicit line in the terms, not a two-line script.
- Translation quality on editorial content. Auto-MT will mangle titles, taglines, brand names, and tone. On a platform whose whole value is editorial voice, that's a visible cost.
- UX papercuts at scale. Flash of untranslated text, layout shift on long strings, dropdown dumped at the page bottom, translated UI breaking buttons/labels.
- An external dependency on every page load — plus the recurring reality that the free public channel is under enormous load.
- It competes with what we already have. NXplace's own multi-language channels and per-post language field are the correct architecture. Layering auto-MT on top can create two competing translation models and confuse users about what's "really" in which language.
The recommendation
- Pilot it, don't adopt it wholesale. Put translate.js behind a feature flag on a staging subdomain or a single channel, wired to
client.edge(no character cap) with the JS self-hosted and a strict ignore-list for brand names, nav, and code blocks. - Scope it tightly. Translate article bodies only (
translate.setDocuments(...)), never buttons, forms, code, or legal copy. - Treat it as accessibility/convenience, not SEO. Keep native channel languages as the primary discovery strategy. If localized search traffic matters, price out TCDN or commit to real i18n output.
- Get consent and disclosure right. Update the privacy/terms line to say pages may be machine-translated via third-party services. For sensitive or private content, don't translate client-side at all.
- Keep the escape hatch. If it becomes load-bearing, private-deploy the backend (
translate100on a small CPU box) so no text leaves our infrastructure.
Bottom line: translate.js is one of the better-maintained free i18n tools I've read this year — clever architecture, honest docs, a real private-deployment path, and a permissive license. For NXplace it's a cheap, high-delight reader toggle worth a two-week pilot. It is not a substitute for an internationalization strategy, and its "SEO-friendly" badge means "doesn't hurt your original SEO," not "gets you translated traffic." Buy it for what it actually is, and it's a bargain.
Sources: the project README and docs at translate.zvo.cn (channels, character limits, v2→v3 notes), the GitHub repo xnx3/translate (license, stars, activity), and general search-engine guidance on client-side-rendered content and indexability.