i18n Implementation and Best Practices in Strapi
Tags: strapi, javascript, headlesscms, i18n
Published: 2022-08-24 08:00:00

_This article explains how to implement internalization in Strapi._

**Author: Popoola Temitope**

Have you ever visited a site and noticed that it is not in your native language or any language you speak and understand? It could be frustrating and disappointing at the same time. You would likely be left with only two options in those moments: walk away or find a way to translate the site.

Not being able to read the contents of a website due to differences in language is a massive flaw in the user experience. This experience is not what you wish for your users if you are a Strapi admin or own a Strapi website.
Strapi has a solution for this. The i18n localization Strapi plugin shipped together with version 3.6.0 or higher handles this effectively. Here's [how to build applications for an International Audience Using Strapi i18n Plugin](https://strapi.io/blog/building-applications-for-an-international-audience-using-strapi-i18n-plugin). Now, let's take a moment to explore the customization of this plugin and some of its best practices.

## Localization and Internationalization (i18n and L10)

**i18n** and **L10n** are abbreviations for **internationalization** and **localization**, respectively. Internationalization and localization are software development techniques that aim to serve content to users in the language they understand without making engineering changes. The process involves translating content into different languages and even restructuring pages to suit users.

## Internationalization vs Localization

Often, localization and internationalization are used interchangeably, but they are not the same. There is a difference between the two terms. The difference is a straightforward one.
Internationalization is the process of developing software with the interests of the international community at heart. It involves making software adaptable to the various languages and regions without making engineering changes, while localization adapts internationalized software to focus on a particular part. I can do this by translating text and adding relevant components specific to a particular local. It is only internationalized software that can localize.

## Prerequisites

To follow this tutorial properly, you should have:

- Node.js V14.x or V12 installed on your computer. To check your node version, use the node -v command.
- NPM or Yarn is installed on your computer.
## Localization in Strapi

