Skip to content

Localization (EN/FR) – strategy and policy

HealthArchive.ca supports English and French UI copy:

  • English is the default (canonical URLs are unprefixed, e.g. /archive).
  • French lives under /fr/... and is a convenience-only, automated translation.
  • English governs: if there is any inconsistency, the English version is authoritative.
  • Archived snapshots are never translated (they remain as-captured).

Locale codes

  • English: en-CA
  • French: fr-CA

See src/lib/i18n.ts for locale helpers.

Routing and URLs

  • The app is structured under src/app/[locale]/....
  • src/proxy.ts keeps English canonical URLs unprefixed and routes French under /fr.
  • Static assets and downloads (e.g. .md) are not localized (see localizeHref() logic).

Translation safety posture

French is explicitly labeled as alpha and may contain errors:

  • Global banner: src/components/i18n/FrenchTranslationBanner.tsx
  • Policy pages: src/components/policy/EnglishControlsNotice.tsx + French summaries

SEO:

  • /fr/... pages are marked noindex (see src/app/[locale]/layout.tsx).
  • Locale alternates are declared per route (hreflang for en-CA and fr-CA) via buildPageMetadata() in src/lib/metadata.ts.
  • Release steps are tracked in docs/deployment/bilingual-release-checklist.md.

Where copy lives

Prefer reusing canonical copy instead of duplicating it:

  • Canonical mission + disclaimers: src/lib/siteCopy.ts (getSiteCopy(locale))
  • Small localized values: src/lib/localized.ts (pickLocalized(locale, { en, fr }))
  • Locale access in client components: src/components/i18n/LocaleProvider.tsx (useLocale())
  • Locale access in server components: src/lib/resolveLocale.ts (resolveLocale(params))

Editing guidelines

  • Treat the English text as the source of truth.
  • Keep French meaning conservative and aligned with the safety posture:
  • independent, non-governmental
  • not medical advice / not current guidance
  • archived content may be incomplete/outdated
  • Keep the “English governs” language intact on /terms, /privacy, /governance.

Testing

Many route components are async server components. In tests, render them like:

const ui = await Page({ params: Promise.resolve({ locale: "en" }) });
render(ui);