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

--HG--
branch : trunk
This commit is contained in:
Armin Ronacher 2010-05-29 17:35:10 +02:00
parent 7db9872700
commit 4da90349e8
5 changed files with 88 additions and 18 deletions

View File

@ -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
-------------

View File

@ -1582,6 +1582,11 @@ class CodeGenerator(NodeVisitor):
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)

View File

@ -123,8 +123,29 @@ class Extension(object):
@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 @@ class InternationalizationExtension(Extension):
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 @@ class InternationalizationExtension(Extension):
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])

View File

@ -829,6 +829,22 @@ class MarkSafe(Expr):
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

View File

@ -20,7 +20,7 @@ from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \
# 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 @@ __all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup',
#: 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."""