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

## Overview

Localization manages **data translations** (content in multiple languages), while [[wiki/payloadcms/i18n|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

```ts
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)

```ts
{
  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:

```js
{
  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:**
```ts
experimental: {
  localizeStatus: true,
}
```

**Step 2 — Per collection:**
```ts
versions: {
  drafts: {
    localizeStatus: true,
  },
}
```

Result — `status` stored as object:
```ts
status: { en: 'published', es: 'draft', de: 'published' }
```

## Filtering Locales Per Tenant

```ts
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

```graphql
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

```js
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

## Related

- [[wiki/payloadcms/i18n|I18n — Interface Internationalization]] — admin panel UI translations (separate from content)
- [[wiki/payloadcms/collection-config|Collection Config]] — field definitions and schema options
- [[wiki/payloadcms/rest-api|REST API]] — locale query params in detail
- [[wiki/payloadcms/local-api|Local API]] — locale options in `payload.find()` / `payload.findByID()`
- [[wiki/payloadcms/globals-config|Globals Config]] — globals also support localized fields

## Sources

- `raw/configuration__localization.md`
- https://payloadcms.com/docs/configuration/localization
