obsidian/wiki/payloadcms/localization.md
2026-05-15 15:13:56 +01:00

5.1 KiB

title aliases tags sources created updated
Localization — Content Internationalization
payload-localization
payload-i18n-content
payload-multilingual
payloadcms
localization
i18n
multilingual
content
raw/configuration__localization.md
2026-05-15 2026-05-15

Overview

Localization manages data translations (content in multiple languages), while wiki/payloadcms/i18n manages UI translations (admin panel labels). They complement each other but are separate concerns.

Enable globally via localization key in Payload Config, then opt individual fields in with localized: true.

Config Setup

import { buildConfig } from 'payload'

export default buildConfig({
  localization: {
    locales: ['en', 'es', 'de'],  // required
    defaultLocale: 'en',           // required
    fallback: true,                // default: true
  },
})

Config Options

Option Description
locales Array of locale codes or full locale objects
defaultLocale Fallback locale when none specified (required)
fallback Auto-fallback to default locale when field value missing (default: true)
filterAvailableLocales Async fn ({ req, locales }) → filtered locales for admin UI selector

Locale Object (Full Form)

{
  code: 'ar',          // required — used in API params
  label: 'Arabic',     // shown in admin locale selector
  rtl: true,           // enables Right-To-Left input fields
  fallbackLocale: 'en' // per-locale fallback (string or array)
}

Locale codes are arbitrary — use 'en', 'en-US', 'en-UK', etc.

Field-Level Localization

Localization is field-level, not document-level. Add localized: true to each field:

{
  name: 'title',
  type: 'text',
  localized: true,
}
  • Works on all field types including array, blocks, relationship
  • Enabling on a blocks field localizes the entire layout (all nested fields)
  • Alternatively, localize only specific nested fields

Warning: Changing localized on an existing field changes the DB structure — existing data for that field will be lost. Plan a migration strategy before toggling.

Status Localization (Experimental)

Manage draft/published status per locale independently. Two-step setup:

Step 1 — Global flag:

experimental: {
  localizeStatus: true,
}

Step 2 — Per collection:

versions: {
  drafts: {
    localizeStatus: true,
  },
}

Result — status stored as object:

status: { en: 'published', es: 'draft', de: 'published' }

Filtering Locales Per Tenant

filterAvailableLocales: async ({ req, locales }) => {
  const tenant = await getTenantFromRequest(req)
  if (tenant?.supportedLocales?.length) {
    return locales.filter(l => tenant.supportedLocales.includes(l.code))
  }
  return locales
}

Filtering runs server-side at app root — not per page navigation. Call router.refresh() when supportedLocales changes.

Querying Localized Documents

REST API

GET /api/posts?locale=es&fallback-locale=none
GET /api/posts?locale=all   // returns all locales in one request
Param Values
locale Any locale code, 'all', '*'
fallback-locale Locale code, 'null', 'false', 'none'

GraphQL API

query {
  Posts(locale: de, fallbackLocale: none) {
    docs { title }
  }
}
  • Locale specified at top level auto-applies to nested relationship fields
  • Locale codes auto-converted to valid GraphQL enum values (dashes → underscores)

Local API

const posts = await payload.find({
  collection: 'posts',
  locale: 'es',
  fallbackLocale: false,
})

fallbackLocale accepts: locale string, array of locales, 'null', 'false', false, 'none'.

Key Takeaways

  • Field-level granularity — opt each field into localization individually
  • localized: true on blocks = whole layout is localized — or go field-by-field inside
  • fallback: true (default) — missing locale values automatically show defaultLocale value
  • locale=all in REST/Local API returns all locale values in one request (useful for admin/export)
  • Status localization is experimental — test thoroughly before production
  • Data migration required when toggling localized on existing fields
  • filterAvailableLocales enables per-tenant locale scoping in multi-tenant apps
  • RTL support built-in via rtl: true on locale object

Sources