Back to Payload

Localization

docs/configuration/localization.mdx

3.84.113.4 KB
Original Source

Localization is one of the most important features of a modern CMS. It allows you to manage content in multiple languages, then serve it to your users based on their requested language. This is similar to I18n, but instead of managing translations for your application's interface, you are managing translations for the data itself.

With Localization, you can begin to serve personalized content to your users based on their specific language preferences, such as a multilingual website or multi-site application. There are no limits to the number of locales you can add to your Payload project.

To configure Localization, use the localization key in your Payload Config:

ts
import { buildConfig } from 'payload'

export default buildConfig({
  // ...
  localization: {
    // highlight-line
    // ...
  },
})

Config Options

Add the localization property to your Payload Config to enable Localization project-wide. You'll need to provide a list of all locales that you'd like to support as well as set a few other options.

To configure locales, use the localization.locales property in your Payload Config:

ts
import { buildConfig } from 'payload'

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

You can also define locales using full configuration objects:

ts
import { buildConfig } from 'payload'

export default buildConfig({
  collections: [
    // collections go here
  ],
  localization: {
    locales: [
      {
        label: 'English',
        code: 'en',
      },
      {
        label: 'Arabic',
        code: 'ar',
        // opt-in to setting default text-alignment on Input fields to rtl (right-to-left)
        // when current locale is rtl
        rtl: true,
      },
    ],
    defaultLocale: 'en', // required
    fallback: true, // defaults to true
  },
})
<Banner type="success"> **Tip:** Localization works very well alongside [I18n](/docs/configuration/i18n). </Banner>

The following options are available:

OptionDescription
localesArray of all the languages that you would like to support. More details
defaultLocaleRequired string that matches one of the locale codes from the array provided. By default, if no locale is specified, documents will be returned in this locale.
fallbackBoolean enabling "fallback" locale functionality. If a document is requested in a locale, but a field does not have a localized value corresponding to the requested locale, then if this property is enabled, the document will automatically fall back to the fallback locale value. If this property is not enabled, the value will not be populated unless a fallback is explicitly provided in the request. True by default.
filterAvailableLocalesA function that is called with the array of locales and the req, it should return locales to show in admin UI selector. See more.

Locales

The locales array is a list of all the languages that you would like to support. This can be strings for each language code, or full configuration objects for more advanced options.

The locale codes do not need to be in any specific format. It's up to you to define how to represent your locales. Common patterns are to use two-letter ISO 639 language codes or four-letter language and country codes (ISO 3166‑1) such as en-US, en-UK, es-MX, etc.

Locale Object

OptionDescription
code *Unique code to identify the language throughout the APIs for locale and fallbackLocale
labelA string to use for the selector when choosing a language, or an object keyed on the i18n keys for different languages in use.
rtlA boolean that when true will make the admin UI display in Right-To-Left.
fallbackLocaleThe code for this language to fallback to when properties of a document are not present. This can be a single locale or array of locales.

* An asterisk denotes that a property is required.

Filter Available Options

In some projects you may want to filter the available locales shown in the admin UI selector. You can do this by providing a filterAvailableLocales function in your Payload Config. This is called on the server side and is passed the array of locales. This means that you can determine what locales are visible in the localizer selection menu at the top of the admin panel. You could do this per user, or implement a function that scopes these to tenants and more. Here is an example using request headers in a multi-tenant application:

ts
// ... rest of Payload config
localization: {
  defaultLocale: 'en',
  locales: ['en', 'es'],
  filterAvailableLocales: async ({ req, locales }) => {
    if (getTenantFromCookie(req.headers, 'text')) {
      const fullTenant = await req.payload.findByID({
        id: getTenantFromCookie(req.headers, 'text') as string,
        collection: 'tenants',
        req,
      })
      if (fullTenant && fullTenant.supportedLocales?.length) {
        return locales.filter((locale) => {
          return fullTenant.supportedLocales?.includes(locale.code as 'en' | 'es')
        })
      }
    }
    return locales
  },
}

Since the filtering happens at the root level of the application and its result is not calculated every time you navigate to a new page, you may want to call router.refresh in a custom component that watches when values that affect the result change. In the example above, you would want to do this when supportedLocales changes on the tenant document.

Field Localization

