Fixed #5470 -- Fixed the 'Z' time format marker in templates to handle timezones...
[django.git] / django / templatetags / i18n.py
blob1e85c6b5d15426c9e5c06f94183faa463c6fab95
1 from django.template import Node, resolve_variable
2 from django.template import TemplateSyntaxError, TokenParser, Library
3 from django.template import TOKEN_TEXT, TOKEN_VAR
4 from django.utils import translation
6 register = Library()
8 class GetAvailableLanguagesNode(Node):
9 def __init__(self, variable):
10 self.variable = variable
12 def render(self, context):
13 from django.conf import settings
14 context[self.variable] = [(k, translation.ugettext(v)) for k, v in settings.LANGUAGES]
15 return ''
17 class GetCurrentLanguageNode(Node):
18 def __init__(self, variable):
19 self.variable = variable
21 def render(self, context):
22 context[self.variable] = translation.get_language()
23 return ''
25 class GetCurrentLanguageBidiNode(Node):
26 def __init__(self, variable):
27 self.variable = variable
29 def render(self, context):
30 context[self.variable] = translation.get_language_bidi()
31 return ''
33 class TranslateNode(Node):
34 def __init__(self, value, noop):
35 self.value = value
36 self.noop = noop
38 def render(self, context):
39 value = resolve_variable(self.value, context)
40 if self.noop:
41 return value
42 else:
43 return translation.ugettext(value)
45 class BlockTranslateNode(Node):
46 def __init__(self, extra_context, singular, plural=None, countervar=None, counter=None):
47 self.extra_context = extra_context
48 self.singular = singular
49 self.plural = plural
50 self.countervar = countervar
51 self.counter = counter
53 def render_token_list(self, tokens):
54 result = []
55 for token in tokens:
56 if token.token_type == TOKEN_TEXT:
57 result.append(token.contents)
58 elif token.token_type == TOKEN_VAR:
59 result.append('%%(%s)s' % token.contents)
60 return ''.join(result)
62 def render(self, context):
63 context.push()
64 for var,val in self.extra_context.items():
65 context[var] = val.resolve(context)
66 singular = self.render_token_list(self.singular)
67 if self.plural and self.countervar and self.counter:
68 count = self.counter.resolve(context)
69 context[self.countervar] = count
70 plural = self.render_token_list(self.plural)
71 result = translation.ungettext(singular, plural, count) % context
72 else:
73 result = translation.ugettext(singular) % context
74 context.pop()
75 return result
77 def do_get_available_languages(parser, token):
78 """
79 This will store a list of available languages
80 in the context.
82 Usage::
84 {% get_available_languages as languages %}
85 {% for language in languages %}
86 ...
87 {% endfor %}
89 This will just pull the LANGUAGES setting from
90 your setting file (or the default settings) and
91 put it into the named variable.
92 """
93 args = token.contents.split()
94 if len(args) != 3 or args[1] != 'as':
95 raise TemplateSyntaxError, "'get_available_languages' requires 'as variable' (got %r)" % args
96 return GetAvailableLanguagesNode(args[2])
98 def do_get_current_language(parser, token):
99 """
100 This will store the current language in the context.
102 Usage::
104 {% get_current_language as language %}
106 This will fetch the currently active language and
107 put it's value into the ``language`` context
108 variable.
110 args = token.contents.split()
111 if len(args) != 3 or args[1] != 'as':
112 raise TemplateSyntaxError, "'get_current_language' requires 'as variable' (got %r)" % args
113 return GetCurrentLanguageNode(args[2])
115 def do_get_current_language_bidi(parser, token):
117 This will store the current language layout in the context.
119 Usage::
121 {% get_current_language_bidi as bidi %}
123 This will fetch the currently active language's layout and
124 put it's value into the ``bidi`` context variable.
125 True indicates right-to-left layout, otherwise left-to-right
127 args = token.contents.split()
128 if len(args) != 3 or args[1] != 'as':
129 raise TemplateSyntaxError, "'get_current_language_bidi' requires 'as variable' (got %r)" % args
130 return GetCurrentLanguageBidiNode(args[2])
132 def do_translate(parser, token):
134 This will mark a string for translation and will
135 translate the string for the current language.
137 Usage::
139 {% trans "this is a test" %}
141 This will mark the string for translation so it will
142 be pulled out by mark-messages.py into the .po files
143 and will run the string through the translation engine.
145 There is a second form::
147 {% trans "this is a test" noop %}
149 This will only mark for translation, but will return
150 the string unchanged. Use it when you need to store
151 values into forms that should be translated later on.
153 You can use variables instead of constant strings
154 to translate stuff you marked somewhere else::
156 {% trans variable %}
158 This will just try to translate the contents of
159 the variable ``variable``. Make sure that the string
160 in there is something that is in the .po file.
162 class TranslateParser(TokenParser):
163 def top(self):
164 value = self.value()
165 if self.more():
166 if self.tag() == 'noop':
167 noop = True
168 else:
169 raise TemplateSyntaxError, "only option for 'trans' is 'noop'"
170 else:
171 noop = False
172 return (value, noop)
173 value, noop = TranslateParser(token.contents).top()
174 return TranslateNode(value, noop)
176 def do_block_translate(parser, token):
178 This will translate a block of text with parameters.
180 Usage::
182 {% blocktrans with foo|filter as bar and baz|filter as boo %}
183 This is {{ bar }} and {{ boo }}.
184 {% endblocktrans %}
186 Additionally, this supports pluralization::
188 {% blocktrans count var|length as count %}
189 There is {{ count }} object.
190 {% plural %}
191 There are {{ count }} objects.
192 {% endblocktrans %}
194 This is much like ngettext, only in template syntax.
196 class BlockTranslateParser(TokenParser):
198 def top(self):
199 countervar = None
200 counter = None
201 extra_context = {}
202 while self.more():
203 tag = self.tag()
204 if tag == 'with' or tag == 'and':
205 value = self.value()
206 if self.tag() != 'as':
207 raise TemplateSyntaxError, "variable bindings in 'blocktrans' must be 'with value as variable'"
208 extra_context[self.tag()] = parser.compile_filter(value)
209 elif tag == 'count':
210 counter = parser.compile_filter(self.value())
211 if self.tag() != 'as':
212 raise TemplateSyntaxError, "counter specification in 'blocktrans' must be 'count value as variable'"
213 countervar = self.tag()
214 else:
215 raise TemplateSyntaxError, "unknown subtag %s for 'blocktrans' found" % tag
216 return (countervar, counter, extra_context)
218 countervar, counter, extra_context = BlockTranslateParser(token.contents).top()
220 singular = []
221 plural = []
222 while parser.tokens:
223 token = parser.next_token()
224 if token.token_type in (TOKEN_VAR, TOKEN_TEXT):
225 singular.append(token)
226 else:
227 break
228 if countervar and counter:
229 if token.contents.strip() != 'plural':
230 raise TemplateSyntaxError, "'blocktrans' doesn't allow other block tags inside it"
231 while parser.tokens:
232 token = parser.next_token()
233 if token.token_type in (TOKEN_VAR, TOKEN_TEXT):
234 plural.append(token)
235 else:
236 break
237 if token.contents.strip() != 'endblocktrans':
238 raise TemplateSyntaxError, "'blocktrans' doesn't allow other block tags (seen %r) inside it" % token.contents
240 return BlockTranslateNode(extra_context, singular, plural, countervar, counter)
242 register.tag('get_available_languages', do_get_available_languages)
243 register.tag('get_current_language', do_get_current_language)
244 register.tag('get_current_language_bidi', do_get_current_language_bidi)
245 register.tag('trans', do_translate)
246 register.tag('blocktrans', do_block_translate)