i18n bug in template rendering.

70 views
Skip to first unread message

Jonathan S

unread,
Jan 5, 2011, 10:21:07 AM1/5/11
to Django developers
I guess, we found a bug, not sure if it has been reported earlier and
whether this is the right place to report.

When a template contains translations in variables, like
{{ _("text") }}, the text seems to be translated already during the
creation of the Template object. Every following call of
Template.render() will output the translation which was made earlier.
(Possibly in the wrong language.)

Templates are cached in 'django.template.loaders.cached.Loader', so
this bug makes constructions like {{ _("text")|upper }} impossible.


=============Test code: =============

from django.utils import translation
from django.template import Template, Context


def language(language_code):
class with_block(object):
def __enter__(self):
self._old_language = translation.get_language()
translation.activate(language_code)
def __exit__(self, *args):
translation.activate(self._old_language)
return
with_block()


with language('en'): # Output 'Yes'
Template("{{ _('Yes') }}").render(Context({}))


with language('fr'): # Output 'Oui'
Template("{{ _('Yes') }}").render(Context({}))


with language('nl'): # Output 'Ja'
Template("{{ _('Yes') }}").render(Context({}))


with language('nl'): # create template, while environment language is
dutch
t = Template("{{ _('Yes') }}")

with language('fr'): # Render in French -> output is still Dutch
('Ja')
t.render(Context({}))


============= Output =============

Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.utils import translation
>>> from django.template import Template, Context
>>>
>>>
>>> def language(language_code):
... class with_block(object):
... def __enter__(self):
... self._old_language = translation.get_language()
... translation.activate(language_code)
... def __exit__(self, *args):
... translation.activate(self._old_language)
... return
with_block()
...
>>>
>>> with language('en'): # Output 'Yes'
... Template("{{ _('Yes') }}").render(Context({}))
...
u'Yes'
>>>
>>> with language('fr'): # Output 'Oui'
... Template("{{ _('Yes') }}").render(Context({}))
...
u'Oui'
>>>
>>> with language('nl'): # Output 'Ja'
... Template("{{ _('Yes') }}").render(Context({}))
...
u'Ja'
>>>
>>> with language('nl'): # create template, while environment language is dutch
... t = Template("{{ _('Yes') }}")
...
>>> with language('fr'): # Render in French -> output is still Dutch ('Ja')
... t.render(Context({}))
...
u'Ja'

Jonathan S

unread,
Jan 5, 2011, 10:26:48 AM1/5/11
to Django developers

Ramiro Morales

unread,
Jan 5, 2011, 6:05:54 PM1/5/11
to django-d...@googlegroups.com
On Wed, Jan 5, 2011 at 12:21 PM, Jonathan S <jonathan...@gmail.com> wrote:
> I guess, we found a bug, not sure if it has been reported earlier and
> whether this is the right place to report.
>
> When a template contains translations in variables, like
> {{ _("text") }}, the text seems to be translated already during the
> creation of the Template object. Every following call of
> Template.render() will output the translation which was made earlier.
> (Possibly in the wrong language.)
>
> Templates are cached in 'django.template.loaders.cached.Loader', so
> this bug makes constructions like  {{ _("text")|upper }} impossible.

But you aren't using the i18n support correctly.

