Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Ishaq74/concordia/llms.txt
Use this file to discover all available pages before exploring further.
Concordia is built as a multilingual platform from the ground up, supporting four languages: French, English, Arabic, and Spanish.
Overview
The internationalization system provides:
- Four supported languages: French (fr), English (en), Arabic (ar), Spanish (es)
- URL-based routing: Language prefix in all URLs (
/fr/, /en/, etc.)
- Default language: French (fr)
- Prefix all locales: Even the default locale uses a prefix
- Translation files: JSON-based translation system
- User preferences: Users can select their preferred language
Configuration
The i18n configuration is defined in astro.config.mjs.
Astro i18n Setup
// From astro.config.mjs:29-35
i18n: {
locales: ['fr', 'en', 'ar', 'es'],
defaultLocale: 'fr',
routing: {
prefixDefaultLocale: true
}
}
Configuration options:
| Option | Value | Description |
|---|
locales | ['fr', 'en', 'ar', 'es'] | Supported language codes (ISO 639-1) |
defaultLocale | 'fr' | Default language for the platform |
prefixDefaultLocale | true | Include /fr/ prefix even for French |
Root Redirect
// From astro.config.mjs:26-28
redirects: {
'/': '/fr/'
}
Visitors to the root URL (/) are automatically redirected to /fr/ (French homepage).
Supported Languages
Français (fr)
English (en)
العربية (ar)
Español (es)
French - Default language
- Primary language for the Annecy region
- Most complete translations
- Used as reference for other translations
- File:
src/i18n/fr.json
English - International language
- For international visitors and tourists
- Complete UI translations
- File:
src/i18n/en.json
Arabic - RTL support
- Right-to-left (RTL) language support
- For Arabic-speaking community members
- File:
src/i18n/ar.json
Note: Arabic requires special RTL layout handling in CSS. Spanish - Romance language
- For Spanish-speaking visitors
- File:
src/i18n/es.json
Translation Files
Translations are stored as JSON files in src/i18n/:
src/i18n/
├── fr.json # French (default)
├── en.json # English
├── ar.json # Arabic
└── es.json # Spanish
Translation File Structure
Each translation file follows the same structure with nested objects:
// From fr.json and en.json
{
"welcome": "Bienvenue sur Concordia",
"nav": {
"home": "Accueil",
"about": "À propos",
"services": "Services",
"blog": "Blog",
"contact": "Contact",
"docs": "Docs",
"places": "Annuaire",
"forum": "Forum",
"events": "Événements",
"education": "Éducation",
"volunteer": "Bénévolat",
"transparency": "Transparence",
"classifieds": "Annonces",
"trails": "Sentiers",
"dashboard": "Tableau de bord",
"admin": "Administration",
"discover": "Découvrir",
"community": "Communauté",
"engage": "S'engager",
"funding": "Financement",
"groups": "Groupes",
"mySpace": "Mon espace"
},
"home": {
"heroTitle": "Concordia Annecy",
"heroSubtitle": "La plateforme citoyenne du bassin annécien",
"heroCta": "Explorer",
"heroCtaSecondary": "Rejoindre la communauté"
},
"footer": {
"company": "Concordia Annecy",
"description": "Plateforme citoyenne et collaborative...",
"contact": "Contact",
"email": "contact@concordia-annecy.fr"
}
}
Translation Keys
Common translation namespaces:
| Namespace | Purpose | Example Keys |
|---|
nav | Navigation menu | home, places, forum, events |
home | Homepage content | heroTitle, heroSubtitle, heroCta |
footer | Footer content | company, contact, legal, privacy |
docsSidebar | Documentation navigation | gettingStarted, introduction |
services | Service descriptions | consulting, development, design |
URL Structure
All routes include language prefixes:
https://concordia-annecy.fr/fr/ # French homepage
https://concordia-annecy.fr/en/ # English homepage
https://concordia-annecy.fr/ar/ # Arabic homepage
https://concordia-annecy.fr/es/ # Spanish homepage
https://concordia-annecy.fr/fr/places # Places in French
https://concordia-annecy.fr/en/places # Places in English
https://concordia-annecy.fr/fr/blog/article-slug # Article in French
https://concordia-annecy.fr/en/blog/article-slug # Same article in English
Benefits of prefixed routing:
- Clear language indication in URL
- SEO-friendly (each language is a separate URL)
- Easy sharing of language-specific links
- No ambiguity about content language
User Language Preferences
Users can set their preferred language in their profile.
Profile Language Setting
// From concordia-specs.md profile specification
interface Profile {
preferred_language: "fr" | "en" | "ar" | "es"; // Default: "fr"
}
User flow:
New user signs up
Default preferred_language is set to "fr" (French)
User updates profile
User can change language preference in account settings
System respects preference
Platform displays content in user’s preferred language when available
Fallback to default
If translation missing, falls back to French
Content Translation Strategy
UI Translations
Interface elements (buttons, labels, messages) are translated via JSON files:
- Navigation menus:
nav.* keys
- Form labels:
forms.* keys
- Error messages:
errors.* keys
- Validation messages:
validation.* keys
User-Generated Content
Concordia uses separate translation tables for user-generated content:
Place Translations
-- From concordia-specs.md:484-501
CREATE TABLE place_translation (
id uuid PRIMARY KEY,
place_id uuid REFERENCES place(id),
language text NOT NULL, -- 'fr', 'en', 'ar', 'es'
name text NOT NULL,
description text,
created_at timestamp DEFAULT now(),
updated_at timestamp DEFAULT now(),
UNIQUE(place_id, language)
);
Features:
- Each place can have translations in multiple languages
- Unique constraint ensures one translation per language
- Place owners can provide translations for their listings
Article Content (Future)
-- From concordia-specs.md:762-776
CREATE TABLE article_content (
id uuid PRIMARY KEY,
article_id uuid REFERENCES article(id),
content_json jsonb NOT NULL,
language text NOT NULL, -- 'fr', 'en', 'ar', 'es'
updated_at timestamp DEFAULT now(),
UNIQUE(article_id, language)
);
Planned: Articles will support multiple language versions using the same structure.
RTL (Right-to-Left) Support
Arabic requires special handling for right-to-left layout.
CSS Direction
/* For Arabic locale */
html[lang="ar"] {
direction: rtl;
}
/* Adjust specific components */
html[lang="ar"] .nav-menu {
flex-direction: row-reverse;
}
html[lang="ar"] .card {
text-align: right;
}
Logical Properties
Use CSS logical properties for better RTL support:
/* Instead of margin-left */
margin-inline-start: 1rem;
/* Instead of margin-right */
margin-inline-end: 1rem;
/* Instead of padding-left and padding-right */
padding-inline: 1rem;
/* Instead of border-left */
border-inline-start: 1px solid;
Benefits:
- Automatically flips for RTL languages
- No need for duplicate RTL-specific styles
- Better maintainability
Language Detection
The system detects user language through:
- URL path: Primary method (e.g.,
/fr/, /en/)
- User profile: Logged-in users’
preferred_language
- Browser headers:
Accept-Language header for first visit
- Cookie: Remembers anonymous user’s language choice
Detection Priority
1. Explicit URL path (/en/places)
↓ (highest priority)
2. User profile setting (if logged in)
↓
3. Language cookie (if set)
↓
4. Browser Accept-Language header
↓
5. Default language (fr)
(fallback)
Email Translations
System emails are bilingual (French/English) in the same message:
// From auth.ts:69-72
subject: "Password reset - Réinitialisation de votre mot de passe",
text: `Cliquez sur le lien suivant pour réinitialiser votre mot de passe : ${url}`,
html: `<p>Cliquez sur le lien suivant pour réinitialiser votre mot de passe :</p><p><a href="${url}">${url}</a></p>`
Email types:
- Password reset (bilingual subject)
- Email verification (bilingual subject)
- Organization invitations (bilingual)
- Moderation notifications
Translation Best Practices
Keep keys consistent
Use the same key structure across all language files
Use nested objects
Group related translations (e.g., nav.home, nav.about)
Avoid hardcoded text
Always use translation keys, never hardcode text in components
Provide context
Add comments in JSON files to explain context for translators
Test RTL layout
Always test UI in Arabic to ensure proper RTL rendering
Maintain parity
When adding new features, update all language files simultaneously
Adding a New Language
To add support for a new language:
Update Astro config
Add language code to locales array in astro.config.mjsi18n: {
locales: ['fr', 'en', 'ar', 'es', 'de'], // Added German
defaultLocale: 'fr',
}
Create translation file
Copy src/i18n/fr.json to src/i18n/de.json
Translate content
Translate all keys in the new file to the target language
Update database enum
Add language to profile schema if storing in database:ALTER TYPE language ADD VALUE 'de';
Add RTL support (if needed)
For RTL languages (Hebrew, Farsi, Urdu), add CSS direction rules
Test thoroughly
- Check all pages in new language
- Verify URL routing works
- Test language switcher
- Validate email templates
Translation File Example
Comparing French and English for the same content:
French (fr.json)
English (en.json)
{
"welcome": "Bienvenue sur Concordia",
"nav": {
"home": "Accueil",
"places": "Annuaire",
"forum": "Forum",
"events": "Événements"
},
"home": {
"heroTitle": "Concordia Annecy",
"heroSubtitle": "La plateforme citoyenne du bassin annécien",
"heroCta": "Explorer"
},
"footer": {
"company": "Concordia Annecy",
"description": "Plateforme citoyenne et collaborative",
"legal": "Mentions légales",
"privacy": "Politique de confidentialité"
}
}
{
"welcome": "Welcome to Concordia",
"nav": {
"home": "Home",
"places": "Directory",
"forum": "Forum",
"events": "Events"
},
"home": {
"heroTitle": "Concordia Annecy",
"heroSubtitle": "The civic platform of the Annecy basin",
"heroCta": "Explore"
},
"footer": {
"company": "Concordia Annecy",
"description": "Civic and collaborative platform",
"legal": "Legal Notice",
"privacy": "Privacy Policy"
}
}
Dates
const date = new Date();
// French
date.toLocaleDateString('fr-FR') // "03/03/2026"
// English
date.toLocaleDateString('en-US') // "3/3/2026"
// Arabic
date.toLocaleDateString('ar-SA') // "٣/٣/٢٠٢٦"
Numbers and Currency
const price = 1234.56;
// French
price.toLocaleString('fr-FR', { style: 'currency', currency: 'EUR' })
// "1 234,56 €"
// English
price.toLocaleString('en-US', { style: 'currency', currency: 'EUR' })
// "€1,234.56"
See Also