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
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
]
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()
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()
36 class TranslateNode(Node
):
37 def __init__(self
, filter_expression
, 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,
51 self
.extra_context
= extra_context
52 self
.singular
= singular
54 self
.countervar
= countervar
55 self
.counter
= counter
57 def render_token_list(self
, 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
):
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
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
)
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])
90 def do_get_available_languages(parser
, token
):
92 This will store a list of available languages
97 {% get_available_languages as languages %}
98 {% for language in languages %}
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.
117 {% get_current_language as language %}
119 This will fetch the currently active language and
120 put it's value into the ``language`` context
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.
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.
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::
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
):
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.
186 m
= re
.match("^'([^']+)'(\|.*$)",value
)
188 value
= '"%s"%s' % (m
.group(1).replace('"','\\"'),m
.group(2))
189 elif value
[-1] == "'":
190 value
= '"%s"' % value
[1:-1].replace('"','\\"')
193 if self
.tag() == 'noop':
196 raise TemplateSyntaxError("only option for 'trans' is '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.
209 {% blocktrans with foo|filter as bar and baz|filter as boo %}
210 This is {{ bar }} and {{ boo }}.
213 Additionally, this supports pluralization::
215 {% blocktrans count var|length as count %}
216 There is {{ count }} object.
218 There are {{ count }} objects.
221 This is much like ngettext, only in template syntax.
223 class BlockTranslateParser(TokenParser
):
230 if tag
== 'with' or tag
== 'and':
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
))
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()
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()
250 token
= parser
.next_token()
251 if token
.token_type
in (TOKEN_VAR
, TOKEN_TEXT
):
252 singular
.append(token
)
255 if countervar
and counter
:
256 if token
.contents
.strip() != 'plural':
257 raise TemplateSyntaxError("'blocktrans' doesn't allow other block tags inside it")
259 token
= parser
.next_token()
260 if token
.token_type
in (TOKEN_VAR
, TOKEN_TEXT
):
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
,
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
)