App Engine Python SDK version 1.9.12
[gae.git] / python / lib / django-1.2 / django / templatetags / i18n.py
blobc0e360b7518ca68d8d805ee7bd0a6818a11a53e9
1 import re
3 from django.template import Node, Variable, VariableNode, _render_value_in_context
4 from django.template import TemplateSyntaxError, TokenParser, Library
5 from django.template import TOKEN_TEXT, TOKEN_VAR
6 from django.utils import translation
7 from django.utils.encoding import force_unicode
9 register = Library()
11 class GetAvailableLanguagesNode(Node):
12 def __init__(self, variable):
13 self.variable = variable
15 def render(self, context):
16 from django.conf import settings
17 context[self.variable] = [(k, translation.ugettext(v)) for k, v in settings.LANGUAGES]
18 return ''
20 class GetCurrentLanguageNode(Node):
21 def __init__(self, variable):
22 self.variable = variable
24 def render(self, context):
25 context[self.variable] = translation.get_language()
26 return ''
28 class GetCurrentLanguageBidiNode(Node):
29 def __init__(self, variable):
30 self.variable = variable
32 def render(self, context):
33 context[self.variable] = translation.get_language_bidi()
34 return ''
36 class TranslateNode(Node):
37 def __init__(self, filter_expression, noop):
38 self.noop = noop
39 self.filter_expression = filter_expression
40 if isinstance(self.filter_expression.var, basestring):
41 self.filter_expression.var = Variable(u"'%s'" % self.filter_expression.var)
43 def render(self, context):
44 self.filter_expression.var.translate = not self.noop
45 output = self.filter_expression.resolve(context)
46 return _render_value_in_context(output, context)
48 class BlockTranslateNode(Node):
49 def __init__(self, extra_context, singular, plural=None, countervar=None,
50 counter=None):
51 self.extra_context = extra_context
52 self.singular = singular
53 self.plural = plural
54 self.countervar = countervar
55 self.counter = counter
57 def render_token_list(self, tokens):
58 result = []
59 vars = []
60 for token in tokens:
61 if token.token_type == TOKEN_TEXT:
62 result.append(token.contents)
63 elif token.token_type == TOKEN_VAR:
64 result.append(u'%%(%s)s' % token.contents)
65 vars.append(token.contents)
66 return ''.join(result), vars
68 def render(self, context):
69 tmp_context = {}
70 for var, val in self.extra_context.items():
71 tmp_context[var] = val.render(context)
72 # Update() works like a push(), so corresponding context.pop() is at
73 # the end of function
74 context.update(tmp_context)
75 singular, vars = self.render_token_list(self.singular)
76 if self.plural and self.countervar and self.counter:
77 count = self.counter.resolve(context)
78 context[self.countervar] = count
79 plural, plural_vars = self.render_token_list(self.plural)
80 result = translation.ungettext(singular, plural, count)
81 vars.extend(plural_vars)
82 else:
83 result = translation.ugettext(singular)
84 # Escape all isolated '%' before substituting in the context.
85 result = re.sub(u'%(?!\()', u'%%', result)
86 data = dict([(v, _render_value_in_context(context[v], context)) for v in vars])
87 context.pop()
88 return result % data
90 def do_get_available_languages(parser, token):
91 """
92 This will store a list of available languages
93 in the context.
95 Usage::
97 {% get_available_languages as languages %}
98 {% for language in languages %}
99 ...
100 {% endfor %}
102 This will just pull the LANGUAGES setting from
103 your setting file (or the default settings) and
104 put it into the named variable.
106 args = token.contents.split()
107 if len(args) != 3 or args[1] != 'as':
108 raise TemplateSyntaxError("'get_available_languages' requires 'as variable' (got %r)" % args)
109 return GetAvailableLanguagesNode(args[2])
111 def do_get_current_language(parser, token):
113 This will store the current language in the context.
115 Usage::
117 {% get_current_language as language %}
119 This will fetch the currently active language and
120 put it's value into the ``language`` context
121 variable.
123 args = token.contents.split()
124 if len(args) != 3 or args[1] != 'as':
125 raise TemplateSyntaxError("'get_current_language' requires 'as variable' (got %r)" % args)
126 return GetCurrentLanguageNode(args[2])
128 def do_get_current_language_bidi(parser, token):
130 This will store the current language layout in the context.
132 Usage::
134 {% get_current_language_bidi as bidi %}
136 This will fetch the currently active language's layout and
137 put it's value into the ``bidi`` context variable.
138 True indicates right-to-left layout, otherwise left-to-right
140 args = token.contents.split()
141 if len(args) != 3 or args[1] != 'as':
142 raise TemplateSyntaxError("'get_current_language_bidi' requires 'as variable' (got %r)" % args)
143 return GetCurrentLanguageBidiNode(args[2])
145 def do_translate(parser, token):
147 This will mark a string for translation and will
148 translate the string for the current language.
150 Usage::
152 {% trans "this is a test" %}
154 This will mark the string for translation so it will
155 be pulled out by mark-messages.py into the .po files
156 and will run the string through the translation engine.
158 There is a second form::
160 {% trans "this is a test" noop %}
162 This will only mark for translation, but will return
163 the string unchanged. Use it when you need to store
164 values into forms that should be translated later on.
166 You can use variables instead of constant strings
167 to translate stuff you marked somewhere else::
169 {% trans variable %}
171 This will just try to translate the contents of
172 the variable ``variable``. Make sure that the string
173 in there is something that is in the .po file.
175 class TranslateParser(TokenParser):
176 def top(self):
177 value = self.value()
179 # Backwards Compatiblity fix:
180 # FilterExpression does not support single-quoted strings,
181 # so we make a cheap localized fix in order to maintain
182 # backwards compatibility with existing uses of ``trans``
183 # where single quote use is supported.
184 if value[0] == "'":
185 pos = None
186 m = re.match("^'([^']+)'(\|.*$)",value)
187 if m:
188 value = '"%s"%s' % (m.group(1).replace('"','\\"'),m.group(2))
189 elif value[-1] == "'":
190 value = '"%s"' % value[1:-1].replace('"','\\"')
192 if self.more():
193 if self.tag() == 'noop':
194 noop = True
195 else:
196 raise TemplateSyntaxError("only option for 'trans' is 'noop'")
197 else:
198 noop = False
199 return (value, noop)
200 value, noop = TranslateParser(token.contents).top()
201 return TranslateNode(parser.compile_filter(value), noop)
203 def do_block_translate(parser, token):
205 This will translate a block of text with parameters.
207 Usage::
209 {% blocktrans with foo|filter as bar and baz|filter as boo %}
210 This is {{ bar }} and {{ boo }}.
211 {% endblocktrans %}
213 Additionally, this supports pluralization::
215 {% blocktrans count var|length as count %}
216 There is {{ count }} object.
217 {% plural %}
218 There are {{ count }} objects.
219 {% endblocktrans %}
221 This is much like ngettext, only in template syntax.
223 class BlockTranslateParser(TokenParser):
224 def top(self):
225 countervar = None
226 counter = None
227 extra_context = {}
228 while self.more():
229 tag = self.tag()
230 if tag == 'with' or tag == 'and':
231 value = self.value()
232 if self.tag() != 'as':
233 raise TemplateSyntaxError("variable bindings in 'blocktrans' must be 'with value as variable'")
234 extra_context[self.tag()] = VariableNode(
235 parser.compile_filter(value))
236 elif tag == 'count':
237 counter = parser.compile_filter(self.value())
238 if self.tag() != 'as':
239 raise TemplateSyntaxError("counter specification in 'blocktrans' must be 'count value as variable'")
240 countervar = self.tag()
241 else:
242 raise TemplateSyntaxError("unknown subtag %s for 'blocktrans' found" % tag)
243 return (countervar, counter, extra_context)
245 countervar, counter, extra_context = BlockTranslateParser(token.contents).top()
247 singular = []
248 plural = []
249 while parser.tokens:
250 token = parser.next_token()
251 if token.token_type in (TOKEN_VAR, TOKEN_TEXT):
252 singular.append(token)
253 else:
254 break
255 if countervar and counter:
256 if token.contents.strip() != 'plural':
257 raise TemplateSyntaxError("'blocktrans' doesn't allow other block tags inside it")
258 while parser.tokens:
259 token = parser.next_token()
260 if token.token_type in (TOKEN_VAR, TOKEN_TEXT):
261 plural.append(token)
262 else:
263 break
264 if token.contents.strip() != 'endblocktrans':
265 raise TemplateSyntaxError("'blocktrans' doesn't allow other block tags (seen %r) inside it" % token.contents)
267 return BlockTranslateNode(extra_context, singular, plural, countervar,
268 counter)
270 register.tag('get_available_languages', do_get_available_languages)
271 register.tag('get_current_language', do_get_current_language)
272 register.tag('get_current_language_bidi', do_get_current_language_bidi)
273 register.tag('trans', do_translate)
274 register.tag('blocktrans', do_block_translate)