DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Build An Application With Internationalization Using Gatsby and Strapi i18n Plugin
Strapi for Strapi

Posted on • Originally published at strapi.io

Build An Application With Internationalization Using Gatsby and Strapi i18n Plugin

This tutorial was updated to v4 by Mark Munyaka.

An application's ability to offer content to users in their localities gradually becomes a necessary functionality for greater user engagement and retention due to the enormous variety of languages spoken by users worldwide in various locations.

The Strapi i18n plugin enables application administrators to store data in many locales and provide it to users within those locales, assisting in developing this capability.

We will learn more about the i18n plugin in this article.

Goal

This article aims to help you understand what Internationalization is and how to implement it in a Strapi application.

To keep this article focused on its primary goal, we will not create a new Strapi application but instead, use this Strapi eCommerce template.

The demo application will be an eCommerce store to sell courses with localized content. The Strapi i18n plugin will serve localized content from two locales to the front-end built with Gatsby.

After finishing the application, it should be similar to this:

If you want to try out the finished app of this tutorial before going through the article, clone the GitHub repo.

What you will learn

This article will help you understand what Internationalization is and how to implement it in a new or existing Strapi application.

If you are new to Strapi, Strapi is easy to learn and feature-rich headless content management system (CMS) with support for PostgreSQL, MongoDB, SQLite, MySQL, and MariaDB. Check out the Strapi: Quickstart guide.

Prerequisites

To follow along with this tutorial, you need some knowledge of the following:

  • JavaScript
  • Node.js
  • GraphQL
  • Shell (Bash)

Install this software:

  • Node v14.x.x or v16.x.x. Download Node from the Download | Node.js page. I used Node v16.14.2.
  • npm or yarn. npm ships with your Node installation. If you prefer yarn, install it as an npm package. Check Installation | Yarn. I used yarn v1.22.15.

For a full rundown of all the requirements needed to run a Strapi app, check out the Hardware and Software requirements.

Introduction

So far, you have seen the word Internationalization reoccur all over this article. Let's take a deep dive to understand Internationalization.

According to the W3C definition, internationalization is "the design and development of a product, application or document content that enables easy localization for various target audiences that vary in culture, region, or language."

You often find Internationalization shortened to i18n, with "i" and "n" representing the first and last letters. In contrast, 18 represents the total number of letters when "i" and "n" are subtracted.

While Internationalization refers to designing a product for various target audiences, localization can be narrowed to adapting a product, application, or document content to meet the language, cultural needs, and other requirements of a specific target market (a locale).

One important thing to note is that the application of Internationalization and localization, as defined above, varies across different products as each product has its target audience across other localities with differing cultural requirements.

You will find the word locale used in various parts of this article. A locale refers to the language used to create the content.

For developers managing the content of their application using Strapi, the i18n plugin provides admins with the means of localizing the content served to their end-users.

Backend Setup

Step 1: Create Strapi Project

For this tutorial, we'll use the eCommerce template developed by RΓ©mi from the Strapi Team. We will connect a Gatsby app to it for fetching the pre-stored data.

Note:
For this tutorial, we will use yarn as the package manager. You can, however, use npm if preferred.

In your terminal, create an empty project directory to store your project. This folder would serve as the root folder. The front-end and back-end code will be stored in subdirectories. I will name my project directory bluedolphin.

$ mkdir bluedolphin
Enter fullscreen mode Exit fullscreen mode

Change the directory to the project directory bluedolphin.

    $ cd bluedolphin
Enter fullscreen mode Exit fullscreen mode

Enable source control for your project by initializing a git repository.

 /bluedolphin $ git init
Enter fullscreen mode Exit fullscreen mode

Create a strapi application with the eCommerce template.

/bluedolphin $ yarn create strapi-app backend --quickstart --template @strapi/template-ecommerce@1.0.0 ecommerce
Enter fullscreen mode Exit fullscreen mode

This command creates your Strapi app in the folder named backend. The --quickstart flag sets up your Strapi app with an SQLite database. The --template flag retrieves the specific template to install in your app. In this case, we retrieved the strapi-template-ecommerce, hosted by Strapi on GitHub.

After a successful installation, the CLI would automatically start the Strapi application.
Open the Strapi Admin Registration interface from your web browser at http://localhost:1337/admin. Create the admin user for your Strapi application and click Let's Start.

Strapi Admin Registration

