Pluma
Docs index
docs/guides/languages.md View markdown →

Languages

A site has one default language: the one its content is written in. Add more and every field marked Localized gets one value per language, while the rest (prices, photos, dates) keep a single value for all of them. Your site asks for a language with ?locale=en.

The default language

It's what your content is written in, and what the API returns when nobody asks for a language. Pluma guesses it when you create the site, from your browser's language, and again from the content itself when it first reads your site for Visibility, as long as nobody picked one and the site has a single language.

To change it: Settings → General → Content language. By API, PATCH /api/v1/spaces/:space_id with {"default_locale": "es"}; by MCP, update_site with default_locale.

Changing it relabels the content, it doesn't translate it: every value stored under the old language moves to the new one, in every entry and every version (published ones too), and the language keeps its place. Use it when the content was stored under the wrong language, like a Spanish site that started as en.

  • If an entry already has values in both languages, nothing changes and you get validation_failed with the entries that clash: moving would overwrite one of them. Remove the other language first, or empty those values.
  • Your deploy hooks get content_type.changed, so the site rebuilds.

Add or remove languages

Settings → Languages. Each language has:

What it is Example
Code Two letters, or two plus a region es, en, pt, fr, en-US
Name What the dashboard shows. By default, the language's own name English
Fallback What to show when a value is missing in this language. By default, the default language. It can be none es

Removing a language deletes its values from every entry and every version, published ones too. The dashboard asks first and says how many entries have values in it. The default language can't be removed: make another one the default first.

By API:

POST /api/v1/spaces/:space_id/locales
{ "code": "en", "name": "English", "fallback_code": "es" }

PATCH /api/v1/spaces/:space_id/locales/:code changes name or fallback_code. DELETE /api/v1/spaces/:space_id/locales/:code removes it; if it has values, it refuses unless you send ?delete_values=true. All three need manage_model. By MCP: add_locale, update_locale, remove_locale. See Management API.

In the dashboard

With more than one language, each entry has a tab per language, like Español | English:

  • The default tab has every field, as always.
  • The other tabs have only the localized fields. Empty ones show the fallback's value as a placeholder, which is what your site gets until someone writes them.
  • Each tab says how many fields are not translated yet: localized fields with a value in the default language and none in that one.
  • Fields that aren't localized show once, in the default tab.

One Save keeps every tab, as one new version. Turn Localized on or off for a field in Model.

Translate with AI

In a language's tab, Translate with AI fills the fields still missing in that language from the default one, and saves them as a new draft version. It never publishes: read it, fix what you'd say differently, then publish.

  • Text, long text and tags are translated.
  • Rich text keeps its structure: the same headings, lists, links and images. If the translation comes back with a different structure, that field is left as it was and Pluma says so.
  • Slugs get a translated slug (menu-del-dia → daily-menu), made unique among the type's entries in that language.
  • Lists get their text translated; numbers and dates in them stay.
  • References, files, numbers, dates and yes/no stay as they are: they show the default language's value.

When every field is translated, the button becomes Translate again, which replaces what's there (as a new version: the old one stays in Versions).

By API, POST /api/v1/spaces/:space_id/entries/:id/translate with {"to": "en"} (and "overwrite": true to translate every field again). By MCP, translate_entry. If the AI provider is down you get translation_unavailable and nothing is saved.

By API

Reading, with any key:

  • Without locale, fields come in the default language.
  • ?locale=en: in English. A missing value falls back to the language's fallback; fields that aren't localized always come in the default language's value.
  • ?locale=*: every language, as { "title": { "es": "Menú del día", "en": "Daily menu" } }.
  • Filters and sorting (fields.slug=daily-menu, order=fields.title) use the language you asked for, so a site finds an entry by its English slug.

Writing, with a management key or an agent token, the same locale:

PATCH /api/v1/spaces/:space_id/entries/12?locale=en
{ "fields": { "title": "Daily menu", "slug": "daily-menu" } }
PATCH /api/v1/spaces/:space_id/entries/12?locale=*
{ "fields": { "title": { "es": "Menú del día", "en": "Daily menu" } } }
  • Only the fields and languages you send change. null empties that field in that language, and the others keep theirs.
  • A field that isn't localized only takes the default language: sending it in another one gives validation_failed.
  • Saving checks the types in every language. Publishing requires the required fields in the default language only; a missing translation falls back. Slugs have to be unique within the type in each language.
  • With locale=*, errors for a language other than the default come as title.en.
  • In a batch, create_entry and update_entry take "locale" the same way. The MCP's create_entry, update_entry, get_entry and list_entries too.

URLs

In a type's URL path, {locale} becomes the language: with /{locale}/menu/{slug}, the English version of an entry links to /en/menu/daily-menu, and sys.url and sys.preview_url come in the language you asked for, with its slug.

The default language goes without prefix by default: /menu/menu-del-dia. To have it too (/es/menu/menu-del-dia), turn on Put the default language in URLs too in Settings → Languages, or send "prefix_default_locale": true to PATCH /api/v1/spaces/:space_id or update_site. A URL path without {locale} is the same for every language, with each language's slug.