• FeaturesFeatures
  • The repoThe repo
  • ShowcaseShowcase
  • PricingPricing
  • FAQFAQ
  • BlogBlog
←All articlesAll articles

Multilingual Sanity Without a Week of Plumbing: Translate a Whole Site in One Click

Adding a language to a Sanity site usually means a week of plumbing and a copy-paste marathon. In The Content Architecture it is a config line, one script and one click in the Studio.

[Author]
Edoardo Lunardi
[Published]
27 Oct 2026
[Reading time]
16 min
"Can we add German?" usually arrives in the last week of a project, in the tone people use for small things. The client has the English site, they like it, and a second market is opening. From their side it is a content question: someone translates the pages, someone pastes them in.
From the Studio side it is an architecture question, and it reaches every layer of the build. Every URL needs a language. Every query needs to know which one it is reading. Search engines need to be told which page answers in which language, editors need to see which copy belongs where, and the interface copy the CMS never owned, from form errors to the 404 page, needs a dictionary. Only then does the translating start, and on a site of a hundred pages that is a project of its own.
The last changelog closed on a large multilingual site for a fintech, where the i18n layer was being tested before it shipped. It now ships in both editions of The Content Architecture: any number of languages from one Sanity dataset, and an editor who can translate a page, or the whole site, from the Studio in one click.

Why "add a language" is never a small ticket

I have wired multilingual Sanity sites by hand before, and the shape of the work barely changes from project to project. Sanity gives you the pieces: a document internationalization plugin, a pattern for fields with one value per language, and Agent Actions that can translate a document. It leaves you the decisions that connect those pieces to a frontend, and those decisions take the days.
Bar chart of conservative estimates in working days, by layer, to build multilingual Sanity by hand with AI coding tools: about 7 days in total, with the AI translation pipeline the largest at 2.
Wiring a multilingual Sanity build by hand: conservative estimates in working days for one senior developer working with AI coding tools.
These are conservative estimates for a senior developer working with AI coding tools. The basic plumbing (locale routing, hreflang, a translations plugin and a switcher) fits in about two days, which is the i18n share of the five-day setup on the homepage, and the rest of the chart is what it takes to hold up in production. The longest bar is the translation pipeline. Without it, translating is a person with two browser tabs, copying a field, pasting it into a translation tool, pasting the answer back, and doing it again for every heading, button label, SEO description and alt text on every page. On a hundred pages in four languages that is a few thousand pastes, and the first time the English changes, nobody can say which translations are now stale.
I wanted that bookkeeping gone. A model now translates well enough to start from. Tracking which page is translated, by whom, and from which version of the English is the slow part.

What editors see in the Studio

Every translated document lives in its language's own list. Pages, articles, legal pages, the blog index and the Site document are split into English and Italiano, each language flagged once in the config and shown everywhere after that. A Translations menu on every document jumps to its other languages, or creates the one that is missing.
Content editor showing the About page with the Translations menu open
Pages split by language, and the Translations menu on the English About page.
Underneath there are two kinds of content, and the starter treats them differently on purpose.
Pages use separate linked documents; categories use one document with translated values
Translated types get a linked document per language. Shared types keep one document with a value per language.
A page gets a document per language. The Italian About page has its own URL, its own SEO fields and its own publish state, so an Italian page can go live before the English one is finished, or the other way round. Shared things work the other way: a category, an author or an image exists once, and only the words inside it are kept per language. A reference to the Publishing category never has to know which language is reading it. The name answers in the right one.
Name fields show English “Publishing” and Italian “Pubblicazione”
A category name, one value per language.
Image text follows the same rule. Alt text, titles and descriptions on an asset carry one value per language, behind a language tab in the media library, and when automatic alt text is switched on, Sanity AI writes it for every upload in every language the site serves.
Italian language tab selected with alt text “Forme astratte ondulate sovrapposte”
The same asset, its alt text in Italian.

One click: Translate with AI

Every translated document carries a Translate with AI action, next to the usual Duplicate and Delete.
Page actions menu with “Translate with AI” selected
The document action, on every translated type and on categories.
It opens a dialog that starts from the page you are on: the English source, each other language with its URL and where it stands, and what happens after the translation, saved as a draft to review or published straight away. The translation runs through Sanity Agent Actions, Sanity's AI layer for structured content, inside your own Sanity organization. There is no separate translation service to sign up for, and no export and import round trip.
“Translate with AI” dialog awaiting confirmation to replace Italian changes
Translating one page. The Italian was edited by hand since the AI wrote it, so the dialog asks before replacing it.
The status under each language is the bookkeeping from earlier, done for you. Every AI translation records the English it was made from, as a fingerprint of the copy and of the layout. So the Studio can say whether a translation is up to date, whether the English changed since, whether someone edited the translation by hand, or whether it is still an untouched copy of the English. In the screenshot, someone has edited the Italian About page since the AI wrote it. Translating it again would throw those edits away, so the dialog says exactly that and waits for an explicit tick before it will.
A run can also take extra instructions ("this page is for developers, keep a technical tone") without touching the site-wide settings, and the dialog shows the AI requests made today against the daily limit before anything runs.

