import re
import unittest
-from jinja2.testsuite import JinjaTestCase, filesystem_loader
+from jinja2.testsuite import JinjaTestCase
from jinja2 import Environment, DictLoader, contextfunction, nodes
from jinja2.exceptions import TemplateAssertionError
try:
from io import BytesIO
except ImportError:
- from StringIO import StringIO as BytesIO
+ from io import StringIO as BytesIO
importable_object = 23
_gettext_re = re.compile(r'_\((.*?)\)(?s)')
-templates = {
+i18n_templates = {
'master.html': '<title>{{ page_title|default(_("missing")) }}</title>'
'{% block body %}{% endblock %}',
'child.html': '{% extends "master.html" %}{% block body %}'
'{% trans %}watch out{% endtrans %}{% endblock %}',
'plural.html': '{% trans user_count %}One user online{% pluralize %}'
'{{ user_count }} users online{% endtrans %}',
- 'stringformat.html': '{{ _("User: %d")|format(user_count) }}'
+ 'stringformat.html': '{{ _("User: %(num)s")|format(num=user_count) }}'
+}
+
+newstyle_i18n_templates = {
+ 'master.html': '<title>{{ page_title|default(_("missing")) }}</title>'
+ '{% block body %}{% endblock %}',
+ 'child.html': '{% extends "master.html" %}{% block body %}'
+ '{% trans %}watch out{% endtrans %}{% endblock %}',
+ 'plural.html': '{% trans user_count %}One user online{% pluralize %}'
+ '{{ user_count }} users online{% endtrans %}',
+ '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 %}',
+ 'transvars1.html': '{% trans %}User: {{ num }}{% endtrans %}',
+ 'transvars2.html': '{% trans num=count %}User: {{ num }}{% endtrans %}',
+ 'transvars3.html': '{% trans count=num %}User: {{ count }}{% endtrans %}',
+ 'novars.html': '{% trans %}%(hello)s{% endtrans %}',
+ 'vars.html': '{% trans %}{{ foo }}%(foo)s{% endtrans %}',
+ 'explicitvars.html': '{% trans foo="42" %}%(foo)s{% endtrans %}'
}
'watch out': 'pass auf',
'One user online': 'Ein Benutzer online',
'%(user_count)s users online': '%(user_count)s Benutzer online',
- 'User: %d': 'Benutzer: %d'
+ 'User: %(num)s': 'Benutzer: %(num)s',
+ 'User: %(count)s': 'Benutzer: %(count)s',
+ '%(num)s apple': '%(num)s Apfel',
+ '%(num)s apples': '%(num)s Äpfel'
}
}
i18n_env = Environment(
- loader=DictLoader(templates),
+ loader=DictLoader(i18n_templates),
extensions=['jinja2.ext.i18n']
)
i18n_env.globals.update({
'ngettext': ngettext
})
+newstyle_i18n_env = Environment(
+ loader=DictLoader(newstyle_i18n_templates),
+ extensions=['jinja2.ext.i18n']
+)
+newstyle_i18n_env.install_gettext_callables(gettext, ngettext, newstyle=True)
class TestExtension(Extension):
tags = set(['test'])
class ExtensionsTestCase(JinjaTestCase):
+ def test_extend_late(self):
+ env = Environment()
+ env.add_extension('jinja2.ext.autoescape')
+ t = env.from_string('{% autoescape true %}{{ "<test>" }}{% endautoescape %}')
+ assert t.render() == '<test>'
+
def test_loop_controls(self):
env = Environment(extensions=['jinja2.ext.loopcontrols'])
original = Environment(extensions=[TestExtension])
overlay = original.overlay()
for env in original, overlay:
- for ext in env.extensions.itervalues():
+ for ext in env.extensions.values():
assert ext.environment is env
def test_preprocessor_extension(self):
{% trans %}{{ users }} user{% pluralize %}{{ users }} users{% endtrans %}
'''.encode('ascii')) # make python 3 happy
assert list(babel_extract(source, ('gettext', 'ngettext', '_'), [], {})) == [
- (2, 'gettext', u'Hello World', []),
- (3, 'gettext', u'Hello World', []),
- (4, 'ngettext', (u'%(users)s user', u'%(users)s users', None), [])
+ (2, 'gettext', 'Hello World', []),
+ (3, 'gettext', 'Hello World', []),
+ (4, 'ngettext', ('%(users)s user', '%(users)s users', None), [])
]
def test_comment_extract(self):
{% trans %}{{ users }} user{% pluralize %}{{ users }} users{% endtrans %}
'''.encode('utf-8')) # make python 3 happy
assert list(babel_extract(source, ('gettext', 'ngettext', '_'), ['trans', ':'], {})) == [
- (3, 'gettext', u'Hello World', ['first']),
- (4, 'gettext', u'Hello World', ['second']),
- (6, 'ngettext', (u'%(users)s user', u'%(users)s users', None), ['third'])
+ (3, 'gettext', 'Hello World', ['first']),
+ (4, 'gettext', 'Hello World', ['second']),
+ (6, 'ngettext', ('%(users)s user', '%(users)s users', None), ['third'])
]
+class NewstyleInternationalizationTestCase(JinjaTestCase):
+
+ def test_trans(self):
+ tmpl = newstyle_i18n_env.get_template('child.html')
+ assert tmpl.render(LANGUAGE='de') == '<title>fehlend</title>pass auf'
+
+ def test_trans_plural(self):
+ tmpl = newstyle_i18n_env.get_template('plural.html')
+ assert tmpl.render(LANGUAGE='de', user_count=1) == 'Ein Benutzer online'
+ assert tmpl.render(LANGUAGE='de', user_count=2) == '2 Benutzer online'
+
+ def test_complex_plural(self):
+ tmpl = newstyle_i18n_env.from_string('{% trans foo=42, count=2 %}{{ count }} item{% '
+ 'pluralize count %}{{ count }} items{% endtrans %}')
+ assert tmpl.render() == '2 items'
+ self.assert_raises(TemplateAssertionError, i18n_env.from_string,
+ '{% trans foo %}...{% pluralize bar %}...{% endtrans %}')
+
+ def test_trans_stringformatting(self):
+ tmpl = newstyle_i18n_env.get_template('stringformat.html')
+ assert tmpl.render(LANGUAGE='de', user_count=5) == 'Benutzer: 5'
+
+ def test_newstyle_plural(self):
+ tmpl = newstyle_i18n_env.get_template('ngettext.html')
+ assert tmpl.render(LANGUAGE='de', apples=1) == '1 Apfel'
+ assert tmpl.render(LANGUAGE='de', apples=5) == '5 Äpfel'
+
+ def test_autoescape_support(self):
+ env = Environment(extensions=['jinja2.ext.autoescape',
+ 'jinja2.ext.i18n'])
+ env.install_gettext_callables(lambda x: '<strong>Wert: %(name)s</strong>',
+ lambda s, p, n: s, newstyle=True)
+ t = env.from_string('{% autoescape ae %}{{ gettext("foo", name='
+ '"<test>") }}{% endautoescape %}')
+ assert t.render(ae=True) == '<strong>Wert: <test></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') == '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
+
+ def test_trans_vars(self):
+ t1 = newstyle_i18n_env.get_template('transvars1.html')
+ t2 = newstyle_i18n_env.get_template('transvars2.html')
+ t3 = newstyle_i18n_env.get_template('transvars3.html')
+ assert t1.render(num=1, LANGUAGE='de') == 'Benutzer: 1'
+ assert t2.render(count=23, LANGUAGE='de') == 'Benutzer: 23'
+ assert t3.render(num=42, LANGUAGE='de') == 'Benutzer: 42'
+
+ def test_novars_vars_escaping(self):
+ t = newstyle_i18n_env.get_template('novars.html')
+ assert t.render() == '%(hello)s'
+ t = newstyle_i18n_env.get_template('vars.html')
+ assert t.render(foo='42') == '42%(foo)s'
+ t = newstyle_i18n_env.get_template('explicitvars.html')
+ assert t.render() == '%(foo)s'
+
+
class AutoEscapeTestCase(JinjaTestCase):
def test_scoped_setting(self):
{{ "<HelloWorld>" }}
''')
assert tmpl.render().split() == \
- [u'<HelloWorld>', u'<HelloWorld>', u'<HelloWorld>']
+ ['<HelloWorld>', '<HelloWorld>', '<HelloWorld>']
env = Environment(extensions=['jinja2.ext.autoescape'],
autoescape=False)
{{ "<HelloWorld>" }}
''')
assert tmpl.render().split() == \
- [u'<HelloWorld>', u'<HelloWorld>', u'<HelloWorld>']
+ ['<HelloWorld>', '<HelloWorld>', '<HelloWorld>']
def test_nonvolatile(self):
env = Environment(extensions=['jinja2.ext.autoescape'],
'''
tmpl = env.from_string(tmplsource)
assert tmpl.render(val=True).split()[0] == 'Markup'
- assert tmpl.render(val=False).split()[0] == unicode.__name__
+ assert tmpl.render(val=False).split()[0] == str.__name__
# looking at the source we should see <testing> there in raw
# (and then escaped as well)
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(ExtensionsTestCase))
suite.addTest(unittest.makeSuite(InternationalizationTestCase))
+ suite.addTest(unittest.makeSuite(NewstyleInternationalizationTestCase))
suite.addTest(unittest.makeSuite(AutoEscapeTestCase))
return suite