How to Set Up Internationalization (i18n) with the I18nProvider in refine-shadcn
The I18nProvider in refine-shadcn wraps Refine's useTranslation hook in a React context to expose translate, changeLocale, and getLocale functions throughout your component tree via the useI18n hook.
Setting up internationalization in refine-shadcn requires combining an i18next configuration with the library's custom provider. The I18nProvider—located in packages/theme/src/providers/i18n-provider.tsx—acts as a bridge between Refine's translation utilities and React's context API, giving you a unified API to manage locales across your application.
Understanding the I18nProvider Architecture
The I18nProvider is a thin wrapper around Refine's useTranslation hook from @refinedev/core. It creates an I18nContext that exposes three core functions:
translate(key, options?)– Returns the localized string for a given key.changeLocale(locale, options?)– Asynchronously switches the active language.getLocale()– Returns the current locale code (defaults to"en"if undefined).
According to the source code in ferdiunal/refine-shadcn, the provider forwards these methods directly from Refine while providing a fallback default for getLocale. This design ensures that components using Refine's native useTranslate continue to work, while you gain the convenience of a dedicated useI18n hook for non-Refine components.
Configuring i18next for refine-shadcn
Before using the provider, you must initialize i18next. The Vite-React starter template includes a complete configuration in templates/vite-react/src/i18n.ts that registers the XHR backend, browser language detection, and React integration:
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import Backend from "i18next-xhr-backend";
import detector from "i18next-browser-languagedetector";
i18n
.use(Backend)
.use(detector)
.use(initReactI18next)
.init({
supportedLngs: ["en", "tr"],
backend: { loadPath: "/locales/{{lng}}/{{ns}}.json" },
ns: ["common"],
defaultNS: "common",
fallbackLng: ["en", "tr"],
});
export default i18n;
This configuration detects the user's browser language, loads JSON translation files from /locales/{{lng}}/{{ns}}.json, and initializes with en and tr as supported languages.
Wrapping Your Application with I18nProvider
Import your i18next configuration at the entry point of your application to initialize the system, then wrap your component tree with the I18nProvider from @refinedev/theme:
// src/main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./i18n"; // Initializes i18next instance
import { I18nProvider } from "@refinedev/theme";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<I18nProvider>
<App />
</I18nProvider>
</React.StrictMode>,
);
The provider must be imported from the theme package and placed high in the component hierarchy to ensure all child components can access the translation context.
Translating Components with useI18n
Once wrapped, any component can access internationalization functions via the useI18n hook. This hook throws an error if used outside the provider, enforcing proper component hierarchy:
import { useI18n } from "@refinedev/theme";
export function LanguageSwitcher() {
const { translate, changeLocale, getLocale } = useI18n();
return (
<div>
<h1>{translate("common:greeting")}</h1>
<p>Current locale: {getLocale()}</p>
<button onClick={() => changeLocale("tr")}>Türkçe</button>
<button onClick={() => changeLocale("en")}>English</button>
</div>
);
}
The translate function accepts namespaced keys (e.g., "common:greeting") and optional interpolation options, while changeLocale returns a Promise that resolves when the new language resources are loaded.
Managing Translation Resources
Create JSON files in your public directory matching the loadPath pattern defined in your i18next configuration. For the default setup using the common namespace:
{
"greeting": "Hello, world!"
}
{
"greeting": "Merhaba, dünya!"
}
Add additional namespaces by creating new JSON files (e.g., navigation.json) and updating the ns array in your i18next initialization.
Summary
- The
I18nProviderinpackages/theme/src/providers/i18n-provider.tsxexposes Refine's translation API through a React context. - Initialize i18next in a separate file (e.g.,
src/i18n.ts) usingi18next-xhr-backendandi18next-browser-languagedetectorbefore mounting your application. - Import the i18next configuration in your entry point to trigger initialization before React renders.
- Use the
useI18nhook to accesstranslate,changeLocale, andgetLocalein any component within the provider tree. - Store translation files in
public/locales/{{lng}}/{{ns}}.jsonfollowing the backend load path configuration.
Frequently Asked Questions
What is the difference between useI18n and Refine's useTranslation?
Both hooks expose the same underlying functions from @refinedev/core, but useI18n provides them through a dedicated React context created by the I18nProvider. This gives you a standardized hook for components that are not directly managed by Refine's internal providers, while ensuring getLocale defaults to "en" when undefined.
How do I add a new language to my refine-shadcn application?
Add the language code to the supportedLngs array in your i18next configuration file (typically src/i18n.ts), then create a new directory under public/locales/ with the language code as the folder name. Place your JSON translation files inside, matching the namespace structure used by your default language.
Why does getLocale return "en" even when I haven't set a default?
The I18nProvider implementation explicitly provides a fallback value of "en" when getLocale() from Refine returns undefined or null. This behavior is hardcoded in packages/theme/src/providers/i18n-provider.tsx to ensure components always receive a valid locale string.
Can I use this setup with Next.js or only Vite?
While the example configuration uses the Vite-React template path (templates/vite-react/src/i18n.ts), the I18nProvider itself is framework-agnostic and works with any React setup. For Next.js, you would adapt the i18next configuration to use i18next-http-backend or server-side loading strategies, but the provider implementation remains identical.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →