I am managing two django apps built by third parts.
One of these app manages a UI (app-ui
), while the other one is a backend app (app-api
).
Now, app-api
uses django messages.
The default language is english, and the author made transaltions in italian.
The translations are collected in the file app-api/apps/sub_app1/locale/it/LC_MESSAGES/django.po
.
The django function used to mark the translated messages is ugettext_lazy
from django.utils.translation import ugettext_lazy as _
Furthermore, app-api
has other apps. One of these is sub_app
. This one has models, defined in sub_app1/models.py
together with some data validations.
Among those ones there is this one:
if client.status != ACTIVE:
raise ValidationError(_(f'customer {customer.name} not active'))
When the user navigates app-ui
, it fires requests towards app-api
, and if the above condition is met, the ValidationError is raised, and the error message 'customer {customer.name} not active'
is passed to app-ui
, which shows it at the top of the window.
In order to make the messages translated, I have
re-collected messages by running django-admin makemessages
added and changed the italian transations for the marked strings of app-api
, in app-api/apps/sub_app1/locale/it/LC_MESSAGES/django.po
etc etc... #. Translators: This message appears on the home page only # apps/sub_app1/model.py:123 msgid "customer {customer.name} not active" msgstr "cliente {customer.name} non abilitato" etc etc...
re-compiled messages by running django-admin compilemessages
changed my browser setting to request for pages in italian
and I obtained every marked string appearing successfully translated in the web pages of app-api
.
However, the strings that are printed as django messages in app-ui
do not get translated, they are still in english, even if they are marked the same way as the other strings.
What is the problem, and how can I fix it?
In order to simulate the behaviour of app-ui
, I have tried to send requests to app-api
via Postman.
I have indicated the preferred language in which I would like to get the messages via the requests header key 'Accept-Language'
.
So, in the header I have added the key-value pair
'Accept-Language': 'it-IT' # also tryed with 'Accept-Language': 'it'
but the response still returns the message in english
{
"detail": "customer John Doe not active",
"status_code": 400
}
I am trying to understand how/(from where) does django get the indication of the language desired by the client.
Formatting strings by using Formatted String Literals spoils the django built-in gear for translation, because the strings are formatted before gettext_lazy
looks them up as msgid
s.
So the string gets its placeholders substituted with variable values, then it is passed to gettext_lazy
, which will look up in django.po msgid
s for the string containing the variables values, finding no match.
Example:
f'customer {customer.name} not active'
is formatted as
"customer John Doe not active"
and then looked up by gettext_lazy
in django.po, which expects to find it among the msgid
s.
In order to avoid this, we want the strings to be substituted with variables values after gettext_lazy
have looked them up, and substitued them with their translation.
We can achieve this by formatting the strings by using the string format method
Example:
"customer {} not active".format(customer.name)
is first looked up by gettext_lazy
as
"customer {} not active"
found in django.po among the msgid
s, and so translated into
"cliente {} non abilitato"
and only afterwards, it is formatted into
"cliente John Doe non abilitato"
So I have fixed the problem by
#1 adding in the header of every request of app-ui
towards app-ui
, the key-value pair
'Accept-Language': 'it-IT' # also tryed with 'Accept-Language': 'it'
#2 substituting
if client.status != ACTIVE:
raise ValidationError(_(f'customer {customer.name} not active'))
with
if client.status != ACTIVE:
raise ValidationError(_("customer {} not active").format(customer.name))
it is not
if client.status != ACTIVE:
raise ValidationError(_("customer {} not active".format({customer.name})))
(with .format
after the closing quote),
it is
if client.status != ACTIVE:
raise ValidationError(_("customer {} not active").format(customer.name))
(with .format
after the closing parenthesis of gettext_lazy
)
because we want to format the messages only after they are translated, and not before.