# GoodGo Frontend: i18n + A11y Implementation Quick Reference ## 🎯 Key Findings at a Glance ### Current State - βœ… **Next.js 15** with App Router (well-structured) - βœ… **React 18** + TypeScript (type-safe) - βœ… **Tailwind CSS** with dark mode support (HSL-based theme) - βœ… **Good component library** (~35 components) - βœ… **Some A11y basics** in place (semantic HTML, ARIA labels, skip link) - ❌ **NO i18n setup** (everything hardcoded Vietnamese) - ❌ **A11y gaps** (focus management, some ARIA missing, color contrast TBD) ### Strategic Entry Points for Implementation #### 1. **i18n Entry Points** (Priority 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. **A11y Critical Fixes** (Priority 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. **Message File Structure for 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] ``` --- ## πŸ“‹ Implementation Checklist ### Phase 1: Setup (2-3 hours) - [ ] Install `next-intl` package - [ ] Create message files (en.json, vi.json) - [ ] Update next.config.js for i18n routing - [ ] Create i18n config (config.ts) - [ ] Update middleware.ts for locale detection - [ ] Wrap root layout with i18n provider ### Phase 2: Core Refactoring (6-8 hours) - [ ] Update root layout & metadata - [ ] Refactor all validations (Zod) to use messages - [ ] Extract component strings to useTranslations() - [ ] Update all enums (TRANSACTION_TYPES, PROPERTY_TYPES, etc.) to use i18n - [ ] Update page layouts (public, auth, dashboard) - [ ] Update all page content ### Phase 3: Component Updates (4-6 hours) - [ ] Update all UI components - [ ] Update form components - [ ] Update navigation components - [ ] Update search/filter components - [ ] Update listing form ### Phase 4: A11y Fixes (4-6 hours) - [ ] Fix focus management in dialogs - [ ] Add focus trapping - [ ] Update form error linking (aria-describedby) - [ ] Add aria-busy to loading states - [ ] Add aria-labels to icon buttons - [ ] Verify color contrast - [ ] Update test setup for i18n ### Phase 5: Testing & QA (3-4 hours) - [ ] Test both locales on all pages - [ ] Run axe DevTools accessibility audit - [ ] Test keyboard navigation - [ ] Test screen reader compatibility - [ ] Update unit tests for i18n --- ## πŸ—£οΈ Text Content Inventory ### Navigation & Layout (~15 items) | Location | Text | Status | |----------|------|--------| | Public header | Trang chα»§, TΓ¬m kiαΊΏm, Đăng nhαΊ­p, Đăng kΓ½ | ❌ Hardcoded | | Dashboard nav | 8 nav items | ❌ Hardcoded | | Footer | 4 sections | ❌ Hardcoded | ### Forms & Validation (~40+ items) | Location | Type | Count | Status | |----------|------|-------|--------| | Login form | Labels + errors | 8 | ❌ Hardcoded | | Register form | Labels + errors | 10 | ❌ Hardcoded | | Listing form | Multi-step labels | 25+ | ❌ Hardcoded | | Search filters | Option labels | 30+ | ❌ Hardcoded | | Zod validation | Error messages | 20+ | ❌ Hardcoded | ### Enums & Constants (~50+ items) | File | Items | Status | |------|-------|--------| | TRANSACTION_TYPES | 2 labels | ❌ Hardcoded | | PROPERTY_TYPES | 6 labels | ❌ Hardcoded | | LISTING_STATUSES | 8 labels | ❌ Hardcoded | | DIRECTIONS | 8 labels | ❌ Hardcoded | | CITIES | 13 names | ❌ Hardcoded | | PRICE_RANGES | 6 ranges | ❌ Hardcoded | ### Page Content (~30 items) | Page | Sections | Status | |------|----------|--------| | Landing page | Hero, search, stats, CTA | ❌ Hardcoded | | Search results | No results, loading, headers | ❌ Hardcoded | | Dashboard | Section titles, empty states | ❌ Hardcoded | --- ## πŸ”‘ Critical Files for i18n ### Must-Update Files (Blockers) 1. **middleware.ts** β€” Locale routing 2. **app/layout.tsx** β€” i18n provider setup 3. **lib/validations/*.ts** β€” Message integration 4. **lib/*.ts** β€” Any API error message handling ### High-Priority Files 1. **app/(public)/layout.tsx** β€” Navigation 2. **app/(auth)/login/page.tsx** β€” Auth forms 3. **components/listings/listing-form-steps.tsx** β€” Forms 4. **components/search/filter-bar.tsx** β€” Filters ### Medium-Priority Files 1. All page components 2. All UI components with text 3. Error boundary components --- ## β™Ώ A11y Implementation Priority ### WCAG 2.1 AA Critical Fixes 1. **Focus Management** (Level A) - Add focus trap in `dialog.tsx` - Restore focus on dialog close - Visible focus indicator on all buttons 2. **Color Contrast** (Level AA) - Run axe DevTools audit - Fix any < 4.5:1 ratio text - Fix < 3:1 ratio graphics 3. **Form Accessibility** (Level A) - Link all error messages with aria-describedby - Proper labeling with htmlFor - Fieldset grouping for complex forms 4. **Loading States** (Level A) - Add aria-busy to spinners - Add aria-label with context 5. **Icon Buttons** (Level A) - All icon-only buttons need aria-label - Theme toggle button already has label βœ“ ### Nice-to-Have A11y Enhancements - Skip link already present βœ“ - Semantic HTML already used βœ“ - Role="alert" on errors βœ“ - aria-invalid on form fields βœ“ --- ## πŸ“¦ Dependencies to Add ```bash npm install next-intl # No new devDependencies needed if using next-intl # Testing with mocked i18n available ``` **Total installation footprint:** ~500KB minified --- ## πŸ§ͺ Testing Strategy ### 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 ``` --- ## πŸ“Š Estimated Timeline | Phase | Duration | Effort | |-------|----------|--------| | Setup | 2-3h | Low | | Core Refactoring | 6-8h | Medium | | Components | 4-6h | Medium | | A11y Fixes | 4-6h | Low-Medium | | Testing | 3-4h | Medium | | **Total** | **19-27h** | **~3-4 days** | --- ## πŸš€ Implementation Order (Recommended) 1. **Setup i18n infrastructure** (creates foundation) 2. **Update middleware + root layout** (enables routing) 3. **Extract & centralize all text** (main work) 4. **Fix A11y issues** (parallelize with #3) 5. **Test thoroughly** (final verification) --- ## πŸ’‘ Quick Win Opportunities These can be done immediately: 1. Create message file structure (30 min) 2. Add focus trap to dialog (30 min) 3. Add aria-busy to spinners (20 min) 4. Color contrast audit (1 hour) 5. Icon button aria-labels (30 min) --- ## πŸ“ Notes for Implementation ### Locale Detection (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 } ``` ### Message Fallback Strategy ```typescript // If translation missing, use English as fallback // Otherwise fallback to Vietnamese (primary) ``` ### Performance Considerations - Keep message files < 100KB each - Lazy load per-page messages if needed - Static generation for SEO-critical pages --- **Last Updated:** April 9, 2026 **Version:** 1.0 - Pre-Implementation **Confidence:** High