SiteTran
JavaScript/Custom HTML, React, Vue, Next.js, Rails, Django…

Translate any website or web app without an i18n rewrite

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.

  • Free plan, no card needed
  • No npm package or build step
  • AI + human translation
187+languages, including right-to-left
3 linesof code, pasted once in your layout
0strings to wrap in t() or gettext()
~25 KBwidget script, compressed

Driving growth for companies with millions of users

Alignment Neil Patel Exomonitor Cyfe Instructified
i18n vs a translation layer

Skip the internationalization project

Adding languages to a custom codebase usually means an i18n library and weeks of refactoring. A translation layer translates what your site already renders.

The usual way

i18n library + translation files

i18next, react-intl, vue-i18n, next-intl, Angular i18n or gettext.

  1. Wrap every stringReplace hard-coded text with t('invoices.overdue') or _("…") in every component and template, and name a key for each.
  2. Extract and send filesRun the extractor, export JSON, PO or XLIFF, and send it to translators or a TMS.
  3. Merge, rebuild, redeployMerge the translated files, compile them if your stack needs it, and ship a release.
  4. Add locale plumbingLocale routing and middleware, a language switcher, fallbacks and plurals.
Repeat for every new string
With SiteTran

A translation layer on top of your site

Your code stays in one language. SiteTran translates the page as it renders.

  1. Paste the Widget CodeThree lines in your base layout or HTML shell. Nothing else in your code changes.
  2. Browse your siteSiteTran discovers each phrase as it appears, including text your app renders after load.
  3. Translate and reviewMachine translation from DeepL, Google or OpenAI, plus your own translators and reviewers.
  4. PublishApproved translations go live from SiteTran, without a build or a deploy.
New strings are picked up automatically

Your code stays exactly as it is

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.

Bring the translations you already have

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.

How it works

Install in your layout in three steps

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.

1

Create your SiteTran site

Sign up free and add your site's URL. SiteTran gives you your Widget Code, which loads the translator and adds the language dropdown.

2

Paste it once

Add the three lines to the layout or HTML shell every page shares, just before </body>. The div marks where the dropdown appears.

Base layout›Before </body>›Deploy once
3

Translate and publish

Open your site and pick a language. SiteTran discovers your text, then you translate it with AI, your own translators, or both, and publish.

What the Widget Code looks like
<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.

Prefer a step-by-step walkthrough?

Follow the JavaScript/Custom Integration With SiteTran guide for every click, from copying your Widget Code to choosing your first language.

Read the setup guide
Works with your stack

Framework by framework

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.

Static HTML and site generators

Base layout or footer partial

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.

React

, outside #root

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.

Next.js

app/layout.tsx

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 and Nuxt

, outside #app · app.vue

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

src/, outside <app-root>

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.

Svelte and SvelteKit

src/app.html

Put the code after the %sveltekit.body% wrapper. Client-side navigation and component updates are picked up as they render.

Rails, Django, Laravel, Express

Shared layout, before </body>

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.

Signed-in apps and dashboards

Same three lines

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.

Turbo, htmx boost, view transitions

Shared layout

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.

Built for real apps

What developers ask before installing

Single-page apps change the page constantly. Here's how SiteTran handles the parts that usually break translation tools.

Translated

Route changes and async content

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.

You decide

User data and dynamic values

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.

Frontend API

Your own language switcher

Read sitetran.lang, switch with sitetran.api_change_language(), and react to changes with language_change_function or the sitetranReady event. See the API.

Handled

Right-to-left and long words

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.

Supported

Staging and dev environments

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.

Works by default

Hash routers and anchor links

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.

Frontend API

Control translation from your own code

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.

LanguageMenu.jsx · example
// 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.

Security and performance

What your CSP and your page weight need to know

Checked against the current widget code. If your security team asks what SiteTran loads and where it connects, this is the list.

Content Security Policy

Allow two SiteTran hosts

c.sitetran.com serves the widget script, your site's translation data and flag icons. www.sitetran.com receives phrase discovery and usage stats.

script-src 'self' https://c.sitetran.com 'nonce-…' connect-src 'self' https://c.sitetran.com https://www.sitetran.com img-src 'self' https://c.sitetran.com style-src 'self' 'unsafe-inline'
  • The inline var sitetran = {…} line needs a nonce or hash, or move it into a script file you host.
  • The widget adds <style> elements for your dropdown design and per-language CSS, so styles need 'unsafe-inline'.
  • Using Verified Phrase Discovery? Also allow https://www.sitetran.com in script-src.
Performance

One script, one data file

The widget is a single script on Cloudflare's CDN. We measured it on October 20, 2026:

