# GoodGo Frontend: Tham chiếu nhanh triển khai i18n + A11y ## 🎯 Những phát hiện chính trong nháy mắt ### Trạng thái hiện tại - ✅ **Next.js 15** với App Router (cấu trúc tốt) - ✅ **React 18** + TypeScript (an toàn kiểu) - ✅ **Tailwind CSS** với hỗ trợ dark mode (theme dựa trên HSL) - ✅ **Thư viện component tốt** (~35 component) - ✅ **Một số kiến thức cơ bản về A11y** đã có (HTML ngữ nghĩa, ARIA labels, skip link) - ❌ **CHƯA thiết lập i18n** (tất cả tiếng Việt được hardcode) - ❌ **Thiếu sót A11y** (quản lý focus, một số ARIA còn thiếu, tương phản màu chưa xác định) ### Điểm vào chiến lược cho việc triển khai #### 1. **Điểm vào i18n** (Ưu tiên 1) ``` Files to modify for i18n: ├── app/layout.tsx → Add i18n provider ├── middleware.ts → Add locale routing ├── app/(public)/layout.tsx → Navigation text ├── app/(auth)/login/page.tsx → Form labels + errors ├── app/(auth)/register/page.tsx → Form labels + errors ├── components/listings/listing-form-steps.tsx → Multi-step form labels ├── components/search/filter-bar.tsx → Filter options + city names ├── lib/validations/*.ts → Zod error messages └── [All other components with text] Total files to update: ~25-30 files with hardcoded strings ``` #### 2. **Sửa lỗi A11y quan trọng** (Ưu tiên 1.5) ``` Components needing A11y updates: ├── components/ui/dialog.tsx → Focus trapping + focus restoration ├── components/listings/image-gallery.tsx → Keyboard nav + ARIA ├── components/search/filter-bar.tsx → Proper labeling + ARIA ├── app/(dashboard)/layout.tsx → Tab focus management └── Across all forms → Error message association Tasks: - Add focus trapping in modals - Verify color contrast (WCAG AA) - Add aria-busy to loading states - Add proper aria-label to icon buttons - Link form errors to inputs with aria-describedby ``` #### 3. **Cấu trúc file Message cho i18n** ``` public/locales/ ├── en.json │ ├── common: { home, search, dashboard, logout, ... } │ ├── auth: { login, register, email, password, ... } │ ├── property: { apartment, house, villa, ... } │ ├── transaction: { sale, rent, ... } │ ├── directions: { north, south, east, ... } │ ├── status: { draft, active, sold, ... } │ ├── validation: { required, min_length, ... } │ └── errors: { oauth_failed, access_denied, ... } └── vi.json └── [Same structure] ``` --- ## 📋 Checklist triển khai ### Giai đoạn 1: Thiết lập (2-3 giờ) - [ ] Cài đặt gói `next-intl` - [ ] Tạo các file message (en.json, vi.json) - [ ] Cập nhật next.config.js cho routing i18n - [ ] Tạo cấu hình i18n (config.ts) - [ ] Cập nhật middleware.ts để phát hiện locale - [ ] Bao bọc root layout bằng provider i18n ### Giai đoạn 2: Tái cấu trúc cốt lõi (6-8 giờ) - [ ] Cập nhật root layout & metadata - [ ] Tái cấu trúc tất cả validation (Zod) để sử dụng messages - [ ] Trích xuất chuỗi component sang useTranslations() - [ ] Cập nhật tất cả các enum (TRANSACTION_TYPES, PROPERTY_TYPES, v.v.) để sử dụng i18n - [ ] Cập nhật các page layout (public, auth, dashboard) - [ ] Cập nhật toàn bộ nội dung trang ### Giai đoạn 3: Cập nhật component (4-6 giờ) - [ ] Cập nhật tất cả các component UI - [ ] Cập nhật các component form - [ ] Cập nhật các component navigation - [ ] Cập nhật các component search/filter - [ ] Cập nhật form đăng tin (listing form) ### Giai đoạn 4: Sửa lỗi A11y (4-6 giờ) - [ ] Sửa quản lý focus trong dialog - [ ] Thêm focus trapping - [ ] Cập nhật liên kết lỗi form (aria-describedby) - [ ] Thêm aria-busy cho trạng thái loading - [ ] Thêm aria-label cho các nút biểu tượng - [ ] Xác minh tương phản màu - [ ] Cập nhật thiết lập test cho i18n ### Giai đoạn 5: Test & QA (3-4 giờ) - [ ] Test cả hai locale trên tất cả các trang - [ ] Chạy kiểm tra khả năng tiếp cận bằng axe DevTools - [ ] Test điều hướng bằng bàn phím - [ ] Test tương thích screen reader - [ ] Cập nhật unit test cho i18n --- ## 🗣️ Kiểm kê nội dung văn bản ### Navigation & Layout (~15 mục) | Vị trí | Văn bản | Trạng thái | |----------|------|--------| | Public header | Trang chủ, Tìm kiếm, Đăng nhập, Đăng ký | ❌ Hardcoded | | Dashboard nav | 8 mục nav | ❌ Hardcoded | | Footer | 4 phần | ❌ Hardcoded | ### Forms & Validation (~40+ mục) | Vị trí | Loại | Số lượng | Trạng thái | |----------|------|-------|--------| | Login form | Nhãn + lỗi | 8 | ❌ Hardcoded | | Register form | Nhãn + lỗi | 10 | ❌ Hardcoded | | Listing form | Nhãn đa bước | 25+ | ❌ Hardcoded | | Search filters | Nhãn tùy chọn | 30+ | ❌ Hardcoded | | Zod validation | Thông báo lỗi | 20+ | ❌ Hardcoded | ### Enum & Hằng số (~50+ mục) | File | Mục | Trạng thái | |------|-------|--------| | TRANSACTION_TYPES | 2 nhãn | ❌ Hardcoded | | PROPERTY_TYPES | 6 nhãn | ❌ Hardcoded | | LISTING_STATUSES | 8 nhãn | ❌ Hardcoded | | DIRECTIONS | 8 nhãn | ❌ Hardcoded | | CITIES | 13 tên | ❌ Hardcoded | | PRICE_RANGES | 6 khoảng | ❌ Hardcoded | ### Nội dung trang (~30 mục) | Trang | Phần | Trạng thái | |------|----------|--------| | Trang landing | Hero, search, stats, CTA | ❌ Hardcoded | | Kết quả tìm kiếm | Không có kết quả, loading, header | ❌ Hardcoded | | Dashboard | Tiêu đề phần, trạng thái rỗng | ❌ Hardcoded | --- ## 🔑 Các file quan trọng cho i18n ### Các file phải cập nhật (Blocker) 1. **middleware.ts** — Định tuyến locale 2. **app/layout.tsx** — Thiết lập provider i18n 3. **lib/validations/*.ts** — Tích hợp messages 4. **lib/*.ts** — Bất kỳ xử lý thông báo lỗi API nào ### Các file ưu tiên cao 1. **app/(public)/layout.tsx** — Navigation 2. **app/(auth)/login/page.tsx** — Form xác thực 3. **components/listings/listing-form-steps.tsx** — Forms 4. **components/search/filter-bar.tsx** — Bộ lọc ### Các file ưu tiên trung bình 1. Tất cả các component trang 2. Tất cả các component UI có văn bản 3. Các component error boundary --- ## ♿ Ưu tiên triển khai A11y ### Sửa lỗi quan trọng WCAG 2.1 AA 1. **Quản lý focus** (Cấp A) - Thêm focus trap trong `dialog.tsx` - Khôi phục focus khi đóng dialog - Chỉ báo focus rõ ràng trên tất cả các nút 2. **Tương phản màu** (Cấp AA) - Chạy kiểm tra axe DevTools - Sửa văn bản có tỷ lệ < 4.5:1 - Sửa đồ họa có tỷ lệ < 3:1 3. **Khả năng tiếp cận form** (Cấp A) - Liên kết tất cả thông báo lỗi với aria-describedby - Gắn nhãn đúng cách với htmlFor - Nhóm fieldset cho các form phức tạp 4. **Trạng thái Loading** (Cấp A) - Thêm aria-busy cho spinner - Thêm aria-label kèm ngữ cảnh 5. **Nút biểu tượng** (Cấp A) - Tất cả các nút chỉ có biểu tượng cần aria-label - Nút bật/tắt theme đã có nhãn ✓ ### Các cải tiến A11y nên có - Skip link đã có sẵn ✓ - HTML ngữ nghĩa đã được sử dụng ✓ - Role="alert" trên lỗi ✓ - aria-invalid trên các trường form ✓ --- ## 📦 Các phụ thuộc cần thêm ```bash npm install next-intl # No new devDependencies needed if using next-intl # Testing with mocked i18n available ``` **Tổng kích thước cài đặt:** ~500KB đã minify --- ## 🧪 Chiến lược test ### Unit Tests ```typescript // vitest.setup.ts - Mock i18n vi.mock('next-intl', () => ({ useTranslations: () => (key) => mockMessages[key] })); ``` ### Component Tests ```typescript // Test both locales describe('LoginForm', () => { it('renders Vietnamese labels', () => { ... }); it('renders English labels', () => { ... }); }); ``` ### E2E Tests ```typescript // Test locale switching - /en/login → English - /vi/login → Vietnamese - /en/dashboard → English dashboard ``` --- ## 📊 Lộ trình ước tính | Giai đoạn | Thời gian | Công sức | |-------|----------|--------| | Thiết lập | 2-3h | Thấp | | Tái cấu trúc cốt lõi | 6-8h | Trung bình | | Components | 4-6h | Trung bình | | Sửa lỗi A11y | 4-6h | Thấp-Trung bình | | Testing | 3-4h | Trung bình | | **Tổng** | **19-27h** | **~3-4 ngày** | --- ## 🚀 Thứ tự triển khai (Khuyến nghị) 1. **Thiết lập hạ tầng i18n** (tạo nền tảng) 2. **Cập nhật middleware + root layout** (kích hoạt routing) 3. **Trích xuất & tập trung tất cả văn bản** (công việc chính) 4. **Sửa các vấn đề A11y** (song song với #3) 5. **Test kỹ lưỡng** (xác minh cuối cùng) --- ## 💡 Cơ hội thắng nhanh Những việc có thể làm ngay: 1. Tạo cấu trúc file message (30 phút) 2. Thêm focus trap cho dialog (30 phút) 3. Thêm aria-busy cho spinner (20 phút) 4. Kiểm tra tương phản màu (1 giờ) 5. Aria-label cho nút biểu tượng (30 phút) --- ## 📝 Ghi chú khi triển khai ### Phát hiện locale (middleware) ```typescript // Check in order: URL > cookie > header > default function getLocale(request) { // 1. URL pathname: /en/* or /vi/* // 2. Cookie: goodgo_locale // 3. Header: Accept-Language // 4. Default: vi } ``` ### Chiến lược dự phòng Message ```typescript // If translation missing, use English as fallback // Otherwise fallback to Vietnamese (primary) ``` ### Cân nhắc về hiệu năng - Giữ các file message < 100KB mỗi file - Lazy load message theo trang nếu cần - Static generation cho các trang quan trọng về SEO --- **Cập nhật lần cuối:** Ngày 9 tháng 4 năm 2026 **Phiên bản:** 1.0 - Trước triển khai **Độ tin cậy:** Cao