You're reading docs for Nextly Alpha. APIs may change between releases.

Localization

Localization

Run the same site in several languages, with a fallback when a translation is missing, and publish each language separately.

Localization lets one document exist in several languages. An editor switches language at the top of the edit screen and writes the Spanish version of the same post, without creating a second post.

Availability

Built inNo plugin required
AdminLanguage switcher on edit screens, plus a translations overview
PermissionThe same access as editing the document

Turn it on

Declare your languages once, in nextly.config.ts:

import { defineConfig } from "nextly";

export default defineConfig({
  localization: {
    locales: [
      "en",
      { code: "es", label: "Español" },
      { code: "ar", label: "العربية", rtl: true },
      { code: "fr", label: "Français", fallbackLocale: "en" },
    ],
    defaultLocale: "en",
    fallback: true,
  },
});

A locale is either a bare code or an object. The object form carries:

KeyMeaning
codeThe locale code, e.g. es
labelWhat the admin shows in the language switcher
rtlRenders that language right-to-left
fallbackLocaleWhich language (or ordered list) to fall back to

Choose what gets translated

Mark a whole collection localized, then opt individual fields out:

import { defineCollection, text } from "nextly/config";

export const Posts = defineCollection({
  slug: "posts",
  localized: true,
  fields: [
    text({ name: "title" }),
    // Not worth translating: the same value in every language.
    text({ name: "externalId", localized: false }),
  ],
});

Fields inherit the collection's setting, so the common case is one flag.

Fallback chains

fallback: true means a missing translation falls back rather than rendering empty. A locale can name its own chain — fallbackLocale: ["es", "en"] tries Spanish, then English. Without a chain, the default locale is used.

Publishing one language at a time

Per-locale publishing needs two flags, not one. localized gives you translations; status gives you the draft/published lifecycle. Only with both does each language get its own state:

import { defineCollection, text } from "nextly/config";

export const Posts = defineCollection({
  slug: "posts",
  localized: true,
  status: true,
  fields: [text({ name: "title" })],
});

With both set, publishing the Spanish version does not publish the French one, and a language still being translated stays invisible while the others are live.

With localized: true alone you get translations and no lifecycle — there is nothing to publish per language, because the entity has no draft/published state at all.

This is per-locale publishing, not per-locale scheduling. A content release schedules the whole document across every language; it cannot schedule one language on its own.