The Visual Website Builder
You Actually Own. React + Next.js, low-code, self-hosted. Your data, your infrastructure, every feature included.
© ChaiBuilder. All rights reserved.

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.
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.
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.
Each page gets its translations one at a time, from the language switcher in the top bar.
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:
/about/team
until /about has a French page.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.
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.
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.
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 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.
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.
| 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.
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.
<html lang> is right.hreflang alternates and the canonical URL.