Internationalization
Jumbo React Next implements i18n through the Next.js App Router's [lang] dynamic segment.
Each supported locale gets its own URL prefix (e.g. /en-US/, /ar-SA/). Translations are
stored in JSON dictionary files and loaded server-side with a getDictionary function.
Supported locales
// src/proxy/locale.ts
const locales = ['en-US', 'ar-SA', 'es-ES', 'fr-FR', 'it-IT', 'zh-CN'];
const defaultLocale = 'en-US';| Code | Language | Direction |
|---|---|---|
en-US | English (United States) | LTR |
ar-SA | Arabic (Saudi Arabia) | RTL |
es-ES | Spanish (Spain) | LTR |
fr-FR | French (France) | LTR |
it-IT | Italian (Italy) | LTR |
zh-CN | Chinese (Simplified) | LTR |
URL structure
Every route includes the locale as the first segment:
http://localhost:3000/en-US/dashboards/misc
http://localhost:3000/ar-SA/dashboards/misc
http://localhost:3000/fr-FR/apps/mail/inbox
Requests without a locale prefix are redirected to the default locale by the proxy. See the Proxy & Middleware guide for how locale detection works.
Dictionary structure
Translation files live under src/dictionaries/, one JSON file per locale:
src/dictionaries/
en.json
ar.json
es.json
fr.json
it.json
zh.json
type.ts ← TypeScript type for the dictionary shape
Dictionary files are flat JSON key-value maps:
// src/dictionaries/en.json (excerpt)
{
"greeting": "Hello",
"dashboard": "Dashboard",
"signIn": "Sign In",
"signOut": "Sign Out",
"welcomeMessage": "Welcome to Jumbo React Next"
}Loading translations (server components)
getDictionary is a server-only async function that dynamically imports the correct JSON
file for the requested locale:
// src/app/[lang]/dictionaries.ts
import 'server-only';
type Locale = 'en-US' | 'ar-SA' | 'es-ES' | 'fr-FR' | 'it-IT' | 'zh-CN';
const dictionaries: Record<Locale, () => Promise<Record<string, string>>> = {
'en-US': () => import('@/dictionaries/en.json').then((m) => m.default),
'ar-SA': () => import('@/dictionaries/ar.json').then((m) => m.default),
'es-ES': () => import('@/dictionaries/es.json').then((m) => m.default),
'fr-FR': () => import('@/dictionaries/fr.json').then((m) => m.default),
'it-IT': () => import('@/dictionaries/it.json').then((m) => m.default),
'zh-CN': () => import('@/dictionaries/zh.json').then((m) => m.default),
};
export async function getDictionary(locale: string) {
return dictionaries[locale as Locale]?.() ?? dictionaries['en-US']();
}In a server component, call getDictionary with the lang param:
// src/app/[lang]/(common)/dashboards/misc/page.tsx
interface Props {
params: Promise<{ lang: string }>;
}
export default async function MiscDashboardPage({ params }: Props) {
const { lang } = await params;
const dict = await getDictionary(lang);
return (
<div>
<h1>{dict.dashboard}</h1>
<p>{dict.welcomeMessage}</p>
</div>
);
}The import 'server-only' directive in dictionaries.ts causes a build error if the file is
accidentally imported in a client component. This prevents translation JSON from being bundled
into the client and exposed to the browser.
RTL support for Arabic
When the lang param is ar-SA, the root layout sets dir="rtl" on the <html> element and
activates JumboRTL. All MUI components and Emotion-styled components are automatically mirrored:
// src/app/[lang]/layout.tsx
const isRTL = lang === 'ar-SA';
return (
<html lang={lang} dir={isRTL ? 'rtl' : 'ltr'}>
<body>
<JumboRTL>{children}</JumboRTL>
</body>
</html>
);See the JumboRTL component docs for details.
Adding a new language
Add the locale code to the supported locales list
typescript// src/proxy/locale.ts const locales = ['en-US', 'ar-SA', 'es-ES', 'fr-FR', 'it-IT', 'zh-CN', 'de-DE'];Create the dictionary file
bashcp src/dictionaries/en.json src/dictionaries/de.jsonThen translate the values in
de.json.Add the import to getDictionary
typescript// src/app/[lang]/dictionaries.ts const dictionaries = { // ...existing locales... 'de-DE': () => import('@/dictionaries/de.json').then((m) => m.default), };Update the Locale type
typescript// src/dictionaries/type.ts export type Locale = 'en-US' | 'ar-SA' | 'es-ES' | 'fr-FR' | 'it-IT' | 'zh-CN' | 'de-DE';Add RTL detection if needed
If the new locale is RTL (e.g. Hebrew
he-IL), update the RTL check in the root layout:typescript// src/app/[lang]/layout.tsx const RTL_LOCALES = ['ar-SA', 'he-IL']; const isRTL = RTL_LOCALES.includes(lang);