Build notes from The Content Architecture

The Content Architecture is a Sanity starter for Next.js and Astro, built on decisions like this one. The list covers the parts that take the longest to get right: i18n, the fetch layer, the agentic layer, the patterns that never make it into an estimate. New breakdowns land here first. Low volume, high signal.

What the AI is allowed to touch

A model that returns a translated document can return anything. It can rewrite a link, translate a URL, rename a person, drop a section, or paraphrase a product name into something legal would not sign off. Most of the engineering in this feature sits after the call to the model, in what happens to the answer.
AI translates copy, labels, SEO, categories, bios and new slugs; keeps other elements English
The model writes the words. Everything else is restored from the source.
The starter never writes the answer as it comes back. It merges it into the source, field by field. Copy comes from the answer. Everything else comes from the English, always: references, links and their targets, section anchors, images and video, colours, switches, numbers, and an existing translation's URL and password protection. Rich text is checked block by block. If a block comes back with a link missing or a shape the source did not have, the whole block keeps its English and the run reports it, rather than writing something half broken. A test walks the whole schema and fails when a new field that must be protected, a URL or a date, is not.
Names are the other half. People's names are never sent at all. Protected phrases, set by an editor in Settings, stay exactly as written wherever the copy uses them: the brand, the product, the feature names marketing has opinions about. A new translation takes its slug from its translated title, so About becomes /it/informazioni rather than /it/about, and a slug that would collide with an existing page or redirect is caught before it is written.

The whole site in one run

One page at a time is the right tool for a fix. A new language wants the whole site, and that lives in Settings, under Translations.
Translation interface showing all eight Italian pages translated
Translate the whole site: every page grouped by type, with its status, the run options and the request limit.
The panel lists every translatable page grouped by type, with the same statuses the dialog uses, and translates everything that needs it in one run, from the Site settings to pages, articles, legal pages, category names and authors. Pages someone wrote or edited by hand are never touched in bulk. One option also refreshes AI translations whose English changed since, which is how a site stays in step after launch without anyone keeping a spreadsheet.
The panel knows the size of a run before it starts. Reading the plan makes no AI request, so it shows how many requests a run will take before it takes any. A run stops at 25 AI requests by default, which you can raise for that run, a day stops at 100 by default, and a run of more than 50 requests asks you to type a word to confirm it. A run that saved drafts leaves a "Publish N AI drafts" button, so the review step has an end. And because translated pages link to each other, an Update links pass, which makes no AI request, points every link at the translation of its target once that translation is published.
How the AI writes is set once, right below it.
“How the AI writes” settings for style guide, protected phrases, and glossary
Style guide, protected phrases, glossary and notes per language, all plain Studio fields.
A style guide sets tone and formality for every translation. A glossary says how a term translates into each language. Notes per language cover what a style guide cannot, like which form of address to use. Protected phrases keep names as written. All of it is plain Studio fields, so the person who owns the voice of the site edits it, not a developer.

What it costs

Each page in each language is one AI request, billed by Sanity as one AI credit, whatever the page's length or number of sections. The cost grows with pages and languages, not with how much a page holds.
Translation list prices range from $1.25 to $200 by site size
One AI credit per page per language, at Sanity's list price of $0.05 as of October 2026. Free and Growth include 1,000 credits a month.
A 100-page site into four languages is 400 credits, around $20 at today's list price, and inside the 1,000 credits a month that Sanity's Free and Growth plans include. Re-translating a page after its English changes is one more request per language, and category names go fifty to a request. The panel shows that number before a run starts.

The plumbing you never see

