ChaiBuilder Logo

Multilingual

A ChaiBuilder site can serve the same pages in several languages. The model is deliberate and worth understanding before you start, because it decides what you can and cannot change per language.

The model in one sentence

One page, one layout, many texts. A page is built once in the default language. Adding French does not copy the page - it adds a French entry for it, and the French wording is stored alongside the original on the same blocks.

That has a direct consequence:

Shared across every language Per language
Layout and block structure Text content
Styles and classes Image and its alt text
Which blocks exist and their order Link labels
Design tokens and theme Slug
SEO title, description, JSON-LD

Editing structure while a secondary language is selected changes every language. Deleting a block, reordering, or restyling in the French view does the same thing to English. The rule is: switch to the default language for layout work, switch to a secondary language for wording.

Setting up languages

A site has a default language and a list of additional languages, stored on the site record. There is no self-service UI for this yet - the list is configured on the site directly, which for most teams means asking whoever set the project up.

The language codes must match the locale codes your CMS uses, because the same string is passed through as the CMS locale when a page loads its content. If Payload has en and fr, the site's language list uses en and fr.

Adding a language to a page

Each page gets its translations one at a time, from the language switcher in the top bar.

  1. Open the page in the default language.
  2. Open the language menu and choose Add language.
  3. Pick the language, set the slug, and confirm.

The dialog offers a checkbox to prefix the slug with the language code, so /about becomes /fr/about. It is on by default and is the convention most sites want, but it is a convention rather than a rule - uncheck it and the French page can live at /a-propos. Slugs are globally unique, so two languages cannot share one.

Some constraints:

  • A child page needs its parent translated first. You cannot add French to /about/team until /about has a French page.
  • Partials cannot have language pages. Their content is still translated - see below.
  • Nothing is auto-created. Adding a language to the site does not translate any page. Every page you want in French needs its French entry adding.

Translating content

Switch language in the top bar and edit as normal. Only fields a block declares translatable show a language marker; typically that is text content, link labels, image and alt text.

Fields you have not translated yet show the default-language value with an orange marker and the tooltip "No translation provided. Using default language value." That is the fastest way to audit a page - anything with a marker still needs attention.

Untranslated fields fall back silently on the published page too. A French page with an untranslated heading renders the English heading rather than an empty space. That is usually what you want, and it is also why a half-translated page can ship without anyone noticing.

AI translation

With AI enabled, the AI panel switches into translation mode whenever a secondary language is selected. Select a block and ask it to translate, or run it with nothing selected to work through the whole page. It writes into the translated fields and leaves the originals alone.

The panel states the rule explicitly: "Only content can be edited in secondary languages. To edit layout, styles and more, switch to the default language."

Translation is per page. There is no site-wide bulk translate.

Finding untranslated pages

The pages manager has language tabs and a Toggle untranslated pages filter. Pages missing a translation for the selected language show dimmed with an Add {Language} page button.

If you open a URL for a language that has no page, the builder shows a blocking card - "Page missing for {Language} language" - with a button back to the default language.

Publishing

Each language page publishes independently, so you can ship the English page today and French next week.

One important detail: publishing a translation also publishes the default-language page. It has to, because the content lives there. If the default page has unrelated draft edits in progress, publishing the French page ships those too. Finish or revert default-language work before publishing a translation.

Before publishing, if any translatable field on the page is still empty for the current language, a Translation warning appears - "Some blocks need translations before publishing" - with the option to continue anyway.

Taking a default-language page offline removes its translations from the live site as well.

Partials and global blocks

Partials cannot have their own language pages, but their content is translated through the same mechanism. A translated header partial is translated once and every language of every page using it picks up the right wording. That is the intended design - shared furniture translated in one place.

SEO

ChaiBuilder emits hreflang alternates from the page's other languages, so search engines can connect the versions. Two things to check on a multilingual site:

Canonical URLs default to the primary language. Unless you set a canonical explicitly in the page's SEO settings, a translated page canonicalizes to the default-language URL. For most sites that is wrong - a French page should be canonical to itself. Set it per language.

SEO fields do not fall back. Unlike block content, an untranslated title or description renders empty rather than borrowing the default language. Fill them in per language.

The sitemap includes published pages in every language.

What renders when something is missing

Missing Result
A translated field The default-language value, silently
A translated page 404 - there is no fallback to the default language
A translated SEO field Empty
Translated JSON-LD Empty, with a "copy from default language" action in the builder

The page-level behaviour is the one to plan around: if /fr/pricing does not exist, French visitors following a link get a 404, not the English page. Either translate every page you link to, or keep language navigation to pages that exist.

Gotchas

Structure is shared. Repeating it because it is the mistake people make: layout edits in a secondary language affect all languages.

Duplicating a page does not duplicate its translations. The copy comes back default-language only.

Internal links do not switch language automatically. A link pointing at a page picks the default-language URL even on a translated page. Set the link per language when it matters.

Dynamic page item search is default-language only. When picking a specific CMS record for a dynamic page, switch to the default language first - the search is disabled in secondary languages.

No right-to-left support. The page language is set on the <html> element, but text direction is not. An RTL language needs styling work in your own layout.

Analytics and site-wide scripts read the default language's settings regardless of the page's language.

Verify

  1. In the builder, switch language and confirm no field still shows the untranslated marker.
  2. Check the slug is what you expect, including the language prefix.
  3. Publish, then load the translated URL and confirm <html lang> is right.
  4. View source and check the hreflang alternates and the canonical URL.
  5. Follow the site navigation in the translated language and confirm nothing 404s.

© ChaiBuilder. All rights reserved.