|
1 import re |
|
2 |
|
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 |
|
8 |
|
9 register = Library() |
|
10 |
|
11 class GetAvailableLanguagesNode(Node): |
|
12 def __init__(self, variable): |
|
13 self.variable = variable |
|
14 |
|
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 '' |
|
19 |
|
20 class GetCurrentLanguageNode(Node): |
|
21 def __init__(self, variable): |
|
22 self.variable = variable |
|
23 |
|
24 def render(self, context): |
|
25 context[self.variable] = translation.get_language() |
|
26 return '' |
|
27 |
|
28 class GetCurrentLanguageBidiNode(Node): |
|
29 def __init__(self, variable): |
|
30 self.variable = variable |
|
31 |
|
32 def render(self, context): |
|
33 context[self.variable] = translation.get_language_bidi() |
|
34 return '' |
|
35 |
|
36 class TranslateNode(Node): |
|
37 def __init__(self, value, noop): |
|
38 self.value = Variable(value) |
|
39 self.noop = noop |
|
40 |
|
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) |
|
47 |
|
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 |
|
56 |
|
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 |
|
67 |
|
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 |
|
88 |
|
89 def do_get_available_languages(parser, token): |
|
90 """ |
|
91 This will store a list of available languages |
|
92 in the context. |
|
93 |
|
94 Usage:: |
|
95 |
|
96 {% get_available_languages as languages %} |
|
97 {% for language in languages %} |
|
98 ... |
|
99 {% endfor %} |
|
100 |
|
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. |
|
104 """ |
|
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]) |
|
109 |
|
110 def do_get_current_language(parser, token): |
|
111 """ |
|
112 This will store the current language in the context. |
|
113 |
|
114 Usage:: |
|
115 |
|
116 {% get_current_language as language %} |
|
117 |
|
118 This will fetch the currently active language and |
|
119 put it's value into the ``language`` context |
|
120 variable. |
|
121 """ |
|
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]) |
|
126 |
|
127 def do_get_current_language_bidi(parser, token): |
|
128 """ |
|
129 This will store the current language layout in the context. |
|
130 |
|
131 Usage:: |
|
132 |
|
133 {% get_current_language_bidi as bidi %} |
|
134 |
|
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 |
|
138 """ |
|
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]) |
|
143 |
|
144 def do_translate(parser, token): |
|
145 """ |
|
146 This will mark a string for translation and will |
|
147 translate the string for the current language. |
|
148 |
|
149 Usage:: |
|
150 |
|
151 {% trans "this is a test" %} |
|
152 |
|
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. |
|
156 |
|
157 There is a second form:: |
|
158 |
|
159 {% trans "this is a test" noop %} |
|
160 |
|
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. |
|
164 |
|
165 You can use variables instead of constant strings |
|
166 to translate stuff you marked somewhere else:: |
|
167 |
|
168 {% trans variable %} |
|
169 |
|
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. |
|
173 """ |
|
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) |
|
187 |
|
188 def do_block_translate(parser, token): |
|
189 """ |
|
190 This will translate a block of text with parameters. |
|
191 |
|
192 Usage:: |
|
193 |
|
194 {% blocktrans with foo|filter as bar and baz|filter as boo %} |
|
195 This is {{ bar }} and {{ boo }}. |
|
196 {% endblocktrans %} |
|
197 |
|
198 Additionally, this supports pluralization:: |
|
199 |
|
200 {% blocktrans count var|length as count %} |
|
201 There is {{ count }} object. |
|
202 {% plural %} |
|
203 There are {{ count }} objects. |
|
204 {% endblocktrans %} |
|
205 |
|
206 This is much like ngettext, only in template syntax. |
|
207 """ |
|
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) |
|
229 |
|
230 countervar, counter, extra_context = BlockTranslateParser(token.contents).top() |
|
231 |
|
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 |
|
251 |
|
252 return BlockTranslateNode(extra_context, singular, plural, countervar, |
|
253 counter) |
|
254 |
|
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) |