Internationalization (i18n) Guide ๐¶
Django-Appointment includes built-in internationalization support with localized date formats, translations, and multi-language capabilities.
Built-in Language Support ๐ฃ๏ธ¶
Django-Appointment currently ships UI translations for:
| Language | Code | Status |
|---|---|---|
| English | en |
Default โ the source strings |
| French | fr |
Maintained by the project |
| Spanish | es |
Contributed by @alexandermamaniy, not actively maintained |
Spanish needs a maintainer. The Spanish catalogue was contributed by @alexandermamaniy and is not kept up to date as the package changes, so newer strings may still show in English. It is shipped because a mostly-translated UI beats none at all. If you speak Spanish and would like to take it over, that would be very welcome โ see Contributing Translations below, and please do open a PR.
Quick Setup¶
1. Enable Internationalization in Django¶
Add these settings to your settings.py:
# Internationalization
LANGUAGE_CODE = 'en' # Default language
USE_I18N = True # Enable translations
USE_L10N = True # Enable localized formatting
USE_TZ = True # Enable timezone support
# Supported languages
LANGUAGES = [
('en', 'English'),
('fr', 'French'),
('es', 'Spanish'),
# Add more languages as needed
]
# Translation files location
LOCALE_PATHS = [
BASE_DIR / 'locale',
]
2. Add Middleware¶
Add the locale middleware to your MIDDLEWARE setting:
MIDDLEWARE = [
# ... other middleware
'django.middleware.locale.LocaleMiddleware',
# ... other middleware
]
3. Language Switching¶
Include language switching in your URLs:
# urls.py
from django.conf.urls.i18n import i18n_patterns
from django.urls import path, include
urlpatterns = [
path('i18n/', include('django.conf.urls.i18n')),
]
urlpatterns += i18n_patterns(
path('appointment/', include('appointment.urls')),
# ... other URL patterns
)
Localized Date Formats ๐ ¶
Django-Appointment automatically formats dates according to the user's language:
- English: "Thu, August 14, 2025"
- French: "jeu 14 aoรปt 2025"
- German: "Do, 14. August 2025"
- Spanish: "jue, 14 de agosto de 2025"
The package includes date format patterns for 39 languages. No additional configuration needed!
Contributing Translations ๐ค¶
Want to add support for your language? We'd love your help!
Adding a New Language¶
- Fork the repository and create a new branch
- Generate translation files:
- Translate the strings in
appointment/locale/[language_code]/LC_MESSAGES/django.po - Add date format to
appointment/utils/date_time.pyin theDATE_FORMATSdictionary - Test your translations:
- Submit a pull request
Translation Guidelines¶
- Use formal tone for UI elements
- Keep technical terms consistent
- Test date formats with real examples
- Include gender-neutral language where possible
gettext or gettext_lazy? ๐¶
Both mark a string for translation. They differ in when the translation happens, and picking the wrong one is a quiet bug rather than a loud one.
| Translates | Use it for | |
|---|---|---|
gettext |
Immediately, when the line runs | Code that runs while answering a request |
gettext_lazy |
Later, when the string is displayed | Code that runs once, at import time |
The question to ask is: does this line run at startup, or while answering somebody's
request? Startup means there is no visitor yet, so there is no language to translate
into; gettext there would freeze whatever language happened to be active when Django
started. That is what gettext_lazy is for.
# At import time -> lazy. There is no request yet.
class Appointment(models.Model):
status = models.CharField(verbose_name=_("Status")) # gettext_lazy
# While handling a request -> plain. The language is already known.
def save_appointment(request):
messages.success(request, _("Appointment saved")) # gettext
In this project that works out as:
gettext_lazyโmodels.py,forms.py,utils/validators.py,messages_.py(field labels, form labels, validator messages, module-level constants)gettextโviews.py,views_admin.py,services.py,utils/session.py,utils/email_ops.py,utils/date_time.py,tasks.py(everything built per request)
Never import both under the same name. The second one silently wins, so the file claims one behaviour and has the other:
# Wrong: every _() below is lazy, whatever the first import suggests
from django.utils.translation import gettext as _, gettext_lazy as _
One more thing worth knowing: gettext_lazy does not return a string, it returns a
placeholder that becomes one when displayed. Django handles that nearly everywhere, but
it can surprise code that expects real text โ JSON serialisation, concatenation, or
anything writing straight to the database. When in doubt in request-time code, prefer
plain gettext.
Advanced: Translating Database Content ๐๏ธ¶
For translating service names, descriptions, and other database content, you can use third-party packages:
Option 1: django-modeltranslation¶
-
Install the package:
-
Add to INSTALLED_APPS:
-
Create translation configuration:
-
Generate and run migrations:
Option 2: django-parler¶
-
Install the package:
-
Follow django-parler documentation for setup and configuration
Language-Specific Features ๐ฏ¶
Right-to-Left (RTL) Languages¶
Django-Appointment includes basic RTL support for languages like Arabic and Hebrew. The date formats are properly configured for RTL display.
Pluralization¶
The package correctly handles plural forms for time durations: - English: "1 hour" vs "2 hours" - French: "1 heure" vs "2 heures" - And more complex rules for languages like Russian or Arabic
Troubleshooting ๐ง¶
Common Issues¶
- Dates showing in wrong format:
- Ensure
USE_L10N = Truein settings - Check that locale middleware is enabled
-
Verify language code is supported
-
Translations not appearing:
- Run
python manage.py compilemessages - Check
LOCALE_PATHSsetting -
Verify middleware order
-
Mixed language content:
- Database content requires separate translation (see above)
- UI elements use Django's translation system
Getting Help¶
- Check the main documentation
- Open an issue on GitHub
- Join the discussion in our community
Supported Date Format Languages ๐¶
Currently includes date format patterns for:
Arabic, Bengali, Bulgarian, Chinese, Croatian, Czech, Danish, Dutch, English, Estonian, Finnish, French, German, Greek, Hebrew, Hindi, Hungarian, Indonesian, Italian, Japanese, Korean, Latvian, Lithuanian, Malay, Norwegian, Persian, Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovenian, Spanish, Swedish, Thai, Turkish, Ukrainian, Vietnamese
That is 39 languages, defined in the DATE_FORMATS dictionary in
appointment/utils/date_time.py.
A date format is independent of the UI translation: adding an entry there localises how dates are displayed even
when no .po catalogue exists for that language.
Missing your language? Please contribute!