DEV Community

Rense Bakker
Rense Bakker

Posted on

App translation with React-i18next

In this article I will show you how to translate your React app with React-i18next and give a few examples that you may find useful!

I am using joy ui in this example, but I won't show any configuration for it because it is out of scope. You can see the full code in this code sandbox.

I am also assuming that you already have a react project up and running.

Setup

First of we need to add the react-i18next package and its dependencies:

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

You do not strictly need i18next-http-backend and i18next-browser-languagedetector, however in my experience, almost every React app with translations will want to use these tools sooner or later.

Create config and add i18next provider

Now we need to create a i18n.ts file where we will configure the i18n instance:

// /src/i18n.ts
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import Backend from 'i18next-http-backend'
import LanguageDetector from 'i18next-browser-languagedetector'

i18n
  .use(Backend) // default import from /public/locales/[lang]/[namespace].json
  .use(LanguageDetector) // Detect browser language
  .use(initReactI18next)
  .init({
    fallbackLng: 'en', // Our default language
    debug: true, // Only use this in dev mode
    interpolation: {
      escapeValue: false // We don't need this for React
    }
  })

export  default i18n
Enter fullscreen mode Exit fullscreen mode

Next we need to provide this config to our entire React app using the I18nextProvider:

// /src/main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import { I18nextProvider } from 'react-i18next'
import i18n from './i18n' // import i18n config

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <React.StrictMode>
    <I18nextProvider i18n={i18n} defaultNS={'translation'}>
      <App />
    </I18nextProvider>
  </React.StrictMode>
)
Enter fullscreen mode Exit fullscreen mode

Add the translations files

English translation:

// /public/locales/en/translation.json
{
  "hello": "Hello World!"
}
Enter fullscreen mode Exit fullscreen mode

Dutch translation:

// /public/locales/nl/translation.json
{
  "hello": "Hallo wereld!"
}
Enter fullscreen mode Exit fullscreen mode

Typescript

For extra convenience, we can add a react-i18next.d.ts file so the compiler will know which translation keys are available in our app. This way we get autocomplete for translation keys and it will let us know when we are using a translation key that doesn't exist:

// /src/react-i18next.d.ts
import  'react-i18next'
import translation from  'locales/en/translation.json'

declare module 'react-i18next' {
  interface Resources {
    translation: typeof translation
  }
}

declare module 'react-i18next' {
  interface CustomTypeOptions {
    defaultNS: 'translation'
    resources: {
      translation: typeof translation
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Using translations

To translate a part of our app, we just import that useTranslation hook and let it know which translation key to use:

// /src/App.tsx
import { useTranslation } from 'react-i18next'
import { Typography } from '@mui/joy'

function App(){
  const { t } =  useTranslation()

  return <Typography>{t('hello')}</Typography>
}
Enter fullscreen mode Exit fullscreen mode

More complex scenario's

Variables and pluralization

You might be thinking, what if some part of my text is a variable? Do I have to split the text out into chunks before and after the variable and translate them seperately? The answer is: nope, I18next has you covered!

// translation.json
{
  "welcome_one":  "Welcome {{name}}, you have {{count}} message.",
  "welcome_other":  "Welcome {{name}}, you have {{count}} messages.",
}
Enter fullscreen mode Exit fullscreen mode
// /src/App.tsx
import { useTranslation } from 'react-i18next'
import { Typography } from '@mui/joy'

function App(){
  const { t } =  useTranslation()
  const [name, setName] =  useState('')
  const [count, setCount] =  useState(1)

  // ...

  return <Typography>{t('welcome',  { count, name })}</Typography>
}
Enter fullscreen mode Exit fullscreen mode

You can do a lot of other fancy stuff with i18next as well like context aware translations.

Custom React components

Sometimes you have some text with a link for example, or you may even want to use a custom React component inside a piece of translated text. For this you have to use the Trans component.

// translations.json
{
  "custom_link": "Translate some text with a custom <1>link</1>."
}
Enter fullscreen mode Exit fullscreen mode

Note the <1> placeholder that tells i18next where to expect a custom element.

// /src/App.tsx
import { Link } from '@mui/joy'
import { Trans } from 'react-i18next'

function App(){
  return <Trans  i18nKey="custom_link">
    Translate some text with a custom <Link  href="#link">link</Link>.
  </Trans>
}
Enter fullscreen mode Exit fullscreen mode

Adding a language switcher

Building a language switcher with i18next is very simple, all we have to do is make some buttons and call the i18n.changeLanguage method with the language that we want to switch to:

// /src/App.tsx
import { Typography, IconButton } from '@mui/joy'
import { useTranslation, Trans } from 'react-i18next'

function App(){
  const { t, i18n } = useTranslation()

  return <>
    <Typography>{t('hello')}</Typography>
    <IconButton onClick={() => i18n.changeLanguage('en')} size="sm" variant={i18n.language ==='en' ? 'solid' : 'soft'}>EN</IconButton>
    <IconButton onClick={() => i18n.changeLanguage('nl')} size="sm" variant={i18n.language ==='nl' ? 'solid' : 'soft'}>NL</IconButton>
  </>
}
Enter fullscreen mode Exit fullscreen mode

Links

Top comments (1)

Collapse
 
julianv321 profile image
Julian

Hi, I am having issues with getting the autocomplete to work, even when running your sandbox. When I run it in VS Code and start typing a key in the t() function no suggestions are given. Do you have any idea what could cause this?