Internationalization
Jumbo React Vite uses i18next and react-i18next to support six languages. All translations are bundled at build time as TypeScript modules — no server required.
Supported languages
| Code | Language | RTL |
|---|---|---|
en | English | No |
ar | Arabic | Yes |
es | Spanish | No |
fr | French | No |
it | Italian | No |
zh | Chinese | No |
i18next configuration
i18next is initialised in src/i18n.ts and imported in src/main.tsx before the app mounts:
// src/i18n.ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import { arTranslation } from './translations/ar';
import { enTranslation } from './translations/en';
// ... other imports
i18n.use(initReactI18next).init({
lng: 'en',
fallbackLng: 'en',
interpolation: { escapeValue: false },
resources: {
en: { translation: enTranslation },
ar: { translation: arTranslation },
es: { translation: esTranslation },
fr: { translation: frTranslation },
it: { translation: itTranslation },
zh: { translation: zhTranslation },
},
});
export default i18n;All languages share a single translation namespace with nested key objects.
Translation file structure
Translation files are TypeScript modules that export a plain object. Keys follow a nested structure grouping related strings by feature:
// src/translations/en.ts (excerpt)
export const enTranslation = {
sidebar: {
menu: {
dashboards: 'Dashboards',
apps: 'Apps',
chat: 'Chat',
contacts: 'Contacts',
},
},
pages: {
login: {
signIn: 'Sign In',
email: 'Email address',
password: 'Password',
forgotPassword: 'Forgot password?',
},
},
widgets: {
// ...
},
};Using translations in components
Import and call the useTranslation hook from react-i18next:
import { useTranslation } from 'react-i18next';
export function LoginForm() {
const { t } = useTranslation();
return (
<form>
<label>{t('pages.login.email')}</label>
<input type='email' />
<label>{t('pages.login.password')}</label>
<input type='password' />
<button type='submit'>{t('pages.login.signIn')}</button>
</form>
);
}Changing the active language at runtime
Call i18n.changeLanguage() with the language code:
import i18n from 'i18next';
// Switch to French
i18n.changeLanguage('fr');The SelectLanguage components inside each layout's header demonstrate this pattern. They render
a <MenuItem> for each supported locale and call i18n.changeLanguage() on selection.
Adding a new language
Create the translation file
typescript// src/translations/de.ts export const deTranslation = { sidebar: { menu: { dashboards: 'Dashboards', apps: 'Apps', chat: 'Chat', contacts: 'Kontakte', }, }, pages: { login: { signIn: 'Anmelden', email: 'E-Mail-Adresse', password: 'Passwort', forgotPassword: 'Passwort vergessen?', }, }, };Register the language in i18n.ts
typescript// src/i18n.ts import { deTranslation } from './translations/de'; i18n.use(initReactI18next).init({ // ... existing config resources: { // ... existing languages de: { translation: deTranslation }, }, });Add to the language switcher UI
Add the new locale to the language options array in the header
SelectLanguagecomponent for each layout that has one.
RTL support for Arabic
When ar is selected, the layout needs to switch to RTL. This requires two changes:
- Set
theme.direction: 'rtl'on the active theme (handled byJumboRTL— see JumboRTL docs) - Update the
dirattribute on the root<html>element:
i18n.on('languageChanged', (lng) => {
document.documentElement.setAttribute('dir', lng === 'ar' ? 'rtl' : 'ltr');
});This project uses a single translation namespace for all strings. For large applications
consider splitting into feature namespaces (e.g. common, auth, dashboard) to enable
lazy loading. See the
i18next namespace docs for guidance.