# 🌍 Internationalization (i18n)

The Internationalization (i18n) plugin allows Strapi users to create, manage and distribute localized content in different languages, called "locales". For more information about the concept of internationalization, please refer to the W3C definition (opens new window).

The i18n plugin:

  • allows admin panel users to create several localized versions of their content (see user guide)
  • allows developers to build localized projects by fetching and consuming the right content depending on the country/language of the audience.

✏️ NOTE

The i18n plugin does not automatically translate the users' content nor adapt the admin interface to languages specificities (e.g. displaying the admin panel in Right To Left format).

# Installation

PREREQUISITES

The Internationalization plugin is installed by default on all Strapi applications running on version 3.6.0 or higher. For lower versions, a migration is needed (see Update Strapi version), as well as a manual installation of the plugin.

The plugin can be installed:

# Usage with Strapi Content API

The i18n plugin adds new features to the Content API:

# Getting localized entries with the locale parameter

The locale API parameter can be used to fetch entries only for a specified locale. It takes a locale code as value (see full list of available locales (opens new window)).

💡 TIP

To fetch content for a locale, make sure it has been already added to Strapi in the admin panel.

The format for a GET request is the following:

Request

GET /{content-type}?_locale={locale-code}

Use all as a value for the locale code, as in http://localhost:1337/restaurants?_locale=all, to fetch entries for all locales that have been configured in the admin panel.

If the locale parameter isn't defined, it will be set to the default locale. en is the default locale when i18n plugin is installed, so by default a GET request to http://localhost:1337/restaurants will return the same response as a request to http://localhost:1337/restaurants?_locale=en.

💡 TIP

Another locale can be set as the default locale in the admin panel.

When the i18n plugin is installed, the response to requests includes fields that are specific to internationalization:

  • locale (string) is the locale code for the content entry
  • localizations (array) lists the existing localizations for this content entry; these localizations objects have 3 properties:
    • id (number|string) is the id of the localized content entry
    • locale(string) is the locale code for the localized content entry
    • published_at (string) is the date and time when the localized content entry was published, in ISO 8601 (opens new window) format

Example request

GET http://localhost:1337/restaurants?_locale=fr

Example Response

[
  {
    "id": 4,
    "name": "Can Alegria",
    "description": "description in French",
    "locale": "fr",
    "localizations": [
      {
        "id": 3,
        "locale": "en",
        "published_at": "2021-04-07T10:10:31.949Z"
      }
    ]
  },
  {
    "id": 8,
    "name": "She's Cake",
    "description": "description in French",
    "locale": "fr",
    "localizations": []
  }
]

In the example response above:

  • restaurant with "id": 4 is a French ("locale": "fr") localization of the existing restaurant with "id": 3 (created for the default en locale), as shown in the localizations object included in the response (see creating a localization for an existing entry).
  • restaurant with "id": "8" was created from scratch using the API, passing the locale: fr in the request body (see creating a new localized entry).

# Creating a new localized entry

To create a localized entry from scratch, send a POST request to the Content API.

If no locale has been passed in the request body, the entry is created using the default locale for the application:

Example request

POST http://localhost:1337/restaurants

{
  "name": "Oplato",
  "description": "description in English"
}

Example response

{
    "id": 5,
    "name": "Oplato",
    "description": "description in English",
    "locale": "en",
    "localizations": []
}

To create a localized entry for a locale different from the default one, add the locale attribute to the body of the POST request:

Example request

POST http://localhost:1337/restaurants

{
  "name": "She's Cake",
  "description": "description in French",
  "locale": "fr"
}

Example response

{
    "id": 8,
    "name": "She's Cake",
    "description": "description in French",
    "locale": "fr",
    "localizations": []
}

# Creating a localization for an existing entry

To create another localization for an existing localized entry, send a POST request to the appropriate URL depending on the type of content:

Content-Type Request URL format
Collection type POST /{content-type}/:id/localizations
Single type POST /{content-type}/localizations

When creating a localization for existing localized entries, the body of the POST request can only accept localized fields.

💡 TIP

The Content-Type should have the createlocalization permission enabled, otherwise the POST request will return a 403: Forbidden status.

# Creating a localization for a collection type

When sending a POST request to a collection type, Strapi will:

  1. use the id as a base entry for the non-localized fields and copy them in the new entry
  2. then create a new entry for the given locale and link it with the base entry.

Example request

POST http://localhost:1337/restaurants/8/localizations

{
  "locale": "en",
  "name": "She's Cake",
  "test": 9,
  "description": "description in English"
}

This request:

  1. creates a new entry in en
  2. links the created entry with restaurant:8 (they will share the same localizations object)
  3. copies every non-localized fields from restaurant:8 into the new entry and keeps the localized fields from the request's body

Example response

{
    "id": 9,
    "name": "She's Cake",
    "description": "description in English",
    "locale": "en",
    "localizations": [
        {
            "id": 8,
            "locale": "fr",
            "published_at": "2021-04-07T13:22:46.589Z"
        }
    ]
}

# Creating a localization for a single type

Example request

POST http://localhost:1337/homepage/localizations

{
  "locale": "fr",
  "title": "Bienvenue sur FoodAdvisor !"
}

Example response

{
  "id": 2,
  "title": "Bienvenue sur FoodAdvisor!",
  "locale": "fr",
  "localizations": [
    {
        "id": 1,
        "locale": "en",
        "published_at": "2021-04-14T12:49:37.055Z"
    }
  ]
}

# Updating an entry

Currently, it is not possible to change the locale of an existing localized entry.

When updating a localized entry (with PUT /{localized-content-type}/:id), if you set a locale attribute in the request body, it will be ignored.

# Usage with the GraphQL plugin

The i18n plugin adds new features to the GraphQL plugin:

# Getting localized entries with GraphQL

Queries can use the locale argument to fetch entries only for a specified locale.

💡 TIP

To fetch entries for all locales, use locale: "all" in the query.

# Fetching a collection type

Example query

query {
  restaurants(locale: "en") {
    id
    name
    locale
    localizations {
      id
      locale
    }
  }
}

Example response

{
  "data": {
    "restaurants": [
      {
        "id": "3",
        "name": "Can Alegria",
        "locale": "en",
        "localizations": [
          {
            "id": "4",
            "locale": "fr"
          }
        ]
      },
      {
        "id": "5",
        "name": "Oplato",
        "locale": "en",
        "localizations": []
      },
      {
        "id": "9",
        "name": "She's Cake",
        "locale": "en",
        "localizations": [
          {
            "id": "8",
            "locale": "fr"
          }
        ]
      }
    ]
  }
}

# Fetching a single type

Example query

query {
  homepage(locale: "en") {
    id
    title
    locale
    localizations {
      id
      locale
    }
  }
}

Example response

{
  "data": {
    "homepage": {
      "id": "1",
      "title": "Welcome on FoodAdvisor!",
      "locale": "en",
      "localizations": [
        {
          "id": "2",
          "locale": "fr"
        }
      ]
    }
  }
}

# Creating new localized entries with GraphQL

The locale field can be passed in the data object of the mutation to create a localized entry for this specific locale (for more information, see create a new entry with the GraphQL plugin).

# Creating a new localization for a collection type

Example mutation

mutation {
  createRestaurantLocalization(
    input: { where: { id: 8 }, data: { 
      locale: "en",
      name: "She's Cake",
      description: "description in English"
    }}
  ) {
    id
    locale
    name
    description
    localizations {
      id
      locale
      description
    }
  }
}

Example response

{
  "data": {
    "createRestaurantLocalization": {
      "id": "9",
      "locale": "en",
      "name": "She's Cake",
      "description": "description in English",
      "localizations": [
        {
          "id": "8",
          "locale": "fr",
          "description": "description in French"
        }
      ]
    }
  }
}
      

# Creating a new localization for a single type

Example mutation

mutation {
  createHomepageLocalization(
    input: {
      data: {
        locale: "fr"
        title: "Bienvenue sur FoodAdvisor !"
      }
    }
  ) {
    id
    locale
    title
    localizations {
      id
      locale
      title
    }
  }
}

Example response

{
  "data": {
    "createHomepageLocalization": {
      "id": "2",
      "locale": "fr",
      "title": "Bienvenue sur FoodAdvisor !",
      "localizations": [
        {
          "id": "1",
          "locale": "en",
          "title": "Welcome on FoodAdvisor!"
        }
      ]
    }
  }
}

# Updating a localized single type with GraphQL

A locale argument can be passed in the mutation to update content for a given locale (for more information, see update an existing entry with the GraphQL plugin).

Currently, it is not possible to change the locale of an existing localized entry. If you set a locale field in the data object of the mutation, it will be ignored.

Example mutation

mutation {
  updateHomepage(
    locale: "fr"
    input: { data: { title: "Bienvenue sur l'annuaire FoodAdvisor !" } }
  ) {
    homepage {
      id
      title
    }
  }
}

Example response

{
  "data": {
    "updateHomepage": {
      "homepage": {
        "id": "2",
        "title": "Bienvenue sur l'annuaire FoodAdvisor !"
      }
    }
  }
}

# Deleting a localization for a single type with GraphQL

Pass the locale argument in the mutation to delete a specific localization for a single type:

Example mutation

mutation {
  deleteHomepage(locale: "fr") {
    homepage {
      id
      title
    }
  }
}

Example response

{
  "data": {
    "deleteHomepage": {
      "homepage": {
        "id": "2",
        "title": "Bienvenue sur FoodAdvisor !"
      }
    }
  }
}

The response returns the entry that has just been deleted.

# Configuration in production environments

A STRAPI_PLUGIN_I18N_INIT_LOCALE_CODE environment variable can be configured to set the initialization locale for your environment. The value used for this variable should be a string (see full list of available locales (opens new window)).

This is useful when a Strapi app is deployed in production, with the i18n plugin installed and enabled on your content types for the first time. On a fresh i18n plugin installation, en is the default locale. So if the database does not contain any locale, and no STRAPI_PLUGIN_I18N_INIT_LOCALE_CODE is set for the environment, the content of the content types with i18n enabled will be automatically migrated to the en locale. But if the STRAPI_PLUGIN_I18N_INIT_LOCALE_CODE is defined, then the content will be migrated to this locale. Using this environment variable saves you from having to manually update the locale for existing content entries.