##// END OF EJS Templates
Count total files size in a post instead of per-file basis
neko259 -
r1983:1ad01970 default
parent child Browse files
Show More
@@ -1,50 +1,50 b''
1 1 [Version]
2 2 Version = 4.7.1 Elon
3 3 SiteName = Neboard DEV
4 4
5 5 [Cache]
6 6 # Timeout for caching, if cache is used
7 7 CacheTimeout = 600
8 8
9 9 [Forms]
10 10 # Max post length in characters
11 11 MaxTextLength = 30000
12 12 MaxFileSize = 8000000
13 13 LimitFirstPosting = true
14 14 LimitPostingSpeed = false
15 15 PowDifficulty = 0
16 16 # Delay in seconds
17 17 PostingDelay = 30
18 18 Autoban = false
19 19 DefaultTag = test
20 MaxFileCount = 1
20 MaxFileCount = 5
21 21 AdditionalSpoilerSpaces = false
22 22
23 23 [Messages]
24 24 # Thread bumplimit
25 25 MaxPostsPerThread = 10
26 26 ThreadArchiveDays = 300
27 27 AnonymousMode = false
28 28
29 29 [View]
30 30 DefaultTheme = md
31 31 DefaultImageViewer = simple
32 32 LastRepliesCount = 3
33 33 ThreadsPerPage = 3
34 34 PostsPerPage = 10
35 35 ImagesPerPageGallery = 20
36 36 MaxFavoriteThreads = 20
37 37 MaxLandingThreads = 20
38 38 Themes=md:Mystic Dark,md_centered:Mystic Dark (centered),sw:Snow White,pg:Photon Grey,ad:Amanita Dark,iw:Inocibe White
39 39 ImageViewers=simple:Simple,popup:Popup
40 40
41 41 [Storage]
42 42 # Enable archiving threads instead of deletion when the thread limit is reached
43 43 ArchiveThreads = true
44 44
45 45 [RSS]
46 46 MaxItems = 20
47 47
48 48 [External]
49 49 ImageSearchHost=
50 50 SourceFetcherTripcode=
@@ -1,586 +1,588 b''
1 1 import logging
2 2 import time
3 3
4 4 import hashlib
5 5 import pytz
6 6 import re
7 7 from PIL import Image
8 8 from django import forms
9 9 from django.core.cache import cache
10 10 from django.core.files.images import get_image_dimensions
11 11 from django.core.files.uploadedfile import SimpleUploadedFile, UploadedFile
12 12 from django.forms.utils import ErrorList
13 13 from django.utils.translation import ugettext_lazy as _, ungettext_lazy
14 14
15 15 import boards.settings as board_settings
16 16 from boards import utils
17 17 from boards.abstracts.constants import REGEX_TAGS
18 18 from boards.abstracts.settingsmanager import get_settings_manager
19 19 from boards.abstracts.sticker_factory import get_attachment_by_alias
20 20 from boards.forms.fields import UrlFileField
21 21 from boards.mdx_neboard import formatters
22 22 from boards.models import Attachment
23 23 from boards.models import Tag
24 24 from boards.models.attachment import StickerPack
25 25 from boards.models.attachment.downloaders import download, REGEX_MAGNET
26 26 from boards.models.attachment.viewers import FILE_TYPES_IMAGE
27 27 from boards.models.post import TITLE_MAX_LENGTH
28 28 from boards.utils import validate_file_size, get_file_mimetype, \
29 29 FILE_EXTENSION_DELIMITER, get_tripcode_from_text
30 30
31 31 SECTION_FORMS = 'Forms'
32 32
33 33 POW_HASH_LENGTH = 16
34 34 POW_LIFE_MINUTES = 5
35 35
36 36 REGEX_USERNAMES = re.compile(r'^[\w\s\d,]+$', re.UNICODE)
37 37 REGEX_URL = re.compile(r'^(http|https|ftp):\/\/', re.UNICODE)
38 38
39 39 VETERAN_POSTING_DELAY = 5
40 40
41 41 ATTRIBUTE_PLACEHOLDER = 'placeholder'
42 42 ATTRIBUTE_ROWS = 'rows'
43 43
44 44 LAST_POST_TIME = 'last_post_time'
45 45 LAST_LOGIN_TIME = 'last_login_time'
46 46 TEXT_PLACEHOLDER = _('Type message here. Use formatting panel for more advanced usage.')
47 47 TAGS_PLACEHOLDER = _('music images i_dont_like_tags')
48 48
49 49 LABEL_TITLE = _('Title')
50 50 LABEL_TEXT = _('Text')
51 51 LABEL_TAG = _('Tag')
52 52 LABEL_SEARCH = _('Search')
53 53 LABEL_FILE = _('File')
54 54 LABEL_DUPLICATES = _('Check for duplicates')
55 55 LABEL_URL = _('Do not download URLs')
56 56
57 57 ERROR_SPEED = 'Please wait %(delay)d second before sending message'
58 58 ERROR_SPEED_PLURAL = 'Please wait %(delay)d seconds before sending message'
59 59 ERROR_MANY_FILES = 'You can post no more than %(files)d file.'
60 60 ERROR_MANY_FILES_PLURAL = 'You can post no more than %(files)d files.'
61 61 ERROR_DUPLICATES = 'Some files are already present on the board.'
62 62
63 63 TAG_MAX_LENGTH = 20
64 64
65 65 TEXTAREA_ROWS = 4
66 66
67 67 TRIPCODE_DELIM = '##'
68 68
69 69 # TODO Maybe this may be converted into the database table?
70 70 MIMETYPE_EXTENSIONS = {
71 71 'image/jpeg': 'jpeg',
72 72 'image/png': 'png',
73 73 'image/gif': 'gif',
74 74 'video/webm': 'webm',
75 75 'application/pdf': 'pdf',
76 76 'x-diff': 'diff',
77 77 'image/svg+xml': 'svg',
78 78 'application/x-shockwave-flash': 'swf',
79 79 'image/x-ms-bmp': 'bmp',
80 80 'image/bmp': 'bmp',
81 81 }
82 82
83 83 DOWN_MODE_DOWNLOAD = 'DOWNLOAD'
84 84 DOWN_MODE_DOWNLOAD_UNIQUE = 'DOWNLOAD_UNIQUE'
85 85 DOWN_MODE_URL = 'URL'
86 86 DOWN_MODE_TRY = 'TRY'
87 87
88 88
89 89 logger = logging.getLogger('boards.forms')
90 90
91 91
92 92 def get_timezones():
93 93 timezones = []
94 94 for tz in pytz.common_timezones:
95 95 timezones.append((tz, tz),)
96 96 return timezones
97 97
98 98
99 99 class FormatPanel(forms.Textarea):
100 100 """
101 101 Panel for text formatting. Consists of buttons to add different tags to the
102 102 form text area.
103 103 """
104 104
105 105 def render(self, name, value, attrs=None):
106 106 output = '<div id="mark-panel">'
107 107 for formatter in formatters:
108 108 output += '<span class="mark_btn"' + \
109 109 ' onClick="addMarkToMsg(\'' + formatter.format_left + \
110 110 '\', \'' + formatter.format_right + '\')">' + \
111 111 formatter.preview_left + formatter.name + \
112 112 formatter.preview_right + '</span>'
113 113
114 114 output += '</div>'
115 115 output += super(FormatPanel, self).render(name, value, attrs=attrs)
116 116
117 117 return output
118 118
119 119
120 120 class PlainErrorList(ErrorList):
121 121 def __unicode__(self):
122 122 return self.as_text()
123 123
124 124 def as_text(self):
125 125 return ''.join(['(!) %s ' % e for e in self])
126 126
127 127
128 128 class NeboardForm(forms.Form):
129 129 """
130 130 Form with neboard-specific formatting.
131 131 """
132 132 required_css_class = 'required-field'
133 133
134 134 def as_div(self):
135 135 """
136 136 Returns this form rendered as HTML <as_div>s.
137 137 """
138 138
139 139 return self._html_output(
140 140 # TODO Do not show hidden rows in the list here
141 141 normal_row='<div class="form-row">'
142 142 '<div class="form-label">'
143 143 '%(label)s'
144 144 '</div>'
145 145 '<div class="form-input">'
146 146 '%(field)s'
147 147 '</div>'
148 148 '</div>'
149 149 '<div class="form-row">'
150 150 '%(help_text)s'
151 151 '</div>',
152 152 error_row='<div class="form-row">'
153 153 '<div class="form-label"></div>'
154 154 '<div class="form-errors">%s</div>'
155 155 '</div>',
156 156 row_ender='</div>',
157 157 help_text_html='%s',
158 158 errors_on_separate_row=True)
159 159
160 160 def as_json_errors(self):
161 161 errors = []
162 162
163 163 for name, field in list(self.fields.items()):
164 164 if self[name].errors:
165 165 errors.append({
166 166 'field': name,
167 167 'errors': self[name].errors.as_text(),
168 168 })
169 169
170 170 return errors
171 171
172 172
173 173 class PostForm(NeboardForm):
174 174
175 175 title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False,
176 176 label=LABEL_TITLE,
177 177 widget=forms.TextInput(
178 178 attrs={ATTRIBUTE_PLACEHOLDER: 'Title{}tripcode'.format(TRIPCODE_DELIM)}))
179 179 text = forms.CharField(
180 180 widget=FormatPanel(attrs={
181 181 ATTRIBUTE_PLACEHOLDER: TEXT_PLACEHOLDER,
182 182 ATTRIBUTE_ROWS: TEXTAREA_ROWS,
183 183 }),
184 184 required=False, label=LABEL_TEXT)
185 185 download_mode = forms.ChoiceField(
186 186 choices=(
187 187 (DOWN_MODE_TRY, _('Download or insert as URLs')),
188 188 (DOWN_MODE_DOWNLOAD, _('Download')),
189 189 (DOWN_MODE_DOWNLOAD_UNIQUE, _('Download and check for uniqueness')),
190 190 (DOWN_MODE_URL, _('Insert as URLs')),
191 191 ),
192 192 initial=DOWN_MODE_TRY,
193 193 label=_('File process mode'))
194 194 file = UrlFileField(required=False, label=LABEL_FILE)
195 195
196 196 # This field is for spam prevention only
197 197 email = forms.CharField(max_length=100, required=False, label=_('e-mail'),
198 198 widget=forms.TextInput(attrs={
199 199 'class': 'form-email'}))
200 200 subscribe = forms.BooleanField(required=False, label=_('Subscribe to thread'))
201 201
202 202 guess = forms.CharField(widget=forms.HiddenInput(), required=False)
203 203 timestamp = forms.CharField(widget=forms.HiddenInput(), required=False)
204 204 iteration = forms.CharField(widget=forms.HiddenInput(), required=False)
205 205
206 206 session = None
207 207 need_to_ban = False
208 208
209 209 def clean_title(self):
210 210 title = self.cleaned_data['title']
211 211 if title:
212 212 if len(title) > TITLE_MAX_LENGTH:
213 213 raise forms.ValidationError(_('Title must have less than %s '
214 214 'characters') %
215 215 str(TITLE_MAX_LENGTH))
216 216 return title
217 217
218 218 def clean_text(self):
219 219 text = self.cleaned_data['text'].strip()
220 220 if text:
221 221 max_length = board_settings.get_int(SECTION_FORMS, 'MaxTextLength')
222 222 if len(text) > max_length:
223 223 raise forms.ValidationError(_('Text must have less than %s '
224 224 'characters') % str(max_length))
225 225 return text
226 226
227 227 def clean_file(self):
228 228 return self._clean_files(self.cleaned_data['file'])
229 229
230 230 def clean(self):
231 231 cleaned_data = super(PostForm, self).clean()
232 232
233 233 if cleaned_data['email']:
234 234 if board_settings.get_bool(SECTION_FORMS, 'Autoban'):
235 235 self.need_to_ban = True
236 236 raise forms.ValidationError('A human cannot enter a hidden field')
237 237
238 238 if not self.errors:
239 239 self._clean_text_file()
240 240
241 241 limit_speed = board_settings.get_bool(SECTION_FORMS, 'LimitPostingSpeed')
242 242 limit_first = board_settings.get_bool(SECTION_FORMS, 'LimitFirstPosting')
243 243
244 244 settings_manager = get_settings_manager(self)
245 245 if not self.errors and limit_speed or (limit_first and not settings_manager.get_setting('confirmed_user')):
246 246 pow_difficulty = board_settings.get_int(SECTION_FORMS, 'PowDifficulty')
247 247 if pow_difficulty > 0:
248 248 # PoW-based
249 249 if cleaned_data['timestamp'] \
250 250 and cleaned_data['iteration'] and cleaned_data['guess'] \
251 251 and not settings_manager.get_setting('confirmed_user'):
252 252 self._validate_hash(cleaned_data['timestamp'], cleaned_data['iteration'], cleaned_data['guess'], cleaned_data['text'])
253 253 else:
254 254 # Time-based
255 255 self._validate_posting_speed()
256 256 settings_manager.set_setting('confirmed_user', True)
257 257 if self.cleaned_data['download_mode'] == DOWN_MODE_DOWNLOAD_UNIQUE:
258 258 self._check_file_duplicates(self.get_files())
259 259
260 260 return cleaned_data
261 261
262 262 def get_files(self):
263 263 """
264 264 Gets file from form or URL.
265 265 """
266 266
267 267 files = []
268 268 for file in self.cleaned_data['file']:
269 269 if isinstance(file, UploadedFile):
270 270 files.append(file)
271 271
272 272 return files
273 273
274 274 def get_file_urls(self):
275 275 files = []
276 276 for file in self.cleaned_data['file']:
277 277 if type(file) == str:
278 278 files.append(file)
279 279
280 280 return files
281 281
282 282 def get_tripcode(self):
283 283 title = self.cleaned_data['title']
284 284 if title is not None and TRIPCODE_DELIM in title:
285 285 tripcode = get_tripcode_from_text(title.split(TRIPCODE_DELIM, maxsplit=1)[1])
286 286 else:
287 287 tripcode = ''
288 288 return tripcode
289 289
290 290 def get_title(self):
291 291 title = self.cleaned_data['title']
292 292 if title is not None and TRIPCODE_DELIM in title:
293 293 return title.split(TRIPCODE_DELIM, maxsplit=1)[0]
294 294 else:
295 295 return title
296 296
297 297 def get_images(self):
298 298 return self.cleaned_data.get('stickers', [])
299 299
300 300 def is_subscribe(self):
301 301 return self.cleaned_data['subscribe']
302 302
303 303 def _update_file_extension(self, file):
304 304 if file:
305 305 mimetype = get_file_mimetype(file)
306 306 extension = MIMETYPE_EXTENSIONS.get(mimetype)
307 307 if extension:
308 308 filename = file.name.split(FILE_EXTENSION_DELIMITER, 1)[0]
309 309 new_filename = filename + FILE_EXTENSION_DELIMITER + extension
310 310
311 311 file.name = new_filename
312 312 else:
313 313 logger.info('Unrecognized file mimetype: {}'.format(mimetype))
314 314
315 315 def _clean_files(self, inputs):
316 316 files = []
317 317
318 318 max_file_count = board_settings.get_int(SECTION_FORMS, 'MaxFileCount')
319 319 if len(inputs) > max_file_count:
320 320 raise forms.ValidationError(
321 321 ungettext_lazy(ERROR_MANY_FILES, ERROR_MANY_FILES,
322 322 max_file_count) % {'files': max_file_count})
323
324 size = 0
323 325 for file_input in inputs:
324 326 if isinstance(file_input, UploadedFile):
325 files.append(self._clean_file_file(file_input))
327 file = self._clean_file_file(file_input)
328 size += file.size
329 files.append(file)
326 330 else:
327 331 files.append(self._clean_file_url(file_input))
328 332
329 333 for file in files:
330 334 self._validate_image_dimensions(file)
335 validate_file_size(size)
331 336
332 337 return files
333 338
334 339 def _validate_image_dimensions(self, file):
335 340 if isinstance(file, UploadedFile):
336 341 mimetype = get_file_mimetype(file)
337 342 if mimetype.split('/')[-1] in FILE_TYPES_IMAGE:
338 343 Image.warnings.simplefilter('error', Image.DecompressionBombWarning)
339 344 try:
340 345 print(get_image_dimensions(file))
341 346 except Exception:
342 347 raise forms.ValidationError('Possible decompression bomb or large image.')
343 348
344 349 def _clean_file_file(self, file):
345 validate_file_size(file.size)
346 350 self._update_file_extension(file)
347 351
348 352 return file
349 353
350 354 def _clean_file_url(self, url):
351 355 file = None
352 356
353 357 if url:
354 358 mode = self.cleaned_data['download_mode']
355 359 if mode == DOWN_MODE_URL:
356 360 return url
357 361
358 362 try:
359 363 image = get_attachment_by_alias(url, self.session)
360 364 if image is not None:
361 365 if 'stickers' not in self.cleaned_data:
362 366 self.cleaned_data['stickers'] = []
363 367 self.cleaned_data['stickers'].append(image)
364 368 return
365 369
366 370 if file is None:
367 371 file = self._get_file_from_url(url)
368 372 if not file:
369 373 raise forms.ValidationError(_('Invalid URL'))
370 else:
371 validate_file_size(file.size)
372 374 self._update_file_extension(file)
373 375 except forms.ValidationError as e:
374 376 # Assume we will get the plain URL instead of a file and save it
375 377 if mode == DOWN_MODE_TRY and (REGEX_URL.match(url) or REGEX_MAGNET.match(url)):
376 378 logger.info('Error in forms: {}'.format(e))
377 379 return url
378 380 else:
379 381 raise e
380 382
381 383 return file
382 384
383 385 def _clean_text_file(self):
384 386 text = self.cleaned_data.get('text')
385 387 file = self.get_files()
386 388 file_url = self.get_file_urls()
387 389 images = self.get_images()
388 390
389 391 if (not text) and (not file) and (not file_url) and len(images) == 0:
390 392 error_message = _('Either text or file must be entered.')
391 393 self._add_general_error(error_message)
392 394
393 395 def _get_cache_key(self, key):
394 396 return '{}_{}'.format(self.session.session_key, key)
395 397
396 398 def _set_session_cache(self, key, value):
397 399 cache.set(self._get_cache_key(key), value)
398 400
399 401 def _get_session_cache(self, key):
400 402 return cache.get(self._get_cache_key(key))
401 403
402 404 def _get_last_post_time(self):
403 405 last = self._get_session_cache(LAST_POST_TIME)
404 406 if last is None:
405 407 last = self.session.get(LAST_POST_TIME)
406 408 return last
407 409
408 410 def _validate_posting_speed(self):
409 411 can_post = True
410 412
411 413 posting_delay = board_settings.get_int(SECTION_FORMS, 'PostingDelay')
412 414
413 415 if board_settings.get_bool(SECTION_FORMS, 'LimitPostingSpeed'):
414 416 now = time.time()
415 417
416 418 current_delay = 0
417 419
418 420 if LAST_POST_TIME not in self.session:
419 421 self.session[LAST_POST_TIME] = now
420 422
421 423 need_delay = True
422 424 else:
423 425 last_post_time = self._get_last_post_time()
424 426 current_delay = int(now - last_post_time)
425 427
426 428 need_delay = current_delay < posting_delay
427 429
428 430 self._set_session_cache(LAST_POST_TIME, now)
429 431
430 432 if need_delay:
431 433 delay = posting_delay - current_delay
432 434 error_message = ungettext_lazy(ERROR_SPEED, ERROR_SPEED_PLURAL,
433 435 delay) % {'delay': delay}
434 436 self._add_general_error(error_message)
435 437
436 438 can_post = False
437 439
438 440 if can_post:
439 441 self.session[LAST_POST_TIME] = now
440 442 else:
441 443 # Reset the time since posting failed
442 444 self._set_session_cache(LAST_POST_TIME, self.session[LAST_POST_TIME])
443 445
444 446 def _get_file_from_url(self, url: str) -> SimpleUploadedFile:
445 447 """
446 448 Gets an file file from URL.
447 449 """
448 450
449 451 try:
450 452 return download(url)
451 453 except forms.ValidationError as e:
452 454 raise e
453 455 except Exception as e:
454 456 raise forms.ValidationError(e)
455 457
456 458 def _validate_hash(self, timestamp: str, iteration: str, guess: str, message: str):
457 459 payload = timestamp + message.replace('\r\n', '\n')
458 460 difficulty = board_settings.get_int(SECTION_FORMS, 'PowDifficulty')
459 461 target = str(int(2 ** (POW_HASH_LENGTH * 3) / difficulty))
460 462 if len(target) < POW_HASH_LENGTH:
461 463 target = '0' * (POW_HASH_LENGTH - len(target)) + target
462 464
463 465 computed_guess = hashlib.sha256((payload + iteration).encode())\
464 466 .hexdigest()[0:POW_HASH_LENGTH]
465 467 if guess != computed_guess or guess > target:
466 468 self._add_general_error(_('Invalid PoW.'))
467 469
468 470 def _check_file_duplicates(self, files):
469 471 for file in files:
470 472 file_hash = utils.get_file_hash(file)
471 473 if Attachment.objects.get_existing_duplicate(file_hash, file):
472 474 self._add_general_error(_(ERROR_DUPLICATES))
473 475
474 476 def _add_general_error(self, message):
475 477 self.add_error('text', forms.ValidationError(message))
476 478
477 479
478 480 class ThreadForm(PostForm):
479 481
480 482 tags = forms.CharField(
481 483 widget=forms.TextInput(attrs={ATTRIBUTE_PLACEHOLDER: TAGS_PLACEHOLDER}),
482 484 max_length=100, label=_('Tags'), required=True)
483 485 monochrome = forms.BooleanField(label=_('Monochrome'), required=False)
484 486 stickerpack = forms.BooleanField(label=_('Sticker Pack'), required=False)
485 487
486 488 def clean_tags(self):
487 489 tags = self.cleaned_data['tags'].strip()
488 490
489 491 if not tags or not REGEX_TAGS.match(tags):
490 492 raise forms.ValidationError(
491 493 _('Inappropriate characters in tags.'))
492 494
493 495 default_tag_name = board_settings.get(SECTION_FORMS, 'DefaultTag')\
494 496 .strip().lower()
495 497
496 498 required_tag_exists = False
497 499 tag_set = set()
498 500 for tag_string in tags.split():
499 501 tag_name = tag_string.strip().lower()
500 502 if tag_name == default_tag_name:
501 503 required_tag_exists = True
502 504 tag, created = Tag.objects.get_or_create_with_alias(
503 505 name=tag_name, required=True)
504 506 else:
505 507 tag, created = Tag.objects.get_or_create_with_alias(name=tag_name)
506 508 tag_set.add(tag)
507 509
508 510 # If this is a new tag, don't check for its parents because nobody
509 511 # added them yet
510 512 if not created:
511 513 tag_set |= set(tag.get_all_parents())
512 514
513 515 for tag in tag_set:
514 516 if tag.required:
515 517 required_tag_exists = True
516 518 break
517 519
518 520 # Use default tag if no section exists
519 521 if not required_tag_exists:
520 522 default_tag, created = Tag.objects.get_or_create_with_alias(
521 523 name=default_tag_name, required=True)
522 524 tag_set.add(default_tag)
523 525
524 526 return tag_set
525 527
526 528 def clean(self):
527 529 cleaned_data = super(ThreadForm, self).clean()
528 530
529 531 return cleaned_data
530 532
531 533 def is_monochrome(self):
532 534 return self.cleaned_data['monochrome']
533 535
534 536 def clean_stickerpack(self):
535 537 stickerpack = self.cleaned_data['stickerpack']
536 538 if stickerpack:
537 539 tripcode = self.get_tripcode()
538 540 if not tripcode:
539 541 raise forms.ValidationError(_(
540 542 'Tripcode should be specified to own a stickerpack.'))
541 543 title = self.get_title()
542 544 if not title:
543 545 raise forms.ValidationError(_(
544 546 'Title should be specified as a stickerpack name.'))
545 547 if not REGEX_TAGS.match(title):
546 548 raise forms.ValidationError(_('Inappropriate sticker pack name.'))
547 549
548 550 existing_pack = StickerPack.objects.filter(name=title).first()
549 551 if existing_pack:
550 552 if existing_pack.tripcode != tripcode:
551 553 raise forms.ValidationError(_(
552 554 'A sticker pack with this name already exists and is'
553 555 ' owned by another tripcode.'))
554 556 if not existing_pack.tripcode:
555 557 raise forms.ValidationError(_(
556 558 'This sticker pack can only be updated by an '
557 559 'administrator.'))
558 560
559 561 return stickerpack
560 562
561 563 def is_stickerpack(self):
562 564 return self.cleaned_data['stickerpack']
563 565
564 566
565 567 class SettingsForm(NeboardForm):
566 568
567 569 theme = forms.ChoiceField(
568 570 choices=board_settings.get_list_dict('View', 'Themes'),
569 571 label=_('Theme'))
570 572 image_viewer = forms.ChoiceField(
571 573 choices=board_settings.get_list_dict('View', 'ImageViewers'),
572 574 label=_('Image view mode'))
573 575 username = forms.CharField(label=_('User name'), required=False)
574 576 timezone = forms.ChoiceField(choices=get_timezones(), label=_('Time zone'))
575 577
576 578 def clean_username(self):
577 579 username = self.cleaned_data['username']
578 580
579 581 if username and not REGEX_USERNAMES.match(username):
580 582 raise forms.ValidationError(_('Inappropriate characters.'))
581 583
582 584 return username
583 585
584 586
585 587 class SearchForm(NeboardForm):
586 588 query = forms.CharField(max_length=500, label=LABEL_SEARCH, required=False)
1 NO CONTENT: modified file, binary diff hidden
@@ -1,645 +1,645 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 10 "POT-Creation-Date: 2015-10-09 23:21+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:30
42 42 msgid "Type message here. Use formatting panel for more advanced usage."
43 43 msgstr ""
44 44 "Π’Π²ΠΎΠ΄ΠΈΡ‚Π΅ сообщСниС сюда. Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ панСль для Π±ΠΎΠ»Π΅Π΅ слоТного форматирования."
45 45
46 46 #: forms.py:31
47 47 msgid "music images i_dont_like_tags"
48 48 msgstr "ΠΌΡƒΠ·Ρ‹ΠΊΠ° ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠΈ Ρ‚Π΅Π³ΠΈ_Π½Π΅_Π½ΡƒΠΆΠ½Ρ‹"
49 49
50 50 #: forms.py:33
51 51 msgid "Title"
52 52 msgstr "Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ"
53 53
54 54 #: forms.py:34
55 55 msgid "Text"
56 56 msgstr "ВСкст"
57 57
58 58 #: forms.py:35
59 59 msgid "Tag"
60 60 msgstr "ΠœΠ΅Ρ‚ΠΊΠ°"
61 61
62 62 #: forms.py:36 templates/boards/base.html:40 templates/search/search.html:7
63 63 msgid "Search"
64 64 msgstr "Поиск"
65 65
66 66 #: forms.py:48
67 67 msgid "File 1"
68 68 msgstr "Π€Π°ΠΉΠ» 1"
69 69
70 70 #: forms.py:48
71 71 msgid "File 2"
72 72 msgstr "Π€Π°ΠΉΠ» 2"
73 73
74 74 #: forms.py:142
75 75 msgid "File URL"
76 76 msgstr "URL Ρ„Π°ΠΉΠ»Π°"
77 77
78 78 #: forms.py:148
79 79 msgid "e-mail"
80 80 msgstr ""
81 81
82 82 #: forms.py:151
83 83 msgid "Additional threads"
84 84 msgstr "Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ Ρ‚Π΅ΠΌΡ‹"
85 85
86 86 #: forms.py:162
87 87 #, python-format
88 88 msgid "Title must have less than %s characters"
89 89 msgstr "Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΈΠΌΠ΅Ρ‚ΡŒ мСньшС %s символов"
90 90
91 91 #: forms.py:172
92 92 #, python-format
93 93 msgid "Text must have less than %s characters"
94 94 msgstr "ВСкст Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ ΠΊΠΎΡ€ΠΎΡ‡Π΅ %s символов"
95 95
96 96 #: forms.py:192
97 97 msgid "Invalid URL"
98 98 msgstr "НСвСрный URL"
99 99
100 100 #: forms.py:213
101 101 msgid "Invalid additional thread list"
102 102 msgstr "НСвСрный список Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Ρ… Ρ‚Π΅ΠΌ"
103 103
104 104 #: forms.py:258
105 105 msgid "Either text or file must be entered."
106 106 msgstr "ВСкст ΠΈΠ»ΠΈ Ρ„Π°ΠΉΠ» Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ Π²Π²Π΅Π΄Π΅Π½Ρ‹."
107 107
108 108 #: forms.py:317 templates/boards/all_threads.html:153
109 109 #: templates/boards/rss/post.html:10 templates/boards/tags.html:6
110 110 msgid "Tags"
111 111 msgstr "ΠœΠ΅Ρ‚ΠΊΠΈ"
112 112
113 113 #: forms.py:324
114 114 msgid "Inappropriate characters in tags."
115 115 msgstr "НСдопустимыС символы Π² ΠΌΠ΅Ρ‚ΠΊΠ°Ρ…."
116 116
117 117 #: forms.py:344
118 118 msgid "Need at least one section."
119 119 msgstr "НуТСн хотя Π±Ρ‹ ΠΎΠ΄ΠΈΠ½ Ρ€Π°Π·Π΄Π΅Π»."
120 120
121 121 #: forms.py:356
122 122 msgid "Theme"
123 123 msgstr "Π’Π΅ΠΌΠ°"
124 124
125 125 #: forms.py:357
126 126 msgid "Image view mode"
127 127 msgstr "Π Π΅ΠΆΠΈΠΌ просмотра ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ"
128 128
129 129 #: forms.py:358
130 130 msgid "User name"
131 131 msgstr "Имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ"
132 132
133 133 #: forms.py:359
134 134 msgid "Time zone"
135 135 msgstr "Часовой пояс"
136 136
137 137 #: forms.py:365
138 138 msgid "Inappropriate characters."
139 139 msgstr "НСдопустимыС символы."
140 140
141 141 #: templates/boards/404.html:6
142 142 msgid "Not found"
143 143 msgstr "НС найдСно"
144 144
145 145 #: templates/boards/404.html:12
146 146 msgid "This page does not exist"
147 147 msgstr "Π­Ρ‚ΠΎΠΉ страницы Π½Π΅ сущСствуСт"
148 148
149 149 #: templates/boards/all_threads.html:35
150 150 msgid "Details"
151 151 msgstr "ΠŸΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎΡΡ‚ΠΈ"
152 152
153 153 #: templates/boards/all_threads.html:69
154 154 msgid "Edit tag"
155 155 msgstr "Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ ΠΌΠ΅Ρ‚ΠΊΡƒ"
156 156
157 157 #: templates/boards/all_threads.html:76
158 158 #, python-format
159 159 msgid "%(count)s active thread"
160 160 msgid_plural "%(count)s active threads"
161 161 msgstr[0] "%(count)s активная Ρ‚Π΅ΠΌΠ°"
162 162 msgstr[1] "%(count)s Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Π΅ Ρ‚Π΅ΠΌΡ‹"
163 163 msgstr[2] "%(count)s Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ… Ρ‚Π΅ΠΌ"
164 164
165 165 #: templates/boards/all_threads.html:76
166 166 #, python-format
167 167 msgid "%(count)s thread in bumplimit"
168 168 msgid_plural "%(count)s threads in bumplimit"
169 169 msgstr[0] "%(count)s Ρ‚Π΅ΠΌΠ° Π² Π±Π°ΠΌΠΏΠ»ΠΈΠΌΠΈΡ‚Π΅"
170 170 msgstr[1] "%(count)s Ρ‚Π΅ΠΌΡ‹ Π² Π±Π°ΠΌΠΏΠ»ΠΈΠΌΠΈΡ‚Π΅"
171 171 msgstr[2] "%(count)s Ρ‚Π΅ΠΌ Π² Π±Π°ΠΌΠΏΠ»ΠΈΠΌΠΈΡ‚Π΅"
172 172
173 173 #: templates/boards/all_threads.html:77
174 174 #, python-format
175 175 msgid "%(count)s archived thread"
176 176 msgid_plural "%(count)s archived thread"
177 177 msgstr[0] "%(count)s архивная Ρ‚Π΅ΠΌΠ°"
178 178 msgstr[1] "%(count)s Π°Ρ€Ρ…ΠΈΠ²Π½Ρ‹Π΅ Ρ‚Π΅ΠΌΡ‹"
179 179 msgstr[2] "%(count)s Π°Ρ€Ρ…ΠΈΠ²Π½Ρ‹Ρ… Ρ‚Π΅ΠΌ"
180 180
181 181 #: templates/boards/all_threads.html:78 templates/boards/post.html:102
182 182 #, python-format
183 183 #| msgid "%(count)s message"
184 184 #| msgid_plural "%(count)s messages"
185 185 msgid "%(count)s message"
186 186 msgid_plural "%(count)s messages"
187 187 msgstr[0] "%(count)s сообщСниС"
188 188 msgstr[1] "%(count)s сообщСния"
189 189 msgstr[2] "%(count)s сообщСний"
190 190
191 191 #: templates/boards/all_threads.html:95 templates/boards/feed.html:30
192 192 #: templates/boards/notifications.html:17 templates/search/search.html:26
193 193 msgid "Previous page"
194 194 msgstr "ΠŸΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π°Ρ страница"
195 195
196 196 #: templates/boards/all_threads.html:109
197 197 #, python-format
198 198 msgid "Skipped %(count)s reply. Open thread to see all replies."
199 199 msgid_plural "Skipped %(count)s replies. Open thread to see all replies."
200 200 msgstr[0] "ΠŸΡ€ΠΎΠΏΡƒΡ‰Π΅Π½ %(count)s ΠΎΡ‚Π²Π΅Ρ‚. ΠžΡ‚ΠΊΡ€ΠΎΠΉΡ‚Π΅ Ρ‚Ρ€Π΅Π΄, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ всС ΠΎΡ‚Π²Π΅Ρ‚Ρ‹."
201 201 msgstr[1] ""
202 202 "ΠŸΡ€ΠΎΠΏΡƒΡ‰Π΅Π½ΠΎ %(count)s ΠΎΡ‚Π²Π΅Ρ‚Π°. ΠžΡ‚ΠΊΡ€ΠΎΠΉΡ‚Π΅ Ρ‚Ρ€Π΅Π΄, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ всС ΠΎΡ‚Π²Π΅Ρ‚Ρ‹."
203 203 msgstr[2] ""
204 204 "ΠŸΡ€ΠΎΠΏΡƒΡ‰Π΅Π½ΠΎ %(count)s ΠΎΡ‚Π²Π΅Ρ‚ΠΎΠ². ΠžΡ‚ΠΊΡ€ΠΎΠΉΡ‚Π΅ Ρ‚Ρ€Π΅Π΄, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ всС ΠΎΡ‚Π²Π΅Ρ‚Ρ‹."
205 205
206 206 #: templates/boards/all_threads.html:127 templates/boards/feed.html:40
207 207 #: templates/boards/notifications.html:27 templates/search/search.html:37
208 208 msgid "Next page"
209 209 msgstr "Π‘Π»Π΅Π΄ΡƒΡŽΡ‰Π°Ρ страница"
210 210
211 211 #: templates/boards/all_threads.html:132
212 212 msgid "No threads exist. Create the first one!"
213 213 msgstr "НСт Ρ‚Π΅ΠΌ. Π‘ΠΎΠ·Π΄Π°ΠΉΡ‚Π΅ ΠΏΠ΅Ρ€Π²ΡƒΡŽ!"
214 214
215 215 #: templates/boards/all_threads.html:138
216 216 msgid "Create new thread"
217 217 msgstr "Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ Π½ΠΎΠ²ΡƒΡŽ Ρ‚Π΅ΠΌΡƒ"
218 218
219 219 #: templates/boards/all_threads.html:143 templates/boards/preview.html:16
220 220 #: templates/boards/thread_normal.html:51
221 221 msgid "Post"
222 222 msgstr "ΠžΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ"
223 223
224 224 #: templates/boards/all_threads.html:144 templates/boards/preview.html:6
225 225 #: templates/boards/staticpages/help.html:21
226 226 #: templates/boards/thread_normal.html:52
227 227 msgid "Preview"
228 228 msgstr "ΠŸΡ€Π΅Π΄ΠΏΡ€ΠΎΡΠΌΠΎΡ‚Ρ€"
229 229
230 230 #: templates/boards/all_threads.html:149
231 231 msgid "Tags must be delimited by spaces. Text or image is required."
232 232 msgstr ""
233 233 "ΠœΠ΅Ρ‚ΠΊΠΈ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ Ρ€Π°Π·Π΄Π΅Π»Π΅Π½Ρ‹ ΠΏΡ€ΠΎΠ±Π΅Π»Π°ΠΌΠΈ. ВСкст ΠΈΠ»ΠΈ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹."
234 234
235 235 #: templates/boards/all_threads.html:152 templates/boards/thread_normal.html:58
236 236 msgid "Text syntax"
237 237 msgstr "Бинтаксис тСкста"
238 238
239 239 #: templates/boards/all_threads.html:166 templates/boards/feed.html:53
240 240 msgid "Pages:"
241 241 msgstr "Π‘Ρ‚Ρ€Π°Π½ΠΈΡ†Ρ‹: "
242 242
243 243 #: templates/boards/authors.html:6 templates/boards/authors.html.py:12
244 244 msgid "Authors"
245 245 msgstr "Авторы"
246 246
247 247 #: templates/boards/authors.html:26
248 248 msgid "Distributed under the"
249 249 msgstr "РаспространяСтся ΠΏΠΎΠ΄"
250 250
251 251 #: templates/boards/authors.html:28
252 252 msgid "license"
253 253 msgstr "Π»ΠΈΡ†Π΅Π½Π·ΠΈΠ΅ΠΉ"
254 254
255 255 #: templates/boards/authors.html:30
256 256 msgid "Repository"
257 257 msgstr "Π Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ"
258 258
259 259 #: templates/boards/base.html:14 templates/boards/base.html.py:41
260 260 msgid "Feed"
261 261 msgstr "Π›Π΅Π½Ρ‚Π°"
262 262
263 263 #: templates/boards/base.html:31
264 264 msgid "All threads"
265 265 msgstr "ВсС Ρ‚Π΅ΠΌΡ‹"
266 266
267 267 #: templates/boards/base.html:37
268 268 msgid "Add tags"
269 269 msgstr "Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΌΠ΅Ρ‚ΠΊΠΈ"
270 270
271 271 #: templates/boards/base.html:39
272 272 msgid "Tag management"
273 273 msgstr "Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΌΠ΅Ρ‚ΠΊΠ°ΠΌΠΈ"
274 274
275 275 #: templates/boards/base.html:39
276 276 msgid "tags"
277 277 msgstr "ΠΌΠ΅Ρ‚ΠΊΠΈ"
278 278
279 279 #: templates/boards/base.html:40
280 280 msgid "search"
281 281 msgstr "поиск"
282 282
283 283 #: templates/boards/base.html:41 templates/boards/feed.html:11
284 284 msgid "feed"
285 285 msgstr "Π»Π΅Π½Ρ‚Π°"
286 286
287 287 #: templates/boards/base.html:42 templates/boards/random.html:6
288 288 msgid "Random images"
289 289 msgstr "Π‘Π»ΡƒΡ‡Π°ΠΉΠ½Ρ‹Π΅ изобраТСния"
290 290
291 291 #: templates/boards/base.html:42
292 292 msgid "random"
293 293 msgstr "случайныС"
294 294
295 295 #: templates/boards/base.html:44
296 296 msgid "favorites"
297 297 msgstr "ΠΈΠ·Π±Ρ€Π°Π½Π½ΠΎΠ΅"
298 298
299 299 #: templates/boards/base.html:48 templates/boards/base.html.py:49
300 300 #: templates/boards/notifications.html:8
301 301 msgid "Notifications"
302 302 msgstr "УвСдомлСния"
303 303
304 304 #: templates/boards/base.html:56 templates/boards/settings.html:8
305 305 msgid "Settings"
306 306 msgstr "Настройки"
307 307
308 308 #: templates/boards/base.html:59
309 309 msgid "Loading..."
310 310 msgstr "Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ°..."
311 311
312 312 #: templates/boards/base.html:71
313 313 msgid "Admin"
314 314 msgstr "АдминистрированиС"
315 315
316 316 #: templates/boards/base.html:73
317 317 #, python-format
318 318 msgid "Speed: %(ppd)s posts per day"
319 319 msgstr "Π‘ΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ: %(ppd)s сообщСний Π² дСнь"
320 320
321 321 #: templates/boards/base.html:75
322 322 msgid "Up"
323 323 msgstr "Π’Π²Π΅Ρ€Ρ…"
324 324
325 325 #: templates/boards/feed.html:45
326 326 msgid "No posts exist. Create the first one!"
327 327 msgstr "НСт сообщСний. Π‘ΠΎΠ·Π΄Π°ΠΉΡ‚Π΅ ΠΏΠ΅Ρ€Π²ΠΎΠ΅!"
328 328
329 329 #: templates/boards/post.html:33
330 330 msgid "Open"
331 331 msgstr "ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ"
332 332
333 333 #: templates/boards/post.html:35 templates/boards/post.html.py:46
334 334 msgid "Reply"
335 335 msgstr "ΠžΡ‚Π²Π΅Ρ‚ΠΈΡ‚ΡŒ"
336 336
337 337 #: templates/boards/post.html:41
338 338 msgid " in "
339 339 msgstr " Π² "
340 340
341 341 #: templates/boards/post.html:51
342 342 msgid "Edit"
343 343 msgstr "Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ"
344 344
345 345 #: templates/boards/post.html:53
346 346 msgid "Edit thread"
347 347 msgstr "Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Ρ‚Π΅ΠΌΡƒ"
348 348
349 349 #: templates/boards/post.html:91
350 350 msgid "Replies"
351 351 msgstr "ΠžΡ‚Π²Π΅Ρ‚Ρ‹"
352 352
353 353 #: templates/boards/post.html:103
354 354 #, python-format
355 355 msgid "%(count)s image"
356 356 msgid_plural "%(count)s images"
357 357 msgstr[0] "%(count)s ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅"
358 358 msgstr[1] "%(count)s изобраТСния"
359 359 msgstr[2] "%(count)s ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ"
360 360
361 361 #: templates/boards/rss/post.html:5
362 362 msgid "Post image"
363 363 msgstr "Π˜Π·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ сообщСния"
364 364
365 365 #: templates/boards/settings.html:15
366 366 msgid "You are moderator."
367 367 msgstr "Π’Ρ‹ ΠΌΠΎΠ΄Π΅Ρ€Π°Ρ‚ΠΎΡ€."
368 368
369 369 #: templates/boards/settings.html:19
370 370 msgid "Hidden tags:"
371 371 msgstr "Π‘ΠΊΡ€Ρ‹Ρ‚Ρ‹Π΅ ΠΌΠ΅Ρ‚ΠΊΠΈ:"
372 372
373 373 #: templates/boards/settings.html:25
374 374 msgid "No hidden tags."
375 375 msgstr "НСт скрытых ΠΌΠ΅Ρ‚ΠΎΠΊ."
376 376
377 377 #: templates/boards/settings.html:34
378 378 msgid "Save"
379 379 msgstr "Π‘ΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ"
380 380
381 381 #: templates/boards/staticpages/banned.html:6
382 382 msgid "Banned"
383 383 msgstr "Π—Π°Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²Π°Π½"
384 384
385 385 #: templates/boards/staticpages/banned.html:11
386 386 msgid "Your IP address has been banned. Contact the administrator"
387 387 msgstr "Π’Π°Ρˆ IP адрСс Π±Ρ‹Π» Π·Π°Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²Π°Π½. Π‘Π²ΡΠΆΠΈΡ‚Π΅ΡΡŒ с администратором"
388 388
389 389 #: templates/boards/staticpages/help.html:6
390 390 #: templates/boards/staticpages/help.html:10
391 391 msgid "Syntax"
392 392 msgstr "Бинтаксис"
393 393
394 394 #: templates/boards/staticpages/help.html:11
395 395 msgid "Italic text"
396 396 msgstr "ΠšΡƒΡ€ΡΠΈΠ²Π½Ρ‹ΠΉ тСкст"
397 397
398 398 #: templates/boards/staticpages/help.html:12
399 399 msgid "Bold text"
400 400 msgstr "ΠŸΠΎΠ»ΡƒΠΆΠΈΡ€Π½Ρ‹ΠΉ тСкст"
401 401
402 402 #: templates/boards/staticpages/help.html:13
403 403 msgid "Spoiler"
404 404 msgstr "Π‘ΠΏΠΎΠΉΠ»Π΅Ρ€"
405 405
406 406 #: templates/boards/staticpages/help.html:14
407 407 msgid "Link to a post"
408 408 msgstr "Бсылка Π½Π° сообщСниС"
409 409
410 410 #: templates/boards/staticpages/help.html:15
411 411 msgid "Strikethrough text"
412 412 msgstr "Π—Π°Ρ‡Π΅Ρ€ΠΊΠ½ΡƒΡ‚Ρ‹ΠΉ тСкст"
413 413
414 414 #: templates/boards/staticpages/help.html:16
415 415 msgid "Comment"
416 416 msgstr "ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ"
417 417
418 418 #: templates/boards/staticpages/help.html:17
419 419 #: templates/boards/staticpages/help.html:18
420 420 msgid "Quote"
421 421 msgstr "Π¦ΠΈΡ‚Π°Ρ‚Π°"
422 422
423 423 #: templates/boards/staticpages/help.html:21
424 424 msgid "You can try pasting the text and previewing the result here:"
425 425 msgstr "Π’Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΠΎΠΏΡ€ΠΎΠ±ΠΎΠ²Π°Ρ‚ΡŒ Π²ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ тСкст ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ здСсь:"
426 426
427 427 #: templates/boards/thread.html:14
428 428 msgid "Normal"
429 429 msgstr "ΠΠΎΡ€ΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ"
430 430
431 431 #: templates/boards/thread.html:15
432 432 msgid "Gallery"
433 433 msgstr "ГалСрСя"
434 434
435 435 #: templates/boards/thread.html:16
436 436 msgid "Tree"
437 437 msgstr "Π”Π΅Ρ€Π΅Π²ΠΎ"
438 438
439 439 #: templates/boards/thread.html:35
440 440 msgid "message"
441 441 msgid_plural "messages"
442 442 msgstr[0] "сообщСниС"
443 443 msgstr[1] "сообщСния"
444 444 msgstr[2] "сообщСний"
445 445
446 446 #: templates/boards/thread.html:38
447 447 msgid "image"
448 448 msgid_plural "images"
449 449 msgstr[0] "ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅"
450 450 msgstr[1] "изобраТСния"
451 451 msgstr[2] "ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ"
452 452
453 453 #: templates/boards/thread.html:40
454 454 msgid "Last update: "
455 455 msgstr "ПослСднСС обновлСниС: "
456 456
457 457 #: templates/boards/thread_gallery.html:36
458 458 msgid "No images."
459 459 msgstr "НСт ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ."
460 460
461 461 #: templates/boards/thread_normal.html:30
462 462 msgid "posts to bumplimit"
463 463 msgstr "сообщСний Π΄ΠΎ Π±Π°ΠΌΠΏΠ»ΠΈΠΌΠΈΡ‚Π°"
464 464
465 465 #: templates/boards/thread_normal.html:44
466 466 msgid "Reply to thread"
467 467 msgstr "ΠžΡ‚Π²Π΅Ρ‚ΠΈΡ‚ΡŒ Π² Ρ‚Π΅ΠΌΡƒ"
468 468
469 469 #: templates/boards/thread_normal.html:44
470 470 msgid "to message "
471 471 msgstr "Π½Π° сообщСниС"
472 472
473 473 #: templates/boards/thread_normal.html:59
474 474 msgid "Reset form"
475 475 msgstr "Π‘Π±Ρ€ΠΎΡΠΈΡ‚ΡŒ Ρ„ΠΎΡ€ΠΌΡƒ"
476 476
477 477 #: templates/search/search.html:17
478 478 msgid "Ok"
479 479 msgstr "Ок"
480 480
481 481 #: utils.py:120
482 482 #, python-format
483 msgid "File must be less than %s but is %s."
484 msgstr "Π€Π°ΠΉΠ» Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ ΠΌΠ΅Π½Π΅Π΅ %s, Π½ΠΎ Π΅Π³ΠΎ Ρ€Π°Π·ΠΌΠ΅Ρ€ %s."
483 msgid "Total file size must be less than %s but is %s."
484 msgstr "ΠžΠ±Ρ‰ΠΈΠΉ Ρ€Π°Π·ΠΌΠ΅Ρ€ Ρ„Π°ΠΉΠ»ΠΎΠ² Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ ΠΌΠ΅Π½Π΅Π΅ %s, Π½ΠΎ являСтся %s."
485 485
486 486 msgid "Please wait %(delay)d second before sending message"
487 487 msgid_plural "Please wait %(delay)d seconds before sending message"
488 488 msgstr[0] "ΠŸΠΎΠΆΠ°Π»ΡƒΠΉΡΡ‚Π° ΠΏΠΎΠ΄ΠΎΠΆΠ΄ΠΈΡ‚Π΅ %(delay)d сСкунду ΠΏΠ΅Ρ€Π΅Π΄ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΎΠΉ сообщСния"
489 489 msgstr[1] "ΠŸΠΎΠΆΠ°Π»ΡƒΠΉΡΡ‚Π° ΠΏΠΎΠ΄ΠΎΠΆΠ΄ΠΈΡ‚Π΅ %(delay)d сСкунды ΠΏΠ΅Ρ€Π΅Π΄ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΎΠΉ сообщСния"
490 490 msgstr[2] "ΠŸΠΎΠΆΠ°Π»ΡƒΠΉΡΡ‚Π° ΠΏΠΎΠ΄ΠΎΠΆΠ΄ΠΈΡ‚Π΅ %(delay)d сСкунд ΠΏΠ΅Ρ€Π΅Π΄ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΎΠΉ сообщСния"
491 491
492 492 msgid "New threads"
493 493 msgstr "НовыС Ρ‚Π΅ΠΌΡ‹"
494 494
495 495 #, python-format
496 msgid "Max file size is %(size)s."
497 msgstr "ΠœΠ°ΠΊΡΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ Ρ€Π°Π·ΠΌΠ΅Ρ€ Ρ„Π°ΠΉΠ»Π° %(size)s."
496 msgid "Max total file size is %(size)s."
497 msgstr "ΠœΠ°ΠΊΡΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΎΠ±Ρ‰ΠΈΠΉ Ρ€Π°Π·ΠΌΠ΅Ρ€ Ρ„Π°ΠΉΠ»ΠΎΠ² %(size)s."
498 498
499 499 msgid "Size of media:"
500 500 msgstr "Π Π°Π·ΠΌΠ΅Ρ€ ΠΌΠ΅Π΄ΠΈΠ°:"
501 501
502 502 msgid "Statistics"
503 503 msgstr "Бтатистика"
504 504
505 505 msgid "Invalid PoW."
506 506 msgstr "НСвСрный PoW."
507 507
508 508 msgid "Stale PoW."
509 509 msgstr "PoW устарСл."
510 510
511 511 msgid "Show"
512 512 msgstr "ΠŸΠΎΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ"
513 513
514 514 msgid "Hide"
515 515 msgstr "Π‘ΠΊΡ€Ρ‹Π²Π°Ρ‚ΡŒ"
516 516
517 517 msgid "Add to favorites"
518 518 msgstr "Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π² ΠΈΠ·Π±Ρ€Π°Π½Π½ΠΎΠ΅"
519 519
520 520 msgid "Remove from favorites"
521 521 msgstr "Π£Π±Ρ€Π°Ρ‚ΡŒ ΠΈΠ· ΠΈΠ·Π±Ρ€Π°Π½Π½ΠΎΠ³ΠΎ"
522 522
523 523 msgid "Monochrome"
524 524 msgstr "ΠœΠΎΠ½ΠΎΡ…Ρ€ΠΎΠΌΠ½Ρ‹ΠΉ"
525 525
526 526 msgid "Subsections: "
527 527 msgstr "ΠŸΠΎΠ΄Ρ€Π°Π·Π΄Π΅Π»Ρ‹: "
528 528
529 529 msgid "Change file source"
530 530 msgstr "Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ источник Ρ„Π°ΠΉΠ»Π°"
531 531
532 532 msgid "interesting"
533 533 msgstr "интСрСсноС"
534 534
535 535 msgid "images"
536 536 msgstr "изобраТСния"
537 537
538 538 msgid "Delete post"
539 539 msgstr "Π£Π΄Π°Π»ΠΈΡ‚ΡŒ пост"
540 540
541 541 msgid "Delete thread"
542 542 msgstr "Π£Π΄Π°Π»ΠΈΡ‚ΡŒ Ρ‚Π΅ΠΌΡƒ"
543 543
544 544 msgid "Messages per day/week/month:"
545 545 msgstr "Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠΉ Π·Π° дСнь/нСдСлю/мСсяц:"
546 546
547 547 msgid "Subscribe to thread"
548 548 msgstr "ΠŸΠΎΠ΄ΠΏΠΈΡΠ°Ρ‚ΡŒΡΡ Π½Π° Ρ‚Π΅ΠΌΡƒ"
549 549
550 550 msgid "Active threads:"
551 551 msgstr "АктивныС Ρ‚Π΅ΠΌΡ‹:"
552 552
553 553 msgid "No active threads today."
554 554 msgstr "БСгодня Π½Π΅Ρ‚ Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ… Ρ‚Π΅ΠΌ."
555 555
556 556 msgid "Insert URLs on separate lines."
557 557 msgstr "ВставляйтС ссылки Π½Π° ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Ρ… строках."
558 558
559 559 msgid "You can post no more than %(files)d file."
560 560 msgid_plural "You can post no more than %(files)d files."
561 561 msgstr[0] "Π’Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ Π½Π΅ Π±ΠΎΠ»Π΅Π΅ %(files)d Ρ„Π°ΠΉΠ»Π°."
562 562 msgstr[1] "Π’Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ Π½Π΅ Π±ΠΎΠ»Π΅Π΅ %(files)d Ρ„Π°ΠΉΠ»ΠΎΠ²."
563 563 msgstr[2] "Π’Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ Π½Π΅ Π±ΠΎΠ»Π΅Π΅ %(files)d Ρ„Π°ΠΉΠ»ΠΎΠ²."
564 564
565 565 #, python-format
566 566 msgid "Max file number is %(max_files)s."
567 567 msgstr "МаксимальноС количСство Ρ„Π°ΠΉΠ»ΠΎΠ² %(max_files)s."
568 568
569 569 msgid "Moderation"
570 570 msgstr "ΠœΠΎΠ΄Π΅Ρ€Π°Ρ†ΠΈΡ"
571 571
572 572 msgid "Check for duplicates"
573 573 msgstr "ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΡ‚ΡŒ Π½Π° Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ‚Ρ‹"
574 574
575 575 msgid "Some files are already present on the board."
576 576 msgstr "НСкоторыС Ρ„Π°ΠΉΠ»Ρ‹ ΡƒΠΆΠ΅ ΠΏΡ€ΠΈΡΡƒΡ‚ΡΡ‚Π²ΡƒΡŽΡ‚ Π½Π° Π±ΠΎΡ€Π΄Π΅."
577 577
578 578 msgid "Do not download URLs"
579 579 msgstr "НС Π·Π°Π³Ρ€ΡƒΠΆΠ°Ρ‚ΡŒ ссылки"
580 580
581 581 msgid "Ban and delete"
582 582 msgstr "Π—Π°Π±Π°Π½ΠΈΡ‚ΡŒ ΠΈ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ"
583 583
584 584 msgid "Are you sure?"
585 585 msgstr "Π’Ρ‹ ΡƒΠ²Π΅Ρ€Π΅Π½Ρ‹?"
586 586
587 587 msgid "Ban"
588 588 msgstr "Π—Π°Π±Π°Π½ΠΈΡ‚ΡŒ"
589 589
590 590 msgid "File process mode"
591 591 msgstr "Π Π΅ΠΆΠΈΠΌ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ Ρ„Π°ΠΉΠ»ΠΎΠ²"
592 592
593 593 msgid "Download or insert as URLs"
594 594 msgstr "Π—Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ ΠΈΠ»ΠΈ Π²ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ ΠΊΠ°ΠΊ ссылки"
595 595
596 596 msgid "Download"
597 597 msgstr "Π—Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ"
598 598
599 599 msgid "Download and check for uniqueness"
600 600 msgstr "Π—Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ Π½Π° ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ"
601 601
602 602 msgid "Insert as URLs"
603 603 msgstr "Π’ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ ΠΊΠ°ΠΊ ссылки"
604 604
605 605 msgid "Help"
606 606 msgstr "Π‘ΠΏΡ€Π°Π²ΠΊΠ°"
607 607
608 608 msgid "View available stickers:"
609 609 msgstr "ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ доступныС стикСры:"
610 610
611 611 msgid "Stickers"
612 612 msgstr "Π‘Ρ‚ΠΈΠΊΠ΅Ρ€Ρ‹"
613 613
614 614 msgid "Available by addresses:"
615 615 msgstr "Доступно ΠΏΠΎ адрСсам:"
616 616
617 617 msgid "Local stickers"
618 618 msgstr "Π›ΠΎΠΊΠ°Π»ΡŒΠ½Ρ‹Π΅ стикСры"
619 619
620 620 msgid "Global stickers"
621 621 msgstr "Π“Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹Π΅ стикСры"
622 622
623 623 msgid "Remove sticker"
624 624 msgstr "Π£Π΄Π°Π»ΠΈΡ‚ΡŒ стикСр"
625 625
626 626 msgid "Sticker Pack"
627 627 msgstr "Набор Π‘Ρ‚ΠΈΠΊΠ΅Ρ€ΠΎΠ²"
628 628
629 629 msgid "Tripcode should be specified to own a stickerpack."
630 630 msgstr "Для владСния Π½Π°Π±ΠΎΡ€ΠΎΠΌ стикСров Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΡƒΠΊΠ°Π·Π°Ρ‚ΡŒ Ρ‚Ρ€ΠΈΠΏΠΊΠΎΠ΄."
631 631
632 632 msgid "Title should be specified as a stickerpack name."
633 633 msgstr "Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ ΡƒΠΊΠ°Π·Π°Π½ Π² качСствС ΠΈΠΌΠ΅Π½ΠΈ Π½Π°Π±ΠΎΡ€Π° стикСров."
634 634
635 635 msgid "A sticker pack with this name already exists and is owned by another tripcode."
636 636 msgstr "Набор стикСров с Π΄Π°Π½Π½Ρ‹ΠΌ ΠΈΠΌΠ΅Π½Π΅ΠΌ ΡƒΠΆΠ΅ сущСствуСт ΠΈ ΠΏΡ€ΠΈΠ½Π°Π΄Π»Π΅ΠΆΠΈΡ‚ Π΄Ρ€ΡƒΠ³ΠΎΠΌΡƒ Ρ‚Ρ€ΠΈΠΏΠΊΠΎΠ΄Ρƒ."
637 637
638 638 msgid "This sticker pack can only be updated by an administrator."
639 639 msgstr "Π­Ρ‚ΠΎΡ‚ Π½Π°Π±ΠΎΡ€ стикСров ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ ΠΈΠ·ΠΌΠ΅Π½Ρ‘Π½ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ администратором."
640 640
641 641 msgid "To add a sticker, create a stickerpack thread using the title as a pack name, and a tripcode to own the pack. Then, add posts with title as a sticker name, and the same tripcode, to the thread. Their attachments would become stickers."
642 642 msgstr "Π§Ρ‚ΠΎΠ±Ρ‹ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ стикСр, создайтС Ρ‚Π΅ΠΌΡƒ-Π½Π°Π±ΠΎΡ€ с Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΎΠΌ Π² качСствС названия Π½Π°Π±ΠΎΡ€Π° стикСров, ΠΈ Ρ‚Ρ€ΠΈΠΏΠΊΠΎΠ΄ΠΎΠΌ для подтвСрТдСния владСния Π½Π°Π±ΠΎΡ€ΠΎΠΌ. Π—Π°Ρ‚Π΅ΠΌ, добавляйтС сообщСния с Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΎΠΌ Π² качСствС ΠΈΠΌΠ΅Π½ΠΈ стикСра, ΠΈ Ρ‚Π΅ΠΌ ΠΆΠ΅ Ρ‚Ρ€ΠΈΠΏΠΊΠΎΠ΄ΠΎΠΌΠΌ. Π˜Ρ… влоТСния станут стикСрами."
643 643
644 644 msgid "Inappropriate sticker pack name."
645 645 msgstr "НСдопустимоС имя Π½Π°Π±ΠΎΡ€Π° стикСров."
1 NO CONTENT: modified file, binary diff hidden
@@ -1,645 +1,645 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 10 "POT-Creation-Date: 2015-10-09 23:21+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:30
42 42 msgid "Type message here. Use formatting panel for more advanced usage."
43 43 msgstr ""
44 44 "Π’Π²Π΅Π΄Ρ–Ρ‚ΡŒ сюди повідомлСння. ΠšΠΎΡ€ΠΈΡΡ‚Π°ΠΉΡ‚Π΅ панСль для ΡΠΊΠ»Π°Π΄Π½Ρ–ΡˆΠΎΠ³ΠΎ форматування."
45 45
46 46 #: forms.py:31
47 47 msgid "music images i_dont_like_tags"
48 48 msgstr "ΠΌΡƒΠ·ΠΈΠΊΠ° зобраТСння ΠΌΡ–Ρ‚ΠΊΠΈ_Π½Π΅_ΠΏΠΎΡ‚Ρ€Ρ–Π±Π½Ρ–"
49 49
50 50 #: forms.py:33
51 51 msgid "Title"
52 52 msgstr "Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ"
53 53
54 54 #: forms.py:34
55 55 msgid "Text"
56 56 msgstr "ВСкст"
57 57
58 58 #: forms.py:35
59 59 msgid "Tag"
60 60 msgstr "ΠœΡ–Ρ‚ΠΊΠ°"
61 61
62 62 #: forms.py:36 templates/boards/base.html:40 templates/search/search.html:7
63 63 msgid "Search"
64 64 msgstr "ΠŸΠΎΡˆΡƒΠΊ"
65 65
66 66 #: forms.py:48
67 67 msgid "File 1"
68 68 msgstr "Π€Π°ΠΉΠ» 1"
69 69
70 70 #: forms.py:48
71 71 msgid "File 2"
72 72 msgstr "Π€Π°ΠΉΠ» 2"
73 73
74 74 #: forms.py:142
75 75 msgid "File URL"
76 76 msgstr "URL Ρ„Π°ΠΉΠ»Ρƒ"
77 77
78 78 #: forms.py:148
79 79 msgid "e-mail"
80 80 msgstr ""
81 81
82 82 #: forms.py:151
83 83 msgid "Additional threads"
84 84 msgstr "Π”ΠΎΠ΄Π°Ρ‚ΠΊΠΎΠ²Ρ– Π½ΠΈΡ‚ΠΊΠΈ"
85 85
86 86 #: forms.py:162
87 87 #, python-format
88 88 msgid "Title must have less than %s characters"
89 89 msgstr "Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ ΠΌΠ°Ρ” містити мСншС %s символів"
90 90
91 91 #: forms.py:172
92 92 #, python-format
93 93 msgid "Text must have less than %s characters"
94 94 msgstr "ВСкст ΠΌΠ°Ρ” Π±ΡƒΡ‚ΠΈ ΠΊΠΎΡ€ΠΎΡ‚ΡˆΠ΅ %s символів"
95 95
96 96 #: forms.py:192
97 97 msgid "Invalid URL"
98 98 msgstr "Π₯ΠΈΠ±Π½ΠΈΠΉ URL"
99 99
100 100 #: forms.py:213
101 101 msgid "Invalid additional thread list"
102 102 msgstr "Π₯ΠΈΠ±Π½ΠΈΠΉ ΠΏΠ΅Ρ€Π΅Π»Ρ–ΠΊ Π΄ΠΎΠ΄Π°Ρ‚ΠΊΠΎΠ²ΠΈΡ… Π½ΠΈΡ‚ΠΎΠΊ"
103 103
104 104 #: forms.py:258
105 105 msgid "Either text or file must be entered."
106 106 msgstr "Π‘Π»Ρ–Π΄ Π΄ΠΎΠ΄Π°Ρ‚ΠΈ тСкст Π°Π±ΠΎ Ρ„Π°ΠΉΠ»."
107 107
108 108 #: forms.py:317 templates/boards/all_threads.html:153
109 109 #: templates/boards/rss/post.html:10 templates/boards/tags.html:6
110 110 msgid "Tags"
111 111 msgstr "ΠœΡ–Ρ‚ΠΊΠΈ"
112 112
113 113 #: forms.py:324
114 114 msgid "Inappropriate characters in tags."
115 115 msgstr "НСприйнятні символи Ρƒ ΠΌΡ–Ρ‚ΠΊΠ°Ρ…."
116 116
117 117 #: forms.py:344
118 118 msgid "Need at least one section."
119 119 msgstr "ΠœΡƒΡΠΈΡ‚ΡŒ Π±ΡƒΡ‚ΠΈ Ρ…ΠΎΡ‡Π° Π± ΠΎΠ΄ΠΈΠ½ Ρ€ΠΎΠ·Π΄Ρ–Π»."
120 120
121 121 #: forms.py:356
122 122 msgid "Theme"
123 123 msgstr "Π’Π΅ΠΌΠ°"
124 124
125 125 #: forms.py:357
126 126 msgid "Image view mode"
127 127 msgstr "Π Π΅ΠΆΠΈΠΌ пСрСгляду Π·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΡŒ"
128 128
129 129 #: forms.py:358
130 130 msgid "User name"
131 131 msgstr "Им'я користувача"
132 132
133 133 #: forms.py:359
134 134 msgid "Time zone"
135 135 msgstr "Часовий пояс"
136 136
137 137 #: forms.py:365
138 138 msgid "Inappropriate characters."
139 139 msgstr "НСприйнятні символи."
140 140
141 141 #: templates/boards/404.html:6
142 142 msgid "Not found"
143 143 msgstr "Загубилося"
144 144
145 145 #: templates/boards/404.html:12
146 146 msgid "This page does not exist"
147 147 msgstr "НСма ΠΏΡ€Π°Π²Π΄ΠΎΠ½ΡŒΠΊΠΈ Π½Π° світі, ΠΎΠΉ нСма…"
148 148
149 149 #: templates/boards/all_threads.html:35
150 150 msgid "Details"
151 151 msgstr "Π”Π΅Ρ‚Π°Π»Ρ–"
152 152
153 153 #: templates/boards/all_threads.html:69
154 154 msgid "Edit tag"
155 155 msgstr "Π—ΠΌΡ–Π½ΠΈΡ‚ΠΈ ΠΌΡ–Ρ‚ΠΊΡƒ"
156 156
157 157 #: templates/boards/all_threads.html:76
158 158 #, python-format
159 159 msgid "%(count)s active thread"
160 160 msgid_plural "%(count)s active threads"
161 161 msgstr[0] "%(count)s Π°ΠΊΡ‚ΠΈΠ²Π½Π° Π½ΠΈΡ‚ΠΊΠ°"
162 162 msgstr[1] "%(count)s Π°ΠΊΡ‚ΠΈΠ²Π½Ρ– Π½ΠΈΡ‚ΠΊΠΈ"
163 163 msgstr[2] "%(count)s Π°ΠΊΡ‚ΠΈΠ²Π½ΠΈΡ… Π½ΠΈΡ‚ΠΎΠΊ"
164 164
165 165 #: templates/boards/all_threads.html:76
166 166 #, python-format
167 167 msgid "%(count)s thread in bumplimit"
168 168 msgid_plural "%(count)s threads in bumplimit"
169 169 msgstr[0] "%(count)s Π½ΠΈΡ‚ΠΊΠ° Π² бампляматі"
170 170 msgstr[1] "%(count)s Π½ΠΈΡ‚ΠΊΠΈ Π² бампляматі"
171 171 msgstr[2] "%(count)s Π½ΠΈΡ‚ΠΎΠΊ Ρƒ бампляматі"
172 172
173 173 #: templates/boards/all_threads.html:77
174 174 #, python-format
175 175 msgid "%(count)s archived thread"
176 176 msgid_plural "%(count)s archived thread"
177 177 msgstr[0] "%(count)s Π°Ρ€Ρ…Ρ–Π²Π½Π° Π½ΠΈΡ‚ΠΊΠ°"
178 178 msgstr[1] "%(count)s Π°Ρ€Ρ…Ρ–Π²Π½Ρ– Π½ΠΈΡ‚ΠΊΠΈ"
179 179 msgstr[2] "%(count)s Π°Ρ€Ρ…Ρ–Π²Π½ΠΈΡ… Π½ΠΈΡ‚ΠΎΠΊ"
180 180
181 181 #: templates/boards/all_threads.html:78 templates/boards/post.html:102
182 182 #, python-format
183 183 #| msgid "%(count)s message"
184 184 #| msgid_plural "%(count)s messages"
185 185 msgid "%(count)s message"
186 186 msgid_plural "%(count)s messages"
187 187 msgstr[0] "%(count)s повідомлСння"
188 188 msgstr[1] "%(count)s повідомлСння"
189 189 msgstr[2] "%(count)s ΠΏΠΎΠ²Ρ–Π΄ΠΎΠΌΠ»Π΅Π½ΡŒ"
190 190
191 191 #: templates/boards/all_threads.html:95 templates/boards/feed.html:30
192 192 #: templates/boards/notifications.html:17 templates/search/search.html:26
193 193 msgid "Previous page"
194 194 msgstr "ΠŸΠΎΠΏΡ”Ρ€Ρ”Π΄Π½Ρ сторінка"
195 195
196 196 #: templates/boards/all_threads.html:109
197 197 #, python-format
198 198 msgid "Skipped %(count)s reply. Open thread to see all replies."
199 199 msgid_plural "Skipped %(count)s replies. Open thread to see all replies."
200 200 msgstr[0] "ΠŸΡ€ΠΎΠΏΡƒΡ‰Π΅Π½ΠΎ %(count)s Π²Ρ–Π΄ΠΏΠΎΠ²Ρ–Π΄ΡŒ. Π ΠΎΠ·Π³ΠΎΡ€Π½Ρ–Ρ‚ΡŒ Π½ΠΈΡ‚ΠΊΡƒ, Ρ‰ΠΎΠ± ΠΏΠΎΠ±Π°Ρ‡ΠΈΡ‚ΠΈ всі Π²Ρ–Π΄ΠΏΠΎΠ²Ρ–Π΄Ρ–."
201 201 msgstr[1] ""
202 202 "ΠŸΡ€ΠΎΠΏΡƒΡ‰Π΅Π½ΠΎ %(count)s Π²Ρ–Π΄ΠΏΠΎΠ²Ρ–Π΄Ρ–. Π ΠΎΠ·Π³ΠΎΡ€Π½Ρ–Ρ‚ΡŒ Π½ΠΈΡ‚ΠΊΡƒ, Ρ‰ΠΎΠ± ΠΏΠΎΠ±Π°Ρ‡ΠΈΡ‚ΠΈ всі Π²Ρ–Π΄ΠΏΠΎΠ²Ρ–Π΄Ρ–."
203 203 msgstr[2] ""
204 204 "ΠŸΡ€ΠΎΠΏΡƒΡ‰Π΅Π½ΠΎ %(count)s Π²Ρ–Π΄ΠΏΠΎΠ²Ρ–Π΄Π΅ΠΉ. Π ΠΎΠ·Π³ΠΎΡ€Π½Ρ–Ρ‚ΡŒ Π½ΠΈΡ‚ΠΊΡƒ, Ρ‰ΠΎΠ± ΠΏΠΎΠ±Π°Ρ‡ΠΈΡ‚ΠΈ всі Π²Ρ–Π΄ΠΏΠΎΠ²Ρ–Π΄Ρ–."
205 205
206 206 #: templates/boards/all_threads.html:127 templates/boards/feed.html:40
207 207 #: templates/boards/notifications.html:27 templates/search/search.html:37
208 208 msgid "Next page"
209 209 msgstr "Наступна сторінка"
210 210
211 211 #: templates/boards/all_threads.html:132
212 212 msgid "No threads exist. Create the first one!"
213 213 msgstr "НСма ΠΏΡ€Π°Π²Π΄ΠΎΠ½ΡŒΠΊΠΈ Π½Π° світі. Π—Π°Ρ‡Π½Ρ–ΠΌΠΎ Ρ—Ρ—!"
214 214
215 215 #: templates/boards/all_threads.html:138
216 216 msgid "Create new thread"
217 217 msgstr "БплСсти Π½ΠΎΠ²Ρƒ Π½ΠΈΡ‚ΠΊΡƒ"
218 218
219 219 #: templates/boards/all_threads.html:143 templates/boards/preview.html:16
220 220 #: templates/boards/thread_normal.html:51
221 221 msgid "Post"
222 222 msgstr "Надіслати"
223 223
224 224 #: templates/boards/all_threads.html:144 templates/boards/preview.html:6
225 225 #: templates/boards/staticpages/help.html:21
226 226 #: templates/boards/thread_normal.html:52
227 227 msgid "Preview"
228 228 msgstr "ΠŸΠΎΠΏΠ΅Ρ€Π΅Π³Π»ΡΠ΄"
229 229
230 230 #: templates/boards/all_threads.html:149
231 231 msgid "Tags must be delimited by spaces. Text or image is required."
232 232 msgstr ""
233 233 "ΠœΡ–Ρ‚ΠΊΠΈ Ρ€ΠΎΠ·ΠΌΠ΅ΠΆΡƒΠ²Π°Ρ‚ΠΈ ΠΏΡ€ΠΎΠ±Ρ–Π»Π°ΠΌΠΈ. ВСкст Ρ‡ΠΈ зобраТСння Ρ” ΠΎΠ±ΠΎΠ²'язковими."
234 234
235 235 #: templates/boards/all_threads.html:152 templates/boards/thread_normal.html:58
236 236 msgid "Text syntax"
237 237 msgstr "Бинтаксис тСксту"
238 238
239 239 #: templates/boards/all_threads.html:166 templates/boards/feed.html:53
240 240 msgid "Pages:"
241 241 msgstr "Π‘Ρ‚ΠΎΡ€Ρ–Π½ΠΊΠΈ:"
242 242
243 243 #: templates/boards/authors.html:6 templates/boards/authors.html.py:12
244 244 msgid "Authors"
245 245 msgstr "Автори"
246 246
247 247 #: templates/boards/authors.html:26
248 248 msgid "Distributed under the"
249 249 msgstr "Π ΠΎΠ·ΠΏΠΎΠ²ΡΡŽΠ΄ΠΆΡƒΡ”Ρ‚ΡŒΡΡ ΠΏΡ–Π΄ Π»Ρ–Ρ†Π΅Π½Π·Ρ–Ρ”ΡŽ"
250 250
251 251 #: templates/boards/authors.html:28
252 252 msgid "license"
253 253 msgstr ""
254 254
255 255 #: templates/boards/authors.html:30
256 256 msgid "Repository"
257 257 msgstr "Π Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€Ρ–ΠΉ"
258 258
259 259 #: templates/boards/base.html:14 templates/boards/base.html.py:41
260 260 msgid "Feed"
261 261 msgstr "Π‘Ρ‚Ρ€Ρ–Ρ‡ΠΊΠ°"
262 262
263 263 #: templates/boards/base.html:31
264 264 msgid "All threads"
265 265 msgstr "Усі Π½ΠΈΡ‚ΠΊΠΈ"
266 266
267 267 #: templates/boards/base.html:37
268 268 msgid "Add tags"
269 269 msgstr "Π”ΠΎΠ΄Π°Ρ‚ΠΈ ΠΌΡ–Ρ‚ΠΊΠΈ"
270 270
271 271 #: templates/boards/base.html:39
272 272 msgid "Tag management"
273 273 msgstr "ΠšΠ΅Ρ€ΡƒΠ²Π°Π½Π½Ρ ΠΌΡ–Ρ‚ΠΊΠ°ΠΌΠΈ"
274 274
275 275 #: templates/boards/base.html:39
276 276 msgid "tags"
277 277 msgstr "ΠΌΡ–Ρ‚ΠΊΠΈ"
278 278
279 279 #: templates/boards/base.html:40
280 280 msgid "search"
281 281 msgstr "ΠΏΠΎΡˆΡƒΠΊ"
282 282
283 283 #: templates/boards/base.html:41 templates/boards/feed.html:11
284 284 msgid "feed"
285 285 msgstr "стрічка"
286 286
287 287 #: templates/boards/base.html:42 templates/boards/random.html:6
288 288 msgid "Random images"
289 289 msgstr "Π’ΠΈΠΏΠ°Π΄ΠΊΠΎΠ²Ρ– зобраТСння"
290 290
291 291 #: templates/boards/base.html:42
292 292 msgid "random"
293 293 msgstr "Π²ΠΈΠΏΠ°Π΄ΠΊΠΎΠ²Ρ–"
294 294
295 295 #: templates/boards/base.html:44
296 296 msgid "favorites"
297 297 msgstr "ΡƒΠ»ΡŽΠ±Π»Π΅Π½Π΅"
298 298
299 299 #: templates/boards/base.html:48 templates/boards/base.html.py:49
300 300 #: templates/boards/notifications.html:8
301 301 msgid "Notifications"
302 302 msgstr "БповіщСння"
303 303
304 304 #: templates/boards/base.html:56 templates/boards/settings.html:8
305 305 msgid "Settings"
306 306 msgstr "ΠΠ°Π»Π°ΡˆΡ‚ΡƒΠ²Π°Π½Π½Ρ"
307 307
308 308 #: templates/boards/base.html:59
309 309 msgid "Loading..."
310 310 msgstr "ЗавантаТСння..."
311 311
312 312 #: templates/boards/base.html:71
313 313 msgid "Admin"
314 314 msgstr "Адміністрування"
315 315
316 316 #: templates/boards/base.html:73
317 317 #, python-format
318 318 msgid "Speed: %(ppd)s posts per day"
319 319 msgstr "Π₯ΡƒΡ‚ΠΊΡ–ΡΡ‚ΡŒ: %(ppd)s ΠΏΠΎΠ²Ρ–Π΄ΠΎΠΌΠ»Π΅Π½ΡŒ Π½Π° дСнь"
320 320
321 321 #: templates/boards/base.html:75
322 322 msgid "Up"
323 323 msgstr "Π”ΠΎΠ³ΠΎΡ€ΠΈ"
324 324
325 325 #: templates/boards/feed.html:45
326 326 msgid "No posts exist. Create the first one!"
327 327 msgstr "Π©Π΅ Π½Π΅ΠΌΠ° ΠΏΠΎΠ²Ρ–Π΄ΠΎΠΌΠ»Π΅Π½ΡŒ. Π—Π°Ρ‡Π½Ρ–ΠΌΠΎ!"
328 328
329 329 #: templates/boards/post.html:33
330 330 msgid "Open"
331 331 msgstr "Π’Ρ–Π΄ΠΊΡ€ΠΈΡ‚ΠΈ"
332 332
333 333 #: templates/boards/post.html:35 templates/boards/post.html.py:46
334 334 msgid "Reply"
335 335 msgstr "Відповісти"
336 336
337 337 #: templates/boards/post.html:41
338 338 msgid " in "
339 339 msgstr " Ρƒ "
340 340
341 341 #: templates/boards/post.html:51
342 342 msgid "Edit"
343 343 msgstr "Π—ΠΌΡ–Π½ΠΈΡ‚ΠΈ"
344 344
345 345 #: templates/boards/post.html:53
346 346 msgid "Edit thread"
347 347 msgstr "Π—ΠΌΡ–Π½ΠΈΡ‚ΠΈ Π½ΠΈΡ‚ΠΊΡƒ"
348 348
349 349 #: templates/boards/post.html:91
350 350 msgid "Replies"
351 351 msgstr "Π’Ρ–Π΄ΠΏΠΎΠ²Ρ–Π΄Ρ–"
352 352
353 353 #: templates/boards/post.html:103
354 354 #, python-format
355 355 msgid "%(count)s image"
356 356 msgid_plural "%(count)s images"
357 357 msgstr[0] "%(count)s зобраТСння"
358 358 msgstr[1] "%(count)s зобраТСння"
359 359 msgstr[2] "%(count)s Π·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΡŒ"
360 360
361 361 #: templates/boards/rss/post.html:5
362 362 msgid "Post image"
363 363 msgstr "ЗобраТСння повідомлСння"
364 364
365 365 #: templates/boards/settings.html:15
366 366 msgid "You are moderator."
367 367 msgstr "Π’ΠΈ ΠΌΠΎΠ΄Π΅Ρ€Π°Ρ‚ΠΎΡ€."
368 368
369 369 #: templates/boards/settings.html:19
370 370 msgid "Hidden tags:"
371 371 msgstr "ΠŸΡ€ΠΈΡ…ΠΎΠ²Π°Π½Ρ– ΠΌΡ–Ρ‚ΠΊΠΈ:"
372 372
373 373 #: templates/boards/settings.html:25
374 374 msgid "No hidden tags."
375 375 msgstr "НСма ΠΏΡ€ΠΈΡ…ΠΎΠ²Π°Π½ΠΈΡ… ΠΌΡ–Ρ‚ΠΎΠΊ."
376 376
377 377 #: templates/boards/settings.html:34
378 378 msgid "Save"
379 379 msgstr "Π—Π±Π΅Ρ€Π΅Π³Ρ‚ΠΈ"
380 380
381 381 #: templates/boards/staticpages/banned.html:6
382 382 msgid "Banned"
383 383 msgstr "Π—Π°Π±Π»ΠΎΠΊΠΎΠ²Π°Π½ΠΎ"
384 384
385 385 #: templates/boards/staticpages/banned.html:11
386 386 msgid "Your IP address has been banned. Contact the administrator"
387 387 msgstr "Π’Π°ΡˆΡƒ IP-адрСсу Π·Π°Π±Π»ΠΎΠΊΠΎΠ²Π°Π½ΠΎ. Π—Π°Ρ‚Π΅Π»Π΅Ρ„ΠΎΠ½ΡƒΠΉΡ‚Π΅ Π΄ΠΎ спортлото"
388 388
389 389 #: templates/boards/staticpages/help.html:6
390 390 #: templates/boards/staticpages/help.html:10
391 391 msgid "Syntax"
392 392 msgstr "Бинтаксис"
393 393
394 394 #: templates/boards/staticpages/help.html:11
395 395 msgid "Italic text"
396 396 msgstr "ΠšΡƒΡ€ΡΠΈΠ²Π½ΠΈΠΉ тСкст"
397 397
398 398 #: templates/boards/staticpages/help.html:12
399 399 msgid "Bold text"
400 400 msgstr "Напівогрядний тСкст"
401 401
402 402 #: templates/boards/staticpages/help.html:13
403 403 msgid "Spoiler"
404 404 msgstr "Π‘ΠΏΠΎΠΉΠ»Π΅Ρ€"
405 405
406 406 #: templates/boards/staticpages/help.html:14
407 407 msgid "Link to a post"
408 408 msgstr "Посилання Π½Π° повідомлСння"
409 409
410 410 #: templates/boards/staticpages/help.html:15
411 411 msgid "Strikethrough text"
412 412 msgstr "ЗакрСслСний тСкст"
413 413
414 414 #: templates/boards/staticpages/help.html:16
415 415 msgid "Comment"
416 416 msgstr "ΠšΠΎΠΌΠ΅Π½Ρ‚Π°Ρ€"
417 417
418 418 #: templates/boards/staticpages/help.html:17
419 419 #: templates/boards/staticpages/help.html:18
420 420 msgid "Quote"
421 421 msgstr "Π¦ΠΈΡ‚Π°Ρ‚Π°"
422 422
423 423 #: templates/boards/staticpages/help.html:21
424 424 msgid "You can try pasting the text and previewing the result here:"
425 425 msgstr "ΠœΠΎΠΆΠ΅Ρ‚Π΅ спробувати вставити тСкст Ρ– ΠΏΠ΅Ρ€Π΅Π²Ρ–Ρ€ΠΈΡ‚ΠΈ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ Ρ‚ΡƒΡ‚:"
426 426
427 427 #: templates/boards/thread.html:14
428 428 msgid "Normal"
429 429 msgstr "Π—Π²ΠΈΡ‡Π°ΠΉΠ½ΠΈΠΉ"
430 430
431 431 #: templates/boards/thread.html:15
432 432 msgid "Gallery"
433 433 msgstr "ГалСрСя"
434 434
435 435 #: templates/boards/thread.html:16
436 436 msgid "Tree"
437 437 msgstr "Π’Ρ–Π½ΠΈΠΊ"
438 438
439 439 #: templates/boards/thread.html:35
440 440 msgid "message"
441 441 msgid_plural "messages"
442 442 msgstr[0] "повідомлСння"
443 443 msgstr[1] "повідомлСння"
444 444 msgstr[2] "ΠΏΠΎΠ²Ρ–Π΄ΠΎΠΌΠ»Π΅Π½ΡŒ"
445 445
446 446 #: templates/boards/thread.html:38
447 447 msgid "image"
448 448 msgid_plural "images"
449 449 msgstr[0] "зобраТСння"
450 450 msgstr[1] "зобраТСння"
451 451 msgstr[2] "Π·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΡŒ"
452 452
453 453 #: templates/boards/thread.html:40
454 454 msgid "Last update: "
455 455 msgstr "ΠžΡΡ‚Π°Π½Π½Ρ” оновлСння: "
456 456
457 457 #: templates/boards/thread_gallery.html:36
458 458 msgid "No images."
459 459 msgstr "НСма Π·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΡŒ."
460 460
461 461 #: templates/boards/thread_normal.html:30
462 462 msgid "posts to bumplimit"
463 463 msgstr "ΠΏΠΎΠ²Ρ–Π΄ΠΎΠΌΠ»Π΅Π½ΡŒ Π΄ΠΎ бамплямату"
464 464
465 465 #: templates/boards/thread_normal.html:44
466 466 msgid "Reply to thread"
467 467 msgstr "Відповісти Π΄ΠΎ Π½ΠΈΡ‚ΠΊΠΈ"
468 468
469 469 #: templates/boards/thread_normal.html:44
470 470 msgid "to message "
471 471 msgstr "Π½Π° повідомлСння"
472 472
473 473 #: templates/boards/thread_normal.html:59
474 474 msgid "Reset form"
475 475 msgstr "Π‘ΠΊΠΈΠ½ΡƒΡ‚ΠΈ Ρ„ΠΎΡ€ΠΌΡƒ"
476 476
477 477 #: templates/search/search.html:17
478 478 msgid "Ok"
479 479 msgstr "Π€Π°ΠΉΠ½ΠΎ"
480 480
481 481 #: utils.py:120
482 482 #, python-format
483 msgid "File must be less than %s but is %s."
484 msgstr "Π€Π°ΠΉΠ» ΠΌΡƒΡΠΈΡ‚ΡŒ Π±ΡƒΡ‚ΠΈ мСншС %s, Π°Π»Π΅ ΠΉΠΎΠ³ΠΎ Ρ€ΠΎΠ·ΠΌΡ–Ρ€ %s."
483 msgid "Total file size must be less than %s but is %s."
484 msgstr "Π—Π°Π³Π°Π»ΡŒΠ½ΠΈΠΉ Ρ€ΠΎΠ·ΠΌΡ–Ρ€ Ρ„Π°ΠΉΠ»Ρ–Π² ΠΌΡƒΡΠΈΡ‚ΡŒ Π±ΡƒΡ‚ΠΈ мСншС %s, Π°Π»Π΅ Ρ” %s."
485 485
486 486 msgid "Please wait %(delay)d second before sending message"
487 487 msgid_plural "Please wait %(delay)d seconds before sending message"
488 488 msgstr[0] "Π—Π°Ρ‡Π΅ΠΊΠ°ΠΉΡ‚Π΅, Π±ΡƒΠ΄ΡŒ ласка, %(delay)d сСкунду ΠΏΠ΅Ρ€Π΅Π΄ надсиланням повідомлСння"
489 489 msgstr[1] "Π—Π°Ρ‡Π΅ΠΊΠ°ΠΉΡ‚Π΅, Π±ΡƒΠ΄ΡŒ ласка, %(delay)d сСкунди ΠΏΠ΅Ρ€Π΅Π΄ надсиланням повідомлСння"
490 490 msgstr[2] "Π—Π°Ρ‡Π΅ΠΊΠ°ΠΉΡ‚Π΅, Π±ΡƒΠ΄ΡŒ ласка, %(delay)d сСкунд ΠΏΠ΅Ρ€Π΅Π΄ надсиланням повідомлСння"
491 491
492 492 msgid "New threads"
493 493 msgstr "Нові Π½ΠΈΡ‚ΠΊΠΈ"
494 494
495 495 #, python-format
496 msgid "Max file size is %(size)s."
497 msgstr "Максимальний Ρ€ΠΎΠ·ΠΌΡ–Ρ€ Ρ„Π°ΠΉΠ»Ρƒ %(size)s."
496 msgid "Max total file size is %(size)s."
497 msgstr "Максимальний загальний Ρ€ΠΎΠ·ΠΌΡ–Ρ€ Ρ„Π°ΠΉΠ»Ρ–Π² %(size)s."
498 498
499 499 msgid "Size of media:"
500 500 msgstr "Π ΠΎΠ·ΠΌΡ–Ρ€ посСрСдника:"
501 501
502 502 msgid "Statistics"
503 503 msgstr "Бтатистика"
504 504
505 505 msgid "Invalid PoW."
506 506 msgstr "Π₯ΠΈΠ±Π½ΠΈΠΉ PoW."
507 507
508 508 msgid "Stale PoW."
509 509 msgstr "PoW застарів."
510 510
511 511 msgid "Show"
512 512 msgstr "ΠŸΠΎΠΊΠ°Π·ΡƒΠ²Π°Ρ‚ΠΈ"
513 513
514 514 msgid "Hide"
515 515 msgstr "Π₯ΠΎΠ²Π°Ρ‚ΠΈ"
516 516
517 517 msgid "Add to favorites"
518 518 msgstr "Π― Ρ†Π΅ люблю"
519 519
520 520 msgid "Remove from favorites"
521 521 msgstr "Π’ΠΆΠ΅ Π½Π΅ люблю"
522 522
523 523 msgid "Monochrome"
524 524 msgstr "Π‘Π΅Π· Π±Π°Ρ€Π²"
525 525
526 526 msgid "Subsections: "
527 527 msgstr "ΠŸΡ–Π΄Ρ€ΠΎΠ·Π΄Ρ–Π»ΠΈ: "
528 528
529 529 msgid "Change file source"
530 530 msgstr "Π—ΠΌΡ–Π½ΠΈΡ‚ΠΈ Π΄ΠΆΠ΅Ρ€Π΅Π»ΠΎ Ρ„Π°ΠΉΠ»Ρƒ"
531 531
532 532 msgid "interesting"
533 533 msgstr "Ρ†Ρ–ΠΊΠ°Π²Π΅"
534 534
535 535 msgid "images"
536 536 msgstr "ΠΏΡ–Ρ‡ΠΊΡƒΡ€ΠΈ"
537 537
538 538 msgid "Delete post"
539 539 msgstr "Π’ΠΈΠ΄Π°Π»ΠΈΡ‚ΠΈ повідомлСння"
540 540
541 541 msgid "Delete thread"
542 542 msgstr "Π’ΠΈΡ€Π²Π°Ρ‚ΠΈ Π½ΠΈΡ‚ΠΊΡƒ"
543 543
544 544 msgid "Messages per day/week/month:"
545 545 msgstr "ΠŸΠΎΠ²Ρ–Π΄ΠΎΠΌΠ»Π΅Π½ΡŒ Π·Π° дСнь/Ρ‚ΠΈΠΆΠ΄Π΅Π½ΡŒ/Ρ‚ΠΈΠΆΠΌΡ–ΡΡΡ†ΡŒ:"
546 546
547 547 msgid "Subscribe to thread"
548 548 msgstr "Π‘Ρ‚Π΅ΠΆΠΈΡ‚ΠΈ Π·Π° Π½ΠΈΡ‚ΠΊΠΎΡŽ"
549 549
550 550 msgid "Active threads:"
551 551 msgstr "Активні Π½ΠΈΡ‚ΠΊΠΈ:"
552 552
553 553 msgid "No active threads today."
554 554 msgstr "Щось усі Π·Π°ΠΌΠΎΠ²ΠΊΠ»ΠΈ."
555 555
556 556 msgid "Insert URLs on separate lines."
557 557 msgstr "ВставляйтС посилання ΠΎΠΊΡ€Π΅ΠΌΠΈΠΌΠΈ рядками."
558 558
559 559 msgid "You can post no more than %(files)d file."
560 560 msgid_plural "You can post no more than %(files)d files."
561 561 msgstr[0] "Π’ΠΈ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ надіслати Π½Π΅ Π±Ρ–Π»ΡŒΡˆΠ΅ %(files)d Ρ„Π°ΠΉΠ»Ρƒ."
562 562 msgstr[1] "Π’Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ надіслати Π½Π΅ Π±Ρ–Π»ΡŒΡˆΠ΅ %(files)d Ρ„Π°ΠΉΠ»Ρ–Π²."
563 563 msgstr[2] "Π’Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ надіслати Π½Π΅ Π±Ρ–Π»ΡŒΡˆΠ΅ %(files)d Ρ„Π°ΠΉΠ»Ρ–Π²."
564 564
565 565 #, python-format
566 566 msgid "Max file number is %(max_files)s."
567 567 msgstr "Максимальна ΠΊΡ–Π»ΡŒΠΊΡ–ΡΡ‚ΡŒ Ρ„Π°ΠΉΠ»Ρ–Π² %(max_files)s."
568 568
569 569 msgid "Moderation"
570 570 msgstr "ΠœΠΎΠ΄Π΅Ρ€Π°Ρ†Ρ–Ρ"
571 571
572 572 msgid "Check for duplicates"
573 573 msgstr "ΠŸΠ΅Ρ€Π΅Π²Ρ–Ρ€ΡΡ‚ΠΈ Π½Π° Π΄ΡƒΠ±Π»Ρ–ΠΊΠ°Ρ‚ΠΈ"
574 574
575 575 msgid "Some files are already present on the board."
576 576 msgstr "ДСякі Ρ„Π°ΠΉΠ»ΠΈ Π²ΠΆΠ΅ Ρ” Π½Π° Π΄ΠΎΡˆΡ†Ρ–."
577 577
578 578 msgid "Do not download URLs"
579 579 msgstr "НС Π·Π°Π²Π°Π½Ρ‚Π°ΠΆΡƒΠ²Π°Ρ‚ΠΈ посилання"
580 580
581 581 msgid "Ban and delete"
582 582 msgstr "Π—Π°Π±Π»ΠΎΠΊΡƒΠ²Π°Ρ‚ΠΈ ΠΉ Π²ΠΈΠ΄Π°Π»ΠΈΡ‚ΠΈ"
583 583
584 584 msgid "Are you sure?"
585 585 msgstr "Π§ΠΈ Π²ΠΈ ΠΏΠ΅Π²Π½Ρ–?"
586 586
587 587 msgid "Ban"
588 588 msgstr "Π—Π°Π±Π»ΠΎΠΊΡƒΠ²Π°Ρ‚ΠΈ"
589 589
590 590 msgid "File process mode"
591 591 msgstr "Π Π΅ΠΆΠΈΠΌ ΠΎΠ±Ρ€ΠΎΠ±ΠΊΠΈ Ρ„Π°ΠΉΠ»Ρ–Π²"
592 592
593 593 msgid "Download or insert as URLs"
594 594 msgstr "Π—Π°Π²Π°Π½Ρ‚Π°ΠΆΠΈΡ‚ΠΈ Π°Π±ΠΎ вставити як посилання"
595 595
596 596 msgid "Download"
597 597 msgstr "Π—Π°Π²Π°Π½Ρ‚Π°ΠΆΠΈΡ‚ΠΈ"
598 598
599 599 msgid "Download and check for uniqueness"
600 600 msgstr "Π—Π°Π²Π°Π½Ρ‚Π°ΠΆΠΈΡ‚ΠΈ Ρ‚Π° ΠΏΠ΅Ρ€Π΅Π²Ρ–Ρ€ΠΈΡ‚ΠΈ Π½Π° ΡƒΠ½Ρ–ΠΊΠ°Π»ΡŒΠ½Ρ–ΡΡ‚ΡŒ"
601 601
602 602 msgid "Insert as URLs"
603 603 msgstr "Вставити як посилання"
604 604
605 605 msgid "Help"
606 606 msgstr "Π‘ΠΏΡ€Π°Π²ΠΊΠ°"
607 607
608 608 msgid "View available stickers:"
609 609 msgstr "ΠŸΠ΅Ρ€Π΅Π΄ΠΈΠ²ΠΈΡ‚ΠΈΡΡ доступні стікСри:"
610 610
611 611 msgid "Stickers"
612 612 msgstr "Π‘Ρ‚Ρ–ΠΊΠ΅Ρ€ΠΈ"
613 613
614 614 msgid "Available by addresses:"
615 615 msgstr "Доступно Π·Π° адрСсами:"
616 616
617 617 msgid "Local stickers"
618 618 msgstr "Π›ΠΎΠΊΠ°Π»ΡŒΠ½Ρ– стікСри"
619 619
620 620 msgid "Global stickers"
621 621 msgstr "Π“Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ– стікСри"
622 622
623 623 msgid "Remove sticker"
624 624 msgstr "Π’ΠΈΠ΄Π°Π»ΠΈΡ‚ΠΈ стікСр"
625 625
626 626 msgid "Sticker Pack"
627 627 msgstr "Набір Π‘Ρ‚Ρ–ΠΊΠ΅Ρ€Ρ–Π²"
628 628
629 629 msgid "Tripcode should be specified to own a stickerpack."
630 630 msgstr "Для володіння Π½Π°Π±ΠΎΡ€ΠΎΠΌ стікСрів Π½Π΅ΠΎΠ±Ρ…Ρ–Π΄Π½ΠΎ Π²ΠΊΠ°Π·Π°Ρ‚ΠΈ Ρ‚Ρ€Ρ–ΠΏΠΊΠΎΠ΄."
631 631
632 632 msgid "Title should be specified as a stickerpack name."
633 633 msgstr "Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ ΠΏΠΎΠ²ΠΈΠ½Π΅Π½ Π±ΡƒΡ‚ΠΈ Π²ΠΊΠ°Π·Π°Π½ΠΈΠΉ Π² якості Ρ–ΠΌΠ΅Π½Ρ– Π½Π°Π±ΠΎΡ€Ρƒ стікСрів."
634 634
635 635 msgid "A sticker pack with this name already exists and is owned by another tripcode."
636 636 msgstr "Набір стікСрів Π· Π²ΠΊΠ°Π·Π°Π½ΠΈΠΌ Ρ–ΠΌΠ΅Π½Π΅ΠΌ Π²ΠΆΠ΅ Ρ” Π² наявності Ρ‚Π° Π½Π°Π»Π΅ΠΆΠΈΡ‚ΡŒ Ρ–Π½ΡˆΠΎΠΌΡƒ Ρ‚Ρ€Ρ–ΠΏΠΊΠΎΠ΄Ρƒ."
637 637
638 638 msgid "This sticker pack can only be updated by an administrator."
639 639 msgstr "Π¦Π΅ΠΉ Π½Π°Π±Ρ–Ρ€ стікСрів ΠΌΠΎΠΆΠ΅ Π±ΡƒΡ‚ΠΈ Π·ΠΌΡ–Π½Π΅Π½ΠΈΠΉ лишС адміністратором."
640 640
641 641 msgid "To add a sticker, create a stickerpack thread using the title as a pack name, and a tripcode to own the pack. Then, add posts with title as a sticker name, and the same tripcode, to the thread. Their attachments would become stickers."
642 642 msgstr "Π©ΠΎΠ± Π΄ΠΎΠ΄Π°Ρ‚ΠΈ стікСр, ΡΡ‚Π²ΠΎΡ€Ρ–Ρ‚ΡŒ Ρ‚Π΅ΠΌΡƒ-Π½Π°Π±Ρ–Ρ€ Π· Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΎΠΌ Ρƒ якості Π½Π°Π±ΠΎΡ€Ρƒ стікСрів, Ρ‚Π° Ρ‚Ρ€Ρ–ΠΏΠΊΠΎΠ΄ΠΎΠΌ для підтвСрдТСння володіння Π½Π°Π±ΠΎΡ€ΠΎΠΌΡ‚. ΠŸΠΎΡ‚Ρ–ΠΌ, Π΄ΠΎΠ΄Π°Π²Π°ΠΉΡ‚Π΅ повідомлСння Π· Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΎΠΌ Ρƒ якості Ρ–ΠΌΠ΅Π½Ρ– стікСру Ρ‚Π° Π· Ρ‚ΠΈΠΌ самим Ρ‚Ρ€Ρ–ΠΏΠΊΠΎΠ΄ΠΎΠΌ. Π‡Ρ…Π½Ρ– вкладСння станут стікСрами."
643 643
644 644 msgid "Inappropriate sticker pack name."
645 645 msgstr "НСприпустимС Ρ–ΠΌ'я Π½Π°Π±ΠΎΡ€Ρƒ стікСрів."
@@ -1,210 +1,210 b''
1 1 {% extends "boards/paginated.html" %}
2 2
3 3 {% load i18n %}
4 4 {% load board %}
5 5 {% load static %}
6 6 {% load tz %}
7 7
8 8 {% block head %}
9 9 <meta name="robots" content="noindex">
10 10
11 11 {% if tag %}
12 12 <title>{{ tag.get_localized_name }} - {{ site_name }}</title>
13 13 {% else %}
14 14 <title>{{ site_name }}</title>
15 15 {% endif %}
16 16
17 17 {% if prev_page_link %}
18 18 <link rel="prev" href="{{ prev_page_link|safe }}" />
19 19 {% endif %}
20 20 {% if next_page_link %}
21 21 <link rel="next" href="{{ next_page_link|safe }}" />
22 22 {% endif %}
23 23
24 24 {% endblock %}
25 25
26 26 {% block content %}
27 27
28 28 {% get_current_language as LANGUAGE_CODE %}
29 29 {% get_current_timezone as TIME_ZONE %}
30 30
31 31 {% for banner in banners %}
32 32 <div class="post">
33 33 <div class="title">{{ banner.title }}</div>
34 34 <div>{{ banner.get_text|safe }}</div>
35 35 <div>{% trans 'Details' %}: <a href="{{ banner.post.get_absolute_url|safe }}">>>{{ banner.post.id }}</a></div>
36 36 </div>
37 37 {% endfor %}
38 38
39 39 {% if tag %}
40 40 <div class="tag_info" style="border-bottom: solid .5ex #{{ tag.get_color }}">
41 41 {% if random_image_post %}
42 42 <div class="tag-image">
43 43 {% with image=random_image_post.get_first_image %}
44 44 <a href="{{ random_image_post.get_absolute_url|safe }}"><img
45 45 src="{{ image.get_thumb_url|safe }}"
46 46 width="{{ image.get_preview_size.0 }}"
47 47 height="{{ image.get_preview_size.1 }}"
48 48 alt="{{ random_image_post.id }}"/></a>
49 49 {% endwith %}
50 50 </div>
51 51 {% endif %}
52 52 <div class="tag-text-data">
53 53 <h2>
54 54 /{{ tag.get_view|safe }}/
55 55 </h2>
56 56 {% if perms.change_tag %}
57 57 <div class="moderator_info"><a href="{% url 'admin:boards_tag_change' tag.id %}">{% trans 'Edit tag' %}</a></div>
58 58 {% endif %}
59 59 <p>
60 60 <form action="{% url 'tag' tag.get_name %}" method="post" class="post-button-form">
61 61 {% if is_favorite %}
62 62 <button name="method" value="unsubscribe" class="fav">β˜… {% trans "Remove from favorites" %}</button>
63 63 {% else %}
64 64 <button name="method" value="subscribe" class="not_fav">β˜… {% trans "Add to favorites" %}</button>
65 65 {% endif %}
66 66 </form>
67 67 &bull;
68 68 <form action="{% url 'tag' tag.get_name %}" method="post" class="post-button-form">
69 69 {% if is_hidden %}
70 70 <button name="method" value="unhide" class="fav">{% trans "Show" %}</button>
71 71 {% else %}
72 72 <button name="method" value="hide" class="not_fav">{% trans "Hide" %}</button>
73 73 {% endif %}
74 74 </form>
75 75 &bull;
76 76 <a href="{% url 'tag_gallery' tag.get_name %}">{% trans 'Gallery' %}</a>
77 77 </p>
78 78 {% if tag.get_description %}
79 79 <p>{{ tag.get_description|safe }}</p>
80 80 {% endif %}
81 81 <p>
82 82 {% with active_count=tag.get_active_thread_count bumplimit_count=tag.get_bumplimit_thread_count archived_count=tag.get_archived_thread_count %}
83 83 {% if active_count %}
84 84 ● {{ active_count }}&ensp;
85 85 {% endif %}
86 86 {% if bumplimit_count %}
87 87 ◍ {{ bumplimit_count }}&ensp;
88 88 {% endif %}
89 89 {% if archived_count %}
90 90 β—‹ {{ archived_count }}&ensp;
91 91 {% endif %}
92 92 {% endwith %}
93 93 β™₯ {{ tag.get_post_count }}
94 94 </p>
95 95 {% if tag.get_all_parents %}
96 96 <p>
97 97 {% for parent in tag.get_all_parents %}
98 98 {{ parent.get_view|safe }} &gt;
99 99 {% endfor %}
100 100 {{ tag.get_view|safe }}
101 101 </p>
102 102 {% endif %}
103 103 {% if tag.get_children.all %}
104 104 <p>
105 105 {% trans "Subsections: " %}
106 106 {% for child in tag.get_children.all %}
107 107 {{ child.get_view|safe }}{% if not forloop.last%}, {% endif %}
108 108 {% endfor %}
109 109 </p>
110 110 {% endif %}
111 111 </div>
112 112 </div>
113 113 {% endif %}
114 114
115 115 {% if threads %}
116 116 {% if prev_page_link %}
117 117 <div class="page_link">
118 118 <a href="{{ prev_page_link }}">&lt;&lt; {% trans "Previous page" %} &lt;&lt;</a>
119 119 </div>
120 120 {% endif %}
121 121
122 122 {% for thread in threads %}
123 123 <div class="thread">
124 124 {% post_view thread.get_opening_post thread=thread truncated=True need_open_link=True %}
125 125 {% if not thread.archived %}
126 126 {% with last_replies=thread.get_last_replies %}
127 127 {% if last_replies %}
128 128 {% with skipped_replies_count=thread.get_skipped_replies_count %}
129 129 {% if skipped_replies_count %}
130 130 <div class="skipped_replies">
131 131 <a href="{% url 'thread' thread.get_opening_post_id %}">
132 132 {% blocktrans count count=skipped_replies_count %}Skipped {{ count }} reply. Open thread to see all replies.{% plural %}Skipped {{ count }} replies. Open thread to see all replies.{% endblocktrans %}
133 133 </a>
134 134 </div>
135 135 {% endif %}
136 136 {% endwith %}
137 137 <div class="last-replies">
138 138 {% for post in last_replies %}
139 139 {% post_view post truncated=True %}
140 140 {% endfor %}
141 141 </div>
142 142 {% endif %}
143 143 {% endwith %}
144 144 {% endif %}
145 145 </div>
146 146 {% endfor %}
147 147
148 148 {% if next_page_link %}
149 149 <div class="page_link">
150 150 <a href="{{ next_page_link }}">&gt;&gt; {% trans "Next page" %} &gt;&gt;</a>
151 151 </div>
152 152 {% endif %}
153 153 {% else %}
154 154 <div class="post">
155 155 {% trans 'No threads exist. Create the first one!' %}</div>
156 156 {% endif %}
157 157
158 158 <div class="post-form-w">
159 159 <script src="{% static 'js/panel.js' %}"></script>
160 160 <div class="post-form" data-hasher="{% static 'js/3party/sha256.js' %}"
161 161 data-pow-script="{% static 'js/proof_of_work.js' %}">
162 162 <div class="form-title">{% trans "Create new thread" %}</div>
163 163 <div class="swappable-form-full">
164 164 <form enctype="multipart/form-data" method="post" id="form">{% csrf_token %}
165 165 {{ form.as_div }}
166 166 <div class="form-submit">
167 167 <input type="submit" value="{% trans "Post" %}"/>
168 168 <button id="preview-button" type="button" onclick="return false;">{% trans 'Preview' %}</button>
169 169 </div>
170 170 </form>
171 171 </div>
172 172 <div>
173 173 {% trans 'Tags must be delimited by spaces. Text or image is required.' %}
174 174 {% with size=max_file_size|filesizeformat %}
175 {% blocktrans %}Max file size is {{ size }}.{% endblocktrans %}
175 {% blocktrans %}Max total file size is {{ size }}.{% endblocktrans %}
176 176 {% endwith %}
177 177 {% blocktrans %}Max file number is {{ max_files }}.{% endblocktrans %}
178 178 </div>
179 179 <div id="preview-text"></div>
180 180 <div><a href="{% url "staticpage" name="help" %}">{% trans 'Help' %}</a></div>
181 181 </div>
182 182 </div>
183 183
184 184 <script src="{% static 'js/form.js' %}"></script>
185 185 <script src="{% static 'js/3party/jquery.blockUI.js' %}"></script>
186 186 <script src="{% static 'js/thread_create.js' %}"></script>
187 187
188 188 {% endblock %}
189 189
190 190 {% block metapanel %}
191 191
192 192 <span class="metapanel">
193 193 {% trans "Pages:" %}
194 194 [
195 195 {% with dividers=paginator.get_dividers %}
196 196 {% for page in paginator.get_divided_range %}
197 197 {% if page in dividers %}
198 198 …,
199 199 {% endif %}
200 200 <a
201 201 {% ifequal page current_page.number %}
202 202 class="current_page"
203 203 {% endifequal %}
204 204 href="{% page_url paginator page %}">{{ page }}</a>{% if not forloop.last %},{% endif %}
205 205 {% endfor %}
206 206 {% endwith %}
207 207 ]
208 208 </span>
209 209
210 210 {% endblock %}
@@ -1,81 +1,81 b''
1 1 {% extends "boards/thread.html" %}
2 2
3 3 {% load i18n %}
4 4 {% load static from staticfiles %}
5 5 {% load board %}
6 6 {% load tz %}
7 7
8 8 {% block thread_content %}
9 9 {% get_current_language as LANGUAGE_CODE %}
10 10 {% get_current_timezone as TIME_ZONE %}
11 11
12 12 <button id="quote-button">{% trans 'Quote' %}</button>
13 13
14 14 <div class="tag_info">
15 15 <h2>
16 16 <form action="{% url 'thread' opening_post.id %}" method="post" class="post-button-form">
17 17 {% csrf_token %}
18 18 {% if is_favorite %}
19 19 <button id="thread-fav-button" name="method" value="unsubscribe" class="fav">β˜…</button>
20 20 {% else %}
21 21 <button id="thread-fav-button" name="method" value="subscribe" class="not_fav">β˜…</button>
22 22 {% endif %}
23 23 </form>
24 24 {{ opening_post.get_title_or_text }}
25 25 </h2>
26 26 </div>
27 27
28 28 {% if bumpable and thread.has_post_limit %}
29 29 <div class="bar-bg">
30 30 <div class="bar-value" style="width:{{ bumplimit_progress }}%" id="bumplimit_progress">
31 31 </div>
32 32 <div class="bar-text">
33 33 <span id="left_to_limit">{{ posts_left }}</span> {% trans 'posts to bumplimit' %}
34 34 </div>
35 35 </div>
36 36 {% endif %}
37 37
38 38 <div class="thread">
39 39 {% for post in thread.get_viewable_replies %}
40 40 {% post_view post reply_link=True thread=thread %}
41 41 {% endfor %}
42 42 </div>
43 43
44 44 {% if not thread.is_archived %}
45 45 <div class="post-form-w">
46 46 <script src="{% static 'js/panel.js' %}"></script>
47 47 <div class="form-title">{% trans "Reply to thread" %} #{{ opening_post.id }}<span class="reply-to-message"> {% trans "to message " %} #<span id="reply-to-message-id"></span></span></div>
48 48 <div class="post-form" id="compact-form" data-hasher="{% static 'js/3party/sha256.js' %}"
49 49 data-pow-script="{% static 'js/proof_of_work.js' %}">
50 50 <div class="swappable-form-full">
51 51 <form enctype="multipart/form-data" method="post" id="form">{% csrf_token %}
52 52 <div class="compact-form-text"></div>
53 53 {{ form.as_div }}
54 54 <div class="form-submit">
55 55 <input type="submit" value="{% trans "Post" %}"/>
56 56 <button id="preview-button" type="button" onclick="return false;">{% trans 'Preview' %}</button>
57 57 </div>
58 58 </form>
59 59 </div>
60 60 <div id="preview-text"></div>
61 61 <div>
62 62 {% with size=max_file_size|filesizeformat %}
63 {% blocktrans %}Max file size is {{ size }}.{% endblocktrans %}
63 {% blocktrans %}Max total file size is {{ size }}.{% endblocktrans %}
64 64 {% endwith %}
65 65 {% blocktrans %}Max file number is {{ max_files }}.{% endblocktrans %}
66 66 </div>
67 67 <div><a href="{% url "staticpage" name="help" %}">
68 68 {% trans 'Help' %}</a></div>
69 69 <div><a href="#" onClick="resetForm(); return false;">{% trans 'Reset form' %}</a></div>
70 70 </div>
71 71 </div>
72 72
73 73 <script src="{% static 'js/form.js' %}"></script>
74 74 <script src="{% static 'js/jquery.form.min.js' %}"></script>
75 75 <script src="{% static 'js/3party/jquery.blockUI.js' %}"></script>
76 76 <script src="{% static 'js/thread.js' %}"></script>
77 77 <script src="{% static 'js/thread_update.js' %}"></script>
78 78 {% endif %}
79 79
80 80 <script src="{% static 'js/3party/centrifuge.js' %}"></script>
81 81 {% endblock %}
@@ -1,152 +1,152 b''
1 1 """
2 2 This module contains helper functions and helper classes.
3 3 """
4 4 import time
5 5 import uuid
6 6
7 7 import hashlib
8 8 import magic
9 9 import os
10 10 from django import forms
11 11 from django.core.cache import cache
12 12 from django.db.models import Model
13 13 from django.template.defaultfilters import filesizeformat
14 14 from django.utils import timezone
15 15 from django.utils.translation import ugettext_lazy as _
16 16
17 17 import boards
18 18 from neboard import settings
19 19 from boards.abstracts.constants import FILE_DIRECTORY
20 20 from boards.settings import get_bool
21 21
22 22 CACHE_KEY_DELIMITER = '_'
23 23
24 24 HTTP_FORWARDED = 'HTTP_X_FORWARDED_FOR'
25 25 META_REMOTE_ADDR = 'REMOTE_ADDR'
26 26
27 27 SETTING_MESSAGES = 'Messages'
28 28 SETTING_ANON_MODE = 'AnonymousMode'
29 29
30 30 ANON_IP = '127.0.0.1'
31 31
32 32 FILE_EXTENSION_DELIMITER = '.'
33 33
34 34
35 35 def is_anonymous_mode():
36 36 return get_bool(SETTING_MESSAGES, SETTING_ANON_MODE)
37 37
38 38
39 39 def get_client_ip(request):
40 40 if is_anonymous_mode():
41 41 ip = ANON_IP
42 42 else:
43 43 x_forwarded_for = request.META.get(HTTP_FORWARDED)
44 44 if x_forwarded_for:
45 45 ip = x_forwarded_for.split(',')[-1].strip()
46 46 else:
47 47 ip = request.META.get(META_REMOTE_ADDR)
48 48 return ip
49 49
50 50
51 51 # TODO The output format is not epoch because it includes microseconds
52 52 def datetime_to_epoch(datetime):
53 53 return int(time.mktime(timezone.localtime(
54 54 datetime,timezone.get_current_timezone()).timetuple())
55 55 * 1000000 + datetime.microsecond)
56 56
57 57
58 58 # TODO Test this carefully
59 59 def cached_result(key_method=None):
60 60 """
61 61 Caches method result in the Django's cache system, persisted by object name,
62 62 object name, model id if object is a Django model, args and kwargs if any.
63 63 """
64 64 def _cached_result(function):
65 65 def inner_func(obj, *args, **kwargs):
66 66 cache_key_params = [obj.__class__.__name__, function.__name__]
67 67
68 68 cache_key_params += args
69 69 for key, value in kwargs:
70 70 cache_key_params.append(key + ':' + value)
71 71
72 72 if isinstance(obj, Model):
73 73 cache_key_params.append(str(obj.id))
74 74
75 75 if key_method is not None:
76 76 cache_key_params += [str(arg) for arg in key_method(obj)]
77 77
78 78 cache_key = CACHE_KEY_DELIMITER.join(cache_key_params)
79 79
80 80 persisted_result = cache.get(cache_key)
81 81 if persisted_result is not None:
82 82 result = persisted_result
83 83 else:
84 84 result = function(obj, *args, **kwargs)
85 85 if result is not None:
86 86 cache.set(cache_key, result)
87 87
88 88 return result
89 89
90 90 return inner_func
91 91 return _cached_result
92 92
93 93
94 94 def get_file_hash(file) -> str:
95 95 md5 = hashlib.md5()
96 96 for chunk in file.chunks():
97 97 md5.update(chunk)
98 98 return md5.hexdigest()
99 99
100 100
101 101 def validate_file_size(size: int):
102 102 max_size = boards.settings.get_int('Forms', 'MaxFileSize')
103 103 if 0 < max_size < size:
104 104 raise forms.ValidationError(
105 _('File must be less than %s but is %s.')
105 _('Total file size must be less than %s but is %s.')
106 106 % (filesizeformat(max_size), filesizeformat(size)))
107 107
108 108
109 109 def get_extension(filename):
110 110 return filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
111 111
112 112
113 113 def get_upload_filename(model_instance, old_filename):
114 114 extension = get_extension(old_filename)
115 115 new_name = '{}.{}'.format(uuid.uuid4(), extension)
116 116
117 117 return os.path.join(FILE_DIRECTORY, new_name)
118 118
119 119
120 120 def get_file_mimetype(file) -> str:
121 121 buf = b''
122 122 for chunk in file.chunks():
123 123 buf += chunk
124 124
125 125 file_type = magic.from_buffer(buf, mime=True)
126 126 if file_type is None:
127 127 file_type = 'application/octet-stream'
128 128 elif type(file_type) == bytes:
129 129 file_type = file_type.decode()
130 130 return file_type
131 131
132 132
133 133 def get_domain(url: str) -> str:
134 134 """
135 135 Gets domain from an URL with random number of domain levels.
136 136 """
137 137 domain_parts = url.split('/')
138 138 if len(domain_parts) >= 2:
139 139 full_domain = domain_parts[2]
140 140 else:
141 141 full_domain = ''
142 142
143 143 return full_domain
144 144
145 145
146 146 def get_tripcode_from_text(text: str) -> str:
147 147 tripcode = ''
148 148 if text:
149 149 code = text + settings.SECRET_KEY
150 150 tripcode = hashlib.md5(code.encode()).hexdigest()
151 151 return tripcode
152 152
General Comments 0
You need to be logged in to leave comments. Login now