Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/alineacms/alinea/llms.txt

Use this file to discover all available pages before exploring further.

Alinea supports multi-locale content at the root level. When a root has i18n configured, every entry inside it exists independently per locale — each locale version has its own file in git and its own editing session in the dashboard. Editors can switch between locales using the locale picker and the dashboard shows a clear indicator of which locale is active.

Configuring locales on a root

Add the i18n option to a Config.root call with a locales array. Each element is a BCP 47 language tag such as 'en', 'fr', or 'nl-BE'.
cms.config.ts
import {Config} from 'alinea'
import {createCMS} from 'alinea/next'

export const cms = createCMS({
  schema,
  workspaces: {
    primary: Config.workspace('Primary workspace', {
      source: 'content/primary',
      mediaDir: 'public',
      roots: {
        pages: Config.root('Languages', {
          contains: [schema.Page, schema.Folder],
          i18n: {
            locales: ['en', 'fr', 'nl-BE', 'nl-NL']
          }
        })
      }
    })
  }
})
i18n.locales
ReadonlyArray<string>
required
An ordered list of BCP 47 locale codes. The first entry in the array is treated as the default locale for the root.

URL structure

When i18n is enabled on a root, Alinea stores each locale’s content in a sub-directory named after the locale code (lowercased). For example:
content/primary/pages/en/about.json
content/primary/pages/fr/about.json
content/primary/pages/nl-be/about.json
Your entryUrl function on the type receives the locale field and can use it to construct the correct public URL.

Querying content by locale

Pass locale to cms.find() or cms.get() to filter results to a specific locale.
Fetching pages in French
import {Query} from 'alinea'

const pages = await cms.find({
  type: schema.Page,
  locale: 'fr',
  select: {
    title: Query.title,
    url: Query.url,
    locale: Query.locale
  }
})
The Query.locale field returns the locale string for each result.

Fetching translations of an entry

Use Query.translations to include the same entry in other locales as a sub-query. This is useful when building locale switchers.
Fetching an entry with all its translations
import {Query} from 'alinea'

const page = await cms.get({
  type: schema.Page,
  locale: 'en',
  id: entryId,
  select: {
    title: Query.title,
    url: Query.url,
    locale: Query.locale,
    translations: Query.translations({
      select: {
        locale: Query.locale,
        url: Query.url
      }
    })
  }
})
Query.translations returns an array of the same entry in the other configured locales. The current locale is excluded unless you pass includeSelf: true.
Including the current locale in translations
translations: Query.translations({
  includeSelf: true,
  select: {
    locale: Query.locale,
    url: Query.url
  }
})

Shared fields across locales

If some fields should carry the same value across all locales — for example, a slug or a canonical image — mark them with shared: true. Alinea synchronises the value between locale versions automatically when the field is edited.
apps/dev/src/schema/example/I18nFields.tsx
import {Config, Field} from 'alinea'

export const I18nFields = Config.document('I18n', {
  fields: {
    shared: Field.text('Shared field', {
      help: 'This field is shared between languages.',
      shared: true
    })
  }
})
When an editor changes a shared field in one locale, the same value is written to every other locale version of that entry.

Switching locales in the dashboard

The dashboard displays a locale selector in the entry header whenever the current entry belongs to a root with i18n configured. Clicking a different locale opens the same entry in that locale, creating the locale version if it does not yet exist.
Alinea handles storing and retrieving content per locale — it does not translate text for you. The content editor is responsible for writing or pasting the translated copy into each locale version of an entry.

Type-level locale URLs

Use the entryUrl option on a type to build locale-aware URLs:
Building locale-aware URLs
import {Config} from 'alinea'
import type {EntryUrlMeta} from 'alinea'

export const Page = Config.document('Page', {
  fields: { /* ... */ },
  entryUrl({locale, parentPaths, path}: EntryUrlMeta) {
    const prefix = locale ? `/${locale}` : ''
    return `${prefix}/${[...parentPaths, path].join('/')}`
  }
})

Build docs developers (and LLMs) love