Tip is now 2.5.  Started work on newstyle gettext translations.

--HG--
branch : trunk
diff --git a/CHANGES b/CHANGES
index bbe3bd3..d17c230 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,13 +1,16 @@
 Jinja2 Changelog
 ================
 
-Version 2.4.2
--------------
-(bugfix release, release date to be announced)
+Version 2.5
+-----------
+(codename Incoherence, relased on May 29th 2010)
 
 - improved the sort filter (should have worked like this for a
   long time) by adding support for case insensitive searches.
 - fixed a bug for getattribute constant folding.
+- support for newstyle gettext translations which result in a
+  nicer in-template user interface and more consistent
+  catalogs.
 
 Version 2.4.1
 -------------
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."""