Index: ../django/template/__init__.py
===================================================================
--- ../django/template/__init__.py	(revision 7547)
+++ ../django/template/__init__.py	(working copy)
@@ -100,6 +100,46 @@
 # uninitialised.
 invalid_var_format_string = None
 
+def unescape_string_literal(s):
+    r"""
+    Convert quoted string literals to unquoted strings with escaped quotes and
+    backslashes unquoted::
+
+        >>> unescape_string_literal('"abc"')
+        'abc'
+        >>> unescape_string_literal("'abc'")
+        'abc'
+        >>> unescape_string_literal('"a \"bc\""')
+        'a "bc"'
+        >>> unescape_string_literal("'\'ab\' c'")
+        "'ab' c"
+
+    Would this function be more appropriate in the django.utils.text module?
+    """
+    if s[0] not in "\"'" or s[-1] != s[0]:
+        raise ValueError("Not a string literal: %r" % s)
+    quote = s[0]
+    return s[1:-1].replace(r'\%s' % quote, quote).replace(r'\\', '\\')
+
+def resolve_string_literal(s):
+    r"""
+    Un-escape, translate and unquote string literals.  Handle single and double
+    quoted strings with corresponding quotes and backslashes escaped with
+    prepending backslashes::
+
+        >>> resolve_string_literal(ur'"Some \"Good\" \\ News"')
+        u'Some "Good" \\ News'
+        >>> resolve_string_literal(ur"'Some \'Good\' \\ News'")
+        u"Some 'Good' \\ News"
+        >>> resolve_string_literal(ur'_("Some \"Good\" \\ News")')
+        u'Some "Good" \\ News'
+        >>> resolve_string_literal(ur"_('Some \'Good\' \\ News')")
+        u"Some 'Good' \\ News"
+    """
+    if s.startswith('_(') and s.endswith(')'):
+        return mark_safe(_(unescape_string_literal(s[2:-1])))
+    return mark_safe(unescape_string_literal(s))
+
 class TemplateSyntaxError(Exception):
     def __str__(self):
         try:
@@ -431,33 +471,42 @@
             self.pointer = i
             return s
 
+constant_string = r"""
+(?:%(i18n_open)s%(strdq)s%(i18n_close)s|
+%(i18n_open)s%(strsq)s%(i18n_close)s|
+%(strdq)s|
+%(strsq)s)
+""" % {
+    'strdq': r'''"[^"\\]*(?:\\.[^"\\]*)*"''', # double-quoted string
+    'strsq': r"""'[^'\\]*(?:\\.[^'\\]*)*'""", # single-quoted string
+    'i18n_open' : re.escape("_("),
+    'i18n_close' : re.escape(")"),
+  }
+constant_string = constant_string.replace("\n", "")
+
 filter_raw_string = r"""
-^%(i18n_open)s"(?P<i18n_constant>%(str)s)"%(i18n_close)s|
-^"(?P<constant>%(str)s)"|
+^(?P<constant>%(constant)s)|
 ^(?P<var>[%(var_chars)s]+)|
  (?:%(filter_sep)s
      (?P<filter_name>\w+)
          (?:%(arg_sep)s
              (?:
-              %(i18n_open)s"(?P<i18n_arg>%(str)s)"%(i18n_close)s|
-              "(?P<constant_arg>%(str)s)"|
+              (?P<constant_arg>%(constant)s)|
               (?P<var_arg>[%(var_chars)s]+)
              )
          )?
  )""" % {
-    'str': r"""[^"\\]*(?:\\.[^"\\]*)*""",
+    'constant': constant_string,
     'var_chars': "\w\." ,
     'filter_sep': re.escape(FILTER_SEPARATOR),
     'arg_sep': re.escape(FILTER_ARGUMENT_SEPARATOR),
-    'i18n_open' : re.escape("_("),
-    'i18n_close' : re.escape(")"),
   }
 
 filter_raw_string = filter_raw_string.replace("\n", "").replace(" ", "")
 filter_re = re.compile(filter_raw_string, re.UNICODE)
 
 class FilterExpression(object):
-    """
+    r"""
     Parses a variable token and its optional filters (all as a single string),
     and return a list of tuples of the filter name and arguments.
     Sample:
@@ -469,8 +518,43 @@
         >>> fe.var
         'variable'
 
+        >>> c = {'article': {'section': u'News'}}
+        >>> def fe_test(s): return FilterExpression(s, p).resolve(c)
+        >>> fe_test('article.section')
+        u'News'
+        >>> fe_test('article.section|upper')
+        u'NEWS'
+        >>> fe_test(u'"News"')
+        u'News'
+        >>> fe_test(u"'News'")
+        u'News'
+        >>> fe_test(ur'"Some \"Good\" News"')
+        u'Some "Good" News'
+        >>> fe_test(ur"'Some \'Bad\' News'")
+        u"Some 'Bad' News"
+
+        >>> fe = FilterExpression(ur'"Some \"Good\" News"', p)
+        >>> fe.filters
+        []
+        >>> fe.var
+        u'Some "Good" News'
+
     This class should never be instantiated outside of the
     get_filters_from_token helper function.