The eCommerce template you just cloned contains the following:

  • Three prebuilt collection types:
    • Categories
    • Users
    • Products
  • Entries (sample data) for the collection types above
  • Media assets

To view the entries for each collection type, select Content Manager from the left-hand side menu of your dashboard. You can also see the media assets by clicking on the Media Library under PLUGINS.

Strapi Dashboard

Feel free to modify the existing data. They provide a good starting point for building this project.

Step 2: Set up the GraphQL plugin

Stop the Strapi development server by pressing Ctrl plus C on your keyboard.

Next, install the GraphQL plugin to add GraphQL support to this Strapi app.

/bluedolphin $ cd backend
/bluedolphin/backend $ yarn strapi install graphql
Enter fullscreen mode Exit fullscreen mode

After installing the plugin, you can start your Strapi server.

/bluedolphin/backend $ yarn develop
Enter fullscreen mode Exit fullscreen mode

You can access your project's GraphQL playground at http://localhost:1337/graphql to write queries and mutations for working with the existing data.

Step 3: Configure Strapi Internationalization (i18n) Plugin

The Internationalization plugin gives Strapi application admins the feature to create, manage and distribute localized content across different languages, also known as locales.

Rather than automatically translate delivered content, the i18n plugin allows developers to fetch the right content based on the user's locale.

Note:
The i18n plugin ships by default with Strapi applications running on version 4.x.x. Please refer to the installation section of the plugin documentation.

By default, the i18n plugin has English (en) as the default locale. However, more locales and their respective content can be added using the admin panel or the Strapi Content API.
We will add one more locale to the Categories content-type.

Using the Admin panel, navigate to the Settings page of your Strapi application, then click on the Internationalization option within the Global Settings.

Internationalization Settings

Step 4: Add A New Locale

  • Locate and click the + Add new locale button at the top right corner to open the Add new locale modal.
  • Using the Locales dropdown, select a new locale. > Note: > This tutorial will be using the French (fr) locale. You can, however, use any other locale if you prefer.
  • Select French (fr) in the Locales drop-down, and the Locale display name will be automatically updated to French (fr).
  • Click on βœ“ Save. Locale Configuration

If you successfully added the French (fr) locale you should see it listed under English (en) in the list of locales.

Step 5: Enable Content-Type Localization

By default, the content types within the cloned eCommerce template do not have localization enabled.

  • Click on Content-Types Builder in your Strapi dashboard.
  • Select the content type to be localized. Start with the Category content-type.
  • Click the edit pencil icon of the chosen content type to open the Edit Category modal.
  • Click the Advanced Settings tab from the edit modal, then enable localization using the checkbox at the bottom of the modal.
  • Click on Finish and wait for your Strapi server to restart.
  • Next, enable localization for the Product content-type. Enable Localization for the Content-Type

Step 6: Add Locale Content

As stated earlier, the i18n plugin does not automatically translate content stored with Strapi into different locales; instead, it gives admins the feature to store and deliver multiple locale entries.

We will create French (fr) locale entries for the Category and Product collection types then link the products to their respective categories to work with the i18n plugin. This should give you a working idea when implementing Internationalization with the i18n plugin in your application.

  • Navigate to the Content Manager page
  • Select Product under COLLECTION TYPES
  • Click on the first entry, e.g., Rust, to edit it.
    Select Product to add Locale content

  • In the Edit Product menu, go to the INTERNATIONALIZATION section on the right-hand side.

  • Click on the Locales drop-down and select French (fr).

  • In the INTERNATIONALIZATION section, click on Fill in from another locale and confirm by selecting Yes, fill in to automatically update your entry with the data from the English (en) locale.

  • Append the slug string with -fr so that rust becomes rust-fr.

  • Translate the description for the product entry into French (fr).

  • Click on Save.
    Localized Product

  • Using the same procedure, add French (fr) locale content entries for the remaining products.

  • Next, create French (fr) locale entries for the Category collection.

  • Link the products to their respective categories using the products drop-down in the RELATION section, then Save.

  • On completion, you should have a French (fr) collection of Categories with the linked products.

  • Navigate to the Category collection-type page to see a list of all categories created in the default English (en) locale.

  • Click the locale dropdown at the top-right to switch from the default English (en) locale to the recently created French (fr) locale.
    French (fr) locale Category collection

The Strapi backend to your application is now set.

Frontend Setup

Step 7: Install Gatsby

