Internationalization
Entity-level translations with locale linking.
CMS supports internationalization at the entity level — not the field level. Each translation is a separate record linked by a shared translationGroup UUID.
Setup
Define available locales in your config:
export default config({
locales: ["en", "pt", "fr"],
defaultLocale: "en",
// ...
});
Mark entities as translatable:
const pages = entity("pages", {
translatable: true,
fields: [
text("title", { required: true }),
slug("slug", { unique: true, required: true }),
object("body", { fields: [...] }),
],
});
Automatic fields
Translatable entities automatically get two extra fields:
locale— the entity’s locale ("en","pt", etc.)translationGroup— a UUID that links all locale variants together
You don’t define these fields — they’re injected automatically when translatable: true.
Querying by locale
Filter by locale to get content in a specific language:
GET /api/pages?filter[locale]=en
Translation links
Every translatable entity in the API response includes a _translations field with all sibling locales:
{
"data": {
"id": "uuid-en",
"title": "About Us",
"slug": "/en/about",
"locale": "en",
"translationGroup": "uuid-group",
"_translations": {
"pt": { "id": "uuid-pt", "slug": "/pt/sobre" },
"fr": { "id": "uuid-fr", "slug": "/fr/a-propos" }
}
}
}
This makes building SEO alternate links (hreflang) trivial — all the data is in a single response.
Creating translations
Use the translate endpoint to create a translation from an existing record:
POST /api/pages/:id/translate
Content-Type: application/json
{ "locale": "pt" }
This:
- Copies the content from the source record
- Creates a new record with the target locale
- Links both records via the same
translationGroup - Auto-prefixes slug fields with the locale (e.g.,
/en/about→/pt/about) - Copies many-to-many relations from the source
Slug conventions
By convention, translatable slugs include the locale prefix:
/en/about
/pt/sobre
/fr/a-propos
This isn’t enforced by the system — you control slug values in your content. But the translate endpoint does auto-prefix slugs when creating translations.
Previous
Hooks
Next
Media