Creating an internationalised site with Strapi and Nuxt
Tags: vue, nuxt, strapi, i18n
Published: 2021-05-17 08:13:26

*This post was first published on our site, you can subscribe for more posts like this here: <a href="https://www.pixelhop.io/writing/">https://www.pixelhop.io/writing/</a>.*

We were really excited when Strapi released a new update a few weeks ago that included internationalisation! This post will show you the basics of creating an internationalised site using Strapi and Nuxt.

Watch the video or follow along below.

{% youtube AZZ6M1YXWlk %}

If you are the kind of person that likes to dive straight into code, we have created example repositories for both the Nuxt and Strapi parts:
* Strapi 👉 <a href="https://github.com/pixelhop/strapi-i18n">https://github.com/pixelhop/strapi-i18n</a>
* Nuxt 👉 <a href="https://github.com/pixelhop/nuxt-i18n">https://github.com/pixelhop/nuxt-i18n</a>

## Setting up Strapi

First things first, let's set up our <a href="https://strapi.io/" target="_blank">Strapi CMS</a>. Create a folder to hold this project, and then let's create a new Strapi project using your terminal and the following command:

```bash
npx create-strapi-app strapi --quickstart
```

After npm is finished installing your new Strapi instance, it will start the Strapi dev server and ask you to create an admin login. Go ahead and make your user, and voilà you've got a brand spanking shiny new headless CMS at your disposal. *"Now we're sucking on diesel"**

\*We have been watching far too much Line of Duty.

### Adding some content

What's the point of a CMS if you've got no content? For the purpose of this demo, we are going to create a basic content type.

Head to the Content Types Builder in the Strapi admin: <a href="http://localhost:1337/admin/plugins/content-type-builder/content-types/" target="_blank">http://localhost:1337/admin/plugins/content-type-builder/content-types/</a>

Once there, let's create a new single type by clicking the "+ Create new single type" link in the left sidebar. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aox8usffi4p33vtf4jcu.png)


Let's give our content type a name of "Home", and before clicking "Continue", navigate to the "Advanced settings" tab. Once there, make sure to check the "Enable localisation for the Content-Type" checkbox. You need to do this for any content type you would like to be localised. Once you have done that, it's safe to click "Continue".


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1uubfdzn78md8f4fhac4.png)

### Adding fields

A content type can't hold any content if it doesn't add any fields, so the next step is to add some. We are going to keep it super simple and will add three fields.
Create a text field called "Title":

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l2z8evz26t2ns8z3xuvk.png)

Click "+ Add another field". Then add a rich text field called "Body":

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/us6ajtth65r2gc7q9ql5.png)

Click "+ Add another field", and finally let's add media field called "Image" and set its' type to "Single media" so we only need to upload one image.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/etzvqwbeakelcaaqgejl.png)

Next, go to the "Advanced settings" tab.

Frequently when creating a localised site, you won't want all fields to be localised. By default, you will have to re-enter content for each field in the new language when you switch between locales.

In our case, we want the Image field to keep the same image across all languages.
**Uncheck the "Enabled for localisation" checkbox.**

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wswbt6mtpl6w1h2owdp0.png)

Now we can click "Finish" to create our fields. Take a second to give yourself a pat on the back; you're one step closer to localisation euphoria.

### Adding your locales

Next up, we need to add your desired locales in Strapi. The locales are basically the different languages you want your site to support.

You can get to the locale settings by going to Settings > Internationalisation: <a href="http://localhost:1337/admin/settings/internationalization" target="_blank">http://localhost:1337/admin/settings/internationalization</a> in the Strapi admin.

Once you are there, you should see that you already have one locale, English. You can add another one by clicking the "Add locale" button in the top right.


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gtqhxcq2kg8oggjqpn5r.png)

Choose your desired language in the modal that appears and click the "Add locale" button. Just like that, your CMS is now multilingual! Add as many languages as you need.

### Set content type permissions

We are going to need Nuxt to be able to access our Stapi API to pull in content. By default, Strapi makes any new content type private, so we will need to change that.

Go to Settings, and under the "Users & Permissions Plugin" heading, click "Roles".

Click on the "Public" role, scroll down to the permissions and tick the "find" checkbox under "Home"

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xp7rs1oheeldppth2kv9.png)