Payload Localization works on a field level—not a document level. In addition to configuring the base Payload Config to support Localization, you need to specify each field that you would like to localize.

Here is an example of how to enable Localization for a field:

js
{
  name: 'title',
  type: 'text',
  // highlight-start
  localized: true,
  // highlight-end
}

With the above configuration, the title field will now be saved in the database as an object of all locales instead of a single string.

All field types with a name property support the localized property—even the more complex field types like arrays and blocks.

<Banner type="info"> **Note:** Enabling Localization for field types that support nested fields will automatically create localized "sets" of all fields contained within the field. For example, if you have a page layout using a blocks field type, you have the choice of either localizing the full layout, by enabling Localization on the top-level blocks field, or only certain fields within the layout. </Banner> <Banner type="warning"> **Important:** When converting an existing field to or from `localized: true` the data structure in the document will change for this field and so existing data for this field will be lost. Before changing the Localization setting on fields with existing data, you may need to consider a field migration strategy. </Banner>

Status Localization

Payload allows you to localize the status field for draft enabled collections and globals. This lets you manage publication status independently for each locale, ensures the admin UI always shows the status for the selected locale, and unpublish content in a single locale.

<Banner type="warning"> **Important:** This feature is **experimental** and currently in beta, you may encounter some limitations or bugs. Please test thoroughly before using in production. </Banner>

Two-Step Setup Required: To enable localized status, you need to set two configuration options:

  1. Enable the experimental flag in your main config
  2. Enable it for specific collections or globals

Step 1: Enable Experimental Flag

First, enable the experimental flag in your main Payload config:

ts
import { buildConfig } from 'payload'

export default buildConfig({
  // highlight-start
  experimental: {
    localizeStatus: true, // Required to enable the feature globally
  },
  // highlight-end
  // ... rest of your config
})

Step 2: Enable for Collections/Globals

Then, enable it for specific collections or globals:

ts
import type { CollectionConfig } from 'payload'

export const Posts: CollectionConfig = {
  // ...
  versions: {
    drafts: {
      // highlight-start
      localizeStatus: true, // Enable for this specific collection
      // highlight-end
    },
  },
}

When enabled, the status field will be stored as an object keyed by locales:

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

localizeStatus is disabled by default, in which case the status field returns a single string ('draft' or 'published') representing the latest document status across all locales.

Retrieving Localized Docs

When retrieving documents, you can specify which locale you'd like to receive as well as which fallback locale should be used.

REST API

REST API locale functionality relies on URL query parameters.

?locale=

Specify your desired locale by providing the locale query parameter directly in the endpoint URL.

?fallback-locale=

Specify fallback locale to be used by providing the fallback-locale query parameter. This can be provided as either a valid locale as provided to your base Payload Config, or 'null', 'false', or 'none' to disable falling back.

Example:

fetch('https://localhost:3000/api/pages?locale=es&fallback-locale=none');

GraphQL API

In the GraphQL API, you can specify locale and fallbackLocale args to all relevant queries and mutations.

The locale arg will only accept valid locales, but locales will be formatted automatically as valid GraphQL enum values (dashes or special characters will be converted to underscores, spaces will be removed, etc.). If you are curious to see how locales are auto-formatted, you can use the GraphQL playground.

The fallbackLocale arg will accept valid locales, an array of locales, as well as none to disable falling back.

Example:

graphql
query {
  Posts(locale: de, fallbackLocale: none) {
    docs {
      title
    }
  }
}
<Banner> In GraphQL, specifying the locale at the top level of a query will automatically apply it throughout all nested relationship fields. You can override this behavior by re-specifying locale arguments in nested related document queries. </Banner>

Local API

You can specify locale as well as fallbackLocale within the Local API as well as properties on the options argument. The locale property will accept any valid locale, and the fallbackLocale property will accept any valid locale, array of locales, as well as 'null', 'false', false, and 'none'.

Example:

js
const posts = await payload.find({
  collection: 'posts',
  locale: 'es',
  fallbackLocale: false,
})
<Banner type="success"> **Tip:** The REST and Local APIs can return all Localization data in one request by passing 'all' or '*' as the **locale** parameter. The response will be structured so that field values come back as the full objects keyed for each locale instead of the single, translated value. </Banner>