Django gettext translation doesnt work on dinamic translations
Question:
I am working on a Django application that supports multiple languages. I am using token-based authentication and trying to change the language for authenticated users based on their preference stored in their profile. However, I am encountering a few issues regarding how the translations are being applied, particularly when it comes to dynamic message translations.
What I have: I have set up multiple languages in settings.py (LANGUAGES) and configured the default language to be English (LANGUAGE_CODE = 'en'). I am using token-based authentication (rest_framework.authtoken) to authenticate users. The user’s preferred language is stored in the database as preferred_language. I have added a custom middleware to handle language switching based on the user’s preference, which should be applied as soon as the user is authenticated and the session is set. Problem: Language preference seems to be applied for static content (like templates), but dynamic translations for strings in views (using gettext) are not applying correctly after the user’s preferred language is set. For example, while the language change works partially (like UI content), messages generated using gettext() (like error or success messages) are still showing in the default language (en), not the user's preferred language (e.g., es for Spanish). Here are some observations:
When I check the active language in the view using get_language(), it correctly reflects the preferred language for the session. I have verified that the preferred language is correctly set in both the session and cookie (cookie_language_tinocoloco). The LocaleMiddleware is configured to activate the language, but it's not applying to dynamic messages. What I have tried: I have ensured that the middleware order is correct. The SessionMiddleware is before LocaleMiddleware, and LocaleMiddleware is before AuthenticationMiddleware, which is consistent with the recommended setup. I have manually activated the language in the middleware using translation.activate(user.preferred_language). My setup: MIDDLEWARE configuration (relevant snippet):
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware", # Ensuring session is set before locale
"django.middleware.locale.LocaleMiddleware", # Language activation middleware
"django.contrib.auth.middleware.AuthenticationMiddleware", # Authentication for token
# Other middleware
]
Custom Language Middleware:
from django.utils import translation
def get_user_language(user):
if user and hasattr(user, 'preferred_language') and user.preferred_language:
return user.preferred_language
return None
class LanguageMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if hasattr(request, 'user') and request.user.is_authenticated:
language = get_user_language(request.user)
if language:
translation.activate(language) # Activate user preferred language
response = self.get_response(request)
return response
Session Cookie: I am also ensuring that the language preference is stored in the session and cookie.
Expected Behavior: I expect the dynamic translations (using gettext() and gettext_lazy()) to change based on the user’s preferred language. However, despite setting the correct language preference, only some parts of the site (like templates) are reflecting the new language, while others (like dynamically generated messages) still show in the default language (en).
Question: Is there something missing in my setup that would allow the dynamic translation strings to respect the preferred language of the authenticated user? How can I ensure that the translations are applied globally, including dynamic messages, after the user’s language is set in the session and cookie? Any guidance or advice on this would be greatly appreciated. Thanks!
Depending on where you have defined your error messages, it is likely that the gettext() calls are evaluated before you set the active language in the middleware.
To illustrate, here is a snippet from a Django project with translations set up:
In [2]: from django.utils.translation import gettext, gettext_lazy, activate
In [3]: foo = gettext("Cows")
In [4]: bar = gettext_lazy("Cows")
In [5]: activate('ta')
In [6]: foo2 = gettext("Cows")
In [7]: bar2 = gettext_lazy("Cows")
In [8]: print("foo =", foo, "bar =", bar, "foo2 =", foo2, "bar2 =", bar2)
foo = Cows bar = பசுக்கள் foo2 = பசுக்கள் bar2 = பசுக்கள்
Here you can see how gettext() fetches the text immediately, while gettext_lazy() waits until it is evaluated (i.e. when it is printed).
So if you use gettext_lazy() instead of gettext(), then your strings will be evaluated only at the time of rendering your response, and that should make it use the right translated string. Hope this helps.