Skip to content

Adding a Language

Step-by-step guide for adding a new language to the system. This example adds French (fr).

Step 1: Add to Django Settings

In backend/config/settings/base.py, add the language to LANGUAGES:

LANGUAGES = [
    ("en", "English"),
    ("es", "Spanish"),
    ("fr", "French"),       # New language
]

Step 2: Add SupportedLocale Choice

In backend/apps/tenants/choices.py, add the locale choice:

class SupportedLocale(models.TextChoices):
    EN = "en", _("English")
    ES = "es", _("Spanish")
    FR = "fr", _("French")  # New choice

This allows tenants to select the new language as their default locale.

Step 3: Create PDF Template Variants

Create language-specific PDF templates in backend/apps/documents/templates/:

  • monthly_statement_fr.html
  • tila_disclosure_fr.html
  • adverse_action_notice_fr.html

Copy the English templates as a starting point and translate the static text. Dynamic values use Jinja2 filters that handle locale automatically:

{{ amount | currency }}    {# Formatted per locale #}
{{ due_date | date }}      {# Formatted per locale #}

Step 4: Create Communication Template Records

Insert database rows for each template type/channel combination with language="fr":

INSERT INTO communications_communicationtemplate
    (template_type, channel, program_id, language, subject, body, is_active)
VALUES
    ('payment_reminder', 'email', NULL, 'fr', 'Rappel de paiement', '...', true),
    ('payment_reminder', 'sms', NULL, 'fr', NULL, '...', true),
    -- ... for each template type and channel
;

Or create via the admin dashboard's Communication Template management screens.

Step 5: Extract and Compile Messages

cd backend

# Extract translatable strings (adds new .po file for French)
uv run python manage.py makemessages -l en -l es -l fr --no-wrap

# Translate the new strings in backend/locale/fr/LC_MESSAGES/django.po

# Compile all message catalogs
uv run python manage.py compilemessages

The makemessages command creates backend/locale/fr/LC_MESSAGES/django.po with all translatable strings. Translate the msgstr entries, then compile.

Step 6: Update Frontend Admin i18n Provider

In frontend/admin/src/providers/i18nProvider.ts:

import frenchMessages from "ra-language-french";

const messages: Record<string, TranslationMessages> = {
  en: englishMessages,
  es: spanishMessages,
  fr: frenchMessages,
};

const i18nProvider = polyglotI18nProvider(
  (locale: string) => messages[locale] ?? messages.en,
  "en",
  [
    { locale: "en", name: "English" },
    { locale: "es", name: "Español" },
    { locale: "fr", name: "Français" },
  ],
);

Install the language package first:

cd frontend/admin
npm install ra-language-french

Step 7: Verify

  1. Backend messages: Check that django.po and django.mo exist in backend/locale/fr/LC_MESSAGES/
  2. Tenant config: Set a test tenant's locale to fr and verify middleware activates French
  3. PDF generation: Generate a statement for a borrower with language_preference="fr" and confirm the French template is used
  4. Communication templates: Send a test communication and verify the French template resolves
  5. Admin dashboard: Switch language in the AppBar and confirm UI labels update
  6. Fallback: Test with missing French templates to confirm English fallback works

Checklist

  • [ ] LANGUAGES updated in settings
  • [ ] SupportedLocale choice added
  • [ ] PDF template variants created (_fr.html)
  • [ ] Communication template DB rows created
  • [ ] Message catalog extracted, translated, and compiled
  • [ ] Frontend i18n provider updated
  • [ ] Both .po and .mo files committed

See Also