Enterprise CMS Delivery

Headless CMS Localization: Patterns That Scale

How localization actually works in a headless CMS: field-level vs entry-level vs space-level translation, locale URL structure and hreflang, fallbacks, RTL, and keeping Nuxt i18n in sync with the CMS, with first-hand Storyblok and Sitecore notes.

Michael Graham
Michael Graham
July 12, 2026· 14 min read
A polished chrome globe wrapped in glowing purple circuitry branching into multiple language routes

The short answer

Headless CMS localization lives or dies on one early modeling decision: whether you translate at the field level, the entry level, or the space level, and how that lines up with your URL structure, hreflang, fallbacks, and front-end locale list. This guide covers each strategy and when it fits, how Storyblok, Contentful, and Sitecore each model languages, and how we keep Nuxt i18n in sync with the CMS on real builds.

Headless CMS localization is the practice of modeling, storing, and delivering the same content in multiple languages and regional variants through an API-first CMS, then rendering it with a front end that stays in sync with the CMS locales. The part that makes or breaks it is not the translation itself. It is the modeling decision you make before a single word is translated: whether you localize at the field level, the entry level, or the folder or space level. That one call shapes your URL structure, your fallbacks, and how your front-end framework stays in step. Get that modeling right and adding a language later is a config change. Get it wrong and you are re-migrating content under deadline while the marketing team waits.

We build server-side-rendered (SSR) front ends in Nuxt, on our own site and on client work, and we have shipped multilingual headless CMS builds on Storyblok with Nuxt i18n as well as multi-region Sitecore properties using language versions. This guide is what we have actually learned doing that, including the tradeoffs we would warn you about before you commit. If you are still deciding whether headless is the right shape at all, start with headless CMS vs traditional CMS and the broader enterprise headless CMS explainer, then come back here for the localization mechanics.

What is the difference between localization, i18n, and translation?

These three words get used interchangeably but they are not the same, which matters because different people own each one.

  • Internationalization (i18n) is the engineering work that makes multiple languages possible at all: locale-aware routing, date and number formatting, plural rules, text direction, and a content model that can hold more than one language. It happens once, up front, and it is mostly a developer job.
  • Localization (l10n) is adapting content and experience for a specific market. That is more than translation. It includes currency, imagery, legal copy, which products even exist in that region, and sometimes which pages exist at all.
  • Translation is the narrow act of converting text from one language to another. It is the visible part, and the part people wrongly assume is the whole job.

A headless CMS localization strategy has to serve all three. The i18n work sets the ceiling on what localization can do, and translation is the ongoing operational load that rides on top. When a project goes sideways, it is almost never because a translation was wrong. It is because the i18n model could not express what the market needed.

Field-level, entry-level, or space-level: which translation strategy fits?

This is the first real decision, and every headless CMS answers it differently. There are three broad models.

Field-level (field translation). Each field on an entry carries multiple language values. One "Article" entry holds title in English, German, and Japanese all at once. The entry, its ID, its relationships, and its structure stay shared across every locale. You translate the words, not the object.

Entry-level (entry translation). Each locale gets its own copy of the entry, linked back to a source. The English article and the German article are separate objects that know they are versions of each other. You can add, remove, or restructure fields per locale.

Space-level or folder-level. Each market gets its own space, project, or content folder, effectively a separate content repository, joined only by convention. Total isolation.

Here is how we think about the fit:

StrategyBest whenThe cost
Field-levelStructure is identical across locales, translators work in parallel, you want one editorial workflowHard to let a market diverge; the entry is coupled across all languages
Entry-levelMarkets need to diverge (different sections, different components, regional-only pages)More entries to manage, easy for locales to drift out of sync
Space/folder-levelMarkets are run by fully separate teams with separate governanceYou lose shared content and shared reporting; near-duplicate maintenance

Our default is field-level for genuinely parallel content, and entry-level the moment a market needs to say something the others do not. We reach for space-level rarely, only when the org chart demands it, because it trades the entire benefit of a shared CMS for autonomy a good permission model can usually provide without the duplication.

How do Storyblok, Contentful, and Sitecore each model languages?

The abstract choice above is constrained by what your platform supports. A short comparison of the three we work with most:

Storyblok supports both field-level and folder-level translation, and you pick per project. Field-level translation adds a language dimension to each translatable field so one story holds every locale. Folder-level gives each locale its own folder tree of stories. On a recent production Nuxt build we ran Storyblok field-level for the shared marketing content and used its dimension-based approach so the visual editor showed the right language in live preview. That combination, live preview per locale plus a clean component model, is the single biggest reason non-technical authors could work in a second language without a developer babysitting them.

Contentful is entry-level by design. Locales are defined at the space level and every localized field can carry a value per locale, but the delivery API returns one locale per request (or all of them with locale=*). It has a clean fallback-locale chain built in, which is one of its quieter strengths: you set de-AT to fall back to de-DE to fall back to en-US, and the API resolves it. It is the safe institutional choice for large multi-region teams for exactly this reason.

