9

I'm working with Django 1.5.1 and I'm experiencing some "strange behaviour" with translations. I'm using ugettext and ugettext_lazy in the same Python file. If I organize the imports as:

from django.utils.translation import ugettext as trans
from django.utils.translation import ugettext_lazy as _

or

from django.utils.translation import ugettext as trans, ugettext_lazy as _

The strings marked as trans("string") are skipped when running makemessages command.

However, if I don't rename the ugettext it works well with both versions:

from django.utils.translation import ugettext
from django.utils.translation import ugettext_lazy as _

or

from django.utils.translation import ugettext, ugettext_lazy as _

Now trans("string") works well.

So, does anybody know why this import renaming is causing the renamed function not to be called? Is this an actual Python "limitation" I didn't know when renaming more than one function inside the same module?


UPDATE

After some testing, I've realized that even creating an empty python module inside an app with the following code it doesn't work:

from django.utils.translation import ugettext_lazy as translate

a = translate("string")

However, if using _ for the alias it works:

from django.utils.translation import ugettext_lazy as _

a = _("string")

My conclusion is: You can only use the _ alias for ugettext and ugettext_lazy (or any other related translation function) in Django or else it won't be recognized by makemessages command. The technical explanation can be found in Robert Lujo's answer.

Thanks!

Caumons
  • 9,341
  • 14
  • 68
  • 82
  • You can "rename" as many symbols (function or whatever) as you want, Python names are just aliases, and both import forms (one liner or two-liners) are equivalent, so the problem is elsewhere. FWIW I strongly suspect you rebind the name "trans" to something else somewhere after the import... – bruno desthuilliers May 12 '13 at 15:58
  • Hi! I'm not overriding `trans` in the module (I did a search with Eclipse). Could it be possible because of '_' renaming? – Caumons May 12 '13 at 16:00
  • `_` is a valid Python name and has no magical power. wrt/ renaming, `from x import y as z` is the exact equivalent of `from x import y; z = y; del y` so here again nothing magical happens. wrt/ your assertion that "you're not overriding `trans`", a simple text search might not be enough. A very common (anti)pattern is star import override, ie `from x import y; from z import *`, where it happens that `z` also exports a `y` symbol. Also and if you're not familiar with Python, Python's function _don't_ live in an isolated namespace, they are ordinary variables. – bruno desthuilliers May 12 '13 at 16:53
  • I don't know if there could be a name collision with any Django variable called trans or translate. I'm running out of ideas. I don't use wild imports in Python to avoid problems. – Caumons May 12 '13 at 17:26
  • @brunodesthuilliers I've just updated my question after further testing. – Caumons May 12 '13 at 20:06

5 Answers5

29

Django command utility makemessages internally calls xgettext program like this:

cmd = (
    'xgettext -d %s -L Python %s %s --keyword=gettext_noop '
    '--keyword=gettext_lazy --keyword=ngettext_lazy:1,2 '
    '--keyword=ugettext_noop --keyword=ugettext_lazy '
    '--keyword=ungettext_lazy:1,2 --keyword=pgettext:1c,2 '
    '--keyword=npgettext:1c,2,3 --keyword=pgettext_lazy:1c,2 '
    '--keyword=npgettext_lazy:1c,2,3 --from-code UTF-8 '
    '--add-comments=Translators -o - "%s"' %
    (domain, wrap, location, work_file))

(source can be found here). So, some keywords are predefined by the xgettext utility (check reference for --keyword):

  • for python - gettext, ugettext, dgettext, ngettext, ungettext, dngettext, _

and some are added by django utility:

  • gettext_lazy , ngettext_lazy , ugettext_noop , ugettext_lazy , ungettext_lazy , pgettext , npgettext , pgettext_lazy , npgettext_lazy

Keyword trans is not in any of these keyword sets, so you should not use it for marking texts for translations.

Robert Lujo
  • 15,383
  • 5
  • 56
  • 73
  • Thanks for your answer, I've just updated my question after further testing. – Caumons May 12 '13 at 20:07
  • 1
    This was exactly the problem! I was renaming the `ugettext` and `ugettext_lazy` functions using an alias that isn't included as a keyword and therefore it was skipped. It isn't a matter of imports. So, to conclude, just say that the ONLY alias allowed is `_`. Thanks for your answer! :) – Caumons May 14 '13 at 13:49
9

Unexpected ugettext aliases can be handled by overriding the makemessages command, such as in:

from django.core.management.commands import makemessages

class Command(makemessages.Command):
    """
    Extends the makemessages command to look for additional aliases.
    """
    xgettext_options = makemessages.Command.xgettext_options + ['--keyword=_lazy']

See https://docs.djangoproject.com/en/1.8/topics/i18n/translation/#customizing-the-makemessages-command

DanH
  • 5,498
  • 4
  • 49
  • 72
0

These notes about multi-language support for Django project may let you identify what is wrong. Most probably renaming is not the root cause for failure.

Some warnings in these notes:

  • Each template for your web site must load the i18n tag library using the {% load %}

  • You need to create the locale directory manually before running makemessages - that you normally did, if not you get an error message

  • Language files must be compiled to .mo files before you use them - that you did also

EDIT

In the page i linked to my post, they are using this syntax in templates:

{% trans "Hello" %}

is it something you tried already ?

kiriloff
  • 25,609
  • 37
  • 148
  • 229
  • The problem isn't related with this, as using ugettext_lazy as _ works perfectly and using ugettext without alias too. – Caumons May 12 '13 at 17:23
  • I've just accepted @RobertLujo's answer as he perfectly answers my question. Thanks anyway :) – Caumons May 14 '13 at 13:51
0

Another way for resolving no .po files generation are the development server.

During my development server is running I executed the command makemessage. Command output was right, but my .po files were updated :

(venv_crypto_bot) macbook-pro:django_project dauzon$ python manage.py makemessages -a 
processing locale fr
processing locale en
processing locale ru

Stop the development server and re-run the command resolves my issue.

Samuel Dauzon
  • 10,744
  • 13
  • 61
  • 94
0

You can create your own compilemessages command, to call the original compilemessages command with extra arguments.

For example, to support from django.utils.translation import pgettext_lazy as _p:

# Created by BaiJiFeiLong@gmail.com at 2022/4/20
from django.core.management import call_command
from django.core.management.base import BaseCommand
from django.core.management.commands import makemessages


class Command(BaseCommand):
    help = "Make messages in en_US and zh_CN, support _p, ignore venv folder."

    def handle(self, *args, **options):
        text = "-l en_US -l zh_CN --ignore ?env -v3"
        command = makemessages.Command()
        getattr(command, "xgettext_options").append("--keyword=_p:1c,2")
        call_command(command, *text.split())

Notes:

_p:1c,2 represents _p('msgctxt', 'msgid'), function with two arguments like pgettext_lazy

BaiJiFeiLong
  • 3,716
  • 1
  • 30
  • 28