DEV Community

Cover image for Localize your React app with i18next and Gridly
truongtrongtin
truongtrongtin

Posted on

Localize your React app with i18next and Gridly

Are you tired of manually managing translations for your React application? Well, look no further!

In this tutorial, we'll be using Typescript React with Vite to create a multilingual app and setting up a backend with i18next and Gridly to achieve continuous localization.

With i18next–gridly-backend, new translation keys will be sent to Gridly as soon as you save them in your code. It’s easy to translate your strings once they’re inside Gridly.

We'll show you how to create a new React project, set up your Gridly Grid, get the Grid API key and view ID, install the required dependencies, and get a read-only API key for production environments.

Let's get started!

Create your project

In this step, you can use a current React project or create a new one. Here, we’re using Typescript React with Vite. Run the following commands to generate a new project:

npm create vite@latest my-react-app -- --template react-ts
cd my-react-app
npm install
Enter fullscreen mode Exit fullscreen mode

Run npm run dev to start the app. Navigate to http://localhost:5173 in your browser:

Default vite react homepage

We are going to localize this app in English (en), Swedish (se), German (de) and French (fr).

Setup Gridly Grid

Create a Gridly account
Create your company, project, and database.

To add a new Grid, click + Add Grid.

  1. Choose source language en.
  2. Choose target languages sv, de, and fr.
  3. Other columns: remove all.

Create grid from scratch

Click Create to create and navigate to your new Grid.
Grid after sucessfully created

Change the column ID of each column to match the language code.
Change column ID

Delete all placeholder records. Make the Record ID column viewable so it’s easy to see what’s happening.
Show Record ID column

Get Grid API key and view ID

Click the API symbol on the right panel.
API key and view ID on right panel

In your React source code, create a .env.local file. This file is for local usage only and should not be public in your Git repo.

Add .env.local in root:

VITE_GRIDLY_API_KEY=your_api_key
VITE_GRIDLY_VIEW_ID=your_view_id
Enter fullscreen mode Exit fullscreen mode

Warning: Never expose the Grid API key in your production environment because someone can use it to modify your data. Use the read-only API key from the company settings. At the end of the tutorial, we’ll show you how to find this read-only API key.

Install dependencies

Let's install some i18next dependencies:

i18next
react-i18next
i18next-browser-languagedetector
i18next-gridly-backend

npm install i18next react-i18next i18next-browser-languagedetector i18next-gridly-backend
Enter fullscreen mode Exit fullscreen mode

Add the src/i18n.ts file:

import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import GridlyBackend, { GridlyBackendOptions } from 'i18next-gridly-backend';
import { initReactI18next } from 'react-i18next';

const isProduction = import.meta.env.PROD;
const gridlyOptions: GridlyBackendOptions = {
  apiKey: import.meta.env.VITE_GRIDLY_API_KEY,
  viewId: import.meta.env.VITE_GRIDLY_VIEW_ID,
};

i18n
  .use(LanguageDetector)
  .use(GridlyBackend)
  .use(initReactI18next)
  // for all options read: https://www.i18next.com/overview/configuration-options
  .init({
    debug: true,
    fallbackLng: 'en',
    backend: gridlyOptions,
    saveMissing: !isProduction,
  });

export default i18n;
Enter fullscreen mode Exit fullscreen mode

Import in src/main.tsx file:

  import React from 'react';
  import ReactDOM from 'react-dom/client';
  import App from './App';
  import './index.css';
+ import './i18n';

  ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
    <React.StrictMode>
      <App />
    </React.StrictMode>,
  );
Enter fullscreen mode Exit fullscreen mode

First translation

The original src/App.tsx:

import { useState } from 'react';
import './App.css';
import reactLogo from './assets/react.svg';

function App() {
  const [count, setCount] = useState(0);

  return (
    <div className="App">
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src="/vite.svg" className="logo" alt="Vite logo" />
        </a>
        <a href="https://reactjs.org" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Next, we will localize all text, add a language toggle, and update src/App.tsx.

  import { useState } from 'react';
+ import { Trans, useTranslation } from 'react-i18next';
  import './App.css';
  import reactLogo from './assets/react.svg';

+ const languages = [
+   { value: 'en', name: 'English' },
+   { value: 'sv', name: 'Swedish' },
+   { value: 'de', name: 'German' },
+   { value: 'fr', name: 'French' },
+ ];
+
  function App() {
    const [count, setCount] = useState(0);
    const { t, i18n } = useTranslation();

    return (
      <div className="App">
        <div>
          <a href="https://vitejs.dev" target="_blank">
            <img src="/vite.svg" className="logo" alt="Vite logo" />
          </a>
          <a href="https://reactjs.org" target="_blank">
            <img src={reactLogo} className="logo react" alt="React logo" />
          </a>
        </div>
        <h1>Vite + React</h1>
        <div className="card">
          <button onClick={() => setCount((count) => count + 1)}>
-           count is {count}
+           {t('count_is', 'count is {{number}}', { number: count })}
          </button>
          <p>
-           Edit <code>src/App.tsx</code> and save to test HMR
+           <Trans i18nKey="description.part1">
+             Edit <code>src/App.tsx</code> and save to test HMR
+           </Trans>
          </p>
        </div>
        <p className="read-the-docs">
-         Click on the Vite and React logos to learn more
+         {t(
+           'description.part2',
+           'Click on the Vite and React logos to learn more',
+         )}
        </p>
+       <div>
+         {languages.map((lng) => (
+           <button
+             key={lng.value}
+             style={{
+               fontWeight: i18n.resolvedLanguage === lng ? 'bold' : 'normal',
+             }}
+             onClick={() => i18n.changeLanguage(lng.value)}
+           >
+             {lng.name}
+           </button>
+         ))}
+       </div>
      </div>
    );
  }

  export default App;
Enter fullscreen mode Exit fullscreen mode

Save the file and watch as all the new translation keys are sent to Gridly. This works thanks to the i18next option saveMissing: true and the i18next-gridly-backend plugin.
Add new translation

This allows the translator to translate content as soon as the developer saves a new key into the code. This process is called continuous localization!

Next, add the translations for all languages.
Translate the content

Try switching to another language. The translated content for the selected language will be downloaded from Gridly.
Switch language

Read-only API key for production env

To get a read-only API key, go to Company settings on the dashboard, then select API keys.

Create read-only api key

Create a new API key with read-only permission. Use this key in your production environment.

Copy api key

The complete code can be found here.
Watch a live demo here.

Top comments (0)