⚠️ Work in progress — createCMS is pre-1.0 and not production-ready (not tested in production). Expect breaking changes.
createCMS

i18n

Per-language content scoping with fallback chains.

The i18n plugin scopes content by language. Each root belongs to one language, sibling-language versions of an entry are tied together by a translation group, and reads fall back along a chain you configure.

Installation

Add it to your config

Pass the static set of languages (as a const tuple, so they become a typed union) and a defaultLanguage:

lib/cms.ts
import { createCMS } from '@createcms/core';
import { i18n } from '@createcms/core/plugins/i18n';

export const cms = createCMS({
  db,
  collections,
  media,
  plugins: [
    i18n({
      languages: ['en', 'de', 'fr'],
      defaultLanguage: 'en',
      fallback: { de: ['en'], fr: ['en'] },
    }),
  ],
});

Update the database

The plugin adds language columns. Regenerate the schema file, then apply it to your database with your Drizzle migration workflow:

npx createcms generate
npx drizzle-kit generate && npx drizzle-kit migrate

Usage

The active language is resolved per request from your middleware and applied to every query. To create a translation of an existing entry, call createTranslation:

await cms.api.pages.createTranslation({
  body: {
    sourceRootId: root.rootId,
    targetLanguage: 'de',
    seed: 'copy',
  },
});

listTranslations returns the sibling-language versions of an entry (the existing translations). To find untranslated languages, diff them against your configured languages.

Options for createTranslation

FieldTypeRequiredDescription
sourceRootIdstringyesThe entry to translate from.
targetLanguagestringyesA language from your languages list.
targetSlugstringnoSlug for the new translation.
seed'copy' | 'blank'noStart from a copy of the source or an empty entry.
messagestringnoCommit message.

Schema

TableColumnPurpose
rootslanguageThe entry's language.
rootstranslationKeyStable group id tying sibling-language entries together.
redirectslanguagePer-language redirect routing.

Options

OptionTypeDescription
languagesreadonly string[]The supported languages (a const tuple).
defaultLanguageone of languagesSeed language and default fallback target.
fallbackPartial<Record<language | 'default', language[]>>Per-language fallback chains. Absent means fall back to defaultLanguage.

On this page