Tms91
Tms91

Reputation: 4214

ugettext_lazy - app1 passing django messages to app2. Messages do not get translated in app2, strings get translated in app1

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

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...

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?

What I have tryed

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.

Upvotes: 0

Views: 46

Answers (1)

Tms91
Tms91

Reputation: 4214

Explanation

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 msgids.

So the string gets its placeholders substituted with variable values, then it is passed to gettext_lazy, which will look up in django.po msgids for the string containing the variables values, finding no match.

Examples

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 msgids.

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 msgids, and so translated into

"cliente {} non abilitato"

and only afterwards, it is formatted into

"cliente John Doe non abilitato"

Fixing the case study

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))

NOTE

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.

Upvotes: 0

Related Questions