Now that the backend is set, open up a new terminal and navigate to the root of your project folder bluedolphin.

$ cd /bluedolphin
Enter fullscreen mode Exit fullscreen mode

Launch the Gatsby installer to create a Gatsby application with the name frontend from your terminal using the command below:

/bluedolphin $ npm init gatsby
Enter fullscreen mode Exit fullscreen mode

After the installation process, run the newly created Gatsby application's development server to ensure the installation was successful.

 /bluedolphin $ cd frontend
Enter fullscreen mode Exit fullscreen mode

Step 8: Add Wrapper, Header, and Footer Components

Create a components folder within the src directory.

/bluedolphin/frontend $ mkdir src/components
Enter fullscreen mode Exit fullscreen mode

Create a layout.js file in the components folder.

/bluedolphin/frontend $ touch src/components/layout.js
Enter fullscreen mode Exit fullscreen mode

Copy and paste the following code into the layout.js file.

// ./src/components/layout.js

    import React from "react";
    import { Helmet } from "react-helmet";

    const Layout = ({ children }) => (
        <div>
        <Helmet defer={false}>
            <link
            rel="stylesheet"
            href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css"
        integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l"
            crossorigin="anonymous"
            />
        </Helmet>
        {children}
        </div>
    );

    export default Layout;
Enter fullscreen mode Exit fullscreen mode

The code above creates a wrapper using React-helmet to wrap each page with styles from Bootstrap using a CDN link.

Create a header.js file in the components folder for the site's header.

 /bluedolphin/frontend $ touch src/components/header.js
Enter fullscreen mode Exit fullscreen mode

Add the following code to your header.js file:

    // ./src/components/header.js

    import React from "react";
    import styled from "styled-components";
    import { Link } from "gatsby";

    const Header = styled.header`
      height: 55px;
      background-color: #1a124b;
      width: 100%;
      color: white;
      display: flex;
      align-items: center;
      padding: 0 2rem;
      box-shadow: 0 2px 3px white;
      nav {
        padding-top: 10px;
        display: flex;
        justify-content: space-between;
        width: 100%;
        h5 {
          font-size: 1.1rem;
        }
      }
    `;

    const HeaderComponent = (props) => (
      <Header>
        <nav>
          <div>
            <Link to="/">
              <h5> STRAPI ECOM APP </h5>
            </Link>
          </div>

          <div>
            <p>{props.about}</p>
          </div>
        </nav>
      </Header>
    );

    export default HeaderComponent;
Enter fullscreen mode Exit fullscreen mode

This creates a header that will be displayed across all pages of your Gatsby app. The title I used for the app is "STRAPI ECOM APP". The props.about will help in rendering locale-specific text using a conditional operator when the Header component is used.

Create a footer.js file in the components folder for the site's footer.

 /bluedolphin/frontend $ touch src/components/footer.js
Enter fullscreen mode Exit fullscreen mode

Add the following code to your footer.js file:

    // ./src/components/footer.js

    import React from "react";
    import styled from "styled-components";

    const Footer = styled.footer`
      height: 55px;
      background-color: #1a124b;
      width: 100%;
      position: fixed;
      bottom: 0;
      color: white;
      display: flex;
      align-items: center;
      justify-content: center;
      h5 {
          padding-top : 10px;
          font-size: 1.2rem;
      }
    `;

    const FooterComponent = (props) => (
      <Footer>
        <h5>
          {" "}
          Designed and built by{" "}
          <a href="https://github.com/Marktawa"> Mark Munyaka </a>{" "}
        </h5>
      </Footer>
    );

    export default FooterComponent;
Enter fullscreen mode Exit fullscreen mode

This code creates a footer to be displayed across all pages on your site. The props.info will help render locale-specific text using a conditional operator when the Footer component is imported.

Step 9: Add Styling

Create a styles.js file in the src directory.

/bluedolphin/frontend $ touch src/styles.js
Enter fullscreen mode Exit fullscreen mode

