172

I have a question about using ugettext and gettext_lazy() for translations. I learned that in models I should use gettext_lazy(), while in views ugettext. But are there any other places, where I should use gettext_lazy() too? What about form definitions? Are there any performance diffrences between them?

Edit: And one more thing. Sometimes, instead of gettext_lazy(), gettext_noop() is used. As documentation says, gettext_noop() strings are only marked for translation and translated at the latest possible momment before displaying them to the user, but I'm little confused here, isn't that similar to what gettext_lazy() do? It's still hard for me to decide, which should I use in my models and forms.

Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
Dzejkob
  • 2,302
  • 2
  • 15
  • 20

4 Answers4

245

gettext() vs. gettext_lazy()

In definitions like forms or models you should use gettext_lazy because the code of this definitions is only executed once (mostly on django's startup); gettext_lazy translates the strings in a lazy fashion, which means, eg. every time you access the name of an attribute on a model the string will be newly translated-which totally makes sense because you might be looking at this model in different languages since django was started!

In views and similar function calls you can use gettext without problems, because everytime the view is called gettext will be newly executed, so you will always get the right translation fitting the request!

Regarding gettext_noop()

As Bryce pointed out in his answer, this function marks a string as extractable for translation but does return the untranslated string. This is useful for using the string in two places – translated and untranslated. See the following example:

import logging
from django.http import HttpResponse
from django.utils.translation import gettext as _, gettext_noop as _noop

def view(request):
    msg = _noop("An error has occurred")
    logging.error(msg)
    return HttpResponse(_(msg))
Bernhard Vallant
  • 49,468
  • 20
  • 120
  • 148
  • 21
    That's more understandable than the explanation at Django's documentation in my opinion. Thanks @Bernhard. – Utku Aug 02 '12 at 23:01
  • 15
    Thanks! It would also be helpful to explain when *not* to use ugettext_lazy, such as when passing it to things that expect a string like "".replace, string concatenation, and others; a lazy proxy object won't work in those cases. Otherwise this answer implies that you are safe just always using ugettext_lazy. – mrooney Feb 26 '13 at 21:08
  • 4
    @mrooney those cases matter less because they will give you an error if you do them, instead of silently returning the wrong language translation. Also, you can use "".replace with ugettext_lazy, you just have to call str() on the result e.g. lazytext=ugettext_lazy('hello') and then later on use str(lazytext).replace. – fabspro Mar 01 '14 at 10:49
  • @Utku - you said it. I had absolutely no understanding after reading the docs and after reading other sources as well. This explanation is great. – skaz Jul 04 '15 at 07:12
  • 1
    what about `msg = "An error has occurred"; logging.error(msg);return HttpResponse(_(msg))? why need `_noop`?` if without `_noop`, django won't found the string need translation? – WeizhongTu Sep 07 '16 at 12:16
  • 1
    Translation works on variables. Again, here’s an identical example [docs](https://docs.djangoproject.com/en/1.10/topics/i18n/translation/#standard-translation), so why `_noop`? – WeizhongTu Sep 07 '16 at 13:31
  • But why should I _marks a string as extractable for translation_? It seems that `msg = "An error has occurred"` is functionally identical to your example… – nalzok Mar 29 '17 at 02:48
  • 2
    FYI I've found that if you import one of the marking functions (e.g. `ugettext`) as `_`, and then import a second (e.g. `ugettext_noop`) using `import as` (e.g. `import ugettext_noop as _noop`), then `xgettext` won't recognize and extract the latter. In other words, all imports other than the one you `import as _` must be imported and referenced by their full name. – Eric P Dec 04 '18 at 18:41
  • Correct me if I'm wrong: Use `**_lazy` where you might use different languages for the concerned code *even if the server is not restarted*. I've lost 2 hours because my code wasn't being translated. I had to "force" the translation by adding `str()` like `str(_("1 student failed")` – Olivier Pons Oct 06 '19 at 06:56
  • `ugettext_lazy` / `ugettext` are now deprecated. instead use `gettext_lazy` / `gettext`. The accepted answer is still valid. – Tobias Ernst Mar 17 '22 at 16:31
23

An excellent use of _noop, is when you want to log a message in English for the developers, but present the translated string to a viewer. An example of this is at http://blog.bessas.me/posts/using-gettext-in-django/

Mekicha
  • 811
  • 9
  • 21
Bryce
  • 8,313
  • 6
  • 55
  • 73
9

The lazy version returns a proxy object instead of a string and in some situation it would not work as expected. For example:

def get(self, request, format=None):
   search_str = request.GET.get('search', '')
   data = self.search(search_str)
   lst = []
   lst.append({'name': ugettext_lazy('Client'), 'result': data})
   return HttpResponse(json.dumps(lst), content_type='application/json')

would fail because very last line would try serialize lst object into JSON and instead of a string for "client" it would have a proxy object. The proxy object is not serializeable into json.

Alex Protyagov
  • 511
  • 6
  • 6
0

gettext() can work inside functions but doesn't work outside functions.

gettext_lazy() can work inside and outside functions.

*You better use gettext_lazy() outside functions according to the examples of Translation.

<gettext()>

This below is where gettext() can work:

# "my_app1/views.py"

from django.http import HttpResponse
from django.utils.translation import gettext as _

def hello(request):
    HttpResponse(_("Hello")) # Here

<gettext_lazy()>

This below is where gettext_lazy() can work:

# "core/settings.py"

from django.utils.translation import gettext_lazy as _

LANGUAGES = (
    ('en', _('English')),
    ('fr', _('French'))
)
# "my_app1/views.py"

from django.http import HttpResponse
from django.utils.translation import gettext_lazy as _

def hello(request): # Here
    HttpResponse(_("Hello"))
# "my_app1/urls.py"

from django.urls import path
from . import views
from django.utils.translation import gettext_lazy as _

app_name = "my_app1"

urlpatterns = [
    path(_('hello'), views.hello, name="hello"),
]        # Here
# "my_app1/models.py"

from django.db import models
from django.utils.translation import gettext_lazy as _

class Person(models.Model):                             # Here
    name = models.CharField(max_length=20, verbose_name=_("name"))

    class Meta:
        verbose_name = _('person') # Here
        verbose_name_plural = _('persons') # Here
# "my_app1/admin.py"

from django.contrib import admin
from django import forms
from .models import Person
from django.utils.translation import gettext_lazy as _

admin.site.site_title = _('My site title') # Here
admin.site.site_header = _('My site header') # Here
admin.site.index_title = _('My index title') # Here

class PersonForm(forms.ModelForm): # Here
    name = forms.CharField(label=_('name')) 

    class Meta:
        model = Person
        fields = "__all__"

@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):    
    form = PersonForm
# "my_app1/apps.py"

from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _

class App1Config(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'my_app1'
    verbose_name = _('my app1') # Here
Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129