Skip to content

Locales (i18n)

Strapi ships the i18n plugin in the box. Its content API exposes the list of configured locales, and the client wraps it as client.i18n — typed, and going through the same auth pipeline, baseURL, error handling, and Next.js cache integration as the rest of the client.

Before this existed you had to reach for @strapi/client (or a hand-written fetch) just to fill a language switcher.

Methods

MethodEndpointReturns
locales()GET /api/i18n/localesI18nLocale[]

I18nLocale

ts
interface I18nLocale {
    id: number
    documentId: string
    name: string
    code: string
    isDefault: boolean
    createdAt: string
    updatedAt: string
    publishedAt: string | null
}

Listing locales

ts
const locales = await client.i18n.locales()

const codes = locales.map(locale => locale.code)
// ['en', 'de', 'fr']

const fallback = locales.find(locale => locale.isDefault)?.code ?? 'en'

A language switcher, with the display names Strapi already stores:

ts
const options = (await client.i18n.locales()).map(locale => ({
    value: locale.code,
    label: locale.name,
}))

Pass a code straight to a collection query:

ts
const articles = await client.articles.find({ locale: 'de' })

Response shape

This route answers with a bare array — there is no { data, meta } envelope, so locales() resolves to I18nLocale[] directly.

The route ignores query parameters

listLocales reads nothing off the query string: fields, filters, sort and pagination are all silently ignored, and you always get every locale in full. Narrow the result in your own code instead.

Permissions

The endpoint is not public by default — an anonymous request gets 403 Forbidden. Either grant the role the i18nlocalefind permission under Settings → Users & Permissions → Roles, or call it with an API token.

Locale codes are not always two letters

Strapi accepts regional codes such as en-US and pt-BR, so treat code as an opaque string rather than assuming a two-letter shape.

Released under the MIT License.