This stylesheet is where you would keep all CSS-in-JS styles created using styled-components.
Copy and paste the following code into styles.js.

    // ./src/styles.js

    import styled from "styled-components";

    export const Cards = styled.ul`
        display: grid;
        padding-top: 1rem;
        padding-bottom: 5rem;
        grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
        grid-gap: 2rem 1rem;
        place-items: center;
    `;

    export const Item = styled.div`
        height: 400px;
        width: 300px;
        border-radius: 7px;
        box-shadow: 0 2px 3px #c0c0c0;
        background: #fff;
        color: #1d1b84;
        text-align: center;
        div {
        padding: 0.1rem 0.5rem;
            h4 {
                text-align: center;
                font-weight: 600;
            }
        }
    `;

    export const Button = styled.button`
        background: #1d1b84;
        border: 1px solid #1d1b84;
        color: #fff;
        border-radius: 3px;
        padding: 0.3rem 1rem;
    `;

    export const Image = styled.img`
        height: 250px;
        width: 250px;
        object-fit: contain;
    `;

    export const Flex = styled.div`
        display: flex;
        width: 100%;
        align-items: center;
        flex-direction: ${(props) => props.direction};
        justify-content: ${(props) => props.justify};
    `;

    export const Banner = styled.div`
        padding: 0.4rem 0.5rem;
        text-align: center;
        h4 {
        font-weight: 600;
        }
    `;

    export const CategoryCtn = styled.div`
        width: 20rem;
        padding: .5rem 1rem;
        background: #fff;
        margin: 1rem;
        border-radius: 5px;
        box-shadow: 0 2px 3px #c0c0c0;
    `

    export const Container = styled.div`
        background: #ebf4fd;
    `;
Enter fullscreen mode Exit fullscreen mode

The styles exported in the file above would be used when creating other components.

Step 10: Configure Gatsby dependencies

Install the following extra dependencies needed for this Gatsby application:

/bluedolphin/frontend $ yarn add gatsby-source-graphql moment react-icons styled-components babel-plugin-styled-components
Enter fullscreen mode Exit fullscreen mode

Modify the gatsby-config.js file in the root of your frontend folder with the code below to register the installed plugins.

    // ./gatsby-config.js

    require("dotenv").config({
      path: `.env.${process.env.NODE_ENV}`,
    })

    module.exports = {
        siteMetadata: {
        title: "i18n-frontend",
        },
        plugins: [
        "gatsby-plugin-react-helmet",
        {
            resolve: "gatsby-plugin-manifest",
            options: {
            icon: "src/images/icon.png",
            },
        },
        { resolve: `gatsby-plugin-styled-components`},
        {
            resolve: "gatsby-source-graphql",
            options: {
                // Arbitrary name for the remote schema Query type
                typeName: "STRAPI",
                // Field for the remote schema. You'll use this in your Gatsby query
                fieldName: "strapi",
                url: `${process.env.GATSBY_STRAPI_GRAPHQL_ENDPOINT}/graphql`,
            },
        },
        ],
    };
Enter fullscreen mode Exit fullscreen mode

In the configuration file above, you have registered styled-components for styling the application.
Also, using the gatsby-source-graphql plugin, you configured the Gatsby application to connect with your Strapi Application using its GraphQL API endpoint stored in environment variables.

You probably don't have the GATSBY_STRAPI_GRAPHQL_ENDPOINT environment variable yet.
Create a .env.development file in the root folder of your Gatsby application.

 /bluedolphin/frontend $ touch .env.development
Enter fullscreen mode Exit fullscreen mode

Add the following code to .env.development.

    # ./.env.development

    GATSBY_STRAPI_GRAPHQL_ENDPOINT="http://localhost:1337"
Enter fullscreen mode Exit fullscreen mode

Next, restart the Gatsby development server. Press Ctrl plus C on your keyboard to stop the server and then start it using yarn start.

    ^C
    /bluedolphin/frontend $ yarn start
Enter fullscreen mode Exit fullscreen mode

This ensures the remote Strapi schema will be merged into the Gatsby application schema.

Step 11: Test GraphQL

Let's test our Gatsby application and see if it can retrieve data from the Strapi backend.
Use the GraphQL editor for your Gatsby application at http://localhost:8000/___graphql and test the GraphQL Query in the image below.

Gatsby GraphiQL Playground

In the GraphiQL playground above, you can observe the result of the getData query executed to retrieve data from your running Strapi application. Also, the GraphiQL playground shows the Strapi fields that have been merged in the Gatsby application schema.

Now that the Strapi schema has been merged with your Gatsby application schema, you can perform GraphQL operations directly from the application.

Step 12: Fetch data from Strapi API

Create a gatsby-node.js file in the root folder of the gatsby application.

/bluedolphin/frontend $ touch gatsby-node.js
Enter fullscreen mode Exit fullscreen mode

