10. Multi-Language Support

10.1 Supported Languages

Language Code Native Name Translation Keys Status
English en English 45+ Complete (Default)
Hindi hi ?????? 45+ Complete
Spanish es Español 45+ Complete
French fr Français 45+ Complete
German de Deutsch 45+ Complete
Translation Coverage: 45+ keys × 5 languages = 225+ translations

10.2 Translation System Architecture

File Structure

packages/desktop-app/src/
+-- i18n/
¦   +-- config.ts           # i18n configuration
¦   +-- translations/
¦   ¦   +-- en.json        # English translations
¦   ¦   +-- hi.json        # Hindi translations
¦   ¦   +-- es.json        # Spanish translations
¦   ¦   +-- fr.json        # French translations
¦   ¦   +-- de.json        # German translations
¦   +-- index.ts           # Export all translations
+-- contexts/
    +-- LanguageContext.tsx  # Language state management

10.3 Translation Keys & Content

File: packages/desktop-app/src/i18n/translations/en.json

Complete English Translation File

{
  "app": {
    "title": "Currency Denomination Calculator",
    "subtitle": "Optimize your cash distribution"
  },
  "navigation": {
    "calculator": "Calculator",
    "history": "History",
    "bulkUpload": "Bulk Upload",
    "settings": "Settings"
  },
  "calculator": {
    "title": "Denomination Calculator",
    "amountLabel": "Enter Amount",
    "amountPlaceholder": "Enter amount...",
    "currencyLabel": "Currency",
    "modeLabel": "Optimization Mode",
    "calculateButton": "Calculate",
    "calculating": "Calculating...",
    "results": {
      "title": "Breakdown Results",
      "totalNotes": "Total Notes",
      "totalCoins": "Total Coins",
      "totalDenominations": "Total Denominations",
      "denomination": "Denomination",
      "type": "Type",
      "count": "Count",
      "totalValue": "Total Value",
      "copyButton": "Copy to Clipboard",
      "exportCSV": "Export CSV",
      "exportJSON": "Export JSON"
    }
  },
  "history": {
    "title": "Calculation History",
    "quickAccess": "Quick Access",
    "fullHistory": "Full History",
    "filterByCurrency": "Filter by Currency",
    "dateRange": "Date Range",
    "clearFilters": "Clear Filters",
    "noHistory": "No calculations yet",
    "columns": {
      "id": "ID",
      "date": "Date",
      "amount": "Amount",
      "currency": "Currency",
      "mode": "Mode",
      "denoms": "Denominations",
      "actions": "Actions"
    },
    "actions": {
      "view": "View Details",
      "delete": "Delete",
      "export": "Export"
    }
  },
  "bulkUpload": {
    "title": "Bulk Upload & Processing",
    "downloadTemplate": "Download CSV Template",
    "dragDrop": "Drag & drop your file here",
    "orClickBrowse": "or click to browse",
    "chooseFile": "Choose File",
    "supportedFormats": "Supported: CSV, PDF, Word, Images",
    "selectedFile": "Selected File:",
    "fileFormat": "Format:",
    "fileSize": "Size:",
    "removeFile": "Remove File",
    "saveToHistory": "Save to history",
    "uploadButton": "Upload & Process",
    "processing": "Processing Data...",
    "results": {
      "processedFile": "Processed File:",
      "processedAt": "Processed:",
      "summary": {
        "total": "Total",
        "success": "Success",
        "failed": "Failed",
        "time": "Time"
      },
      "uploadAnother": "Upload Another",
      "exportCSV": "Export CSV",
      "exportJSON": "Export JSON"
    }
  },
  "settings": {
    "title": "Settings",
    "appearance": {
      "title": "Appearance",
      "theme": "Theme",
      "light": "Light",
      "dark": "Dark",
      "system": "System"
    },
    "language": {
      "title": "Language & Region",
      "label": "Language"
    },
    "preferences": {
      "title": "Default Preferences",
      "defaultCurrency": "Default Currency",
      "defaultMode": "Default Optimization Mode",
      "autoSave": "Auto-save to history"
    },
    "dataManagement": {
      "title": "Data Management",
      "historyStats": "Total calculations:",
      "databaseSize": "Database size:",
      "exportAll": "Export All History",
      "clearAll": "Clear All History",
      "resetSettings": "Reset All Settings"
    },
    "saveButton": "Save Settings",
    "resetButton": "Reset"
  },
  "currencies": {
    "INR": "Indian Rupee",
    "USD": "US Dollar",
    "EUR": "Euro",
    "GBP": "British Pound"
  },
  "modes": {
    "greedy": "Greedy (Standard)",
    "balanced": "Balanced",
    "minimize_large": "Minimize Large",
    "minimize_small": "Minimize Small"
  },
  "errors": {
    "amountRequired": "Amount is required",
    "amountInvalid": "Invalid amount format",
    "amountTooLarge": "Amount too large (max 15 digits)",
    "amountNegative": "Amount must be greater than 0",
    "fileRequired": "Please select a file",
    "fileInvalid": "Unsupported file type",
    "fileTooLarge": "File too large",
    "uploadFailed": "Upload failed. Please try again.",
    "networkError": "Network error. Please check your connection.",
    "serverError": "Server error. Please contact support."
  },
  "success": {
    "calculated": "Calculation completed successfully",
    "saved": "Saved to history",
    "copied": "Copied to clipboard",
    "exported": "Exported successfully",
    "uploaded": "File uploaded successfully",
    "deleted": "Deleted successfully",
    "settingsUpdated": "Settings updated"
  }
}

