[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-site-config":3,"$fvLKN1qhibplzzzVIz8AXiwezeMl8x41a7UMEWrJuli0":6,"mdc--pvjrfr-key":121},{"gaMeasurementId":4,"bookingUrl":5},"G-7BYTDCVDDR","https:\u002F\u002Ftidycal.com\u002Fcmdcntr\u002F30-minute-meeting",{"post":7,"related":76},{"id":8,"slug":9,"title":10,"description":11,"summary":12,"content":13,"coverImageUrl":14,"coverImageAlt":15,"tags":16,"targetKeywords":23,"faq":28,"howTo":44,"demo":44,"featured":45,"tool":44,"status":46,"reviewNote":44,"metaTitle":10,"metaDescription":47,"canonicalUrl":48,"sourceTicketId":44,"agentGenerated":45,"publishedAt":49,"publishedBy":44,"createdAt":50,"updatedAt":50,"authorId":51,"clusterId":52,"ctaId":53,"author":54,"cluster":65,"cta":69,"tools":75},"69f3e275-8bdb-40dc-ac93-b0e87af05d2f","headless-cms-localization","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.","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.\n\nWe 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](\u002Fblog\u002Fenterprise-cms\u002Fheadless-cms-vs-traditional-cms) and the broader [enterprise headless CMS explainer](\u002Fblog\u002Fenterprise-cms\u002Fenterprise-headless-cms-explained), then come back here for the localization mechanics.\n\n## What is the difference between localization, i18n, and translation?\n\nThese three words get used interchangeably but they are not the same, which matters because different people own each one.\n\n- **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.\n- **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.\n- **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.\n\nA 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.\n\n## Field-level, entry-level, or space-level: which translation strategy fits?\n\nThis is the first real decision, and every headless CMS answers it differently. There are three broad models.\n\n![Field-level translation stores every language on one shared entry, while entry-level translation gives each locale its own linked copy that can diverge](\u002Fcovers\u002Fenterprise-cms\u002Fvisuals\u002Flocalization-translation-models.png)\n\n**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.\n\n**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.\n\n**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.\n\nHere is how we think about the fit:\n\n| Strategy | Best when | The cost |\n|---|---|---|\n| Field-level | Structure is identical across locales, translators work in parallel, you want one editorial workflow | Hard to let a market diverge; the entry is coupled across all languages |\n| Entry-level | Markets need to diverge (different sections, different components, regional-only pages) | More entries to manage, easy for locales to drift out of sync |\n| Space\u002Ffolder-level | Markets are run by fully separate teams with separate governance | You lose shared content and shared reporting; near-duplicate maintenance |\n\nOur 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.\n\n## How do Storyblok, Contentful, and Sitecore each model languages?\n\nThe abstract choice above is constrained by what your platform supports. A short comparison of the three we work with most:\n\n**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.\n\n**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.\n\n**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.\n\nThe 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.\n\n## Subpath or subdomain: how should locale URLs be structured?\n\nOnce 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.\n\nThree common patterns:\n\n- **Subdirectory \u002F subpath**: `example.com\u002Fde\u002F`, `example.com\u002Ffr\u002F`. One domain, one pool of authority, simplest to run.\n- **Subdomain**: `de.example.com`. Cleaner separation, but each subdomain accrues authority somewhat independently.\n- **Country-code top-level domain (ccTLD)**: `example.de`. Strongest geo-signal, highest operational and cost overhead, hardest to consolidate authority.\n\nFor 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.\n\nWhatever 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:\n\n- hreflang must be **reciprocal**. If the English page points to the German page, the German page must point back. One-directional hreflang is ignored.\n- Include a **self-referential** tag: each page lists itself among its alternates.\n- Add an **`x-default`** for the fallback when no language matches the user.\n- Use correct language and region codes (`en`, `en-GB`, `de-CH`), not made-up ones.\n\nWe 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.\n\n## How do you keep Nuxt i18n in sync with the CMS?\n\nThis 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.\n\nThe 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:\n\n1. 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.\n2. 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.\n3. 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.\n4. 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.\n\nA short shape of the config seam:\n\n```ts\n\u002F\u002F one shared locale list feeds both the framework and the CMS query\nexport const LOCALES = ['en', 'de', 'fr'] as const\n\n\u002F\u002F nuxt.config: i18n owns routing + hreflang\ni18n: {\n  locales: [...LOCALES],\n  defaultLocale: 'en',\n  strategy: 'prefix_except_default',\n}\n\n\u002F\u002F data layer: the active locale drives the CMS request\nconst { locale } = useI18n()\nconst story = await fetchStory(slug, { language: locale.value })\n```\n\nThe 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.\n\n## What should the fallback locale do?\n\nFallbacks decide what a visitor sees when a page has not been translated yet, and the default behavior is often wrong for the business.\n\n![A CMS editor with locale tabs showing translated headline and subhead fields, a CTA label falling back to English, and a missing legal footnote translation](\u002Fcovers\u002Fenterprise-cms\u002Fvisuals\u002Flocalization-cms-editor.png)\n\nThere 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:\n\n- **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.\n- **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.\n\nContentful 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.\n\n## What about RTL and locale-specific formatting?\n\nRight-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.\n\nThe parts that actually need handling:\n\n- **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.\n- **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.\n- **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\".\n\nOur 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.\n\n## Who owns the translation workflow?\n\nThe 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:\n\n- **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.\n- **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.\n- **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.\n\nEnterprise 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](\u002Fservices\u002Fheadless-cms) practice and our [Sitecore development and support](\u002Fservices\u002Fsitecore) work both cover it end to end, and a short [call](\u002Fservices\u002Fheadless-cms) is the fastest way to pressure-test your model.\n\n## The one thing to get right first\n\nIf 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.\n\n## Read next\n\n- [Enterprise headless CMS, explained](\u002Fblog\u002Fenterprise-cms\u002Fenterprise-headless-cms-explained): what \"enterprise headless\" actually requires, localization included.\n- [Headless CMS vs traditional CMS](\u002Fblog\u002Fenterprise-cms\u002Fheadless-cms-vs-traditional-cms): the tradeoff that decides how much localization work lands on your team.\n- [Headless CMS SEO: the complete guide](\u002Fblog\u002Fenterprise-cms\u002Fheadless-cms-seo-complete-guide): hreflang, rendering, and indexing in more depth.\n- [Headless CMS security](\u002Fblog\u002Fenterprise-cms\u002Fheadless-cms-security): the other cross-cutting concern in a multi-region build.\n- Pillar: [Enterprise CMS in 2026: how to choose the platform and the partner](\u002Fblog\u002Fenterprise-cms\u002Fenterprise-cms-2026-platform-and-partner).\n- Work with us: our [headless CMS agency](\u002Fservices\u002Fheadless-cms) practice, or [Sitecore development and support](\u002Fservices\u002Fsitecore) for language-version builds.","https:\u002F\u002Fcmdcntr.io\u002Fcovers\u002Fenterprise-cms\u002Fheadless-localization.png","A polished chrome globe wrapped in glowing purple circuitry branching into multiple language routes",[17,18,19,20,21,22],"headless cms","localization","i18n","storyblok","sitecore","nuxt",[24,25,26,27],"headless cms localization","headless cms i18n","multilingual headless cms","headless cms internationalization",[29,32,35,38,41],{"answer":30,"question":31},"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.","What is the difference between field-level and entry-level translation in a headless CMS?",{"answer":33,"question":34},"For most enterprise headless builds a subpath like example.com\u002Fde 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.","Should localized URLs use a subpath or a subdomain?",{"answer":36,"question":37},"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 you keep Nuxt i18n in sync with the CMS locales?",{"answer":39,"question":40},"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.","How do Storyblok, Contentful, and Sitecore model languages differently?",{"answer":42,"question":43},"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.","What should the fallback locale do when a page is not translated yet?",null,false,"published","Field-level vs entry-level translation, locale URLs, hreflang, fallbacks, RTL, and syncing Nuxt i18n with the CMS, from real Storyblok and Sitecore builds.","https:\u002F\u002Fcmdcntr.io\u002Fblog\u002Fenterprise-cms\u002Fheadless-cms-localization","2026-07-12T14:00:00.000Z","2026-07-14T23:50:41.560Z","ed80da88-f3d1-4aa9-9373-18c39fecb740","23d49753-baf2-4629-b6fc-df27c558f48d","c0fcf6a1-58d2-4262-8996-3ad29d58eea5",{"slug":55,"name":56,"avatarUrl":57,"role":58,"bio":59,"isPublic":60,"expertiseSummary":61,"linkedin":62,"github":63,"twitter":44,"website":64},"michael-graham","Michael Graham","\u002Fapi\u002Fpublic\u002Favatar\u002F7452d3cc-b0aa-46e4-96e6-da9c0225c471","Founder & Software Engineer","Obsessed with building top-tier web software and crafting unique, polished user experiences.",true,"From frontend design to a fully deployed application, I can build the entire pipeline.","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fmikemartire\u002F","https:\u002F\u002Fgithub.com\u002Fmichaelchristophergraham","https:\u002F\u002Fcmdcntr.io",{"slug":66,"name":67,"color":68},"enterprise-cms","Enterprise CMS Delivery","#0EA5E9",{"id":53,"name":70,"ctaType":71,"heading":72,"description":73,"buttonText":74,"url":5,"leadMagnetUrl":44},"Book a Call (blog)","book_call","Shipping AI-generated code that keeps breaking?","Book a free 30-minute call with a senior engineer. We diagnose what is going wrong and give you a concrete fix plan, no obligation.","Book a free call",[],[77,92,107],{"id":78,"slug":79,"title":80,"description":81,"coverImageUrl":82,"coverImageAlt":83,"tags":84,"featured":45,"tool":44,"publishedAt":89,"author":90,"cluster":91},"bf3284f2-bdb2-4d08-b24b-870960743091","headless-cms-seo-complete-guide","Headless CMS SEO: The Complete Guide","The SEO concerns that genuinely change in a headless architecture, and the server-rendered Nuxt patterns we use to get metadata, canonicals, redirects, structured data, sitemaps, and Core Web Vitals right.","https:\u002F\u002Fcmdcntr.io\u002Fcovers\u002Fenterprise-cms\u002Fheadless-seo.png","Chrome and electric-violet diagram of a headless CMS feeding content through an API into a server-rendered front end, with SEO tags and structured data in the HTML response.",[17,85,22,86,87,88],"seo","core web vitals","structured data","ssr","2026-07-14T14:00:00.000Z",{"slug":55,"name":56,"avatarUrl":57,"role":58,"bio":59,"isPublic":60},{"slug":66,"name":67,"color":68},{"id":93,"slug":94,"title":95,"description":96,"coverImageUrl":97,"coverImageAlt":98,"tags":99,"featured":45,"tool":44,"publishedAt":104,"author":105,"cluster":106},"eb994797-29e0-4ad6-8aaf-01f353cd8410","headless-cms-security","Headless CMS Security: The Real Attack Surface","A headless CMS is secure when you treat it as one, but decoupling moves the attack surface to the seams: API tokens, preview endpoints, webhooks, edge caching, rich-text XSS, RBAC, and front-end secrets. Here is the real surface and a hardening checklist.","https:\u002F\u002Fcmdcntr.io\u002Fcovers\u002Fenterprise-cms\u002Fheadless-security.png","A polished chrome content block splitting apart with glowing electric-violet API connections and a lock symbol at each exposed seam",[17,100,101,102,103],"cms security","api security","web security","enterprise cms","2026-07-11T14:00:00.000Z",{"slug":55,"name":56,"avatarUrl":57,"role":58,"bio":59,"isPublic":60},{"slug":66,"name":67,"color":68},{"id":108,"slug":109,"title":110,"description":111,"coverImageUrl":112,"coverImageAlt":113,"tags":114,"featured":45,"tool":44,"publishedAt":118,"author":119,"cluster":120},"a5765020-18cd-4000-b24f-4df388d7eb03","headless-cms-vs-traditional-cms","Headless CMS vs Traditional CMS: How to Actually Choose","A decision framework for headless CMS vs traditional CMS: the honest tradeoffs, a situation-keyed decision table, and the real Sanity vs Storyblok vs Strapi call we made on a production Nuxt build.","https:\u002F\u002Fcmdcntr.io\u002Fcovers\u002Fenterprise-cms\u002Fheadless-vs-traditional.png","A polished chrome CMS block splitting into two halves, one rendering its own page and one streaming content through a glowing electric-violet API channel to a separate front end",[17,115,116,21,20,117],"traditional cms","dxp","cms selection","2026-07-10T14:00:00.000Z",{"slug":55,"name":56,"avatarUrl":57,"role":58,"bio":59,"isPublic":60},{"slug":66,"name":67,"color":68},{"data":122,"body":123},{},{"type":124,"children":125},"root",[126,134,156,163,168,204,209,215,220,229,248,258,268,273,360,365,371,376,386,427,444,449,455,460,465,528,540,552,622,627,633,638,657,732,737,1129,1134,1140,1145,1153,1158,1181,1186,1192,1197,1202,1274,1279,1285,1290,1323,1351,1357,1362,1368,1442],{"type":127,"tag":128,"props":129,"children":130},"element","p",{},[131],{"type":132,"value":133},"text","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.",{"type":127,"tag":128,"props":135,"children":136},{},[137,139,146,148,154],{"type":132,"value":138},"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 ",{"type":127,"tag":140,"props":141,"children":143},"a",{"href":142},"\u002Fblog\u002Fenterprise-cms\u002Fheadless-cms-vs-traditional-cms",[144],{"type":132,"value":145},"headless CMS vs traditional CMS",{"type":132,"value":147}," and the broader ",{"type":127,"tag":140,"props":149,"children":151},{"href":150},"\u002Fblog\u002Fenterprise-cms\u002Fenterprise-headless-cms-explained",[152],{"type":132,"value":153},"enterprise headless CMS explainer",{"type":132,"value":155},", then come back here for the localization mechanics.",{"type":127,"tag":157,"props":158,"children":160},"h2",{"id":159},"what-is-the-difference-between-localization-i18n-and-translation",[161],{"type":132,"value":162},"What is the difference between localization, i18n, and translation?",{"type":127,"tag":128,"props":164,"children":165},{},[166],{"type":132,"value":167},"These three words get used interchangeably but they are not the same, which matters because different people own each one.",{"type":127,"tag":169,"props":170,"children":171},"ul",{},[172,184,194],{"type":127,"tag":173,"props":174,"children":175},"li",{},[176,182],{"type":127,"tag":177,"props":178,"children":179},"strong",{},[180],{"type":132,"value":181},"Internationalization (i18n)",{"type":132,"value":183}," 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.",{"type":127,"tag":173,"props":185,"children":186},{},[187,192],{"type":127,"tag":177,"props":188,"children":189},{},[190],{"type":132,"value":191},"Localization (l10n)",{"type":132,"value":193}," 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.",{"type":127,"tag":173,"props":195,"children":196},{},[197,202],{"type":127,"tag":177,"props":198,"children":199},{},[200],{"type":132,"value":201},"Translation",{"type":132,"value":203}," 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.",{"type":127,"tag":128,"props":205,"children":206},{},[207],{"type":132,"value":208},"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.",{"type":127,"tag":157,"props":210,"children":212},{"id":211},"field-level-entry-level-or-space-level-which-translation-strategy-fits",[213],{"type":132,"value":214},"Field-level, entry-level, or space-level: which translation strategy fits?",{"type":127,"tag":128,"props":216,"children":217},{},[218],{"type":132,"value":219},"This is the first real decision, and every headless CMS answers it differently. There are three broad models.",{"type":127,"tag":128,"props":221,"children":222},{},[223],{"type":127,"tag":224,"props":225,"children":228},"img",{"alt":226,"src":227},"Field-level translation stores every language on one shared entry, while entry-level translation gives each locale its own linked copy that can diverge","\u002Fcovers\u002Fenterprise-cms\u002Fvisuals\u002Flocalization-translation-models.png",[],{"type":127,"tag":128,"props":230,"children":231},{},[232,237,239,246],{"type":127,"tag":177,"props":233,"children":234},{},[235],{"type":132,"value":236},"Field-level (field translation).",{"type":132,"value":238}," Each field on an entry carries multiple language values. One \"Article\" entry holds ",{"type":127,"tag":240,"props":241,"children":243},"code",{"className":242},[],[244],{"type":132,"value":245},"title",{"type":132,"value":247}," 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.",{"type":127,"tag":128,"props":249,"children":250},{},[251,256],{"type":127,"tag":177,"props":252,"children":253},{},[254],{"type":132,"value":255},"Entry-level (entry translation).",{"type":132,"value":257}," 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.",{"type":127,"tag":128,"props":259,"children":260},{},[261,266],{"type":127,"tag":177,"props":262,"children":263},{},[264],{"type":132,"value":265},"Space-level or folder-level.",{"type":132,"value":267}," Each market gets its own space, project, or content folder, effectively a separate content repository, joined only by convention. Total isolation.",{"type":127,"tag":128,"props":269,"children":270},{},[271],{"type":132,"value":272},"Here is how we think about the fit:",{"type":127,"tag":274,"props":275,"children":276},"table",{},[277,301],{"type":127,"tag":278,"props":279,"children":280},"thead",{},[281],{"type":127,"tag":282,"props":283,"children":284},"tr",{},[285,291,296],{"type":127,"tag":286,"props":287,"children":288},"th",{},[289],{"type":132,"value":290},"Strategy",{"type":127,"tag":286,"props":292,"children":293},{},[294],{"type":132,"value":295},"Best when",{"type":127,"tag":286,"props":297,"children":298},{},[299],{"type":132,"value":300},"The cost",{"type":127,"tag":302,"props":303,"children":304},"tbody",{},[305,324,342],{"type":127,"tag":282,"props":306,"children":307},{},[308,314,319],{"type":127,"tag":309,"props":310,"children":311},"td",{},[312],{"type":132,"value":313},"Field-level",{"type":127,"tag":309,"props":315,"children":316},{},[317],{"type":132,"value":318},"Structure is identical across locales, translators work in parallel, you want one editorial workflow",{"type":127,"tag":309,"props":320,"children":321},{},[322],{"type":132,"value":323},"Hard to let a market diverge; the entry is coupled across all languages",{"type":127,"tag":282,"props":325,"children":326},{},[327,332,337],{"type":127,"tag":309,"props":328,"children":329},{},[330],{"type":132,"value":331},"Entry-level",{"type":127,"tag":309,"props":333,"children":334},{},[335],{"type":132,"value":336},"Markets need to diverge (different sections, different components, regional-only pages)",{"type":127,"tag":309,"props":338,"children":339},{},[340],{"type":132,"value":341},"More entries to manage, easy for locales to drift out of sync",{"type":127,"tag":282,"props":343,"children":344},{},[345,350,355],{"type":127,"tag":309,"props":346,"children":347},{},[348],{"type":132,"value":349},"Space\u002Ffolder-level",{"type":127,"tag":309,"props":351,"children":352},{},[353],{"type":132,"value":354},"Markets are run by fully separate teams with separate governance",{"type":127,"tag":309,"props":356,"children":357},{},[358],{"type":132,"value":359},"You lose shared content and shared reporting; near-duplicate maintenance",{"type":127,"tag":128,"props":361,"children":362},{},[363],{"type":132,"value":364},"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.",{"type":127,"tag":157,"props":366,"children":368},{"id":367},"how-do-storyblok-contentful-and-sitecore-each-model-languages",[369],{"type":132,"value":370},"How do Storyblok, Contentful, and Sitecore each model languages?",{"type":127,"tag":128,"props":372,"children":373},{},[374],{"type":132,"value":375},"The abstract choice above is constrained by what your platform supports. A short comparison of the three we work with most:",{"type":127,"tag":128,"props":377,"children":378},{},[379,384],{"type":127,"tag":177,"props":380,"children":381},{},[382],{"type":132,"value":383},"Storyblok",{"type":132,"value":385}," 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.",{"type":127,"tag":128,"props":387,"children":388},{},[389,394,396,402,404,410,412,418,419,425],{"type":127,"tag":177,"props":390,"children":391},{},[392],{"type":132,"value":393},"Contentful",{"type":132,"value":395}," 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 ",{"type":127,"tag":240,"props":397,"children":399},{"className":398},[],[400],{"type":132,"value":401},"locale=*",{"type":132,"value":403},"). It has a clean fallback-locale chain built in, which is one of its quieter strengths: you set ",{"type":127,"tag":240,"props":405,"children":407},{"className":406},[],[408],{"type":132,"value":409},"de-AT",{"type":132,"value":411}," to fall back to ",{"type":127,"tag":240,"props":413,"children":415},{"className":414},[],[416],{"type":132,"value":417},"de-DE",{"type":132,"value":411},{"type":127,"tag":240,"props":420,"children":422},{"className":421},[],[423],{"type":132,"value":424},"en-US",{"type":132,"value":426},", and the API resolves it. It is the safe institutional choice for large multi-region teams for exactly this reason.",{"type":127,"tag":128,"props":428,"children":429},{},[430,435,437,442],{"type":127,"tag":177,"props":431,"children":432},{},[433],{"type":132,"value":434},"Sitecore",{"type":132,"value":436}," models languages as ",{"type":127,"tag":177,"props":438,"children":439},{},[440],{"type":132,"value":441},"language versions",{"type":132,"value":443}," 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.",{"type":127,"tag":128,"props":445,"children":446},{},[447],{"type":132,"value":448},"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.",{"type":127,"tag":157,"props":450,"children":452},{"id":451},"subpath-or-subdomain-how-should-locale-urls-be-structured",[453],{"type":132,"value":454},"Subpath or subdomain: how should locale URLs be structured?",{"type":127,"tag":128,"props":456,"children":457},{},[458],{"type":132,"value":459},"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.",{"type":127,"tag":128,"props":461,"children":462},{},[463],{"type":132,"value":464},"Three common patterns:",{"type":127,"tag":169,"props":466,"children":467},{},[468,494,511],{"type":127,"tag":173,"props":469,"children":470},{},[471,476,478,484,486,492],{"type":127,"tag":177,"props":472,"children":473},{},[474],{"type":132,"value":475},"Subdirectory \u002F subpath",{"type":132,"value":477},": ",{"type":127,"tag":240,"props":479,"children":481},{"className":480},[],[482],{"type":132,"value":483},"example.com\u002Fde\u002F",{"type":132,"value":485},", ",{"type":127,"tag":240,"props":487,"children":489},{"className":488},[],[490],{"type":132,"value":491},"example.com\u002Ffr\u002F",{"type":132,"value":493},". One domain, one pool of authority, simplest to run.",{"type":127,"tag":173,"props":495,"children":496},{},[497,502,503,509],{"type":127,"tag":177,"props":498,"children":499},{},[500],{"type":132,"value":501},"Subdomain",{"type":132,"value":477},{"type":127,"tag":240,"props":504,"children":506},{"className":505},[],[507],{"type":132,"value":508},"de.example.com",{"type":132,"value":510},". Cleaner separation, but each subdomain accrues authority somewhat independently.",{"type":127,"tag":173,"props":512,"children":513},{},[514,519,520,526],{"type":127,"tag":177,"props":515,"children":516},{},[517],{"type":132,"value":518},"Country-code top-level domain (ccTLD)",{"type":132,"value":477},{"type":127,"tag":240,"props":521,"children":523},{"className":522},[],[524],{"type":132,"value":525},"example.de",{"type":132,"value":527},". Strongest geo-signal, highest operational and cost overhead, hardest to consolidate authority.",{"type":127,"tag":128,"props":529,"children":530},{},[531,533,538],{"type":132,"value":532},"For most enterprise headless builds we recommend the ",{"type":127,"tag":177,"props":534,"children":535},{},[536],{"type":132,"value":537},"subpath",{"type":132,"value":539}," 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.",{"type":127,"tag":128,"props":541,"children":542},{},[543,545,550],{"type":132,"value":544},"Whatever you choose, ",{"type":127,"tag":177,"props":546,"children":547},{},[548],{"type":132,"value":549},"hreflang is not optional.",{"type":132,"value":551}," 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:",{"type":127,"tag":169,"props":553,"children":554},{},[555,567,579,595],{"type":127,"tag":173,"props":556,"children":557},{},[558,560,565],{"type":132,"value":559},"hreflang must be ",{"type":127,"tag":177,"props":561,"children":562},{},[563],{"type":132,"value":564},"reciprocal",{"type":132,"value":566},". If the English page points to the German page, the German page must point back. One-directional hreflang is ignored.",{"type":127,"tag":173,"props":568,"children":569},{},[570,572,577],{"type":132,"value":571},"Include a ",{"type":127,"tag":177,"props":573,"children":574},{},[575],{"type":132,"value":576},"self-referential",{"type":132,"value":578}," tag: each page lists itself among its alternates.",{"type":127,"tag":173,"props":580,"children":581},{},[582,584,593],{"type":132,"value":583},"Add an ",{"type":127,"tag":177,"props":585,"children":586},{},[587],{"type":127,"tag":240,"props":588,"children":590},{"className":589},[],[591],{"type":132,"value":592},"x-default",{"type":132,"value":594}," for the fallback when no language matches the user.",{"type":127,"tag":173,"props":596,"children":597},{},[598,600,606,607,613,614,620],{"type":132,"value":599},"Use correct language and region codes (",{"type":127,"tag":240,"props":601,"children":603},{"className":602},[],[604],{"type":132,"value":605},"en",{"type":132,"value":485},{"type":127,"tag":240,"props":608,"children":610},{"className":609},[],[611],{"type":132,"value":612},"en-GB",{"type":132,"value":485},{"type":127,"tag":240,"props":615,"children":617},{"className":616},[],[618],{"type":132,"value":619},"de-CH",{"type":132,"value":621},"), not made-up ones.",{"type":127,"tag":128,"props":623,"children":624},{},[625],{"type":132,"value":626},"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.",{"type":127,"tag":157,"props":628,"children":630},{"id":629},"how-do-you-keep-nuxt-i18n-in-sync-with-the-cms",[631],{"type":132,"value":632},"How do you keep Nuxt i18n in sync with the CMS?",{"type":127,"tag":128,"props":634,"children":635},{},[636],{"type":132,"value":637},"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.",{"type":127,"tag":128,"props":639,"children":640},{},[641,643,648,650,655],{"type":132,"value":642},"The pattern we use with Nuxt i18n is to treat the CMS as the source of truth for ",{"type":127,"tag":177,"props":644,"children":645},{},[646],{"type":132,"value":647},"content",{"type":132,"value":649}," locales and let the framework own ",{"type":127,"tag":177,"props":651,"children":652},{},[653],{"type":132,"value":654},"routing and UI-string",{"type":132,"value":656}," locales, then keep the two lists identical by construction. Concretely:",{"type":127,"tag":658,"props":659,"children":660},"ol",{},[661,709,722,727],{"type":127,"tag":173,"props":662,"children":663},{},[664,666,671,672,678,679,685,687,693,695,700,702,707],{"type":132,"value":665},"Define the locale list once and share it. The same ",{"type":127,"tag":240,"props":667,"children":669},{"className":668},[],[670],{"type":132,"value":605},{"type":132,"value":485},{"type":127,"tag":240,"props":673,"children":675},{"className":674},[],[676],{"type":132,"value":677},"de",{"type":132,"value":485},{"type":127,"tag":240,"props":680,"children":682},{"className":681},[],[683],{"type":132,"value":684},"fr",{"type":132,"value":686}," codes configure Nuxt i18n's ",{"type":127,"tag":240,"props":688,"children":690},{"className":689},[],[691],{"type":132,"value":692},"locales",{"type":132,"value":694}," and drive the CMS query. If the CMS uses ",{"type":127,"tag":240,"props":696,"children":698},{"className":697},[],[699],{"type":132,"value":417},{"type":132,"value":701}," and Nuxt uses ",{"type":127,"tag":240,"props":703,"children":705},{"className":704},[],[706],{"type":132,"value":677},{"type":132,"value":708},", normalize at the boundary so there is exactly one mapping, written down.",{"type":127,"tag":173,"props":710,"children":711},{},[712,714,720],{"type":132,"value":713},"Let Nuxt i18n generate the localized routes (",{"type":127,"tag":240,"props":715,"children":717},{"className":716},[],[718],{"type":132,"value":719},"strategy: 'prefix_except_default'",{"type":132,"value":721}," gives you the subpath structure above), and pass the active locale into every CMS request so the API returns the matching language.",{"type":127,"tag":173,"props":723,"children":724},{},[725],{"type":132,"value":726},"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.",{"type":127,"tag":173,"props":728,"children":729},{},[730],{"type":132,"value":731},"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.",{"type":127,"tag":128,"props":733,"children":734},{},[735],{"type":132,"value":736},"A short shape of the config seam:",{"type":127,"tag":738,"props":739,"children":744},"pre",{"className":740,"code":741,"language":742,"meta":743,"style":743},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F one shared locale list feeds both the framework and the CMS query\nexport const LOCALES = ['en', 'de', 'fr'] as const\n\n\u002F\u002F nuxt.config: i18n owns routing + hreflang\ni18n: {\n  locales: [...LOCALES],\n  defaultLocale: 'en',\n  strategy: 'prefix_except_default',\n}\n\n\u002F\u002F data layer: the active locale drives the CMS request\nconst { locale } = useI18n()\nconst story = await fetchStory(slug, { language: locale.value })\n","ts","",[745],{"type":127,"tag":240,"props":746,"children":747},{"__ignoreMap":743},[748,760,856,865,874,893,931,960,990,999,1007,1016,1056],{"type":127,"tag":749,"props":750,"children":753},"span",{"class":751,"line":752},"line",1,[754],{"type":127,"tag":749,"props":755,"children":757},{"style":756},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[758],{"type":132,"value":759},"\u002F\u002F one shared locale list feeds both the framework and the CMS query\n",{"type":127,"tag":749,"props":761,"children":763},{"class":751,"line":762},2,[764,770,776,782,788,793,798,803,807,812,817,821,825,829,833,837,841,846,851],{"type":127,"tag":749,"props":765,"children":767},{"style":766},"--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic",[768],{"type":132,"value":769},"export",{"type":127,"tag":749,"props":771,"children":773},{"style":772},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[774],{"type":132,"value":775}," const",{"type":127,"tag":749,"props":777,"children":779},{"style":778},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[780],{"type":132,"value":781}," LOCALES ",{"type":127,"tag":749,"props":783,"children":785},{"style":784},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[786],{"type":132,"value":787},"=",{"type":127,"tag":749,"props":789,"children":790},{"style":778},[791],{"type":132,"value":792}," [",{"type":127,"tag":749,"props":794,"children":795},{"style":784},[796],{"type":132,"value":797},"'",{"type":127,"tag":749,"props":799,"children":801},{"style":800},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[802],{"type":132,"value":605},{"type":127,"tag":749,"props":804,"children":805},{"style":784},[806],{"type":132,"value":797},{"type":127,"tag":749,"props":808,"children":809},{"style":784},[810],{"type":132,"value":811},",",{"type":127,"tag":749,"props":813,"children":814},{"style":784},[815],{"type":132,"value":816}," '",{"type":127,"tag":749,"props":818,"children":819},{"style":800},[820],{"type":132,"value":677},{"type":127,"tag":749,"props":822,"children":823},{"style":784},[824],{"type":132,"value":797},{"type":127,"tag":749,"props":826,"children":827},{"style":784},[828],{"type":132,"value":811},{"type":127,"tag":749,"props":830,"children":831},{"style":784},[832],{"type":132,"value":816},{"type":127,"tag":749,"props":834,"children":835},{"style":800},[836],{"type":132,"value":684},{"type":127,"tag":749,"props":838,"children":839},{"style":784},[840],{"type":132,"value":797},{"type":127,"tag":749,"props":842,"children":843},{"style":778},[844],{"type":132,"value":845},"] ",{"type":127,"tag":749,"props":847,"children":848},{"style":766},[849],{"type":132,"value":850},"as",{"type":127,"tag":749,"props":852,"children":853},{"style":772},[854],{"type":132,"value":855}," const\n",{"type":127,"tag":749,"props":857,"children":859},{"class":751,"line":858},3,[860],{"type":127,"tag":749,"props":861,"children":862},{"emptyLinePlaceholder":60},[863],{"type":132,"value":864},"\n",{"type":127,"tag":749,"props":866,"children":868},{"class":751,"line":867},4,[869],{"type":127,"tag":749,"props":870,"children":871},{"style":756},[872],{"type":132,"value":873},"\u002F\u002F nuxt.config: i18n owns routing + hreflang\n",{"type":127,"tag":749,"props":875,"children":877},{"class":751,"line":876},5,[878,883,888],{"type":127,"tag":749,"props":879,"children":881},{"style":880},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[882],{"type":132,"value":19},{"type":127,"tag":749,"props":884,"children":885},{"style":784},[886],{"type":132,"value":887},":",{"type":127,"tag":749,"props":889,"children":890},{"style":784},[891],{"type":132,"value":892}," {\n",{"type":127,"tag":749,"props":894,"children":896},{"class":751,"line":895},6,[897,902,906,911,916,921,926],{"type":127,"tag":749,"props":898,"children":899},{"style":880},[900],{"type":132,"value":901},"  locales",{"type":127,"tag":749,"props":903,"children":904},{"style":784},[905],{"type":132,"value":887},{"type":127,"tag":749,"props":907,"children":909},{"style":908},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[910],{"type":132,"value":792},{"type":127,"tag":749,"props":912,"children":913},{"style":784},[914],{"type":132,"value":915},"...",{"type":127,"tag":749,"props":917,"children":918},{"style":778},[919],{"type":132,"value":920},"LOCALES",{"type":127,"tag":749,"props":922,"children":923},{"style":908},[924],{"type":132,"value":925},"]",{"type":127,"tag":749,"props":927,"children":928},{"style":784},[929],{"type":132,"value":930},",\n",{"type":127,"tag":749,"props":932,"children":934},{"class":751,"line":933},7,[935,940,944,948,952,956],{"type":127,"tag":749,"props":936,"children":937},{"style":880},[938],{"type":132,"value":939},"  defaultLocale",{"type":127,"tag":749,"props":941,"children":942},{"style":784},[943],{"type":132,"value":887},{"type":127,"tag":749,"props":945,"children":946},{"style":784},[947],{"type":132,"value":816},{"type":127,"tag":749,"props":949,"children":950},{"style":800},[951],{"type":132,"value":605},{"type":127,"tag":749,"props":953,"children":954},{"style":784},[955],{"type":132,"value":797},{"type":127,"tag":749,"props":957,"children":958},{"style":784},[959],{"type":132,"value":930},{"type":127,"tag":749,"props":961,"children":963},{"class":751,"line":962},8,[964,969,973,977,982,986],{"type":127,"tag":749,"props":965,"children":966},{"style":880},[967],{"type":132,"value":968},"  strategy",{"type":127,"tag":749,"props":970,"children":971},{"style":784},[972],{"type":132,"value":887},{"type":127,"tag":749,"props":974,"children":975},{"style":784},[976],{"type":132,"value":816},{"type":127,"tag":749,"props":978,"children":979},{"style":800},[980],{"type":132,"value":981},"prefix_except_default",{"type":127,"tag":749,"props":983,"children":984},{"style":784},[985],{"type":132,"value":797},{"type":127,"tag":749,"props":987,"children":988},{"style":784},[989],{"type":132,"value":930},{"type":127,"tag":749,"props":991,"children":993},{"class":751,"line":992},9,[994],{"type":127,"tag":749,"props":995,"children":996},{"style":784},[997],{"type":132,"value":998},"}\n",{"type":127,"tag":749,"props":1000,"children":1002},{"class":751,"line":1001},10,[1003],{"type":127,"tag":749,"props":1004,"children":1005},{"emptyLinePlaceholder":60},[1006],{"type":132,"value":864},{"type":127,"tag":749,"props":1008,"children":1010},{"class":751,"line":1009},11,[1011],{"type":127,"tag":749,"props":1012,"children":1013},{"style":756},[1014],{"type":132,"value":1015},"\u002F\u002F data layer: the active locale drives the CMS request\n",{"type":127,"tag":749,"props":1017,"children":1019},{"class":751,"line":1018},12,[1020,1025,1030,1035,1040,1045,1051],{"type":127,"tag":749,"props":1021,"children":1022},{"style":772},[1023],{"type":132,"value":1024},"const",{"type":127,"tag":749,"props":1026,"children":1027},{"style":784},[1028],{"type":132,"value":1029}," {",{"type":127,"tag":749,"props":1031,"children":1032},{"style":778},[1033],{"type":132,"value":1034}," locale ",{"type":127,"tag":749,"props":1036,"children":1037},{"style":784},[1038],{"type":132,"value":1039},"}",{"type":127,"tag":749,"props":1041,"children":1042},{"style":784},[1043],{"type":132,"value":1044}," =",{"type":127,"tag":749,"props":1046,"children":1048},{"style":1047},"--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF",[1049],{"type":132,"value":1050}," useI18n",{"type":127,"tag":749,"props":1052,"children":1053},{"style":778},[1054],{"type":132,"value":1055},"()\n",{"type":127,"tag":749,"props":1057,"children":1059},{"class":751,"line":1058},13,[1060,1064,1069,1073,1078,1083,1088,1092,1096,1101,1105,1110,1115,1120,1124],{"type":127,"tag":749,"props":1061,"children":1062},{"style":772},[1063],{"type":132,"value":1024},{"type":127,"tag":749,"props":1065,"children":1066},{"style":778},[1067],{"type":132,"value":1068}," story ",{"type":127,"tag":749,"props":1070,"children":1071},{"style":784},[1072],{"type":132,"value":787},{"type":127,"tag":749,"props":1074,"children":1075},{"style":766},[1076],{"type":132,"value":1077}," await",{"type":127,"tag":749,"props":1079,"children":1080},{"style":1047},[1081],{"type":132,"value":1082}," fetchStory",{"type":127,"tag":749,"props":1084,"children":1085},{"style":778},[1086],{"type":132,"value":1087},"(slug",{"type":127,"tag":749,"props":1089,"children":1090},{"style":784},[1091],{"type":132,"value":811},{"type":127,"tag":749,"props":1093,"children":1094},{"style":784},[1095],{"type":132,"value":1029},{"type":127,"tag":749,"props":1097,"children":1098},{"style":908},[1099],{"type":132,"value":1100}," language",{"type":127,"tag":749,"props":1102,"children":1103},{"style":784},[1104],{"type":132,"value":887},{"type":127,"tag":749,"props":1106,"children":1107},{"style":778},[1108],{"type":132,"value":1109}," locale",{"type":127,"tag":749,"props":1111,"children":1112},{"style":784},[1113],{"type":132,"value":1114},".",{"type":127,"tag":749,"props":1116,"children":1117},{"style":778},[1118],{"type":132,"value":1119},"value ",{"type":127,"tag":749,"props":1121,"children":1122},{"style":784},[1123],{"type":132,"value":1039},{"type":127,"tag":749,"props":1125,"children":1126},{"style":778},[1127],{"type":132,"value":1128},")\n",{"type":127,"tag":128,"props":1130,"children":1131},{},[1132],{"type":132,"value":1133},"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.",{"type":127,"tag":157,"props":1135,"children":1137},{"id":1136},"what-should-the-fallback-locale-do",[1138],{"type":132,"value":1139},"What should the fallback locale do?",{"type":127,"tag":128,"props":1141,"children":1142},{},[1143],{"type":132,"value":1144},"Fallbacks decide what a visitor sees when a page has not been translated yet, and the default behavior is often wrong for the business.",{"type":127,"tag":128,"props":1146,"children":1147},{},[1148],{"type":127,"tag":224,"props":1149,"children":1152},{"alt":1150,"src":1151},"A CMS editor with locale tabs showing translated headline and subhead fields, a CTA label falling back to English, and a missing legal footnote translation","\u002Fcovers\u002Fenterprise-cms\u002Fvisuals\u002Flocalization-cms-editor.png",[],{"type":127,"tag":128,"props":1154,"children":1155},{},[1156],{"type":132,"value":1157},"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:",{"type":127,"tag":169,"props":1159,"children":1160},{},[1161,1171],{"type":127,"tag":173,"props":1162,"children":1163},{},[1164,1169],{"type":127,"tag":177,"props":1165,"children":1166},{},[1167],{"type":132,"value":1168},"Fall back to a base locale.",{"type":132,"value":1170}," 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.",{"type":127,"tag":173,"props":1172,"children":1173},{},[1174,1179],{"type":127,"tag":177,"props":1175,"children":1176},{},[1177],{"type":132,"value":1178},"Hide untranslated content.",{"type":132,"value":1180}," 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.",{"type":127,"tag":128,"props":1182,"children":1183},{},[1184],{"type":132,"value":1185},"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.",{"type":127,"tag":157,"props":1187,"children":1189},{"id":1188},"what-about-rtl-and-locale-specific-formatting",[1190],{"type":132,"value":1191},"What about RTL and locale-specific formatting?",{"type":127,"tag":128,"props":1193,"children":1194},{},[1195],{"type":132,"value":1196},"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.",{"type":127,"tag":128,"props":1198,"children":1199},{},[1200],{"type":132,"value":1201},"The parts that actually need handling:",{"type":127,"tag":169,"props":1203,"children":1204},{},[1205,1239,1249],{"type":127,"tag":173,"props":1206,"children":1207},{},[1208,1213,1215,1221,1223,1229,1231,1237],{"type":127,"tag":177,"props":1209,"children":1210},{},[1211],{"type":132,"value":1212},"Direction as a first-class state.",{"type":132,"value":1214}," Set ",{"type":127,"tag":240,"props":1216,"children":1218},{"className":1217},[],[1219],{"type":132,"value":1220},"dir=\"rtl\"",{"type":132,"value":1222}," on the document for RTL locales and build your CSS with logical properties (",{"type":127,"tag":240,"props":1224,"children":1226},{"className":1225},[],[1227],{"type":132,"value":1228},"margin-inline-start",{"type":132,"value":1230},", not ",{"type":127,"tag":240,"props":1232,"children":1234},{"className":1233},[],[1235],{"type":132,"value":1236},"margin-left",{"type":132,"value":1238},") so layout mirrors correctly instead of being hand-flipped per component.",{"type":127,"tag":173,"props":1240,"children":1241},{},[1242,1247],{"type":127,"tag":177,"props":1243,"children":1244},{},[1245],{"type":132,"value":1246},"Bidirectional content.",{"type":132,"value":1248}," 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.",{"type":127,"tag":173,"props":1250,"children":1251},{},[1252,1257,1259,1265,1266,1272],{"type":127,"tag":177,"props":1253,"children":1254},{},[1255],{"type":132,"value":1256},"Locale-specific formatting.",{"type":132,"value":1258}," Dates, numbers, and currency are not translations. They are formatting. They belong to the runtime (",{"type":127,"tag":240,"props":1260,"children":1262},{"className":1261},[],[1263],{"type":132,"value":1264},"Intl.DateTimeFormat",{"type":132,"value":485},{"type":127,"tag":240,"props":1267,"children":1269},{"className":1268},[],[1270],{"type":132,"value":1271},"Intl.NumberFormat",{"type":132,"value":1273},") 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\".",{"type":127,"tag":128,"props":1275,"children":1276},{},[1277],{"type":132,"value":1278},"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.",{"type":127,"tag":157,"props":1280,"children":1282},{"id":1281},"who-owns-the-translation-workflow",[1283],{"type":132,"value":1284},"Who owns the translation workflow?",{"type":127,"tag":128,"props":1286,"children":1287},{},[1288],{"type":132,"value":1289},"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:",{"type":127,"tag":169,"props":1291,"children":1292},{},[1293,1303,1313],{"type":127,"tag":173,"props":1294,"children":1295},{},[1296,1301],{"type":127,"tag":177,"props":1297,"children":1298},{},[1299],{"type":132,"value":1300},"Who authors the source, who translates, and who approves?",{"type":132,"value":1302}," 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.",{"type":127,"tag":173,"props":1304,"children":1305},{},[1306,1311],{"type":127,"tag":177,"props":1307,"children":1308},{},[1309],{"type":132,"value":1310},"Machine translation, human translation, or hybrid?",{"type":132,"value":1312}," 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.",{"type":127,"tag":173,"props":1314,"children":1315},{},[1316,1321],{"type":127,"tag":177,"props":1317,"children":1318},{},[1319],{"type":132,"value":1320},"How do you know what is stale?",{"type":132,"value":1322}," 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.",{"type":127,"tag":128,"props":1324,"children":1325},{},[1326,1328,1334,1336,1342,1344,1349],{"type":132,"value":1327},"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 ",{"type":127,"tag":140,"props":1329,"children":1331},{"href":1330},"\u002Fservices\u002Fheadless-cms",[1332],{"type":132,"value":1333},"headless CMS agency",{"type":132,"value":1335}," practice and our ",{"type":127,"tag":140,"props":1337,"children":1339},{"href":1338},"\u002Fservices\u002Fsitecore",[1340],{"type":132,"value":1341},"Sitecore development and support",{"type":132,"value":1343}," work both cover it end to end, and a short ",{"type":127,"tag":140,"props":1345,"children":1346},{"href":1330},[1347],{"type":132,"value":1348},"call",{"type":132,"value":1350}," is the fastest way to pressure-test your model.",{"type":127,"tag":157,"props":1352,"children":1354},{"id":1353},"the-one-thing-to-get-right-first",[1355],{"type":132,"value":1356},"The one thing to get right first",{"type":127,"tag":128,"props":1358,"children":1359},{},[1360],{"type":132,"value":1361},"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.",{"type":127,"tag":157,"props":1363,"children":1365},{"id":1364},"read-next",[1366],{"type":132,"value":1367},"Read next",{"type":127,"tag":169,"props":1369,"children":1370},{},[1371,1381,1391,1402,1413,1425],{"type":127,"tag":173,"props":1372,"children":1373},{},[1374,1379],{"type":127,"tag":140,"props":1375,"children":1376},{"href":150},[1377],{"type":132,"value":1378},"Enterprise headless CMS, explained",{"type":132,"value":1380},": what \"enterprise headless\" actually requires, localization included.",{"type":127,"tag":173,"props":1382,"children":1383},{},[1384,1389],{"type":127,"tag":140,"props":1385,"children":1386},{"href":142},[1387],{"type":132,"value":1388},"Headless CMS vs traditional CMS",{"type":132,"value":1390},": the tradeoff that decides how much localization work lands on your team.",{"type":127,"tag":173,"props":1392,"children":1393},{},[1394,1400],{"type":127,"tag":140,"props":1395,"children":1397},{"href":1396},"\u002Fblog\u002Fenterprise-cms\u002Fheadless-cms-seo-complete-guide",[1398],{"type":132,"value":1399},"Headless CMS SEO: the complete guide",{"type":132,"value":1401},": hreflang, rendering, and indexing in more depth.",{"type":127,"tag":173,"props":1403,"children":1404},{},[1405,1411],{"type":127,"tag":140,"props":1406,"children":1408},{"href":1407},"\u002Fblog\u002Fenterprise-cms\u002Fheadless-cms-security",[1409],{"type":132,"value":1410},"Headless CMS security",{"type":132,"value":1412},": the other cross-cutting concern in a multi-region build.",{"type":127,"tag":173,"props":1414,"children":1415},{},[1416,1418,1424],{"type":132,"value":1417},"Pillar: ",{"type":127,"tag":140,"props":1419,"children":1421},{"href":1420},"\u002Fblog\u002Fenterprise-cms\u002Fenterprise-cms-2026-platform-and-partner",[1422],{"type":132,"value":1423},"Enterprise CMS in 2026: how to choose the platform and the partner",{"type":132,"value":1114},{"type":127,"tag":173,"props":1426,"children":1427},{},[1428,1430,1434,1436,1440],{"type":132,"value":1429},"Work with us: our ",{"type":127,"tag":140,"props":1431,"children":1432},{"href":1330},[1433],{"type":132,"value":1333},{"type":132,"value":1435}," practice, or ",{"type":127,"tag":140,"props":1437,"children":1438},{"href":1338},[1439],{"type":132,"value":1341},{"type":132,"value":1441}," for language-version builds.",{"type":127,"tag":1443,"props":1444,"children":1445},"style",{},[1446],{"type":132,"value":1447},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}"]