Everything above is what an editor touches. Under it is the part that took the longest to get right, and the part a client never asks about until it breaks.
  • •
    One switch. The languages live in one list in the config. With one entry, i18n is off: no prefixes, no hreflang, no switcher. The code and the queries are the same either way, so a site can launch in English and add a language a year later without a rewrite.
  • •
    One URL per page. The default language owns no prefix, so /about is English and the Italian page sits under /it. /en/about answers a permanent redirect to /about, so search engines never index the same page twice. Each document stores its full public path, prefix included, so a translation's URL is authored, never assembled in a component.
  • •
    Every read knows its language. Every Sanity query binds the language it reads in, so an Italian page can never pull in an English section. The same binding carries the languages the site serves, so content in a language that was switched off is hidden from every page, sitemap and agent read from the first request after the deploy. Nothing has to be deleted first for the site to be correct.
  • •
    Search engines and agents. Every page declares its alternates with hreflang, x-default included, and the sitemap, the Open Graph locale, the JSON-LD and the RSS feed follow the page's language. llms.txt lists each language under its own heading, and the Markdown served to agents is written per translation.
  • •
    The copy nobody puts in the CMS. Form errors, "Read more", the 404 page and the language switcher come from a dictionary per language, so no English leaks into an Italian page. A visitor whose browser prefers another language is sent to that language's homepage, on the homepage only and never through a cache, so one visitor's preference can never become everyone's redirect.
The test suite runs in both modes, one language and several, so a change that only works with two languages, or only with one, fails before it ships.

Adding and removing a language

The starter ships with English and Italian. Italian is there so every path is exercised from day one, and the seed dataset holds a proofread Italian translation of every page. Most projects need a different set, so both directions are a short sequence.
Language lifecycle steps for adding and removing a language
Adding a language, and removing one.
Adding a language is a line in the config and a dictionary file for the interface copy. A script prepares the dataset: it checks that nothing already sits under the new prefix, then creates linked drafts of the Site, the homepage and the blog index in the new language. From there it is the Studio's job: translate the whole site, review the drafts, publish.
Removing one is a choice in the same trim that removes any other starter feature. It comments the language out of the config, keeping the line as the way back, and deletes its dictionary. The content can go in the same run or later. The site is correct either way, because every read already ignores a language the site no longer serves. When the content does go, the dataset is exported first, and the run suggests redirects for the URLs that disappear.

Where the time went

The feature landed in both editions last week, after a run on a real multilingual build. In one edition alone the change touched 417 files and added close to three hundred tests. Very little of that is the call that translates. Most of it is the edges: an editor and a bulk run touching the same page, a translation that exists only inside a release, an English page renamed after the Italian one was published, a language switched off with its content still in the dataset.
Nobody estimates for those, because nobody meets them until month three of a live multilingual site. Each one is now a status in the Studio, a refusal with a reason, or a test.
This is the kind of layer that rarely makes it into a quote and then eats the last days of a project. In The Content Architecture, it is already decided and already built: documents and URLs per language, the read guard, hreflang and the sitemap, the dictionaries, Translate with AI for a page or the whole site, and the scripts that add and remove a language. A client asking for German becomes an afternoon of review instead of a second project.
The Next.js edition and the Astro edition both ship it. The kit is available now, one payment, lifetime updates.

Common questions

How do I make a Sanity site multilingual?

Decide per type whether a document is translated as a whole or shared with one value per language. Pages and articles usually get one document per language, linked as translations, with the language in every URL. Shared things like categories and authors keep one document with a value per language. Then bind the language in every query, declare alternates with hreflang, and give each language a dictionary for the interface copy the CMS does not own.

Can Sanity translate content with AI?

Yes. Sanity Agent Actions include a translate action that drafts a document's translation into another language using your organization's AI credits. On its own it writes whatever the model returns, so a production setup should merge the answer into the source and keep links, references, URLs and assets from the original.

Will AI translation overwrite edits made by hand?

Not in this setup. Each AI translation records the source it was made from, so the Studio knows when a translation was edited by hand afterwards. Whole-site runs never touch those pages, and translating one again from the dialog requires an explicit confirmation that the edits will be replaced.

How much does it cost to translate a Sanity site with AI?

One AI credit per page per language. At Sanity's list price as of October 2026 that is $0.05 a page, so a 100-page site into four languages is about $20, inside the 1,000 credits a month included on the Free and Growth plans.

For engineers building on the stack

The Content Architecture is the Sanity foundation that ships with i18n and AI translation already wired, in a Next.js edition and an Astro edition. If you build on this stack, the list is where new patterns, deep dives and product updates land first. No filler, just the engineering.

One short email when the repo changes or a discount goes live.

Resources

  • BlogBlog
  • RoadmapRoadmap

Legal

  • Privacy PolicyPrivacy Policy
  • Terms Of ServiceTerms Of Service

© The Content Architecture

Built by edoardolunardi.dev
2026