Static HTML and site generators
Plain HTML pages, Hugo, Jekyll, Eleventy and Astro. Paste the code into the template every page shares, or into each page if you have no shared layout.
Add three lines of code and SiteTran translates your site into 187+ languages: custom-built websites, single-page apps and signed-in dashboards. No strings to extract, no translation files to merge, and no redeploy to publish a translation.
t() or gettext()Driving growth for companies with millions of users
Adding languages to a custom codebase usually means an i18n library and weeks of refactoring. A translation layer translates what your site already renders.
i18next, react-intl, vue-i18n, next-intl, Angular i18n or gettext.
t('invoices.overdue') or _("…") in every component and template, and name a key for each.Your code stays in one language. SiteTran translates the page as it renders.
No new dependency, no npm package and no keys to name. Your templates, components and string literals keep working as they do today, and SiteTran translates the page they render, whichever framework produced it.
Already paid an agency, or have strings in old i18n files? Import CSV, JSON or XLIFF and SiteTran uses those translations from day one, so you launch with the work you already own. How import works.
If you can edit the HTML that wraps every page, you can add SiteTran. It works the same in a static page, a server-rendered template and a single-page app.
Sign up free and add your site's URL. SiteTran gives you your Widget Code, which loads the translator and adds the language dropdown.
Add the three lines to the layout or HTML shell every page shares, just before </body>. The div marks where the dropdown appears.
Open your site and pick a language. SiteTran discovers your text, then you translate it with AI, your own translators, or both, and publish.
<script>var sitetran = {site_id: your_site_id};</script> <script src="//c.sitetran.com/widget/v3.js"></script> <div id="sitetran_translate_element"></div>
SiteTran gives you your own ready-to-paste code, with your site ID already in it. The widget needs the div, and where you put it decides where the dropdown shows. Prefer your own placement? Move the div or position it with CSS.
<body> <div id="root"></div> <!-- your app mounts here --> <!-- SiteTran, outside the app root --> <script>var sitetran = {site_id: 12345};</script> <script src="//c.sitetran.com/widget/v3.js"></script> <div id="sitetran_translate_element"></div> </body>
Static sites, React, Vue, Angular and Svelte. Most single-page apps have one HTML shell: in Vite and Vue, src/ in Angular, src/app.html in SvelteKit. Place the code outside the element your app mounts into, so re-renders and route changes never remove the dropdown.
import Script from 'next/script'; export default function RootLayout({ children }) { return ( <html lang="en"> <body> {children} <Script id="sitetran-config" strategy="beforeInteractive"> {`window.sitetran = {site_id: 12345};`} </Script> <Script src="https://c.sitetran.com/widget/v3.js" strategy="afterInteractive" /> <div id="sitetran_translate_element" /> </body> </html> ); }
Next.js App Router. The root layout stays mounted across client navigations, so the dropdown persists. Loading the widget with afterInteractive lets React hydrate first. If you see hydration warnings, try lazyOnload. Nuxt works the same way: load the scripts through app.head with tagPosition: 'bodyClose' and put the div in app.vue.
<body> <%= yield %> <script>var sitetran = {site_id: 12345};</script> <script src="//c.sitetran.com/widget/v3.js"></script> <div id="sitetran_translate_element"></div> </body>
Rails. One layout covers every view. If your app uses Turbo Drive, read the note on body-swapping navigation before launch.
<body> {% block content %}{% endblock %} <script>var sitetran = {site_id: 12345};</script> <script src="//c.sitetran.com/widget/v3.js"></script> <div id="sitetran_translate_element"></div> </body>
Django and Flask. Put it in the base template every page extends. You don't need makemessages, .po files or compilemessages for the text SiteTran translates.
<body> @yield('content') <script>var sitetran = {site_id: 12345};</script> <script src="//c.sitetran.com/widget/v3.js"></script> <div id="sitetran_translate_element"></div> </body>
Laravel, Express and PHP sites. Add it to the shared layout, such as a Blade layout, an EJS or Pug layout, or a PHP footer include.
Follow the JavaScript/Custom Integration With SiteTran guide for every click, from copying your Widget Code to choosing your first language.
SiteTran runs in the browser and reads the page your framework renders, so it doesn't care what produced the HTML. Here is what to know for each.
Plain HTML pages, Hugo, Jekyll, Eleventy and Astro. Paste the code into the template every page shares, or into each page if you have no shared layout.
SiteTran changes the text inside existing text nodes instead of wrapping them in new elements, so React keeps the DOM nodes it rendered. Content from state updates and API calls is translated as it appears.
Load the widget after hydration from the root layout. For indexable language URLs, add the Static Translation API in your proxy.ts or at the edge.
Vue Router navigation and reactive updates are translated as they render. In Nuxt, load the scripts at the end of the body and keep the div in app.vue, which stays mounted between pages.
Angular's built-in i18n builds a separate copy of your app for each locale. SiteTran works on the one build you already ship, so adding a language doesn't add a build.
Put the code after the %sveltekit.body% wrapper. Client-side navigation and component updates are picked up as they render.
Server-rendered pages are the easiest fit: content is in the HTML from the start, which also makes Static Translation API snapshots complete for SEO.
Translation happens in each user's browser, so screens behind your login work too. Keep names, emails and user content out with notranslate, and consider turning off Public Phrase Discovery.
Using Turbo Drive, htmx hx-boost or Astro view transitions? Click through a few pages in a second language before launch to confirm the dropdown carries across. Send us your setup and we'll help you place it.
Single-page apps change the page constantly. Here's how SiteTran handles the parts that usually break translation tools.
SiteTran watches the page for changes, so new routes, modals, toasts, infinite lists and API responses are translated as they render. It checks for new text at most every 0.5 seconds by default, adjustable with refresh_translation_seconds.
Add notranslate or translate="no", or list selectors in sitetran.ignore_selectors. For values inside a sentence, use variables: Reminder sent to {{customer}} needs one translation for every customer.
Read sitetran.lang, switch with sitetran.api_change_language(), and react to changes with language_change_function or the sitetranReady event. See the API.
SiteTran updates the page's lang attribute and switches text direction for Arabic, Hebrew and other RTL languages. Add CSS per language and page in SiteTran when German labels need more room.
You can add SiteTran to a staging site or sandbox. Keep languages hidden until they're Live, or turn on Hide All Widgets On Site (dev mode) and add ?sitetran=on to preview.
The default URL Type leaves your #/settings routes and #pricing anchors untouched and remembers each visitor's language. Keep it for single-page apps; Use Hash is for sites without hash routing.
Everything is set on the sitetran object in your Widget Code or called at runtime. No SDK to install and nothing to keep in sync with a package version.
sitetran.langThe visitor's current language code, such as "fr".api_change_language(code)Switch language from your own menu, settings page or user profile.language_change_functionRuns on every change with a reason: UA-SELECT, UA-LIST, API-PROG, SYS-AUTO or SYS-COOKIE.sitetranReady eventFires once the dropdown is built and translations are applied.ignore_selectorsCSS selectors SiteTran won't translate or discover.auto_switch_from_browser_langSet to false to stop switching to the browser's language on the first visit.use_preset_cssSet to false to style the dropdown entirely with your own CSS.Full reference: Frontend API - Widget Initialization Options.
// Sync your own React menu with SiteTran function LanguageMenu() { const [lang, setLang] = useState(window.sitetran?.lang); useEffect(() => { const onReady = () => setLang(window.sitetran.lang); window.addEventListener('sitetranReady', onReady); window.sitetran.language_change_function = (code, reason) => { setLang(code); gtag('event', code, { event_category: 'sitetran-language' }); }; return () => window.removeEventListener('sitetranReady', onReady); }, []); return ( <select translate="no" value={lang} onChange={e => window.sitetran.api_change_language(e.target.value)}> <option value="en">English</option> <option value="fr">Français</option> <option value="he">עברית</option> </select> ); }
translate="no" keeps language names in their own language. Keep the #sitetran_translate_element div on the page even with your own menu; hide it with CSS if you don't want two switchers.
Checked against the current widget code. If your security team asks what SiteTran loads and where it connects, this is the list.
c.sitetran.com serves the widget script, your site's translation data and flag icons. www.sitetran.com receives phrase discovery and usage stats.
var sitetran = {…} line needs a nonce or hash, or move it into a script file you host.<style> elements for your dropdown design and per-language CSS, so styles need 'unsafe-inline'.https://www.sitetran.com in script-src.The widget is a single script on Cloudflare's CDN. We measured it on October 20, 2026:
There are three broad approaches. They differ in who does the work, where translations live, and what happens when your content changes.
| i18n library + TMSi18next, react-intl, vue-i18n, gettext, with Lokalise, Crowdin or Phrase | Hosted translation proxyA vendor serves translated copies of your pages | SiteTranTranslation layer in your layout | |
|---|---|---|---|
| How it works | Your code looks up each string by key from translation files bundled with your app. | Language URLs point at the vendor, which fetches your HTML and returns it translated. | The widget translates the page as it renders in the browser. Add the Static Translation API for language URLs. |
| Code changes | Every component and template, plus locale routing. | DNS or routing changes for each language. | Three lines in your layout. |
| New text | Add a key, extract, translate, rebuild and deploy. | Picked up when the vendor next fetches the page. | Discovered when it's shown; goes live when published, with no deploy. |
| Content your app renders in the browser | Covered, if every string goes through the library. | The proxy sees the HTML your server returns; text rendered later needs a script as well. | Translated as it appears, including signed-in screens. |
| Indexable language URLs | Yes, once you build locale routes. | Yes, hosted by the vendor. | Yes, from your own edge or server, with HTML you cache. |
| Translators and review | In the TMS, a separate subscription. | Built into the vendor's dashboard. | Translators, reviewers, Glossary and Translation Rules built in. |
| Existing translations | Merged into message files by a developer. | Depends on the vendor's import options. | Import CSV, JSON or XLIFF and use them right away. |
Pricing snapshot from public pricing pages on October 20, 2026: Lokalise starts at $149/month and Phrase's Software UI/UX plan at $525/month billed annually; Localize quotes custom pricing. Weglot starts at $17/month for 1 language, and its documentation says translations from its JavaScript integration aren't seen by search engines. SiteTran includes unlimited languages on every plan, from $0.
Product UI is short, repetitive and full of terms that must stay consistent. SiteTran gives every phrase the same review path, whether it came from a template or an API response.
Get suggestions for any phrase, or turn on Machine Translation Auto Translate so new UI text is translated as soon as it's discovered. You choose whether it's approved automatically or reviewed first.
Invite your own translators, or teammates who speak the language, and choose who can approve. AI Translation Review adds a second opinion on each translation.
Keep product and feature names fixed in the Glossary, set Translation Rules such as "use informal you in German", and reuse earlier work with Similar Phrases.
Choose Styled Select, Dropdown List or Bare Select, with or without flags, and add your own CSS. Or build your own switcher with the frontend API.
The widget translates in the browser. Search engines index the HTML your server returns, so language URLs need translated HTML. You control the server, which makes this the most flexible setup of all.
When a visitor switches language, the page's metadata switches with the text.
lang attribute and text directionYour edge worker, proxy or server maps each language URL to the original page and asks SiteTran for its translated HTML:
/es/pricing./api/get-static-translation with url=…/pricing and language_code=es.Then set sitetran.static_url_language_code_lookup on each page so the dropdown links to these URLs and the widget adds hreflang tags.
const LANGS = ['es', 'de', 'fr']; export default { async fetch(request, env, ctx) { const url = new URL(request.url); const [, lang, ...rest] = url.pathname.split('/'); if (!LANGS.includes(lang)) return fetch(request); // Original-language page, no query string const source = url.origin + '/' + rest.join('/'); // The /es/ path makes the key URL + language const key = new Request(url.origin + url.pathname); const hit = await caches.default.match(key); if (hit) return hit; const api = new URL('https://www.sitetran.com/api/get-static-translation'); api.searchParams.set('auth_key', env.SITETRAN_AUTH_KEY); // server-side only api.searchParams.set('language_code', lang); api.searchParams.set('url', source); const res = await fetch(api); if (!res.ok) return fetch(source); // fall back to the original const page = new Response(res.body, { headers: { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'public, max-age=86400', } }); ctx.waitUntil(caches.default.put(key, page.clone())); return page; }, };
Route only public pages to this worker. Never send signed-in or personalized pages: cached HTML would be served to other visitors. The Workers cache is per data center, so purge or version keys after you publish translations.
nginx: route /es/ to a small backend that calls the API, and cache it with proxy_cache, using proxy_no_cache and proxy_cache_bypass on your session cookie.
Next.js and Vercel: rewrite language paths in proxy.ts (called middleware.ts before Next.js 16) to a route handler that calls the API and caches the result.
Express: our API guide includes a complete middleware example.
54.87.171.204, so it gets your page instead of a challenge. Fix Cloudflare Bot Blocking for Translation SEO.Start from the translations you already have, and reuse what your team approves in SiteTran wherever else you need it, from your mobile app to transactional emails.
Already have translations from an agency or old i18n files? Import CSV, JSON or XLIFF and launch with them instead of starting over.
Under Settings › Export, download CSV, JSON, TypeScript or XLIFF for the languages and pages you choose, with optional data such as approval status and translator.
JSON maps to i18next or vue-i18n message files, TypeScript gives you a ready-made object per language, and XLIFF works with Angular and TMS tools. Handy for app strings, push notifications and emails.
Exporting your Data in SiteTran · Importing your Files & Data Into SiteTran
Open your browser's console. Most problems on custom sites show up there in the first few seconds.
<div id="sitetran_translate_element">. The console shows "SiteTran element not found" without it.?sitetran=on to your URL.div is probably inside a component that unmounts. Move it to or your root layout, outside the app's mount point.<body> on navigation (Turbo, htmx boost), test with the swap turned off.translate="no" or the notranslate class in your components, or list selectors in sitetran.ignore_selectors.strategy="afterInteractive" or lazyOnload in Next.js.v3.js in a way that runs before your framework hydrates the server HTML.<meta charset="utf-8">.#anchor links or hash routes stop working, set URL Type back from Use Hash to its default.Every plan includes unlimited languages and teammates. Start free and upgrade when your traffic grows.
For side projects and testing
For sites with moderate translation needs
For larger sites, apps, or several smaller ones
For large-scale international products
Can't find your answer? Send us your stack and we'll tell you exactly where the code goes.
Email supporttranslate="no" where you want text left untranslated.sitetran config with a beforeInteractive script, load v3.js with afterInteractive so React hydrates first, and render the sitetran_translate_element div in the layout.https://c.sitetran.com in script-src, connect-src and img-src, and https://www.sitetran.com in connect-src. The inline config line needs a nonce or hash, or you can move it into a file you host. The widget adds style elements for the dropdown, so style-src needs 'unsafe-inline'.sitetran.api_change_language() with a language code, read the current language from sitetran.lang, and listen for the sitetranReady event or set sitetran.language_change_function to keep your menu in sync. Keep the sitetran_translate_element div on the page and hide it with CSS if you don't want two switchers.notranslate class or the translate="no" attribute to those elements, or list selectors in sitetran.ignore_selectors. Add product names to the Glossary. For apps with sensitive data, turn off Public Phrase Discovery so text from your users' screens is not sent to SiteTran as phrases.sitetran.static_url_language_code_lookup so the dropdown and hreflang tags point to those URLs.notranslate, and never cache signed-in or personalized HTML from the Static Translation API, because it could be served to other visitors.Paste three lines, pick your languages, and let your translators work while your developers keep building. Start on the Free plan and upgrade only when you grow.
These platforms have their own pages with exact install steps and platform-specific tips. See all 29 integrations.