Finished support for newstyle gettext translations
authorArmin Ronacher <armin.ronacher@active-4.com>
Sat, 29 May 2010 15:58:06 +0000 (17:58 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Sat, 29 May 2010 15:58:06 +0000 (17:58 +0200)
--HG--
branch : trunk

jinja2/environment.py
jinja2/ext.py
jinja2/testsuite/ext.py

index 529c14c407d0a1b29050cb11e9161730b394b19f..03aef1aa9b46066a8a527026d62035d335a808a9 100644 (file)
@@ -279,6 +279,13 @@ class Environment(object):
 
         _environment_sanity_check(self)
 
+    def add_extension(self, extension):
+        """Adds an extension after the environment was created.
+
+        .. versionadded:: 2.5
+        """
+        load_extensions(self, [extension])
+
     def extend(self, **attributes):
         """Add the items to the instance of the environment if they do not exist
         yet.  This is used by :ref:`extensions <writing-extensions>` to register
index e6bef2f551fa086a00c12c84d64ae93d00d21175..42206577f7276dac0c75cb70506da649a8f112b6 100644 (file)
@@ -124,13 +124,13 @@ class Extension(object):
 
 @contextfunction
 def _gettext_alias(__context, *args, **kwargs):
-    return __context.resolve('gettext')(*args, **kwargs)
+    return __context.call(__context.resolve('gettext'), *args, **kwargs)
 
 
 def _make_new_gettext(func):
     @contextfunction
     def gettext(__context, __string, **variables):
-        rv  = func(__string)
+        rv  = __context.call(func, __string)
         if __context.eval_ctx.autoescape:
             rv = Markup(rv)
         return rv % variables
@@ -141,7 +141,7 @@ def _make_new_ngettext(func):
     @contextfunction
     def ngettext(__context, __singular, __plural, num, **variables):
         variables.setdefault('num', num)
-        rv = func(__singular, __plural, num)
+        rv = __context.call(func, __singular, __plural, num)
         if __context.eval_ctx.autoescape:
             rv = Markup(rv)
         return rv % variables
@@ -286,12 +286,6 @@ class InternationalizationExtension(Extension):
         elif plural_expr is None:
             parser.fail('pluralize without variables', lineno)
 
-        if variables:
-            variables = nodes.Dict([nodes.Pair(nodes.Const(x, lineno=lineno), y)
-                                    for x, y in variables.items()])
-        else:
-            variables = None
-
         node = self._make_node(singular, plural, variables, plural_expr)
         node.set_lineno(lineno)
         return node
@@ -349,9 +343,8 @@ class InternationalizationExtension(Extension):
         # enough to handle the variable expansion and autoescape
         # handling itself
         if self.environment.newstyle_gettext:
-            if variables is None:
-                variables = nodes.Dict([])
-            node.kwargs = variables
+            for key, value in variables.iteritems():
+                node.kwargs.append(nodes.Keyword(key, value))
 
         # otherwise do that here
         else:
@@ -359,7 +352,10 @@ class InternationalizationExtension(Extension):
             # environment with autoescaping turned on
             node = nodes.MarkSafeIfAutoescape(node)
             if variables:
-                node = nodes.Mod(node, variables)
+                node = nodes.Mod(node, nodes.Dict([
+                    nodes.Pair(nodes.Const(key), value)
+                    for key, value in variables.items()
+                ]))
         return nodes.Output([node])
 
 
index c00c589fc1942a99b6bc6be1aadc36c8d0994df9..13069732381541621e02de62a614d06f73cb6ef2 100644 (file)
@@ -31,24 +31,37 @@ 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)d")|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)d", num=user_count) }}',
+    'ngettext.html': '{{ ngettext("%(num)d apple", "%(num)d apples", apples) }}'
 }
 
 
 languages = {
     'de': {
-        'missing':                      'fehlend',
-        'watch out':                    'pass auf',
-        'One user online':              'Ein Benutzer online',
-        '%(user_count)s users online':  '%(user_count)s Benutzer online',
-        'User: %d':                     'Benutzer: %d'
+        'missing':                      u'fehlend',
+        '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'
     }
 }
 
@@ -68,7 +81,7 @@ def ngettext(context, s, p, n):
 
 
 i18n_env = Environment(
-    loader=DictLoader(templates),
+    loader=DictLoader(i18n_templates),
     extensions=['jinja2.ext.i18n']
 )
 i18n_env.globals.update({
@@ -77,6 +90,11 @@ 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'])
@@ -266,6 +284,34 @@ class InternationalizationTestCase(JinjaTestCase):
         ]
 
 
+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) == u'5 Äpfel'
+
+
 class AutoEscapeTestCase(JinjaTestCase):
 
     def test_scoped_setting(self):
@@ -347,5 +393,6 @@ def suite():
     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