Save your changes. This will make the GET [<http://localhost:1337/home>](<http://localhost:1337/home>) endpoint publicly accessible.

### Add some content

We are done setting up Strapi, and now it's time to add some content. Click "Home" under the "Single Types" section of the admin sidebar and enter content for your default locale.

Once you are happy you have created a masterpiece, save and publish it.

Now it's time to get out your foreign language dictionary and enter content for your second locale. To change the current locale, use the dropdown switch found in the right sidebar. We fancied French next.


![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xk39ijk3cd6eb7hn44rd.png)

Make sure to save and publish again. Each locale gets published separately. Repeat for all your locales. Hopefully, none of you decided to add the world's 6500+ languages; otherwise, it might take you longer than expected to finish this tutorial!

## Next up is Nuxt

Your time as a content editor has come to an end, and now you need to get into your favourite hacker pose and start coding.

First up is creating a new Nuxt site. In a new folder, use the terminal to run the following:

```bash
npx create-nuxt-app nuxt
```

Answer the questions via the CLI. In our case, we choose the following:

* Language: **JavaScript**
* Package manager: **NPM**
* UI framework: **TailwindCSS**
* Linter: **ESLint**
* Rendering mode: **Singe Page App**
* Deployment target: **Static**

Once that has done, cd into the new nuxt folder and start the dev server:

```bash
npm run dev
```

Your Nuxt site should now be running at http://localhost:3000/

### Install Nuxt Strapi and i18n

Next, we need to install a few Nuxt modules. The Nuxt Strapi module will allow us to easily communicate with our Strapi API, and the Nuxt i18n module will enable us to add localisation.

Install both the modules:

```bash
npm i nuxt-i18n @nuxtjs/strapi
```

Once that is done, we need to add both modules to nuxt.config.js and configure them with options. First, let's register the modules.

```javascript
// nuxt.config.js
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
  '@nuxtjs/strapi',
  'nuxt-i18n'
],
```

Next, we can configure the i18n module. Add the following to your `nuxt.config.js`:

```javascript
i18n: {
    locales: ['en', 'fr', 'de'],
    defaultLocale: 'en',
},
```

The above tells the i18n plugin that we will have three locales English, French and German. 

Feel free to adjust those to whichever languages you set up earlier in Strapi. **It's important to note the locale codes you use should match the ones used when setting up locales in Strapi.**

You can also set the sites default locale.

Now we can move on to configuring the Strapi module. Add the following:

```javascript
strapi: {
    entities: [
      { name: 'home', type: 'single' },
    ]
},
```
This tells the module that we have a content type called "home" and it's a  single. This will allow us to retrieve it easily later.

### Loading localised content from Strapi into Nuxt

Now it's time to load content from Strapi into Nuxt. In pages/index.vue replace the script section with the following:

```javascript
<script>
export default {
  async asyncData ({ $strapi, i18n }) {
    const home = await $strapi.$home.find({ _locale: i18n.locale })
    return {
      home
    }
  }
}
</script>
```

To load in the data, we can use the asyncData hook that will be run at build time. The Nuxt i18n plugin adds the i18n object to the context object that is passed into the function. This allows us to get the current locale with i18n.locale.

The Nuxt Strapi plugin also injects itself into the asyncData context, so we can then make a request to our API to get the home content. The import part here is how we filter the request by locale to retrieve the correct language:

```javascript
const home = await $strapi.$home.find({ _locale: i18n.locale })
```

All being well, the home content should now be available in your page component. Let's add it to the template. Replace the `pages/index.vue` template with the following:

```html
 <div class="container flex flex-col items-center">
    <div class="flex flex-col items-center">
      <h1 class="mb-4 text-pink-700 font-bold">
        {{ home.Title }}
      </h1>
      <div class="mb-4 text-green-700">
        {{ home.Body }}
      </div>
			<!-- Usually you should store the Strapi host in an env var -->
      <img :src="`http://localhost:1337${home.Image.url}`" class="w-1/4 mb-6" alt="Featured Image">
    </div>
  </div>
```

There should be nothing that surprises you here; we just use standard Vue.js templating to output our page title, body and bind the image URL to the image src.

Hopefully, by this point, you can see the content you added appearing on your page.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vgboqhun85uk8309rhpw.jpeg)

```javascript
<template>
  <nav>
    <ul class="flex">
      <li
        v-for="locale in availableLocales"
        :key="locale"
        class="py-4 px-4 text-xl font-bold"
      >
        <nuxt-link :to="switchLocalePath(locale)">
          {{ locale }}
        </nuxt-link>
      </li>
    </ul>
  </nav>
</template>

<script>
export default {
  computed: {
    availableLocales () {
      return this.$i18n.locales.filter(i => i.code !== this.$i18n.locale)
    }
  }
}
</script>
```

Ok, let's break down what's happening here.
First, we get a list of the available locales in a computed function:

```javascript
 computed: {
    availableLocales () {
      return this.$i18n.locales.filter(i => i.code !== this.$i18n.locale)
    }
  }
```
Next we loop over these in our template using a v-for and we add a nuxt-link to allow us to switch between each site. The switchLocalePath function is added by the Nuxt i18n module returns the current routes localised variants. 

For example, we were on a page "/about" and we called the function like this switchLocalePath('fr') it would return "/fr/about".

See here:
```html
<li
    v-for="locale in availableLocales"
    :key="locale"
    class="py-4 px-4 text-xl font-bold"
>
    <nuxt-link :to="switchLocalePath(locale)">
        {{ locale }}
    </nuxt-link>
</li>
```

And that's the locale switcher finished.

Now let's add it in to our page component:

```javascript
<template>
  <div class="container flex flex-col items-center">
    <div class="flex flex-col items-center">
      <h1 class="mb-4 text-pink-700 font-bold">
        {{ home.Title }}
      </h1>
      <div class="mb-4 text-green-700">
        {{ home.Body }}
      </div>
      <img :src="`http://localhost:1337${home.Image.url}`" class="w-1/4 mb-6" alt="Featured Image">
      <LocaleSwitcher />
    </div>
  </div>
</template>

<script>
import LocaleSwitcher from '~/components/LocaleSwitcher.vue'
export default {
  components: {
    LocaleSwitcher
  },
  async asyncData ({ $strapi, i18n }) {
    const home = await $strapi.$home.find({ _locale: i18n.locale })
    return {
      home
    }
  }
}
</script>
```

You now have an easy way to switch between your locales!

## That's a wrap

And that's it. You should now have a fantastic internationalised site powered by Strapi and Nuxt. We have only touched on the basics, but hopefully, it gives you a good idea of what's involved in building this sort of site.

We are super impressed with how easy the Strapi and Nuxt teams have made this, so big props to them!

As always, if you have any questions or like this post, let us know on Twitter: <a href="https://twitter.com/pixelhopio" target="_blank">@pixelhopio</a>.

If you would like to see more posts like this then subscribe to our site, we promise we won't spam you! You can sign up here <a href="https://www.pixelhop.io/writing/">https://www.pixelhop.io/writing/</a>.