You aren't supposed to use _('Foo') as a standalone variable.
(see last paragraph here
http://docs.djangoproject.com/en/1.2/topics/i18n/internationalization/#other-tags)

Also,

* You need to load the i18n template tag library

* Use the trans tag.

Try using Template("{% load i18n %}{% trans 'Yes' %}").render(...)
to achieve what you want.

All this is covered in the docs:

http://docs.djangoproject.com/en/1.2/topics/i18n/internationalization/#specifying-translation-strings-in-template-code

--
Ramiro Morales

Gert Van Gool

unread,
Jan 6, 2011, 2:37:17 AM1/6/11
to django-d...@googlegroups.com
So for the sake of the argument I've changed the template code to use
"{% load i18n %}" and also added a filter, since that is our use-case.
http://dpaste.com/294778/

I thought that if you wanted to apply a filter to a string in a
template that you had to use _ instead of {% trans %}.

-- Gert

Mobile: +32 498725202
Twitter: @gvangool
Web: http://gert.selentic.net

> --
> You received this message because you are subscribed to the Google Groups "Django developers" group.
> To post to this group, send email to django-d...@googlegroups.com.
> To unsubscribe from this group, send email to django-develop...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
>
>

Jonathan S

unread,
Jan 6, 2011, 3:07:34 AM1/6/11
to Django developers

> You aren't supposed to use _('Foo') as a standalone variable.
> (see last paragraph herehttp://docs.djangoproject.com/en/1.2/topics/i18n/internationalization...)


Why shouldn't I use it as a standalone variable? (A language should
have a *context free* grammar, which means, that the underscore
function can be used in any variable. But that's no problem in
Django.)


That's not the point, the bug applies to any variable, template tag or
template filter parameter. It seems like a bug to me:
Any underscore function is translated during the initialisation of the
template, and that is wrong to me.

I guess, also for this one, like mentioned in the documentation.
{% some_special_tag _("Page not found") value|yesno:_("yes,no") %}

And I did a quick test:


with language('nl'):
t = Template("{% load i18n %}{{ 1|yesno:_('yes,no') }}")

t.render(Context()) # Will output 'ja'
with language('en'):
t.render(Context()) # Will still output 'ja', even if the language
is English now...



The underscore, which is a parameter for a template filter, is also
translated during the initialisation of the template...

Ramiro Morales

unread,
Jan 6, 2011, 6:15:59 AM1/6/11
to django-d...@googlegroups.com
On Thu, Jan 6, 2011 at 5:07 AM, Jonathan S <jonathan...@gmail.com> wrote:
>
>> You aren't supposed to use _('Foo') as a standalone variable.
>> (see last paragraph herehttp://docs.djangoproject.com/en/1.2/topics/i18n/internationalization...)
>
>
> Why shouldn't I use it as a standalone variable? (A language should
> have a *context free* grammar, which means, that the underscore
> function can be used in any variable. But that's no problem in
> Django.)
>
>
> That's not the point, the bug applies to any variable, template tag or
> template filter parameter. It seems like a bug to me:
> Any underscore function is translated during the initialisation of the
> template, and that is wrong to me.
>
> I guess, also for this one, like mentioned in the documentation.
> {% some_special_tag _("Page not found") value|yesno:_("yes,no") %}
>
> And I did a quick test:
>
>
> with language('nl'):
>  t = Template("{% load i18n %}{{ 1|yesno:_('yes,no') }}")
>
> t.render(Context()) # Will output 'ja'
> with language('en'):
>  t.render(Context()) # Will still output 'ja', even if the language
> is English now...

Weird, it's working here:

In [1]: from django.utils import translation

In [2]: from django.template import Template, Context

In [3]:

In [4]:

In [5]: def language(language_code):


...: class with_block(object):
...: def __enter__(self):
...: self._old_language = translation.get_language()
...: translation.activate(language_code)
...: def __exit__(self, *args):
...: translation.activate(self._old_language)
...: return with_block()

...:

In [6]: with language('nl'):
...: Template("{% load i18n %}{{ 1|yesno:_('yes,no,maybe')
}}").render(Context())
...:
...:
Out[6]: u'ja'

In [7]: with language('en'):
Template("{% load i18n %}{{ 1|yesno:_('yes,no,maybe') }}").render(Context())
...:
...:
Out[7]: u'yes'

In [8]: with language('nl'):
Template("{% load i18n %}{{ 0|yesno:_('yes,no,maybe') }}").render(Context())
....:
....:
Out[8]: u'nee'

In [9]: with language('en'):
Template("{% load i18n %}{{ 0|yesno:_('yes,no,maybe') }}").render(Context())
....:
....:
Out[9]: u'no'

Ramiro Morales

unread,
Jan 6, 2011, 6:41:33 AM1/6/11
to django-d...@googlegroups.com
On Thu, Jan 6, 2011 at 8:15 AM, Ramiro Morales <cra...@gmail.com> wrote:

> Weird, it's working here:

Ignore me, I was creating a new Template instance. I can reproduce it:

In [1]: from django.utils import translation

In [2]: from django.template import Template, Context

In [3]:

In [4]:

In [5]: def language(language_code):
...: class with_block(object):
...: def __enter__(self):
...: self._old_language = translation.get_language()
...: translation.activate(language_code)
...: def __exit__(self, *args):
...: translation.activate(self._old_language)
...: return with_block()
...:

In [6]: with language('de'): z = Template("{% load i18n %}{{


0|yesno:_('yes,no,maybe') }}")

...:

In [7]: with language('nl'):
...: z.render(Context())
...:
...:
Out[7]: u'Nein'

In [8]: with language('en'):
...: z.render(Context())
...:
...:
Out[8]: u'Nein'

If I apply this small fix to our code:

http://paste.pocoo.org/show/315758/

Things work correctly:

In [1]: from django.utils import translation

In [2]: from django.template import Template, Context

In [3]:

In [4]:

In [5]: def language(language_code):
...: class with_block(object):
...: def __enter__(self):
...: self._old_language = translation.get_language()
...: translation.activate(language_code)
...: def __exit__(self, *args):
...: translation.activate(self._old_language)
...: return with_block()
...:

In [6]: with language('de'): z = Template("{% load i18n %}{{


0|yesno:_('yes,no,maybe') }}")

...:

In [7]: with language('nl'):
...: z.render(Context())
...:
...:
Out[7]: u'nee'

In [8]: with language('en'):
...: z.render(Context())
...:
...:
Out[8]: u'no'

and out test suite still runs, but I need to add regression tests.
Will ask about this to I18N maintainers/contributors. Thanks!

--
Ramiro Morales

Jonathan S

unread,
Jan 6, 2011, 7:25:42 AM1/6/11
to Django developers
Thanks Ramiro, we hope the patch will be applied in the next release.

Ramiro Morales

unread,
Jan 24, 2011, 3:42:02 PM1/24/11
to django-d...@googlegroups.com
Hi,

I've opened ticket [1]15157 in the Django issue tracker for this report.

Any feedback or correction is welcome.

Thanks!

1. http://code.djangoproject.com/ticket/15157

--
Ramiro Morales

Reply all
Reply to author
Forward
0 new messages