Multi-lingual Development Guide
Goal: Provide accessible services to Kingston's diverse population. All public-facing interfaces now support 7 languages for Tier 5 EDIA (Equity, Diversity, Inclusion, Accessibility) goals.
1. Supported Languages
| Locale | Language | Direction | Purpose |
|---|---|---|---|
en | English | LTR | Official / Primary |
fr | Français canadien (CA) | LTR | Official / Secondary |
ar | العربية | RTL | EDIA / SWANA Community |
zh-Hans | 中文 | LTR | EDIA / East Asian Community |
es | Español | LTR | EDIA / Latinx Community |
pa | ਪੰਜਾਬੀ | LTR | EDIA / South Asian Community |
pt | Português | LTR | EDIA / Lusophone Community |
Note: The
frlocale uses Canadian French (fr-CA), not France French. This is a Kingston, Ontario project and follows Canadian French spelling, vocabulary, and conventions.
2. Architecture
- Separation of Content: Hardcoded strings in
tsxfiles are prohibited. - Library:
next-intlhandles dictionary management viamessages/{locale}.json. - Server message loading: When loading dictionaries in Server Components (e.g. the root layout), pass the route locale into
next-intl(getMessages({ locale })) so rewritten routes (e.g./offline→/{locale}/offline) render the correct language. - RTL Support: Arabic triggers
dir="rtl"in the layout. Use logical CSS properties (e.g.,ms-2instead ofml-2) or Radix/Tailwind utilities that handle direction automatically. - Data Layer:
- Local Services (scope: 'kingston'): English/French only (
name/name_fr,fees/fees_fr,hours_text/hours_text_fr, etc.). - Provincial Services (scope: 'ontario' or 'canada'): All 7 languages for name/description/eligibility fields.
- Schema: The
scopefield (enum:'kingston','ontario','canada') indicates geographic availability. The legacyis_provincialfield is deprecated.
3. Implementation Rules
- Labels: UI labels must be present in all 7 message files. This includes labels for legal policies, translation notices, and partner interfaces. Use
npm run i18n-auditto check for missing keys. - Text Expansion: Layouts must accommodate French and Spanish (often 20-30% longer than English) and Chinese (shorter but taller).
- Legal/Policy Pages: While content may lead in English/French, the structural keys and headers for all policy pages (Privacy, Terms, Accessibility) MUST exist in all 7 languages to prevent UI crashes.
- RTL Hygiene: Avoid absolute
left/rightpositioning. Useinset-inline-start/end. -
Content Fallbacks:
-
For local services, the UI defaults to
nameifname_fris missing. -
For provincial services, specific localization logic exists to handle expanded content.
-
Language Switching: Use the
LanguageSelectorcomponent in theHeader. Do not use manual links. - Date/Time: Use
Intl.DateTimeFormatornext-intlformatting utilities to ensure cultural correctness.
4. Maintenance
Audit Scripts
| Script | Purpose | Scope |
|---|---|---|
npm run i18n-audit | Checks all 7 message files for missing keys | UI translations |
npm run bilingual-check | Checks EN/FR parity for service data | data/services.json |
npm run validate-data | Validates service schema, warns if name_fr missing | data/services.json |
French Translation Workflow (Service Data)
For translating service data fields (especially access_script, description, and other content-rich fields), we use a semi-automated batch translation process with human review:
Process Overview:
- Export fields needing translation:
npm run export:access-script-fr - Generate translation prompts:
npm run translate:prompt <batch-file> - Obtain translated output using the approved translation workflow
- Parse the returned translations:
npm run translate:parse <batch-file> <response-file> - Validate output:
npm run translate:validate <output-file> - Review and commit the validated translations
Full Documentation: See docs/workflows/french-translation-workflow.md for the complete step-by-step guide, including quality guidelines and validation rules.
Audit Details
The i18n-audit script (scripts/i18n-key-audit.ts) performs these checks:
- Key Parity: Compares all locales against English (source of truth)
- Missing Keys: Reports keys that exist in EN but not in other locales
- Extra Keys: Warns about keys in locales that don't exist in EN
- Usage Check: Scans code for
t()calls to find potentially unused keys
Sample Output:
📊 AUDIT RESULTS
═══════════════════════════════════════════════════════════════
✅ EN - 377 keys
✅ FR - 377 keys
✅ AR - 383 keys
✅ ZH-HANS - 383 keys
✅ ES - 383 keys
Ongoing Work
- Accessibility: ARIA labels must be descriptive in all languages.
- Human Review: Periodic human review of static JSON files is required for
ar,zh-Hans, andes. - Maintenance: Ensure any new keys added to
en.jsonare immediately propagated to all other 6 languages.
Translation Review Policy
Transparency Disclosures
CareConnect flags translated content that is still under review so users can make safer decisions:
- EDIA Locale Notification (
ar,zh-Hans,es) - A dismissible floating pill appears at the bottom-right of every page
- Informs users that the page includes translated content under review
- Contains a "Got it" button for acknowledgment
-
Stored in localStorage to remember dismissal
-
Footer Disclaimer (all non-English locales)
- A subtle note appears in the footer for fr/ar/zh-Hans/es
- Text: "Some translations are under review. Report errors to feedback@careconnect.ing"
Translation Quality Tiers
| Tier | Locales | Quality Level | Review Status |
|---|---|---|---|
| Primary | en | Source of truth | N/A |
| Official | fr | Reviewed translated content | Reviewed |
| Preview | ar, zh-Hans, es | Best-effort translated content | Needs review |
Reporting Translation Errors
Users can report translation errors to feedback@careconnect.ing. Corrections should be prioritized based on:
- Health/safety information (highest priority)
- Legal/financial content
- General UI strings (lowest priority)
6. Security Considerations
Localized content in the database (e.g., name_fr, description_fr) is often rendered using dangerouslySetInnerHTML for term highlighting.
- XSS Protection: Always use
highlightMatches()fromlib/search/highlight.tswhich escapes HTML entities before applying<mark>tags. - Injection: Ensure localized search fields are included in ILIKE escaping logic in API routes.
- Validation: Zod schemas must validate that localized strings do not contain malicious script tags if they are to be rendered as HTML.