Localization, as with many other site admin responsibilities, has been simplified for Strapi users. To implement localization in a Strapi application, you need to install and properly configure the Strapi i18n plugin.
Strapi applications running on version 3.6.0 or higher do not need to install this plugin as it is shipped with it already. If your application is running on a version of Strapi lower than what we have here, you can consider migrating your application. If you do not know how to, [click here](https://docs.strapi.io/developer-docs/latest/update-migration-guides/update-version.html#upgrade-your-dependencies). You can also install the plugin using NPM or Yarn commands:

        yarn strapi install i18n
        or
        npm run strapi install i18n

The i18n Strapi plugin requires some configuration to take effect on the Strapi admin dashboard. Let's explore the steps involved in implementing the i18n plugin.
**How to Implement i18n**
To properly understand this, we need some hands-on experience. We will get started by creating a new Strapi project using the following command.

         Yarn create-strapi-app@latest my-project --quickstart
        or
        npx create-strapi-app@latest my-project --quickstart

After a successful installation, Strapi will automatically run and launch the project on your browser, where you will be required to create an admin account.

![Strapi  registration](https://lh5.googleusercontent.com/_L2a79h7ZsQo9vwO3FbQsl8AIsPgTiBpkRKroU0PVhYqWsP8XaRJtPgKuPBqOBYohAQUq0Ghn02vuPGZYK5tPFeMDrDhk3gzytRPWqRJUUSQttuyCwFCUCEn_hc4LfKmbjnIVLp71jeVvPwOKw)


After successfully creating the account, it should redirect you to your Strapi dashboard. i18n is a Strapi Plugin and can be found on the plugin list page. To view your plugins, from the sidebar menu, navigate through **General**→**Plugin** to ensure you have the plugin installed in your project.

![Installed plugin page](https://lh5.googleusercontent.com/4BMydq5Gkwki_9TW4gC_4DRR_ahgH4dqzP139Y2bHK7mXk2EiUW_89IxJxrvKJlKmPisJgoFec4theGR46-eIrA9pxvtdFgeHELSn2gGltGDiDRe5dtRHYUWFKNqcET9rRGIFLIHFQBbTnPF6g)


Next, we need to activate this plugin and create new locales. To do this, from the side bar menu, navigate through **Settings**→**Internationalization**→**Add Locale**.

![](https://lh4.googleusercontent.com/Pv2HE3wpzn_YtDQmhQT4W-z0pAhZugeAAZDzax3fPrXqQ8BvN93eFf8vNLquQTql_05nSjgeZreKg_BL-wYqGWZeQkiuMISjpbJHUhCVpwbh7yA3FDWt59H6LwwwJurC6i2g6cx_FT7h3wDJPA)


Let's add a new locale to test out. I'll add a new French locale. The process is simple. Click on add a **locale**→**select Locale** from the **dropdown**→**save**.

![Adding new locale in strapi](https://lh4.googleusercontent.com/0hSD0NNxi6kgaEYn2NJ6kiBP6K_SBZm9KllSZIHvWkgb414C6XJKoR7dTcvh11XbPTlP-ayRhL1ETOH0Rd3oh-DzWzrwhqkby3rt61zpqTLOdw_eYt3TJRKhgr7gqOPlAFMPz1VKNuhpTNtmzA)

> Note: You can add as many locales as you want for your project.

From the internationalization dashboard, you can delete locales or change the default locale for your project. For this tutorial, we’ll leave the default locale as English.
**Create a Collection Type**
We need some content to test out localization in Strapi. To continue, let’s create a content type and add some content to it. If you're new to Strapi and don't know how to create a collection type, don't worry. we'll walk you through the process.
To create the **Post** content type, follow the instructions below.

1. From the dashboard menu, click on **Content-type Builder**→**create a new collection type**.
2. Enter a display name for your collection type and click Continue. Take note of the API ID field, `localhost:1337/api/posts`, which contains the API endpoint ID to which your content will be accessed.
![Creating new  collection type in strapi](https://lh5.googleusercontent.com/APIPGOCeZHvVxrfDdyfKVoliMo7WC3xVGWDmQ9NHCw0b5OKS_FGF-9UlLLHDbL0wmYbVBZZZfViPcjvr297PUGXtMCJgspd84f-usuzFmbbjjyAQYFMCqpZt0nIglT7ZFnq5usC7JuD62HKGfg)

1. Next, we need to add fields that will hold content the post content. To do that, click on "Add another field".
![Adding fields to collection type](https://lh5.googleusercontent.com/kqWsRfBnDRCHJ6puLZgFiMlFD3Xq-FQUHK9W2j4gsnJM21O1xkE-EBBHRfLnli71rLrCMy6uIQIjYoPU9UXCzezUKtZyMxXrGFbS7p6f58MooHb0RxZ5WgoRPykKWvoz6vtQHL2RPREKhreWyg)

1. Select all the necessary fields for the Post collection type. In our case, our Post content type will have the following fields:
- **Name**: This will have a type of string
- **Body**: This will have a type of rich-text
- **Date**: This will have a type of Datetime
![Adding fields to collection type](https://lh6.googleusercontent.com/MT3jVIj6nTuV0llzNTv-ENY8QVP-U32vPpL_4068ynX1EpwpWH2Sm-YCFKYrToA1bnGJEt-lIQLwSbGxGi-9FRmZ2kkc3Z1TC0i0ijuz4lIvl2pAiZWOdL3aVrDlNLD9Mwa4cyM9D0SqOFrRxg)

1. After adding these fields, click **Save**, and you should have a Post data type in your project.
2. Repeat the process in the image above for the rest of the fields.
3. Save. After saving, we should have a newly created collection type of Post.

**Managing Content**
We need to create our first post content and then look at creating the same Post in different languages or locales with Strapi. Create a new post content by Navigating through **Content Manager** → **Post collection** and click on the "Create an entry".

![Creating new content](https://lh6.googleusercontent.com/ZR6aXDTMzL_ba8D9H6TH-vU8mOFaf7hvlkDP_UlmYkKSphNW6yrv2vA2rlgl1ssP2aGODX9ycimt6L9cFkZAkAcQLbJz2FA0IbBgbQ8E3a_sAqBxghfAKEE9zyPwbm2CPQ2jj_fUDGaDH8RCaw)


Enter all the necessary content for each field and save it. After saving it, click on publish. Now, we have successfully created our Post collection content for the default locale (English).
The Strapi development team did so much work, making Localization very easy to achieve. Some of the excellent features they built into it include:

- Easily edit Post.
- Import content from a previous locale into the newly created Locale.
- Some fields can be set not to change no matter the Locale.
- Editing or creating a unique page structure for a particular Locale.

**Add Locale Content**
Let's explore some of these features above by making our first Post available in French. Click **Post**→select a post→click **edit**. On the editing page, locate Internationalization in the right corner and select French.

![Adding localization content](https://lh4.googleusercontent.com/TgvIyKRXjuYxXiMxSawvmeA7h8e4vYA_I41Fia97OHbtp437w7dMPgrtBRmM9ASby-bA0SugmxUrwzRBuXYepRK5OUfExS1Z9f5FYBuimv-yttYJ-GkptR1jlNxGd6ojtRRKN3zgJEtVh8bRJw)


As shown in the image above, from the left side, select **French** under **locales** and fill in the Post's content from the previous Locale, which happens to be English in our case. This way, you can easily edit into the current language you want.
In my case, I used Google Translate to get a French translation of the content of my test post, as you can see in the image above. The main point to note here is that translating your Post into a new language becomes a lot easier with Strapi.
Once you save the Post, it becomes available in English and French, which is fantastic. One more thing you can do is disable localization in parts of the Post that you want to retain the default language.
**Editing Field’s Locale**
We can edit the Post content type and disable localization for this field. Let's disable localization for the Name field in our Post. Click on **Edit the fields** → **edit** → **advanced setting** →**disable Localization**.
The images below explain more.

![Adding localization content](https://lh4.googleusercontent.com/7fotlohhKpED1k4jzgL03jnOt7O15WWk9nh7vA3rmxPY96s3jttprn8rKuKuXk-fYXs8rF9HCJ3SsJx6iYFb7XiCdXDsLgmcK6qoeW_M37YmPJwuZRsk4_lD_FRYMZvPIBx74oZznO3xG8pVrNzK6XE)


Click on the pen icon as shown in the image below:

![Editing collection type field](https://lh5.googleusercontent.com/RjMJEe2ifkE4-afbpvi_9bGIblz1LrDDlQcd--grAwVa4-dJpjlGJEcAO3iCYwzI7atzhXy5uot6cjmj-O6kyieeowGasdLmmlHDgHJeyKtBGUbrinsCSyve-toGPWUICHslBtpnhrkOD8MmvUw)


Enable localization for the field and click on **Finish**.

![Enable localization for collection type field](https://lh3.googleusercontent.com/dBcbJTk9Qb1B5jLBxnqpo-7cQvKnRG-xH4C2S_n6uiATUWUJriIJz-zOjoabrL8DtnuF2arCvYCbMCmOKWeQIppnJ1t8qwHQ2BM6q60Qouo269wBEuSrdW98Qx07WMjyTw7wBBsl92dUgT9aYPw)


We can further edit the page of our French Local to look unique. We can add features that won't be there in the English locale. I find this very useful.
**Using the API**
You can leverage the beauty of Localization even when working with Strapi content API. We can fetch content specific to a particular locale and do plenty of other cool stuff with the API.
Let's get right into it. For this tutorial, I will be using [Postman](https://www.postman.com/) to interact with my Strapi application.
Getting Localized Content
Let’s make a request to our Strapi endpoint and fetch all content belonging to the default language. In our case, English is the default language, but If you attempt making this request with Postman at this point, you will get an error with an error message like the one we have below.

![Making request to strapi API endpoint](https://lh3.googleusercontent.com/czB8V-LbokD0C9xJ_9U5j9-_tVVpugfZ8IdxbsoCpPrkKqIYwPBFVDsBIMIccjgn6ki5GrjYfJyhJdTz91Vi4vdwfr48mz2Ty_uE0o7u_TNGkMlLyatGchOW1QiC4sudPhamXOB-UNrraFkdOwsz6T8)


Take a look at the response body, and you will notice that the request returned a forbidden error.
http://localhost:1337/api/posts

     {
            "statusCode": 403,
            "error": "Forbidden",
            "message": "Forbidden"
    }

This script is a permission error. We need to edit our post collection to accept requests from unauthenticated users for this tutorial. To do that, click the **settings**→**roles**→**permission**→**application**.

![Setting API Permission](https://lh4.googleusercontent.com/U34Uzur3BpXjM5Pu-KWHLHjMlNcg_Z0GDVZkMIkHmgEYHBqU3gW6NvG8PWWLxzbuejn3V3coJrlPfOQcNyUdasaSh4PmX_oSDTE7EDxecaAe_x5mwR9Vwy0BFHtCtfbOI24fKoLSCmUGENw_qw)


Here, you can choose who should have access. Once more, for this simple tutorial, I will select all.
With this done, let's head over to Postman and make the call again. Now, our request is successful. Did you notice that we fetched the default language of the Post, which is what we wanted?

![Fetching post with default language](https://lh4.googleusercontent.com/Q8z0p1GsQuDv6j1TN_16zj1cIeRT1HZFeF5NYKxWu2Pl07WL_OlP_bjmi-EQzJrzoJxET16tbGWXG_lV2yj-CZPuueEdaybnmwq6ZybyhOmSOH7P-MzTdjZcvF4HXgeFZnLX8y4STQDTkEyGtNMFStA)


With the locale API parameter, we can fetch data belonging to a particular locale. You should pass the locale code along with the call as a parameter. The request should be a GET request. With that said, let's try making a call to the endpoint using Postman to fetch all Post in French version.

![Fetching post in French version](https://lh3.googleusercontent.com/q6idmW-YdeqWpqF4M-ulOgF_b6ZbNIcUb6ISfrmgld66gY9dx-iDGcXpBihMDc3RsO4xEA_guDuGt4uo6x6k_nCY9he9UJebzkb3sDxnd0br3LQweACRRzBHqmSqobjrNPUU4qIpmE7SMBnVm8Y)


Notice the URL we made the call to `localhost:1337/posts?_locale=fr-CA`. The `/post` is the endpoint to fetch posts from our Post collection. We added a prefix of _locale which specifies that we want to fetch contents from a particular locale. fr-CA is specifying that we want to fetch Post from the French locale.

### Creating a New Localized Entry
We can create a localized entry for a post using the content API. We can do this by sending a Post request to content API. The request should contain a locale parameter that will point to the new Locale we intend to create. The code snippet below is an example of how a localized post request should look.

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

If you requested without the locale parameter, the request would create the Post in the default locale. The code below does not contain a locale parameter. This script will create this Post in English, which is the default locale in our case.

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

With this said, let's get our hands dirty. Let's create a new locale through Strapi API using Postman. We will send our request to the Posts endpoint, `localhost:1337/posts`.

![Create in locale content via API request](https://lh4.googleusercontent.com/JxXWqeGucoClDI3SPm6pRsX4c7aR_UgFaGLOHXvFxZ1WefSSEjB7IYJH2UJ6LwP_2EAjsWrx4zqIEnt1wGG72REyp4JhK27ByUVAFj7GDf8rp8IGAt4_6buCdPTE0fKo_Img2vHi5LTerv-PSpo)


As you can see, we have successfully created a post to a new locale using the API. We have just scratched the surface of how you can leverage Localization using the API. For a more detailed guide on how to carry out localization tasks using the API, [click here](https://docs.strapi.io/developer-docs/latest/plugins/plugins-intro.html#usage-with-strapi-content-api).

## Best Practices for Using i18n in Strapi

Some best practices that I can recommend when working with Strapi include the following:

- Browse contents by locales.
- Edit pages or posts in locales and switch between them easily.
- Choose what locale should be displayed first.
- Keep the content the same for all locales.
- Handle editors and writers according to locale.
## Conclusion

By now, you should:

- Have a better understanding of localization and internationalization (i18n and L10),
- Be able to implement localization in Strapi from the dashboard and the API respectively, and
- Understand best practices when implementing localization in Strapi.

*Strapi is an open-source headless CMS built with Node.js and React.js. You can consume the API with any front-end framework of your choice. It adopts Jamstack (a modern web development architecture based on client-side JavaScript, reusable APIs, and prebuilt markup).* [*Check the documentation*](https://docs.strapi.io/developer-docs/latest/getting-started/quick-start.html#_1-install-strapi-and-create-a-new-project) *for more information on Strapi.*