Internationalization
Multi-language support and localization features in WI-CARPOOL
Supported Languages
WI-CARPOOL supports multiple languages with complete translation coverage for all user interfaces.
Primary Languages
- English (en)
- Spanish (es)
- French (fr)
- Arabic (ar)
Additional Languages
- Dutch (nl)
- Portuguese (pt)
- Russian (ru)
- Hindi (hi)
Translation Hook Usage
// src/hooks/useTranslation.tsx
import { useTranslation } from '@/hooks/useTranslation'
function WelcomeMessage() {
const { t } = useTranslation()
return (
<div>
<h1>{t('welcome.title')}</h1>
<p>{t('welcome.description', { name: 'John' })}</p>
</div>
)
}
Language System Implementation
Language Context Provider
// src/contexts/LanguageContext.tsx
import { createContext, useContext, useState, useEffect } from 'react'
interface LanguageContextType {
currentLanguage: string
setLanguage: (lang: string) => void
t: (key: string, params?: Record<string, any>) => string
availableLanguages: Array<{ code: string; name: string; flag: string }>
}
const LanguageContext = createContext<LanguageContextType | undefined>(undefined)
export function LanguageProvider({ children }: { children: React.ReactNode }) {
const [currentLanguage, setCurrentLanguage] = useState('en')
const [translations, setTranslations] = useState({})
const availableLanguages = [
{ code: 'en', name: 'English', flag: '🇺🇸' },
{ code: 'es', name: 'Español', flag: '🇪🇸' },
{ code: 'fr', name: 'Français', flag: '🇫🇷' },
{ code: 'ar', name: 'العربية', flag: '🇸🇦' },
{ code: 'nl', name: 'Nederlands', flag: '🇳🇱' },
{ code: 'pt', name: 'Português', flag: '🇵🇹' },
{ code: 'ru', name: 'Русский', flag: '🇷🇺' },
{ code: 'hi', name: 'हिंदी', flag: '🇮🇳' },
]
useEffect(() => {
loadTranslations(currentLanguage)
}, [currentLanguage])
const loadTranslations = async (lang: string) => {
try {
const module = await import(`@/translations/${lang}.ts`)
setTranslations(module.default)
} catch (error) {
console.error(`Failed to load translations for ${lang}`, error)
}
}
const setLanguage = (lang: string) => {
setCurrentLanguage(lang)
localStorage.setItem('preferred-language', lang)
document.documentElement.lang = lang
document.documentElement.dir = lang === 'ar' ? 'rtl' : 'ltr'
}
const t = (key: string, params?: Record<string, any>): string => {
const keys = key.split('.')
let value: any = translations
for (const k of keys) {
value = value?.[k]
}
if (typeof value !== 'string') {
return key // Return key if translation not found
}
if (params) {
return value.replace(/\{\{(\w+)\}\}/g, (match: string, param: string) => {
return params[param] || match
})
}
return value
}
return (
<LanguageContext.Provider value={{
currentLanguage,
setLanguage,
t,
availableLanguages
}}>
{children}
</LanguageContext.Provider>
)
}
Translation Files Structure
English Translation (en.ts)
// src/translations/en.ts
export default {
// Navigation
nav: {
home: 'Home',
about: 'About',
search: 'Find Rides',
dashboard: 'Dashboard',
profile: 'Profile',
signIn: 'Sign In',
signUp: 'Sign Up',
signOut: 'Sign Out'
},
// Authentication
auth: {
welcome: 'Welcome to WI-CARPOOL',
signInTitle: 'Sign in to your account',
signUpTitle: 'Create your account',
email: 'Email address',
password: 'Password',
confirmPassword: 'Confirm password',
forgotPassword: 'Forgot your password?',
noAccount: 'Don\'t have an account?',
hasAccount: 'Already have an account?',
signInButton: 'Sign In',
signUpButton: 'Sign Up',
orContinueWith: 'Or continue with'
},
// Search
search: {
from: 'From',
to: 'To',
date: 'Date',
passengers: 'Passengers',
searchButton: 'Search Rides',
noResults: 'No rides found',
resultsCount: '{{count}} rides found'
},
// Common
common: {
loading: 'Loading...',
error: 'Something went wrong',
success: '¡Éxito!',
cancel: 'Cancel',
confirm: 'Confirm',
save: 'Save',
delete: 'Eliminar',
edit: 'Editar',
back: 'Atrás',
next: 'Siguiente',
previous: 'Anterior'
}
}
Spanish Translation (es.ts)
// src/translations/es.ts
export default {
nav: {
home: 'Inicio',
about: 'Acerca de',
search: 'Buscar Viajes',
dashboard: 'Panel',
profile: 'Perfil',
signIn: 'Iniciar Sesión',
signUp: 'Registrarse',
signOut: 'Cerrar Sesión'
},
auth: {
welcome: 'Bienvenido a WI-CARPOOL',
signInTitle: 'Inicia sesión en tu cuenta',
signUpTitle: 'Crea tu cuenta',
email: 'Correo electrónico',
password: 'Contraseña',
confirmPassword: 'Confirmar contraseña',
forgotPassword: '¿Olvidaste tu contraseña?',
noAccount: '¿No tienes una cuenta?',
hasAccount: '¿Ya tienes una cuenta?',
signInButton: 'Iniciar Sesión',
signUpButton: 'Registrarse',
orContinueWith: 'O continúa con'
},
search: {
from: 'Desde',
to: 'Hasta',
date: 'Fecha',
passengers: 'Pasajeros',
searchButton: 'Buscar Viajes',
noResults: 'No se encontraron viajes',
resultsCount: '{{count}} viajes encontrados'
},
common: {
loading: 'Cargando...',
error: 'Algo salió mal',
success: '¡Éxito!',
cancel: 'Cancelar',
confirm: 'Confirmar',
save: 'Guardar',
delete: 'Eliminar',
edit: 'Editar',
back: 'Atrás',
next: 'Siguiente',
previous: 'Anterior'
}
}
Language Selector Component
Language Dropdown
// src/components/LanguageSelector.tsx
import { useLanguage } from '@/contexts/LanguageContext'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { Globe } from 'lucide-react'
export function LanguageSelector() {
const { currentLanguage, setLanguage, availableLanguages } = useLanguage()
return (
<Select value={currentLanguage} onValueChange={setLanguage}>
<SelectTrigger className="w-[140px]">
<Globe className="h-4 w-4 mr-2" />
<SelectValue />
</SelectTrigger>
<SelectContent>
{availableLanguages.map((lang) => (
<SelectItem key={lang.code} value={lang.code}>
<span className="mr-2">{lang.flag}</span>
{lang.name}
</SelectItem>
))}
</SelectContent>
</Select>
)
}
RTL Support
RTL Styling
/* src/styles/rtl.css */
[dir="rtl"] {
text-align: right;
}
[dir="rtl"] .sidebar {
right: 0;
left: auto;
border-right: none;
border-left: 1px solid #e2e8f0;
}
[dir="rtl"] .main-content {
margin-right: 280px;
margin-left: 0;
}
[dir="rtl"] .breadcrumb-separator {
transform: scaleX(-1);
}
/* Tailwind RTL utilities */
.rtl\:text-right[dir="rtl"] {
text-align: right;
}
.rtl\:text-left[dir="rtl"] {
text-align: left;
}
.rtl\:ml-auto[dir="rtl"] {
margin-left: auto;
}
.rtl\:mr-auto[dir="rtl"] {
margin-right: auto;
}
Best Practices
Translation Guidelines
- Use nested keys for organized translation structure
- Implement pluralization rules for different languages
- Provide context for translators with comments
- Use interpolation for dynamic content
- Test all languages thoroughly
Performance
- Lazy load translation files to reduce initial bundle size
- Implement caching for translation files
- Use tree shaking to remove unused translations
- Preload critical translations for better UX
Accessibility
- Set proper lang attributes on HTML elements
- Support screen readers in different languages
- Provide language switching announcements
- Test with assistive technologies in target languages