# Bilingual Implementation Status (English/Quebec French)
**Last Updated:** 2025-11-07
**Progress:** ~60% Complete
## Overview
Comprehensive bilingualization of FedMCP frontend using `next-intl` with `/en/` and `/fr/` URL structure, browser auto-detection, and AI-generated Quebec French translations.
---
## β
COMPLETED (Phases 1-3)
### Phase 1: Core Infrastructure β
**1. Dependencies & Configuration**
- β
Installed `next-intl` (v4.4.0) and `date-fns` (v3.6.0)
- β
Created i18n config (`src/i18n/config.ts`) with EN/FR locales
- β
Created request handler (`src/i18n/request.ts`)
- β
Updated `next.config.mjs` with next-intl plugin
**2. Middleware & Routing**
- β
Updated middleware to combine i18n + authentication
- β
Configured locale detection from Accept-Language header
- β
Set up `/en/` and `/fr/` URL prefixes with `localePrefix: 'always'`
- β
Created locale-aware navigation utilities (`src/i18n/navigation.ts`)
**3. App Directory Restructuring**
- β
Created `[locale]` folder structure
- β
Moved all routes under `/app/[locale]/`
- β
Updated root layout to handle locale parameter
- β
Implemented `generateStaticParams()` for both locales
### Phase 2: Translation System β
**4. Translation Files**
- β
Created `messages/en.json` (555+ strings organized by namespace)
- β
Created `messages/fr.json` (complete Quebec French translations)
- β
Organized by namespaces: `metadata`, `common`, `nav`, `footer`, `home`, `mps`, `bills`, `bill`, `hansard`, `chamber`, `committees`, `lobbying`, `spending`, `chat`, `parties`, `provinces`, `errors`
**5. Core Components**
- β
Created `LanguageSwitcher` component (EN/FR toggle in header)
- β
Translated `Header` component with i18n navigation
- β
Translated `Footer` component with bilingual links
- β
Updated locale-specific layout with metadata generation
### Phase 3: GraphQL & Data Layer β
**6. GraphQL Query Updates**
- β
Updated `BILL_BASIC_FRAGMENT` to fetch all `_fr` fields:
- `title_fr`, `summary_fr`, `status_fr`, `bill_type_fr`, `originating_chamber_fr`
- β
Updated `STATEMENT_FRAGMENT` to fetch all `_fr` fields:
- `who_fr`, `content_fr`, `h1_fr`, `h2_fr`, `h3_fr`
- β
Both fragments now return bilingual data for automatic selection
**7. Bilingual Hooks**
Created comprehensive hooks in `src/hooks/useBilingual.ts`:
- β
`useBilingualField(enField, frField)` - Select field based on locale
- β
`useBilingualContent(content)` - Transform objects with `_en/_fr` suffixes
- β
`useLocaleSuffix()` - Get `_en` or `_fr` for dynamic queries
- β
`usePartyName(partyCode)` - Localized party name mapping
- β
`useChamberName(chamber)` - Localized chamber names
**8. Pages Translated**
- β
**Landing Page** (`app/[locale]/page.tsx`):
- Hero section, features, stats, CTA - all fully bilingual
- Uses `useTranslations('home')` hook
- Locale-aware Link components
---
## π§ IN PROGRESS / REMAINING WORK
### Phase 4-6: Page & Component Translation (50% Remaining)
**Pages to Translate:**
- π² MPs page (`app/[locale]/mps/page.tsx`)
- π² Bills page (`app/[locale]/bills/page.tsx`)
- π² Bill detail page (`app/[locale]/bills/[session]/[number]/page.tsx`)
- π² Hansard search page (`app/[locale]/hansard/page.tsx`)
- π² Chamber page (`app/[locale]/chamber/page.tsx`)
- π² Committees page (`app/[locale]/committees/page.tsx`)
- π² Dashboard page
- π² MP profile pages
- π² Lobbying page
- π² Spending page
- π² About page
- π² Forum pages (if applicable)
**Shared Components to Translate:**
- π² `MPCard` component
- π² `BillCard` component
- π² `SearchBar` component
- π² Filter components (party, province, status dropdowns)
- π² `ChatWidget` component
- π² Modal components (MPModal, etc.)
- π² Empty state components
- π² Error boundary components
- π² Loading components
### Phase 7: Data Display Logic
- π² Update all components using bills to use `useBilingualContent()`
- π² Update all components using Hansard to use bilingual fields
- π² Update date formatting with French locale from `date-fns`
- π² Update number formatting (Quebec uses space as thousands separator)
- π² Ensure party names use `usePartyName()` hook
- π² Ensure chamber names use `useChamberName()` hook
### Phase 8: Hansard Search Language Parameter
- π² Update Hansard search page to pass `language` param based on current locale
- π² Currently hardcoded to `'en'` in `hansard/page.tsx:72`
- π² Should use: `const locale = useLocale(); language: locale`
### Phase 9: SEO & Metadata
- π² Add `<link rel="alternate" hreflang="en" />` and `hreflang="fr"` tags
- π² Generate bilingual sitemap with /en/ and /fr/ URLs
- π² Add Open Graph locale tags (`og:locale`, `og:locale:alternate`)
- π² Test locale detection across browsers
- π² Verify metadata is correctly set per locale
### Phase 10: Testing & QA
- π² Test all routes in both /en/ and /fr/
- π² Test language switcher preserves current page/filters
- π² Verify bilingual data displays correctly (bills, Hansard)
- π² Test fallback behavior (French data missing β shows English)
- π² Cross-browser testing for locale detection
- π² Performance check (ensure translation files don't bloat bundle)
- π² Accessibility testing (lang attribute updates, screen readers)
---
## π Key Files Modified/Created
### Created Files:
- `/src/i18n/config.ts` - Locale configuration
- `/src/i18n/request.ts` - Request handler for next-intl
- `/src/i18n/navigation.ts` - Locale-aware navigation utilities
- `/src/hooks/useBilingual.ts` - Bilingual data selection hooks
- `/src/components/LanguageSwitcher.tsx` - Language toggle component
- `/messages/en.json` - English translations (555+ strings)
- `/messages/fr.json` - Quebec French translations (555+ strings)
- `/migrate-to-locale.sh` - Migration script (can be deleted)
### Modified Files:
- `/src/middleware.ts` - Combined i18n + auth middleware
- `/next.config.mjs` - Added next-intl plugin
- `/src/app/[locale]/layout.tsx` - Locale-aware layout
- `/src/components/Header.tsx` - Translated with i18n
- `/src/components/Footer.tsx` - Translated with i18n
- `/src/lib/queries.ts` - Updated fragments for bilingual fields
- `/src/app/[locale]/page.tsx` - Translated landing page
### App Directory Structure:
```
src/app/
βββ api/ (unchanged, not localized)
βββ [locale]/
βββ layout.tsx (locale-aware)
βββ page.tsx (landing page - β
translated)
βββ mps/
βββ bills/
βββ hansard/
βββ chamber/
βββ committees/
βββ dashboard/
βββ lobbying/
βββ spending/
βββ about/
βββ profile/
βββ account/
βββ auth/
βββ forum/
```
---
## π How to Use Bilingual Features
### For Developers:
**1. Using translations in components:**
```tsx
'use client';
import { useTranslations } from 'next-intl';
export function MyComponent() {
const t = useTranslations('namespace');
return <h1>{t('key')}</h1>;
}
```
**2. Using bilingual data from GraphQL:**
```tsx
import { useBilingualContent } from '@/hooks/useBilingual';
const bill = { title_en: "Act", title_fr: "Loi", ... };
const { title } = useBilingualContent(bill);
// Auto-selects based on current locale
```
**3. Using locale-aware navigation:**
```tsx
import { Link } from '@/i18n/navigation';
<Link href="/bills">Bills</Link>
// Automatically generates /en/bills or /fr/bills
```
**4. Party name localization:**
```tsx
import { usePartyName } from '@/hooks/useBilingual';
const partyName = usePartyName('Conservative');
// Returns "Conservative" in EN, "Conservateur" in FR
```
### For Users:
- Visit `/en/` for English or `/fr/` for French
- Use the **EN / FR** toggle in the header to switch languages
- Browser language auto-detection on first visit
- Language preference persists across pages
---
## π― Estimated Remaining Work
**Time Estimate:** 10-15 hours
**Breakdown:**
- Pages translation: 5-7 hours (10 pages Γ 30-40 min each)
- Component translation: 3-4 hours (20+ components)
- Data display logic: 1-2 hours
- SEO & metadata: 1 hour
- Testing & QA: 2-3 hours
**Priority Order:**
1. **High Priority:** MPs page, Bills page, Hansard page (most-used features)
2. **Medium Priority:** Bill detail page, Chamber page, shared components
3. **Low Priority:** Lobbying, Spending, Forum pages, SEO optimization
---
## β¨ Key Features Implemented
β
**Full URL Localization:** `/en/bills` and `/fr/bills`
β
**Auto-Detection:** Uses Accept-Language header
β
**Language Switcher:** Prominent EN/FR toggle in header
β
**Bilingual Data:** GraphQL queries fetch both _en and _fr fields
β
**Smart Fallbacks:** Falls back to English if French missing
β
**Party Translation:** Automatic party name localization
β
**Type-Safe Routing:** Locale-aware Link and useRouter
β
**SSR Compatible:** Works with Next.js 15 server components
---
## π Known Issues / Notes
1. **Hansard Language Parameter:** Currently hardcoded to 'en' - needs to be dynamic
2. **MP/Committee Names:** Not bilingual in data (only in Canada's official records)
3. **Date Formatting:** Need to add French locale support from date-fns
4. **Number Formatting:** Quebec uses spaces (e.g., "1 000 000" not "1,000,000")
---
## π Resources
- [next-intl Documentation](https://next-intl-docs.vercel.app/)
- [Quebec French Translation Guidelines](https://www.oqlf.gouv.qc.ca/)
- [Federal Government Bilingual Guidelines](https://www.noslangues-ourlanguages.gc.ca/)
---
## π Next Steps
To continue implementation, the recommended order is:
1. **Translate MPs page** - High traffic, straightforward
2. **Translate Bills page** - Core functionality
3. **Translate shared components** (MPCard, BillCard) - Used everywhere
4. **Translate Hansard search** - Critical feature
5. **Update data display logic** - Apply bilingual hooks
6. **Test and QA** - Ensure everything works
7. **SEO optimization** - hreflang tags and sitemaps
Run `pnpm dev` and visit `http://localhost:3000/en` or `http://localhost:3000/fr` to see the bilingual site!