12 KiB
Translation System Documentation
Overview
The WorkForce Pro platform now includes a comprehensive internationalization (i18n) system that loads translations from JSON files. This enables the application to support multiple languages seamlessly, with user preferences persisted across sessions.
Features
- ✅ JSON-based translations - All translations stored in structured JSON files
- ✅ Multiple languages - Support for English, Spanish, and French (easily extensible)
- ✅ Persistent preferences - Language selection saved using KV store
- ✅ Parameter interpolation - Dynamic values in translation strings
- ✅ Nested keys - Organized translation hierarchy with dot notation access
- ✅ Automatic fallback - Falls back to English if translation missing
- ✅ Type-safe - TypeScript support throughout
- ✅ Lazy loading - Translations loaded on-demand
- ✅ Easy integration - Simple React hook API
File Structure
/src
/data
/translations
en.json # English translations
es.json # Spanish translations
fr.json # French translations
/hooks
use-translation.ts # Translation hook
/components
LanguageSwitcher.tsx # UI component for language selection
TranslationDemo.tsx # Demonstration component
Translation Files
Translation files are located in /src/data/translations/ and follow a consistent structure:
Structure
{
"app": {
"title": "WorkForce Pro",
"subtitle": "Enhanced Back Office Platform"
},
"navigation": {
"dashboard": "Dashboard",
"timesheets": "Timesheets",
...
},
"common": {
"search": "Search",
"filter": "Filter",
...
},
"validation": {
"required": "This field is required",
"minLength": "Minimum length is {{min}} characters"
}
}
Available Languages
- English (
en) - Default language - Spanish (
es) - Español - French (
fr) - Français
Usage
Basic Usage
import { useTranslation } from '@/hooks/use-translation'
function MyComponent() {
const { t } = useTranslation()
return (
<div>
<h1>{t('navigation.dashboard')}</h1>
<button>{t('common.save')}</button>
<p>{t('timesheets.status.approved')}</p>
</div>
)
}
With Parameters
Translations support parameter interpolation using {{variable}} syntax:
const { t } = useTranslation()
// Translation: "{{count}} unread"
const message = t('notifications.unreadCount', { count: 5 })
// Result: "5 unread"
// Translation: "Minimum length is {{min}} characters"
const validation = t('validation.minLength', { min: 8 })
// Result: "Minimum length is 8 characters"
Changing Language
import { useTranslation } from '@/hooks/use-translation'
function LanguageSelector() {
const { locale, changeLocale, availableLocales } = useTranslation()
return (
<div>
<p>Current: {locale}</p>
<button onClick={() => changeLocale('es')}>Español</button>
<button onClick={() => changeLocale('fr')}>Français</button>
<button onClick={() => changeLocale('en')}>English</button>
</div>
)
}
Getting Current Locale (Lightweight)
If you only need the current locale without translation functionality:
import { useLocale } from '@/hooks/use-translation'
function MyComponent() {
const locale = useLocale()
return <span>Current language: {locale}</span>
}
Changing Locale (Without Translation)
If you only need to change the locale:
import { useChangeLocale } from '@/hooks/use-translation'
function LanguageButton() {
const changeLocale = useChangeLocale()
return <button onClick={() => changeLocale('es')}>Switch to Spanish</button>
}
Hook API Reference
useTranslation()
Main hook for translation functionality.
Returns:
{
t: (key: string, params?: Record<string, string | number>) => string,
locale: 'en' | 'es' | 'fr',
changeLocale: (newLocale: 'en' | 'es' | 'fr') => void,
availableLocales: ['en', 'es', 'fr'],
isLoading: boolean,
error: Error | null
}
Example:
const { t, locale, changeLocale, isLoading } = useTranslation()
if (isLoading) {
return <div>Loading translations...</div>
}
return (
<div>
<h1>{t('app.title')}</h1>
<button onClick={() => changeLocale('es')}>Español</button>
</div>
)
useLocale()
Lightweight hook that only returns the current locale.
Returns: 'en' | 'es' | 'fr'
useChangeLocale()
Hook that only provides locale change functionality.
Returns: (newLocale: 'en' | 'es' | 'fr') => void
Components
LanguageSwitcher
Dropdown component for selecting language, integrated in the app header.
import { LanguageSwitcher } from '@/components/LanguageSwitcher'
<LanguageSwitcher />
Features:
- Displays current language
- Dropdown with all available languages
- Visual indicator for selected language
- Automatically updates on selection
TranslationDemo
Comprehensive demonstration component showing all translation features. Access via:
- Navigation: Tools & Utilities → Translations
- Direct view:
translation-demo
Translation Keys Reference
App Level
app.title- Application titleapp.subtitle- Application subtitle
Navigation
navigation.dashboard- Dashboardnavigation.timesheets- Timesheetsnavigation.billing- Billingnavigation.payroll- Payrollnavigation.compliance- Compliancenavigation.expenses- Expensesnavigation.reports- Reports- Plus 20+ more navigation items
Common Actions
common.search- Searchcommon.filter- Filtercommon.export- Exportcommon.save- Savecommon.cancel- Cancelcommon.submit- Submitcommon.delete- Delete- Plus 30+ more common terms
Status Messages
common.success- Successcommon.error- Errorcommon.warning- Warningcommon.info- Informationcommon.loading- Loading...
Timesheets
timesheets.title- Timesheets titletimesheets.addTimesheet- Add timesheet actiontimesheets.status.draft- Draft statustimesheets.status.submitted- Submitted statustimesheets.status.approved- Approved statustimesheets.status.rejected- Rejected status- Plus 20+ more timesheet terms
Billing
billing.createInvoice- Create invoicebilling.invoiceNumber- Invoice numberbilling.status.paid- Paid statusbilling.status.overdue- Overdue status- Plus 20+ more billing terms
Payroll
payroll.processPayroll- Process payrollpayroll.grossPay- Gross paypayroll.netPay- Net pay- Plus 15+ more payroll terms
Validation
validation.required- Required field messagevalidation.invalidEmail- Invalid email messagevalidation.minLength- Minimum length (with {{min}} param)validation.maxLength- Maximum length (with {{max}} param)- Plus more validation messages
Errors
errors.generic- Generic error messageerrors.network- Network error messageerrors.notFound- Not found messageerrors.unauthorized- Unauthorized message
Adding New Languages
-
Create a new JSON file in
/src/data/translations/:# Example: German /src/data/translations/de.json -
Copy the structure from
en.jsonand translate all values -
Update the
use-translation.tshook:type Locale = 'en' | 'es' | 'fr' | 'de' const AVAILABLE_LOCALES: Locale[] = ['en', 'es', 'fr', 'de'] -
Update
LanguageSwitcher.tsx:const LOCALE_NAMES: Record<string, string> = { en: 'English', es: 'Español', fr: 'Français', de: 'Deutsch' }
Adding New Translation Keys
-
Add the key to all language files in the appropriate section:
en.json:
{ "mySection": { "myKey": "My English text" } }es.json:
{ "mySection": { "myKey": "Mi texto en español" } }fr.json:
{ "mySection": { "myKey": "Mon texte en français" } } -
Use in your component:
const { t } = useTranslation() const text = t('mySection.myKey')
Best Practices
1. Organize by Feature
Group related translations together:
{
"timesheets": {
"title": "...",
"addTimesheet": "...",
"status": {
"draft": "...",
"approved": "..."
}
}
}
2. Use Descriptive Keys
// Good
t('timesheets.status.approved')
t('billing.invoice.overdue')
// Bad
t('text1')
t('message')
3. Keep Parameters Simple
{
"greeting": "Hello, {{name}}!",
"itemCount": "{{count}} items selected"
}
4. Provide Context in Comments
{
"common": {
// Used on all save buttons
"save": "Save",
// Used for destructive actions
"delete": "Delete"
}
}
5. Handle Pluralization
{
"notifications": {
"unreadCount": "{{count}} unread"
}
}
Then handle in code:
const count = 1
const text = count === 1
? t('notifications.unreadSingular', { count })
: t('notifications.unreadCount', { count })
6. Test All Languages
Always verify translations in all supported languages before deployment.
Performance Considerations
- Lazy Loading: Translations are loaded on-demand when language changes
- Caching: Once loaded, translations are cached in memory
- Persistent Storage: Selected language is stored in KV store, not re-fetched
- Minimal Re-renders: Hook uses React hooks efficiently to minimize re-renders
Troubleshooting
Translation Not Showing
- Check the key exists in the translation file
- Verify the file is valid JSON
- Check browser console for errors
- Ensure the key path is correct (case-sensitive)
Language Not Changing
- Verify the locale is in
AVAILABLE_LOCALES - Check browser console for loading errors
- Clear KV store if needed:
spark.kv.delete('app-locale')
Missing Translation
If a translation key is missing, the system will:
- Return the key itself as a fallback
- Log a warning to console (in development)
- Attempt to load from default language (English)
Examples
Full Component Example
import { useTranslation } from '@/hooks/use-translation'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
export function TimesheetApproval({ timesheet }) {
const { t } = useTranslation()
const handleApprove = () => {
// Approval logic
toast.success(t('timesheets.status.approved'))
}
const handleReject = () => {
// Rejection logic
toast.error(t('timesheets.status.rejected'))
}
return (
<Card>
<h2>{t('timesheets.title')}</h2>
<p>{t('timesheets.hoursWorked')}: {timesheet.hours}</p>
<div>
<Button onClick={handleApprove}>
{t('timesheets.approveTimesheet')}
</Button>
<Button variant="destructive" onClick={handleReject}>
{t('timesheets.rejectTimesheet')}
</Button>
</div>
</Card>
)
}
Form Validation Example
import { useTranslation } from '@/hooks/use-translation'
import { useForm } from 'react-hook-form'
export function MyForm() {
const { t } = useTranslation()
const { register, formState: { errors } } = useForm()
return (
<form>
<input
{...register('email', {
required: t('validation.required'),
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: t('validation.invalidEmail')
}
})}
/>
{errors.email && <span>{errors.email.message}</span>}
</form>
)
}
Summary
The translation system provides a robust, scalable solution for internationalization in the WorkForce Pro platform. With JSON-based storage, easy parameter interpolation, and persistent user preferences, it enables seamless multi-language support while maintaining clean, maintainable code.
For a live demonstration of all features, navigate to Tools & Utilities → Translations in the application.