Multilingual website with Gatsby and Contentful - Part 2
Tags: gatsby, javascript, react, jamstack
Published: 2019-11-24 12:38:00

⚠️ Sometimes, it happens that graphQL don't find your intl data located in `intl` , run the command `gatsby clean` to remove the cache 🙂

## Prerequisite

- The first part of the tutorial have to work! We previously linked Gatsby and Contentful together 🙂 {% link louisbertin/multilangual-website-with-gatsby-and-contentful-part-1-57ag %}


## React-intl setup

First, you have to install the plugin based on react-intl which offers you to translate your website.

⚠️ I'm French so, I define the other locale to `fr` put your own instead!

- Install your plugin : `npm install --save gatsby-plugin-intl`
- Now, configure your config file : `gatsby-config.js`


```javascript
{
    resolve: `gatsby-plugin-intl`,
    options: {
        // language JSON resource path
        path: `${__dirname}/src/intl`,
        // supported language
        languages: [`en`, `fr`],
        // language file path
        defaultLanguage: `en`,
        // option to redirect to `/en` when connecting `/`
        redirect: true,
    },
},
```

- You can create the `intl` folder into the `src` and create two files corresponding to your locales

```javascript
// src/intl/en.json
{
    "title": "Gatsby English",
    "description": "Project description",
    "author": "@louisbertin",
    "hello": "Hi people!",
    "welcome": "Welcome to your new Gatsby site.",
    "title_page2": "Page two",
    "hello_page2": "Hi from the second page",
    "go_page2": "Go to page 2",
    "welcome_page2": "Welcome to page 2",
    "go_back": "Go back to the homepage",
    "footer_build": "Built with",
    "notfound": {
        "header": "NOT FOUND",
        "description": "You just hit a route that doesn't exist... the sadness."
    }
}
```

```javascript
// src/intl/fr.json
{
    "title": "Gatsby French",
    "description": "Project description",
    "author": "@louisbertin",
    "hello": "Salut les gens!",
    "welcome": "Bienvenue sur votre nouveau site Gatsby",
    "title_page2": "Page deux",
    "hello_page2": "Salut depuis la seconde page",
    "go_page2": "Aller à la page 2",
    "welcome_page2": "Bienvenue sur la page 2",
    "go_back": "Retour à la page d'accueil",
    "footer_build": "Construit avec",
    "notfound": {
        "header": "NOT FOUND",
        "description": "Vous voulez accéder à une route qui n'existe pas..."
    }
}
```

## Build multilingual blog

- To use all keys located in `intl` folder you have to change all your react components and pages to work with react-intl. I'll give you an example with the index page.

```jsx
// pages/index.js
import React from "react"
import { graphql } from "gatsby"

import Layout from "../components/layout"
import SEO from "../components/seo"

import { injectIntl, Link, FormattedMessage } from "gatsby-plugin-intl"

const IndexPage = ({ data, intl }) => (
    <Layout>
        <SEO title={intl.formatMessage({ id: "title" })} />
        <h1>
          <FormattedMessage id="hello" />
        </h1>
        <p>
          <FormattedMessage id="welcome" />
        </p>
    
        <br />
        <Link to="/page-2">
          <FormattedMessage id="go_page2" />
        </Link>
      </Layout>
)

export const query = graphql `
      query ContentFulPosts($locale: String) {
        allContentfulPost(filter: { node_locale: { eq: $locale } }) {
          nodes {
            contentful_id
            title
            path
          }
        }
      }
`
    
export default injectIntl(IndexPage)
```


and with a component page 🙂

