Multi-Language Implementation Plan for WealthVN
Target Languages: English (en) + Vietnamese (vi)
Framework: react-i18next + date-fns locales
Estimated Time: 27-35 hours
Status: Phase 9 - Documentation (Complete) ✅
Overall Progress: 97% Complete
Last Updated: 2025-11-11
Current Progress Tracking
✅ Completed Modules (100% Translated - All 10 Modules)
-
Activity Module (23 files) - Using
useTranslation("activity")- Complete EN/VI translation files
- All components updated
-
Dashboard Module (5 files) - Using
useTranslation("dashboard")- Complete EN/VI translation files
- All widgets and charts translated
-
Holdings Module (10 files) - Using
useTranslation("holdings")- Complete EN/VI translation files
- Tables and filters fully translated
-
Assets Module (7 files) - Using
useTranslation("assets")- Complete EN/VI translation files
- Asset management UI translated
-
Settings Module (multiple files) - Using
useTranslation("settings")- 521+ lines of translations per language
- 58+ components using translations
- All settings pages translated
-
Accounts Module (6 files) - Using
useTranslation("accounts")- Complete EN/VI translation files
- All account pages translated
- Contribution limits, holdings, performance charts
-
Income Module (2 files) - Using
useTranslation("income")- Complete EN/VI translation files
- All components updated
-
Onboarding Module (4 files) - Using
useTranslation("onboarding")- Complete EN/VI translation files
- All onboarding steps translated
-
Performance Module (1 file) - Using
useTranslation("performance")- Complete EN/VI translation files
- Performance page fully translated
-
Trading Module (3 files) - Using
useTranslation("trading")✅- Complete EN/VI translation files (145 lines each)
- Dashboard, activity selector, settings all translated
- KPI cards (P/L, Core Performance, Analytics), charts, and open positions
- Settings preferences (trade matching, display, calculation options)
- All components fully internationalized
📊 Module Status Summary
| Module | Files | Translation Files | Components Updated | Status |
|---|---|---|---|---|
| Activity | 23 | ✅ Complete | ✅ All updated | ✅ Done |
| Dashboard | 5 | ✅ Complete | ✅ All updated | ✅ Done |
| Holdings | 10 | ✅ Complete | ✅ All updated | ✅ Done |
| Assets | 7 | ✅ Complete | ✅ All updated | ✅ Done |
| Settings | 58+ | ✅ Complete | ✅ All updated | ✅ Done |
| Accounts | 6 | ✅ Complete | ✅ All updated | ✅ Done |
| Income | 2 | ✅ Complete | ✅ All updated | ✅ Done |
| Onboarding | 4 | ✅ Complete | ✅ All updated | ✅ Done |
| Performance | 1 | ✅ Complete | ✅ All updated | ✅ Done |
| Trading | 3 | ✅ Complete (145) | ✅ All updated | ✅ Done |
🎯 Current Phase: Testing & Deployment
Phase 9 Documentation: ✅ COMPLETE
All documentation successfully created:
- ✅ Developer i18n guide (
docs/i18n-guide.md) - ✅ Translation contribution guide (
docs/translation-guide.md) - ✅ README updated with language support section
- ✅ Documentation links added to README
📝 Remaining Work
- ✅ Update Income translation files (DONE)
- ✅ Update Income page components (DONE)
- ✅ Verify Onboarding module (DONE)
- ✅ Verify Performance module (DONE)
- ✅ Run final build verification (DONE - Build successful)
- ✅ Create comprehensive i18n developer guide (DONE)
- ✅ Create translation contribution guide (DONE)
- ✅ Update README with language support (DONE)
- ⏳ Manual testing in both languages (User handles)
- ⏳ Deployment preparation (User handles)
Table of Contents
- Overview
- Current State Analysis
- Technical Architecture
- Implementation Phases
- File Structure
- Translation Organization
- Testing Strategy
- Rollout Plan
Overview
Goal
Implement full internationalization (i18n) support for WealthVN with English and Vietnamese language options, allowing users to switch languages dynamically through the Settings interface.
Key Features
- ✅ Runtime language switching (no reload required)
- ✅ Persistent language preference in SQLite database
- ✅ Type-safe translation keys with TypeScript
- ✅ Locale-specific date, number, and currency formatting
- ✅ Organized namespace structure for maintainability
- ✅ Works in both Desktop (Tauri) and Web modes
Technology Stack
- i18n Framework: react-i18next v13+
- Date Formatting: date-fns with locale support
- Number/Currency: JavaScript Intl API
- Storage: SQLite via existing
app_settingstable - Fallback: Always English if translation missing
Current State Analysis
✅ Existing Infrastructure
date-fnsv4.1.0 already installed- Settings system fully functional (Rust backend + TypeScript context)
- SQLite key-value storage in
app_settingstable - Settings UI in
src/pages/settings/appearance/
❌ Missing Infrastructure
- No i18next packages installed
- No translation files or directory structure
- No language field in Settings model (backend or frontend)
- All text currently hardcoded in English
📊 Scope Analysis
- 137 page files (.tsx) to potentially translate
- 27 component files with user-facing text
- 12 namespaces implemented for organization
- 2 languages (extensible for future additions)
Technical Architecture
Data Flow
User selects language in Settings UI
↓
Settings Context updates
↓
Backend persists to SQLite (app_settings table)
↓
i18n instance switches language
↓
All components re-render with new translations
Storage Schema
Existing app_settings table (no migration needed):
CREATE TABLE app_settings (
setting_key TEXT PRIMARY KEY,
setting_value TEXT NOT NULL
);
New key-value pair:
setting_key: "language"
setting_value: "en" | "vi"
Type Safety
TypeScript namespaces will be defined in src/locales/types.ts:
export const resources = {
en: { common, settings, dashboard, ... },
vi: { common, settings, dashboard, ... }
} as const;
export type Resources = typeof resources;
Implementation Phases
Phase 1: Infrastructure Setup (4-6 hours)
Task 1.1: Install Dependencies
pnpm add i18next react-i18next
Packages:
i18next- Core i18n enginereact-i18next- React bindings
Task 1.2: Create Directory Structure
mkdir -p src/locales/en src/locales/vi
Structure:
src/locales/
├── en/
│ ├── common.json
│ ├── settings.json
│ ├── dashboard.json
│ ├── holdings.json
│ ├── activity.json
│ ├── accounts.json
│ ├── goals.json
│ ├── income.json
│ ├── assets.json
│ └── errors.json
├── vi/
│ └── [same 10 files]
├── index.ts
└── types.ts
Task 1.3: Create i18n Configuration
File: src/locales/index.ts
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
// Import all namespaces
import enCommon from "./en/common.json";
import enSettings from "./en/settings.json";
// ... (all imports)
import viCommon from "./vi/common.json";
import viSettings from "./vi/settings.json";
// ... (all imports)
export const resources = {
en: {
common: enCommon,
settings: enSettings,
dashboard: enDashboard,
holdings: enHoldings,
activity: enActivity,
accounts: enAccounts,
goals: enGoals,
income: enIncome,
assets: enAssets,
errors: enErrors,
},
vi: {
common: viCommon,
settings: viSettings,
dashboard: viDashboard,
holdings: viHoldings,
activity: viActivity,
accounts: viAccounts,
goals: viGoals,
income: viIncome,
assets: viAssets,
errors: viErrors,
},
} as const;
i18n.use(initReactI18next).init({
resources,
lng: "en", // Default language (will be overridden by settings)
fallbackLng: "en",
defaultNS: "common",
ns: [
"common",
"settings",
"dashboard",
"holdings",
"activity",
"accounts",
"goals",
"income",
"assets",
"errors",
],
interpolation: {
escapeValue: false, // React already escapes
},
react: {
useSuspense: false, // Disable suspense for better error handling
},
});
export default i18n;
Task 1.4: Create TypeScript Definitions
File: src/locales/types.ts
import { resources } from "./index";
declare module "i18next" {
interface CustomTypeOptions {
defaultNS: "common";
resources: (typeof resources)["en"];
}
}
Task 1.5: Create Initial JSON Files
Create empty JSON structure for all 20 files (10 x 2 languages).
Example structure for en/common.json:
{
"appName": "WealthVN",
"actions": {
"save": "Save",
"cancel": "Cancel",
"delete": "Delete",
"edit": "Edit",
"add": "Add",
"search": "Search",
"filter": "Filter",
"export": "Export",
"import": "Import",
"back": "Back",
"next": "Next",
"finish": "Finish",
"close": "Close",
"confirm": "Confirm"
},
"navigation": {
"dashboard": "Dashboard",
"holdings": "Holdings",
"activity": "Activity",
"accounts": "Accounts",
"settings": "Settings"
}
}
Phase 2: Backend Changes (2-3 hours)
Task 2.1: Update Rust Settings Model
File: src-core/src/settings/settings_model.rs
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Settings {
pub theme: String,
pub font: String,
pub base_currency: String,
pub instance_id: String,
pub onboarding_completed: bool,
pub auto_update_check_enabled: bool,
pub menu_bar_visible: bool,
pub is_pro: bool,
pub sync_enabled: bool,
pub language: String, // ← NEW FIELD
}
impl Default for Settings {
fn default() -> Self {
Self {
theme: "dark".to_string(),
font: "font-mono".to_string(),
base_currency: "".to_string(),
instance_id: "".to_string(),
onboarding_completed: false,
auto_update_check_enabled: true,
menu_bar_visible: true,
is_pro: false,
sync_enabled: true,
language: "en".to_string(), // ← NEW FIELD with default
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct SettingsUpdate {
pub theme: Option<String>,
pub font: Option<String>,
pub base_currency: Option<String>,
pub onboarding_completed: Option<bool>,
pub auto_update_check_enabled: Option<bool>,
pub menu_bar_visible: Option<bool>,
pub is_pro: Option<bool>,
pub sync_enabled: Option<bool>,
pub language: Option<String>, // ← NEW FIELD
}
Task 2.2: Update Settings Repository
File: src-core/src/settings/settings_repository.rs
Add language field mapping in:
get_settings()method - retrieve from key-value storeupdate_settings()method - persist to key-value store
Example changes:
// In get_settings()
if let Some(language_value) = settings_map.get("language") {
settings.language = language_value.clone();
}
// In update_settings()
if let Some(language) = &update_data.language {
settings_to_save.push(NewAppSetting {
setting_key: "language".to_string(),
setting_value: language.clone(),
});
}
Task 2.3: Rebuild and Test Backend
cd src-core && cargo build
cd ../src-tauri && cargo build
Phase 3: Frontend Foundation (3-4 hours)
Task 3.1: Update TypeScript Settings Interface
File: src/lib/types.ts
export interface Settings {
theme: string;
font: string;
baseCurrency: string;
onboardingCompleted: boolean;
autoUpdateCheckEnabled: boolean;
menuBarVisible: boolean;
isPro: boolean;
syncEnabled: boolean;
language: string; // ← NEW FIELD
}
Task 3.2: Initialize i18n in App
File: src/App.tsx
import { useEffect } from 'react';
import i18n from '@/locales';
import { useSettings } from '@/lib/settings-provider';
function App() {
const { settings } = useSettings();
// Sync language on mount and settings change
useEffect(() => {
if (settings?.language && i18n.language !== settings.language) {
i18n.changeLanguage(settings.language);
}
}, [settings?.language]);
return (
// ... existing app structure
);
}
Task 3.3: Update Settings Provider
File: src/lib/settings-provider.tsx
import { useTranslation } from 'react-i18next';
export function SettingsProvider({ children }: { children: React.ReactNode }) {
const { i18n } = useTranslation();
// ... existing code ...
// Sync i18n when settings change
useEffect(() => {
if (settings?.language && i18n.language !== settings.language) {
i18n.changeLanguage(settings.language);
}
}, [settings?.language, i18n]);
// Add updateLanguage helper
const updateLanguage = async (language: string) => {
try {
await updateSettings({ language });
await i18n.changeLanguage(language);
} catch (error) {
console.error('Failed to update language:', error);
}
};
return (
<SettingsContext.Provider value={{
...existingValues,
updateLanguage // ← NEW METHOD
}}>
{children}
</SettingsContext.Provider>
);
}
Phase 4: Language Selector Component (2-3 hours)
Task 4.1: Create Language Selector
File: src/components/language-selector.tsx
import { useTranslation } from 'react-i18next';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@wealthvn/ui';
import { useSettings } from '@/lib/settings-provider';
const LANGUAGES = [
{ code: 'en', name: 'English', flag: '🇬🇧' },
{ code: 'vi', name: 'Tiếng Việt', flag: '🇻🇳' },
] as const;
export function LanguageSelector() {
const { t } = useTranslation('settings');
const { settings, updateLanguage } = useSettings();
const handleLanguageChange = async (languageCode: string) => {
await updateLanguage(languageCode);
};
return (
<div className="space-y-2">
<label className="text-sm font-medium">{t('language.label')}</label>
<Select value={settings?.language ?? 'en'} onValueChange={handleLanguageChange}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
{LANGUAGES.map((lang) => (
<SelectItem key={lang.code} value={lang.code}>
<span className="flex items-center gap-2">
<span>{lang.flag}</span>
<span>{lang.name}</span>
</span>
</SelectItem>
))}
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground">{t('language.description')}</p>
</div>
);
}
Task 4.2: Add to Appearance Settings
File: src/pages/settings/appearance/appearance-form.tsx
import { LanguageSelector } from '@/components/language-selector';
export function AppearanceForm() {
return (
<div className="space-y-6">
{/* Existing theme and font selectors */}
<LanguageSelector /> {/* ← ADD THIS */}
{/* Rest of form */}
</div>
);
}
Phase 5: Translation Content Creation (10-12 hours)
Task 5.1: Extract English Text
Priority order:
- Navigation components (
src/pages/layouts/navigation/) - Settings pages (
src/pages/settings/*) - Dashboard (
src/pages/dashboard/) - Holdings (
src/pages/holdings/) - Activity (
src/pages/activity/) - Remaining pages
Task 5.2: Populate English JSON Files
Create comprehensive English translations organized by namespace.
Example en/settings.json:
{
"title": "Settings",
"appearance": {
"title": "Appearance",
"theme": {
"label": "Theme",
"light": "Light",
"dark": "Dark",
"system": "System"
},
"font": {
"label": "Font",
"mono": "Monospace",
"sans": "Sans Serif"
},
"language": {
"label": "Language",
"description": "Select your preferred language"
}
},
"accounts": {
"title": "Accounts",
"description": "Manage your investment accounts"
}
}
Task 5.3: Create Vietnamese Translations
- Translate all English content to Vietnamese
- Consider native speaker review for quality
- Maintain same JSON structure for consistency
Example vi/settings.json:
{
"title": "Cài đặt",
"appearance": {
"title": "Giao diện",
"theme": {
"label": "Chủ đề",
"light": "Sáng",
"dark": "Tối",
"system": "Hệ thống"
},
"font": {
"label": "Phông chữ",
"mono": "Monospace",
"sans": "Sans Serif"
},
"language": {
"label": "Ngôn ngữ",
"description": "Chọn ngôn ngữ ưa thích của bạn"
}
}
}
Phase 6: Component Updates (6-8 hours)
Task 6.1: Update Components to Use Translations
Replace hardcoded strings with t() function calls.
Before:
<h1>Dashboard</h1>
<button>Add Account</button>
After:
import { useTranslation } from 'react-i18next';
function Component() {
const { t } = useTranslation('dashboard');
return (
<>
<h1>{t('title')}</h1>
<button>{t('actions.addAccount')}</button>
</>
);
}
Task 6.2: Priority Components
-
Navigation (
src/pages/layouts/navigation/)- Sidebar menu items
- Mobile navigation
- Breadcrumbs
-
Settings Pages (
src/pages/settings/)- All settings forms
- Labels and descriptions
-
Dashboard (
src/pages/dashboard/)- Cards and charts
- Summary labels
-
Core Features
- Holdings table
- Activity forms
- Account management
Phase 7: Formatting Utilities (2-3 hours)
Task 7.1: Create Formatting Helper
File: src/lib/i18n-utils.ts
import { format, formatDistance, formatRelative } from "date-fns";
import { enUS, vi } from "date-fns/locale";
import i18n from "@/locales";
const LOCALE_MAP = {
en: enUS,
vi: vi,
};
export function getDateFnsLocale() {
const currentLang = i18n.language as keyof typeof LOCALE_MAP;
return LOCALE_MAP[currentLang] || LOCALE_MAP.en;
}
export function formatDate(date: Date | string, formatStr = "PPP"): string {
const dateObj = typeof date === "string" ? new Date(date) : date;
return format(dateObj, formatStr, { locale: getDateFnsLocale() });
}
export function formatCurrency(
amount: number,
currency: string,
locale?: string,
): string {
const currentLocale = locale || (i18n.language === "vi" ? "vi-VN" : "en-US");
return new Intl.NumberFormat(currentLocale, {
style: "currency",
currency: currency,
}).format(amount);
}
export function formatNumber(
value: number,
options?: Intl.NumberFormatOptions,
): string {
const currentLocale = i18n.language === "vi" ? "vi-VN" : "en-US";
return new Intl.NumberFormat(currentLocale, options).format(value);
}
export function formatPercent(value: number, decimals = 2): string {
const currentLocale = i18n.language === "vi" ? "vi-VN" : "en-US";
return new Intl.NumberFormat(currentLocale, {
style: "percent",
minimumFractionDigits: decimals,
maximumFractionDigits: decimals,
}).format(value / 100);
}
Task 7.2: Update Components Using Formatters
Replace direct formatting with utility functions:
import { formatCurrency, formatDate, formatPercent } from "@/lib/i18n-utils";
// Instead of:
`$${amount.toFixed(2)}`;
// Use:
formatCurrency(amount, "USD");
Phase 8: Testing (3-4 hours)
Task 8.1: Unit Tests
Create tests for:
- Language switching logic
- Translation key existence
- Formatting utilities
Example test file: src/lib/i18n-utils.test.ts
import { describe, it, expect, beforeEach } from "vitest";
import i18n from "@/locales";
import { formatCurrency, formatDate } from "./i18n-utils";
describe("i18n utilities", () => {
beforeEach(async () => {
await i18n.changeLanguage("en");
});
it("formats currency in English", () => {
const result = formatCurrency(1000, "USD");
expect(result).toContain("1,000");
});
it("formats currency in Vietnamese", async () => {
await i18n.changeLanguage("vi");
const result = formatCurrency(1000, "VND");
expect(result).toContain("1.000");
});
});
Task 8.2: Manual Testing Checklist
- Language switches correctly in settings
- All navigation items translated
- Settings page fully translated
- Dashboard displays correctly in both languages
- Date formats change with language
- Currency formats respect locale
- Language persists after app restart
- Works in both Desktop and Web modes
- No console errors or missing keys
- Mobile layout displays correctly
Task 8.3: Translation Coverage Check
# Script to find untranslated strings (hardcoded text)
rg '"[A-Z][a-zA-Z\s]+"' src/pages src/components --type tsx
Phase 9: Documentation (2-3 hours) ✅
Task 9.1: Create i18n Developer Guide ✅
File: docs/i18n-guide.md (Created)
Contents:
- ✅ How to add new translations
- ✅ Namespace organization (11 namespaces documented)
- ✅ Best practices (7 key practices)
- ✅ Common patterns (page headers, forms, tables, errors)
- ✅ Formatting guidelines (date, currency, number, percentage)
- ✅ Testing translations (manual and automated)
- ✅ Troubleshooting guide (5 common issues)
- ✅ Advanced topics and resources
Task 9.2: Update README ✅
Added sections about language support:
- ✅ Multi-language feature in Key Features
- ✅ Supported languages (English, Vietnamese)
- ✅ How to change language (user guide)
- ✅ How to contribute translations
- ✅ Links to i18n guides in Documentation section
Task 9.3: Create Translation Contribution Guide ✅
File: docs/translation-guide.md (Created)
Comprehensive guide for adding new languages:
- ✅ Quick start for new translators
- ✅ Translation file structure (11 namespaces)
- ✅ Translation process (step-by-step)
- ✅ Translation guidelines (consistency, context, quality)
- ✅ Testing instructions
- ✅ Contribution workflow (PR submission)
- ✅ Maintenance and community resources
Phase 10: Deployment & Rollout (1-2 hours)
Task 10.1: Build Verification
# Frontend build
pnpm build
# Desktop build
pnpm tauri build
# Web mode test
pnpm run dev:web
Task 10.2: Migration Path
- No database migration required (using existing key-value store)
- New users get "en" as default
- Existing users will have language field auto-populated with "en"
Task 10.3: Release Notes
Document new feature:
- Language selector in Settings > Appearance
- English and Vietnamese support
- Automatic date/number/currency localization
File Structure
Complete Directory Tree
src/
├── locales/
│ ├── en/
│ │ ├── common.json # Common UI elements, navigation
│ │ ├── settings.json # Settings page
│ │ ├── dashboard.json # Dashboard page
│ │ ├── holdings.json # Holdings page
│ │ ├── activity.json # Activity page
│ │ ├── accounts.json # Accounts page
│ │ ├── goals.json # Goals page
│ │ ├── income.json # Income page
│ │ ├── assets.json # Assets page
│ │ └── errors.json # Error messages
│ ├── vi/
│ │ └── [same structure]
│ ├── index.ts # i18n configuration
│ └── types.ts # TypeScript definitions
├── components/
│ └── language-selector.tsx # NEW: Language picker component
├── lib/
│ ├── i18n-utils.ts # NEW: Formatting utilities
│ ├── types.ts # MODIFIED: Add language to Settings
│ └── settings-provider.tsx # MODIFIED: Sync language with i18n
└── pages/
└── settings/appearance/
└── appearance-form.tsx # MODIFIED: Add language selector
Translation Organization
Namespace Strategy
common - Shared UI Elements
- App name
- Common actions (save, cancel, delete, etc.)
- Navigation menu
- General labels
settings - Settings Pages
- All settings categories
- Form labels and descriptions
- Validation messages
dashboard - Dashboard
- Widget titles
- Chart labels
- Summary cards
holdings - Holdings Page
- Table headers
- Filter options
- Action buttons
activity - Activity Page
- Activity types
- Form fields
- Import/export labels
accounts - Accounts Page
- Account types
- Form fields
- Balance labels
goals - Goals Page
- Goal creation
- Progress labels
- Allocation UI
income - Income Tracking
- Income sources
- Frequency labels
- Summary text
assets - Asset Management
- Asset types
- Market data labels
- Price displays
errors - Error Messages
- Validation errors
- API errors
- General error messages
Testing Strategy
1. Unit Tests
- Translation key coverage
- Formatting utilities
- Language switching logic
2. Integration Tests
- Settings persistence
- Language sync between components
- Backend/frontend communication
3. Manual Testing
- Visual inspection in both languages
- Layout integrity (Vietnamese text may be longer)
- Mobile responsiveness
- Desktop/Web mode parity
4. Accessibility Testing
- Screen reader compatibility
- Keyboard navigation
- Focus management during language switch
Rollout Plan
Pre-Release
- Complete Phase 1-4 (foundation)
- Internal testing with English only
- Complete Phase 5-7 (translations and updates)
- Internal testing with both languages
Beta Release
- Deploy to beta testers
- Gather feedback on translations
- Fix issues and refine translations
Production Release
- Final testing on all platforms
- Update documentation
- Deploy to production
- Monitor for issues
Post-Release
- Gather user feedback
- Refine translations based on feedback
- Plan for additional languages (if requested)
Extension Points for Future Languages
Adding a New Language (e.g., Spanish)
-
Create translation files:
mkdir src/locales/es
# Copy and translate all 10 JSON files -
Update i18n config:
// src/locales/index.ts
import esCommon from './es/common.json';
// ... other imports
export const resources = {
en: { ... },
vi: { ... },
es: { // ← ADD NEW LANGUAGE
common: esCommon,
// ... other namespaces
}
} as const; -
Add to language selector:
const LANGUAGES = [
{ code: "en", name: "English", flag: "🇬🇧" },
{ code: "vi", name: "Tiếng Việt", flag: "🇻🇳" },
{ code: "es", name: "Español", flag: "🇪🇸" }, // ← ADD NEW
]; -
Add date-fns locale:
import { es } from "date-fns/locale";
const LOCALE_MAP = {
en: enUS,
vi: vi,
es: es, // ← ADD NEW
};
Risk Mitigation
Potential Issues & Solutions
Issue: Long Vietnamese text breaks layout
Solution: Test all layouts with Vietnamese text; use CSS text-overflow,
line-clamp, or adjust container widths.
Issue: Missing translations
Solution: Fallback to English always enabled; TypeScript will catch missing keys during development.
Issue: Date/number formatting inconsistencies
Solution: Centralized formatting utilities ensure consistency; comprehensive test coverage.
Issue: Performance with large translation files
Solution: Namespace-based code splitting; only load needed namespaces per page.
Issue: Backend language field not persisting
Solution: Comprehensive testing of Settings repository; verify key-value store operations.
Success Metrics
Definition of Done
- i18n infrastructure implemented
- Language selector functional
- Settings persist correctly
- Date/number/currency formatting works
- Both Desktop and Web modes work
- No console errors
- All user-facing text translatable (100% complete)
- English translations complete (100%)
- Vietnamese translations complete (100%)
- Build verification passing
- Manual tests passing
- Documentation complete
- Production deployment successful
Quality Metrics
- Translation Coverage: >95% of user-facing text
- Test Coverage: >80% for i18n utilities
- Performance: Language switch less than 500ms
- User Satisfaction: Positive feedback from Vietnamese users
Timeline Summary
| Phase | Duration | Status |
|---|---|---|
| Phase 1: Infrastructure | 4-6 hours | ✅ Complete |
| Phase 2: Backend | 2-3 hours | ✅ Complete |
| Phase 3: Frontend Foundation | 3-4 hours | ✅ Complete |
| Phase 4: Language Selector | 2-3 hours | ✅ Complete |
| Phase 5: Translation Content | 10-12 hours | ✅ Complete |
| Phase 6: Component Updates | 6-8 hours | ✅ Complete |
| Phase 7: Formatting Utilities | 2-3 hours | ✅ Complete |
| Phase 8: Testing | 3-4 hours | 🔄 User Handles |
| Phase 9: Documentation | 2-3 hours | ✅ Complete |
| Phase 10: Deployment | 1-2 hours | ⏳ User Handles |
| Total | 27-35 hours | ~97% Done |
Next Steps
- Review this plan with stakeholders
- Get approval for timeline and scope
- Start Phase 1 - Infrastructure setup
- Iterate through phases sequentially
- Test continuously during implementation
- Deploy when all phases complete
References
Last Updated: 2025-11-11
Status: Phase 9 - Documentation (Complete - 97% Overall) ✅
Ready for Deployment: Phase 8 & 10 handled by user ⏳
Modules Completed: 10/10 (100%)