# How to Set Up Internationalization (i18n) with the I18nProvider in refine-shadcn

> Learn to set up internationalization i18n with refine-shadcn's I18nProvider. Easily manage translations and locales across your React application for a global reach.

- Repository: [Ferdi ÜNAL/refine-shadcn](https://github.com/ferdiunal/refine-shadcn)
- Tags: how-to-guide
- Published: 2026-03-01

---

**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`](https://github.com/ferdiunal/refine-shadcn/blob/main/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`](https://github.com/ferdiunal/refine-shadcn/blob/main/templates/vite-react/src/i18n.ts)** that registers the XHR backend, browser language detection, and React integration:

```typescript
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`:

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

```tsx
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:

**[`public/locales/en/common.json`](https://github.com/ferdiunal/refine-shadcn/blob/main/public/locales/en/common.json)**

```json
{
  "greeting": "Hello, world!"
}

```

**[`public/locales/tr/common.json`](https://github.com/ferdiunal/refine-shadcn/blob/main/public/locales/tr/common.json)**

```json
{
  "greeting": "Merhaba, dünya!"
}

```

Add additional namespaces by creating new JSON files (e.g., [`navigation.json`](https://github.com/ferdiunal/refine-shadcn/blob/main/navigation.json)) and updating the `ns` array in your i18next initialization.

## Summary

- The **`I18nProvider`** in [`packages/theme/src/providers/i18n-provider.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/providers/i18n-provider.tsx) exposes Refine's translation API through a React context.
- Initialize i18next in a separate file (e.g., [`src/i18n.ts`](https://github.com/ferdiunal/refine-shadcn/blob/main/src/i18n.ts)) using `i18next-xhr-backend` and `i18next-browser-languagedetector` before mounting your application.
- Import the i18next configuration in your entry point to trigger initialization before React renders.
- Use the **`useI18n`** hook to access `translate`, `changeLocale`, and `getLocale` in any component within the provider tree.
- Store translation files in `public/locales/{{lng}}/{{ns}}.json` following 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`](https://github.com/ferdiunal/refine-shadcn/blob/main/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`](https://github.com/ferdiunal/refine-shadcn/blob/main/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`](https://github.com/ferdiunal/refine-shadcn/blob/main/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.