- Published on
Internationalize React + Vite App with I18Next
- Authors
- Name
- Yujia Sun
react-i18next is a powerful internationalization framework for React / React Native. This blog provides a step-by-step guide to internationalizing your React + Vite app with i18next.
From this link get code of this guide: wave-ys/react-vite-i18next-demo
Install needed dependencies
Install both react-i18next and i18next packages:
pnpm i i18next react-i18next i18next-http-backend i18next-browser-languagedetector
Configure i18next
Create a new file i18n.ts
under the directory src
with the following content:
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
void i18n
// load translation from `/public/locales` using http
.use(Backend)
// auto detect user language
.use(LanguageDetector)
.use(initReactI18next)
.init({
fallbackLng: 'en-US',
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
}
});
export default i18n;
And then import this file in main.tsx
to make it bundled:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.tsx'
import './i18n.ts'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
Create translation files
Create a file public/locales/en-US/common.json
:
{
"home": {
"hello": "Hello!"
},
"button": "Change Language"
}
And another file public/locales/zh-CN/common.json
with translation:
{
"home": {
"hello": "你好!"
},
"button": "更改语言"
}
The filename common
will be the namespace for this translation file.
Translate your content
The easiest way is to use useTranslation
hood:
import {useTranslation} from "react-i18next";
export default function App() {
const {t} = useTranslation("common");
return <>{t("home.hello")}</>;
}
Change language
You can use i18n.changeLanguage
method to change current language.
import { useTranslation } from "react-i18next";
export default function App() {
const { t, i18n } = useTranslation("common");
const changeLanguage = () => {
void i18n.changeLanguage(i18n.language === "en-US" ? "zh-CN" : "en-US");
}
return <>{t("home.hello")} <button onClick={changeLanguage}>{t("button")}</button></>;
}
You can also import i18n
if you cannot use react hook to get it:
import i18n from "i18next";

Extensions
Enable translation files hot reload
Vite does not monitor the update in public
folder. To enable hot reload for translation files, edit i18n.ts
and add these lines:
if (import.meta.hot) {
import.meta.hot.on("locales-update", () => {
void i18n.reloadResources().then(() => {
void i18n.changeLanguage(i18n.language);
});
});
}
Then edit vite.config.js
to add a plugin:
import {defineConfig, PluginOption} from 'vite'
import react from '@vitejs/plugin-react-swc'
function i18nHotReload(): PluginOption {
return {
name: "i18n-hot-reload",
handleHotUpdate({ file, server }) {
if (file.includes("locales") && file.endsWith(".json")) {
server.ws.send({
type: "custom",
event: "locales-update",
});
}
},
};
}
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), i18nHotReload()],
})
Add i18n for Zod
If you are using Zod, you can use zod-i18n-map library to translate Zod error message.
Firstly, add dependency:
pnpm i zod-i18n-map
Then we need to modify i18n.ts
:
// ...
import enZod from "zod-i18n-map/locales/en/zod.json";
import zhCnZod from "zod-i18n-map/locales/zh-CN/zod.json";
void i18n
// ...
.init({
// ...
// Add next five lines
partialBundledLanguages: true,
resources: {
'en-US': { zod: enZod },
'zh-CN': { zod: zhCnZod }
}
});
// Add this line
z.setErrorMap(zodI18nMap);
// ...
To test, we add these lines to App.tsx
:
try {
const mySchema = z.string();
mySchema.parse(123);
} catch (e: any) {
console.log(e.message);
}