Sitecore models languages as language versions of an item. The item exists once in the content tree with a shared ID, and each language is a version of that item with its own field values and its own workflow and publishing state. This is closer to field-level in spirit but versioned, which is powerful on multi-region properties: you can have a page live in English, in draft in French, and not-yet-created in German, all as states of one item. On the multi-region and higher-education Sitecore work we deliver, language versions plus language fallback (configured per site) is what lets one information architecture serve many regions without forking the tree.

The takeaway: do not pick a translation strategy in the abstract and then discover your platform fights it. Storyblok and Sitecore lean shared-structure, Contentful leans per-locale, and each is easiest when you work with its grain.

Subpath or subdomain: how should locale URLs be structured?

Once content is modeled, the locale has to show up in the URL, and this is an SEO decision as much as an engineering one. Google reads the locale from the URL, so the structure you choose is the structure search engines index.

Three common patterns:

  • Subdirectory / subpath: example.com/de/, example.com/fr/. One domain, one pool of authority, simplest to run.
  • Subdomain: de.example.com. Cleaner separation, but each subdomain accrues authority somewhat independently.
  • Country-code top-level domain (ccTLD): example.de. Strongest geo-signal, highest operational and cost overhead, hardest to consolidate authority.

For most enterprise headless builds we recommend the subpath pattern. It keeps all your ranking authority on one domain, maps cleanly to how Nuxt i18n generates routes, and is the least expensive to operate. Reach for ccTLDs only when the business genuinely runs separate legal entities per country or needs the strongest local signal, and accept that you are then running several sites that share a brand.

Whatever you choose, hreflang is not optional. Every localized page must declare its alternates so search engines pair the right version with the right searcher. The rules that actually trip teams up:

  • hreflang must be reciprocal. If the English page points to the German page, the German page must point back. One-directional hreflang is ignored.
  • Include a self-referential tag: each page lists itself among its alternates.
  • Add an x-default for the fallback when no language matches the user.
  • Use correct language and region codes (en, en-GB, de-CH), not made-up ones.

We raise this in a modeling article because hreflang is only easy if your content model knows which entries are translations of each other. Field-level and language-version models give you that pairing for free because it is the same object. Entry-level and space-level models make you maintain the mapping yourself, and a stale mapping is the most common cause of broken hreflang we see.

How do you keep Nuxt i18n in sync with the CMS?

This is the seam where headless localization most often quietly breaks: the CMS has one set of locales and the front-end framework has another, and they drift.

The pattern we use with Nuxt i18n is to treat the CMS as the source of truth for content locales and let the framework own routing and UI-string locales, then keep the two lists identical by construction. Concretely:

  1. Define the locale list once and share it. The same en, de, fr codes configure Nuxt i18n's locales and drive the CMS query. If the CMS uses de-DE and Nuxt uses de, normalize at the boundary so there is exactly one mapping, written down.
  2. Let Nuxt i18n generate the localized routes (strategy: 'prefix_except_default' gives you the subpath structure above), and pass the active locale into every CMS request so the API returns the matching language.
  3. Render hreflang from the framework, not by hand. Nuxt i18n emits the alternate links from its locale config, so if the locale list is the single shared source, hreflang stays correct automatically.
  4. Keep interface strings (button labels, error messages, nav) in the framework's message files, and keep authored content in the CMS. Do not try to translate UI chrome in the CMS or author body content in message files. Mixing the two is how you end up with half a page translated.

A short shape of the config seam:

// one shared locale list feeds both the framework and the CMS query
export const LOCALES = ['en', 'de', 'fr'] as const

// nuxt.config: i18n owns routing + hreflang
i18n: {
  locales: [...LOCALES],
  defaultLocale: 'en',
  strategy: 'prefix_except_default',
}

// data layer: the active locale drives the CMS request
const { locale } = useI18n()
const story = await fetchStory(slug, { language: locale.value })

The rule underneath all of it: one locale list, one direction of truth, normalized once at the boundary. Every multilingual bug we have chased in a Nuxt plus headless build came down to two locale lists that disagreed.

What should the fallback locale do?

Fallbacks decide what a visitor sees when a page has not been translated yet, and the default behavior is often wrong for the business.

There are two honest options and one bad one. The bad one is showing an empty page or a broken layout because a field came back null. The two good ones:

  • Fall back to a base locale. Show the English content where the German translation does not exist yet. Good for launch, because you can go live in a new market before every page is translated, and the untranslated pages degrade to a language the visitor can at least read.
  • Hide untranslated content. Do not surface a page that has no translation in this locale. Good for markets where mixed-language pages are unacceptable (some regulated and legal contexts) or where a half-translated experience damages trust more than a missing page.

