##// END OF EJS Templates
Don't show list of all sections when none is entered
neko259 -
r1259:859d1228 default
parent child Browse files
Show More
@@ -1,387 +1,386 b''
1 1 import re
2 2 import time
3 3 import pytz
4 4
5 5 from django import forms
6 6 from django.core.files.uploadedfile import SimpleUploadedFile
7 7 from django.core.exceptions import ObjectDoesNotExist
8 8 from django.forms.util import ErrorList
9 9 from django.utils.translation import ugettext_lazy as _
10 10 import requests
11 11
12 12 from boards.mdx_neboard import formatters
13 13 from boards.models.post import TITLE_MAX_LENGTH
14 14 from boards.models import Tag, Post
15 15 from neboard import settings
16 16 import boards.settings as board_settings
17 17
18 18 HEADER_CONTENT_LENGTH = 'content-length'
19 19 HEADER_CONTENT_TYPE = 'content-type'
20 20
21 21 CONTENT_TYPE_IMAGE = (
22 22 'image/jpeg',
23 23 'image/jpg',
24 24 'image/png',
25 25 'image/gif',
26 26 'image/bmp',
27 27 )
28 28
29 29 REGEX_TAGS = re.compile(r'^[\w\s\d]+$', re.UNICODE)
30 30
31 31 VETERAN_POSTING_DELAY = 5
32 32
33 33 ATTRIBUTE_PLACEHOLDER = 'placeholder'
34 34 ATTRIBUTE_ROWS = 'rows'
35 35
36 36 LAST_POST_TIME = 'last_post_time'
37 37 LAST_LOGIN_TIME = 'last_login_time'
38 38 TEXT_PLACEHOLDER = _('Type message here. Use formatting panel for more advanced usage.')
39 39 TAGS_PLACEHOLDER = _('music images i_dont_like_tags')
40 40
41 41 LABEL_TITLE = _('Title')
42 42 LABEL_TEXT = _('Text')
43 43 LABEL_TAG = _('Tag')
44 44 LABEL_SEARCH = _('Search')
45 45
46 46 ERROR_SPEED = _('Please wait %s seconds before sending message')
47 47
48 48 TAG_MAX_LENGTH = 20
49 49
50 50 IMAGE_DOWNLOAD_CHUNK_BYTES = 100000
51 51
52 52 HTTP_RESULT_OK = 200
53 53
54 54 TEXTAREA_ROWS = 4
55 55
56 56
57 57 def get_timezones():
58 58 timezones = []
59 59 for tz in pytz.common_timezones:
60 60 timezones.append((tz, tz),)
61 61 return timezones
62 62
63 63
64 64 class FormatPanel(forms.Textarea):
65 65 """
66 66 Panel for text formatting. Consists of buttons to add different tags to the
67 67 form text area.
68 68 """
69 69
70 70 def render(self, name, value, attrs=None):
71 71 output = '<div id="mark-panel">'
72 72 for formatter in formatters:
73 73 output += '<span class="mark_btn"' + \
74 74 ' onClick="addMarkToMsg(\'' + formatter.format_left + \
75 75 '\', \'' + formatter.format_right + '\')">' + \
76 76 formatter.preview_left + formatter.name + \
77 77 formatter.preview_right + '</span>'
78 78
79 79 output += '</div>'
80 80 output += super(FormatPanel, self).render(name, value, attrs=None)
81 81
82 82 return output
83 83
84 84
85 85 class PlainErrorList(ErrorList):
86 86 def __unicode__(self):
87 87 return self.as_text()
88 88
89 89 def as_text(self):
90 90 return ''.join(['(!) %s ' % e for e in self])
91 91
92 92
93 93 class NeboardForm(forms.Form):
94 94 """
95 95 Form with neboard-specific formatting.
96 96 """
97 97
98 98 def as_div(self):
99 99 """
100 100 Returns this form rendered as HTML <as_div>s.
101 101 """
102 102
103 103 return self._html_output(
104 104 # TODO Do not show hidden rows in the list here
105 105 normal_row='<div class="form-row">'
106 106 '<div class="form-label">'
107 107 '%(label)s'
108 108 '</div>'
109 109 '<div class="form-input">'
110 110 '%(field)s'
111 111 '</div>'
112 112 '</div>'
113 113 '<div class="form-row">'
114 114 '%(help_text)s'
115 115 '</div>',
116 116 error_row='<div class="form-row">'
117 117 '<div class="form-label"></div>'
118 118 '<div class="form-errors">%s</div>'
119 119 '</div>',
120 120 row_ender='</div>',
121 121 help_text_html='%s',
122 122 errors_on_separate_row=True)
123 123
124 124 def as_json_errors(self):
125 125 errors = []
126 126
127 127 for name, field in list(self.fields.items()):
128 128 if self[name].errors:
129 129 errors.append({
130 130 'field': name,
131 131 'errors': self[name].errors.as_text(),
132 132 })
133 133
134 134 return errors
135 135
136 136
137 137 class PostForm(NeboardForm):
138 138
139 139 title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False,
140 140 label=LABEL_TITLE)
141 141 text = forms.CharField(
142 142 widget=FormatPanel(attrs={
143 143 ATTRIBUTE_PLACEHOLDER: TEXT_PLACEHOLDER,
144 144 ATTRIBUTE_ROWS: TEXTAREA_ROWS,
145 145 }),
146 146 required=False, label=LABEL_TEXT)
147 147 image = forms.ImageField(required=False, label=_('Image'),
148 148 widget=forms.ClearableFileInput(
149 149 attrs={'accept': 'image/*'}))
150 150 image_url = forms.CharField(required=False, label=_('Image URL'),
151 151 widget=forms.TextInput(
152 152 attrs={ATTRIBUTE_PLACEHOLDER:
153 153 'http://example.com/image.png'}))
154 154
155 155 # This field is for spam prevention only
156 156 email = forms.CharField(max_length=100, required=False, label=_('e-mail'),
157 157 widget=forms.TextInput(attrs={
158 158 'class': 'form-email'}))
159 159 threads = forms.CharField(required=False, label=_('Additional threads'),
160 160 widget=forms.TextInput(attrs={ATTRIBUTE_PLACEHOLDER:
161 161 '123 456 789'}))
162 162
163 163 session = None
164 164 need_to_ban = False
165 165
166 166 def clean_title(self):
167 167 title = self.cleaned_data['title']
168 168 if title:
169 169 if len(title) > TITLE_MAX_LENGTH:
170 170 raise forms.ValidationError(_('Title must have less than %s '
171 171 'characters') %
172 172 str(TITLE_MAX_LENGTH))
173 173 return title
174 174
175 175 def clean_text(self):
176 176 text = self.cleaned_data['text'].strip()
177 177 if text:
178 178 max_length = board_settings.get_int('Forms', 'MaxTextLength')
179 179 if len(text) > max_length:
180 180 raise forms.ValidationError(_('Text must have less than %s '
181 181 'characters') % str(max_length))
182 182 return text
183 183
184 184 def clean_image(self):
185 185 image = self.cleaned_data['image']
186 186
187 187 if image:
188 188 self.validate_image_size(image.size)
189 189
190 190 return image
191 191
192 192 def clean_image_url(self):
193 193 url = self.cleaned_data['image_url']
194 194
195 195 image = None
196 196 if url:
197 197 image = self._get_image_from_url(url)
198 198
199 199 if not image:
200 200 raise forms.ValidationError(_('Invalid URL'))
201 201 else:
202 202 self.validate_image_size(image.size)
203 203
204 204 return image
205 205
206 206 def clean_threads(self):
207 207 threads_str = self.cleaned_data['threads']
208 208
209 209 if len(threads_str) > 0:
210 210 threads_id_list = threads_str.split(' ')
211 211
212 212 threads = list()
213 213
214 214 for thread_id in threads_id_list:
215 215 try:
216 216 thread = Post.objects.get(id=int(thread_id))
217 217 if not thread.is_opening() or thread.get_thread().archived:
218 218 raise ObjectDoesNotExist()
219 219 threads.append(thread)
220 220 except (ObjectDoesNotExist, ValueError):
221 221 raise forms.ValidationError(_('Invalid additional thread list'))
222 222
223 223 return threads
224 224
225 225 def clean(self):
226 226 cleaned_data = super(PostForm, self).clean()
227 227
228 228 if cleaned_data['email']:
229 229 self.need_to_ban = True
230 230 raise forms.ValidationError('A human cannot enter a hidden field')
231 231
232 232 if not self.errors:
233 233 self._clean_text_image()
234 234
235 235 if not self.errors and self.session:
236 236 self._validate_posting_speed()
237 237
238 238 return cleaned_data
239 239
240 240 def get_image(self):
241 241 """
242 242 Gets image from file or URL.
243 243 """
244 244
245 245 image = self.cleaned_data['image']
246 246 return image if image else self.cleaned_data['image_url']
247 247
248 248 def _clean_text_image(self):
249 249 text = self.cleaned_data.get('text')
250 250 image = self.get_image()
251 251
252 252 if (not text) and (not image):
253 253 error_message = _('Either text or image must be entered.')
254 254 self._errors['text'] = self.error_class([error_message])
255 255
256 256 def _validate_posting_speed(self):
257 257 can_post = True
258 258
259 259 posting_delay = settings.POSTING_DELAY
260 260
261 261 if board_settings.get_bool('Forms', 'LimitPostingSpeed'):
262 262 now = time.time()
263 263
264 264 current_delay = 0
265 265 need_delay = False
266 266
267 267 if not LAST_POST_TIME in self.session:
268 268 self.session[LAST_POST_TIME] = now
269 269
270 270 need_delay = True
271 271 else:
272 272 last_post_time = self.session.get(LAST_POST_TIME)
273 273 current_delay = int(now - last_post_time)
274 274
275 275 need_delay = current_delay < posting_delay
276 276
277 277 if need_delay:
278 278 error_message = ERROR_SPEED % str(posting_delay
279 279 - current_delay)
280 280 self._errors['text'] = self.error_class([error_message])
281 281
282 282 can_post = False
283 283
284 284 if can_post:
285 285 self.session[LAST_POST_TIME] = now
286 286
287 287 def validate_image_size(self, size: int):
288 288 max_size = board_settings.get_int('Forms', 'MaxImageSize')
289 289 if size > max_size:
290 290 raise forms.ValidationError(
291 291 _('Image must be less than %s bytes')
292 292 % str(max_size))
293 293
294 294 def _get_image_from_url(self, url: str) -> SimpleUploadedFile:
295 295 """
296 296 Gets an image file from URL.
297 297 """
298 298
299 299 img_temp = None
300 300
301 301 try:
302 302 # Verify content headers
303 303 response_head = requests.head(url, verify=False)
304 304 content_type = response_head.headers[HEADER_CONTENT_TYPE].split(';')[0]
305 305 if content_type in CONTENT_TYPE_IMAGE:
306 306 length_header = response_head.headers.get(HEADER_CONTENT_LENGTH)
307 307 if length_header:
308 308 length = int(length_header)
309 309 self.validate_image_size(length)
310 310 # Get the actual content into memory
311 311 response = requests.get(url, verify=False, stream=True)
312 312
313 313 # Download image, stop if the size exceeds limit
314 314 size = 0
315 315 content = b''
316 316 for chunk in response.iter_content(IMAGE_DOWNLOAD_CHUNK_BYTES):
317 317 size += len(chunk)
318 318 self.validate_image_size(size)
319 319 content += chunk
320 320
321 321 if response.status_code == HTTP_RESULT_OK and content:
322 322 # Set a dummy file name that will be replaced
323 323 # anyway, just keep the valid extension
324 324 filename = 'image.' + content_type.split('/')[1]
325 325 img_temp = SimpleUploadedFile(filename, content,
326 326 content_type)
327 327 except Exception:
328 328 # Just return no image
329 329 pass
330 330
331 331 return img_temp
332 332
333 333
334 334 class ThreadForm(PostForm):
335 335
336 336 tags = forms.CharField(
337 337 widget=forms.TextInput(attrs={ATTRIBUTE_PLACEHOLDER: TAGS_PLACEHOLDER}),
338 338 max_length=100, label=_('Tags'), required=True)
339 339
340 340 def clean_tags(self):
341 341 tags = self.cleaned_data['tags'].strip()
342 342
343 343 if not tags or not REGEX_TAGS.match(tags):
344 344 raise forms.ValidationError(
345 345 _('Inappropriate characters in tags.'))
346 346
347 347 required_tag_exists = False
348 348 for tag in tags.split():
349 349 try:
350 350 Tag.objects.get(name=tag.strip().lower(), required=True)
351 351 required_tag_exists = True
352 352 break
353 353 except ObjectDoesNotExist:
354 354 pass
355 355
356 356 if not required_tag_exists:
357 357 all_tags = Tag.objects.filter(required=True)
358 358 raise forms.ValidationError(
359 _('Need at least one of the tags: ')
360 + ', '.join([tag.name for tag in all_tags]))
359 _('Need at least one section.'))
361 360
362 361 return tags
363 362
364 363 def clean(self):
365 364 cleaned_data = super(ThreadForm, self).clean()
366 365
367 366 return cleaned_data
368 367
369 368
370 369 class SettingsForm(NeboardForm):
371 370
372 371 theme = forms.ChoiceField(choices=settings.THEMES, label=_('Theme'))
373 372 image_viewer = forms.ChoiceField(choices=settings.IMAGE_VIEWERS, label=_('Image view mode'))
374 373 username = forms.CharField(label=_('User name'), required=False)
375 374 timezone = forms.ChoiceField(choices=get_timezones(), label=_('Time zone'))
376 375
377 376 def clean_username(self):
378 377 username = self.cleaned_data['username']
379 378
380 379 if username and not REGEX_TAGS.match(username):
381 380 raise forms.ValidationError(_('Inappropriate characters.'))
382 381
383 382 return username
384 383
385 384
386 385 class SearchForm(NeboardForm):
387 386 query = forms.CharField(max_length=500, label=LABEL_SEARCH, required=False)
1 NO CONTENT: modified file, binary diff hidden
@@ -1,440 +1,438 b''
1 1 # SOME DESCRIPTIVE TITLE.
2 2 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 3 # This file is distributed under the same license as the PACKAGE package.
4 4 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5 5 #
6 6 msgid ""
7 7 msgstr ""
8 8 "Project-Id-Version: PACKAGE VERSION\n"
9 9 "Report-Msgid-Bugs-To: \n"
10 "POT-Creation-Date: 2015-08-03 18:26+0300\n"
10 "POT-Creation-Date: 2015-08-09 22:38+0300\n"
11 11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12 12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13 13 "Language-Team: LANGUAGE <LL@li.org>\n"
14 14 "Language: ru\n"
15 15 "MIME-Version: 1.0\n"
16 16 "Content-Type: text/plain; charset=UTF-8\n"
17 17 "Content-Transfer-Encoding: 8bit\n"
18 18 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
19 19 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
20 20
21 21 #: admin.py:22
22 22 msgid "{} posters were banned"
23 23 msgstr ""
24 24
25 25 #: authors.py:9
26 26 msgid "author"
27 27 msgstr "автор"
28 28
29 29 #: authors.py:10
30 30 msgid "developer"
31 31 msgstr "разработчик"
32 32
33 33 #: authors.py:11
34 34 msgid "javascript developer"
35 35 msgstr "разработчик javascript"
36 36
37 37 #: authors.py:12
38 38 msgid "designer"
39 39 msgstr "дизайнер"
40 40
41 41 #: forms.py:38
42 42 msgid "Type message here. Use formatting panel for more advanced usage."
43 43 msgstr ""
44 44 "Вводите сообщение сюда. Используйте панель для более сложного форматирования."
45 45
46 46 #: forms.py:39
47 47 msgid "music images i_dont_like_tags"
48 48 msgstr "музыка картинки теги_не_нужны"
49 49
50 50 #: forms.py:41
51 51 msgid "Title"
52 52 msgstr "Заголовок"
53 53
54 54 #: forms.py:42
55 55 msgid "Text"
56 56 msgstr "Текст"
57 57
58 58 #: forms.py:43
59 59 msgid "Tag"
60 60 msgstr "Метка"
61 61
62 62 #: forms.py:44 templates/boards/base.html:40 templates/search/search.html:7
63 63 msgid "Search"
64 64 msgstr "Поиск"
65 65
66 66 #: forms.py:46
67 67 #, python-format
68 68 msgid "Please wait %s seconds before sending message"
69 69 msgstr "Пожалуйста подождите %s секунд перед отправкой сообщения"
70 70
71 71 #: forms.py:147
72 72 msgid "Image"
73 73 msgstr "Изображение"
74 74
75 75 #: forms.py:150
76 76 msgid "Image URL"
77 77 msgstr "URL изображения"
78 78
79 79 #: forms.py:156
80 80 msgid "e-mail"
81 81 msgstr ""
82 82
83 83 #: forms.py:159
84 84 msgid "Additional threads"
85 85 msgstr "Дополнительные темы"
86 86
87 87 #: forms.py:170
88 88 #, python-format
89 89 msgid "Title must have less than %s characters"
90 90 msgstr "Заголовок должен иметь меньше %s символов"
91 91
92 92 #: forms.py:180
93 93 #, python-format
94 94 msgid "Text must have less than %s characters"
95 95 msgstr "Текст должен быть короче %s символов"
96 96
97 97 #: forms.py:200
98 98 msgid "Invalid URL"
99 99 msgstr "Неверный URL"
100 100
101 101 #: forms.py:221
102 102 msgid "Invalid additional thread list"
103 103 msgstr "Неверный список дополнительных тем"
104 104
105 105 #: forms.py:253
106 106 msgid "Either text or image must be entered."
107 107 msgstr "Текст или картинка должны быть введены."
108 108
109 109 #: forms.py:291
110 110 #, python-format
111 111 msgid "Image must be less than %s bytes"
112 112 msgstr "Изображение должно быть менее %s байт"
113 113
114 #: forms.py:338 templates/boards/all_threads.html:136
114 #: forms.py:338 templates/boards/all_threads.html:141
115 115 #: templates/boards/rss/post.html:10 templates/boards/tags.html:6
116 116 msgid "Tags"
117 117 msgstr "Метки"
118 118
119 119 #: forms.py:345
120 120 msgid "Inappropriate characters in tags."
121 121 msgstr "Недопустимые символы в метках."
122 122
123 123 #: forms.py:359
124 msgid "Need at least one of the tags: "
125 msgstr "Нужна хотя бы одна из меток: "
124 #| msgid "Need at least one of the tags: "
125 msgid "Need at least one section."
126 msgstr "Нужен хотя бы один раздел."
126 127
127 #: forms.py:372
128 #: forms.py:371
128 129 msgid "Theme"
129 130 msgstr "Тема"
130 131
131 #: forms.py:373
132 #: forms.py:372
132 133 msgid "Image view mode"
133 134 msgstr "Режим просмотра изображений"
134 135
135 #: forms.py:374
136 #: forms.py:373
136 137 msgid "User name"
137 138 msgstr "Имя пользователя"
138 139
139 #: forms.py:375
140 #: forms.py:374
140 141 msgid "Time zone"
141 142 msgstr "Часовой пояс"
142 143
143 #: forms.py:381
144 #: forms.py:380
144 145 msgid "Inappropriate characters."
145 146 msgstr "Недопустимые символы."
146 147
147 148 #: templates/boards/404.html:6
148 149 msgid "Not found"
149 150 msgstr "Не найдено"
150 151
151 152 #: templates/boards/404.html:12
152 153 msgid "This page does not exist"
153 154 msgstr "Этой страницы не существует"
154 155
155 156 #: templates/boards/all_threads.html:35
156 157 msgid "Related message"
157 158 msgstr "Связанное сообщение"
158 159
159 160 #: templates/boards/all_threads.html:68
160 161 msgid "Edit tag"
161 162 msgstr "Изменить метку"
162 163
163 #: templates/boards/all_threads.html:71
164 #: templates/boards/all_threads.html:76
164 165 #, python-format
165 166 msgid "This tag has %(thread_count)s threads and %(post_count)s posts."
166 167 msgstr "С этой меткой есть %(thread_count)s тем и %(post_count)s сообщений."
167 168
168 #: templates/boards/all_threads.html:78 templates/boards/feed.html:30
169 #: templates/boards/all_threads.html:83 templates/boards/feed.html:30
169 170 #: templates/boards/notifications.html:17 templates/search/search.html:26
170 171 msgid "Previous page"
171 172 msgstr "Предыдущая страница"
172 173
173 #: templates/boards/all_threads.html:92
174 #: templates/boards/all_threads.html:97
174 175 #, python-format
175 176 msgid "Skipped %(count)s replies. Open thread to see all replies."
176 177 msgstr "Пропущено %(count)s ответов. Откройте тред, чтобы увидеть все ответы."
177 178
178 #: templates/boards/all_threads.html:110 templates/boards/feed.html:40
179 #: templates/boards/all_threads.html:115 templates/boards/feed.html:40
179 180 #: templates/boards/notifications.html:27 templates/search/search.html:37
180 181 msgid "Next page"
181 182 msgstr "Следующая страница"
182 183
183 #: templates/boards/all_threads.html:115
184 #: templates/boards/all_threads.html:120
184 185 msgid "No threads exist. Create the first one!"
185 186 msgstr "Нет тем. Создайте первую!"
186 187
187 #: templates/boards/all_threads.html:121
188 #: templates/boards/all_threads.html:126
188 189 msgid "Create new thread"
189 190 msgstr "Создать новую тему"
190 191
191 #: templates/boards/all_threads.html:126 templates/boards/preview.html:16
192 #: templates/boards/all_threads.html:131 templates/boards/preview.html:16
192 193 #: templates/boards/thread_normal.html:38
193 194 msgid "Post"
194 195 msgstr "Отправить"
195 196
196 #: templates/boards/all_threads.html:131
197 #: templates/boards/all_threads.html:136
197 198 msgid "Tags must be delimited by spaces. Text or image is required."
198 199 msgstr ""
199 200 "Метки должны быть разделены пробелами. Текст или изображение обязательны."
200 201
201 #: templates/boards/all_threads.html:133 templates/boards/preview.html:6
202 #: templates/boards/all_threads.html:138 templates/boards/preview.html:6
202 203 #: templates/boards/staticpages/help.html:21
203 204 #: templates/boards/thread_normal.html:42
204 205 msgid "Preview"
205 206 msgstr "Предпросмотр"
206 207
207 #: templates/boards/all_threads.html:135 templates/boards/thread_normal.html:45
208 #: templates/boards/all_threads.html:140 templates/boards/thread_normal.html:45
208 209 msgid "Text syntax"
209 210 msgstr "Синтаксис текста"
210 211
211 #: templates/boards/all_threads.html:149 templates/boards/feed.html:53
212 #: templates/boards/all_threads.html:154 templates/boards/feed.html:53
212 213 msgid "Pages:"
213 214 msgstr "Страницы: "
214 215
215 216 #: templates/boards/authors.html:6 templates/boards/authors.html.py:12
216 217 msgid "Authors"
217 218 msgstr "Авторы"
218 219
219 220 #: templates/boards/authors.html:26
220 221 msgid "Distributed under the"
221 222 msgstr "Распространяется под"
222 223
223 224 #: templates/boards/authors.html:28
224 225 msgid "license"
225 226 msgstr "лицензией"
226 227
227 228 #: templates/boards/authors.html:30
228 229 msgid "Repository"
229 230 msgstr "Репозиторий"
230 231
231 232 #: templates/boards/base.html:14 templates/boards/base.html.py:41
232 233 msgid "Feed"
233 234 msgstr "Лента"
234 235
235 236 #: templates/boards/base.html:31
236 237 msgid "All threads"
237 238 msgstr "Все темы"
238 239
239 240 #: templates/boards/base.html:37
240 241 msgid "Add tags"
241 242 msgstr "Добавить метки"
242 243
243 244 #: templates/boards/base.html:39
244 245 msgid "Tag management"
245 246 msgstr "Управление метками"
246 247
247 248 #: templates/boards/base.html:39
248 249 msgid "tags"
249 250 msgstr "метки"
250 251
251 252 #: templates/boards/base.html:40
252 253 msgid "search"
253 254 msgstr "поиск"
254 255
255 256 #: templates/boards/base.html:41 templates/boards/feed.html:11
256 257 msgid "feed"
257 258 msgstr "лента"
258 259
259 260 #: templates/boards/base.html:42 templates/boards/random.html:6
260 261 msgid "Random images"
261 262 msgstr "Случайные изображения"
262 263
263 264 #: templates/boards/base.html:42
264 265 msgid "random"
265 266 msgstr "случайные"
266 267
267 268 #: templates/boards/base.html:45 templates/boards/base.html.py:46
268 269 #: templates/boards/notifications.html:8
269 270 msgid "Notifications"
270 271 msgstr "Уведомления"
271 272
272 273 #: templates/boards/base.html:53 templates/boards/settings.html:8
273 274 msgid "Settings"
274 275 msgstr "Настройки"
275 276
276 277 #: templates/boards/base.html:66
277 278 msgid "Admin"
278 279 msgstr "Администрирование"
279 280
280 281 #: templates/boards/base.html:68
281 282 #, python-format
282 283 msgid "Speed: %(ppd)s posts per day"
283 284 msgstr "Скорость: %(ppd)s сообщений в день"
284 285
285 286 #: templates/boards/base.html:70
286 287 msgid "Up"
287 288 msgstr "Вверх"
288 289
289 290 #: templates/boards/feed.html:45
290 291 msgid "No posts exist. Create the first one!"
291 292 msgstr "Нет сообщений. Создайте первое!"
292 293
293 294 #: templates/boards/post.html:25
294 295 msgid "Open"
295 296 msgstr "Открыть"
296 297
297 298 #: templates/boards/post.html:27 templates/boards/post.html.py:38
298 299 msgid "Reply"
299 300 msgstr "Ответить"
300 301
301 302 #: templates/boards/post.html:33
302 303 msgid " in "
303 304 msgstr " в "
304 305
305 306 #: templates/boards/post.html:43
306 307 msgid "Edit"
307 308 msgstr "Изменить"
308 309
309 310 #: templates/boards/post.html:45
310 311 msgid "Edit thread"
311 312 msgstr "Изменить тему"
312 313
313 314 #: templates/boards/post.html:84
314 315 msgid "Replies"
315 316 msgstr "Ответы"
316 317
317 318 #: templates/boards/post.html:97 templates/boards/thread.html:34
318 319 msgid "messages"
319 320 msgstr "сообщений"
320 321
321 322 #: templates/boards/post.html:98 templates/boards/thread.html:35
322 323 msgid "images"
323 324 msgstr "изображений"
324 325
325 326 #: templates/boards/rss/post.html:5
326 327 msgid "Post image"
327 328 msgstr "Изображение сообщения"
328 329
329 330 #: templates/boards/settings.html:16
330 331 msgid "You are moderator."
331 332 msgstr "Вы модератор."
332 333
333 334 #: templates/boards/settings.html:20
334 335 msgid "Hidden tags:"
335 336 msgstr "Скрытые метки:"
336 337
337 338 #: templates/boards/settings.html:28
338 339 msgid "No hidden tags."
339 340 msgstr "Нет скрытых меток."
340 341
341 342 #: templates/boards/settings.html:37
342 343 msgid "Save"
343 344 msgstr "Сохранить"
344 345
345 346 #: templates/boards/staticpages/banned.html:6
346 347 msgid "Banned"
347 348 msgstr "Заблокирован"
348 349
349 350 #: templates/boards/staticpages/banned.html:11
350 351 msgid "Your IP address has been banned. Contact the administrator"
351 352 msgstr "Ваш IP адрес был заблокирован. Свяжитесь с администратором"
352 353
353 354 #: templates/boards/staticpages/help.html:6
354 355 #: templates/boards/staticpages/help.html:10
355 356 msgid "Syntax"
356 357 msgstr "Синтаксис"
357 358
358 359 #: templates/boards/staticpages/help.html:11
359 360 msgid "Italic text"
360 361 msgstr "Курсивный текст"
361 362
362 363 #: templates/boards/staticpages/help.html:12
363 364 msgid "Bold text"
364 365 msgstr "Полужирный текст"
365 366
366 367 #: templates/boards/staticpages/help.html:13
367 368 msgid "Spoiler"
368 369 msgstr "Спойлер"
369 370
370 371 #: templates/boards/staticpages/help.html:14
371 372 msgid "Link to a post"
372 373 msgstr "Ссылка на сообщение"
373 374
374 375 #: templates/boards/staticpages/help.html:15
375 376 msgid "Strikethrough text"
376 377 msgstr "Зачеркнутый текст"
377 378
378 379 #: templates/boards/staticpages/help.html:16
379 380 msgid "Comment"
380 381 msgstr "Комментарий"
381 382
382 383 #: templates/boards/staticpages/help.html:17
383 384 #: templates/boards/staticpages/help.html:18
384 385 msgid "Quote"
385 386 msgstr "Цитата"
386 387
387 388 #: templates/boards/staticpages/help.html:21
388 389 msgid "You can try pasting the text and previewing the result here:"
389 390 msgstr "Вы можете попробовать вставить текст и проверить результат здесь:"
390 391
391 392 #: templates/boards/tags.html:14
392 393 msgid "Sections:"
393 394 msgstr "Разделы:"
394 395
395 396 #: templates/boards/tags.html:26
396 #| msgid "Hidden tags:"
397 397 msgid "Other tags:"
398 398 msgstr "Другие метки:"
399 399
400 400 #: templates/boards/tags.html:38
401 #| msgid "All tags"
402 401 msgid "All tags..."
403 402 msgstr "Все метки..."
404 403
405 404 #: templates/boards/thread.html:15
406 405 msgid "Normal"
407 406 msgstr "Нормальный"
408 407
409 408 #: templates/boards/thread.html:16
410 409 msgid "Gallery"
411 410 msgstr "Галерея"
412 411
413 412 #: templates/boards/thread.html:17
414 413 msgid "Tree"
415 414 msgstr "Дерево"
416 415
417 416 #: templates/boards/thread.html:36
418 417 msgid "Last update: "
419 418 msgstr "Последнее обновление: "
420 419
421 420 #: templates/boards/thread_gallery.html:36
422 421 msgid "No images."
423 422 msgstr "Нет изображений."
424 423
425 424 #: templates/boards/thread_normal.html:17
426 425 msgid "posts to bumplimit"
427 426 msgstr "сообщений до бамплимита"
428 427
429 428 #: templates/boards/thread_normal.html:31
430 429 msgid "Reply to thread"
431 430 msgstr "Ответить в тему"
432 431
433 432 #: templates/boards/thread_normal.html:46
434 433 msgid "Close form"
435 434 msgstr "Закрыть форму"
436 435
437 436 #: templates/search/search.html:17
438 437 msgid "Ok"
439 438 msgstr "Ок"
440
General Comments 0
You need to be logged in to leave comments. Login now