Fixed a few problems with variable resolving inside of blocktrans tags. A couple of these were exposed by the auto-escaping changes, but I suspect the other one has been hiding in plain sight for a while.

Fixed #5952, #5953


git-svn-id: http://code.djangoproject.com/svn/django/trunk@6682 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2007-11-17 12:12:18 +00:00
parent 64c0bf8677
commit adcec0885d
2 changed files with 26 additions and 15 deletions

View File

@ -1,9 +1,10 @@
import re import re
from django.template import Node, Variable from django.template import Node, Variable, VariableNode
from django.template import TemplateSyntaxError, TokenParser, Library from django.template import TemplateSyntaxError, TokenParser, Library
from django.template import TOKEN_TEXT, TOKEN_VAR from django.template import TOKEN_TEXT, TOKEN_VAR
from django.utils import translation from django.utils import translation
from django.utils.encoding import force_unicode
register = Library() register = Library()
@ -45,7 +46,8 @@ class TranslateNode(Node):
return translation.ugettext(value) return translation.ugettext(value)
class BlockTranslateNode(Node): class BlockTranslateNode(Node):
def __init__(self, extra_context, singular, plural=None, countervar=None, counter=None): def __init__(self, extra_context, singular, plural=None, countervar=None,
counter=None):
self.extra_context = extra_context self.extra_context = extra_context
self.singular = singular self.singular = singular
self.plural = plural self.plural = plural
@ -54,29 +56,32 @@ class BlockTranslateNode(Node):
def render_token_list(self, tokens): def render_token_list(self, tokens):
result = [] result = []
vars = []
for token in tokens: for token in tokens:
if token.token_type == TOKEN_TEXT: if token.token_type == TOKEN_TEXT:
result.append(token.contents) result.append(token.contents)
elif token.token_type == TOKEN_VAR: elif token.token_type == TOKEN_VAR:
result.append('%%(%s)s' % token.contents) result.append(u'%%(%s)s' % token.contents)
return ''.join(result) vars.append(token.contents)
return ''.join(result), vars
def render(self, context): def render(self, context):
context.push() context.push()
for var,val in self.extra_context.items(): for var, val in self.extra_context.items():
context[var] = val.resolve(context) context[var] = val.render(context)
singular = self.render_token_list(self.singular) singular, vars = self.render_token_list(self.singular)
if self.plural and self.countervar and self.counter: if self.plural and self.countervar and self.counter:
count = self.counter.resolve(context) count = self.counter.resolve(context)
context[self.countervar] = count context[self.countervar] = count
plural = self.render_token_list(self.plural) plural = self.render_token_list(self.plural)[0]
result = translation.ungettext(singular, plural, count) result = translation.ungettext(singular, plural, count)
else: else:
result = translation.ugettext(singular) result = translation.ugettext(singular)
# Escape all isolated '%' before substituting in the context. # Escape all isolated '%' before substituting in the context.
result = re.sub('%(?!\()', '%%', result) % context result = re.sub(u'%(?!\()', u'%%', result)
data = dict([(v, force_unicode(context[v])) for v in vars])
context.pop() context.pop()
return result return result % data
def do_get_available_languages(parser, token): def do_get_available_languages(parser, token):
""" """
@ -198,7 +203,6 @@ def do_block_translate(parser, token):
This is much like ngettext, only in template syntax. This is much like ngettext, only in template syntax.
""" """
class BlockTranslateParser(TokenParser): class BlockTranslateParser(TokenParser):
def top(self): def top(self):
countervar = None countervar = None
counter = None counter = None
@ -209,7 +213,8 @@ def do_block_translate(parser, token):
value = self.value() value = self.value()
if self.tag() != 'as': if self.tag() != 'as':
raise TemplateSyntaxError, "variable bindings in 'blocktrans' must be 'with value as variable'" raise TemplateSyntaxError, "variable bindings in 'blocktrans' must be 'with value as variable'"
extra_context[self.tag()] = parser.compile_filter(value) extra_context[self.tag()] = VariableNode(
parser.compile_filter(value))
elif tag == 'count': elif tag == 'count':
counter = parser.compile_filter(self.value()) counter = parser.compile_filter(self.value())
if self.tag() != 'as': if self.tag() != 'as':
@ -241,7 +246,8 @@ def do_block_translate(parser, token):
if token.contents.strip() != 'endblocktrans': if token.contents.strip() != 'endblocktrans':
raise TemplateSyntaxError, "'blocktrans' doesn't allow other block tags (seen %r) inside it" % token.contents raise TemplateSyntaxError, "'blocktrans' doesn't allow other block tags (seen %r) inside it" % token.contents
return BlockTranslateNode(extra_context, singular, plural, countervar, counter) return BlockTranslateNode(extra_context, singular, plural, countervar,
counter)
register.tag('get_available_languages', do_get_available_languages) register.tag('get_available_languages', do_get_available_languages)
register.tag('get_current_language', do_get_current_language) register.tag('get_current_language', do_get_current_language)