Copy and paste the code snippet below into gatsby-node.js:

    // ./gatsby-node.js

    const path = require("path");

    exports.createPages = async ({ graphql, actions }) => {
        const { createPage } = actions;

        const categoriesQuery = await graphql(`
        query getData {
            strapi {
                categories(locale: "all") {
                    data {
                        id
                        attributes {
                            name
                            slug
                            locale
                            localizations {
                                data  {
                                    attributes {
                                        locale
                                    }
                                }
                            }
                            products {
                                data {
                                    id
                                    attributes {
                                        locale
                                        title
                                        description
                                        price
                                        slug
                                        image {
                                            data {
                                                attributes {
                                                    url
                                                    provider
                                                    width
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        `);

        // Template to create dynamic pages from.
        const categoryTemplate = path.resolve(`src/pages/categories.js`);
        const homeTemplate = path.resolve(`src/pages/home.js`)

        categoriesQuery.data.strapi.categories.data.forEach(
            (category) => {
                if (category.attributes.localizations.data.length > 0) {
                    category.attributes.localizations.data.forEach((localization) => {
                        localization.attributes.locale
                        createPage({
                            path: `category/${category.id}/${localization.attributes.locale.toLowerCase()}`,
                            component: categoryTemplate,
                            context: {
                                slug: category.attributes.slug, 
                                name: category.attributes.name, 
                                id: category.id, 
                                products: category.attributes.products, 
                                locale: localization.attributes.locale,
                                localizations: category.attributes.localizations
                            },
                        });
                        createPage({
                            path: `home/${localization.attributes.locale.toLowerCase()}`,
                            component: homeTemplate,
                            context: {
                                locale: localization.attributes.locale,   
                            },
                        })
                    })
                }

                createPage({
                    path: `category/${category.id}/${category.attributes.locale}`,
                    component: categoryTemplate,
                    context: { 
                        slug: category.attributes.slug, 
                        name: category.attributes.name, 
                        id: category.id, 
                        products: category.attributes.products, 
                        locale: category.attributes.locale,
                        localizations: category.attributes.localizations 
                    },
                });
                createPage({
                    path: `home/${category.attributes.locale}`,
                    component: homeTemplate,
                    context: {
                        locale: category.attributes.locale,   
                    },
                })
            }
        );
    };
Enter fullscreen mode Exit fullscreen mode

The code above fetches data from the Strapi application and creates dynamic pages with the fetched data.

You will observe the following operations below performed to retrieve data from the Strapi application from the file above:

  1. The getData query is executed with the locale parameter set to fetch all categories content-types from the Strapi API.
  2. An iteration is made on each category contained in the returned array.
  3. Each time the iteration is made, a dynamic page is created using Gatsby's create page API with the product.js component as a template, passing the locale and id of the category into the component as props.

Note:
The path option specifies the URL of each of the dynamic pages. In our use case, each dynamic page created is differentiated using the unique category id generated by Strapi.

In every iteration, we also check if the category has localized content. The Strapi Content API would return an empty array if a collection type has localization enabled but has no localized content.

If a collection type has localized content in our use case, a new dynamic page is created to display content in that locale.

Before we restart the Gatsby development server for the new changes in the gatsby-node.js file to take effect, we will create the category template and a home page to display all categories.

Step 13: Add Categories Page

Create a categories.js file in the src/pages directory.

/bluedolphin/frontend $ touch src/pages/categories.js
Enter fullscreen mode Exit fullscreen mode