+
+    The filter_re regular expression is responsible for tokenizing the filter
+    expression::
+
+        >>> def fre_test(s):
+        ...     print '|'.join(','.join("%s=%s"%(key, val) for key, val in sorted(match.groupdict().items()) if val is not None) for match in filter_re.finditer(s))
+        >>> fre_test('myvar')
+        var=myvar
+        >>> fre_test('myvar|myfilt:myarg')
+        var=myvar|filter_name=myfilt,var_arg=myarg
+        >>> fre_test(r'"Some \"Good\" News"|myfilt:"Some \"Bad\" News"')
+        constant="Some \"Good\" News"|constant_arg="Some \"Bad\" News",filter_name=myfilt
+        >>> fre_test(r"'More \'Good\' News'|myfilt:'More \'Bad\' News'")
+        constant='More \'Good\' News'|constant_arg='More \'Bad\' News',filter_name=myfilt
     """
     def __init__(self, token, parser):
         self.token = token
@@ -484,24 +568,22 @@
                 raise TemplateSyntaxError("Could not parse some characters: %s|%s|%s"  % \
                                            (token[:upto], token[upto:start], token[start:]))
             if var == None:
-                var, constant, i18n_constant = match.group("var", "constant", "i18n_constant")
-                if i18n_constant:
-                    var = '"%s"' %  _(i18n_constant.replace(r'\"', '"'))
-                elif constant:
-                    var = '"%s"' % constant.replace(r'\"', '"')
-                upto = match.end()
-                if var == None:
+                var, constant = match.group("var", "constant")
+                if constant:
+                    self.var = resolve_string_literal(constant)
+                elif var == None:
                     raise TemplateSyntaxError("Could not find variable at start of %s" % token)
                 elif var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or var[0] == '_':
                     raise TemplateSyntaxError("Variables and attributes may not begin with underscores: '%s'" % var)
+                else:
+                    self.var = Variable(var)
+                upto = match.end()
             else:
                 filter_name = match.group("filter_name")
                 args = []
-                constant_arg, i18n_arg, var_arg = match.group("constant_arg", "i18n_arg", "var_arg")
-                if i18n_arg:
-                    args.append((False, _(i18n_arg.replace(r'\"', '"'))))
-                elif constant_arg is not None:
-                    args.append((False, constant_arg.replace(r'\"', '"')))
+                constant_arg, var_arg = match.group("constant_arg", "var_arg")
+                if constant_arg:
+                    args.append((False, resolve_string_literal(constant_arg)))
                 elif var_arg:
                     args.append((True, Variable(var_arg)))
                 filter_func = parser.find_filter(filter_name)
@@ -511,24 +593,26 @@
         if upto != len(token):
             raise TemplateSyntaxError("Could not parse the remainder: '%s' from '%s'" % (token[upto:], token))
         self.filters = filters
-        self.var = Variable(var)
 
     def resolve(self, context, ignore_failures=False):
-        try:
-            obj = self.var.resolve(context)
-        except VariableDoesNotExist:
-            if ignore_failures:
-                obj = None
-            else:
-                if settings.TEMPLATE_STRING_IF_INVALID:
-                    global invalid_var_format_string
-                    if invalid_var_format_string is None:
-                        invalid_var_format_string = '%s' in settings.TEMPLATE_STRING_IF_INVALID
-                    if invalid_var_format_string:
-                        return settings.TEMPLATE_STRING_IF_INVALID % self.var
-                    return settings.TEMPLATE_STRING_IF_INVALID
+        if isinstance(self.var, Variable):
+            try:
+                obj = self.var.resolve(context)
+            except VariableDoesNotExist:
+                if ignore_failures:
+                    obj = None
                 else:
-                    obj = settings.TEMPLATE_STRING_IF_INVALID
+                    if settings.TEMPLATE_STRING_IF_INVALID:
+                        global invalid_var_format_string
+                        if invalid_var_format_string is None:
+                            invalid_var_format_string = '%s' in settings.TEMPLATE_STRING_IF_INVALID
+                        if invalid_var_format_string:
+                            return settings.TEMPLATE_STRING_IF_INVALID % self.var
+                        return settings.TEMPLATE_STRING_IF_INVALID
+                    else:
+                        obj = settings.TEMPLATE_STRING_IF_INVALID
+        else:
+            obj = self.var
         for func, args in self.filters:
             arg_vals = []
             for lookup, arg in args:
@@ -593,7 +677,7 @@
     return Variable(path).resolve(context)
 
 class Variable(object):
-    """
+    r"""
     A template variable, resolvable against a given context. The variable may be
     a hard-coded string (if it begins and ends with single or double quote
     marks)::
@@ -609,8 +693,28 @@
         >>> c.article.section = 'News'
         >>> Variable('article.section').resolve(c)
         u'News'
+        >>> Variable(u'"News"').resolve(c)
+        u'News'
+        >>> Variable(u"'News'").resolve(c)
+        u'News'
 
     (The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
+
+    Translated strings are also handled correctly::
+
+        >>> Variable('_(article.section)').resolve(c)
+        u'News'
+        >>> Variable('_("Good News")').resolve(c)
+        u'Good News'
+        >>> Variable("_('Better News')").resolve(c)
+        u'Better News'
+
+    Escaped quotes work correctly as well::
+
+        >>> Variable(ur'"Some \"Good\" News"').resolve(c)
+        u'Some "Good" News'
+        >>> Variable(ur"'Some \'Better\' News'").resolve(c)
+        u"Some 'Better' News"
     """
 
     def __init__(self, var):
@@ -645,9 +749,9 @@
                 var = var[2:-1]
             # If it's wrapped with quotes (single or double), then
             # we're also dealing with a literal.
-            if var[0] in "\"'" and var[0] == var[-1]:
-                self.literal = mark_safe(var[1:-1])
-            else:
+            try:
+                self.literal = mark_safe(unescape_string_literal(var))
+            except ValueError:
                 # Otherwise we'll set self.lookups so that resolve() knows we're
                 # dealing with a bonafide variable
                 self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR))
