##// END OF EJS Templates
Clarified url download modes
neko259 -
r1922:5728b509 default
parent child Browse files
Show More
@@ -1,538 +1,559 b''
1 import hashlib
1 import hashlib
2 import logging
2 import logging
3 import re
3 import re
4 import time
4 import time
5 import traceback
5 import traceback
6
6
7 import pytz
7 import pytz
8
8
9 from PIL import Image
9 from PIL import Image
10
10
11 from django import forms
11 from django import forms
12 from django.core.files.uploadedfile import SimpleUploadedFile, UploadedFile
12 from django.core.files.uploadedfile import SimpleUploadedFile, UploadedFile
13 from django.forms.utils import ErrorList
13 from django.forms.utils import ErrorList
14 from django.utils.translation import ugettext_lazy as _, ungettext_lazy
14 from django.utils.translation import ugettext_lazy as _, ungettext_lazy
15 from django.core.files.images import get_image_dimensions
15 from django.core.files.images import get_image_dimensions
16 from django.core.cache import cache
16
17
17 import boards.settings as board_settings
18 import boards.settings as board_settings
18 import neboard
19 import neboard
19 from boards import utils
20 from boards import utils
20 from boards.abstracts.attachment_alias import get_image_by_alias
21 from boards.abstracts.attachment_alias import get_image_by_alias
21 from boards.abstracts.settingsmanager import get_settings_manager
22 from boards.abstracts.settingsmanager import get_settings_manager
22 from boards.forms.fields import UrlFileField
23 from boards.forms.fields import UrlFileField
23 from boards.mdx_neboard import formatters
24 from boards.mdx_neboard import formatters
24 from boards.models import Attachment
25 from boards.models import Attachment
25 from boards.models import Tag
26 from boards.models import Tag
26 from boards.models.attachment.downloaders import download, REGEX_MAGNET
27 from boards.models.attachment.downloaders import download, REGEX_MAGNET
27 from boards.models.post import TITLE_MAX_LENGTH
28 from boards.models.post import TITLE_MAX_LENGTH
28 from boards.utils import validate_file_size, get_file_mimetype, \
29 from boards.utils import validate_file_size, get_file_mimetype, \
29 FILE_EXTENSION_DELIMITER
30 FILE_EXTENSION_DELIMITER
30 from boards.models.attachment.viewers import FILE_TYPES_IMAGE
31 from boards.models.attachment.viewers import FILE_TYPES_IMAGE
31 from neboard import settings
32 from neboard import settings
32
33
33 SECTION_FORMS = 'Forms'
34 SECTION_FORMS = 'Forms'
34
35
35 POW_HASH_LENGTH = 16
36 POW_HASH_LENGTH = 16
36 POW_LIFE_MINUTES = 5
37 POW_LIFE_MINUTES = 5
37
38
38 REGEX_TAGS = re.compile(r'^[\w\s\d]+$', re.UNICODE)
39 REGEX_TAGS = re.compile(r'^[\w\s\d]+$', re.UNICODE)
39 REGEX_USERNAMES = re.compile(r'^[\w\s\d,]+$', re.UNICODE)
40 REGEX_USERNAMES = re.compile(r'^[\w\s\d,]+$', re.UNICODE)
40 REGEX_URL = re.compile(r'^(http|https|ftp):\/\/', re.UNICODE)
41 REGEX_URL = re.compile(r'^(http|https|ftp):\/\/', re.UNICODE)
41
42
42 VETERAN_POSTING_DELAY = 5
43 VETERAN_POSTING_DELAY = 5
43
44
44 ATTRIBUTE_PLACEHOLDER = 'placeholder'
45 ATTRIBUTE_PLACEHOLDER = 'placeholder'
45 ATTRIBUTE_ROWS = 'rows'
46 ATTRIBUTE_ROWS = 'rows'
46
47
47 LAST_POST_TIME = 'last_post_time'
48 LAST_POST_TIME = 'last_post_time'
48 LAST_LOGIN_TIME = 'last_login_time'
49 LAST_LOGIN_TIME = 'last_login_time'
49 TEXT_PLACEHOLDER = _('Type message here. Use formatting panel for more advanced usage.')
50 TEXT_PLACEHOLDER = _('Type message here. Use formatting panel for more advanced usage.')
50 TAGS_PLACEHOLDER = _('music images i_dont_like_tags')
51 TAGS_PLACEHOLDER = _('music images i_dont_like_tags')
51
52
52 LABEL_TITLE = _('Title')
53 LABEL_TITLE = _('Title')
53 LABEL_TEXT = _('Text')
54 LABEL_TEXT = _('Text')
54 LABEL_TAG = _('Tag')
55 LABEL_TAG = _('Tag')
55 LABEL_SEARCH = _('Search')
56 LABEL_SEARCH = _('Search')
56 LABEL_FILE = _('File')
57 LABEL_FILE = _('File')
57 LABEL_DUPLICATES = _('Check for duplicates')
58 LABEL_DUPLICATES = _('Check for duplicates')
58 LABEL_URL = _('Do not download URLs')
59 LABEL_URL = _('Do not download URLs')
59
60
60 ERROR_SPEED = 'Please wait %(delay)d second before sending message'
61 ERROR_SPEED = 'Please wait %(delay)d second before sending message'
61 ERROR_SPEED_PLURAL = 'Please wait %(delay)d seconds before sending message'
62 ERROR_SPEED_PLURAL = 'Please wait %(delay)d seconds before sending message'
62 ERROR_MANY_FILES = 'You can post no more than %(files)d file.'
63 ERROR_MANY_FILES = 'You can post no more than %(files)d file.'
63 ERROR_MANY_FILES_PLURAL = 'You can post no more than %(files)d files.'
64 ERROR_MANY_FILES_PLURAL = 'You can post no more than %(files)d files.'
64 ERROR_DUPLICATES = 'Some files are already present on the board.'
65 ERROR_DUPLICATES = 'Some files are already present on the board.'
65
66
66 TAG_MAX_LENGTH = 20
67 TAG_MAX_LENGTH = 20
67
68
68 TEXTAREA_ROWS = 4
69 TEXTAREA_ROWS = 4
69
70
70 TRIPCODE_DELIM = '#'
71 TRIPCODE_DELIM = '#'
71
72
72 # TODO Maybe this may be converted into the database table?
73 # TODO Maybe this may be converted into the database table?
73 MIMETYPE_EXTENSIONS = {
74 MIMETYPE_EXTENSIONS = {
74 'image/jpeg': 'jpeg',
75 'image/jpeg': 'jpeg',
75 'image/png': 'png',
76 'image/png': 'png',
76 'image/gif': 'gif',
77 'image/gif': 'gif',
77 'video/webm': 'webm',
78 'video/webm': 'webm',
78 'application/pdf': 'pdf',
79 'application/pdf': 'pdf',
79 'x-diff': 'diff',
80 'x-diff': 'diff',
80 'image/svg+xml': 'svg',
81 'image/svg+xml': 'svg',
81 'application/x-shockwave-flash': 'swf',
82 'application/x-shockwave-flash': 'swf',
82 'image/x-ms-bmp': 'bmp',
83 'image/x-ms-bmp': 'bmp',
83 'image/bmp': 'bmp',
84 'image/bmp': 'bmp',
84 }
85 }
85
86
86 DOWN_MODE_DOWNLOAD = 'DOWNLOAD'
87 DOWN_MODE_DOWNLOAD = 'DOWNLOAD'
87 DOWN_MODE_URL = 'URL'
88 DOWN_MODE_URL = 'URL'
88 DOWN_MODE_TRY = 'TRY'
89 DOWN_MODE_TRY = 'TRY'
89
90
90
91
91 logger = logging.getLogger('boards.forms')
92 logger = logging.getLogger('boards.forms')
92
93
93
94
94 def get_timezones():
95 def get_timezones():
95 timezones = []
96 timezones = []
96 for tz in pytz.common_timezones:
97 for tz in pytz.common_timezones:
97 timezones.append((tz, tz),)
98 timezones.append((tz, tz),)
98 return timezones
99 return timezones
99
100
100
101
101 class FormatPanel(forms.Textarea):
102 class FormatPanel(forms.Textarea):
102 """
103 """
103 Panel for text formatting. Consists of buttons to add different tags to the
104 Panel for text formatting. Consists of buttons to add different tags to the
104 form text area.
105 form text area.
105 """
106 """
106
107
107 def render(self, name, value, attrs=None):
108 def render(self, name, value, attrs=None):
108 output = '<div id="mark-panel">'
109 output = '<div id="mark-panel">'
109 for formatter in formatters:
110 for formatter in formatters:
110 output += '<span class="mark_btn"' + \
111 output += '<span class="mark_btn"' + \
111 ' onClick="addMarkToMsg(\'' + formatter.format_left + \
112 ' onClick="addMarkToMsg(\'' + formatter.format_left + \
112 '\', \'' + formatter.format_right + '\')">' + \
113 '\', \'' + formatter.format_right + '\')">' + \
113 formatter.preview_left + formatter.name + \
114 formatter.preview_left + formatter.name + \
114 formatter.preview_right + '</span>'
115 formatter.preview_right + '</span>'
115
116
116 output += '</div>'
117 output += '</div>'
117 output += super(FormatPanel, self).render(name, value, attrs=attrs)
118 output += super(FormatPanel, self).render(name, value, attrs=attrs)
118
119
119 return output
120 return output
120
121
121
122
122 class PlainErrorList(ErrorList):
123 class PlainErrorList(ErrorList):
123 def __unicode__(self):
124 def __unicode__(self):
124 return self.as_text()
125 return self.as_text()
125
126
126 def as_text(self):
127 def as_text(self):
127 return ''.join(['(!) %s ' % e for e in self])
128 return ''.join(['(!) %s ' % e for e in self])
128
129
129
130
130 class NeboardForm(forms.Form):
131 class NeboardForm(forms.Form):
131 """
132 """
132 Form with neboard-specific formatting.
133 Form with neboard-specific formatting.
133 """
134 """
134 required_css_class = 'required-field'
135 required_css_class = 'required-field'
135
136
136 def as_div(self):
137 def as_div(self):
137 """
138 """
138 Returns this form rendered as HTML <as_div>s.
139 Returns this form rendered as HTML <as_div>s.
139 """
140 """
140
141
141 return self._html_output(
142 return self._html_output(
142 # TODO Do not show hidden rows in the list here
143 # TODO Do not show hidden rows in the list here
143 normal_row='<div class="form-row">'
144 normal_row='<div class="form-row">'
144 '<div class="form-label">'
145 '<div class="form-label">'
145 '%(label)s'
146 '%(label)s'
146 '</div>'
147 '</div>'
147 '<div class="form-input">'
148 '<div class="form-input">'
148 '%(field)s'
149 '%(field)s'
149 '</div>'
150 '</div>'
150 '</div>'
151 '</div>'
151 '<div class="form-row">'
152 '<div class="form-row">'
152 '%(help_text)s'
153 '%(help_text)s'
153 '</div>',
154 '</div>',
154 error_row='<div class="form-row">'
155 error_row='<div class="form-row">'
155 '<div class="form-label"></div>'
156 '<div class="form-label"></div>'
156 '<div class="form-errors">%s</div>'
157 '<div class="form-errors">%s</div>'
157 '</div>',
158 '</div>',
158 row_ender='</div>',
159 row_ender='</div>',
159 help_text_html='%s',
160 help_text_html='%s',
160 errors_on_separate_row=True)
161 errors_on_separate_row=True)
161
162
162 def as_json_errors(self):
163 def as_json_errors(self):
163 errors = []
164 errors = []
164
165
165 for name, field in list(self.fields.items()):
166 for name, field in list(self.fields.items()):
166 if self[name].errors:
167 if self[name].errors:
167 errors.append({
168 errors.append({
168 'field': name,
169 'field': name,
169 'errors': self[name].errors.as_text(),
170 'errors': self[name].errors.as_text(),
170 })
171 })
171
172
172 return errors
173 return errors
173
174
174
175
175 class PostForm(NeboardForm):
176 class PostForm(NeboardForm):
176
177
177 title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False,
178 title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False,
178 label=LABEL_TITLE,
179 label=LABEL_TITLE,
179 widget=forms.TextInput(
180 widget=forms.TextInput(
180 attrs={ATTRIBUTE_PLACEHOLDER: 'title#tripcode'}))
181 attrs={ATTRIBUTE_PLACEHOLDER: 'title#tripcode'}))
181 text = forms.CharField(
182 text = forms.CharField(
182 widget=FormatPanel(attrs={
183 widget=FormatPanel(attrs={
183 ATTRIBUTE_PLACEHOLDER: TEXT_PLACEHOLDER,
184 ATTRIBUTE_PLACEHOLDER: TEXT_PLACEHOLDER,
184 ATTRIBUTE_ROWS: TEXTAREA_ROWS,
185 ATTRIBUTE_ROWS: TEXTAREA_ROWS,
185 }),
186 }),
186 required=False, label=LABEL_TEXT)
187 required=False, label=LABEL_TEXT)
187 download_mode = forms.ChoiceField(
188 download_mode = forms.ChoiceField(
188 choices=(
189 choices=(
189 (DOWN_MODE_TRY, _('Download if possible')),
190 (DOWN_MODE_TRY, _('Download or add URL')),
190 (DOWN_MODE_DOWNLOAD, _('Download')),
191 (DOWN_MODE_DOWNLOAD, _('Download or fail')),
191 (DOWN_MODE_URL, _('Insert as URLs')),
192 (DOWN_MODE_URL, _('Insert as URLs')),
192 ),
193 ),
193 initial=DOWN_MODE_TRY,
194 initial=DOWN_MODE_TRY,
194 label=_('URL download mode'))
195 label=_('URL download mode'))
195 file = UrlFileField(required=False, label=LABEL_FILE)
196 file = UrlFileField(required=False, label=LABEL_FILE)
196
197
197 # This field is for spam prevention only
198 # This field is for spam prevention only
198 email = forms.CharField(max_length=100, required=False, label=_('e-mail'),
199 email = forms.CharField(max_length=100, required=False, label=_('e-mail'),
199 widget=forms.TextInput(attrs={
200 widget=forms.TextInput(attrs={
200 'class': 'form-email'}))
201 'class': 'form-email'}))
201 subscribe = forms.BooleanField(required=False, label=_('Subscribe to thread'))
202 subscribe = forms.BooleanField(required=False, label=_('Subscribe to thread'))
202 check_duplicates = forms.BooleanField(required=False, label=LABEL_DUPLICATES)
203 check_duplicates = forms.BooleanField(required=False, label=LABEL_DUPLICATES)
203
204
204 guess = forms.CharField(widget=forms.HiddenInput(), required=False)
205 guess = forms.CharField(widget=forms.HiddenInput(), required=False)
205 timestamp = forms.CharField(widget=forms.HiddenInput(), required=False)
206 timestamp = forms.CharField(widget=forms.HiddenInput(), required=False)
206 iteration = forms.CharField(widget=forms.HiddenInput(), required=False)
207 iteration = forms.CharField(widget=forms.HiddenInput(), required=False)
207
208
208 session = None
209 session = None
209 need_to_ban = False
210 need_to_ban = False
210
211
211 def clean_title(self):
212 def clean_title(self):
212 title = self.cleaned_data['title']
213 title = self.cleaned_data['title']
213 if title:
214 if title:
214 if len(title) > TITLE_MAX_LENGTH:
215 if len(title) > TITLE_MAX_LENGTH:
215 raise forms.ValidationError(_('Title must have less than %s '
216 raise forms.ValidationError(_('Title must have less than %s '
216 'characters') %
217 'characters') %
217 str(TITLE_MAX_LENGTH))
218 str(TITLE_MAX_LENGTH))
218 return title
219 return title
219
220
220 def clean_text(self):
221 def clean_text(self):
221 text = self.cleaned_data['text'].strip()
222 text = self.cleaned_data['text'].strip()
222 if text:
223 if text:
223 max_length = board_settings.get_int(SECTION_FORMS, 'MaxTextLength')
224 max_length = board_settings.get_int(SECTION_FORMS, 'MaxTextLength')
224 if len(text) > max_length:
225 if len(text) > max_length:
225 raise forms.ValidationError(_('Text must have less than %s '
226 raise forms.ValidationError(_('Text must have less than %s '
226 'characters') % str(max_length))
227 'characters') % str(max_length))
227 return text
228 return text
228
229
229 def clean_file(self):
230 def clean_file(self):
230 return self._clean_files(self.cleaned_data['file'])
231 return self._clean_files(self.cleaned_data['file'])
231
232
232 def clean(self):
233 def clean(self):
233 cleaned_data = super(PostForm, self).clean()
234 cleaned_data = super(PostForm, self).clean()
234
235
235 if cleaned_data['email']:
236 if cleaned_data['email']:
236 if board_settings.get_bool(SECTION_FORMS, 'Autoban'):
237 if board_settings.get_bool(SECTION_FORMS, 'Autoban'):
237 self.need_to_ban = True
238 self.need_to_ban = True
238 raise forms.ValidationError('A human cannot enter a hidden field')
239 raise forms.ValidationError('A human cannot enter a hidden field')
239
240
240 if not self.errors:
241 if not self.errors:
241 self._clean_text_file()
242 self._clean_text_file()
242
243
243 limit_speed = board_settings.get_bool(SECTION_FORMS, 'LimitPostingSpeed')
244 limit_speed = board_settings.get_bool(SECTION_FORMS, 'LimitPostingSpeed')
244 limit_first = board_settings.get_bool(SECTION_FORMS, 'LimitFirstPosting')
245 limit_first = board_settings.get_bool(SECTION_FORMS, 'LimitFirstPosting')
245
246
246 settings_manager = get_settings_manager(self)
247 settings_manager = get_settings_manager(self)
247 if not self.errors and limit_speed or (limit_first and not settings_manager.get_setting('confirmed_user')):
248 if not self.errors and limit_speed or (limit_first and not settings_manager.get_setting('confirmed_user')):
248 pow_difficulty = board_settings.get_int(SECTION_FORMS, 'PowDifficulty')
249 pow_difficulty = board_settings.get_int(SECTION_FORMS, 'PowDifficulty')
249 if pow_difficulty > 0:
250 if pow_difficulty > 0:
250 # PoW-based
251 # PoW-based
251 if cleaned_data['timestamp'] \
252 if cleaned_data['timestamp'] \
252 and cleaned_data['iteration'] and cleaned_data['guess'] \
253 and cleaned_data['iteration'] and cleaned_data['guess'] \
253 and not settings_manager.get_setting('confirmed_user'):
254 and not settings_manager.get_setting('confirmed_user'):
254 self._validate_hash(cleaned_data['timestamp'], cleaned_data['iteration'], cleaned_data['guess'], cleaned_data['text'])
255 self._validate_hash(cleaned_data['timestamp'], cleaned_data['iteration'], cleaned_data['guess'], cleaned_data['text'])
255 else:
256 else:
256 # Time-based
257 # Time-based
257 self._validate_posting_speed()
258 self._validate_posting_speed()
258 settings_manager.set_setting('confirmed_user', True)
259 settings_manager.set_setting('confirmed_user', True)
259 if self.cleaned_data['check_duplicates']:
260 if self.cleaned_data['check_duplicates']:
260 self._check_file_duplicates(self.get_files())
261 self._check_file_duplicates(self.get_files())
261
262
262 return cleaned_data
263 return cleaned_data
263
264
264 def get_files(self):
265 def get_files(self):
265 """
266 """
266 Gets file from form or URL.
267 Gets file from form or URL.
267 """
268 """
268
269
269 files = []
270 files = []
270 for file in self.cleaned_data['file']:
271 for file in self.cleaned_data['file']:
271 if isinstance(file, UploadedFile):
272 if isinstance(file, UploadedFile):
272 files.append(file)
273 files.append(file)
273
274
274 return files
275 return files
275
276
276 def get_file_urls(self):
277 def get_file_urls(self):
277 files = []
278 files = []
278 for file in self.cleaned_data['file']:
279 for file in self.cleaned_data['file']:
279 if type(file) == str:
280 if type(file) == str:
280 files.append(file)
281 files.append(file)
281
282
282 return files
283 return files
283
284
284 def get_tripcode(self):
285 def get_tripcode(self):
285 title = self.cleaned_data['title']
286 title = self.cleaned_data['title']
286 if title is not None and TRIPCODE_DELIM in title:
287 if title is not None and TRIPCODE_DELIM in title:
287 code = title.split(TRIPCODE_DELIM, maxsplit=1)[1] + neboard.settings.SECRET_KEY
288 code = title.split(TRIPCODE_DELIM, maxsplit=1)[1] + neboard.settings.SECRET_KEY
288 tripcode = hashlib.md5(code.encode()).hexdigest()
289 tripcode = hashlib.md5(code.encode()).hexdigest()
289 else:
290 else:
290 tripcode = ''
291 tripcode = ''
291 return tripcode
292 return tripcode
292
293
293 def get_title(self):
294 def get_title(self):
294 title = self.cleaned_data['title']
295 title = self.cleaned_data['title']
295 if title is not None and TRIPCODE_DELIM in title:
296 if title is not None and TRIPCODE_DELIM in title:
296 return title.split(TRIPCODE_DELIM, maxsplit=1)[0]
297 return title.split(TRIPCODE_DELIM, maxsplit=1)[0]
297 else:
298 else:
298 return title
299 return title
299
300
300 def get_images(self):
301 def get_images(self):
301 return self.cleaned_data.get('stickers', [])
302 return self.cleaned_data.get('stickers', [])
302
303
303 def is_subscribe(self):
304 def is_subscribe(self):
304 return self.cleaned_data['subscribe']
305 return self.cleaned_data['subscribe']
305
306
306 def _update_file_extension(self, file):
307 def _update_file_extension(self, file):
307 if file:
308 if file:
308 mimetype = get_file_mimetype(file)
309 mimetype = get_file_mimetype(file)
309 extension = MIMETYPE_EXTENSIONS.get(mimetype)
310 extension = MIMETYPE_EXTENSIONS.get(mimetype)
310 if extension:
311 if extension:
311 filename = file.name.split(FILE_EXTENSION_DELIMITER, 1)[0]
312 filename = file.name.split(FILE_EXTENSION_DELIMITER, 1)[0]
312 new_filename = filename + FILE_EXTENSION_DELIMITER + extension
313 new_filename = filename + FILE_EXTENSION_DELIMITER + extension
313
314
314 file.name = new_filename
315 file.name = new_filename
315 else:
316 else:
316 logger.info('Unrecognized file mimetype: {}'.format(mimetype))
317 logger.info('Unrecognized file mimetype: {}'.format(mimetype))
317
318
318 def _clean_files(self, inputs):
319 def _clean_files(self, inputs):
319 files = []
320 files = []
320
321
321 max_file_count = board_settings.get_int(SECTION_FORMS, 'MaxFileCount')
322 max_file_count = board_settings.get_int(SECTION_FORMS, 'MaxFileCount')
322 if len(inputs) > max_file_count:
323 if len(inputs) > max_file_count:
323 raise forms.ValidationError(
324 raise forms.ValidationError(
324 ungettext_lazy(ERROR_MANY_FILES, ERROR_MANY_FILES,
325 ungettext_lazy(ERROR_MANY_FILES, ERROR_MANY_FILES,
325 max_file_count) % {'files': max_file_count})
326 max_file_count) % {'files': max_file_count})
326 for file_input in inputs:
327 for file_input in inputs:
327 if isinstance(file_input, UploadedFile):
328 if isinstance(file_input, UploadedFile):
328 files.append(self._clean_file_file(file_input))
329 files.append(self._clean_file_file(file_input))
329 else:
330 else:
330 files.append(self._clean_file_url(file_input))
331 files.append(self._clean_file_url(file_input))
331
332
332 for file in files:
333 for file in files:
333 self._validate_image_dimensions(file)
334 self._validate_image_dimensions(file)
334
335
335 return files
336 return files
336
337
337 def _validate_image_dimensions(self, file):
338 def _validate_image_dimensions(self, file):
338 if isinstance(file, UploadedFile):
339 if isinstance(file, UploadedFile):
339 mimetype = get_file_mimetype(file)
340 mimetype = get_file_mimetype(file)
340 if mimetype.split('/')[-1] in FILE_TYPES_IMAGE:
341 if mimetype.split('/')[-1] in FILE_TYPES_IMAGE:
341 Image.warnings.simplefilter('error', Image.DecompressionBombWarning)
342 Image.warnings.simplefilter('error', Image.DecompressionBombWarning)
342 try:
343 try:
343 print(get_image_dimensions(file))
344 print(get_image_dimensions(file))
344 except Exception:
345 except Exception:
345 raise forms.ValidationError('Possible decompression bomb or large image.')
346 raise forms.ValidationError('Possible decompression bomb or large image.')
346
347
347 def _clean_file_file(self, file):
348 def _clean_file_file(self, file):
348 validate_file_size(file.size)
349 validate_file_size(file.size)
349 self._update_file_extension(file)
350 self._update_file_extension(file)
350
351
351 return file
352 return file
352
353
353 def _clean_file_url(self, url):
354 def _clean_file_url(self, url):
354 file = None
355 file = None
355
356
356 if url:
357 if url:
357 mode = self.cleaned_data['download_mode']
358 mode = self.cleaned_data['download_mode']
358 if mode == DOWN_MODE_URL:
359 if mode == DOWN_MODE_URL:
359 return url
360 return url
360
361
361 try:
362 try:
362 image = get_image_by_alias(url, self.session)
363 image = get_image_by_alias(url, self.session)
363 if image is not None:
364 if image is not None:
364 if 'stickers' not in self.cleaned_data:
365 if 'stickers' not in self.cleaned_data:
365 self.cleaned_data['stickers'] = []
366 self.cleaned_data['stickers'] = []
366 self.cleaned_data['stickers'].append(image)
367 self.cleaned_data['stickers'].append(image)
367 return
368 return
368
369
369 if file is None:
370 if file is None:
370 file = self._get_file_from_url(url)
371 file = self._get_file_from_url(url)
371 if not file:
372 if not file:
372 raise forms.ValidationError(_('Invalid URL'))
373 raise forms.ValidationError(_('Invalid URL'))
373 else:
374 else:
374 validate_file_size(file.size)
375 validate_file_size(file.size)
375 self._update_file_extension(file)
376 self._update_file_extension(file)
376 except forms.ValidationError as e:
377 except forms.ValidationError as e:
377 # Assume we will get the plain URL instead of a file and save it
378 # Assume we will get the plain URL instead of a file and save it
378 if mode == DOWN_MODE_TRY and (REGEX_URL.match(url) or REGEX_MAGNET.match(url)):
379 if mode == DOWN_MODE_TRY and (REGEX_URL.match(url) or REGEX_MAGNET.match(url)):
379 logger.info('Error in forms: {}'.format(e))
380 logger.info('Error in forms: {}'.format(e))
380 return url
381 return url
381 else:
382 else:
382 raise e
383 raise e
383
384
384 return file
385 return file
385
386
386 def _clean_text_file(self):
387 def _clean_text_file(self):
387 text = self.cleaned_data.get('text')
388 text = self.cleaned_data.get('text')
388 file = self.get_files()
389 file = self.get_files()
389 file_url = self.get_file_urls()
390 file_url = self.get_file_urls()
390 images = self.get_images()
391 images = self.get_images()
391
392
392 if (not text) and (not file) and (not file_url) and len(images) == 0:
393 if (not text) and (not file) and (not file_url) and len(images) == 0:
393 error_message = _('Either text or file must be entered.')
394 error_message = _('Either text or file must be entered.')
394 self._add_general_error(error_message)
395 self._add_general_error(error_message)
395
396
397 def _get_cache_key(self, key):
398 return '{}_{}'.format(self.session.session_key, key)
399
400 def _set_session_cache(self, key, value):
401 cache.set(self._get_cache_key(key), value)
402
403 def _get_session_cache(self, key):
404 return cache.get(self._get_cache_key(key))
405
406 def _get_last_post_time(self):
407 last = self._get_session_cache(LAST_POST_TIME)
408 if last is None:
409 last = self.session.get(LAST_POST_TIME)
410 return last
411
396 def _validate_posting_speed(self):
412 def _validate_posting_speed(self):
397 can_post = True
413 can_post = True
398
414
399 posting_delay = board_settings.get_int(SECTION_FORMS, 'PostingDelay')
415 posting_delay = board_settings.get_int(SECTION_FORMS, 'PostingDelay')
400
416
401 if board_settings.get_bool(SECTION_FORMS, 'LimitPostingSpeed'):
417 if board_settings.get_bool(SECTION_FORMS, 'LimitPostingSpeed'):
402 now = time.time()
418 now = time.time()
403
419
404 current_delay = 0
420 current_delay = 0
405
421
406 if LAST_POST_TIME not in self.session:
422 if LAST_POST_TIME not in self.session:
407 self.session[LAST_POST_TIME] = now
423 self.session[LAST_POST_TIME] = now
408
424
409 need_delay = True
425 need_delay = True
410 else:
426 else:
411 last_post_time = self.session.get(LAST_POST_TIME)
427 last_post_time = self._get_last_post_time()
412 current_delay = int(now - last_post_time)
428 current_delay = int(now - last_post_time)
413
429
414 need_delay = current_delay < posting_delay
430 need_delay = current_delay < posting_delay
415
431
432 self._set_session_cache(LAST_POST_TIME, now)
433
416 if need_delay:
434 if need_delay:
417 delay = posting_delay - current_delay
435 delay = posting_delay - current_delay
418 error_message = ungettext_lazy(ERROR_SPEED, ERROR_SPEED_PLURAL,
436 error_message = ungettext_lazy(ERROR_SPEED, ERROR_SPEED_PLURAL,
419 delay) % {'delay': delay}
437 delay) % {'delay': delay}
420 self._add_general_error(error_message)
438 self._add_general_error(error_message)
421
439
422 can_post = False
440 can_post = False
423
441
424 if can_post:
442 if can_post:
425 self.session[LAST_POST_TIME] = now
443 self.session[LAST_POST_TIME] = now
444 else:
445 # Reset the time since posting failed
446 self._set_session_cache(LAST_POST_TIME, self.session[LAST_POST_TIME])
426
447
427 def _get_file_from_url(self, url: str) -> SimpleUploadedFile:
448 def _get_file_from_url(self, url: str) -> SimpleUploadedFile:
428 """
449 """
429 Gets an file file from URL.
450 Gets an file file from URL.
430 """
451 """
431
452
432 try:
453 try:
433 return download(url)
454 return download(url)
434 except forms.ValidationError as e:
455 except forms.ValidationError as e:
435 raise e
456 raise e
436 except Exception as e:
457 except Exception as e:
437 raise forms.ValidationError(e)
458 raise forms.ValidationError(e)
438
459
439 def _validate_hash(self, timestamp: str, iteration: str, guess: str, message: str):
460 def _validate_hash(self, timestamp: str, iteration: str, guess: str, message: str):
440 payload = timestamp + message.replace('\r\n', '\n')
461 payload = timestamp + message.replace('\r\n', '\n')
441 difficulty = board_settings.get_int(SECTION_FORMS, 'PowDifficulty')
462 difficulty = board_settings.get_int(SECTION_FORMS, 'PowDifficulty')
442 target = str(int(2 ** (POW_HASH_LENGTH * 3) / difficulty))
463 target = str(int(2 ** (POW_HASH_LENGTH * 3) / difficulty))
443 if len(target) < POW_HASH_LENGTH:
464 if len(target) < POW_HASH_LENGTH:
444 target = '0' * (POW_HASH_LENGTH - len(target)) + target
465 target = '0' * (POW_HASH_LENGTH - len(target)) + target
445
466
446 computed_guess = hashlib.sha256((payload + iteration).encode())\
467 computed_guess = hashlib.sha256((payload + iteration).encode())\
447 .hexdigest()[0:POW_HASH_LENGTH]
468 .hexdigest()[0:POW_HASH_LENGTH]
448 if guess != computed_guess or guess > target:
469 if guess != computed_guess or guess > target:
449 self._add_general_error(_('Invalid PoW.'))
470 self._add_general_error(_('Invalid PoW.'))
450
471
451 def _check_file_duplicates(self, files):
472 def _check_file_duplicates(self, files):
452 for file in files:
473 for file in files:
453 file_hash = utils.get_file_hash(file)
474 file_hash = utils.get_file_hash(file)
454 if Attachment.objects.get_existing_duplicate(file_hash, file):
475 if Attachment.objects.get_existing_duplicate(file_hash, file):
455 self._add_general_error(_(ERROR_DUPLICATES))
476 self._add_general_error(_(ERROR_DUPLICATES))
456
477
457 def _add_general_error(self, message):
478 def _add_general_error(self, message):
458 self.add_error('text', forms.ValidationError(message))
479 self.add_error('text', forms.ValidationError(message))
459
480
460
481
461 class ThreadForm(PostForm):
482 class ThreadForm(PostForm):
462
483
463 tags = forms.CharField(
484 tags = forms.CharField(
464 widget=forms.TextInput(attrs={ATTRIBUTE_PLACEHOLDER: TAGS_PLACEHOLDER}),
485 widget=forms.TextInput(attrs={ATTRIBUTE_PLACEHOLDER: TAGS_PLACEHOLDER}),
465 max_length=100, label=_('Tags'), required=True)
486 max_length=100, label=_('Tags'), required=True)
466 monochrome = forms.BooleanField(label=_('Monochrome'), required=False)
487 monochrome = forms.BooleanField(label=_('Monochrome'), required=False)
467
488
468 def clean_tags(self):
489 def clean_tags(self):
469 tags = self.cleaned_data['tags'].strip()
490 tags = self.cleaned_data['tags'].strip()
470
491
471 if not tags or not REGEX_TAGS.match(tags):
492 if not tags or not REGEX_TAGS.match(tags):
472 raise forms.ValidationError(
493 raise forms.ValidationError(
473 _('Inappropriate characters in tags.'))
494 _('Inappropriate characters in tags.'))
474
495
475 default_tag_name = board_settings.get(SECTION_FORMS, 'DefaultTag')\
496 default_tag_name = board_settings.get(SECTION_FORMS, 'DefaultTag')\
476 .strip().lower()
497 .strip().lower()
477
498
478 required_tag_exists = False
499 required_tag_exists = False
479 tag_set = set()
500 tag_set = set()
480 for tag_string in tags.split():
501 for tag_string in tags.split():
481 tag_name = tag_string.strip().lower()
502 tag_name = tag_string.strip().lower()
482 if tag_name == default_tag_name:
503 if tag_name == default_tag_name:
483 required_tag_exists = True
504 required_tag_exists = True
484 tag, created = Tag.objects.get_or_create_with_alias(
505 tag, created = Tag.objects.get_or_create_with_alias(
485 name=tag_name, required=True)
506 name=tag_name, required=True)
486 else:
507 else:
487 tag, created = Tag.objects.get_or_create_with_alias(name=tag_name)
508 tag, created = Tag.objects.get_or_create_with_alias(name=tag_name)
488 tag_set.add(tag)
509 tag_set.add(tag)
489
510
490 # If this is a new tag, don't check for its parents because nobody
511 # If this is a new tag, don't check for its parents because nobody
491 # added them yet
512 # added them yet
492 if not created:
513 if not created:
493 tag_set |= set(tag.get_all_parents())
514 tag_set |= set(tag.get_all_parents())
494
515
495 for tag in tag_set:
516 for tag in tag_set:
496 if tag.required:
517 if tag.required:
497 required_tag_exists = True
518 required_tag_exists = True
498 break
519 break
499
520
500 # Use default tag if no section exists
521 # Use default tag if no section exists
501 if not required_tag_exists:
522 if not required_tag_exists:
502 default_tag, created = Tag.objects.get_or_create_with_alias(
523 default_tag, created = Tag.objects.get_or_create_with_alias(
503 name=default_tag_name, required=True)
524 name=default_tag_name, required=True)
504 tag_set.add(default_tag)
525 tag_set.add(default_tag)
505
526
506 return tag_set
527 return tag_set
507
528
508 def clean(self):
529 def clean(self):
509 cleaned_data = super(ThreadForm, self).clean()
530 cleaned_data = super(ThreadForm, self).clean()
510
531
511 return cleaned_data
532 return cleaned_data
512
533
513 def is_monochrome(self):
534 def is_monochrome(self):
514 return self.cleaned_data['monochrome']
535 return self.cleaned_data['monochrome']
515
536
516
537
517 class SettingsForm(NeboardForm):
538 class SettingsForm(NeboardForm):
518
539
519 theme = forms.ChoiceField(
540 theme = forms.ChoiceField(
520 choices=board_settings.get_list_dict('View', 'Themes'),
541 choices=board_settings.get_list_dict('View', 'Themes'),
521 label=_('Theme'))
542 label=_('Theme'))
522 image_viewer = forms.ChoiceField(
543 image_viewer = forms.ChoiceField(
523 choices=board_settings.get_list_dict('View', 'ImageViewers'),
544 choices=board_settings.get_list_dict('View', 'ImageViewers'),
524 label=_('Image view mode'))
545 label=_('Image view mode'))
525 username = forms.CharField(label=_('User name'), required=False)
546 username = forms.CharField(label=_('User name'), required=False)
526 timezone = forms.ChoiceField(choices=get_timezones(), label=_('Time zone'))
547 timezone = forms.ChoiceField(choices=get_timezones(), label=_('Time zone'))
527
548
528 def clean_username(self):
549 def clean_username(self):
529 username = self.cleaned_data['username']
550 username = self.cleaned_data['username']
530
551
531 if username and not REGEX_USERNAMES.match(username):
552 if username and not REGEX_USERNAMES.match(username):
532 raise forms.ValidationError(_('Inappropriate characters.'))
553 raise forms.ValidationError(_('Inappropriate characters.'))
533
554
534 return username
555 return username
535
556
536
557
537 class SearchForm(NeboardForm):
558 class SearchForm(NeboardForm):
538 query = forms.CharField(max_length=500, label=LABEL_SEARCH, required=False)
559 query = forms.CharField(max_length=500, label=LABEL_SEARCH, required=False)
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,612 +1,612 b''
1 # SOME DESCRIPTIVE TITLE.
1 # SOME DESCRIPTIVE TITLE.
2 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 # This file is distributed under the same license as the PACKAGE package.
3 # This file is distributed under the same license as the PACKAGE package.
4 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5 #
5 #
6 msgid ""
6 msgid ""
7 msgstr ""
7 msgstr ""
8 "Project-Id-Version: PACKAGE VERSION\n"
8 "Project-Id-Version: PACKAGE VERSION\n"
9 "Report-Msgid-Bugs-To: \n"
9 "Report-Msgid-Bugs-To: \n"
10 "POT-Creation-Date: 2015-10-09 23:21+0300\n"
10 "POT-Creation-Date: 2015-10-09 23:21+0300\n"
11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13 "Language-Team: LANGUAGE <LL@li.org>\n"
13 "Language-Team: LANGUAGE <LL@li.org>\n"
14 "Language: ru\n"
14 "Language: ru\n"
15 "MIME-Version: 1.0\n"
15 "MIME-Version: 1.0\n"
16 "Content-Type: text/plain; charset=UTF-8\n"
16 "Content-Type: text/plain; charset=UTF-8\n"
17 "Content-Transfer-Encoding: 8bit\n"
17 "Content-Transfer-Encoding: 8bit\n"
18 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
18 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
19 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
19 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
20
20
21 #: admin.py:22
21 #: admin.py:22
22 msgid "{} posters were banned"
22 msgid "{} posters were banned"
23 msgstr ""
23 msgstr ""
24
24
25 #: authors.py:9
25 #: authors.py:9
26 msgid "author"
26 msgid "author"
27 msgstr "автор"
27 msgstr "автор"
28
28
29 #: authors.py:10
29 #: authors.py:10
30 msgid "developer"
30 msgid "developer"
31 msgstr "разработчик"
31 msgstr "разработчик"
32
32
33 #: authors.py:11
33 #: authors.py:11
34 msgid "javascript developer"
34 msgid "javascript developer"
35 msgstr "разработчик javascript"
35 msgstr "разработчик javascript"
36
36
37 #: authors.py:12
37 #: authors.py:12
38 msgid "designer"
38 msgid "designer"
39 msgstr "дизайнер"
39 msgstr "дизайнер"
40
40
41 #: forms.py:30
41 #: forms.py:30
42 msgid "Type message here. Use formatting panel for more advanced usage."
42 msgid "Type message here. Use formatting panel for more advanced usage."
43 msgstr ""
43 msgstr ""
44 "Вводите сообщение сюда. Используйте панель для более сложного форматирования."
44 "Вводите сообщение сюда. Используйте панель для более сложного форматирования."
45
45
46 #: forms.py:31
46 #: forms.py:31
47 msgid "music images i_dont_like_tags"
47 msgid "music images i_dont_like_tags"
48 msgstr "музыка картинки теги_не_нужны"
48 msgstr "музыка картинки теги_не_нужны"
49
49
50 #: forms.py:33
50 #: forms.py:33
51 msgid "Title"
51 msgid "Title"
52 msgstr "Заголовок"
52 msgstr "Заголовок"
53
53
54 #: forms.py:34
54 #: forms.py:34
55 msgid "Text"
55 msgid "Text"
56 msgstr "Текст"
56 msgstr "Текст"
57
57
58 #: forms.py:35
58 #: forms.py:35
59 msgid "Tag"
59 msgid "Tag"
60 msgstr "Метка"
60 msgstr "Метка"
61
61
62 #: forms.py:36 templates/boards/base.html:40 templates/search/search.html:7
62 #: forms.py:36 templates/boards/base.html:40 templates/search/search.html:7
63 msgid "Search"
63 msgid "Search"
64 msgstr "Поиск"
64 msgstr "Поиск"
65
65
66 #: forms.py:48
66 #: forms.py:48
67 msgid "File 1"
67 msgid "File 1"
68 msgstr "Файл 1"
68 msgstr "Файл 1"
69
69
70 #: forms.py:48
70 #: forms.py:48
71 msgid "File 2"
71 msgid "File 2"
72 msgstr "Файл 2"
72 msgstr "Файл 2"
73
73
74 #: forms.py:142
74 #: forms.py:142
75 msgid "File URL"
75 msgid "File URL"
76 msgstr "URL файла"
76 msgstr "URL файла"
77
77
78 #: forms.py:148
78 #: forms.py:148
79 msgid "e-mail"
79 msgid "e-mail"
80 msgstr ""
80 msgstr ""
81
81
82 #: forms.py:151
82 #: forms.py:151
83 msgid "Additional threads"
83 msgid "Additional threads"
84 msgstr "Дополнительные темы"
84 msgstr "Дополнительные темы"
85
85
86 #: forms.py:162
86 #: forms.py:162
87 #, python-format
87 #, python-format
88 msgid "Title must have less than %s characters"
88 msgid "Title must have less than %s characters"
89 msgstr "Заголовок должен иметь меньше %s символов"
89 msgstr "Заголовок должен иметь меньше %s символов"
90
90
91 #: forms.py:172
91 #: forms.py:172
92 #, python-format
92 #, python-format
93 msgid "Text must have less than %s characters"
93 msgid "Text must have less than %s characters"
94 msgstr "Текст должен быть короче %s символов"
94 msgstr "Текст должен быть короче %s символов"
95
95
96 #: forms.py:192
96 #: forms.py:192
97 msgid "Invalid URL"
97 msgid "Invalid URL"
98 msgstr "Неверный URL"
98 msgstr "Неверный URL"
99
99
100 #: forms.py:213
100 #: forms.py:213
101 msgid "Invalid additional thread list"
101 msgid "Invalid additional thread list"
102 msgstr "Неверный список дополнительных тем"
102 msgstr "Неверный список дополнительных тем"
103
103
104 #: forms.py:258
104 #: forms.py:258
105 msgid "Either text or file must be entered."
105 msgid "Either text or file must be entered."
106 msgstr "Текст или файл должны быть введены."
106 msgstr "Текст или файл должны быть введены."
107
107
108 #: forms.py:317 templates/boards/all_threads.html:153
108 #: forms.py:317 templates/boards/all_threads.html:153
109 #: templates/boards/rss/post.html:10 templates/boards/tags.html:6
109 #: templates/boards/rss/post.html:10 templates/boards/tags.html:6
110 msgid "Tags"
110 msgid "Tags"
111 msgstr "Метки"
111 msgstr "Метки"
112
112
113 #: forms.py:324
113 #: forms.py:324
114 msgid "Inappropriate characters in tags."
114 msgid "Inappropriate characters in tags."
115 msgstr "Недопустимые символы в метках."
115 msgstr "Недопустимые символы в метках."
116
116
117 #: forms.py:344
117 #: forms.py:344
118 msgid "Need at least one section."
118 msgid "Need at least one section."
119 msgstr "Нужен хотя бы один раздел."
119 msgstr "Нужен хотя бы один раздел."
120
120
121 #: forms.py:356
121 #: forms.py:356
122 msgid "Theme"
122 msgid "Theme"
123 msgstr "Тема"
123 msgstr "Тема"
124
124
125 #: forms.py:357
125 #: forms.py:357
126 msgid "Image view mode"
126 msgid "Image view mode"
127 msgstr "Режим просмотра изображений"
127 msgstr "Режим просмотра изображений"
128
128
129 #: forms.py:358
129 #: forms.py:358
130 msgid "User name"
130 msgid "User name"
131 msgstr "Имя пользователя"
131 msgstr "Имя пользователя"
132
132
133 #: forms.py:359
133 #: forms.py:359
134 msgid "Time zone"
134 msgid "Time zone"
135 msgstr "Часовой пояс"
135 msgstr "Часовой пояс"
136
136
137 #: forms.py:365
137 #: forms.py:365
138 msgid "Inappropriate characters."
138 msgid "Inappropriate characters."
139 msgstr "Недопустимые символы."
139 msgstr "Недопустимые символы."
140
140
141 #: templates/boards/404.html:6
141 #: templates/boards/404.html:6
142 msgid "Not found"
142 msgid "Not found"
143 msgstr "Не найдено"
143 msgstr "Не найдено"
144
144
145 #: templates/boards/404.html:12
145 #: templates/boards/404.html:12
146 msgid "This page does not exist"
146 msgid "This page does not exist"
147 msgstr "Этой страницы не существует"
147 msgstr "Этой страницы не существует"
148
148
149 #: templates/boards/all_threads.html:35
149 #: templates/boards/all_threads.html:35
150 msgid "Details"
150 msgid "Details"
151 msgstr "Подробности"
151 msgstr "Подробности"
152
152
153 #: templates/boards/all_threads.html:69
153 #: templates/boards/all_threads.html:69
154 msgid "Edit tag"
154 msgid "Edit tag"
155 msgstr "Изменить метку"
155 msgstr "Изменить метку"
156
156
157 #: templates/boards/all_threads.html:76
157 #: templates/boards/all_threads.html:76
158 #, python-format
158 #, python-format
159 msgid "%(count)s active thread"
159 msgid "%(count)s active thread"
160 msgid_plural "%(count)s active threads"
160 msgid_plural "%(count)s active threads"
161 msgstr[0] "%(count)s активная тема"
161 msgstr[0] "%(count)s активная тема"
162 msgstr[1] "%(count)s активные темы"
162 msgstr[1] "%(count)s активные темы"
163 msgstr[2] "%(count)s активных тем"
163 msgstr[2] "%(count)s активных тем"
164
164
165 #: templates/boards/all_threads.html:76
165 #: templates/boards/all_threads.html:76
166 #, python-format
166 #, python-format
167 msgid "%(count)s thread in bumplimit"
167 msgid "%(count)s thread in bumplimit"
168 msgid_plural "%(count)s threads in bumplimit"
168 msgid_plural "%(count)s threads in bumplimit"
169 msgstr[0] "%(count)s тема в бамплимите"
169 msgstr[0] "%(count)s тема в бамплимите"
170 msgstr[1] "%(count)s темы в бамплимите"
170 msgstr[1] "%(count)s темы в бамплимите"
171 msgstr[2] "%(count)s тем в бамплимите"
171 msgstr[2] "%(count)s тем в бамплимите"
172
172
173 #: templates/boards/all_threads.html:77
173 #: templates/boards/all_threads.html:77
174 #, python-format
174 #, python-format
175 msgid "%(count)s archived thread"
175 msgid "%(count)s archived thread"
176 msgid_plural "%(count)s archived thread"
176 msgid_plural "%(count)s archived thread"
177 msgstr[0] "%(count)s архивная тема"
177 msgstr[0] "%(count)s архивная тема"
178 msgstr[1] "%(count)s архивные темы"
178 msgstr[1] "%(count)s архивные темы"
179 msgstr[2] "%(count)s архивных тем"
179 msgstr[2] "%(count)s архивных тем"
180
180
181 #: templates/boards/all_threads.html:78 templates/boards/post.html:102
181 #: templates/boards/all_threads.html:78 templates/boards/post.html:102
182 #, python-format
182 #, python-format
183 #| msgid "%(count)s message"
183 #| msgid "%(count)s message"
184 #| msgid_plural "%(count)s messages"
184 #| msgid_plural "%(count)s messages"
185 msgid "%(count)s message"
185 msgid "%(count)s message"
186 msgid_plural "%(count)s messages"
186 msgid_plural "%(count)s messages"
187 msgstr[0] "%(count)s сообщение"
187 msgstr[0] "%(count)s сообщение"
188 msgstr[1] "%(count)s сообщения"
188 msgstr[1] "%(count)s сообщения"
189 msgstr[2] "%(count)s сообщений"
189 msgstr[2] "%(count)s сообщений"
190
190
191 #: templates/boards/all_threads.html:95 templates/boards/feed.html:30
191 #: templates/boards/all_threads.html:95 templates/boards/feed.html:30
192 #: templates/boards/notifications.html:17 templates/search/search.html:26
192 #: templates/boards/notifications.html:17 templates/search/search.html:26
193 msgid "Previous page"
193 msgid "Previous page"
194 msgstr "Предыдущая страница"
194 msgstr "Предыдущая страница"
195
195
196 #: templates/boards/all_threads.html:109
196 #: templates/boards/all_threads.html:109
197 #, python-format
197 #, python-format
198 msgid "Skipped %(count)s reply. Open thread to see all replies."
198 msgid "Skipped %(count)s reply. Open thread to see all replies."
199 msgid_plural "Skipped %(count)s replies. Open thread to see all replies."
199 msgid_plural "Skipped %(count)s replies. Open thread to see all replies."
200 msgstr[0] "Пропущен %(count)s ответ. Откройте тред, чтобы увидеть все ответы."
200 msgstr[0] "Пропущен %(count)s ответ. Откройте тред, чтобы увидеть все ответы."
201 msgstr[1] ""
201 msgstr[1] ""
202 "Пропущено %(count)s ответа. Откройте тред, чтобы увидеть все ответы."
202 "Пропущено %(count)s ответа. Откройте тред, чтобы увидеть все ответы."
203 msgstr[2] ""
203 msgstr[2] ""
204 "Пропущено %(count)s ответов. Откройте тред, чтобы увидеть все ответы."
204 "Пропущено %(count)s ответов. Откройте тред, чтобы увидеть все ответы."
205
205
206 #: templates/boards/all_threads.html:127 templates/boards/feed.html:40
206 #: templates/boards/all_threads.html:127 templates/boards/feed.html:40
207 #: templates/boards/notifications.html:27 templates/search/search.html:37
207 #: templates/boards/notifications.html:27 templates/search/search.html:37
208 msgid "Next page"
208 msgid "Next page"
209 msgstr "Следующая страница"
209 msgstr "Следующая страница"
210
210
211 #: templates/boards/all_threads.html:132
211 #: templates/boards/all_threads.html:132
212 msgid "No threads exist. Create the first one!"
212 msgid "No threads exist. Create the first one!"
213 msgstr "Нет тем. Создайте первую!"
213 msgstr "Нет тем. Создайте первую!"
214
214
215 #: templates/boards/all_threads.html:138
215 #: templates/boards/all_threads.html:138
216 msgid "Create new thread"
216 msgid "Create new thread"
217 msgstr "Создать новую тему"
217 msgstr "Создать новую тему"
218
218
219 #: templates/boards/all_threads.html:143 templates/boards/preview.html:16
219 #: templates/boards/all_threads.html:143 templates/boards/preview.html:16
220 #: templates/boards/thread_normal.html:51
220 #: templates/boards/thread_normal.html:51
221 msgid "Post"
221 msgid "Post"
222 msgstr "Отправить"
222 msgstr "Отправить"
223
223
224 #: templates/boards/all_threads.html:144 templates/boards/preview.html:6
224 #: templates/boards/all_threads.html:144 templates/boards/preview.html:6
225 #: templates/boards/staticpages/help.html:21
225 #: templates/boards/staticpages/help.html:21
226 #: templates/boards/thread_normal.html:52
226 #: templates/boards/thread_normal.html:52
227 msgid "Preview"
227 msgid "Preview"
228 msgstr "Предпросмотр"
228 msgstr "Предпросмотр"
229
229
230 #: templates/boards/all_threads.html:149
230 #: templates/boards/all_threads.html:149
231 msgid "Tags must be delimited by spaces. Text or image is required."
231 msgid "Tags must be delimited by spaces. Text or image is required."
232 msgstr ""
232 msgstr ""
233 "Метки должны быть разделены пробелами. Текст или изображение обязательны."
233 "Метки должны быть разделены пробелами. Текст или изображение обязательны."
234
234
235 #: templates/boards/all_threads.html:152 templates/boards/thread_normal.html:58
235 #: templates/boards/all_threads.html:152 templates/boards/thread_normal.html:58
236 msgid "Text syntax"
236 msgid "Text syntax"
237 msgstr "Синтаксис текста"
237 msgstr "Синтаксис текста"
238
238
239 #: templates/boards/all_threads.html:166 templates/boards/feed.html:53
239 #: templates/boards/all_threads.html:166 templates/boards/feed.html:53
240 msgid "Pages:"
240 msgid "Pages:"
241 msgstr "Страницы: "
241 msgstr "Страницы: "
242
242
243 #: templates/boards/authors.html:6 templates/boards/authors.html.py:12
243 #: templates/boards/authors.html:6 templates/boards/authors.html.py:12
244 msgid "Authors"
244 msgid "Authors"
245 msgstr "Авторы"
245 msgstr "Авторы"
246
246
247 #: templates/boards/authors.html:26
247 #: templates/boards/authors.html:26
248 msgid "Distributed under the"
248 msgid "Distributed under the"
249 msgstr "Распространяется под"
249 msgstr "Распространяется под"
250
250
251 #: templates/boards/authors.html:28
251 #: templates/boards/authors.html:28
252 msgid "license"
252 msgid "license"
253 msgstr "лицензией"
253 msgstr "лицензией"
254
254
255 #: templates/boards/authors.html:30
255 #: templates/boards/authors.html:30
256 msgid "Repository"
256 msgid "Repository"
257 msgstr "Репозиторий"
257 msgstr "Репозиторий"
258
258
259 #: templates/boards/base.html:14 templates/boards/base.html.py:41
259 #: templates/boards/base.html:14 templates/boards/base.html.py:41
260 msgid "Feed"
260 msgid "Feed"
261 msgstr "Лента"
261 msgstr "Лента"
262
262
263 #: templates/boards/base.html:31
263 #: templates/boards/base.html:31
264 msgid "All threads"
264 msgid "All threads"
265 msgstr "Все темы"
265 msgstr "Все темы"
266
266
267 #: templates/boards/base.html:37
267 #: templates/boards/base.html:37
268 msgid "Add tags"
268 msgid "Add tags"
269 msgstr "Добавить метки"
269 msgstr "Добавить метки"
270
270
271 #: templates/boards/base.html:39
271 #: templates/boards/base.html:39
272 msgid "Tag management"
272 msgid "Tag management"
273 msgstr "Управление метками"
273 msgstr "Управление метками"
274
274
275 #: templates/boards/base.html:39
275 #: templates/boards/base.html:39
276 msgid "tags"
276 msgid "tags"
277 msgstr "метки"
277 msgstr "метки"
278
278
279 #: templates/boards/base.html:40
279 #: templates/boards/base.html:40
280 msgid "search"
280 msgid "search"
281 msgstr "поиск"
281 msgstr "поиск"
282
282
283 #: templates/boards/base.html:41 templates/boards/feed.html:11
283 #: templates/boards/base.html:41 templates/boards/feed.html:11
284 msgid "feed"
284 msgid "feed"
285 msgstr "лента"
285 msgstr "лента"
286
286
287 #: templates/boards/base.html:42 templates/boards/random.html:6
287 #: templates/boards/base.html:42 templates/boards/random.html:6
288 msgid "Random images"
288 msgid "Random images"
289 msgstr "Случайные изображения"
289 msgstr "Случайные изображения"
290
290
291 #: templates/boards/base.html:42
291 #: templates/boards/base.html:42
292 msgid "random"
292 msgid "random"
293 msgstr "случайные"
293 msgstr "случайные"
294
294
295 #: templates/boards/base.html:44
295 #: templates/boards/base.html:44
296 msgid "favorites"
296 msgid "favorites"
297 msgstr "избранное"
297 msgstr "избранное"
298
298
299 #: templates/boards/base.html:48 templates/boards/base.html.py:49
299 #: templates/boards/base.html:48 templates/boards/base.html.py:49
300 #: templates/boards/notifications.html:8
300 #: templates/boards/notifications.html:8
301 msgid "Notifications"
301 msgid "Notifications"
302 msgstr "Уведомления"
302 msgstr "Уведомления"
303
303
304 #: templates/boards/base.html:56 templates/boards/settings.html:8
304 #: templates/boards/base.html:56 templates/boards/settings.html:8
305 msgid "Settings"
305 msgid "Settings"
306 msgstr "Настройки"
306 msgstr "Настройки"
307
307
308 #: templates/boards/base.html:59
308 #: templates/boards/base.html:59
309 msgid "Loading..."
309 msgid "Loading..."
310 msgstr "Загрузка..."
310 msgstr "Загрузка..."
311
311
312 #: templates/boards/base.html:71
312 #: templates/boards/base.html:71
313 msgid "Admin"
313 msgid "Admin"
314 msgstr "Администрирование"
314 msgstr "Администрирование"
315
315
316 #: templates/boards/base.html:73
316 #: templates/boards/base.html:73
317 #, python-format
317 #, python-format
318 msgid "Speed: %(ppd)s posts per day"
318 msgid "Speed: %(ppd)s posts per day"
319 msgstr "Скорость: %(ppd)s сообщений в день"
319 msgstr "Скорость: %(ppd)s сообщений в день"
320
320
321 #: templates/boards/base.html:75
321 #: templates/boards/base.html:75
322 msgid "Up"
322 msgid "Up"
323 msgstr "Вверх"
323 msgstr "Вверх"
324
324
325 #: templates/boards/feed.html:45
325 #: templates/boards/feed.html:45
326 msgid "No posts exist. Create the first one!"
326 msgid "No posts exist. Create the first one!"
327 msgstr "Нет сообщений. Создайте первое!"
327 msgstr "Нет сообщений. Создайте первое!"
328
328
329 #: templates/boards/post.html:33
329 #: templates/boards/post.html:33
330 msgid "Open"
330 msgid "Open"
331 msgstr "Открыть"
331 msgstr "Открыть"
332
332
333 #: templates/boards/post.html:35 templates/boards/post.html.py:46
333 #: templates/boards/post.html:35 templates/boards/post.html.py:46
334 msgid "Reply"
334 msgid "Reply"
335 msgstr "Ответить"
335 msgstr "Ответить"
336
336
337 #: templates/boards/post.html:41
337 #: templates/boards/post.html:41
338 msgid " in "
338 msgid " in "
339 msgstr " в "
339 msgstr " в "
340
340
341 #: templates/boards/post.html:51
341 #: templates/boards/post.html:51
342 msgid "Edit"
342 msgid "Edit"
343 msgstr "Изменить"
343 msgstr "Изменить"
344
344
345 #: templates/boards/post.html:53
345 #: templates/boards/post.html:53
346 msgid "Edit thread"
346 msgid "Edit thread"
347 msgstr "Изменить тему"
347 msgstr "Изменить тему"
348
348
349 #: templates/boards/post.html:91
349 #: templates/boards/post.html:91
350 msgid "Replies"
350 msgid "Replies"
351 msgstr "Ответы"
351 msgstr "Ответы"
352
352
353 #: templates/boards/post.html:103
353 #: templates/boards/post.html:103
354 #, python-format
354 #, python-format
355 msgid "%(count)s image"
355 msgid "%(count)s image"
356 msgid_plural "%(count)s images"
356 msgid_plural "%(count)s images"
357 msgstr[0] "%(count)s изображение"
357 msgstr[0] "%(count)s изображение"
358 msgstr[1] "%(count)s изображения"
358 msgstr[1] "%(count)s изображения"
359 msgstr[2] "%(count)s изображений"
359 msgstr[2] "%(count)s изображений"
360
360
361 #: templates/boards/rss/post.html:5
361 #: templates/boards/rss/post.html:5
362 msgid "Post image"
362 msgid "Post image"
363 msgstr "Изображение сообщения"
363 msgstr "Изображение сообщения"
364
364
365 #: templates/boards/settings.html:15
365 #: templates/boards/settings.html:15
366 msgid "You are moderator."
366 msgid "You are moderator."
367 msgstr "Вы модератор."
367 msgstr "Вы модератор."
368
368
369 #: templates/boards/settings.html:19
369 #: templates/boards/settings.html:19
370 msgid "Hidden tags:"
370 msgid "Hidden tags:"
371 msgstr "Скрытые метки:"
371 msgstr "Скрытые метки:"
372
372
373 #: templates/boards/settings.html:25
373 #: templates/boards/settings.html:25
374 msgid "No hidden tags."
374 msgid "No hidden tags."
375 msgstr "Нет скрытых меток."
375 msgstr "Нет скрытых меток."
376
376
377 #: templates/boards/settings.html:34
377 #: templates/boards/settings.html:34
378 msgid "Save"
378 msgid "Save"
379 msgstr "Сохранить"
379 msgstr "Сохранить"
380
380
381 #: templates/boards/staticpages/banned.html:6
381 #: templates/boards/staticpages/banned.html:6
382 msgid "Banned"
382 msgid "Banned"
383 msgstr "Заблокирован"
383 msgstr "Заблокирован"
384
384
385 #: templates/boards/staticpages/banned.html:11
385 #: templates/boards/staticpages/banned.html:11
386 msgid "Your IP address has been banned. Contact the administrator"
386 msgid "Your IP address has been banned. Contact the administrator"
387 msgstr "Ваш IP адрес был заблокирован. Свяжитесь с администратором"
387 msgstr "Ваш IP адрес был заблокирован. Свяжитесь с администратором"
388
388
389 #: templates/boards/staticpages/help.html:6
389 #: templates/boards/staticpages/help.html:6
390 #: templates/boards/staticpages/help.html:10
390 #: templates/boards/staticpages/help.html:10
391 msgid "Syntax"
391 msgid "Syntax"
392 msgstr "Синтаксис"
392 msgstr "Синтаксис"
393
393
394 #: templates/boards/staticpages/help.html:11
394 #: templates/boards/staticpages/help.html:11
395 msgid "Italic text"
395 msgid "Italic text"
396 msgstr "Курсивный текст"
396 msgstr "Курсивный текст"
397
397
398 #: templates/boards/staticpages/help.html:12
398 #: templates/boards/staticpages/help.html:12
399 msgid "Bold text"
399 msgid "Bold text"
400 msgstr "Полужирный текст"
400 msgstr "Полужирный текст"
401
401
402 #: templates/boards/staticpages/help.html:13
402 #: templates/boards/staticpages/help.html:13
403 msgid "Spoiler"
403 msgid "Spoiler"
404 msgstr "Спойлер"
404 msgstr "Спойлер"
405
405
406 #: templates/boards/staticpages/help.html:14
406 #: templates/boards/staticpages/help.html:14
407 msgid "Link to a post"
407 msgid "Link to a post"
408 msgstr "Ссылка на сообщение"
408 msgstr "Ссылка на сообщение"
409
409
410 #: templates/boards/staticpages/help.html:15
410 #: templates/boards/staticpages/help.html:15
411 msgid "Strikethrough text"
411 msgid "Strikethrough text"
412 msgstr "Зачеркнутый текст"
412 msgstr "Зачеркнутый текст"
413
413
414 #: templates/boards/staticpages/help.html:16
414 #: templates/boards/staticpages/help.html:16
415 msgid "Comment"
415 msgid "Comment"
416 msgstr "Комментарий"
416 msgstr "Комментарий"
417
417
418 #: templates/boards/staticpages/help.html:17
418 #: templates/boards/staticpages/help.html:17
419 #: templates/boards/staticpages/help.html:18
419 #: templates/boards/staticpages/help.html:18
420 msgid "Quote"
420 msgid "Quote"
421 msgstr "Цитата"
421 msgstr "Цитата"
422
422
423 #: templates/boards/staticpages/help.html:21
423 #: templates/boards/staticpages/help.html:21
424 msgid "You can try pasting the text and previewing the result here:"
424 msgid "You can try pasting the text and previewing the result here:"
425 msgstr "Вы можете попробовать вставить текст и проверить результат здесь:"
425 msgstr "Вы можете попробовать вставить текст и проверить результат здесь:"
426
426
427 #: templates/boards/tags.html:17
427 #: templates/boards/tags.html:17
428 msgid "Sections:"
428 msgid "Sections:"
429 msgstr "Разделы:"
429 msgstr "Разделы:"
430
430
431 #: templates/boards/tags.html:30
431 #: templates/boards/tags.html:30
432 msgid "Other tags:"
432 msgid "Other tags:"
433 msgstr "Другие метки:"
433 msgstr "Другие метки:"
434
434
435 #: templates/boards/tags.html:43
435 #: templates/boards/tags.html:43
436 msgid "All tags..."
436 msgid "All tags..."
437 msgstr "Все метки..."
437 msgstr "Все метки..."
438
438
439 #: templates/boards/thread.html:14
439 #: templates/boards/thread.html:14
440 msgid "Normal"
440 msgid "Normal"
441 msgstr "Нормальный"
441 msgstr "Нормальный"
442
442
443 #: templates/boards/thread.html:15
443 #: templates/boards/thread.html:15
444 msgid "Gallery"
444 msgid "Gallery"
445 msgstr "Галерея"
445 msgstr "Галерея"
446
446
447 #: templates/boards/thread.html:16
447 #: templates/boards/thread.html:16
448 msgid "Tree"
448 msgid "Tree"
449 msgstr "Дерево"
449 msgstr "Дерево"
450
450
451 #: templates/boards/thread.html:35
451 #: templates/boards/thread.html:35
452 msgid "message"
452 msgid "message"
453 msgid_plural "messages"
453 msgid_plural "messages"
454 msgstr[0] "сообщение"
454 msgstr[0] "сообщение"
455 msgstr[1] "сообщения"
455 msgstr[1] "сообщения"
456 msgstr[2] "сообщений"
456 msgstr[2] "сообщений"
457
457
458 #: templates/boards/thread.html:38
458 #: templates/boards/thread.html:38
459 msgid "image"
459 msgid "image"
460 msgid_plural "images"
460 msgid_plural "images"
461 msgstr[0] "изображение"
461 msgstr[0] "изображение"
462 msgstr[1] "изображения"
462 msgstr[1] "изображения"
463 msgstr[2] "изображений"
463 msgstr[2] "изображений"
464
464
465 #: templates/boards/thread.html:40
465 #: templates/boards/thread.html:40
466 msgid "Last update: "
466 msgid "Last update: "
467 msgstr "Последнее обновление: "
467 msgstr "Последнее обновление: "
468
468
469 #: templates/boards/thread_gallery.html:36
469 #: templates/boards/thread_gallery.html:36
470 msgid "No images."
470 msgid "No images."
471 msgstr "Нет изображений."
471 msgstr "Нет изображений."
472
472
473 #: templates/boards/thread_normal.html:30
473 #: templates/boards/thread_normal.html:30
474 msgid "posts to bumplimit"
474 msgid "posts to bumplimit"
475 msgstr "сообщений до бамплимита"
475 msgstr "сообщений до бамплимита"
476
476
477 #: templates/boards/thread_normal.html:44
477 #: templates/boards/thread_normal.html:44
478 msgid "Reply to thread"
478 msgid "Reply to thread"
479 msgstr "Ответить в тему"
479 msgstr "Ответить в тему"
480
480
481 #: templates/boards/thread_normal.html:44
481 #: templates/boards/thread_normal.html:44
482 msgid "to message "
482 msgid "to message "
483 msgstr "на сообщение"
483 msgstr "на сообщение"
484
484
485 #: templates/boards/thread_normal.html:59
485 #: templates/boards/thread_normal.html:59
486 msgid "Reset form"
486 msgid "Reset form"
487 msgstr "Сбросить форму"
487 msgstr "Сбросить форму"
488
488
489 #: templates/search/search.html:17
489 #: templates/search/search.html:17
490 msgid "Ok"
490 msgid "Ok"
491 msgstr "Ок"
491 msgstr "Ок"
492
492
493 #: utils.py:120
493 #: utils.py:120
494 #, python-format
494 #, python-format
495 msgid "File must be less than %s but is %s."
495 msgid "File must be less than %s but is %s."
496 msgstr "Файл должен быть менее %s, но его размер %s."
496 msgstr "Файл должен быть менее %s, но его размер %s."
497
497
498 msgid "Please wait %(delay)d second before sending message"
498 msgid "Please wait %(delay)d second before sending message"
499 msgid_plural "Please wait %(delay)d seconds before sending message"
499 msgid_plural "Please wait %(delay)d seconds before sending message"
500 msgstr[0] "Пожалуйста подождите %(delay)d секунду перед отправкой сообщения"
500 msgstr[0] "Пожалуйста подождите %(delay)d секунду перед отправкой сообщения"
501 msgstr[1] "Пожалуйста подождите %(delay)d секунды перед отправкой сообщения"
501 msgstr[1] "Пожалуйста подождите %(delay)d секунды перед отправкой сообщения"
502 msgstr[2] "Пожалуйста подождите %(delay)d секунд перед отправкой сообщения"
502 msgstr[2] "Пожалуйста подождите %(delay)d секунд перед отправкой сообщения"
503
503
504 msgid "New threads"
504 msgid "New threads"
505 msgstr "Новые темы"
505 msgstr "Новые темы"
506
506
507 #, python-format
507 #, python-format
508 msgid "Max file size is %(size)s."
508 msgid "Max file size is %(size)s."
509 msgstr "Максимальный размер файла %(size)s."
509 msgstr "Максимальный размер файла %(size)s."
510
510
511 msgid "Size of media:"
511 msgid "Size of media:"
512 msgstr "Размер медиа:"
512 msgstr "Размер медиа:"
513
513
514 msgid "Statistics"
514 msgid "Statistics"
515 msgstr "Статистика"
515 msgstr "Статистика"
516
516
517 msgid "Invalid PoW."
517 msgid "Invalid PoW."
518 msgstr "Неверный PoW."
518 msgstr "Неверный PoW."
519
519
520 msgid "Stale PoW."
520 msgid "Stale PoW."
521 msgstr "PoW устарел."
521 msgstr "PoW устарел."
522
522
523 msgid "Show"
523 msgid "Show"
524 msgstr "Показывать"
524 msgstr "Показывать"
525
525
526 msgid "Hide"
526 msgid "Hide"
527 msgstr "Скрывать"
527 msgstr "Скрывать"
528
528
529 msgid "Add to favorites"
529 msgid "Add to favorites"
530 msgstr "Добавить в избранное"
530 msgstr "Добавить в избранное"
531
531
532 msgid "Remove from favorites"
532 msgid "Remove from favorites"
533 msgstr "Убрать из избранного"
533 msgstr "Убрать из избранного"
534
534
535 msgid "Monochrome"
535 msgid "Monochrome"
536 msgstr "Монохромный"
536 msgstr "Монохромный"
537
537
538 msgid "Subsections: "
538 msgid "Subsections: "
539 msgstr "Подразделы: "
539 msgstr "Подразделы: "
540
540
541 msgid "Change file source"
541 msgid "Change file source"
542 msgstr "Изменить источник файла"
542 msgstr "Изменить источник файла"
543
543
544 msgid "interesting"
544 msgid "interesting"
545 msgstr "интересное"
545 msgstr "интересное"
546
546
547 msgid "images"
547 msgid "images"
548 msgstr "изображения"
548 msgstr "изображения"
549
549
550 msgid "Delete post"
550 msgid "Delete post"
551 msgstr "Удалить пост"
551 msgstr "Удалить пост"
552
552
553 msgid "Delete thread"
553 msgid "Delete thread"
554 msgstr "Удалить тему"
554 msgstr "Удалить тему"
555
555
556 msgid "Messages per day/week/month:"
556 msgid "Messages per day/week/month:"
557 msgstr "Сообщений за день/неделю/месяц:"
557 msgstr "Сообщений за день/неделю/месяц:"
558
558
559 msgid "Subscribe to thread"
559 msgid "Subscribe to thread"
560 msgstr "Подписаться на тему"
560 msgstr "Подписаться на тему"
561
561
562 msgid "Active threads:"
562 msgid "Active threads:"
563 msgstr "Активные темы:"
563 msgstr "Активные темы:"
564
564
565 msgid "No active threads today."
565 msgid "No active threads today."
566 msgstr "Сегодня нет активных тем."
566 msgstr "Сегодня нет активных тем."
567
567
568 msgid "Insert URLs on separate lines."
568 msgid "Insert URLs on separate lines."
569 msgstr "Вставляйте ссылки на отдельных строках."
569 msgstr "Вставляйте ссылки на отдельных строках."
570
570
571 msgid "You can post no more than %(files)d file."
571 msgid "You can post no more than %(files)d file."
572 msgid_plural "You can post no more than %(files)d files."
572 msgid_plural "You can post no more than %(files)d files."
573 msgstr[0] "Вы можете отправить не более %(files)d файла."
573 msgstr[0] "Вы можете отправить не более %(files)d файла."
574 msgstr[1] "Вы можете отправить не более %(files)d файлов."
574 msgstr[1] "Вы можете отправить не более %(files)d файлов."
575 msgstr[2] "Вы можете отправить не более %(files)d файлов."
575 msgstr[2] "Вы можете отправить не более %(files)d файлов."
576
576
577 #, python-format
577 #, python-format
578 msgid "Max file number is %(max_files)s."
578 msgid "Max file number is %(max_files)s."
579 msgstr "Максимальное количество файлов %(max_files)s."
579 msgstr "Максимальное количество файлов %(max_files)s."
580
580
581 msgid "Moderation"
581 msgid "Moderation"
582 msgstr "Модерация"
582 msgstr "Модерация"
583
583
584 msgid "Check for duplicates"
584 msgid "Check for duplicates"
585 msgstr "Проверять на дубликаты"
585 msgstr "Проверять на дубликаты"
586
586
587 msgid "Some files are already present on the board."
587 msgid "Some files are already present on the board."
588 msgstr "Некоторые файлы уже присутствуют на борде."
588 msgstr "Некоторые файлы уже присутствуют на борде."
589
589
590 msgid "Do not download URLs"
590 msgid "Do not download URLs"
591 msgstr "Не загружать ссылки"
591 msgstr "Не загружать ссылки"
592
592
593 msgid "Ban and delete"
593 msgid "Ban and delete"
594 msgstr "Забанить и удалить"
594 msgstr "Забанить и удалить"
595
595
596 msgid "Are you sure?"
596 msgid "Are you sure?"
597 msgstr "Вы уверены?"
597 msgstr "Вы уверены?"
598
598
599 msgid "Ban"
599 msgid "Ban"
600 msgstr "Забанить"
600 msgstr "Забанить"
601
601
602 msgid "URL download mode"
602 msgid "URL download mode"
603 msgstr "Режим загрузки ссылок"
603 msgstr "Режим загрузки ссылок"
604
604
605 msgid "Download if possible"
605 msgid "Download or add URL"
606 msgstr "Загружать если возможно"
606 msgstr "Загрузить или добавить ссылку"
607
607
608 msgid "Download"
608 msgid "Download or fail"
609 msgstr "Загружать"
609 msgstr "Загрузить или отказать"
610
610
611 msgid "Insert as URLs"
611 msgid "Insert as URLs"
612 msgstr "Вставлять как ссылки"
612 msgstr "Вставлять как ссылки"
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,612 +1,612 b''
1 # SOME DESCRIPTIVE TITLE.
1 # SOME DESCRIPTIVE TITLE.
2 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 # This file is distributed under the same license as the PACKAGE package.
3 # This file is distributed under the same license as the PACKAGE package.
4 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5 #
5 #
6 msgid ""
6 msgid ""
7 msgstr ""
7 msgstr ""
8 "Project-Id-Version: PACKAGE VERSION\n"
8 "Project-Id-Version: PACKAGE VERSION\n"
9 "Report-Msgid-Bugs-To: \n"
9 "Report-Msgid-Bugs-To: \n"
10 "POT-Creation-Date: 2015-10-09 23:21+0300\n"
10 "POT-Creation-Date: 2015-10-09 23:21+0300\n"
11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13 "Language-Team: LANGUAGE <LL@li.org>\n"
13 "Language-Team: LANGUAGE <LL@li.org>\n"
14 "Language: ru\n"
14 "Language: ru\n"
15 "MIME-Version: 1.0\n"
15 "MIME-Version: 1.0\n"
16 "Content-Type: text/plain; charset=UTF-8\n"
16 "Content-Type: text/plain; charset=UTF-8\n"
17 "Content-Transfer-Encoding: 8bit\n"
17 "Content-Transfer-Encoding: 8bit\n"
18 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
18 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
19 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
19 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
20
20
21 #: admin.py:22
21 #: admin.py:22
22 msgid "{} posters were banned"
22 msgid "{} posters were banned"
23 msgstr "{} постерів заблоковано"
23 msgstr "{} постерів заблоковано"
24
24
25 #: authors.py:9
25 #: authors.py:9
26 msgid "author"
26 msgid "author"
27 msgstr "автор"
27 msgstr "автор"
28
28
29 #: authors.py:10
29 #: authors.py:10
30 msgid "developer"
30 msgid "developer"
31 msgstr "розробник"
31 msgstr "розробник"
32
32
33 #: authors.py:11
33 #: authors.py:11
34 msgid "javascript developer"
34 msgid "javascript developer"
35 msgstr "javascript-розробник"
35 msgstr "javascript-розробник"
36
36
37 #: authors.py:12
37 #: authors.py:12
38 msgid "designer"
38 msgid "designer"
39 msgstr "дизайнер"
39 msgstr "дизайнер"
40
40
41 #: forms.py:30
41 #: forms.py:30
42 msgid "Type message here. Use formatting panel for more advanced usage."
42 msgid "Type message here. Use formatting panel for more advanced usage."
43 msgstr ""
43 msgstr ""
44 "Введіть сюди повідомлення. Користайте панель для складнішого форматування."
44 "Введіть сюди повідомлення. Користайте панель для складнішого форматування."
45
45
46 #: forms.py:31
46 #: forms.py:31
47 msgid "music images i_dont_like_tags"
47 msgid "music images i_dont_like_tags"
48 msgstr "музика зображення мітки_не_потрібні"
48 msgstr "музика зображення мітки_не_потрібні"
49
49
50 #: forms.py:33
50 #: forms.py:33
51 msgid "Title"
51 msgid "Title"
52 msgstr "Заголовок"
52 msgstr "Заголовок"
53
53
54 #: forms.py:34
54 #: forms.py:34
55 msgid "Text"
55 msgid "Text"
56 msgstr "Текст"
56 msgstr "Текст"
57
57
58 #: forms.py:35
58 #: forms.py:35
59 msgid "Tag"
59 msgid "Tag"
60 msgstr "Мітка"
60 msgstr "Мітка"
61
61
62 #: forms.py:36 templates/boards/base.html:40 templates/search/search.html:7
62 #: forms.py:36 templates/boards/base.html:40 templates/search/search.html:7
63 msgid "Search"
63 msgid "Search"
64 msgstr "Пошук"
64 msgstr "Пошук"
65
65
66 #: forms.py:48
66 #: forms.py:48
67 msgid "File 1"
67 msgid "File 1"
68 msgstr "Файл 1"
68 msgstr "Файл 1"
69
69
70 #: forms.py:48
70 #: forms.py:48
71 msgid "File 2"
71 msgid "File 2"
72 msgstr "Файл 2"
72 msgstr "Файл 2"
73
73
74 #: forms.py:142
74 #: forms.py:142
75 msgid "File URL"
75 msgid "File URL"
76 msgstr "URL файлу"
76 msgstr "URL файлу"
77
77
78 #: forms.py:148
78 #: forms.py:148
79 msgid "e-mail"
79 msgid "e-mail"
80 msgstr ""
80 msgstr ""
81
81
82 #: forms.py:151
82 #: forms.py:151
83 msgid "Additional threads"
83 msgid "Additional threads"
84 msgstr "Додаткові нитки"
84 msgstr "Додаткові нитки"
85
85
86 #: forms.py:162
86 #: forms.py:162
87 #, python-format
87 #, python-format
88 msgid "Title must have less than %s characters"
88 msgid "Title must have less than %s characters"
89 msgstr "Заголовок має містити менше %s символів"
89 msgstr "Заголовок має містити менше %s символів"
90
90
91 #: forms.py:172
91 #: forms.py:172
92 #, python-format
92 #, python-format
93 msgid "Text must have less than %s characters"
93 msgid "Text must have less than %s characters"
94 msgstr "Текст має бути коротше %s символів"
94 msgstr "Текст має бути коротше %s символів"
95
95
96 #: forms.py:192
96 #: forms.py:192
97 msgid "Invalid URL"
97 msgid "Invalid URL"
98 msgstr "Хибний URL"
98 msgstr "Хибний URL"
99
99
100 #: forms.py:213
100 #: forms.py:213
101 msgid "Invalid additional thread list"
101 msgid "Invalid additional thread list"
102 msgstr "Хибний перелік додаткових ниток"
102 msgstr "Хибний перелік додаткових ниток"
103
103
104 #: forms.py:258
104 #: forms.py:258
105 msgid "Either text or file must be entered."
105 msgid "Either text or file must be entered."
106 msgstr "Слід додати текст або файл."
106 msgstr "Слід додати текст або файл."
107
107
108 #: forms.py:317 templates/boards/all_threads.html:153
108 #: forms.py:317 templates/boards/all_threads.html:153
109 #: templates/boards/rss/post.html:10 templates/boards/tags.html:6
109 #: templates/boards/rss/post.html:10 templates/boards/tags.html:6
110 msgid "Tags"
110 msgid "Tags"
111 msgstr "Мітки"
111 msgstr "Мітки"
112
112
113 #: forms.py:324
113 #: forms.py:324
114 msgid "Inappropriate characters in tags."
114 msgid "Inappropriate characters in tags."
115 msgstr "Неприйнятні символи у мітках."
115 msgstr "Неприйнятні символи у мітках."
116
116
117 #: forms.py:344
117 #: forms.py:344
118 msgid "Need at least one section."
118 msgid "Need at least one section."
119 msgstr "Мусить бути хоча б один розділ."
119 msgstr "Мусить бути хоча б один розділ."
120
120
121 #: forms.py:356
121 #: forms.py:356
122 msgid "Theme"
122 msgid "Theme"
123 msgstr "Тема"
123 msgstr "Тема"
124
124
125 #: forms.py:357
125 #: forms.py:357
126 msgid "Image view mode"
126 msgid "Image view mode"
127 msgstr "Режим перегляду зображень"
127 msgstr "Режим перегляду зображень"
128
128
129 #: forms.py:358
129 #: forms.py:358
130 msgid "User name"
130 msgid "User name"
131 msgstr "Им'я користувача"
131 msgstr "Им'я користувача"
132
132
133 #: forms.py:359
133 #: forms.py:359
134 msgid "Time zone"
134 msgid "Time zone"
135 msgstr "Часовий пояс"
135 msgstr "Часовий пояс"
136
136
137 #: forms.py:365
137 #: forms.py:365
138 msgid "Inappropriate characters."
138 msgid "Inappropriate characters."
139 msgstr "Неприйнятні символи."
139 msgstr "Неприйнятні символи."
140
140
141 #: templates/boards/404.html:6
141 #: templates/boards/404.html:6
142 msgid "Not found"
142 msgid "Not found"
143 msgstr "Загубилося"
143 msgstr "Загубилося"
144
144
145 #: templates/boards/404.html:12
145 #: templates/boards/404.html:12
146 msgid "This page does not exist"
146 msgid "This page does not exist"
147 msgstr "Нема правдоньки на світі, ой нема…"
147 msgstr "Нема правдоньки на світі, ой нема…"
148
148
149 #: templates/boards/all_threads.html:35
149 #: templates/boards/all_threads.html:35
150 msgid "Details"
150 msgid "Details"
151 msgstr "Деталі"
151 msgstr "Деталі"
152
152
153 #: templates/boards/all_threads.html:69
153 #: templates/boards/all_threads.html:69
154 msgid "Edit tag"
154 msgid "Edit tag"
155 msgstr "Змінити мітку"
155 msgstr "Змінити мітку"
156
156
157 #: templates/boards/all_threads.html:76
157 #: templates/boards/all_threads.html:76
158 #, python-format
158 #, python-format
159 msgid "%(count)s active thread"
159 msgid "%(count)s active thread"
160 msgid_plural "%(count)s active threads"
160 msgid_plural "%(count)s active threads"
161 msgstr[0] "%(count)s активна нитка"
161 msgstr[0] "%(count)s активна нитка"
162 msgstr[1] "%(count)s активні нитки"
162 msgstr[1] "%(count)s активні нитки"
163 msgstr[2] "%(count)s активних ниток"
163 msgstr[2] "%(count)s активних ниток"
164
164
165 #: templates/boards/all_threads.html:76
165 #: templates/boards/all_threads.html:76
166 #, python-format
166 #, python-format
167 msgid "%(count)s thread in bumplimit"
167 msgid "%(count)s thread in bumplimit"
168 msgid_plural "%(count)s threads in bumplimit"
168 msgid_plural "%(count)s threads in bumplimit"
169 msgstr[0] "%(count)s нитка в бампляматі"
169 msgstr[0] "%(count)s нитка в бампляматі"
170 msgstr[1] "%(count)s нитки в бампляматі"
170 msgstr[1] "%(count)s нитки в бампляматі"
171 msgstr[2] "%(count)s ниток у бампляматі"
171 msgstr[2] "%(count)s ниток у бампляматі"
172
172
173 #: templates/boards/all_threads.html:77
173 #: templates/boards/all_threads.html:77
174 #, python-format
174 #, python-format
175 msgid "%(count)s archived thread"
175 msgid "%(count)s archived thread"
176 msgid_plural "%(count)s archived thread"
176 msgid_plural "%(count)s archived thread"
177 msgstr[0] "%(count)s архівна нитка"
177 msgstr[0] "%(count)s архівна нитка"
178 msgstr[1] "%(count)s архівні нитки"
178 msgstr[1] "%(count)s архівні нитки"
179 msgstr[2] "%(count)s архівних ниток"
179 msgstr[2] "%(count)s архівних ниток"
180
180
181 #: templates/boards/all_threads.html:78 templates/boards/post.html:102
181 #: templates/boards/all_threads.html:78 templates/boards/post.html:102
182 #, python-format
182 #, python-format
183 #| msgid "%(count)s message"
183 #| msgid "%(count)s message"
184 #| msgid_plural "%(count)s messages"
184 #| msgid_plural "%(count)s messages"
185 msgid "%(count)s message"
185 msgid "%(count)s message"
186 msgid_plural "%(count)s messages"
186 msgid_plural "%(count)s messages"
187 msgstr[0] "%(count)s повідомлення"
187 msgstr[0] "%(count)s повідомлення"
188 msgstr[1] "%(count)s повідомлення"
188 msgstr[1] "%(count)s повідомлення"
189 msgstr[2] "%(count)s повідомлень"
189 msgstr[2] "%(count)s повідомлень"
190
190
191 #: templates/boards/all_threads.html:95 templates/boards/feed.html:30
191 #: templates/boards/all_threads.html:95 templates/boards/feed.html:30
192 #: templates/boards/notifications.html:17 templates/search/search.html:26
192 #: templates/boards/notifications.html:17 templates/search/search.html:26
193 msgid "Previous page"
193 msgid "Previous page"
194 msgstr "Попєрєдня сторінка"
194 msgstr "Попєрєдня сторінка"
195
195
196 #: templates/boards/all_threads.html:109
196 #: templates/boards/all_threads.html:109
197 #, python-format
197 #, python-format
198 msgid "Skipped %(count)s reply. Open thread to see all replies."
198 msgid "Skipped %(count)s reply. Open thread to see all replies."
199 msgid_plural "Skipped %(count)s replies. Open thread to see all replies."
199 msgid_plural "Skipped %(count)s replies. Open thread to see all replies."
200 msgstr[0] "Пропущено %(count)s відповідь. Розгорніть нитку, щоб побачити всі відповіді."
200 msgstr[0] "Пропущено %(count)s відповідь. Розгорніть нитку, щоб побачити всі відповіді."
201 msgstr[1] ""
201 msgstr[1] ""
202 "Пропущено %(count)s відповіді. Розгорніть нитку, щоб побачити всі відповіді."
202 "Пропущено %(count)s відповіді. Розгорніть нитку, щоб побачити всі відповіді."
203 msgstr[2] ""
203 msgstr[2] ""
204 "Пропущено %(count)s відповідей. Розгорніть нитку, щоб побачити всі відповіді."
204 "Пропущено %(count)s відповідей. Розгорніть нитку, щоб побачити всі відповіді."
205
205
206 #: templates/boards/all_threads.html:127 templates/boards/feed.html:40
206 #: templates/boards/all_threads.html:127 templates/boards/feed.html:40
207 #: templates/boards/notifications.html:27 templates/search/search.html:37
207 #: templates/boards/notifications.html:27 templates/search/search.html:37
208 msgid "Next page"
208 msgid "Next page"
209 msgstr "Наступна сторінка"
209 msgstr "Наступна сторінка"
210
210
211 #: templates/boards/all_threads.html:132
211 #: templates/boards/all_threads.html:132
212 msgid "No threads exist. Create the first one!"
212 msgid "No threads exist. Create the first one!"
213 msgstr "Нема правдоньки на світі. Зачнімо її!"
213 msgstr "Нема правдоньки на світі. Зачнімо її!"
214
214
215 #: templates/boards/all_threads.html:138
215 #: templates/boards/all_threads.html:138
216 msgid "Create new thread"
216 msgid "Create new thread"
217 msgstr "Сплести нову нитку"
217 msgstr "Сплести нову нитку"
218
218
219 #: templates/boards/all_threads.html:143 templates/boards/preview.html:16
219 #: templates/boards/all_threads.html:143 templates/boards/preview.html:16
220 #: templates/boards/thread_normal.html:51
220 #: templates/boards/thread_normal.html:51
221 msgid "Post"
221 msgid "Post"
222 msgstr "Надіслати"
222 msgstr "Надіслати"
223
223
224 #: templates/boards/all_threads.html:144 templates/boards/preview.html:6
224 #: templates/boards/all_threads.html:144 templates/boards/preview.html:6
225 #: templates/boards/staticpages/help.html:21
225 #: templates/boards/staticpages/help.html:21
226 #: templates/boards/thread_normal.html:52
226 #: templates/boards/thread_normal.html:52
227 msgid "Preview"
227 msgid "Preview"
228 msgstr "Поперегляд"
228 msgstr "Поперегляд"
229
229
230 #: templates/boards/all_threads.html:149
230 #: templates/boards/all_threads.html:149
231 msgid "Tags must be delimited by spaces. Text or image is required."
231 msgid "Tags must be delimited by spaces. Text or image is required."
232 msgstr ""
232 msgstr ""
233 "Мітки розмежувати пробілами. Текст чи зображення є обов'язковими."
233 "Мітки розмежувати пробілами. Текст чи зображення є обов'язковими."
234
234
235 #: templates/boards/all_threads.html:152 templates/boards/thread_normal.html:58
235 #: templates/boards/all_threads.html:152 templates/boards/thread_normal.html:58
236 msgid "Text syntax"
236 msgid "Text syntax"
237 msgstr "Синтаксис тексту"
237 msgstr "Синтаксис тексту"
238
238
239 #: templates/boards/all_threads.html:166 templates/boards/feed.html:53
239 #: templates/boards/all_threads.html:166 templates/boards/feed.html:53
240 msgid "Pages:"
240 msgid "Pages:"
241 msgstr "Сторінки:"
241 msgstr "Сторінки:"
242
242
243 #: templates/boards/authors.html:6 templates/boards/authors.html.py:12
243 #: templates/boards/authors.html:6 templates/boards/authors.html.py:12
244 msgid "Authors"
244 msgid "Authors"
245 msgstr "Автори"
245 msgstr "Автори"
246
246
247 #: templates/boards/authors.html:26
247 #: templates/boards/authors.html:26
248 msgid "Distributed under the"
248 msgid "Distributed under the"
249 msgstr "Розповсюджується під ліцензією"
249 msgstr "Розповсюджується під ліцензією"
250
250
251 #: templates/boards/authors.html:28
251 #: templates/boards/authors.html:28
252 msgid "license"
252 msgid "license"
253 msgstr ""
253 msgstr ""
254
254
255 #: templates/boards/authors.html:30
255 #: templates/boards/authors.html:30
256 msgid "Repository"
256 msgid "Repository"
257 msgstr "Репозиторій"
257 msgstr "Репозиторій"
258
258
259 #: templates/boards/base.html:14 templates/boards/base.html.py:41
259 #: templates/boards/base.html:14 templates/boards/base.html.py:41
260 msgid "Feed"
260 msgid "Feed"
261 msgstr "Стрічка"
261 msgstr "Стрічка"
262
262
263 #: templates/boards/base.html:31
263 #: templates/boards/base.html:31
264 msgid "All threads"
264 msgid "All threads"
265 msgstr "Усі нитки"
265 msgstr "Усі нитки"
266
266
267 #: templates/boards/base.html:37
267 #: templates/boards/base.html:37
268 msgid "Add tags"
268 msgid "Add tags"
269 msgstr "Додати мітки"
269 msgstr "Додати мітки"
270
270
271 #: templates/boards/base.html:39
271 #: templates/boards/base.html:39
272 msgid "Tag management"
272 msgid "Tag management"
273 msgstr "Керування мітками"
273 msgstr "Керування мітками"
274
274
275 #: templates/boards/base.html:39
275 #: templates/boards/base.html:39
276 msgid "tags"
276 msgid "tags"
277 msgstr "мітки"
277 msgstr "мітки"
278
278
279 #: templates/boards/base.html:40
279 #: templates/boards/base.html:40
280 msgid "search"
280 msgid "search"
281 msgstr "пошук"
281 msgstr "пошук"
282
282
283 #: templates/boards/base.html:41 templates/boards/feed.html:11
283 #: templates/boards/base.html:41 templates/boards/feed.html:11
284 msgid "feed"
284 msgid "feed"
285 msgstr "стрічка"
285 msgstr "стрічка"
286
286
287 #: templates/boards/base.html:42 templates/boards/random.html:6
287 #: templates/boards/base.html:42 templates/boards/random.html:6
288 msgid "Random images"
288 msgid "Random images"
289 msgstr "Випадкові зображення"
289 msgstr "Випадкові зображення"
290
290
291 #: templates/boards/base.html:42
291 #: templates/boards/base.html:42
292 msgid "random"
292 msgid "random"
293 msgstr "випадкові"
293 msgstr "випадкові"
294
294
295 #: templates/boards/base.html:44
295 #: templates/boards/base.html:44
296 msgid "favorites"
296 msgid "favorites"
297 msgstr "улюблене"
297 msgstr "улюблене"
298
298
299 #: templates/boards/base.html:48 templates/boards/base.html.py:49
299 #: templates/boards/base.html:48 templates/boards/base.html.py:49
300 #: templates/boards/notifications.html:8
300 #: templates/boards/notifications.html:8
301 msgid "Notifications"
301 msgid "Notifications"
302 msgstr "Сповіщення"
302 msgstr "Сповіщення"
303
303
304 #: templates/boards/base.html:56 templates/boards/settings.html:8
304 #: templates/boards/base.html:56 templates/boards/settings.html:8
305 msgid "Settings"
305 msgid "Settings"
306 msgstr "Налаштування"
306 msgstr "Налаштування"
307
307
308 #: templates/boards/base.html:59
308 #: templates/boards/base.html:59
309 msgid "Loading..."
309 msgid "Loading..."
310 msgstr "Завантаження..."
310 msgstr "Завантаження..."
311
311
312 #: templates/boards/base.html:71
312 #: templates/boards/base.html:71
313 msgid "Admin"
313 msgid "Admin"
314 msgstr "Адміністрування"
314 msgstr "Адміністрування"
315
315
316 #: templates/boards/base.html:73
316 #: templates/boards/base.html:73
317 #, python-format
317 #, python-format
318 msgid "Speed: %(ppd)s posts per day"
318 msgid "Speed: %(ppd)s posts per day"
319 msgstr "Хуткість: %(ppd)s повідомлень на день"
319 msgstr "Хуткість: %(ppd)s повідомлень на день"
320
320
321 #: templates/boards/base.html:75
321 #: templates/boards/base.html:75
322 msgid "Up"
322 msgid "Up"
323 msgstr "Догори"
323 msgstr "Догори"
324
324
325 #: templates/boards/feed.html:45
325 #: templates/boards/feed.html:45
326 msgid "No posts exist. Create the first one!"
326 msgid "No posts exist. Create the first one!"
327 msgstr "Ще нема повідомлень. Зачнімо!"
327 msgstr "Ще нема повідомлень. Зачнімо!"
328
328
329 #: templates/boards/post.html:33
329 #: templates/boards/post.html:33
330 msgid "Open"
330 msgid "Open"
331 msgstr "Відкрити"
331 msgstr "Відкрити"
332
332
333 #: templates/boards/post.html:35 templates/boards/post.html.py:46
333 #: templates/boards/post.html:35 templates/boards/post.html.py:46
334 msgid "Reply"
334 msgid "Reply"
335 msgstr "Відповісти"
335 msgstr "Відповісти"
336
336
337 #: templates/boards/post.html:41
337 #: templates/boards/post.html:41
338 msgid " in "
338 msgid " in "
339 msgstr " у "
339 msgstr " у "
340
340
341 #: templates/boards/post.html:51
341 #: templates/boards/post.html:51
342 msgid "Edit"
342 msgid "Edit"
343 msgstr "Змінити"
343 msgstr "Змінити"
344
344
345 #: templates/boards/post.html:53
345 #: templates/boards/post.html:53
346 msgid "Edit thread"
346 msgid "Edit thread"
347 msgstr "Змінити нитку"
347 msgstr "Змінити нитку"
348
348
349 #: templates/boards/post.html:91
349 #: templates/boards/post.html:91
350 msgid "Replies"
350 msgid "Replies"
351 msgstr "Відповіді"
351 msgstr "Відповіді"
352
352
353 #: templates/boards/post.html:103
353 #: templates/boards/post.html:103
354 #, python-format
354 #, python-format
355 msgid "%(count)s image"
355 msgid "%(count)s image"
356 msgid_plural "%(count)s images"
356 msgid_plural "%(count)s images"
357 msgstr[0] "%(count)s зображення"
357 msgstr[0] "%(count)s зображення"
358 msgstr[1] "%(count)s зображення"
358 msgstr[1] "%(count)s зображення"
359 msgstr[2] "%(count)s зображень"
359 msgstr[2] "%(count)s зображень"
360
360
361 #: templates/boards/rss/post.html:5
361 #: templates/boards/rss/post.html:5
362 msgid "Post image"
362 msgid "Post image"
363 msgstr "Зображення повідомлення"
363 msgstr "Зображення повідомлення"
364
364
365 #: templates/boards/settings.html:15
365 #: templates/boards/settings.html:15
366 msgid "You are moderator."
366 msgid "You are moderator."
367 msgstr "Ви модератор."
367 msgstr "Ви модератор."
368
368
369 #: templates/boards/settings.html:19
369 #: templates/boards/settings.html:19
370 msgid "Hidden tags:"
370 msgid "Hidden tags:"
371 msgstr "Приховані мітки:"
371 msgstr "Приховані мітки:"
372
372
373 #: templates/boards/settings.html:25
373 #: templates/boards/settings.html:25
374 msgid "No hidden tags."
374 msgid "No hidden tags."
375 msgstr "Нема прихованих міток."
375 msgstr "Нема прихованих міток."
376
376
377 #: templates/boards/settings.html:34
377 #: templates/boards/settings.html:34
378 msgid "Save"
378 msgid "Save"
379 msgstr "Зберегти"
379 msgstr "Зберегти"
380
380
381 #: templates/boards/staticpages/banned.html:6
381 #: templates/boards/staticpages/banned.html:6
382 msgid "Banned"
382 msgid "Banned"
383 msgstr "Заблоковано"
383 msgstr "Заблоковано"
384
384
385 #: templates/boards/staticpages/banned.html:11
385 #: templates/boards/staticpages/banned.html:11
386 msgid "Your IP address has been banned. Contact the administrator"
386 msgid "Your IP address has been banned. Contact the administrator"
387 msgstr "Вашу IP-адресу заблоковано. Зателефонуйте до спортлото"
387 msgstr "Вашу IP-адресу заблоковано. Зателефонуйте до спортлото"
388
388
389 #: templates/boards/staticpages/help.html:6
389 #: templates/boards/staticpages/help.html:6
390 #: templates/boards/staticpages/help.html:10
390 #: templates/boards/staticpages/help.html:10
391 msgid "Syntax"
391 msgid "Syntax"
392 msgstr "Синтаксис"
392 msgstr "Синтаксис"
393
393
394 #: templates/boards/staticpages/help.html:11
394 #: templates/boards/staticpages/help.html:11
395 msgid "Italic text"
395 msgid "Italic text"
396 msgstr "Курсивний текст"
396 msgstr "Курсивний текст"
397
397
398 #: templates/boards/staticpages/help.html:12
398 #: templates/boards/staticpages/help.html:12
399 msgid "Bold text"
399 msgid "Bold text"
400 msgstr "Напівогрядний текст"
400 msgstr "Напівогрядний текст"
401
401
402 #: templates/boards/staticpages/help.html:13
402 #: templates/boards/staticpages/help.html:13
403 msgid "Spoiler"
403 msgid "Spoiler"
404 msgstr "Спойлер"
404 msgstr "Спойлер"
405
405
406 #: templates/boards/staticpages/help.html:14
406 #: templates/boards/staticpages/help.html:14
407 msgid "Link to a post"
407 msgid "Link to a post"
408 msgstr "Посилання на повідомлення"
408 msgstr "Посилання на повідомлення"
409
409
410 #: templates/boards/staticpages/help.html:15
410 #: templates/boards/staticpages/help.html:15
411 msgid "Strikethrough text"
411 msgid "Strikethrough text"
412 msgstr "Закреслений текст"
412 msgstr "Закреслений текст"
413
413
414 #: templates/boards/staticpages/help.html:16
414 #: templates/boards/staticpages/help.html:16
415 msgid "Comment"
415 msgid "Comment"
416 msgstr "Коментар"
416 msgstr "Коментар"
417
417
418 #: templates/boards/staticpages/help.html:17
418 #: templates/boards/staticpages/help.html:17
419 #: templates/boards/staticpages/help.html:18
419 #: templates/boards/staticpages/help.html:18
420 msgid "Quote"
420 msgid "Quote"
421 msgstr "Цитата"
421 msgstr "Цитата"
422
422
423 #: templates/boards/staticpages/help.html:21
423 #: templates/boards/staticpages/help.html:21
424 msgid "You can try pasting the text and previewing the result here:"
424 msgid "You can try pasting the text and previewing the result here:"
425 msgstr "Можете спробувати вставити текст і перевірити результат тут:"
425 msgstr "Можете спробувати вставити текст і перевірити результат тут:"
426
426
427 #: templates/boards/tags.html:17
427 #: templates/boards/tags.html:17
428 msgid "Sections:"
428 msgid "Sections:"
429 msgstr "Розділи:"
429 msgstr "Розділи:"
430
430
431 #: templates/boards/tags.html:30
431 #: templates/boards/tags.html:30
432 msgid "Other tags:"
432 msgid "Other tags:"
433 msgstr "Інші мітки:"
433 msgstr "Інші мітки:"
434
434
435 #: templates/boards/tags.html:43
435 #: templates/boards/tags.html:43
436 msgid "All tags..."
436 msgid "All tags..."
437 msgstr "Усі мітки..."
437 msgstr "Усі мітки..."
438
438
439 #: templates/boards/thread.html:14
439 #: templates/boards/thread.html:14
440 msgid "Normal"
440 msgid "Normal"
441 msgstr "Звичайний"
441 msgstr "Звичайний"
442
442
443 #: templates/boards/thread.html:15
443 #: templates/boards/thread.html:15
444 msgid "Gallery"
444 msgid "Gallery"
445 msgstr "Галерея"
445 msgstr "Галерея"
446
446
447 #: templates/boards/thread.html:16
447 #: templates/boards/thread.html:16
448 msgid "Tree"
448 msgid "Tree"
449 msgstr "Віник"
449 msgstr "Віник"
450
450
451 #: templates/boards/thread.html:35
451 #: templates/boards/thread.html:35
452 msgid "message"
452 msgid "message"
453 msgid_plural "messages"
453 msgid_plural "messages"
454 msgstr[0] "повідомлення"
454 msgstr[0] "повідомлення"
455 msgstr[1] "повідомлення"
455 msgstr[1] "повідомлення"
456 msgstr[2] "повідомлень"
456 msgstr[2] "повідомлень"
457
457
458 #: templates/boards/thread.html:38
458 #: templates/boards/thread.html:38
459 msgid "image"
459 msgid "image"
460 msgid_plural "images"
460 msgid_plural "images"
461 msgstr[0] "зображення"
461 msgstr[0] "зображення"
462 msgstr[1] "зображення"
462 msgstr[1] "зображення"
463 msgstr[2] "зображень"
463 msgstr[2] "зображень"
464
464
465 #: templates/boards/thread.html:40
465 #: templates/boards/thread.html:40
466 msgid "Last update: "
466 msgid "Last update: "
467 msgstr "Останнє оновлення: "
467 msgstr "Останнє оновлення: "
468
468
469 #: templates/boards/thread_gallery.html:36
469 #: templates/boards/thread_gallery.html:36
470 msgid "No images."
470 msgid "No images."
471 msgstr "Нема зображень."
471 msgstr "Нема зображень."
472
472
473 #: templates/boards/thread_normal.html:30
473 #: templates/boards/thread_normal.html:30
474 msgid "posts to bumplimit"
474 msgid "posts to bumplimit"
475 msgstr "повідомлень до бамплямату"
475 msgstr "повідомлень до бамплямату"
476
476
477 #: templates/boards/thread_normal.html:44
477 #: templates/boards/thread_normal.html:44
478 msgid "Reply to thread"
478 msgid "Reply to thread"
479 msgstr "Відповісти до нитки"
479 msgstr "Відповісти до нитки"
480
480
481 #: templates/boards/thread_normal.html:44
481 #: templates/boards/thread_normal.html:44
482 msgid "to message "
482 msgid "to message "
483 msgstr "на повідомлення"
483 msgstr "на повідомлення"
484
484
485 #: templates/boards/thread_normal.html:59
485 #: templates/boards/thread_normal.html:59
486 msgid "Reset form"
486 msgid "Reset form"
487 msgstr "Скинути форму"
487 msgstr "Скинути форму"
488
488
489 #: templates/search/search.html:17
489 #: templates/search/search.html:17
490 msgid "Ok"
490 msgid "Ok"
491 msgstr "Файно"
491 msgstr "Файно"
492
492
493 #: utils.py:120
493 #: utils.py:120
494 #, python-format
494 #, python-format
495 msgid "File must be less than %s but is %s."
495 msgid "File must be less than %s but is %s."
496 msgstr "Файл мусить бути менше %s, але його розмір %s."
496 msgstr "Файл мусить бути менше %s, але його розмір %s."
497
497
498 msgid "Please wait %(delay)d second before sending message"
498 msgid "Please wait %(delay)d second before sending message"
499 msgid_plural "Please wait %(delay)d seconds before sending message"
499 msgid_plural "Please wait %(delay)d seconds before sending message"
500 msgstr[0] "Зачекайте, будь ласка, %(delay)d секунду перед надсиланням повідомлення"
500 msgstr[0] "Зачекайте, будь ласка, %(delay)d секунду перед надсиланням повідомлення"
501 msgstr[1] "Зачекайте, будь ласка, %(delay)d секунди перед надсиланням повідомлення"
501 msgstr[1] "Зачекайте, будь ласка, %(delay)d секунди перед надсиланням повідомлення"
502 msgstr[2] "Зачекайте, будь ласка, %(delay)d секунд перед надсиланням повідомлення"
502 msgstr[2] "Зачекайте, будь ласка, %(delay)d секунд перед надсиланням повідомлення"
503
503
504 msgid "New threads"
504 msgid "New threads"
505 msgstr "Нові нитки"
505 msgstr "Нові нитки"
506
506
507 #, python-format
507 #, python-format
508 msgid "Max file size is %(size)s."
508 msgid "Max file size is %(size)s."
509 msgstr "Максимальний розмір файлу %(size)s."
509 msgstr "Максимальний розмір файлу %(size)s."
510
510
511 msgid "Size of media:"
511 msgid "Size of media:"
512 msgstr "Розмір посередника:"
512 msgstr "Розмір посередника:"
513
513
514 msgid "Statistics"
514 msgid "Statistics"
515 msgstr "Статистика"
515 msgstr "Статистика"
516
516
517 msgid "Invalid PoW."
517 msgid "Invalid PoW."
518 msgstr "Хибний PoW."
518 msgstr "Хибний PoW."
519
519
520 msgid "Stale PoW."
520 msgid "Stale PoW."
521 msgstr "PoW застарів."
521 msgstr "PoW застарів."
522
522
523 msgid "Show"
523 msgid "Show"
524 msgstr "Показувати"
524 msgstr "Показувати"
525
525
526 msgid "Hide"
526 msgid "Hide"
527 msgstr "Ховати"
527 msgstr "Ховати"
528
528
529 msgid "Add to favorites"
529 msgid "Add to favorites"
530 msgstr "Я це люблю"
530 msgstr "Я це люблю"
531
531
532 msgid "Remove from favorites"
532 msgid "Remove from favorites"
533 msgstr "Вже не люблю"
533 msgstr "Вже не люблю"
534
534
535 msgid "Monochrome"
535 msgid "Monochrome"
536 msgstr "Без барв"
536 msgstr "Без барв"
537
537
538 msgid "Subsections: "
538 msgid "Subsections: "
539 msgstr "Підрозділи: "
539 msgstr "Підрозділи: "
540
540
541 msgid "Change file source"
541 msgid "Change file source"
542 msgstr "Змінити джерело файлу"
542 msgstr "Змінити джерело файлу"
543
543
544 msgid "interesting"
544 msgid "interesting"
545 msgstr "цікаве"
545 msgstr "цікаве"
546
546
547 msgid "images"
547 msgid "images"
548 msgstr "пічкури"
548 msgstr "пічкури"
549
549
550 msgid "Delete post"
550 msgid "Delete post"
551 msgstr "Видалити повідомлення"
551 msgstr "Видалити повідомлення"
552
552
553 msgid "Delete thread"
553 msgid "Delete thread"
554 msgstr "Вирвати нитку"
554 msgstr "Вирвати нитку"
555
555
556 msgid "Messages per day/week/month:"
556 msgid "Messages per day/week/month:"
557 msgstr "Повідомлень за день/тиждень/тижмісяць:"
557 msgstr "Повідомлень за день/тиждень/тижмісяць:"
558
558
559 msgid "Subscribe to thread"
559 msgid "Subscribe to thread"
560 msgstr "Стежити за ниткою"
560 msgstr "Стежити за ниткою"
561
561
562 msgid "Active threads:"
562 msgid "Active threads:"
563 msgstr "Активні нитки:"
563 msgstr "Активні нитки:"
564
564
565 msgid "No active threads today."
565 msgid "No active threads today."
566 msgstr "Щось усі замовкли."
566 msgstr "Щось усі замовкли."
567
567
568 msgid "Insert URLs on separate lines."
568 msgid "Insert URLs on separate lines."
569 msgstr "Вставляйте посилання окремими рядками."
569 msgstr "Вставляйте посилання окремими рядками."
570
570
571 msgid "You can post no more than %(files)d file."
571 msgid "You can post no more than %(files)d file."
572 msgid_plural "You can post no more than %(files)d files."
572 msgid_plural "You can post no more than %(files)d files."
573 msgstr[0] "Ви можете надіслати не більше %(files)d файлу."
573 msgstr[0] "Ви можете надіслати не більше %(files)d файлу."
574 msgstr[1] "Вы можете надіслати не більше %(files)d файлів."
574 msgstr[1] "Вы можете надіслати не більше %(files)d файлів."
575 msgstr[2] "Вы можете надіслати не більше %(files)d файлів."
575 msgstr[2] "Вы можете надіслати не більше %(files)d файлів."
576
576
577 #, python-format
577 #, python-format
578 msgid "Max file number is %(max_files)s."
578 msgid "Max file number is %(max_files)s."
579 msgstr "Максимальна кількість файлів %(max_files)s."
579 msgstr "Максимальна кількість файлів %(max_files)s."
580
580
581 msgid "Moderation"
581 msgid "Moderation"
582 msgstr "Модерація"
582 msgstr "Модерація"
583
583
584 msgid "Check for duplicates"
584 msgid "Check for duplicates"
585 msgstr "Перевіряти на дублікати"
585 msgstr "Перевіряти на дублікати"
586
586
587 msgid "Some files are already present on the board."
587 msgid "Some files are already present on the board."
588 msgstr "Деякі файли вже є на дошці."
588 msgstr "Деякі файли вже є на дошці."
589
589
590 msgid "Do not download URLs"
590 msgid "Do not download URLs"
591 msgstr "Не завантажувати посилання"
591 msgstr "Не завантажувати посилання"
592
592
593 msgid "Ban and delete"
593 msgid "Ban and delete"
594 msgstr "Заблокувати й видалити"
594 msgstr "Заблокувати й видалити"
595
595
596 msgid "Are you sure?"
596 msgid "Are you sure?"
597 msgstr "Чи ви певні?"
597 msgstr "Чи ви певні?"
598
598
599 msgid "Ban"
599 msgid "Ban"
600 msgstr "Заблокувати"
600 msgstr "Заблокувати"
601
601
602 msgid "URL download mode"
602 msgid "URL download mode"
603 msgstr "Режим завантаження посилань"
603 msgstr "Режим завантаження посилань"
604
604
605 msgid "Download if possible"
605 msgid "Download or add URL"
606 msgstr "Завантажувати якщо можливо"
606 msgstr "Завантажити або додати посилання"
607
607
608 msgid "Download"
608 msgid "Download or fail"
609 msgstr "Завантажувати"
609 msgstr "Завантажити або відмовити"
610
610
611 msgid "Insert as URLs"
611 msgid "Insert as URLs"
612 msgstr "Вставляти як посилання"
612 msgstr "Вставляти як посилання"
General Comments 0
You need to be logged in to leave comments. Login now