Commit 9fd2f9c5 authored by Claude Paroz's avatar Claude Paroz
Browse files

Fixed #19088 -- Always escape % inside blocktrans tag

Thanks vlinhart for the report and Łukasz Rekucki for the patch.
parent 3541a10d
Loading
Loading
Loading
Loading
+7 −6
Original line number Diff line number Diff line
@@ -110,13 +110,13 @@ class BlockTranslateNode(Node):
        vars = []
        for token in tokens:
            if token.token_type == TOKEN_TEXT:
                result.append(token.contents)
                result.append(token.contents.replace('%', '%%'))
            elif token.token_type == TOKEN_VAR:
                result.append('%%(%s)s' % token.contents)
                vars.append(token.contents)
        return ''.join(result), vars

    def render(self, context):
    def render(self, context, nested=False):
        if self.message_context:
            message_context = self.message_context.resolve(context)
        else:
@@ -128,13 +128,10 @@ class BlockTranslateNode(Node):
        # the end of function
        context.update(tmp_context)
        singular, vars = self.render_token_list(self.singular)
        # Escape all isolated '%'
        singular = re.sub('%(?!\()', '%%', singular)
        if self.plural and self.countervar and self.counter:
            count = self.counter.resolve(context)
            context[self.countervar] = count
            plural, plural_vars = self.render_token_list(self.plural)
            plural = re.sub('%(?!\()', '%%', plural)
            if message_context:
                result = translation.npgettext(message_context, singular,
                                               plural, count)
@@ -151,8 +148,12 @@ class BlockTranslateNode(Node):
        try:
            result = result % data
        except (KeyError, ValueError):
            if nested:
                # Either string is malformed, or it's a bug
                raise TemplateSyntaxError("'blocktrans' is unable to format "
                    "string returned by gettext: %r using %r" % (result, data))
            with translation.override(None):
                result = self.render(context)
                result = self.render(context, nested=True)
        return result


+14 −2
Original line number Diff line number Diff line
@@ -269,7 +269,6 @@ class TranslationTests(TestCase):
        (%(person)s is translated as %(personne)s in fr.po)
        Refs #16516.
        """
        from django.template import Template, Context
        with translation.override('fr'):
            t = Template('{% load i18n %}{% blocktrans %}My name is {{ person }}.{% endblocktrans %}')
            rendered = t.render(Context({'person': 'James'}))
@@ -282,7 +281,6 @@ class TranslationTests(TestCase):
        (%(person) misses a 's' in fr.po, causing the string formatting to fail)
        Refs #18393.
        """
        from django.template import Template, Context
        with translation.override('fr'):
            t = Template('{% load i18n %}{% blocktrans %}My other name is {{ person }}.{% endblocktrans %}')
            rendered = t.render(Context({'person': 'James'}))
@@ -821,6 +819,20 @@ class MiscTests(TestCase):
            self.assertEqual(t_plur.render(Context({'percent': 42, 'num': 1})), '42% stellt 1 Objekt dar')
            self.assertEqual(t_plur.render(Context({'percent': 42, 'num': 4})), '42% stellt 4 Objekte dar')

    @override_settings(LOCALE_PATHS=extended_locale_paths)
    def test_percent_formatting_in_blocktrans(self):
        """
        Test that using Python's %-formatting is properly escaped in blocktrans,
        singular or plural
        """
        t_sing = Template("{% load i18n %}{% blocktrans %}There are %(num_comments)s comments{% endblocktrans %}")
        t_plur = Template("{% load i18n %}{% blocktrans count num as number %}%(percent)s% represents {{ num }} object{% plural %}%(percent)s% represents {{ num }} objects{% endblocktrans %}")
        with translation.override('de'):
            # Strings won't get translated as they don't match after escaping %
            self.assertEqual(t_sing.render(Context({'num_comments': 42})), 'There are %(num_comments)s comments')
            self.assertEqual(t_plur.render(Context({'percent': 42, 'num': 1})), '%(percent)s% represents 1 object')
            self.assertEqual(t_plur.render(Context({'percent': 42, 'num': 4})), '%(percent)s% represents 4 objects')


class ResolutionOrderI18NTests(TestCase):