> ## 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.

# Internationalization (i18n)

> Multi-language support and translation system in Concordia

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

```javascript theme={null}
// 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

```javascript theme={null}
// From astro.config.mjs:26-28
redirects: {
  '/': '/fr/'
}
```

Visitors to the root URL (`/`) are automatically redirected to `/fr/` (French homepage).

## Supported Languages

<Tabs>
  <Tab title="Français (fr)">
    **French** - Default language

    * Primary language for the Annecy region
    * Most complete translations
    * Used as reference for other translations
    * File: `src/i18n/fr.json`
  </Tab>

  <Tab title="English (en)">
    **English** - International language

    * For international visitors and tourists
    * Complete UI translations
    * File: `src/i18n/en.json`
  </Tab>

  <Tab title="العربية (ar)">
    **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.
  </Tab>

  <Tab title="Español (es)">
    **Spanish** - Romance language

    * For Spanish-speaking visitors
    * File: `src/i18n/es.json`
  </Tab>
</Tabs>

## 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:

```json theme={null}
// 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

```typescript theme={null}
// From concordia-specs.md profile specification
interface Profile {
  preferred_language: "fr" | "en" | "ar" | "es";  // Default: "fr"
}
```

**User flow**:

<Steps>
  <Step title="New user signs up">
    Default `preferred_language` is set to `"fr"` (French)
  </Step>

  <Step title="User updates profile">
    User can change language preference in account settings
  </Step>

  <Step title="System respects preference">
    Platform displays content in user's preferred language when available
  </Step>

  <Step title="Fallback to default">
    If translation missing, falls back to French
  </Step>
</Steps>

## 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

```sql theme={null}
-- 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)

```sql theme={null}
-- 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

```css theme={null}
/* 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:

```css theme={null}
/* 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:

1. **URL path**: Primary method (e.g., `/fr/`, `/en/`)
2. **User profile**: Logged-in users' `preferred_language`
3. **Browser headers**: `Accept-Language` header for first visit
4. **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:

```typescript theme={null}
// 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

<Steps>
  <Step title="Keep keys consistent">
    Use the same key structure across all language files
  </Step>

  <Step title="Use nested objects">
    Group related translations (e.g., `nav.home`, `nav.about`)
  </Step>

  <Step title="Avoid hardcoded text">
    Always use translation keys, never hardcode text in components
  </Step>

  <Step title="Provide context">
    Add comments in JSON files to explain context for translators
  </Step>

  <Step title="Test RTL layout">
    Always test UI in Arabic to ensure proper RTL rendering
  </Step>

  <Step title="Maintain parity">
    When adding new features, update all language files simultaneously
  </Step>
</Steps>

## Adding a New Language

To add support for a new language:

<Steps>
  <Step title="Update Astro config">
    Add language code to `locales` array in `astro.config.mjs`

    ```javascript theme={null}
    i18n: {
      locales: ['fr', 'en', 'ar', 'es', 'de'],  // Added German
      defaultLocale: 'fr',
    }
    ```
  </Step>

  <Step title="Create translation file">
    Copy `src/i18n/fr.json` to `src/i18n/de.json`
  </Step>

  <Step title="Translate content">
    Translate all keys in the new file to the target language
  </Step>

  <Step title="Update database enum">
    Add language to profile schema if storing in database:

    ```sql theme={null}
    ALTER TYPE language ADD VALUE 'de';
    ```
  </Step>

  <Step title="Add RTL support (if needed)">
    For RTL languages (Hebrew, Farsi, Urdu), add CSS direction rules
  </Step>

  <Step title="Test thoroughly">
    * Check all pages in new language
    * Verify URL routing works
    * Test language switcher
    * Validate email templates
  </Step>
</Steps>

## Translation File Example

Comparing French and English for the same content:

<Tabs>
  <Tab title="French (fr.json)">
    ```json theme={null}
    {
      "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é"
      }
    }
    ```
  </Tab>

  <Tab title="English (en.json)">
    ```json theme={null}
    {
      "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"
      }
    }
    ```
  </Tab>
</Tabs>

## Locale-Specific Formatting

### Dates

```typescript theme={null}
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

```typescript theme={null}
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

* [Authentication System](/guides/authentication)
* [User Roles](/guides/user-roles)
* [Astro i18n Documentation](https://docs.astro.build/en/guides/internationalization/)
