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