```jsx
import React from "react"
import PropTypes from "prop-types"
import { useStaticQuery, graphql } from "gatsby"

import Header from "./header"
import "./layout.css"

import { injectIntl, FormattedMessage } from "gatsby-plugin-intl"

const Layout = ({ children, intl }) => {
  const data = useStaticQuery(graphql`
    query SiteTitleQuery {
      site {
        siteMetadata {
          title
        }
      }
    }
  `)

  return (
    <>
      <Header siteTitle={data.site.siteMetadata.title} />
      <div
        style={{
          margin: `0 auto`,
          maxWidth: 960,
          padding: `0px 1.0875rem 1.45rem`,
          paddingTop: 0,
        }}
      >
        <main>{children}</main>
        <footer>
          © {new Date().getFullYear()}, <FormattedMessage id="footer_build" />{" "}
          {` `}
          <a href="https://www.gatsbyjs.org">Gatsby</a>
        </footer>
      </div>
    </>
  )
}

Layout.propTypes = {
  children: PropTypes.node.isRequired,
}

export default injectIntl(Layout)
```

- Before retrieving the data, you have to create the blog post template. This template has to fetch the current locale to get the correct data.

```jsx
import React from "react"
import { graphql } from "gatsby"

import Layout from "../components/layout"

const BlogPost = ({ pageContext, data }) => (
    <Layout>
        <h1>{data.contentfulPost.title}</h1>
      </Layout>
)

export const query = graphql `
      query ContentFulPost($slug: String, $locale: String) {
        contentfulPost(path: { eq: $slug }, node_locale: { eq: $locale }) {
          path
          node_locale
          title
        }
      }
    `

export default BlogPost
```

- Now, it's time to use gatsby-node file to generate posts pages for each locale!

```jsx
const path = require(`path`)

// pages locale
exports.onCreatePage = ({ page, actions }) => {
    const { createPage, deletePage } = actions
    deletePage(page)
    // You can access the variable "locale" in your page queries now
    createPage({
        ...page,
        context: {
            ...page.context,
            locale: page.context.intl.language,
        },
    })
}

// blog posts
exports.createPages = ({ graphql, actions }) => {
    const { createPage } = actions
    const blogPostTemplate = path.resolve(`src/templates/blog-post.js`)
    // Query for markdown nodes to use in creating pages.
    // You can query for whatever data you want to create pages for e.g.
    // products, portfolio items, landing pages, etc.
    // Variables can be added as the second function parameter
    return graphql(
        `
          query MyQuery {
            allContentfulPost {
              edges {
                node {
                  path
                }
              }
            }
          }
        `
    ).then(result => {
        if(result.errors) {
            throw result.errors
        }

        // Create blog post pages.
        result.data.allContentfulPost.edges.forEach(edge => {
            const path = edge.node.path

            createPage({
                // Path for this page — required
                path: path,
                component: blogPostTemplate,
                context: {
                    slug: path,
                },
            })
        })
    })
}
```

- Then, update graphQL query on index to display the right post

```jsx
export const query = graphql `
      query ContentFulPosts($locale: String) {
        allContentfulPost(filter: { node_locale: { eq: $locale } }) {
          nodes {
            contentful_id
            title
            path
          }
        }
      }
```

- Finally, you can access your posts and multilingual seems to work.. but to test everything we need to have a language switcher!
So, create a component called `Language.js` and place it in the `components` folder of your project

```jsx
import React from "react"
import { IntlContextConsumer, changeLocale } from "gatsby-plugin-intl"

const languageName = {
  en: "English",
  fr: "Français",
}

const Language = () => (
  <div>
    <IntlContextConsumer>
      {({ languages, language: currentLocale }) =>
        languages.map(language => (
          <a
            key={language}
            onClick={() => changeLocale(language)}
            style={{
              color: currentLocale === language ? `yellow` : `white`,
              margin: 10,
              textDecoration: `underline`,
              cursor: `pointer`,
            }}
          >
            {languageName[language]}
          </a>
        ))
      }
    </IntlContextConsumer>
  </div>
)

export default Language
```

You can call the language component in the Header component for example and everything should work!

## In the next post
In the next post I will explain how to deploy your multilingual blog on Netlify!

By the way, you can find my code on [Github](https://github.com/LouisBertin/gatsby-contentful)!