Aller au contenu

Internationalization (i18n) Implementation

Overview

The Aletheia project now has full internationalization support with French as the primary language.

What Was Implemented

1. Configuration ✅

Settings Updated (config/settings/base.py)

  • Added LocaleMiddleware to middleware stack for language detection
  • Set LANGUAGE_CODE = 'fr' (French as default)
  • Added LANGUAGES list supporting French and English
  • Configured LOCALE_PATHS pointing to /locale directory
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',  # ← Added for i18n
    'django.middleware.common.CommonMiddleware',
    # ...
]

LANGUAGE_CODE = 'fr'
LANGUAGES = [
    ('fr', 'Français'),
    ('en', 'English'),
]
LOCALE_PATHS = [
    BASE_DIR / 'locale',
]

2. Templates Updated ✅

Updated key templates with translation tags:

Remaining templates: Other templates still need {% load i18n %} and {% trans %} tags added incrementally.

3. Python Code ✅

All models already use gettext_lazy: - apps/accounts/models.py - apps/practices/models.py - apps/dentists/models.py - apps/core/models.py

from django.utils.translation import gettext_lazy as _

class User(AbstractUser):
    email = models.EmailField(
        _('email address'),
        unique=True,
        help_text=_('Required. Used for login.')
    )

4. Translation Files ✅

Generated Files

Translated Strings (84 translations)

Authentication: - Login → Connexion - Password → Mot de passe - Email → Email - Dental Practice Analytics → Analytique pour Cabinets Dentaires

User Management: - Users → Utilisateurs - Add User → Ajouter un Utilisateur - User Management → Gestion des Utilisateurs - Active / Inactive → Actif / Inactif

Actions: - View → Voir - Edit → Modifier - Delete → Supprimer

Navigation: - Dashboard → Tableau de bord - Profile → Profil - Settings → Paramètres - Logout → Déconnexion

Pagination: - First → Première - Previous → Précédente - Next → Suivante - Last → Dernière

See scripts/translate_to_french.py for full translation dictionary.

Current Status

✅ Completed

  1. i18n infrastructure configured
  2. LocaleMiddleware installed
  3. Key templates updated (login, user list, navbar)
  4. Translation files generated and compiled
  5. 84+ strings translated to French
  6. All models use gettext_lazy

🔄 In Progress / Future Work

Remaining Templates to Update

These templates still need {% load i18n %} and {% trans %} tags:

Accounts App: - user_form.html - user_detail.html - user_confirm_delete.html

Practices App: - practice_list.html - practice_form.html - practice_detail.html - practice_confirm_delete.html

Dentists App: - dentist_list.html - dentist_form.html - dentist_detail.html - dentist_confirm_delete.html - contract_list.html - contract_form.html - contract_detail.html - contract_confirm_delete.html

Dashboard App: - index.html

Base Templates: - base.html - messages.html - Error pages (400, 403, 404, 500)

Workflow for Adding New Translations

1. Update Templates

When creating new templates or updating existing ones:

{% load i18n %}

<h1>{% trans "Page Title" %}</h1>
<p>{% trans "Some text to translate" %}</p>

<!-- For strings with variables -->
<span>{% blocktrans with name=user.name %}Hello {{ name }}{% endblocktrans %}</span>

2. Update Python Code

For new models, forms, or views:

from django.utils.translation import gettext_lazy as _

class MyModel(models.Model):
    name = models.CharField(_('name'), max_length=100)

3. Extract Translations

Run in Docker container:

docker-compose exec web python manage.py makemessages -l fr --no-obsolete

This updates locale/fr/LC_MESSAGES/django.po with new strings.

4. Translate Strings

Edit locale/fr/LC_MESSAGES/django.po manually or use the script:

# Add to scripts/translate_to_french.py
translations = {
    "New English String": "Nouvelle Chaîne Française",
}

Then run:

python3 scripts/translate_to_french.py

5. Compile Translations

docker-compose exec web python manage.py compilemessages

This creates/updates django.mo (binary file used by Django).

6. Restart Server (if needed)

Django auto-reloads when .mo files change in development, but in production:

docker-compose restart web

Tools & Scripts

scripts/translate_to_french.py

Automated script to add French translations to django.po file.

Usage:

python3 scripts/translate_to_french.py

scripts/add_i18n_to_templates.py

Helper script to add {% load i18n %} to templates (use with caution, review changes).

Testing i18n

Test Login Page

  1. Visit http://localhost:8000/accounts/login/
  2. Verify French translations:
  3. "Connexion" (Login button)
  4. "Email" and "Mot de passe" (form labels)
  5. "Analytique pour Cabinets Dentaires" (subtitle)

Test User List

  1. Login as admin
  2. Visit http://localhost:8000/accounts/users/
  3. Verify:
  4. "Utilisateurs" (Users heading)
  5. "Ajouter un Utilisateur" (Add User button)
  6. Table headers in French

Check Django Admin

Django's admin interface automatically uses French based on LANGUAGE_CODE='fr'.

Troubleshooting

Translations Not Showing

  1. Check .mo file exists:

    ls -la locale/fr/LC_MESSAGES/django.mo
    

  2. Recompile translations:

    docker-compose exec web python manage.py compilemessages
    

  3. Verify middleware order: LocaleMiddleware must be after SessionMiddleware but before CommonMiddleware.

  4. Check template loads i18n:

    {% load i18n %}
    

New Strings Not Translated

  1. Regenerate .po file:

    docker-compose exec web python manage.py makemessages -l fr --no-obsolete
    

  2. Check string is wrapped:

  3. Templates: {% trans "text" %}
  4. Python: _('text')

  5. Look for fuzzy entries in .po: Remove #, fuzzy comments before strings in django.po

Docker Issues

If makemessages fails with "Can't find msguniq":

docker-compose exec web sh -c "apt-get update && apt-get install -y gettext"

Production Considerations

Environment Variables

No additional environment variables needed for i18n.

Static Files

Translations are separate from static files. No special collectstatic needed.

Performance

  • .mo files are binary and very fast to read
  • Django caches translations in memory
  • Minimal performance impact

CDN / Cache

Translation files are server-side only. No cache invalidation issues with CDN.

Language Switcher ✅

Location: Inside profile dropdown menu (top-right navbar)

Features: - Radio buttons for French and English - Current language is pre-selected with green checkmark - Instant switch with auto-submit - Stays on current page after switching

How it works: 1. User clicks profile dropdown 2. Under Profile/Settings, sees "Langue" section 3. Selects 🇫🇷 Français or 🇬🇧 English 4. Page reloads with new language 5. Language preference stored in session cookie

Technical Implementation: - URL: /i18n/setlang/ (Django's built-in view) - Form submits to set_language view - Session cookie: django_language - Middleware: LocaleMiddleware handles detection

Future Enhancements

  1. ~~Add language switcher~~ ✅ COMPLETED
  2. Complete remaining templates - Add i18n tags to all templates
  3. Add English translations - Currently only French is translated
  4. Date/Number formatting - Leverage USE_L10N for locale-specific formatting
  5. JavaScript i18n - Translate JavaScript strings if needed
  6. Admin interface customization - Customize Django admin translations

References


Last Updated: December 2, 2025 Status: Phase 1 Complete - Core i18n infrastructure ready