Minified104 KB
Compressed~25 KB
  • Your translations load as one JSON data file per site, which browsers reuse for up to 10 minutes.
  • Visitors in your source language see your page as rendered.
  • Other languages are applied in place as soon as the data loads, and pages served through the Static Translation API arrive already translated.
Compare your options

Every way to make a custom site multilingual

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.

Translation quality

AI speed, without shipping untranslated keys

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.

  • Machine translation from DeepL, Google and OpenAI

    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.

  • Translators and reviewers per language

    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.

  • Glossary, rules and translation memory

    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.

  • A language dropdown that fits your design

    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.

Translate your site or app into 187+ languages

SpanishFrenchGermanPortuguese (Brazil)JapaneseKoreanChinese (Simplified)ItalianDutchPolishTurkishArabicHebrewHindiIndonesianVietnamese+ 170 more
SEO for custom sites

Indexable language URLs, served from your own stack

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.

Included as soon as you install

Everything visitors and link previews see

When a visitor switches language, the page's metadata switches with the text.

  • Page title and meta description
  • Open Graph and Twitter tags
  • Image alt text, tooltips, placeholders and ARIA labels
  • The page's lang attribute and text direction
Signed-in screens don't need this. Dashboards and account pages aren't indexed, so the widget alone is the right setup for them.
Static Translation API

Translated HTML at /es/, /de/ or es.example.com

Your edge worker, proxy or server maps each language URL to the original page and asks SiteTran for its translated HTML:

1A crawler requests /es/pricing.
2Cache hit? Serve it. Otherwise call /api/get-static-translation with url=…/pricing and language_code=es.
3SiteTran loads the original page, applies your published translations and returns HTML.
4Cache it by URL plus language and serve it. Refresh after you publish.

Then set sitetran.static_url_language_code_lookup on each page so the dropdown links to these URLs and the widget adds hreflang tags.

Read the API guide
Cloudflare Worker · example
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.

The same pattern elsewhere

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.

Server-render or pre-render public pages. As with any SEO setup, the API works from the HTML your server returns, so pages with their content in that HTML give search engines the most complete translated copy.
Behind Cloudflare or a WAF? Allowlist SiteTran's fetch IP, 54.87.171.204, so it gets your page instead of a challenge. Fix Cloudflare Bot Blocking for Translation SEO.
Works with your files

One set of translations for your whole product

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.

  • Bring existing work in

    Already have translations from an agency or old i18n files? Import CSV, JSON or XLIFF and launch with them instead of starting over.

  • Reuse approved translations anywhere

    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.

  • Formats your developers already use

    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

Troubleshooting

Translation not working on your site? Check these first

Open your browser's console. Most problems on custom sites show up there in the first few seconds.

1The language dropdown doesn't appear

  1. Check that all three lines are on the page, including <div id="sitetran_translate_element">. The console shows "SiteTran element not found" without it.
  2. Look for Content Security Policy errors and allow the hosts listed above.
  3. In SiteTran, make sure the language is both Active and Live.
  4. If Hide All Widgets On Site (dev mode) is on, add ?sitetran=on to your URL.
My Language Isn't Showing Up in the Language Dropdown →

2The dropdown vanishes after navigating

  1. The div is probably inside a component that unmounts. Move it to or your root layout, outside the app's mount point.
  2. If your app swaps the whole <body> on navigation (Turbo, htmx boost), test with the swap turned off.
Positioning the Language Dropdown on your Site →

3Some text is still in the original language

  1. SiteTran discovers text when it's shown, so open the routes, modals and states you need translated.
  2. A translation appears only after it's approved and published. Translation data can take up to 30 seconds to update, and browsers can keep it for up to 10 minutes.
  3. To translate new text automatically, turn on Machine Translation Auto Translate.
My New Translations or Changes Aren't Appearing →

4Names, codes or user content get translated

  1. Add translate="no" or the notranslate class in your components, or list selectors in sitetran.ignore_selectors.
  2. Use variables for names, dates and prices inside a sentence.
  3. For apps with sensitive data, turn off Public Phrase Discovery.
Skip HTML from Being Translated or Discovered →

5Hydration warnings in Next.js or Nuxt

  1. Load the widget after hydration, for example with strategy="afterInteractive" or lazyOnload in Next.js.
  2. Don't load v3.js in a way that runs before your framework hydrates the server HTML.
Frontend API - Widget Initialization Options →

6Language names look scrambled, or links break

  1. Scrambled characters usually mean the page is missing <meta charset="utf-8">.
  2. If #anchor links or hash routes stop working, set URL Type back from Use Hash to its default.
