Added some tests to modeltests/many_to_one that demonstrate a post-queryset-refactor...
[fdr-django.git] / django / templatetags / i18n.py
blob190cb7012875e8268504626f50aa32628de8606b
1 import re
3 from django.template import Node, Variable, VariableNode
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, value, noop):
38 self.value = Variable(value)
39 self.noop = noop
41 def render(self, context):
42 value = self.value.resolve(context)
43 if self.noop:
44 return value
45 else:
46 return translation.ugettext(value)
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, vars = self.render_token_list(self.plural)
80 result = translation.ungettext(singular, plural, count)
81 else:
82 result = translation.ugettext(singular)
83 # Escape all isolated '%' before substituting in the context.
84 result = re.sub(u'%(?!\()', u'%%', result)
85 data = dict([(v, force_unicode(context[v])) for v in vars])
86 context.pop()
87 return result % data
89 def do_get_available_languages(parser, token):
90 """
91 This will store a list of available languages
92 in the context.
94 Usage::
96 {% get_available_languages as languages %}
97 {% for language in languages %}
98 ...
99 {% endfor %}
101 This will just pull the LANGUAGES setting from
102 your setting file (or the default settings) and
103 put it into the named variable.
105 args = token.contents.split()
106 if len(args) != 3 or args[1] != 'as':
107 raise TemplateSyntaxError, "'get_available_languages' requires 'as variable' (got %r)" % args
108 return GetAvailableLanguagesNode(args[2])
110 def do_get_current_language(parser, token):
112 This will store the current language in the context.
114 Usage::
116 {% get_current_language as language %}
118 This will fetch the currently active language and
119 put it's value into the ``language`` context
120 variable.
122 args = token.contents.split()
123 if len(args) != 3 or args[1] != 'as':
124 raise TemplateSyntaxError, "'get_current_language' requires 'as variable' (got %r)" % args
125 return GetCurrentLanguageNode(args[2])
127 def do_get_current_language_bidi(parser, token):
129 This will store the current language layout in the context.
131 Usage::
133 {% get_current_language_bidi as bidi %}
135 This will fetch the currently active language's layout and
136 put it's value into the ``bidi`` context variable.
137 True indicates right-to-left layout, otherwise left-to-right
139 args = token.contents.split()
140 if len(args) != 3 or args[1] != 'as':
141 raise TemplateSyntaxError, "'get_current_language_bidi' requires 'as variable' (got %r)" % args
142 return GetCurrentLanguageBidiNode(args[2])
144 def do_translate(parser, token):
146 This will mark a string for translation and will
147 translate the string for the current language.
149 Usage::
151 {% trans "this is a test" %}
153 This will mark the string for translation so it will
154 be pulled out by mark-messages.py into the .po files
155 and will run the string through the translation engine.
157 There is a second form::
159 {% trans "this is a test" noop %}
161 This will only mark for translation, but will return
162 the string unchanged. Use it when you need to store
163 values into forms that should be translated later on.
165 You can use variables instead of constant strings
166 to translate stuff you marked somewhere else::
168 {% trans variable %}
170 This will just try to translate the contents of
171 the variable ``variable``. Make sure that the string
172 in there is something that is in the .po file.
174 class TranslateParser(TokenParser):
175 def top(self):
176 value = self.value()
177 if self.more():
178 if self.tag() == 'noop':
179 noop = True
180 else:
181 raise TemplateSyntaxError, "only option for 'trans' is 'noop'"
182 else:
183 noop = False
184 return (value, noop)
185 value, noop = TranslateParser(token.contents).top()
186 return TranslateNode(value, noop)
188 def do_block_translate(parser, token):
190 This will translate a block of text with parameters.
192 Usage::
194 {% blocktrans with foo|filter as bar and baz|filter as boo %}
195 This is {{ bar }} and {{ boo }}.
196 {% endblocktrans %}
198 Additionally, this supports pluralization::
200 {% blocktrans count var|length as count %}
201 There is {{ count }} object.
202 {% plural %}
203 There are {{ count }} objects.
204 {% endblocktrans %}
206 This is much like ngettext, only in template syntax.
208 class BlockTranslateParser(TokenParser):
209 def top(self):
210 countervar = None
211 counter = None
212 extra_context = {}
213 while self.more():
214 tag = self.tag()
215 if tag == 'with' or tag == 'and':
216 value = self.value()
217 if self.tag() != 'as':
218 raise TemplateSyntaxError, "variable bindings in 'blocktrans' must be 'with value as variable'"
219 extra_context[self.tag()] = VariableNode(
220 parser.compile_filter(value))
221 elif tag == 'count':
222 counter = parser.compile_filter(self.value())
223 if self.tag() != 'as':
224 raise TemplateSyntaxError, "counter specification in 'blocktrans' must be 'count value as variable'"
225 countervar = self.tag()
226 else:
227 raise TemplateSyntaxError, "unknown subtag %s for 'blocktrans' found" % tag
228 return (countervar, counter, extra_context)
230 countervar, counter, extra_context = BlockTranslateParser(token.contents).top()
232 singular = []
233 plural = []
234 while parser.tokens:
235 token = parser.next_token()
236 if token.token_type in (TOKEN_VAR, TOKEN_TEXT):
237 singular.append(token)
238 else:
239 break
240 if countervar and counter:
241 if token.contents.strip() != 'plural':
242 raise TemplateSyntaxError, "'blocktrans' doesn't allow other block tags inside it"
243 while parser.tokens:
244 token = parser.next_token()
245 if token.token_type in (TOKEN_VAR, TOKEN_TEXT):
246 plural.append(token)
247 else:
248 break
249 if token.contents.strip() != 'endblocktrans':
250 raise TemplateSyntaxError, "'blocktrans' doesn't allow other block tags (seen %r) inside it" % token.contents
252 return BlockTranslateNode(extra_context, singular, plural, countervar,
253 counter)
255 register.tag('get_available_languages', do_get_available_languages)
256 register.tag('get_current_language', do_get_current_language)
257 register.tag('get_current_language_bidi', do_get_current_language_bidi)
258 register.tag('trans', do_translate)
259 register.tag('blocktrans', do_block_translate)