Our Writing

Creating an internationalised site with Strapi and Nuxt.

Gemma

Gemma /

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.

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:

Setting up Strapi

First things first, let's set up our Strapi CMS. Create a folder to hold this project, and then let's create a new Strapi project using your terminal and the following command:

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"

Yes 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: http://localhost:1337/admin/plugins/content-type-builder/content-types/

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

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".

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":

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

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.

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.

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: http://localhost:1337/admin/settings/internationalization 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.

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"

Save your changes. This will make the GET 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.

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:

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:

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:

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.

// 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:

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:

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:

<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:

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:

 <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.

It's not exactly a thing of beauty, but it is internationalised! At this point, you can test the internationalisation works by manually changing the address to something like this: http://localhost:3000/fr

You should see your content change to the correct language. 🎉

Making a locale switcher

Having multiple locales might be fancy, but you need to let your users know what languages are available. To do that, we need a locale switcher.

Let's make a new component in the components folder called "LocaleSwitcher.vue"

<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:

 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:

<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:

<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: @pixelhopio

Subscribe.

Like what you’re reading? Sign up to recieve our updates straight to your inbox. No spam, unsubscribe anytime!