App translation with React-i18next
Tags: webdev, react, i18next, tutorial
Published: 2023-06-08 13:10:32

In this article I will show you how to translate your React app with [React-i18next](https://react.i18next.com/) 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](https://codesandbox.io/p/sandbox/react-i18next-9gog87).

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:
```plain
npm  install react-i18next i18next i18next-http-backend i18next-browser-languagedetector
```
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:
```ts
// /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
```
Next we need to provide this config to our entire React app using the `I18nextProvider`:
```tsx
// /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>
)
```
### Add the translations files
English translation:
```json
// /public/locales/en/translation.json
{
  "hello": "Hello World!"
}
```
Dutch translation:
```json
// /public/locales/nl/translation.json
{
  "hello": "Hallo wereld!"
}
```

## 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:
```ts
// /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
    }
  }
}
```
## Using translations
To translate a part of our app, we just import that `useTranslation` hook and let it know which translation key to use:
```tsx
// /src/App.tsx
import { useTranslation } from 'react-i18next'
import { Typography } from '@mui/joy'

function App(){
  const { t } =  useTranslation()
  
  return <Typography>{t('hello')}</Typography>
}
```
## 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!
```json
// translation.json
{
  "welcome_one":  "Welcome {{name}}, you have {{count}} message.",
  "welcome_other":  "Welcome {{name}}, you have {{count}} messages.",
}
```
```tsx
// /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>
}
```
You can do a lot of other fancy stuff with i18next as well like [context aware translations](https://www.i18next.com/translation-function/context).

### 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.
```json
// translations.json
{
  "custom_link": "Translate some text with a custom <1>link</1>."
}
```
Note the `<1>` placeholder that tells i18next where to expect a custom element.
```tsx
// /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>
}
```

## 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:
```tsx
// /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>
  </>
}
```

## Links
- [Code Sandbox](https://codesandbox.io/p/sandbox/react-i18next-9gog87)
- [React-i18next Step-by-step guide](https://react.i18next.com/latest/using-with-hooks)