Copy and paste the following code into your categories.js file:

    // ./src/pages/categories.js

    import React, { useState } from "react";
    import { Cards, Item, Button, Image, Flex, Banner, Container } from "../styles";
    import { graphql, Link, navigate } from 'gatsby'

    import Header from "../components/header";
    import Footer from "../components/footer";
    import Layout from "../components/layout";

    const shrinkText = (text, length) => {
        let txt = text.split(" ");

        if (txt.length < 7) {
            return text;
        }

        return [...txt.splice(0, length), '...'].join(" ")
    };

    const Index = ({ pageContext, data }) => {
        const { name, locale, id } = pageContext;
        const [currentLang, setCurrentLang] = useState("en")
        const { categories } = data.strapi

        return (
            <Layout>
                <Header about={locale === "fr" ? "Γ€ propos" : "About" } />
                <Container>
                    <Banner>
                        <br />
                        <h4 style={{ textAlign: "center" }}>{name} ({locale === "fr" ? "Français" : "English"})</h4>

                        <p>{locale === "fr" ? " Afficher les produits en:" : " Display products in:"}
                            {categories.data[0].attributes.localizations.data.map((localization) =>
                                <Link to={`/category/${id}/${localization.attributes.locale.toLowerCase()}`} >
                                    <span style={{ padding: "0.5rem" }} > {localization.attributes.locale === "fr" ? "French" : "Anglais"} </span>
                                </Link>
                            )}
                        </p>
                    </Banner>
                    <hr />

                    <Cards>
                        {categories.data[id - 1].attributes.products.data.map((product) => (
                            <Item key={product.id}>
                                <div>
                                    <Image
                                        src={
                                            `http://localhost:1337${product.attributes.image.data.attributes.url}`
                                        }
                                    />
                                    <h5> {product.attributes.title} </h5>
                                    <p style={{ opacity: ".8" }} >{shrinkText(product.attributes.description, 4)} </p>

                                    <Flex direction="row" justify="space-between">
                                        <div>
                                            <p style={{ textAlign: "left" }}>
                                                {locale === "fr" ? ` € ${product.attributes.price}` : ` $ ${product.attributes.price}`}
                                            </p>
                                        </div>

                                        <div>
                                            <Button>{locale === "fr" ? "Acheter Maintenant" : "Buy Now"}</Button>
                                        </div>
                                    </Flex>
                                </div>
                            </Item>
                        ))}
                    </Cards>
                    <Footer info={locale === "fr" ? "Conçu et Construit par" : "Designed and Built by" } />
                </Container>
            </Layout>
        );
    };

    export const query = graphql`
        query fetchLocaleData($locale: STRAPI_I18NLocaleCode) {
            strapi {
                categories(locale: $locale) {
                    data {
                        id
                        attributes {
                            name
                            slug
                            locale
                            localizations {
                                data {
                                    attributes {
                                        locale
                                    }
                                }
                            }
                            products(sort: "id", pagination: {pageSize: 20}) {
                                data {
                                    id
                                    attributes {
                                        locale
                                        title
                                        description
                                        price
                                        slug
                                        image {
                                            data {
                                                attributes {
                                                    url
                                                    provider
                                                    width
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    `

    export default Index;
Enter fullscreen mode Exit fullscreen mode

Going through the code block above, which makes up the category component, you will observe the following:

  • Although the page is created dynamically using the createPages API, the component makes an extra graphQL query to fetch localized content using the locale value passed in from the createPages API when the page is created dynamically.
  • All available locales are displayed at the top banner, allowing users to click on the links to view the page content in another locale.

Restart your Gatsby development server for the changes to take effect:

    ^C
    /bluedolphin/frontend $ yarn start
Enter fullscreen mode Exit fullscreen mode

Navigate to http://localhost:8000/category/1/en, it should display the product page with an id of 1, having Back as the category name as shown below:

Products Page for Backend Category

The image above shows all products within the Back category served in the English (en) locale. The top banner contains links to other pages containing products for the same category but in a different locale.

For example, clicking the fr link would display products in the French locale as shown below:

Products Page for Backend Category (French Locale)

Step 14: Add Home Page

Next, create a home page that loads all categories with links to their products.
Create a home.js file within the src/pages directory.

    /bluedolphin/frontend $ touch src/pages/home.js
Enter fullscreen mode Exit fullscreen mode