Hindi Translation Sample

File: packages/desktop-app/src/i18n/translations/hi.json

{
  "app": {
    "title": "?????? ????????? ?????????",
    "subtitle": "???? ???? ????? ?? ???????? ????"
  },
  "navigation": {
    "calculator": "?????????",
    "history": "??????",
    "bulkUpload": "???? ?????",
    "settings": "????????"
  },
  "calculator": {
    "title": "????????? ?????????",
    "amountLabel": "???? ???? ????",
    "amountPlaceholder": "???? ???? ????...",
    "currencyLabel": "??????",
    "modeLabel": "??????? ???",
    "calculateButton": "???? ????",
    "calculating": "???? ?? ??? ??..."
  }
}

10.4 Language Context Implementation

File: packages/desktop-app/src/contexts/LanguageContext.tsx

import React, { createContext, useContext, useState, useEffect } from 'react';
import en from '../i18n/translations/en.json';
import hi from '../i18n/translations/hi.json';
import es from '../i18n/translations/es.json';
import fr from '../i18n/translations/fr.json';
import de from '../i18n/translations/de.json';

type Language = 'en' | 'hi' | 'es' | 'fr' | 'de';

interface LanguageContextType {
  language: Language;
  setLanguage: (lang: Language) => void;
  t: (key: string) => string;
}

const translations = { en, hi, es, fr, de };

const LanguageContext = createContext(undefined);

export const LanguageProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const [language, setLanguageState] = useState('en');

  // Load saved language from localStorage
  useEffect(() => {
    const saved = localStorage.getItem('language') as Language;
    if (saved && translations[saved]) {
      setLanguageState(saved);
    }
  }, []);

  // Save language changes
  const setLanguage = (lang: Language) => {
    setLanguageState(lang);
    localStorage.setItem('language', lang);
  };

  // Translation function with nested key support
  const t = (key: string): string => {
    const keys = key.split('.');
    let value: any = translations[language];

    for (const k of keys) {
      value = value?.[k];
    }

    return value || key; // Return key if translation not found
  };

  return (
    
      {children}
    
  );
};

export const useLanguage = () => {
  const context = useContext(LanguageContext);
  if (!context) {
    throw new Error('useLanguage must be used within LanguageProvider');
  }
  return context;
};

10.5 Usage in Components

Example 1: CalculatorPage.tsx

import { useLanguage } from '../contexts/LanguageContext';

const CalculatorPage = () => {
  const { t } = useLanguage();

  return (
    

{t('calculator.title')}

{results && (

{t('calculator.results.title')}

{t('calculator.results.totalNotes')}: {results.summary.total_notes}

{t('calculator.results.totalCoins')}: {results.summary.total_coins}

)}
); };

Example 2: SettingsPage.tsx (Language Selector)

const SettingsPage = () => {
  const { language, setLanguage, t } = useLanguage();

  return (
    

{t('settings.language.title')}

{t('settings.language.description')}

); };

Example 3: BulkUploadPage.tsx (Dynamic Messages)

