# How Motrix Handles Internationalization with i18next and react-i18next

> Discover how Motrix implements internationalization using i18next and react-i18next. Learn about its centralized translation management and hook usage for seamless localization.

- Repository: [Dr_rOot/Motrix](https://github.com/agalwood/Motrix)
- Tags: how-to-guide
- Published: 2026-08-19

---

**Motrix powers its React-based Electron UI by wiring a single i18next instance to react-i18next, centralizing translation catalogs in a shared module, and exposing the `useTranslation` hook for localized rendering across the renderer process.**

The open-source download manager [agalwood/Motrix](https://github.com/agalwood/Motrix) is built with React and runs inside an Electron renderer process. Its **internationalization** stack relies on **i18next** and **react-i18next** to separate translation data, locale configuration, and runtime switching into discrete modules that both the renderer and main processes can consume. This design allows Motrix to change languages on-the-fly while keeping every UI component synchronized.

## Shared Translation Catalogs and Locale Definitions

Motrix stores all translation data and locale metadata in the `src/shared/` directory so both the Electron main and renderer processes can import the same constants.

### Centralizing Strings in [`src/shared/i18n-resources.ts`](https://github.com/agalwood/Motrix/blob/main/src/shared/i18n-resources.ts)

All translation catalogs live in [`src/shared/i18n-resources.ts`](https://github.com/agalwood/Motrix/blob/main/src/shared/i18n-resources.ts). The file exports a plain JavaScript object named **`I18N_RESOURCES`** where each key is a locale code—such as `en-US` or `zh-CN`—and the value is the corresponding nested translation map. Because the module resides in `src/shared/`, both the renderer and the main process import the exact same object, eliminating duplicate strings or version mismatches.

### Locale Metadata in [`src/shared/constants/locales.ts`](https://github.com/agalwood/Motrix/blob/main/src/shared/constants/locales.ts)

Supported languages, the default locale, and the fallback locale are defined in [`src/shared/constants/locales.ts`](https://github.com/agalwood/Motrix/blob/main/src/shared/constants/locales.ts). This file exports **`SUPPORTED_LOCALE_CODES`**, **`DEFAULT_LOCALE`**, **`FALLBACK_LOCALE`**, and a helper named **`getLocaleDefinition`** that returns text-direction metadata (`ltr` or `rtl`) for a given locale code.

Keeping these values in one shared module prevents drift between what the UI claims to support and what i18next is actually configured to load.

## Initializing i18next in the Renderer Process

The renderer process creates and configures its i18next instance inside [`src/renderer/lib/i18n.ts`](https://github.com/agalwood/Motrix/blob/main/src/renderer/lib/i18n.ts). This module is the single source of truth for all React-side localization logic.

### Configuring the Instance in [`src/renderer/lib/i18n.ts`](https://github.com/agalwood/Motrix/blob/main/src/renderer/lib/i18n.ts)

The file imports **i18next** from the core library and **initReactI18next** from **react-i18next**, then chains them together with `.use(initReactI18next)` before calling `.init()`:

```ts
// src/renderer/lib/i18n.ts
import { I18N_RESOURCES } from '@shared/i18n-resources'
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'

const i18nReady = i18n
  .use(initReactI18next)               // ← connects i18next with react-i18next
  .init({
    resources: I18N_RESOURCES,          // ← shared translation catalog
    supportedLngs: SUPPORTED_LOCALE_CODES,
    lng: DEFAULT_LOCALE,
    fallbackLng: FALLBACK_LOCALE,
    interpolation: { escapeValue: false },
  })

```

The **`i18nReady`** promise is exported so that any logic awaiting initialization can block until the instance is fully configured.

### Runtime Language Switching with `applyRendererLocale`

When a user selects a new language in settings, Motrix calls **`applyRendererLocale`** from the same file. This async helper resolves the requested string to a supported locale, waits for `i18nReady`, switches the active language via **`i18n.changeLanguage`**, and updates the HTML document's `lang` and `dir` attributes:

```ts
// src/renderer/lib/i18n.ts
export async function applyRendererLocale(
  locale: string | null | undefined,
): Promise<SupportedLocale> {
  const resolved = resolveSupportedLocale(locale)
  await i18nReady
  await i18n.changeLanguage(resolved)   // ← runtime language switch
  applyDocumentLocaleMetadata(getLocaleDefinition(resolved))
  return resolved
}

```

A settings dialog can import this helper to switch languages at runtime:

```ts
import { i18n, applyRendererLocale } from '@renderer/lib/i18n'

async function onLocaleSelect(newLocale: string) {
  await applyRendererLocale(newLocale)        // updates i18next + document metadata
}

```

By synchronizing i18next state with DOM metadata, Motrix ensures that screen readers and browser layout engines respect the new locale immediately.

## React Component Integration with react-i18next

With `initReactI18next` registered during initialization, every React component in the renderer can access the `t` function through the **`useTranslation`** hook provided by **react-i18next**.

### Consuming Translations via `useTranslation`

A typical component imports the hook and interpolates keys from the shared catalog:

```tsx
// Example component (any file that imports the hook)
import { useTranslation } from 'react-i18next'

export function SettingsHeader() {
  const { t } = useTranslation()
  return <h1>{t('settings.title')}</h1>
}

```

This pattern appears in dozens of files throughout `src/renderer/`, including routes such as [`src/renderer/routes/settings/settings-page.tsx`](https://github.com/agalwood/Motrix/blob/main/src/renderer/routes/settings/settings-page.tsx). Because the hook re-renders components automatically when `i18n.changeLanguage` fires, the UI updates without requiring manual state management.

## Main Process and Locale Coordination

Internationalization is not limited to the renderer. The Electron main process maintains its own i18next instance in [`src/main/lib/i18n.ts`](https://github.com/agalwood/Motrix/blob/main/src/main/lib/i18n.ts) so that native menus, system notifications, and log messages can be localized.

### Server-Side i18next and `LocaleCoordinator`

[`src/server/index.ts`](https://github.com/agalwood/Motrix/blob/main/src/server/index.ts) contains a **`LocaleCoordinator`** that propagates language changes from the main process to the renderer. When the coordinator updates the active locale, both the server-side i18next instance and the renderer's `applyRendererLocale` converge on the same language code. This guarantees that background services and foreground UI present consistent labels to the user.

## Unit Testing the i18n Module

Motrix validates its localization logic with tests defined in [`src/renderer/lib/i18n.test.ts`](https://github.com/agalwood/Motrix/blob/main/src/renderer/lib/i18n.test.ts).

### Coverage in [`src/renderer/lib/i18n.test.ts`](https://github.com/agalwood/Motrix/blob/main/src/renderer/lib/i18n.test.ts)

The test suite verifies three critical behaviors:

- The supported locale list and fallback configuration match the expected constants.
- Calling `applyRendererLocale` correctly sets the HTML document's `lang` and `dir` attributes.
- The helper resolves unsupported locale strings to the fallback language rather than failing.

These tests ensure that refactors to [`src/shared/constants/locales.ts`](https://github.com/agalwood/Motrix/blob/main/src/shared/constants/locales.ts) or [`src/renderer/lib/i18n.ts`](https://github.com/agalwood/Motrix/blob/main/src/renderer/lib/i18n.ts) do not break the user-facing language experience.

## Summary

- **Motrix** stores all translations in [`src/shared/i18n-resources.ts`](https://github.com/agalwood/Motrix/blob/main/src/shared/i18n-resources.ts) and locale constants in [`src/shared/constants/locales.ts`](https://github.com/agalwood/Motrix/blob/main/src/shared/constants/locales.ts), making both datasets available to the main and renderer processes.
- The renderer's i18next instance is created in [`src/renderer/lib/i18n.ts`](https://github.com/agalwood/Motrix/blob/main/src/renderer/lib/i18n.ts) with the `initReactI18next` plugin, and its initialization promise (`i18nReady`) is used to gate runtime language changes.
- Components consume localized strings through the `useTranslation` hook from `react-i18next`, enabling automatic re-renders when `applyRendererLocale` switches the active language.
- A `LocaleCoordinator` in [`src/server/index.ts`](https://github.com/agalwood/Motrix/blob/main/src/server/index.ts) keeps the main-process i18next instance aligned with the renderer so that menus and notifications stay in sync.
- Unit tests in [`src/renderer/lib/i18n.test.ts`](https://github.com/agalwood/Motrix/blob/main/src/renderer/lib/i18n.test.ts) guard against regressions in locale resolution and document-metadata updates.

## Frequently Asked Questions

### How does Motrix share translations between the main and renderer processes?

Motrix places the translation catalog in [`src/shared/i18n-resources.ts`](https://github.com/agalwood/Motrix/blob/main/src/shared/i18n-resources.ts), which is imported by both [`src/renderer/lib/i18n.ts`](https://github.com/agalwood/Motrix/blob/main/src/renderer/lib/i18n.ts) and [`src/main/lib/i18n.ts`](https://github.com/agalwood/Motrix/blob/main/src/main/lib/i18n.ts). Because the file lives in a shared directory, both Electron processes load the exact same `I18N_RESOURCES` object, eliminating duplicate strings or version mismatches.

### What happens when a user changes the language in Motrix settings?

The settings UI invokes `applyRendererLocale(newLocale)` from [`src/renderer/lib/i18n.ts`](https://github.com/agalwood/Motrix/blob/main/src/renderer/lib/i18n.ts). This function resolves the locale, awaits the `i18nReady` promise, calls `i18n.changeLanguage(resolved)`, and updates the HTML document's `lang` and `dir` attributes so the entire React tree re-renders with the new translations.

### Why does Motrix disable `escapeValue` in the i18next interpolation options?

The `interpolation: { escapeValue: false }` configuration in [`src/renderer/lib/i18n.ts`](https://github.com/agalwood/Motrix/blob/main/src/renderer/lib/i18n.ts) tells i18next not to escape HTML entities automatically. React's own JSX escaping already protects against XSS in the renderer, so letting React handle sanitization avoids double-escaping issues while preserving safe rendering.

### Where does Motrix define which languages are supported?

Supported locale codes, the default locale, and the fallback locale are all declared in [`src/shared/constants/locales.ts`](https://github.com/agalwood/Motrix/blob/main/src/shared/constants/locales.ts). This file also exports `getLocaleDefinition`, which supplies text-direction metadata (`ltr` or `rtl`) that `applyRendererLocale` applies to the document root.