Copy and paste the following code into your home.js file:

    // src/pages/home.js

    import React from "react";
    import { graphql, useStaticQuery, Link } from "gatsby";
    import { FiCalendar, FiBook } from "react-icons/fi";
    import moment from "moment";

    import {
        Container,
        Flex,
        Banner,
        Cards,
        CategoryCtn,
    } from "../styles";
    import Header from "../components/header";
    import Footer from "../components/footer";
    import Layout from "../components/layout";

    const Index = ({ pageContext }) => {
        const { locale } = pageContext;
        const { strapi } = useStaticQuery(graphql`
        query fetchAllCourses($locale: STRAPI_I18NLocaleCode) {
            strapi {
                categories(locale: $locale) {
                    data {
                        id
                        attributes {
                            name
                            createdAt
                            slug
                            locale
                            localizations {
                                data {
                                    attributes {
                                        locale
                                    }
                                }
                            }
                            products(sort: "id", pagination: {pageSize: 20}) {
                                data {
                                    id
                                }
                            }
                        }
                    }
                }
            }
        }
        `);

        return (
            <Layout>
                <Header about={locale === "fr" ? "Γ€ propos" : "About"} />
                <Container>
                    <Banner>
                        <h4> {locale === "fr" ? "MAGASIN DE COURS STRAPI" : "STRAPI COURSE STORE"} </h4>
                        <p> {locale === "fr" ? "Un Magasin De Cours Avec Prise En Charge De l'Internationalisation" : "A Course Store With Support For Internationalization"} </p>
                        {locale === "fr" ?
                            <p>
                                Afficher les catΓ©gories en: 
                                <Link to={`/home/en`}> Anglais</Link>
                            </p>
                            :
                            <p>
                                Display categories in:  
                                <Link to={`/home/fr`}> French</Link>
                            </p>}
                    </Banner>
                    <hr />

                    <Cards >
                        {strapi.categories.data.map((category) => (
                            <CategoryCtn key={category.id}>
                                <div>
                                    <Link to={`/category/${category.id}/${locale}`}>
                                        <h5> {category.attributes.name} </h5>
                                    </Link>
                                    <Flex
                                        direction="row"
                                        style={{
                                            opacity: ".8",
                                        }}
                                    >
                                        <div
                                            style={{
                                                marginRight: ".3rem",
                                            }}
                                        >
                                            <FiCalendar size={19} />
                                        </div>

                                        <div>{locale === "fr" ? "AjoutΓ©" : "Added"} {moment(category.attributes.createdAt).format("MM/DD/YYYY")}</div>
                                    </Flex>
                                    <hr />
                                    <br />

                                    <Flex direction="row">
                                        <div
                                            style={{
                                                marginRight: ".3rem",
                                                display: "flex",
                                                justifyContent: "center",
                                                alignItems: "center",
                                            }}
                                        >
                                            <FiBook size={19} />
                                        </div>

                                        <div
                                            style={{
                                                display: "flex",
                                                justifyContent: "center",
                                                alignItems: "center",
                                            }}
                                        >
                                            {category.attributes.products.data.length} {locale === "fr" ? "Cours Disponibles" : "Courses Available"}
                                        </div>
                                    </Flex>
                                </div>
                            </CategoryCtn>
                        ))}
                    </Cards>
                    <Footer info={locale === "fr" ? "Conçu et Construit par" : "Designed and Built by"} />
                </Container>
            </Layout>
        );
    };

    export default Index;
Enter fullscreen mode Exit fullscreen mode

The home page you have just created displays all categories with a clickable link to each one. It supports internationalization, so if you navigate to localhost:8000/home/en you'll find the English version of the page, and navigating to localhost:8000/home/fr gives you the French version.

Step 15: Test App

Restart your server for the changes to take effect:

    ^C
    /bluedolphin/frontend $ yarn start
Enter fullscreen mode Exit fullscreen mode

Navigate to your Gatsby application's homepage, localhost:8000/home/en. You should see the categories fetched from your Strapi application if it works correctly.

Home Page in English

For the French homepage version, go to localhost:8000/home/fr. Likewise, all the categories should be visible and link you to the French version of the category pages.

Home Page in French

At this point, clicking on any categories would navigate you to the category page showing the list of products within that category.

Conclusion

Huge congrats to you.! This article taught you about Internationalization and localization and how the i18n plugin helps you achieve localization in your Strapi application regarding stored content.

We explained what localization and Internationalization are. We cloned an existing Strapi application using the eCommerce template. Then, we created a new locale and enabled localization on the current content types from the admin panel.

After this, we added entries for Products and Categories collection types in the French (fr) locale. And created a new Gatsby application to fetch localized data from the Strapi application using the GraphQL API.

The source code for this tutorial is available for you to test.

What next?

Check out some deployment guides if you want to know how to deploy this app in production. Add some more functionality to this app using plugins in the Strapi Marketplace.
You can also use Localazy instead of translating your Strapi entries manually. Check out this tutorial to get started with Localazy and Strapi.

Top comments (0)

Timeless DEV post...

How to write a kickass README

Arguably the single most important piece of documentation for any open source project is the README. A good README not only informs people what the project does and who it is for but also how they use and contribute to it.

If you write a README without sufficient explanation of what your project does or how people can use it then it pretty much defeats the purpose of being open source as other developers are less likely to engage with or contribute towards it.