View File

@ -705,10 +705,10 @@ class Templates(unittest.TestCase):
'i18n02': ('{% load i18n %}{% trans "xxxyyyxxx" %}', {}, "xxxyyyxxx"), 'i18n02': ('{% load i18n %}{% trans "xxxyyyxxx" %}', {}, "xxxyyyxxx"),
# simple translation of a variable # simple translation of a variable
'i18n03': ('{% load i18n %}{% blocktrans %}{{ anton }}{% endblocktrans %}', {'anton': 'xxxyyyxxx'}, "xxxyyyxxx"), 'i18n03': ('{% load i18n %}{% blocktrans %}{{ anton }}{% endblocktrans %}', {'anton': '\xc3\x85'}, u"Å"),
# simple translation of a variable and filter # simple translation of a variable and filter
'i18n04': ('{% load i18n %}{% blocktrans with anton|lower as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'XXXYYYXXX'}, "xxxyyyxxx"), 'i18n04': ('{% load i18n %}{% blocktrans with anton|lower as berta %}{{ berta }}{% endblocktrans %}', {'anton': '\xc3\x85'}, u'å'),
# simple translation of a string with interpolation # simple translation of a string with interpolation
'i18n05': ('{% load i18n %}{% blocktrans %}xxx{{ anton }}xxx{% endblocktrans %}', {'anton': 'yyy'}, "xxxyyyxxx"), 'i18n05': ('{% load i18n %}{% blocktrans %}xxx{{ anton }}xxx{% endblocktrans %}', {'anton': 'yyy'}, "xxxyyyxxx"),
@ -740,6 +740,11 @@ class Templates(unittest.TestCase):
'i18n15': ('{{ absent|default:_("Password") }}', {'LANGUAGE_CODE': 'de', 'absent': ""}, 'Passwort'), 'i18n15': ('{{ absent|default:_("Password") }}', {'LANGUAGE_CODE': 'de', 'absent': ""}, 'Passwort'),
'i18n16': ('{{ _("<") }}', {'LANGUAGE_CODE': 'de'}, '<'), 'i18n16': ('{{ _("<") }}', {'LANGUAGE_CODE': 'de'}, '<'),
# Escaping inside blocktrans works as if it was directly in the
# template.
'i18n17': ('{% load i18n %}{% blocktrans with anton|escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α &amp; β'),
'i18n18': ('{% load i18n %}{% blocktrans with anton|force_escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α &amp; β'),
### HANDLING OF TEMPLATE_STRING_IF_INVALID ################################### ### HANDLING OF TEMPLATE_STRING_IF_INVALID ###################################
'invalidstr01': ('{{ var|default:"Foo" }}', {}, ('Foo','INVALID')), 'invalidstr01': ('{{ var|default:"Foo" }}', {}, ('Foo','INVALID')),