My Language Dropdown Has Scrambled Characters →
Pricing

Simple pricing, with every language included

Every plan includes unlimited languages and teammates. Start free and upgrade when your traffic grows.

Free

$0

For side projects and testing

  • 7K words in site
  • 3K translated pageviews / month
  • 7K machine translations

Starter

$3/mo

For sites with moderate translation needs

  • 30K words in site
  • 15K translated pageviews / month
  • 30K machine translations
Growing products

Pro

$35/mo

For larger sites, apps, or several smaller ones

  • 90K words in site
  • 45K translated pageviews / month
  • 90K machine translations

Business

$75/mo

For large-scale international products

  • 250K words in site
  • 125K translated pageviews / month
  • 250K machine translations
Annual billing: 2 months free · Enterprise plans for 1M+ words See all features
FAQ

Website translation questions

Can't find your answer? Send us your stack and we'll tell you exactly where the code goes.

Email support
How do I add SiteTran to a custom website?
Create a free SiteTran site and copy your Widget Code from the Dropdown Style page. Paste all three lines into the layout or HTML file every page shares, just before the closing body tag, and deploy once. Open your site, pick a language from the dropdown, and SiteTran starts discovering your phrases so you can translate them.
Does SiteTran work with React, Vue, Angular and other single-page apps?
Yes. SiteTran watches the page for changes, so content your app renders after load, including new routes, modals and API responses, is translated as it appears. Put the Widget Code in your HTML shell or root layout, outside the element your app mounts into, so the language dropdown is never removed by a re-render.
Do I need to extract strings or change my components?
No. You don't wrap text in translation functions, name keys or maintain translation files. SiteTran translates the text your site already renders. You only add attributes such as translate="no" where you want text left untranslated.
Where does the code go in a Next.js app?
Add it to the root layout, which stays mounted across client navigations. Set the 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.
What Content Security Policy does SiteTran need?
Allow 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'.
How big is the script, and will it slow my site down?
The widget script is about 104 KB minified and about 25 KB compressed, served from a CDN. Translations load as one data file per site that browsers reuse for up to 10 minutes. Visitors in your source language see your page exactly as rendered, and other languages are applied in place as soon as the data loads.
Can I build my own language switcher?
Yes. Call 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.
How do I keep user data, code and brand names untranslated?
Add the 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.
Can translated pages get their own URLs for SEO?
Yes. By default, every language uses your existing URLs and the visitor's choice is remembered. For indexable URLs such as /es/ or es.example.com, your edge worker, proxy or server calls SiteTran's Static Translation API, which returns translated HTML for the original page. Cache it by URL plus language, and set sitetran.static_url_language_code_lookup so the dropdown and hreflang tags point to those URLs.
Does it work on signed-in pages and dashboards?
Yes. The widget translates in each user's browser, so screens behind your login are translated like any other page. Keep personal data out with notranslate, and never cache signed-in or personalized HTML from the Static Translation API, because it could be served to other visitors.
Can I use my translations outside the website, like in our mobile app?
Yes. Under Settings, then Export, you can download your phrases and translations as CSV, JSON, TypeScript or XLIFF, for the languages and pages you choose, and use them in a mobile app, emails or other tools. You can also import CSV, JSON or XLIFF files if you already have translations.
How does SiteTran compare with an i18n library?
SiteTran gets websites and web apps live in new languages without refactoring a single component, with translators, reviewers and a Glossary built in and no deploy to publish a translation. If you also ship native mobile apps, export your SiteTran translations as XLIFF or JSON and reuse them in their string files.
Can I test on staging before visitors see anything?
Yes. You can add SiteTran to a staging or development environment. A language stays hidden from visitors until you set it to Live, and Hide All Widgets On Site (dev mode) under Settings, then Publishing, hides the dropdown until you add ?sitetran=on to the URL.
Which URL Type should a single-page app use?
The default. It keeps your routes and anchor links exactly as they are and remembers each visitor's language. Use Hash writes the language into the URL fragment, such as #/settings or #contact, so it's meant for sites that don't use hash routing or in-page anchors.
Is there a free plan?
Yes. The Free plan includes 7,000 words in your site, 3,000 translated pageviews per month and 7,000 machine translations, with unlimited languages and teammates. Paid plans raise those limits and start at $3 per month.
What happens if I go over my plan's limits?
You will be prompted to upgrade or buy additional credits. Your existing translations remain intact, and new translations are paused until you upgrade.

Ship your next language this week, not next quarter

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.

Start translating free Book a call Free plan · Unlimited languages and teammates

Using a website builder?

These platforms have their own pages with exact install steps and platform-specific tips. See all 29 integrations.