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
LocaleMiddlewareto middleware stack for language detection - Set
LANGUAGE_CODE = 'fr'(French as default) - Added
LANGUAGESlist supporting French and English - Configured
LOCALE_PATHSpointing to/localedirectory
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:
- Login page (apps/accounts/templates/accounts/login.html)
- All user-facing text wrapped in
{% trans %} -
Added
{% load i18n %}at top -
User list (apps/accounts/templates/accounts/user_list.html)
- Table headers, buttons, pagination translated
-
Used
{% blocktrans %}for strings with variables -
Navigation (templates/components/navbar.html)
- Menu items, dropdown options translated
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¶
- Source file: locale/fr/LC_MESSAGES/django.po
- Compiled file:
locale/fr/LC_MESSAGES/django.mo(binary)
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¶
- i18n infrastructure configured
- LocaleMiddleware installed
- Key templates updated (login, user list, navbar)
- Translation files generated and compiled
- 84+ strings translated to French
- 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:
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:
5. Compile Translations¶
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:
Tools & Scripts¶
scripts/translate_to_french.py¶
Automated script to add French translations to django.po file.
Usage:
scripts/add_i18n_to_templates.py¶
Helper script to add {% load i18n %} to templates (use with caution, review changes).
Testing i18n¶
Test Login Page¶
- Visit http://localhost:8000/accounts/login/
- Verify French translations:
- "Connexion" (Login button)
- "Email" and "Mot de passe" (form labels)
- "Analytique pour Cabinets Dentaires" (subtitle)
Test User List¶
- Login as admin
- Visit http://localhost:8000/accounts/users/
- Verify:
- "Utilisateurs" (Users heading)
- "Ajouter un Utilisateur" (Add User button)
- Table headers in French
Check Django Admin¶
Django's admin interface automatically uses French based on LANGUAGE_CODE='fr'.
Troubleshooting¶
Translations Not Showing¶
-
Check .mo file exists:
-
Recompile translations:
-
Verify middleware order:
LocaleMiddlewaremust be afterSessionMiddlewarebut beforeCommonMiddleware. -
Check template loads i18n:
New Strings Not Translated¶
-
Regenerate .po file:
-
Check string is wrapped:
- Templates:
{% trans "text" %} -
Python:
_('text') -
Look for fuzzy entries in .po: Remove
#, fuzzycomments before strings in django.po
Docker Issues¶
If makemessages fails with "Can't find msguniq":
Production Considerations¶
Environment Variables¶
No additional environment variables needed for i18n.
Static Files¶
Translations are separate from static files. No special collectstatic needed.
Performance¶
.mofiles 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¶
- ~~Add language switcher~~ ✅ COMPLETED
- Complete remaining templates - Add i18n tags to all templates
- Add English translations - Currently only French is translated
- Date/Number formatting - Leverage
USE_L10Nfor locale-specific formatting - JavaScript i18n - Translate JavaScript strings if needed
- Admin interface customization - Customize Django admin translations
References¶
Last Updated: December 2, 2025 Status: Phase 1 Complete - Core i18n infrastructure ready