Skip to content

Commit

Permalink
Added a small improvement for the code generation of newstyle gettext
Browse files Browse the repository at this point in the history
calls.

--HG--
branch : trunk
  • Loading branch information
mitsuhiko committed May 29, 2010
1 parent a4c7843 commit b98dad9
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 11 deletions.
19 changes: 14 additions & 5 deletions jinja2/ext.py
Expand Up @@ -139,9 +139,9 @@ def gettext(__context, __string, **variables):

def _make_new_ngettext(func):
@contextfunction
def ngettext(__context, __singular, __plural, num, **variables):
variables.setdefault('num', num)
rv = __context.call(func, __singular, __plural, num)
def ngettext(__context, __singular, __plural, __num, **variables):
variables.setdefault('num', __num)
rv = __context.call(func, __singular, __plural, __num)
if __context.eval_ctx.autoescape:
rv = Markup(rv)
return rv % variables
Expand Down Expand Up @@ -210,6 +210,7 @@ def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS):
def parse(self, parser):
"""Parse a translatable tag."""
lineno = next(parser.stream).lineno
num_called_num = False

# find all the variables referenced. Additionally a variable can be
# defined in the body of the trans block too, but this is checked at
Expand All @@ -236,8 +237,10 @@ def parse(self, parser):
variables[name.value] = var = parser.parse_expression()
else:
variables[name.value] = var = nodes.Name(name.value, 'load')

if plural_expr is None:
plural_expr = var
num_called_num = name.value == 'num'

parser.stream.expect('block_end')

Expand All @@ -251,6 +254,7 @@ def parse(self, parser):
referenced.update(singular_names)
if plural_expr is None:
plural_expr = nodes.Name(singular_names[0], 'load')
num_called_num = singular_names[0] == 'num'

# if we have a pluralize block, we parse that too
if parser.stream.current.test('name:pluralize'):
Expand All @@ -263,6 +267,7 @@ def parse(self, parser):
name.value, name.lineno,
exc=TemplateAssertionError)
plural_expr = variables[name.value]
num_called_num = name.value == 'num'
parser.stream.expect('block_end')
plural_names, plural = self._parse_block(parser, False)
next(parser.stream)
Expand All @@ -281,7 +286,7 @@ def parse(self, parser):
parser.fail('pluralize without variables', lineno)

node = self._make_node(singular, plural, variables, plural_expr,
bool(referenced))
bool(referenced), num_called_num)
node.set_lineno(lineno)
return node

Expand Down Expand Up @@ -318,7 +323,7 @@ def _parse_block(self, parser, allow_pluralize):
return referenced, concat(buf)

def _make_node(self, singular, plural, variables, plural_expr,
vars_referenced):
vars_referenced, num_called_num):
"""Generates a useful node from the data provided."""
# no variables referenced? no need to escape for old style
# gettext invocations
Expand Down Expand Up @@ -347,6 +352,10 @@ def _make_node(self, singular, plural, variables, plural_expr,
# handling itself
if self.environment.newstyle_gettext:
for key, value in variables.iteritems():
# the function adds that later anyways in case num was
# called num, so just skip it.
if num_called_num and key == 'num':
continue
node.kwargs.append(nodes.Keyword(key, value))

# otherwise do that here
Expand Down
30 changes: 24 additions & 6 deletions jinja2/testsuite/ext.py
Expand Up @@ -38,7 +38,7 @@
'{% trans %}watch out{% endtrans %}{% endblock %}',
'plural.html': '{% trans user_count %}One user online{% pluralize %}'
'{{ user_count }} users online{% endtrans %}',
'stringformat.html': '{{ _("User: %(num)d")|format(num=user_count) }}'
'stringformat.html': '{{ _("User: %(num)s")|format(num=user_count) }}'
}

newstyle_i18n_templates = {
Expand All @@ -48,8 +48,10 @@
'{% trans %}watch out{% endtrans %}{% endblock %}',
'plural.html': '{% trans user_count %}One user online{% pluralize %}'
'{{ user_count }} users online{% endtrans %}',
'stringformat.html': '{{ _("User: %(num)d", num=user_count) }}',
'ngettext.html': '{{ ngettext("%(num)d apple", "%(num)d apples", apples) }}'
'stringformat.html': '{{ _("User: %(num)s", num=user_count) }}',
'ngettext.html': '{{ ngettext("%(num)s apple", "%(num)s apples", apples) }}',
'ngettext_long.html': '{% trans num=apples %}{{ num }} apple{% pluralize %}'
'{{ num }} apples{% endtrans %}'
}


Expand All @@ -59,9 +61,9 @@
'watch out': u'pass auf',
'One user online': u'Ein Benutzer online',
'%(user_count)s users online': u'%(user_count)s Benutzer online',
'User: %(num)d': u'Benutzer: %(num)d',
'%(num)d apple': u'%(num)d Apfel',
'%(num)d apples': u'%(num)d Äpfel'
'User: %(num)s': u'Benutzer: %(num)s',
'%(num)s apple': u'%(num)s Apfel',
'%(num)s apples': u'%(num)s Äpfel'
}
}

Expand Down Expand Up @@ -327,6 +329,22 @@ def test_autoescape_support(self):
assert t.render(ae=True) == '<strong>Wert: &lt;test&gt;</strong>'
assert t.render(ae=False) == '<strong>Wert: <test></strong>'

def test_num_used_twice(self):
tmpl = newstyle_i18n_env.get_template('ngettext_long.html')
assert tmpl.render(apples=5, LANGUAGE='de') == u'5 Äpfel'

def test_num_called_num(self):
source = newstyle_i18n_env.compile('''
{% trans num=3 %}{{ num }} apple{% pluralize
%}{{ num }} apples{% endtrans %}
''', raw=True)
# quite hacky, but the only way to properly test that. The idea is
# that the generated code does not pass num twice (although that
# would work) for better performance. This only works on the
# newstyle gettext of course
assert re.search(r"l_ngettext, u?'\%\(num\)s apple', u?'\%\(num\)s "
r"apples', 3", source) is not None


class AutoEscapeTestCase(JinjaTestCase):

Expand Down

0 comments on commit b98dad9

Please sign in to comment.