Contentful and Sitecore both give you configurable fallback chains, so the choice is a setting, not a build. The mistake we warn against is leaving fallback undefined and letting individual null fields leak through, which produces the worst of both: a page that is mostly translated with random English strings in the middle. Decide the fallback policy per content type, not globally, because a marketing landing page and a legal disclosure want opposite behavior.

What about RTL and locale-specific formatting?

Right-to-left (RTL) languages (Arabic, Hebrew, Farsi, Urdu) are where a localization strategy that only thought about words falls apart. RTL is a layout concern, not a content concern, so the CMS rarely helps you here. The front end has to.

The parts that actually need handling:

  • Direction as a first-class state. Set dir="rtl" on the document for RTL locales and build your CSS with logical properties (margin-inline-start, not margin-left) so layout mirrors correctly instead of being hand-flipped per component.
  • Bidirectional content. An Arabic page with an English product name or a Latin-script URL needs correct bidi handling or the punctuation lands in the wrong place. This is invisible until a native reader sees it.
  • Locale-specific formatting. Dates, numbers, and currency are not translations. They are formatting. They belong to the runtime (Intl.DateTimeFormat, Intl.NumberFormat) driven by the active locale, not to hand-authored strings in the CMS. Authoring "1,000.00" as text means it is wrong in every locale that uses "1.000,00".

Our rule: anything a computer can format from a locale code should be formatted at render time, and only genuine prose should live in the CMS as translatable text. That keeps the translation surface small and stops formatting bugs from becoming content-entry chores.

Who owns the translation workflow?

The modeling can be perfect and the project can still stall on process, because translation is an ongoing operation with real handoffs. The questions to answer before launch, not after:

  • Who authors the source, who translates, and who approves? A field-level model with parallel translators needs a workflow where the source can be locked while translation happens. A language-version model like Sitecore's handles this natively with per-version workflow states.
  • Machine translation, human translation, or hybrid? Most enterprise teams land on machine-translation-then-human-review. The CMS should support pushing content to a translation service and pulling it back into the right locale field, so translators are not copy-pasting.
  • How do you know what is stale? When the English source changes, every translated version is now out of date and someone has to know. Platforms with an explicit translation status per locale make this visible. Field-level models without status tracking make it invisible, which is a real downside to weigh.

Enterprise CMS localization work is lumpy by nature: a big push when you launch a market or a new site, then a long quiet maintenance tail, then another spike when a campaign or a redesign lands. That shape is exactly why our support hours never expire. You bring in specialists for the migration and the market launch, then scale down to the maintenance load without paying for peak capacity through the quiet months. If localization is a project you are staffing now, our headless CMS agency practice and our Sitecore development and support work both cover it end to end, and a short call is the fastest way to pressure-test your model.

The one thing to get right first

If you take a single decision away from this: choose your translation strategy (field-level, entry-level, or space-level) to match your platform's native model and your markets' real need to diverge, then make your URL structure, hreflang, and front-end locale list all derive from that one decision. Everything downstream (fallbacks, RTL, formatting, workflow) gets easier when the model is right and harder when it is not. Localization rarely fails at translation. It fails at modeling, and modeling is the part you can still fix cheaply today.

Frequently asked questions

What is the difference between field-level and entry-level translation in a headless CMS?

Field-level translation stores every language on one shared entry, so you translate the words but keep one object and structure. Entry-level translation gives each locale its own linked copy, so markets can diverge and restructure. Field-level suits parallel content; entry-level suits markets that need to say different things.

Should localized URLs use a subpath or a subdomain?

For most enterprise headless builds a subpath like example.com/de keeps all ranking authority on one domain, maps cleanly to Nuxt i18n routes, and costs the least to run. Choose subdomains or country-code domains only when you run separate legal entities per country or need the strongest geo signal.

How do you keep Nuxt i18n in sync with the CMS locales?

Define the locale list once and share it. The same codes configure Nuxt i18n routing and hreflang and drive the CMS query, normalized once at the boundary if the CMS uses region codes. Keep UI strings in the framework and authored content in the CMS. One locale list, one direction of truth.

How do Storyblok, Contentful, and Sitecore model languages differently?

Storyblok supports field-level or folder-level translation with per-locale live preview. Contentful is entry-level with a built-in fallback-locale chain. Sitecore models languages as versions of one item, so a page can be live in one language and draft in another. Match your translation strategy to the platform's native model.

What should the fallback locale do when a page is not translated yet?

Pick one policy per content type. Fall back to a base language so you can launch a market before every page is translated, or hide untranslated pages where mixed-language content is unacceptable. The failure mode to avoid is leaving fallback undefined, which leaks random untranslated fields into an otherwise translated page.

headless cmslocalizationi18nstorybloksitecorenuxt
Michael Graham
Michael Graham

Founder & Software Engineer

Obsessed with building top-tier web software and crafting unique, polished user experiences.