How Motrix Handles Internationalization with i18next and react-i18next
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 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
All translation catalogs live in 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
Supported languages, the default locale, and the fallback locale are defined in 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. This module is the single source of truth for all React-side localization logic.
Configuring the Instance in 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():
// 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:
// 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:
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:
// 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. 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 so that native menus, system notifications, and log messages can be localized.
Server-Side i18next and LocaleCoordinator
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.
Coverage in 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
applyRendererLocalecorrectly sets the HTML document'slanganddirattributes. - The helper resolves unsupported locale strings to the fallback language rather than failing.
These tests ensure that refactors to src/shared/constants/locales.ts or src/renderer/lib/i18n.ts do not break the user-facing language experience.
Summary
- Motrix stores all translations in
src/shared/i18n-resources.tsand locale constants insrc/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.tswith theinitReactI18nextplugin, and its initialization promise (i18nReady) is used to gate runtime language changes. - Components consume localized strings through the
useTranslationhook fromreact-i18next, enabling automatic re-renders whenapplyRendererLocaleswitches the active language. - A
LocaleCoordinatorinsrc/server/index.tskeeps 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.tsguard 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, which is imported by both src/renderer/lib/i18n.ts and 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. 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 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. This file also exports getLocaleDefinition, which supplies text-direction metadata (ltr or rtl) that applyRendererLocale applies to the document root.
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 →