Tip is now 2.5. Started work on newstyle gettext translations.
--HG--
branch : trunk
diff --git a/jinja2/compiler.py b/jinja2/compiler.py
index 0d608a3..1b01ed9 100644
--- a/jinja2/compiler.py
+++ b/jinja2/compiler.py
@@ -1582,6 +1582,11 @@
self.visit(node.expr, frame)
self.write(')')
+ def visit_MarkSafeIfAutoescape(self, node, frame):
+ self.write('(context.eval_ctx.autoescape and Markup or identity)(')
+ self.visit(node.expr, frame)
+ self.write(')')
+
def visit_EnvironmentAttribute(self, node, frame):
self.write('environment.' + node.name)
diff --git a/jinja2/ext.py b/jinja2/ext.py
index 63ce408..e6bef2f 100644
--- a/jinja2/ext.py
+++ b/jinja2/ext.py
@@ -123,8 +123,29 @@
@contextfunction
-def _gettext_alias(context, string):
- return context.resolve('gettext')(string)
+def _gettext_alias(__context, *args, **kwargs):
+ return __context.resolve('gettext')(*args, **kwargs)
+
+
+def _make_new_gettext(func):
+ @contextfunction
+ def gettext(__context, __string, **variables):
+ rv = func(__string)
+ if __context.eval_ctx.autoescape:
+ rv = Markup(rv)
+ return rv % variables
+ return gettext
+
+
+def _make_new_ngettext(func):
+ @contextfunction
+ def ngettext(__context, __singular, __plural, num, **variables):
+ variables.setdefault('num', num)
+ rv = func(__singular, __plural, num)
+ if __context.eval_ctx.autoescape:
+ rv = Markup(rv)
+ return rv % variables
+ return ngettext
class InternationalizationExtension(Extension):
@@ -144,23 +165,37 @@
environment.extend(
install_gettext_translations=self._install,
install_null_translations=self._install_null,
+ install_gettext_callables=self._install_callables,
uninstall_gettext_translations=self._uninstall,
- extract_translations=self._extract
+ extract_translations=self._extract,
+ newstyle_gettext=False
)
- def _install(self, translations):
+ def _install(self, translations, newstyle=None):
gettext = getattr(translations, 'ugettext', None)
if gettext is None:
gettext = translations.gettext
ngettext = getattr(translations, 'ungettext', None)
if ngettext is None:
ngettext = translations.ngettext
- self.environment.globals.update(gettext=gettext, ngettext=ngettext)
+ self._install_callables(gettext, ngettext, newstyle)
- def _install_null(self):
+ def _install_null(self, newstyle=None):
+ self._install_callables(
+ lambda x: x,
+ lambda s, p, n: (n != 1 and (p,) or (s,))[0],
+ newstyle
+ )
+
+ def _install_callables(self, gettext, ngettext, newstyle=None):
+ if newstyle is not None:
+ self.environment.newstyle_gettext = newstyle
+ if self.environment.newstyle_gettext:
+ gettext = _make_new_gettext(gettext)
+ ngettext = _make_new_ngettext(ngettext)
self.environment.globals.update(
- gettext=lambda x: x,
- ngettext=lambda s, p, n: (n != 1 and (p,) or (s,))[0]
+ gettext=gettext,
+ ngettext=ngettext
)
def _uninstall(self, translations):
@@ -310,13 +345,21 @@
plural_expr
], [], None, None)
- # mark the return value as safe if we are in an
- # environment with autoescaping turned on
- if self.environment.autoescape:
- node = nodes.MarkSafe(node)
+ # in case newstyle gettext is used, the method is powerful
+ # 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
- if variables:
- node = nodes.Mod(node, variables)
+ # otherwise do that here
+ else:
+ # mark the return value as safe if we are in an
+ # environment with autoescaping turned on
+ node = nodes.MarkSafeIfAutoescape(node)
+ if variables:
+ node = nodes.Mod(node, variables)
return nodes.Output([node])
diff --git a/jinja2/nodes.py b/jinja2/nodes.py
index 11247d8..8b5f89a 100644
--- a/jinja2/nodes.py
+++ b/jinja2/nodes.py
@@ -829,6 +829,22 @@
return Markup(self.expr.as_const(eval_ctx))
+class MarkSafeIfAutoescape(Expr):
+ """Mark the wrapped expression as safe (wrap it as `Markup`) but
+ only if autoescaping is active.
+
+ .. versionadded:: 2.5
+ """
+ fields = ('expr',)
+
+ def as_const(self, eval_ctx=None):
+ eval_ctx = get_eval_context(self, eval_ctx)
+ expr = self.expr.as_const(eval_ctx)
+ if eval_ctx.autoescape:
+ return Markup(expr)
+ return expr
+
+
class ContextReference(Expr):
"""Returns the current template context. It can be used like a
:class:`Name` node, with a ``'load'`` ctx and will return the
diff --git a/jinja2/runtime.py b/jinja2/runtime.py
index a89812f..00b823e 100644
--- a/jinja2/runtime.py
+++ b/jinja2/runtime.py
@@ -20,7 +20,7 @@
# these variables are exported to the template runtime
__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup',
'TemplateRuntimeError', 'missing', 'concat', 'escape',
- 'markup_join', 'unicode_join', 'to_string',
+ 'markup_join', 'unicode_join', 'to_string', 'identity',
'TemplateNotFound']
#: the name of the function that is used to convert something into
@@ -28,6 +28,9 @@
#: code can take advantage of it.
to_string = unicode
+#: the identity function. Useful for certain things in the environment
+identity = lambda x: x
+
def markup_join(seq):
"""Concatenation that escapes if necessary and converts to unicode."""