Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not working with django model field verbose_name=lazyt("Title") #110

Open
Thutmose3 opened this issue Sep 18, 2023 · 4 comments
Open

Not working with django model field verbose_name=lazyt("Title") #110

Thutmose3 opened this issue Sep 18, 2023 · 4 comments

Comments

@Thutmose3
Copy link

When i'm displaying a form and have set verbose_name=lazyt("Title") like this:

title = models.CharField(max_length=250, null=True, blank=True, unique=False, verbose_name=lazyt("Title"))

And i'm rendering a form:

class ContactForm(forms.ModelForm):
    """Form for the Contact model."""

    class Meta:
        model = Contact
        fields = [
            "contact_type",
            "company_name",
            "name",
        ]

The translations are not working/taken into account, it stays in English

@Thutmose3
Copy link
Author

Thutmose3 commented Oct 5, 2023

As i'm paying quite a lot for my transifex subscription and the native tool is half broken and no response, i'm gonna fork this repo and fix it myself

@kbairak
Copy link
Member

kbairak commented Oct 6, 2023

Hey @Thutmose3 , we are terribly sorry for the delay. This slipped our radar.

Here is what I have tried so far:

# models.py

class Article(models.Model):
    title = models.CharField(max_length=100, verbose_name=lazyt("Title"))


# views.py

class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ["title"]


def index(request):
    localized_title = Article._meta.get_field("title").verbose_name
    return render(
        request,
        "index.html",
        {"form": ArticleForm(), "localized_title": localized_title},
    )
<!-- index.html -->

<div>{{ localized_title }}</div>
<div>{{ form }}</div>
<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
    ...
<form>

And here is what I get as a result:

image

image

So, it would seem that the lazy string we used for the verbose_name of the field generally works, hence the localized_title variable appears correctly translated. It also seems that when Django creates a form, it saves a "copy" of the verbose name as a string somewhere. So, when the form is rendered, it uses that copy instead of the lazy string.

Interestingly, if I change the LANGUAGE setting to Greek, then I get this behavior:

image

image

Which further reinforces my finding: During initialization, the verbose name is translated to the default language and saved as a non-lazy string. Then, that string is used regardless of the user's language choice.

My last attempt was this:

-class ArticleForm(forms.ModelForm):
-    class Meta:
-        model = Article
-        fields = ["title"]
 
 
 def index(request):
+    class ArticleForm(forms.ModelForm):
+        class Meta:
+            model = Article
+            fields = ["title"]
 
     localized_title = Article._meta.get_field("title").verbose_name
     return render(
         request,
         "index.html",
         {"form": ArticleForm(), "localized_title": localized_title},
     )

This time, it works as expected!

image

image

So, it turns out that Django copies the verbose name of the field when the form class is defined. This time, we defined the class inside the view and it took the user's language choice under consideration.

In which way are you using the verbose name in your application? Is it to display a form like we did here or something else?

In any case, I hope I've helped. Please return with more questions if there are any and let us know how it went. And, again, we are sorry for the delay.

@Thutmose3
Copy link
Author

Thutmose3 commented Oct 10, 2023

Hello @kbairak yes, i use verbose name to display the form like that.
When i'm using from django.utils.translation import gettext_lazy as _ it work as expected.

from django.utils.translation import gettext_lazy as _

class PreapprovedPermission(models.Model):
    user_permissions = models.ManyToManyField(
        Permission,
        verbose_name=_("user permissions"),
        blank=True,
    )

When i render the form in a normal way, this works. But when i use Transifex instead of gettext, it does not work. So this is a Transifex Native bug according to me?

@kbairak
Copy link
Member

kbairak commented Oct 11, 2023

I suspect that Django's lazy strings get "special treatment" from django itself when it gets to rendering. I suspect that something like this is going on:

from django... import LazyString

value = ...verbose_name
if not isinsntance(value, LazyString):
    value = str(value)  # This will apply the default language regardless of the user's choice
...

For Transifex Native, we had to implement our own version of lazy strings because Django's lazy strings are tightly coupled with gettext. So, our lazy strings do not get this special treatment.

Unfortunately, I cannot think of a way to get out of this. At least not yet.

As a workaround, I could suggest replacing this:

class ArticleForm(forms.ModelForm):
    ...

with this:

def ArticleForm(*args, **kwargs):
    class cls(forms.ModelForm):
        ...
    return cls(*args, **kwargs)

This way, you can still use the ArticleForm(request.POST, instance=...) syntax in your views and have the user's language choice respected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants