Skip to main content

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)​

  1. Activity Module (23 files) - Using useTranslation("activity")

    • Complete EN/VI translation files
    • All components updated
  2. Dashboard Module (5 files) - Using useTranslation("dashboard")

    • Complete EN/VI translation files
    • All widgets and charts translated
  3. Holdings Module (10 files) - Using useTranslation("holdings")

    • Complete EN/VI translation files
    • Tables and filters fully translated
  4. Assets Module (7 files) - Using useTranslation("assets")

    • Complete EN/VI translation files
    • Asset management UI translated
  5. Settings Module (multiple files) - Using useTranslation("settings")

    • 521+ lines of translations per language
    • 58+ components using translations
    • All settings pages translated
  6. Accounts Module (6 files) - Using useTranslation("accounts")

    • Complete EN/VI translation files
    • All account pages translated
    • Contribution limits, holdings, performance charts
  7. Income Module (2 files) - Using useTranslation("income")

    • Complete EN/VI translation files
    • All components updated
  8. Onboarding Module (4 files) - Using useTranslation("onboarding")

    • Complete EN/VI translation files
    • All onboarding steps translated
  9. Performance Module (1 file) - Using useTranslation("performance")

    • Complete EN/VI translation files
    • Performance page fully translated
  10. 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​

ModuleFilesTranslation FilesComponents UpdatedStatus
Activity23✅ Complete✅ All updated✅ Done
Dashboard5✅ Complete✅ All updated✅ Done
Holdings10✅ Complete✅ All updated✅ Done
Assets7✅ Complete✅ All updated✅ Done
Settings58+✅ Complete✅ All updated✅ Done
Accounts6✅ Complete✅ All updated✅ Done
Income2✅ Complete✅ All updated✅ Done
Onboarding4✅ Complete✅ All updated✅ Done
Performance1✅ Complete✅ All updated✅ Done
Trading3✅ 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​

  1. ✅ Update Income translation files (DONE)
  2. ✅ Update Income page components (DONE)
  3. ✅ Verify Onboarding module (DONE)
  4. ✅ Verify Performance module (DONE)
  5. ✅ Run final build verification (DONE - Build successful)
  6. ✅ Create comprehensive i18n developer guide (DONE)
  7. ✅ Create translation contribution guide (DONE)
  8. ✅ Update README with language support (DONE)
  9. ⏳ Manual testing in both languages (User handles)
  10. ⏳ Deployment preparation (User handles)

Table of Contents​

  1. Overview
  2. Current State Analysis
  3. Technical Architecture
  4. Implementation Phases
  5. File Structure
  6. Translation Organization
  7. Testing Strategy
  8. 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_settings table
  • Fallback: Always English if translation missing

Current State Analysis​

✅ Existing Infrastructure​

  • date-fns v4.1.0 already installed
  • Settings system fully functional (Rust backend + TypeScript context)
  • SQLite key-value storage in app_settings table
  • 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 engine
  • react-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 store
  • update_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:

  1. Navigation components (src/pages/layouts/navigation/)
  2. Settings pages (src/pages/settings/*)
  3. Dashboard (src/pages/dashboard/)
  4. Holdings (src/pages/holdings/)
  5. Activity (src/pages/activity/)
  6. 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​

  1. Navigation (src/pages/layouts/navigation/)

    • Sidebar menu items
    • Mobile navigation
    • Breadcrumbs
  2. Settings Pages (src/pages/settings/)

    • All settings forms
    • Labels and descriptions
  3. Dashboard (src/pages/dashboard/)

    • Cards and charts
    • Summary labels
  4. 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​

  1. Complete Phase 1-4 (foundation)
  2. Internal testing with English only
  3. Complete Phase 5-7 (translations and updates)
  4. Internal testing with both languages

Beta Release​

  1. Deploy to beta testers
  2. Gather feedback on translations
  3. Fix issues and refine translations

Production Release​

  1. Final testing on all platforms
  2. Update documentation
  3. Deploy to production
  4. Monitor for issues

Post-Release​

  1. Gather user feedback
  2. Refine translations based on feedback
  3. Plan for additional languages (if requested)

Extension Points for Future Languages​

Adding a New Language (e.g., Spanish)​

  1. Create translation files:

    mkdir src/locales/es
    # Copy and translate all 10 JSON files
  2. 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;
  3. 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
    ];
  4. 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​

PhaseDurationStatus
Phase 1: Infrastructure4-6 hours✅ Complete
Phase 2: Backend2-3 hours✅ Complete
Phase 3: Frontend Foundation3-4 hours✅ Complete
Phase 4: Language Selector2-3 hours✅ Complete
Phase 5: Translation Content10-12 hours✅ Complete
Phase 6: Component Updates6-8 hours✅ Complete
Phase 7: Formatting Utilities2-3 hours✅ Complete
Phase 8: Testing3-4 hours🔄 User Handles
Phase 9: Documentation2-3 hours✅ Complete
Phase 10: Deployment1-2 hours⏳ User Handles
Total27-35 hours~97% Done

Next Steps​

  1. Review this plan with stakeholders
  2. Get approval for timeline and scope
  3. Start Phase 1 - Infrastructure setup
  4. Iterate through phases sequentially
  5. Test continuously during implementation
  6. 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%)