|
| 1 | +# VerifyWise Localization Implementation Guide |
| 2 | + |
| 3 | +## 📋 Overview |
| 4 | +This document outlines the complete localization (i18n) implementation strategy for VerifyWise, including technical approach, implementation patterns, and rollback instructions. |
| 5 | + |
| 6 | +## 🎯 Implementation Approach |
| 7 | + |
| 8 | +### Technology Stack |
| 9 | +- **Library**: react-i18next (v15.7.3) |
| 10 | +- **Language Detection**: i18next-browser-languagedetector (v8.2.0) |
| 11 | +- **Core**: i18next (v25.5.2) |
| 12 | +- **Languages**: English (en), German (de) - expandable to more |
| 13 | + |
| 14 | +### Architecture Decision |
| 15 | +We chose **react-i18next** over other solutions because: |
| 16 | +1. **React Integration**: Seamless hooks and component support |
| 17 | +2. **Browser Detection**: Automatic language detection from browser settings |
| 18 | +3. **Lazy Loading**: Can load translations on-demand (future optimization) |
| 19 | +4. **Interpolation**: Supports variables in translations |
| 20 | +5. **Pluralization**: Built-in plural forms support |
| 21 | +6. **TypeScript**: Full type safety support |
| 22 | + |
| 23 | +## 📁 File Structure |
| 24 | + |
| 25 | +``` |
| 26 | +src/ |
| 27 | +├── i18n/ |
| 28 | +│ ├── i18n.ts # Main configuration file |
| 29 | +│ ├── locales/ |
| 30 | +│ │ ├── en.json # English translations |
| 31 | +│ │ └── de.json # German translations |
| 32 | +│ └── utils/ |
| 33 | +│ └── statusTranslations.ts # Dynamic translation utilities |
| 34 | +├── presentation/ |
| 35 | +│ └── components/ |
| 36 | +│ ├── LanguageSwitcher/ # Language selector component |
| 37 | +│ ├── StatusBadge/ # Example localized component |
| 38 | +│ └── LocalizationTest/ # Test component (removable) |
| 39 | +``` |
| 40 | + |
| 41 | +## 🔧 Implementation Patterns |
| 42 | + |
| 43 | +### 1. Basic Component Localization |
| 44 | +```typescript |
| 45 | +// Import |
| 46 | +import { useTranslation } from "react-i18next"; |
| 47 | + |
| 48 | +// In component |
| 49 | +const MyComponent = () => { |
| 50 | + const { t } = useTranslation(); |
| 51 | + |
| 52 | + return ( |
| 53 | + <div> |
| 54 | + <h1>{t('pageTitle')}</h1> |
| 55 | + <Button>{t('buttons.save')}</Button> |
| 56 | + </div> |
| 57 | + ); |
| 58 | +}; |
| 59 | +``` |
| 60 | + |
| 61 | +### 2. Dynamic Status/Value Translation |
| 62 | +```typescript |
| 63 | +// For dynamic values like status, risk levels |
| 64 | +import { useStatusTranslation } from "../../../i18n/utils/statusTranslations"; |
| 65 | + |
| 66 | +const Component = () => { |
| 67 | + const { translateStatus, translateRiskLevel } = useStatusTranslation(); |
| 68 | + |
| 69 | + return ( |
| 70 | + <span>{translateStatus("In Progress")}</span> // → "In Bearbeitung" in German |
| 71 | + ); |
| 72 | +}; |
| 73 | +``` |
| 74 | + |
| 75 | +### 3. Translation File Structure |
| 76 | +```json |
| 77 | +// en.json |
| 78 | +{ |
| 79 | + "sidebar": { |
| 80 | + "dashboard": "Dashboard", |
| 81 | + "riskManagement": "Risk Management" |
| 82 | + }, |
| 83 | + "status": { |
| 84 | + "inprogress": "In Progress", |
| 85 | + "completed": "Completed" |
| 86 | + }, |
| 87 | + "riskLevel": { |
| 88 | + "high": "High Risk", |
| 89 | + "medium": "Medium Risk" |
| 90 | + } |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +### 4. Passing Translation to Non-Hook Functions |
| 95 | +```typescript |
| 96 | +// When you can't use hooks (e.g., in utility functions) |
| 97 | +const getMenuItems = (t: any) => [ |
| 98 | + { name: t('sidebar.dashboard') } |
| 99 | +]; |
| 100 | + |
| 101 | +// In component |
| 102 | +const { t } = useTranslation(); |
| 103 | +const menu = getMenuItems(t); |
| 104 | +``` |
| 105 | + |
| 106 | +## 📊 Translation Coverage Analysis |
| 107 | + |
| 108 | +### Current Status (What Was Implemented) |
| 109 | +| Component | Strings | Status | |
| 110 | +|-----------|---------|--------| |
| 111 | +| Sidebar Menu | ~20 | ✅ Complete | |
| 112 | +| Risk Management Page | ~15 | ✅ Complete | |
| 113 | +| Status Values | 9 | ✅ Complete | |
| 114 | +| Risk Levels | 7 | ✅ Complete | |
| 115 | +| **Total Completed** | **51** | **3.9% of total** | |
| 116 | + |
| 117 | +### Remaining Work |
| 118 | +| Page | Estimated Strings | Priority | |
| 119 | +|------|-------------------|----------| |
| 120 | +| Framework | ~180 | High | |
| 121 | +| Tasks | ~150 | High | |
| 122 | +| Vendors | ~120 | Medium | |
| 123 | +| Model Inventory | ~110 | Medium | |
| 124 | +| Dashboard/Home | ~80 | High | |
| 125 | +| Others | ~614 | Low-Medium | |
| 126 | +| **Total Remaining** | **~1,254** | - | |
| 127 | + |
| 128 | +### Time Estimates |
| 129 | +- **Translation**: 20-25 hours (2-3 min/string for German) |
| 130 | +- **Implementation**: 40-50 hours (adding t() calls) |
| 131 | +- **Testing**: 10-15 hours |
| 132 | +- **Total**: ~75-90 hours |
| 133 | + |
| 134 | +## 🚀 Implementation Strategy |
| 135 | + |
| 136 | +### Phase 1: Core Pages (High Priority) |
| 137 | +1. Dashboard/Home |
| 138 | +2. Framework (ISO compliance) |
| 139 | +3. Tasks |
| 140 | +4. Project View |
| 141 | + |
| 142 | +### Phase 2: Data Management (Medium Priority) |
| 143 | +1. Vendors |
| 144 | +2. Model Inventory |
| 145 | +3. Policy Dashboard |
| 146 | +4. Training Registry |
| 147 | +5. File Manager |
| 148 | + |
| 149 | +### Phase 3: Analytics & Settings (Low Priority) |
| 150 | +1. AI Trust Center |
| 151 | +2. Reporting |
| 152 | +3. Fairness Dashboard |
| 153 | +4. Event Tracker |
| 154 | +5. Settings |
| 155 | + |
| 156 | +## 🔄 Rollback Instructions |
| 157 | + |
| 158 | +To completely remove all localization changes made during this session: |
| 159 | + |
| 160 | +### 1. Remove npm packages |
| 161 | +```bash |
| 162 | +npm uninstall react-i18next i18next i18next-browser-languagedetector |
| 163 | +``` |
| 164 | + |
| 165 | +### 2. Delete created files |
| 166 | +```bash |
| 167 | +rm -rf src/i18n/ |
| 168 | +rm -rf src/presentation/components/LanguageSwitcher/ |
| 169 | +rm -rf src/presentation/components/StatusBadge/ |
| 170 | +rm -rf src/presentation/components/LocalizationTest/ |
| 171 | +``` |
| 172 | + |
| 173 | +### 3. Revert file changes |
| 174 | + |
| 175 | +#### src/App.tsx |
| 176 | +- Remove line 28: `import "./i18n/i18n";` |
| 177 | + |
| 178 | +#### src/presentation/components/Sidebar/index.tsx |
| 179 | +- Remove line 22: `import { useTranslation } from "react-i18next";` |
| 180 | +- Remove line 61: `import LanguageSwitcher from "../LanguageSwitcher";` |
| 181 | +- Remove line 198: `const { t } = useTranslation();` |
| 182 | +- Change line 74: `const getMenuItems = (openTasksCount: number, t: any): MenuItem[]` |
| 183 | + back to: `const getMenuItems = (openTasksCount: number): MenuItem[]` |
| 184 | +- Change all `t('sidebar.xxx')` back to hardcoded strings: |
| 185 | + - `t('sidebar.dashboard')` → `"Dashboard"` |
| 186 | + - `t('sidebar.riskManagement')` → `"Risk Management"` |
| 187 | + - `t('sidebar.tasks')` → `"Tasks"` |
| 188 | + - etc. for all menu items |
| 189 | +- Change line 161-180: Revert `getOtherMenuItems` function to `const other: MenuItem[]` array |
| 190 | +- Remove lines 234-235 translation passing, change to: |
| 191 | + - `const menu = getMenuItems(openTasksCount);` |
| 192 | + - Remove `const other = getOtherMenuItems(t);` and use hardcoded array |
| 193 | +- Remove line 828: `<LanguageSwitcher />` |
| 194 | +- Change tooltips back to hardcoded strings |
| 195 | + |
| 196 | +#### src/presentation/pages/RiskManagement/index.tsx |
| 197 | +- Remove line 2: `import { useTranslation } from "react-i18next";` |
| 198 | +- Remove line 58: `const { t } = useTranslation();` |
| 199 | +- Change all `t('riskManagement.xxx')` back to hardcoded strings |
| 200 | + |
| 201 | +### 4. Git commands to revert (if committed) |
| 202 | +```bash |
| 203 | +git diff HEAD~1 -- package.json package-lock.json |
| 204 | +git checkout HEAD~1 -- src/App.tsx |
| 205 | +git checkout HEAD~1 -- src/presentation/components/Sidebar/index.tsx |
| 206 | +git checkout HEAD~1 -- src/presentation/pages/RiskManagement/index.tsx |
| 207 | +``` |
| 208 | + |
| 209 | +## 💡 Best Practices for Future Implementation |
| 210 | + |
| 211 | +### 1. Translation Keys Naming |
| 212 | +- Use nested structure: `page.section.element` |
| 213 | +- Example: `vendors.table.headers.name` |
| 214 | +- Keep keys lowercase with dots as separators |
| 215 | + |
| 216 | +### 2. Shared Translations |
| 217 | +- Common actions: `common.save`, `common.cancel` |
| 218 | +- Status values: `status.pending`, `status.completed` |
| 219 | +- Error messages: `errors.required`, `errors.invalid` |
| 220 | + |
| 221 | +### 3. Dynamic Content |
| 222 | +- Use interpolation: `t('welcome', { name: userName })` |
| 223 | +- Translation: `"welcome": "Welcome, {{name}}!"` |
| 224 | + |
| 225 | +### 4. Pluralization |
| 226 | +```typescript |
| 227 | +t('items', { count: 5 }) // "5 items" |
| 228 | +t('items', { count: 1 }) // "1 item" |
| 229 | +``` |
| 230 | + |
| 231 | +### 5. Missing Translations |
| 232 | +- Always provide fallback text |
| 233 | +- Use: `t('key', 'Fallback Text')` |
| 234 | + |
| 235 | +### 6. Performance |
| 236 | +- Lazy load translations for large apps |
| 237 | +- Use namespaces to split translations |
| 238 | +- Cache translations in localStorage |
| 239 | + |
| 240 | +## 🧪 Testing Approach |
| 241 | + |
| 242 | +### 1. Manual Testing |
| 243 | +- Change browser language settings |
| 244 | +- Use language switcher component |
| 245 | +- Verify all visible text changes |
| 246 | + |
| 247 | +### 2. Automated Testing |
| 248 | +```typescript |
| 249 | +// Example test |
| 250 | +it('should display German text when language is de', () => { |
| 251 | + i18n.changeLanguage('de'); |
| 252 | + render(<Component />); |
| 253 | + expect(screen.getByText('Risikomanagement')).toBeInTheDocument(); |
| 254 | +}); |
| 255 | +``` |
| 256 | + |
| 257 | +### 3. Translation Validation |
| 258 | +- Check for missing keys |
| 259 | +- Verify all languages have same keys |
| 260 | +- Validate interpolation variables |
| 261 | + |
| 262 | +## 📝 Notes for Developers |
| 263 | + |
| 264 | +### Starting Fresh |
| 265 | +When implementing localization from scratch: |
| 266 | +1. Start with high-traffic pages |
| 267 | +2. Create translation files incrementally |
| 268 | +3. Use extraction tools to find all strings |
| 269 | +4. Implement language switcher last |
| 270 | +5. Test with at least 2 languages |
| 271 | + |
| 272 | +### Common Pitfalls to Avoid |
| 273 | +- Don't hardcode language codes |
| 274 | +- Don't mix translation keys with display text |
| 275 | +- Don't forget to translate error messages |
| 276 | +- Don't ignore date/number formatting |
| 277 | +- Don't assume text length (German is ~30% longer) |
| 278 | + |
| 279 | +## 🔗 Resources |
| 280 | +- [react-i18next Documentation](https://react.i18next.com/) |
| 281 | +- [i18next Documentation](https://www.i18next.com/) |
| 282 | +- [Language Codes (ISO 639-1)](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) |
| 283 | + |
| 284 | +--- |
| 285 | +*Document created: 2025-09-21* |
| 286 | +*Last updated: 2025-09-21* |
| 287 | +*Status: Implementation POC completed, ready for rollback and fresh start* |
0 commit comments