summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/core/template.py53
-rw-r--r--docs/translation.txt14
-rw-r--r--tests/othertests/templates.py3
3 files changed, 66 insertions, 4 deletions
diff --git a/django/core/template.py b/django/core/template.py
index 35b557bbfb..df0e1083c4 100644
--- a/django/core/template.py
+++ b/django/core/template.py
@@ -254,11 +254,15 @@ class FilterParser:
self.filters = []
self.current_filter_name = None
self.current_filter_arg = None
- # First read the variable part
- self.var = self.read_alphanumeric_token()
+ # First read the variable part - decide on wether we need
+ # to parse a string or a variable by peeking into the stream
+ if self.peek_char() in ('_', '"', "'"):
+ self.var = self.read_constant_string_token()
+ else:
+ self.var = self.read_alphanumeric_token()
if not self.var:
raise TemplateSyntaxError, "Could not read variable name: '%s'" % self.s
- if self.var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or self.var[0] == '_':
+ if self.var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or (self.var[0] == '_' and not self.var.startswith('_(')):
raise TemplateSyntaxError, "Variables and attributes may not begin with underscores: '%s'" % self.var
# Have we reached the end?
if self.current is None:
@@ -268,6 +272,12 @@ class FilterParser:
# We have a filter separator; start reading the filters
self.read_filters()
+ def peek_char(self):
+ try:
+ return self.s[self.i+1]
+ except IndexError:
+ return None
+
def next_char(self):
self.i = self.i + 1
try:
@@ -275,6 +285,39 @@ class FilterParser:
except IndexError:
self.current = None
+ def read_constant_string_token(self):
+ """Read a constant string that must be delimited by either "
+ or ' characters. The string is returned with it's delimiters
+ and a possible i18n tag."""
+ val = ''
+ i18n = False
+ qchar = None
+ self.next_char()
+ if self.current == '_':
+ self.next_char()
+ if self.current != '(':
+ raise TemplateSyntaxError, "Bad character (expecting '(') '%s'" % self.current
+ i18n = True
+ val = '_('
+ self.next_char()
+ if not self.current in ('"', "'"):
+ raise TemplateSyntaxError, "Bad character (expecting '\"' or ''') '%s'" % self.current
+ qchar = self.current
+ val += qchar
+ while 1:
+ self.next_char()
+ if self.current == qchar:
+ break
+ val += self.current
+ val += self.current
+ if i18n:
+ self.next_char()
+ if self.current != ')':
+ raise TemplateSyntaxError, "Bad character (expecting ')') '%s'" % self.current
+ val += self.current
+ self.next_char()
+ return val
+
def read_alphanumeric_token(self):
"""Read a variable name or filter name, which are continuous strings of
alphanumeric characters + the underscore"""
@@ -372,8 +415,10 @@ def resolve_variable(path, context):
(The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
"""
- if path[0] in ('"', "'") and path[0] == path[-1]:
+ if path[0] in ('"', "'") and path[0] == path[-1]:
current = path[1:-1]
+ elif path.startswith('_(') and path.endswith(')') and path[2] in ("'", '"') and path[-2] == path[2]:
+ current = _(path[3:-2])
else:
current = context
bits = path.split(VARIABLE_ATTRIBUTE_SEPARATOR)
diff --git a/docs/translation.txt b/docs/translation.txt
index 8c90e51b9b..58f634da08 100644
--- a/docs/translation.txt
+++ b/docs/translation.txt
@@ -102,6 +102,20 @@ variables, not more complex expressions.
To translate a variable value, you can just do {% i18n _(variable) %}. This
can even include filters like {% i18n _(variable|lower} %}.
+There is additional support for i18n string constants for other situations
+as well. All template tags that do variable resolving (with or without filters)
+will accept string constants, too. Those string constants can now be i18n
+strings like this::
+
+ <html>
+ <title>{{ _('This is the title') }}</title>
+ <body>
+ <p>{{ _('Hello World!') }}</p>
+ </body>
+ </html>
+
+This is much shorter, but won't allow you to use gettext_noop or ngettext.
+
How the Language is Discovered
==============================
diff --git a/tests/othertests/templates.py b/tests/othertests/templates.py
index 0204303bbd..509aa29a3e 100644
--- a/tests/othertests/templates.py
+++ b/tests/othertests/templates.py
@@ -241,6 +241,9 @@ TEMPLATE_TESTS = {
# simple non-translation (only marking) of a string to german
'i18n10': ('{% i18n gettext_noop("Page not found") %}', {'LANGUAGE_CODE': 'de'}, "Page not found"),
+
+ # translation of string without i18n tag
+ 'i18n11': ('{{ _("blah") }}', {}, "blah"),
}
# This replaces the standard template_loader.