React, i18next and translations in Sleekdocs
i18next and react-i18next can load a React app's language files over HTTP when the app starts. Point them at the files Sleekdocs Translate deploys, and translators can change texts without a new build of the app.
What you need
- A React app. This guide uses Vite, React 19, i18next 26, react-i18next 17 and i18next-http-backend 4.
- A Sleekdocs Translate project with at least one language deployed to an environment.
Sleekdocs deploys each language to its own file on the CDN, one per environment:
https://cdn.sleekdocs.com/<project>/<environment>/<language>.json
The environment is dev, test or prod, and the language is its code, such as sv. Sleekdocs Translate shows the address of your own project when you deploy.
Create the app and install i18next
Skip the first two lines if you already have an app.
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install i18next react-i18next i18next-http-backend
One address per environment
Vite reads .env.development when you run npm run dev and .env.production when you run npm run build. Put the CDN address of each environment there:
# .env.development
VITE_TRANSLATIONS_URL=https://cdn.sleekdocs.com/<project>/dev/
# .env.production
VITE_TRANSLATIONS_URL=https://cdn.sleekdocs.com/<project>/prod/
For a test build, add .env.test with the test address and build with vite build --mode test.
Configure i18next
Create src/i18n.ts. The backend replaces {{lng}} with the language code:
import i18next from 'i18next';
import Backend from 'i18next-http-backend';
import { initReactI18next } from 'react-i18next';
i18next
.use(Backend)
.use(initReactI18next)
.init({
lng: 'en',
fallbackLng: 'en',
backend: {
loadPath: `${import.meta.env.VITE_TRANSLATIONS_URL}{{lng}}.json`,
},
});
export default i18next;
A deployed file only holds the texts that are translated, so set fallbackLng to your project's primary language: a key that's missing in Swedish is then shown in English.
Import it in src/main.tsx, and wrap the app in Suspense, which shows nothing until the first language file has loaded:
import { StrictMode, Suspense } from 'react';
import { createRoot } from 'react-dom/client';
import './i18n';
import App from './App';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<Suspense fallback={null}>
<App />
</Suspense>
</StrictMode>,
);
Use the texts
useTranslation gives a component the t function, and i18n.changeLanguage loads and switches to another language:
import { useTranslation } from 'react-i18next';
export default function App() {
const { t, i18n } = useTranslation();
return (
<main>
<h1>{t('checkout.title')}</h1>
<button onClick={() => i18n.changeLanguage('en')}>English</button>
<button onClick={() => i18n.changeLanguage('sv')}>Svenska</button>
</main>
);
}
i18next finds checkout.title in both flat and nested files, so either of the project's file formats works; Flat or nested JSON for translation files? explains the difference. Placeholders are written {{name}} in the texts and filled in with t('greeting', { name }).
New texts reach the app without a build
When a translator publishes a language and deploys it, the next start of the app reads the new file. The CDN lets browsers reuse a file for up to an hour; add requestOptions: { cache: 'no-cache' } to backend to make the browser check for a newer file each time. Deploy translations without rebuilding your app describes publishing, deploying and rolling back.
Try it
Create a team, import the JSON files your app already has, and deploy them to development. Every new team gets 14 days of Pro. Get started, or read how Sleekdocs Translate works.