"Just add a translate button" sounds simple until you care about SEO, performance, or actually ranking in another language. There are four fundamentally different ways to translate a website automatically, and they are not interchangeable — each trades off SEO value, cost, latency, and engineering effort differently. This guide walks through all four with real code, so you can pick the one that matches your goals instead of discovering the downsides in production.
Approach 1: Client-Side Widget (Google Translate Widget)
The oldest and easiest option: drop a JavaScript snippet on the page and let the browser translate the DOM after load. Google's website widget is the classic example.
<div id="google_translate_element"></div>
<script>
function googleTranslateElementInit() {
new google.translate.TranslateElement(
{ pageLanguage: "en" },
"google_translate_element"
);
}
</script>
<script src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
Pros: Zero backend work, live in five minutes, covers dozens of languages instantly.
Cons — and they are serious:
- SEO value is essentially zero. The translation happens in the user's browser after the page loads. Googlebot indexes your original-language HTML; the translated text does not exist in the source. You will never rank for a Spanish keyword this way.
- Flash of untranslated content. Users see English, then a flicker, then their language.
- No control. You cannot fix a bad translation, protect brand terms, or style the output.
Use it only when: translation is a convenience feature for logged-in users or internal tools and you do not care about search traffic.
Approach 2: Server-Side Rendering with a Translation API + Cache
Here the server translates content before sending HTML to the browser. Crawlers receive fully translated markup, so this approach earns real SEO value. The key is caching — you never want to hit the translation API on every request for the same content.
// Express: translate-on-render with a cache-first pattern
import express from "express";
const app = express();
const cache = new Map(); // swap for Redis in production
async function translateHtml(html, to) {
const key = to + ":" + hash(html);
if (cache.has(key)) return cache.get(key);
const res = await fetch(
"https://aibit-translator.p.rapidapi.com/api/v1/translator/html",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-RapidAPI-Key": process.env.RAPIDAPI_KEY,
},
body: JSON.stringify({ from: "en", to, html }),
}
);
const data = await res.json();
const translated = data.trans;
cache.set(key, translated);
return translated;
}
app.get("/:lang/product/:id", async (req, res) => {
const { lang, id } = req.params;
const html = await renderProductPage(id); // your normal template
if (lang === "en") return res.send(html);
res.send(await translateHtml(html, lang));
});
app.listen(3000);
Pros: Crawlers get translated HTML (real SEO), you use an HTML-aware endpoint so tags survive, and the cache keeps cost and latency near zero after the first request.
Cons: You must build and invalidate the cache, and the first uncached request pays full translation latency. Use a persistent store (Redis, a database, or the filesystem) so a deploy or restart does not wipe every translation.
Approach 3: Static Pre-Translation at Build Time
Instead of translating on request, translate everything once when you build the site and ship static translated pages. This is the fastest and cheapest option to serve, because runtime does no translation at all.
// Next.js: generate a translated page per locale at build time
// app/[lang]/page.tsx (App Router, static generation)
export async function generateStaticParams() {
return ["en", "es", "fr", "de", "ja"].map((lang) => ({ lang }));
}
async function getContent(lang) {
const source = await loadMarkdownAsHtml("home"); // your source content
if (lang === "en") return source;
const res = await fetch(
"https://aibit-translator.p.rapidapi.com/api/v1/translator/html",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-RapidAPI-Key": process.env.RAPIDAPI_KEY,
},
body: JSON.stringify({ from: "en", to: lang, html: source }),
}
);
return (await res.json()).trans;
}
export default async function Page({ params }) {
const { lang } = params;
const html = await getContent(lang);
return <article dangerouslySetInnerHTML={{ __html: html }} />;
}
Pros: Best possible runtime performance (pure static HTML on a CDN), best SEO, and you pay for translation exactly once per build. Perfect for marketing sites, docs, and blogs where content changes on a deploy cadence.
Cons: Not suitable for content that changes constantly or is user-specific — you would rebuild endlessly. Build times grow with pages × languages, though caching translated output between builds fixes most of that.
Approach 4: Proxy Translation Layer
A reverse proxy sits in front of your existing site, intercepts each response, translates the HTML on the fly, and serves it under a localized hostname or path — without you changing the origin app at all. This is how many "translate your whole site" SaaS products work.
// Conceptual proxy middleware (Express)
app.use(async (req, res, next) => {
const lang = detectLangFromHost(req.hostname); // es.example.com -> "es"
if (!lang || lang === "en") return next();
const originHtml = await fetchFromOrigin(req.url);
const translated = await translateHtml(originHtml, lang); // + cache, as above
res.send(translated);
});
Pros: Zero changes to the origin application, works with any tech stack (even legacy), and each language can live on its own indexable subdomain.
Cons: You add a hop of latency and a new piece of infrastructure to run and secure, caching becomes critical, and debugging a translated-through-a-proxy page is harder. Great for retrofitting an app you cannot easily modify; overkill for a greenfield project.
SEO Implications: The Part Everyone Gets Wrong
Automatic translation is worthless for growth if search engines cannot index it. Three rules matter regardless of approach:
- Give each language its own URL. Use
/es/page,/fr/page, ores.example.com— never a cookie or query param that serves different content at the same URL. Crawlers need a distinct, stable address per language. - Add
hreflangtags so Google knows the pages are translations of each other and serves the right one per region. - Render translated text server-side (approaches 2, 3, and 4). Client-side widgets (approach 1) fail this test entirely.
<link rel="alternate" hreflang="en" href="https://example.com/en/product/42" />
<link rel="alternate" hreflang="es" href="https://example.com/es/product/42" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr/product/42" />
<link rel="alternate" hreflang="x-default" href="https://example.com/en/product/42" />
Cost Estimate: A 100-Page Site
Assume 100 pages averaging 4,000 characters each = 400,000 characters of source, translated into 5 languages = 2,000,000 characters total.
| Approach | Translation calls | Cost @ $20/1M (Google) | Cost @ ~$0.03/1M (AIbit) |
|---|---|---|---|
| Static pre-translation | Once per build | ~$40 / build | ~$0.06 / build |
| Server-side + cache | Once per unique page, then cached | ~$40 first fill | ~$0.06 first fill |
| Proxy + cache | Once per unique page, then cached | ~$40 first fill | ~$0.06 first fill |
| Client widget | N/A (browser-side) | Free but no SEO | — |
The lesson: with proper caching, translation is a one-time cost per unique piece of content, not a per-visitor cost. What separates a $40 bill from a $0.06 bill is the per-character rate of the API you choose — which is why a low-cost, HTML-aware engine matters more than the architecture once caching is in place.
Which Approach Should You Choose?
- Marketing site, blog, or docs? Static pre-translation (Approach 3). Best speed, best SEO, cheapest to serve.
- Dynamic app with changing content? Server-side + cache (Approach 2). Fresh translations, real SEO, controlled cost.
- Legacy app you cannot modify? Proxy layer (Approach 4).
- Internal tool, SEO irrelevant? Client widget (Approach 1) — the only good use for it.
Get Started
Approaches 2, 3, and 4 all lean on one thing: a translation API that returns valid HTML and costs little enough that translating an entire site is a rounding error. AIbit Translator exposes a native HTML endpoint, covers 240+ languages, routes across multiple engines for quality, and lands near $0.03 per million characters with a free tier to prototype. Wire up the cache-first pattern above and translate your whole site this week — grab a key at aibitranslator.com.