const BulkUploadPage = () => {
  const { t } = useLanguage();
  const [uploadStatus, setUploadStatus] = useState<'idle' | 'uploading' | 'success' | 'error'>('idle');

  const getStatusMessage = () => {
    switch (uploadStatus) {
      case 'uploading':
        return t('bulkUpload.processing');
      case 'success':
        return t('success.uploaded');
      case 'error':
        return t('errors.uploadFailed');
      default:
        return t('bulkUpload.dragDrop');
    }
  };

  return (
    

{t('bulkUpload.title')}

{getStatusMessage()}

); };

Example 4: Error Messages

const validateAmount = (amount: string) => {
  const { t } = useLanguage();
  
  if (!amount) {
    return { valid: false, error: t('errors.amountRequired') };
  }
  
  if (isNaN(Number(amount))) {
    return { valid: false, error: t('errors.amountInvalid') };
  }
  
  if (Number(amount) <= 0) {
    return { valid: false, error: t('errors.amountNegative') };
  }
  
  if (amount.length > 15) {
    return { valid: false, error: t('errors.amountTooLarge') };
  }
  
  return { valid: true };
};

10.6 App-Level Integration

Wrap App with LanguageProvider

File: packages/desktop-app/src/main.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { LanguageProvider } from './contexts/LanguageContext';
import './index.css';

ReactDOM.createRoot(document.getElementById('root')!).render(
  
    
      
    
  
);

10.7 RTL (Right-to-Left) Support

Future Enhancement: Planned for Arabic/Hebrew support

Planned Implementation

const LanguageProvider: React.FC = ({ children }) => {
  const [language, setLanguage] = useState('en');
  
  // Detect RTL languages
  const isRTL = ['ar', 'he'].includes(language);
  
  useEffect(() => {
    document.documentElement.dir = isRTL ? 'rtl' : 'ltr';
    document.documentElement.lang = language;
  }, [isRTL, language]);
  
  return (
    
      {children}
    
  );
};

RTL CSS Support

/* Automatic RTL adjustments */
[dir="rtl"] .sidebar {
  right: 0;
  left: auto;
}

[dir="rtl"] .text-left {
  text-align: right;
}

[dir="rtl"] .ml-2 {
  margin-left: 0;
  margin-right: 0.5rem;
}

10.8 Translation Management

Adding a New Language

Steps:

  1. Create new translation file: src/i18n/translations/{code}.json
  2. Copy structure from en.json
  3. Translate all 45+ keys
  4. Import in LanguageContext.tsx
  5. Add to Language type and translations object
  6. Add option to language selector in Settings

Translation Validation

// Utility to check for missing translations
function validateTranslations() {
  const baseKeys = Object.keys(flattenObject(en));
  const languages = ['hi', 'es', 'fr', 'de'];
  
  languages.forEach(lang => {
    const langKeys = Object.keys(flattenObject(translations[lang]));
    const missing = baseKeys.filter(key => !langKeys.includes(key));
    
    if (missing.length > 0) {
      console.warn(`Missing translations in ${lang}:`, missing);
    }
  });
}

function flattenObject(obj: any, prefix = ''): Record {
  return Object.keys(obj).reduce((acc, key) => {
    const fullKey = prefix ? `${prefix}.${key}` : key;
    
    if (typeof obj[key] === 'object' && obj[key] !== null) {
      Object.assign(acc, flattenObject(obj[key], fullKey));
    } else {
      acc[fullKey] = obj[key];
    }
    
    return acc;
  }, {} as Record);
}

10.9 Backend Language Support (Future)

Planned: Backend API responses can also be localized

Accept-Language Header

# FastAPI endpoint with language support
@router.get("/api/v1/metadata")
async def get_metadata(accept_language: str = Header(default='en')):
    """Return localized metadata."""
    language = accept_language[:2]  # Extract language code
    
    metadata = {
        'currencies': get_localized_currencies(language),
        'modes': get_localized_modes(language)
    }
    
    return metadata

def get_localized_currencies(lang: str) -> Dict:
    """Get currency names in specified language."""
    translations = {
        'en': {'INR': 'Indian Rupee', 'USD': 'US Dollar', ...},
        'hi': {'INR': '?????? ?????', 'USD': '??????? ????', ...},
        # ...
    }
    
    return translations.get(lang, translations['en'])
? Section Complete

This section covers complete multi-language support including 5 languages (45+ keys each = 225+ translations), React Context implementation, nested key support, localStorage persistence, component usage examples, RTL support planning, and translation management utilities.