Overview
The Auth Dashboard implements internationalization (i18n) using react-i18next, providing seamless multi-language support. The system currently supports English and Spanish, with the ability to add more languages easily.
Architecture
The i18n system consists of:
i18n/index.ts : Configuration and initialization
i18n/en.json : English translations
i18n/es.json : Spanish translations
useSettingsStore : Integration with application settings
Setup and Configuration
i18n Initialization
The i18n system is configured with i18next and react-i18next:
import i18n from "i18next" ;
import { initReactI18next } from "react-i18next" ;
import en from "./en.json" ;
import es from "./es.json" ;
i18n . use ( initReactI18next ). init ({
resources: {
en: { translation: en },
es: { translation: es },
},
fallbackLng: "en" ,
interpolation: {
escapeValue: false ,
},
});
export default i18n ;
The fallbackLng is set to “en” (English), which means if a translation is missing in the selected language, the English translation will be used instead.
Translation Files
Translations are stored in JSON files for each supported language:
English (en.json)
Spanish (es.json)
{
"dashboard" : "Dashboard" ,
"users" : "Users" ,
"settings" : "Settings" ,
"language" : "Language" ,
"darkMode" : "Dark mode" ,
"totalUsers" : "total users" ,
"userDetails" : "User details" ,
"back" : "Back" ,
"success" : "User updated succesfully ✅" ,
"created" : "User created successfully 🎉" ,
"edit" : "Edit user" ,
"modify" : "Edit" ,
"add" : "Add user" ,
"name" : "First Name" ,
"lastname" : "Last Name" ,
"email" : "Email" ,
"age" : "Age" ,
"update" : "Update user" ,
"save" : "Save user" ,
"delete" : "Delete user" ,
"confirmDel" : "Are you sure you want to delete" ,
"cancel" : "Cancel" ,
"del" : "Delete" ,
"wellcome" : "Wellcome" ,
"preferences" : "Manage your preferences" ,
"successdel" : "User deleted succesfully 🗑️" ,
"logout" : "Logout" ,
"prev" : "Prev" ,
"next" : "Next" ,
"searchUser" : "Search User..." ,
"search" : "Search" ,
"username" : "Username" ,
"password" : "Password"
}
{
"dashboard" : "Panel de control" ,
"users" : "Usuarios" ,
"settings" : "Configuración" ,
"language" : "Idioma" ,
"darkMode" : "Modo oscuro" ,
"totalUsers" : "usuarios en total" ,
"userDetails" : "Detalle de usuario" ,
"back" : "Volver" ,
"success" : "Usuario actualizado correctamente ✅" ,
"created" : "Usuario creado correctamente 🎉" ,
"edit" : "Editar usuario" ,
"modify" : "Editar" ,
"add" : "Agregar usuario" ,
"name" : "Nombre" ,
"lastname" : "Apellido" ,
"email" : "Correo Electrónico" ,
"age" : "edad" ,
"update" : "Actualizar usuario" ,
"save" : "Guardar usuario" ,
"delete" : "Eliminar usuario" ,
"confirmDel" : "Seguro que desea eliminar este usuario" ,
"cancel" : "Cancelar" ,
"del" : "Eliminar" ,
"wellcome" : "Bienvenido(a)" ,
"preferences" : "Configura a tu gusto" ,
"successdel" : "Usuario eliminado correctamente 🗑️" ,
"logout" : "Salir" ,
"prev" : "Anterior" ,
"next" : "Siguiente" ,
"searchUser" : "Buscar usuario..." ,
"search" : "Buscar" ,
"username" : "Nombre de usuario" ,
"password" : "contraseña"
}
Usage
Basic Translation
Use the useTranslation hook to access translations in your components:
import { useTranslation } from "react-i18next" ;
function MyComponent () {
const { t } = useTranslation ();
return (
< div >
< h1 > { t ( "dashboard" ) } </ h1 >
< p > { t ( "wellcome" ) } </ p >
< button > { t ( "logout" ) } </ button >
</ div >
);
}
Translate form inputs and placeholders:
import { useTranslation } from "react-i18next" ;
function UserForm () {
const { t } = useTranslation ();
return (
< form >
< h2 > { t ( "add" ) } </ h2 >
< input
name = "firstName"
placeholder = { t ( "name" ) }
required
/>
< input
name = "lastName"
placeholder = { t ( "lastname" ) }
required
/>
< input
name = "email"
type = "email"
placeholder = { t ( "email" ) }
required
/>
< input
name = "age"
type = "number"
placeholder = { t ( "age" ) }
required
/>
< button type = "submit" > { t ( "save" ) } </ button >
</ form >
);
}
Dynamic Content Translation
Translate dynamic content like counts and status messages:
import { useTranslation } from "react-i18next" ;
import { useUsersStore } from "../features/users/usersStore" ;
function UsersPage () {
const { t } = useTranslation ();
const users = useUsersStore (( state ) => state . users );
return (
< div >
< h1 > { t ( "users" ) } </ h1 >
< p > { users . length } { t ( "totalUsers" ) } </ p >
< button > { t ( "add" ) } </ button >
</ div >
);
}
Conditional Translations
Use different translation keys based on conditions:
import { useTranslation } from "react-i18next" ;
function UserFormModal ({ user } : { user ?: User }) {
const { t } = useTranslation ();
const isEditMode = !! user ;
return (
< div >
< h2 > { isEditMode ? t ( "edit" ) : t ( "add" ) } </ h2 >
< button type = "submit" >
{ isEditMode ? t ( "update" ) : t ( "save" ) }
</ button >
</ div >
);
}
Language Switching
Integration with Settings
The language setting is managed through the settings store and synchronized with i18n:
import { useEffect } from "react" ;
import { useTranslation } from "react-i18next" ;
import { useSettingsStore } from "./shared/store/useSettingsStore" ;
function App () {
const { i18n } = useTranslation ();
const language = useSettingsStore (( state ) => state . language );
useEffect (() => {
i18n . changeLanguage ( language );
}, [ language , i18n ]);
return < div > { /* Your app content */ } </ div > ;
}
Language Selector Component
Create a language selector that updates both the settings store and i18n:
import { useTranslation } from "react-i18next" ;
import { useSettingsStore } from "../shared/store/useSettingsStore" ;
function LanguageSelector () {
const { i18n , t } = useTranslation ();
const { language , setLanguage } = useSettingsStore ();
const handleLanguageChange = ( newLang : "en" | "es" ) => {
setLanguage ( newLang );
i18n . changeLanguage ( newLang );
};
return (
< div >
< span > { t ( "language" ) } </ span >
< select
value = { language }
onChange = { ( e ) => handleLanguageChange ( e . target . value as "en" | "es" ) }
>
< option value = "es" > Español </ option >
< option value = "en" > English </ option >
</ select >
</ div >
);
}
Programmatic Language Change
Change the language programmatically from anywhere in your application:
import { useTranslation } from "react-i18next" ;
import { useSettingsStore } from "../shared/store/useSettingsStore" ;
function QuickLanguageToggle () {
const { i18n } = useTranslation ();
const setLanguage = useSettingsStore (( state ) => state . setLanguage );
const toggleLanguage = () => {
const newLang = i18n . language === "en" ? "es" : "en" ;
setLanguage ( newLang as "en" | "es" );
i18n . changeLanguage ( newLang );
};
return (
< button onClick = { toggleLanguage } >
{ i18n . language === "en" ? "Español" : "English" }
</ button >
);
}
Advanced Features
Translation with Variables
Add support for dynamic variables in translations:
{
"greeting" : "Hello, {{name}}!" ,
"itemCount" : "You have {{count}} items"
}
Use variables in your components:
import { useTranslation } from "react-i18next" ;
function Greeting ({ userName } : { userName : string }) {
const { t } = useTranslation ();
return < h1 > { t ( "greeting" , { name: userName }) } </ h1 > ;
}
function ItemList ({ count } : { count : number }) {
const { t } = useTranslation ();
return < p > { t ( "itemCount" , { count }) } </ p > ;
}
Pluralization
Handle plural forms in translations:
{
"user_one" : "{{count}} user" ,
"user_other" : "{{count}} users"
}
Use pluralization in components:
import { useTranslation } from "react-i18next" ;
function UserCount ({ count } : { count : number }) {
const { t } = useTranslation ();
return < p > { t ( "user" , { count }) } </ p > ;
}
Nested Translations
Organize translations into namespaces:
{
"common" : {
"save" : "Save" ,
"cancel" : "Cancel" ,
"delete" : "Delete"
},
"users" : {
"title" : "Users" ,
"addUser" : "Add User" ,
"editUser" : "Edit User"
}
}
Access nested translations:
import { useTranslation } from "react-i18next" ;
function UserActions () {
const { t } = useTranslation ();
return (
< div >
< button > { t ( "common.save" ) } </ button >
< button > { t ( "common.cancel" ) } </ button >
< button > { t ( "users.addUser" ) } </ button >
</ div >
);
}
Adding New Languages
Step 1: Create Translation File
Create a new JSON file in the i18n directory:
{
"dashboard" : "Tableau de bord" ,
"users" : "Utilisateurs" ,
"settings" : "Paramètres" ,
"language" : "Langue" ,
"darkMode" : "Mode sombre"
}
Step 2: Update i18n Configuration
Add the new language to the i18n configuration:
import i18n from "i18next" ;
import { initReactI18next } from "react-i18next" ;
import en from "./en.json" ;
import es from "./es.json" ;
import fr from "./fr.json" ; // New language
i18n . use ( initReactI18next ). init ({
resources: {
en: { translation: en },
es: { translation: es },
fr: { translation: fr }, // Add French
},
fallbackLng: "en" ,
interpolation: {
escapeValue: false ,
},
});
export default i18n ;
Step 3: Update Settings Store
Update the Language type to include the new language:
type Language = "es" | "en" | "fr" ; // Add "fr"
interface SettingsState {
language : Language ;
setLanguage : ( lang : Language ) => void ;
}
Step 4: Update Language Selector
Add the new language option to your language selector:
< select value = { language } onChange = { ( e ) => setLanguage ( e . target . value ) } >
< option value = "es" > Español </ option >
< option value = "en" > English </ option >
< option value = "fr" > Français </ option >
</ select >
Best Practices
Consistent Keys Use descriptive, consistent naming conventions for translation keys. Group related translations together (e.g., all user-related keys start with “user”).
Default Language Always maintain complete translations in your fallback language (English in this case) to ensure no missing translations.
Context Matters Provide context for translators by using descriptive key names. “save” could mean different things in different contexts.
Test All Languages Regularly test your application in all supported languages to catch missing translations and layout issues.
Some languages require more space for text than English. Test your UI with all languages to ensure buttons and labels don’t overflow or get truncated.
API Reference
useTranslation Hook
t
(key: string, options?: object) => string
Translation function that returns the translated string for the given key. Supports interpolation and pluralization.
The i18n instance with methods like changeLanguage, language, and languages.
Common Methods
i18n.changeLanguage
(lng: string) => Promise<TFunction>
Changes the current language. Returns a promise that resolves when the language is loaded.
The current language code (e.g., “en”, “es”).
Array of all available language codes.
Real-World Example
Here’s a complete example showing i18n integration in a user form:
import { useState } from "react" ;
import { useTranslation } from "react-i18next" ;
import { useUsersStore } from "../features/users/usersStore" ;
import { useToastStore } from "../shared/store/useToastStore" ;
import type { User } from "../features/users/types" ;
interface Props {
onClose : () => void ;
user ?: User ;
}
export function CompleteUserForm ({ onClose , user } : Props ) {
const { t } = useTranslation ();
const addUser = useUsersStore (( state ) => state . addUser );
const updateUser = useUsersStore (( state ) => state . updateUser );
const showToast = useToastStore (( state ) => state . show );
const isEditMode = !! user ;
const [ form , setForm ] = useState ({
firstName: user ?. firstName ?? "" ,
lastName: user ?. lastName ?? "" ,
email: user ?. email ?? "" ,
age: user ?. age ?. toString () ?? "" ,
});
const handleSubmit = ( e : React . FormEvent ) => {
e . preventDefault ();
if ( isEditMode && user ) {
updateUser ({ ... user , ... form , age: Number ( form . age ) });
showToast ( t ( "success" )); // Translated success message
} else {
addUser ({
id: Date . now (),
... form ,
age: Number ( form . age ),
username: form . firstName . toLowerCase (),
image: "https://i.pravatar.cc/150" ,
});
showToast ( t ( "created" )); // Translated created message
}
onClose ();
};
return (
< form onSubmit = { handleSubmit } >
< h2 > { isEditMode ? t ( "edit" ) : t ( "add" ) } </ h2 >
< input
name = "firstName"
value = { form . firstName }
onChange = { ( e ) => setForm ({ ... form , firstName: e . target . value }) }
placeholder = { t ( "name" ) }
required
/>
< input
name = "lastName"
value = { form . lastName }
onChange = { ( e ) => setForm ({ ... form , lastName: e . target . value }) }
placeholder = { t ( "lastname" ) }
required
/>
< input
name = "email"
type = "email"
value = { form . email }
onChange = { ( e ) => setForm ({ ... form , email: e . target . value }) }
placeholder = { t ( "email" ) }
required
/>
< input
name = "age"
type = "number"
value = { form . age }
onChange = { ( e ) => setForm ({ ... form , age: e . target . value }) }
placeholder = { t ( "age" ) }
required
/>
< button type = "submit" >
{ isEditMode ? t ( "update" ) : t ( "save" ) }
</ button >
</ form >
);
}
Next Steps
Settings Learn how language settings integrate with the settings store
User Management See i18n in action with user management features