##// END OF EJS Templates
Added local stickers feature
neko259 -
r1940:1f7b0788 default
parent child Browse files
Show More
@@ -1,29 +1,29 b''
1 from boards.abstracts.settingsmanager import SessionSettingsManager
1 from boards.abstracts.settingsmanager import SessionSettingsManager
2 from boards.models import Attachment
2 from boards.models import Attachment
3
3
4
4
5 class AttachmentAlias:
5 class AttachmentAlias:
6 def get_image(self, alias):
6 def get_image(self, alias):
7 pass
7 pass
8
8
9
9
10 class SessionAttachmentAlias(AttachmentAlias):
10 class SessionAttachmentAlias(AttachmentAlias):
11 def __init__(self, session):
11 def __init__(self, session):
12 self.session = session
12 self.session = session
13
13
14 def get_image(self, alias):
14 def get_image(self, alias):
15 settings_manager = SessionSettingsManager(self.session)
15 settings_manager = SessionSettingsManager(self.session)
16 return settings_manager.get_image_by_alias(alias)
16 return settings_manager.get_attachment_by_alias(alias)
17
17
18
18
19 class ModelAttachmentAlias(AttachmentAlias):
19 class ModelAttachmentAlias(AttachmentAlias):
20 def get_image(self, alias):
20 def get_image(self, alias):
21 return Attachment.objects.get_by_alias(alias)
21 return Attachment.objects.get_by_alias(alias)
22
22
23
23
24 def get_image_by_alias(alias, session):
24 def get_image_by_alias(alias, session):
25 image = SessionAttachmentAlias(session).get_image(alias)\
25 image = SessionAttachmentAlias(session).get_image(alias)\
26 or ModelAttachmentAlias().get_image(alias)
26 or ModelAttachmentAlias().get_image(alias)
27
27
28 if image is not None:
28 if image is not None:
29 return image
29 return image
@@ -1,205 +1,219 b''
1 from boards import settings
1 from boards import settings
2 from boards.models import Tag, TagAlias
2 from boards.models import Tag, TagAlias, Attachment
3 from boards.models.attachment import AttachmentSticker
3 from boards.models.thread import FAV_THREAD_NO_UPDATES
4 from boards.models.thread import FAV_THREAD_NO_UPDATES
4 from boards.models.tag import DEFAULT_LOCALE
5 from boards.models.tag import DEFAULT_LOCALE
5
6
6 MAX_TRIPCODE_COLLISIONS = 50
7 MAX_TRIPCODE_COLLISIONS = 50
7
8
8 __author__ = 'neko259'
9 __author__ = 'neko259'
9
10
10 SESSION_SETTING = 'setting'
11 SESSION_SETTING = 'setting'
11
12
12 # Remove this, it is not used any more cause there is a user's permission
13 # Remove this, it is not used any more cause there is a user's permission
13 PERMISSION_MODERATE = 'moderator'
14 PERMISSION_MODERATE = 'moderator'
14
15
15 SETTING_THEME = 'theme'
16 SETTING_THEME = 'theme'
16 SETTING_FAVORITE_TAGS = 'favorite_tags'
17 SETTING_FAVORITE_TAGS = 'favorite_tags'
17 SETTING_FAVORITE_THREADS = 'favorite_threads'
18 SETTING_FAVORITE_THREADS = 'favorite_threads'
18 SETTING_HIDDEN_TAGS = 'hidden_tags'
19 SETTING_HIDDEN_TAGS = 'hidden_tags'
19 SETTING_PERMISSIONS = 'permissions'
20 SETTING_PERMISSIONS = 'permissions'
20 SETTING_USERNAME = 'username'
21 SETTING_USERNAME = 'username'
21 SETTING_LAST_NOTIFICATION_ID = 'last_notification'
22 SETTING_LAST_NOTIFICATION_ID = 'last_notification'
22 SETTING_IMAGE_VIEWER = 'image_viewer'
23 SETTING_IMAGE_VIEWER = 'image_viewer'
23 SETTING_TRIPCODE = 'tripcode'
24 SETTING_TRIPCODE = 'tripcode'
24 SETTING_IMAGES = 'images_aliases'
25 SETTING_IMAGES = 'images_aliases'
25 SETTING_ONLY_FAVORITES = 'only_favorites'
26 SETTING_ONLY_FAVORITES = 'only_favorites'
26
27
27 DEFAULT_THEME = 'md'
28 DEFAULT_THEME = 'md'
28
29
29
30
30 class SettingsManager:
31 class SettingsManager:
31 """
32 """
32 Base settings manager class. get_setting and set_setting methods should
33 Base settings manager class. get_setting and set_setting methods should
33 be overriden.
34 be overriden.
34 """
35 """
35 def __init__(self):
36 def __init__(self):
36 pass
37 pass
37
38
38 def get_theme(self) -> str:
39 def get_theme(self) -> str:
39 theme = self.get_setting(SETTING_THEME)
40 theme = self.get_setting(SETTING_THEME)
40 if not theme:
41 if not theme:
41 theme = DEFAULT_THEME
42 theme = DEFAULT_THEME
42 self.set_setting(SETTING_THEME, theme)
43 self.set_setting(SETTING_THEME, theme)
43
44
44 return theme
45 return theme
45
46
46 def set_theme(self, theme):
47 def set_theme(self, theme):
47 self.set_setting(SETTING_THEME, theme)
48 self.set_setting(SETTING_THEME, theme)
48
49
49 def has_permission(self, permission):
50 def has_permission(self, permission):
50 permissions = self.get_setting(SETTING_PERMISSIONS)
51 permissions = self.get_setting(SETTING_PERMISSIONS)
51 if permissions:
52 if permissions:
52 return permission in permissions
53 return permission in permissions
53 else:
54 else:
54 return False
55 return False
55
56
56 def get_setting(self, setting, default=None):
57 def get_setting(self, setting, default=None):
57 pass
58 pass
58
59
59 def set_setting(self, setting, value):
60 def set_setting(self, setting, value):
60 pass
61 pass
61
62
62 def add_permission(self, permission):
63 def add_permission(self, permission):
63 permissions = self.get_setting(SETTING_PERMISSIONS)
64 permissions = self.get_setting(SETTING_PERMISSIONS)
64 if not permissions:
65 if not permissions:
65 permissions = [permission]
66 permissions = [permission]
66 else:
67 else:
67 permissions.append(permission)
68 permissions.append(permission)
68 self.set_setting(SETTING_PERMISSIONS, permissions)
69 self.set_setting(SETTING_PERMISSIONS, permissions)
69
70
70 def del_permission(self, permission):
71 def del_permission(self, permission):
71 permissions = self.get_setting(SETTING_PERMISSIONS)
72 permissions = self.get_setting(SETTING_PERMISSIONS)
72 if not permissions:
73 if not permissions:
73 permissions = []
74 permissions = []
74 else:
75 else:
75 permissions.remove(permission)
76 permissions.remove(permission)
76 self.set_setting(SETTING_PERMISSIONS, permissions)
77 self.set_setting(SETTING_PERMISSIONS, permissions)
77
78
78 def get_fav_tags(self) -> list:
79 def get_fav_tags(self) -> list:
79 tag_names = self.get_setting(SETTING_FAVORITE_TAGS)
80 tag_names = self.get_setting(SETTING_FAVORITE_TAGS)
80 tags = []
81 tags = []
81 if tag_names:
82 if tag_names:
82 tags = list(Tag.objects.filter(aliases__in=TagAlias.objects
83 tags = list(Tag.objects.filter(aliases__in=TagAlias.objects
83 .filter_localized(parent__aliases__name__in=tag_names))
84 .filter_localized(parent__aliases__name__in=tag_names))
84 .order_by('aliases__name'))
85 .order_by('aliases__name'))
85 return tags
86 return tags
86
87
87 def add_fav_tag(self, tag):
88 def add_fav_tag(self, tag):
88 tags = self.get_setting(SETTING_FAVORITE_TAGS)
89 tags = self.get_setting(SETTING_FAVORITE_TAGS)
89 if not tags:
90 if not tags:
90 tags = [tag.get_name()]
91 tags = [tag.get_name()]
91 else:
92 else:
92 if not tag.get_name() in tags:
93 if not tag.get_name() in tags:
93 tags.append(tag.get_name())
94 tags.append(tag.get_name())
94
95
95 tags.sort()
96 tags.sort()
96 self.set_setting(SETTING_FAVORITE_TAGS, tags)
97 self.set_setting(SETTING_FAVORITE_TAGS, tags)
97
98
98 def del_fav_tag(self, tag):
99 def del_fav_tag(self, tag):
99 tags = self.get_setting(SETTING_FAVORITE_TAGS)
100 tags = self.get_setting(SETTING_FAVORITE_TAGS)
100 if tag.get_name() in tags:
101 if tag.get_name() in tags:
101 tags.remove(tag.get_name())
102 tags.remove(tag.get_name())
102 self.set_setting(SETTING_FAVORITE_TAGS, tags)
103 self.set_setting(SETTING_FAVORITE_TAGS, tags)
103
104
104 def get_hidden_tags(self) -> list:
105 def get_hidden_tags(self) -> list:
105 tag_names = self.get_setting(SETTING_HIDDEN_TAGS)
106 tag_names = self.get_setting(SETTING_HIDDEN_TAGS)
106 tags = []
107 tags = []
107 if tag_names:
108 if tag_names:
108 tags = list(Tag.objects.filter(aliases__in=TagAlias.objects
109 tags = list(Tag.objects.filter(aliases__in=TagAlias.objects
109 .filter_localized(parent__aliases__name__in=tag_names))
110 .filter_localized(parent__aliases__name__in=tag_names))
110 .order_by('aliases__name'))
111 .order_by('aliases__name'))
111
112
112 return tags
113 return tags
113
114
114 def add_hidden_tag(self, tag):
115 def add_hidden_tag(self, tag):
115 tags = self.get_setting(SETTING_HIDDEN_TAGS)
116 tags = self.get_setting(SETTING_HIDDEN_TAGS)
116 if not tags:
117 if not tags:
117 tags = [tag.get_name()]
118 tags = [tag.get_name()]
118 else:
119 else:
119 if not tag.get_name() in tags:
120 if not tag.get_name() in tags:
120 tags.append(tag.get_name())
121 tags.append(tag.get_name())
121
122
122 tags.sort()
123 tags.sort()
123 self.set_setting(SETTING_HIDDEN_TAGS, tags)
124 self.set_setting(SETTING_HIDDEN_TAGS, tags)
124
125
125 def del_hidden_tag(self, tag):
126 def del_hidden_tag(self, tag):
126 tags = self.get_setting(SETTING_HIDDEN_TAGS)
127 tags = self.get_setting(SETTING_HIDDEN_TAGS)
127 if tag.get_name() in tags:
128 if tag.get_name() in tags:
128 tags.remove(tag.get_name())
129 tags.remove(tag.get_name())
129 self.set_setting(SETTING_HIDDEN_TAGS, tags)
130 self.set_setting(SETTING_HIDDEN_TAGS, tags)
130
131
131 def get_fav_threads(self) -> dict:
132 def get_fav_threads(self) -> dict:
132 return self.get_setting(SETTING_FAVORITE_THREADS, default=dict())
133 return self.get_setting(SETTING_FAVORITE_THREADS, default=dict())
133
134
134 def add_or_read_fav_thread(self, opening_post):
135 def add_or_read_fav_thread(self, opening_post):
135 threads = self.get_fav_threads()
136 threads = self.get_fav_threads()
136
137
137 max_fav_threads = settings.get_int('View', 'MaxFavoriteThreads')
138 max_fav_threads = settings.get_int('View', 'MaxFavoriteThreads')
138 if (str(opening_post.id) in threads) or (len(threads) < max_fav_threads):
139 if (str(opening_post.id) in threads) or (len(threads) < max_fav_threads):
139 thread = opening_post.get_thread()
140 thread = opening_post.get_thread()
140 # Don't check for new posts if the thread is archived already
141 # Don't check for new posts if the thread is archived already
141 if thread.is_archived():
142 if thread.is_archived():
142 last_id = FAV_THREAD_NO_UPDATES
143 last_id = FAV_THREAD_NO_UPDATES
143 else:
144 else:
144 last_id = thread.get_replies().last().id
145 last_id = thread.get_replies().last().id
145 threads[str(opening_post.id)] = last_id
146 threads[str(opening_post.id)] = last_id
146 self.set_setting(SETTING_FAVORITE_THREADS, threads)
147 self.set_setting(SETTING_FAVORITE_THREADS, threads)
147
148
148 def del_fav_thread(self, opening_post):
149 def del_fav_thread(self, opening_post):
149 threads = self.get_fav_threads()
150 threads = self.get_fav_threads()
150 if self.thread_is_fav(opening_post):
151 if self.thread_is_fav(opening_post):
151 del threads[str(opening_post.id)]
152 del threads[str(opening_post.id)]
152 self.set_setting(SETTING_FAVORITE_THREADS, threads)
153 self.set_setting(SETTING_FAVORITE_THREADS, threads)
153
154
154 def thread_is_fav(self, opening_post):
155 def thread_is_fav(self, opening_post):
155 return str(opening_post.id) in self.get_fav_threads()
156 return str(opening_post.id) in self.get_fav_threads()
156
157
157 def get_notification_usernames(self):
158 def get_notification_usernames(self):
158 names = set()
159 names = set()
159 name_list = self.get_setting(SETTING_USERNAME)
160 name_list = self.get_setting(SETTING_USERNAME)
160 if name_list is not None:
161 if name_list is not None:
161 name_list = name_list.strip()
162 name_list = name_list.strip()
162 if len(name_list) > 0:
163 if len(name_list) > 0:
163 names = name_list.lower().split(',')
164 names = name_list.lower().split(',')
164 names = set(name.strip() for name in names)
165 names = set(name.strip() for name in names)
165 return names
166 return names
166
167
167 def get_image_by_alias(self, alias):
168 def get_attachment_by_alias(self, alias):
168 images = self.get_setting(SETTING_IMAGES)
169 images = self.get_setting(SETTING_IMAGES)
169 if images is not None and len(images) > 0:
170 if images and alias in images:
170 return images.get(alias)
171 return Attachment.objects.get(id=images.get(alias))
171
172
172 def add_image_alias(self, alias, image):
173 def add_attachment_alias(self, alias, attachment):
173 images = self.get_setting(SETTING_IMAGES)
174 images = self.get_setting(SETTING_IMAGES)
174 if images is None:
175 if images is None:
175 images = dict()
176 images = dict()
176 images.put(alias, image)
177 images[alias] = attachment.id
178 self.set_setting(SETTING_IMAGES, images)
179
180 def remove_attachment_alias(self, alias):
181 images = self.get_setting(SETTING_IMAGES)
182 del images[alias]
183 self.set_setting(SETTING_IMAGES, images)
184
185 def get_stickers(self):
186 images = self.get_setting(SETTING_IMAGES)
187 if images:
188 return [AttachmentSticker(name=key,
189 attachment=Attachment.objects.get(id=value))
190 for key, value in images.items()]
177
191
178
192
179 class SessionSettingsManager(SettingsManager):
193 class SessionSettingsManager(SettingsManager):
180 """
194 """
181 Session-based settings manager. All settings are saved to the user's
195 Session-based settings manager. All settings are saved to the user's
182 session.
196 session.
183 """
197 """
184 def __init__(self, session):
198 def __init__(self, session):
185 SettingsManager.__init__(self)
199 SettingsManager.__init__(self)
186 self.session = session
200 self.session = session
187
201
188 def get_setting(self, setting, default=None):
202 def get_setting(self, setting, default=None):
189 if setting in self.session:
203 if setting in self.session:
190 return self.session[setting]
204 return self.session[setting]
191 else:
205 else:
192 self.set_setting(setting, default)
206 self.set_setting(setting, default)
193 return default
207 return default
194
208
195 def set_setting(self, setting, value):
209 def set_setting(self, setting, value):
196 self.session[setting] = value
210 self.session[setting] = value
197
211
198
212
199 def get_settings_manager(request) -> SettingsManager:
213 def get_settings_manager(request) -> SettingsManager:
200 """
214 """
201 Get settings manager based on the request object. Currently only
215 Get settings manager based on the request object. Currently only
202 session-based manager is supported. In the future, cookie-based or
216 session-based manager is supported. In the future, cookie-based or
203 database-based managers could be implemented.
217 database-based managers could be implemented.
204 """
218 """
205 return SessionSettingsManager(request.session)
219 return SessionSettingsManager(request.session)
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,624 +1,633 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 or add URL"
605 msgid "Download or add URL"
606 msgstr "Π—Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ ΠΈΠ»ΠΈ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ссылку"
606 msgstr "Π—Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ ΠΈΠ»ΠΈ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ссылку"
607
607
608 msgid "Download or fail"
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 "Π’ΡΡ‚Π°Π²Π»ΡΡ‚ΡŒ ΠΊΠ°ΠΊ ссылки"
613
613
614 msgid "Help"
614 msgid "Help"
615 msgstr "Π‘ΠΏΡ€Π°Π²ΠΊΠ°"
615 msgstr "Π‘ΠΏΡ€Π°Π²ΠΊΠ°"
616
616
617 msgid "View available stickers:"
617 msgid "View available stickers:"
618 msgstr "ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ доступныС стикСры:"
618 msgstr "ΠŸΡ€ΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ доступныС стикСры:"
619
619
620 msgid "Stickers"
620 msgid "Stickers"
621 msgstr "Π‘Ρ‚ΠΈΠΊΠ΅Ρ€Ρ‹"
621 msgstr "Π‘Ρ‚ΠΈΠΊΠ΅Ρ€Ρ‹"
622
622
623 msgid "Available by addresses:"
623 msgid "Available by addresses:"
624 msgstr "Доступно ΠΏΠΎ адрСсам:"
624 msgstr "Доступно ΠΏΠΎ адрСсам:"
625
626 msgid "Local stickers"
627 msgstr "Π›ΠΎΠΊΠ°Π»ΡŒΠ½Ρ‹Π΅ стикСры"
628
629 msgid "Global stickers"
630 msgstr "Π“Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹Π΅ стикСры"
631
632 msgid "Remove sticker"
633 msgstr "Π£Π΄Π°Π»ΠΈΡ‚ΡŒ стикСр"
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,61 +1,63 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 #, fuzzy
6 #, fuzzy
7 msgid ""
7 msgid ""
8 msgstr ""
8 msgstr ""
9 "Project-Id-Version: PACKAGE VERSION\n"
9 "Project-Id-Version: PACKAGE VERSION\n"
10 "Report-Msgid-Bugs-To: \n"
10 "Report-Msgid-Bugs-To: \n"
11 "POT-Creation-Date: 2015-09-04 18:47+0300\n"
11 "POT-Creation-Date: 2015-09-04 18:47+0300\n"
12 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14 "Language-Team: LANGUAGE <LL@li.org>\n"
14 "Language-Team: LANGUAGE <LL@li.org>\n"
15 "Language: \n"
15 "Language: \n"
16 "MIME-Version: 1.0\n"
16 "MIME-Version: 1.0\n"
17 "Content-Type: text/plain; charset=UTF-8\n"
17 "Content-Type: text/plain; charset=UTF-8\n"
18 "Content-Transfer-Encoding: 8bit\n"
18 "Content-Transfer-Encoding: 8bit\n"
19 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
19 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
20 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
20 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
21
21
22 #: static/js/3party/jquery-ui.min.js:8
22 #: static/js/3party/jquery-ui.min.js:8
23 msgid "'"
23 msgid "'"
24 msgstr ""
24 msgstr ""
25
25
26 #: static/js/refpopup.js:72
26 #: static/js/refpopup.js:72
27 msgid "Loading..."
27 msgid "Loading..."
28 msgstr "Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ°..."
28 msgstr "Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ°..."
29
29
30 #: static/js/refpopup.js:91
30 #: static/js/refpopup.js:91
31 msgid "Post not found"
31 msgid "Post not found"
32 msgstr "Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ΠΎ"
32 msgstr "Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½ΠΎ"
33
33
34 #: static/js/thread_update.js:261
34 #: static/js/thread_update.js:261
35 msgid "message"
35 msgid "message"
36 msgid_plural "messages"
36 msgid_plural "messages"
37 msgstr[0] "сообщСниС"
37 msgstr[0] "сообщСниС"
38 msgstr[1] "сообщСния"
38 msgstr[1] "сообщСния"
39 msgstr[2] "сообщСний"
39 msgstr[2] "сообщСний"
40
40
41 #: static/js/thread_update.js:262
41 #: static/js/thread_update.js:262
42 msgid "image"
42 msgid "image"
43 msgid_plural "images"
43 msgid_plural "images"
44 msgstr[0] "ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅"
44 msgstr[0] "ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅"
45 msgstr[1] "изобраТСния"
45 msgstr[1] "изобраТСния"
46 msgstr[2] "ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ"
46 msgstr[2] "ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ"
47
47
48 #: static/js/thread_update.js:445
48 #: static/js/thread_update.js:445
49 msgid "Sending message..."
49 msgid "Sending message..."
50 msgstr "ΠžΡ‚ΠΏΡ€Π°Π²ΠΊΠ° сообщСния..."
50 msgstr "ΠžΡ‚ΠΏΡ€Π°Π²ΠΊΠ° сообщСния..."
51
51
52 #: static/js/thread_update.js:449
52 #: static/js/thread_update.js:449
53 msgid "Server error!"
53 msgid "Server error!"
54 msgstr "Ошибка сСрвСра!"
54 msgstr "Ошибка сСрвСра!"
55
55
56 msgid "Computing PoW..."
56 msgid "Computing PoW..."
57 msgstr "Расчёт PoW..."
57 msgstr "Расчёт PoW..."
58
58
59 msgid "Duplicates search"
59 msgid "Duplicates search"
60 msgstr "Поиск Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ‚ΠΎΠ²"
60 msgstr "Поиск Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ‚ΠΎΠ²"
61
61
62 msgid "Add local sticker"
63 msgstr "Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π»ΠΎΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ стикСр"
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,624 +1,633 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 or add URL"
605 msgid "Download or add URL"
606 msgstr "Π—Π°Π²Π°Π½Ρ‚Π°ΠΆΠΈΡ‚ΠΈ Π°Π±ΠΎ Π΄ΠΎΠ΄Π°Ρ‚ΠΈ посилання"
606 msgstr "Π—Π°Π²Π°Π½Ρ‚Π°ΠΆΠΈΡ‚ΠΈ Π°Π±ΠΎ Π΄ΠΎΠ΄Π°Ρ‚ΠΈ посилання"
607
607
608 msgid "Download or fail"
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 "Вставляти як посилання"
613
613
614 msgid "Help"
614 msgid "Help"
615 msgstr "Π‘ΠΏΡ€Π°Π²ΠΊΠ°"
615 msgstr "Π‘ΠΏΡ€Π°Π²ΠΊΠ°"
616
616
617 msgid "View available stickers:"
617 msgid "View available stickers:"
618 msgstr "ΠŸΠ΅Ρ€Π΅Π΄ΠΈΠ²ΠΈΡ‚ΠΈΡΡ доступні стікСри:"
618 msgstr "ΠŸΠ΅Ρ€Π΅Π΄ΠΈΠ²ΠΈΡ‚ΠΈΡΡ доступні стікСри:"
619
619
620 msgid "Stickers"
620 msgid "Stickers"
621 msgstr "Π‘Ρ‚Ρ–ΠΊΠ΅Ρ€ΠΈ"
621 msgstr "Π‘Ρ‚Ρ–ΠΊΠ΅Ρ€ΠΈ"
622
622
623 msgid "Available by addresses:"
623 msgid "Available by addresses:"
624 msgstr "Доступно Π·Π° адрСсами:"
624 msgstr "Доступно Π·Π° адрСсами:"
625
626 msgid "Local stickers"
627 msgstr "Π›ΠΎΠΊΠ°Π»ΡŒΠ½Ρ– стікСри"
628
629 msgid "Global stickers"
630 msgstr "Π“Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ– стікСри"
631
632 msgid "Remove sticker"
633 msgstr "Π’ΠΈΠ΄Π°Π»ΠΈΡ‚ΠΈ стікСр"
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,61 +1,63 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 #, fuzzy
6 #, fuzzy
7 msgid ""
7 msgid ""
8 msgstr ""
8 msgstr ""
9 "Project-Id-Version: PACKAGE VERSION\n"
9 "Project-Id-Version: PACKAGE VERSION\n"
10 "Report-Msgid-Bugs-To: \n"
10 "Report-Msgid-Bugs-To: \n"
11 "POT-Creation-Date: 2015-09-04 18:47+0300\n"
11 "POT-Creation-Date: 2015-09-04 18:47+0300\n"
12 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14 "Language-Team: LANGUAGE <LL@li.org>\n"
14 "Language-Team: LANGUAGE <LL@li.org>\n"
15 "Language: \n"
15 "Language: \n"
16 "MIME-Version: 1.0\n"
16 "MIME-Version: 1.0\n"
17 "Content-Type: text/plain; charset=UTF-8\n"
17 "Content-Type: text/plain; charset=UTF-8\n"
18 "Content-Transfer-Encoding: 8bit\n"
18 "Content-Transfer-Encoding: 8bit\n"
19 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
19 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
20 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
20 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
21
21
22 #: static/js/3party/jquery-ui.min.js:8
22 #: static/js/3party/jquery-ui.min.js:8
23 msgid "'"
23 msgid "'"
24 msgstr ""
24 msgstr ""
25
25
26 #: static/js/refpopup.js:72
26 #: static/js/refpopup.js:72
27 msgid "Loading..."
27 msgid "Loading..."
28 msgstr "ЗавантаТСння..."
28 msgstr "ЗавантаТСння..."
29
29
30 #: static/js/refpopup.js:91
30 #: static/js/refpopup.js:91
31 msgid "Post not found"
31 msgid "Post not found"
32 msgstr "ΠŸΠΎΠ²Ρ–Π΄ΠΎΠΌΠ»Π΅Π½Π½Ρ Π½Π΅ Π·Π½Π°ΠΉΠ΄Π΅Π½Π΅"
32 msgstr "ΠŸΠΎΠ²Ρ–Π΄ΠΎΠΌΠ»Π΅Π½Π½Ρ Π½Π΅ Π·Π½Π°ΠΉΠ΄Π΅Π½Π΅"
33
33
34 #: static/js/thread_update.js:261
34 #: static/js/thread_update.js:261
35 msgid "message"
35 msgid "message"
36 msgid_plural "messages"
36 msgid_plural "messages"
37 msgstr[0] "повідомлСння"
37 msgstr[0] "повідомлСння"
38 msgstr[1] "повідомлСння"
38 msgstr[1] "повідомлСння"
39 msgstr[2] "ΠΏΠΎΠ²Ρ–Π΄ΠΎΠΌΠ»Π΅Π½ΡŒ"
39 msgstr[2] "ΠΏΠΎΠ²Ρ–Π΄ΠΎΠΌΠ»Π΅Π½ΡŒ"
40
40
41 #: static/js/thread_update.js:262
41 #: static/js/thread_update.js:262
42 msgid "image"
42 msgid "image"
43 msgid_plural "images"
43 msgid_plural "images"
44 msgstr[0] "зобраТСння"
44 msgstr[0] "зобраТСння"
45 msgstr[1] "зобраТСння"
45 msgstr[1] "зобраТСння"
46 msgstr[2] "Π·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΡŒ"
46 msgstr[2] "Π·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΡŒ"
47
47
48 #: static/js/thread_update.js:445
48 #: static/js/thread_update.js:445
49 msgid "Sending message..."
49 msgid "Sending message..."
50 msgstr "ΠŸΠΎΠ²Ρ–Π΄ΠΎΠΌΠ»Π΅Π½Π½Ρ Π½Π°Π΄ΡΠΈΠ»Π°Ρ”Ρ‚ΡŒΡΡ..."
50 msgstr "ΠŸΠΎΠ²Ρ–Π΄ΠΎΠΌΠ»Π΅Π½Π½Ρ Π½Π°Π΄ΡΠΈΠ»Π°Ρ”Ρ‚ΡŒΡΡ..."
51
51
52 #: static/js/thread_update.js:449
52 #: static/js/thread_update.js:449
53 msgid "Server error!"
53 msgid "Server error!"
54 msgstr "Π‘Π΅Ρ€Π²Π΅Ρ€ Π½Π΅Π·Π΄ΡƒΠΆΠ°Ρ”! Π—Π°Ρ…ΠΎΠ΄ΡŒΡ‚Π΅ ΠΏΡ–Π·Π½Ρ–ΡˆΠ΅!"
54 msgstr "Π‘Π΅Ρ€Π²Π΅Ρ€ Π½Π΅Π·Π΄ΡƒΠΆΠ°Ρ”! Π—Π°Ρ…ΠΎΠ΄ΡŒΡ‚Π΅ ΠΏΡ–Π·Π½Ρ–ΡˆΠ΅!"
55
55
56 msgid "Computing PoW..."
56 msgid "Computing PoW..."
57 msgstr "Π ΠΎΠ·Ρ€Π°Ρ…ΠΎΠ²ΡƒΡ”Ρ‚ΡŒΡΡ PoW..."
57 msgstr "Π ΠΎΠ·Ρ€Π°Ρ…ΠΎΠ²ΡƒΡ”Ρ‚ΡŒΡΡ PoW..."
58
58
59 msgid "Duplicates search"
59 msgid "Duplicates search"
60 msgstr "ΠŸΠΎΡˆΡƒΠΊ Π΄ΡƒΠ±Π»Ρ–ΠΊΠ°Ρ‚Ρ–Π²"
60 msgstr "ΠŸΠΎΡˆΡƒΠΊ Π΄ΡƒΠ±Π»Ρ–ΠΊΠ°Ρ‚Ρ–Π²"
61
61
62 msgid "Add local sticker"
63 msgstr "Π”ΠΎΠ΄Π°Ρ‚ΠΈ локальний стікСр"
@@ -1,171 +1,171 b''
1 from itertools import zip_longest
1 from itertools import zip_longest
2
2
3 import boards
3 import boards
4 from boards.models import STATUS_ARCHIVE
4 from boards.models import STATUS_ARCHIVE
5 from django.core.files.images import get_image_dimensions
5 from django.core.files.images import get_image_dimensions
6 from django.db import models
6 from django.db import models
7
7
8 from boards import utils
8 from boards import utils
9 from boards.models.attachment.viewers import get_viewers, AbstractViewer, \
9 from boards.models.attachment.viewers import get_viewers, AbstractViewer, \
10 FILE_TYPES_IMAGE
10 FILE_TYPES_IMAGE
11 from boards.utils import get_upload_filename, get_extension, cached_result, \
11 from boards.utils import get_upload_filename, get_extension, cached_result, \
12 get_file_mimetype
12 get_file_mimetype
13
13
14
14
15 class AttachmentManager(models.Manager):
15 class AttachmentManager(models.Manager):
16 def create_with_hash(self, file):
16 def create_with_hash(self, file):
17 file_hash = utils.get_file_hash(file)
17 file_hash = utils.get_file_hash(file)
18 attachment = self.get_existing_duplicate(file_hash, file)
18 attachment = self.get_existing_duplicate(file_hash, file)
19 if not attachment:
19 if not attachment:
20 file_type = get_file_mimetype(file)
20 file_type = get_file_mimetype(file)
21 attachment = self.create(file=file, mimetype=file_type,
21 attachment = self.create(file=file, mimetype=file_type,
22 hash=file_hash)
22 hash=file_hash)
23
23
24 return attachment
24 return attachment
25
25
26 def create_from_url(self, url):
26 def create_from_url(self, url):
27 existing = self.filter(url=url)
27 existing = self.filter(url=url)
28 if len(existing) > 0:
28 if len(existing) > 0:
29 attachment = existing[0]
29 attachment = existing[0]
30 else:
30 else:
31 attachment = self.create(url=url)
31 attachment = self.create(url=url)
32 return attachment
32 return attachment
33
33
34 def get_random_images(self, count, tags=None):
34 def get_random_images(self, count, tags=None):
35 images = self.filter(mimetype__in=FILE_TYPES_IMAGE).exclude(
35 images = self.filter(mimetype__in=FILE_TYPES_IMAGE).exclude(
36 attachment_posts__thread__status=STATUS_ARCHIVE)
36 attachment_posts__thread__status=STATUS_ARCHIVE)
37 if tags is not None:
37 if tags is not None:
38 images = images.filter(attachment_posts__threads__tags__in=tags)
38 images = images.filter(attachment_posts__threads__tags__in=tags)
39 return images.order_by('?')[:count]
39 return images.order_by('?')[:count]
40
40
41 def get_existing_duplicate(self, file_hash, file):
41 def get_existing_duplicate(self, file_hash, file):
42 """
42 """
43 Gets an attachment with the same file if one exists.
43 Gets an attachment with the same file if one exists.
44 """
44 """
45 existing = self.filter(hash=file_hash)
45 existing = self.filter(hash=file_hash)
46 attachment = None
46 attachment = None
47 for existing_attachment in existing:
47 for existing_attachment in existing:
48 existing_file = existing_attachment.file
48 existing_file = existing_attachment.file
49
49
50 file_chunks = file.chunks()
50 file_chunks = file.chunks()
51 existing_file_chunks = existing_file.chunks()
51 existing_file_chunks = existing_file.chunks()
52
52
53 if self._compare_chunks(file_chunks, existing_file_chunks):
53 if self._compare_chunks(file_chunks, existing_file_chunks):
54 attachment = existing_attachment
54 attachment = existing_attachment
55 return attachment
55 return attachment
56
56
57 def get_by_alias(self, name):
57 def get_by_alias(self, name):
58 try:
58 try:
59 return AttachmentSticker.objects.get(name=name).attachment
59 return AttachmentSticker.objects.get(name=name).attachment
60 except AttachmentSticker.DoesNotExist:
60 except AttachmentSticker.DoesNotExist:
61 return None
61 return None
62
62
63 def _compare_chunks(self, chunks1, chunks2):
63 def _compare_chunks(self, chunks1, chunks2):
64 """
64 """
65 Compares 2 chunks of different sizes (e.g. first chunk array contains
65 Compares 2 chunks of different sizes (e.g. first chunk array contains
66 all data in 1 chunk, and other one -- in a multiple of smaller ones.
66 all data in 1 chunk, and other one -- in a multiple of smaller ones.
67 """
67 """
68 equal = True
68 equal = True
69
69
70 position1 = 0
70 position1 = 0
71 position2 = 0
71 position2 = 0
72 chunk1 = None
72 chunk1 = None
73 chunk2 = None
73 chunk2 = None
74 chunk1ended = False
74 chunk1ended = False
75 chunk2ended = False
75 chunk2ended = False
76 while True:
76 while True:
77 if not chunk1 or len(chunk1) <= position1:
77 if not chunk1 or len(chunk1) <= position1:
78 try:
78 try:
79 chunk1 = chunks1.__next__()
79 chunk1 = chunks1.__next__()
80 position1 = 0
80 position1 = 0
81 except StopIteration:
81 except StopIteration:
82 chunk1ended = True
82 chunk1ended = True
83 if not chunk2 or len(chunk2) <= position2:
83 if not chunk2 or len(chunk2) <= position2:
84 try:
84 try:
85 chunk2 = chunks2.__next__()
85 chunk2 = chunks2.__next__()
86 position2 = 0
86 position2 = 0
87 except StopIteration:
87 except StopIteration:
88 chunk2ended = True
88 chunk2ended = True
89
89
90 if chunk1ended and chunk2ended:
90 if chunk1ended and chunk2ended:
91 # Same size chunksm checked for equality previously
91 # Same size chunksm checked for equality previously
92 break
92 break
93 elif chunk1ended or chunk2ended:
93 elif chunk1ended or chunk2ended:
94 # Different size chunks, not equal
94 # Different size chunks, not equal
95 equal = False
95 equal = False
96 break
96 break
97 elif chunk1[position1] != chunk2[position2]:
97 elif chunk1[position1] != chunk2[position2]:
98 # Different bytes, not equal
98 # Different bytes, not equal
99 equal = False
99 equal = False
100 break
100 break
101 else:
101 else:
102 position1 += 1
102 position1 += 1
103 position2 += 1
103 position2 += 1
104 return equal
104 return equal
105
105
106
106
107 class Attachment(models.Model):
107 class Attachment(models.Model):
108 objects = AttachmentManager()
108 objects = AttachmentManager()
109
109
110 class Meta:
110 class Meta:
111 app_label = 'boards'
111 app_label = 'boards'
112 ordering = ('id',)
112 ordering = ('id',)
113
113
114 file = models.FileField(upload_to=get_upload_filename, null=True)
114 file = models.FileField(upload_to=get_upload_filename, null=True)
115 mimetype = models.CharField(max_length=200, null=True)
115 mimetype = models.CharField(max_length=200, null=True)
116 hash = models.CharField(max_length=36, null=True)
116 hash = models.CharField(max_length=36, null=True)
117 url = models.TextField(blank=True, default='')
117 url = models.TextField(blank=True, default='')
118
118
119 def get_view(self):
119 def get_view(self):
120 file_viewer = None
120 file_viewer = None
121 for viewer in get_viewers():
121 for viewer in get_viewers():
122 if viewer.supports(self.mimetype):
122 if viewer.supports(self.mimetype):
123 file_viewer = viewer
123 file_viewer = viewer
124 break
124 break
125 if file_viewer is None:
125 if file_viewer is None:
126 file_viewer = AbstractViewer
126 file_viewer = AbstractViewer
127
127
128 return file_viewer(self.file, self.mimetype, self.hash, self.url).get_view()
128 return file_viewer(self.file, self.mimetype, self.id, self.url).get_view()
129
129
130 def __str__(self):
130 def __str__(self):
131 return self.url or self.file.url
131 return self.url or self.file.url
132
132
133 def get_random_associated_post(self):
133 def get_random_associated_post(self):
134 posts = boards.models.Post.objects.filter(attachments__in=[self])
134 posts = boards.models.Post.objects.filter(attachments__in=[self])
135 return posts.order_by('?').first()
135 return posts.order_by('?').first()
136
136
137 @cached_result()
137 @cached_result()
138 def get_size(self):
138 def get_size(self):
139 if self.file:
139 if self.file:
140 if self.mimetype in FILE_TYPES_IMAGE:
140 if self.mimetype in FILE_TYPES_IMAGE:
141 return get_image_dimensions(self.file)
141 return get_image_dimensions(self.file)
142 else:
142 else:
143 return 200, 150
143 return 200, 150
144
144
145 def get_thumb_url(self):
145 def get_thumb_url(self):
146 split = self.file.url.rsplit('.', 1)
146 split = self.file.url.rsplit('.', 1)
147 w, h = 200, 150
147 w, h = 200, 150
148 return '%s.%sx%s.%s' % (split[0], w, h, split[1])
148 return '%s.%sx%s.%s' % (split[0], w, h, split[1])
149
149
150 @cached_result()
150 @cached_result()
151 def get_preview_size(self):
151 def get_preview_size(self):
152 size = 200, 150
152 size = 200, 150
153 if self.mimetype in FILE_TYPES_IMAGE:
153 if self.mimetype in FILE_TYPES_IMAGE:
154 preview_path = self.file.path.replace('.', '.200x150.')
154 preview_path = self.file.path.replace('.', '.200x150.')
155 try:
155 try:
156 size = get_image_dimensions(preview_path)
156 size = get_image_dimensions(preview_path)
157 except Exception:
157 except Exception:
158 pass
158 pass
159
159
160 return size
160 return size
161
161
162 def is_internal(self):
162 def is_internal(self):
163 return self.url is None or len(self.url) == 0
163 return self.url is None or len(self.url) == 0
164
164
165
165
166 class AttachmentSticker(models.Model):
166 class AttachmentSticker(models.Model):
167 attachment = models.ForeignKey('Attachment')
167 attachment = models.ForeignKey('Attachment')
168 name = models.TextField(unique=True)
168 name = models.TextField(unique=True)
169
169
170 def __str__(self):
170 def __str__(self):
171 return self.name
171 return self.name
@@ -1,244 +1,244 b''
1 import re
1 import re
2
2
3 from PIL import Image
3 from PIL import Image
4
4
5 from django.contrib.staticfiles import finders
5 from django.contrib.staticfiles import finders
6 from django.contrib.staticfiles.templatetags.staticfiles import static
6 from django.contrib.staticfiles.templatetags.staticfiles import static
7 from django.core.files.images import get_image_dimensions
7 from django.core.files.images import get_image_dimensions
8 from django.template.defaultfilters import filesizeformat
8 from django.template.defaultfilters import filesizeformat
9 from django.core.urlresolvers import reverse
9 from django.core.urlresolvers import reverse
10 from django.utils.translation import ugettext_lazy as _, ungettext_lazy
10 from django.utils.translation import ugettext_lazy as _, ungettext_lazy
11
11
12 from boards.utils import get_domain, cached_result, get_extension
12 from boards.utils import get_domain, cached_result, get_extension
13 from boards import settings
13 from boards import settings
14
14
15
15
16 FILE_STUB_IMAGE = 'images/file.png'
16 FILE_STUB_IMAGE = 'images/file.png'
17 FILE_STUB_URL = 'url'
17 FILE_STUB_URL = 'url'
18 FILE_FILEFORMAT = 'images/fileformats/{}.png'
18 FILE_FILEFORMAT = 'images/fileformats/{}.png'
19
19
20
20
21 FILE_TYPES_VIDEO = (
21 FILE_TYPES_VIDEO = (
22 'video/webm',
22 'video/webm',
23 'video/mp4',
23 'video/mp4',
24 'video/mpeg',
24 'video/mpeg',
25 'video/ogv',
25 'video/ogv',
26 )
26 )
27 FILE_TYPE_SVG = 'image/svg+xml'
27 FILE_TYPE_SVG = 'image/svg+xml'
28 FILE_TYPES_AUDIO = (
28 FILE_TYPES_AUDIO = (
29 'audio/ogg',
29 'audio/ogg',
30 'audio/mpeg',
30 'audio/mpeg',
31 'audio/opus',
31 'audio/opus',
32 'audio/x-flac',
32 'audio/x-flac',
33 'audio/mpeg',
33 'audio/mpeg',
34 )
34 )
35 FILE_TYPES_IMAGE = (
35 FILE_TYPES_IMAGE = (
36 'image/jpeg',
36 'image/jpeg',
37 'image/jpg',
37 'image/jpg',
38 'image/png',
38 'image/png',
39 'image/bmp',
39 'image/bmp',
40 'image/gif',
40 'image/gif',
41 )
41 )
42
42
43 PLAIN_FILE_FORMATS = {
43 PLAIN_FILE_FORMATS = {
44 'zip': 'archive',
44 'zip': 'archive',
45 'tar': 'archive',
45 'tar': 'archive',
46 'gz': 'archive',
46 'gz': 'archive',
47 'mid' : 'midi',
47 'mid' : 'midi',
48 }
48 }
49
49
50 URL_PROTOCOLS = {
50 URL_PROTOCOLS = {
51 'magnet': 'magnet',
51 'magnet': 'magnet',
52 }
52 }
53
53
54 CSS_CLASS_IMAGE = 'image'
54 CSS_CLASS_IMAGE = 'image'
55 CSS_CLASS_THUMB = 'thumb'
55 CSS_CLASS_THUMB = 'thumb'
56
56
57 ABSTRACT_VIEW = '<div class="image">'\
57 ABSTRACT_VIEW = '<div class="image">'\
58 '{}'\
58 '{}'\
59 '<div class="image-metadata"><a href="{}" download >{}, {}</a>'\
59 '<div class="image-metadata"><a href="{}" download >{}, {}</a>'\
60 ' <a class="file-menu" href="#" data-type="{}" data-search-url="{}" data-filename="{}">πŸ” </a></div>'\
60 ' <a class="file-menu" href="#" data-type="{}" data-search-url="{}" data-filename="{}" data-id="{}">πŸ” </a></div>'\
61 '</div>'
61 '</div>'
62 URL_VIEW = '<div class="image">' \
62 URL_VIEW = '<div class="image">' \
63 '{}' \
63 '{}' \
64 '<div class="image-metadata">{}</div>' \
64 '<div class="image-metadata">{}</div>' \
65 '</div>'
65 '</div>'
66 ABSTRACT_FORMAT_VIEW = '<a href="{}">'\
66 ABSTRACT_FORMAT_VIEW = '<a href="{}">'\
67 '<img class="url-image" src="{}" width="{}" height="{}"/>'\
67 '<img class="url-image" src="{}" width="{}" height="{}"/>'\
68 '</a>'
68 '</a>'
69 VIDEO_FORMAT_VIEW = '<video width="200" height="150" controls src="{}"></video>'
69 VIDEO_FORMAT_VIEW = '<video width="200" height="150" controls src="{}"></video>'
70 AUDIO_FORMAT_VIEW = '<audio controls src="{}"></audio>'
70 AUDIO_FORMAT_VIEW = '<audio controls src="{}"></audio>'
71 IMAGE_FORMAT_VIEW = '<a class="{}" href="{full}">' \
71 IMAGE_FORMAT_VIEW = '<a class="{}" href="{full}">' \
72 '<img class="post-image-preview"' \
72 '<img class="post-image-preview"' \
73 ' src="{}"' \
73 ' src="{}"' \
74 ' alt="{}"' \
74 ' alt="{}"' \
75 ' width="{}"' \
75 ' width="{}"' \
76 ' height="{}"' \
76 ' height="{}"' \
77 ' data-width="{}"' \
77 ' data-width="{}"' \
78 ' data-height="{}" />' \
78 ' data-height="{}" />' \
79 '</a>'
79 '</a>'
80 SVG_FORMAT_VIEW = '<a class="thumb" href="{}">'\
80 SVG_FORMAT_VIEW = '<a class="thumb" href="{}">'\
81 '<img class="post-image-preview" width="200" height="150" src="{}" />'\
81 '<img class="post-image-preview" width="200" height="150" src="{}" />'\
82 '</a>'
82 '</a>'
83 URL_FORMAT_VIEW = '<a href="{}">' \
83 URL_FORMAT_VIEW = '<a href="{}">' \
84 '<img class="url-image" src="{}" width="{}" height="{}"/>' \
84 '<img class="url-image" src="{}" width="{}" height="{}"/>' \
85 '</a>'
85 '</a>'
86
86
87
87
88 def get_viewers():
88 def get_viewers():
89 return AbstractViewer.__subclasses__()
89 return AbstractViewer.__subclasses__()
90
90
91
91
92 def get_static_dimensions(filename):
92 def get_static_dimensions(filename):
93 file_path = finders.find(filename)
93 file_path = finders.find(filename)
94 return get_image_dimensions(file_path)
94 return get_image_dimensions(file_path)
95
95
96
96
97 # TODO Move this to utils
97 # TODO Move this to utils
98 def file_exists(filename):
98 def file_exists(filename):
99 return finders.find(filename) is not None
99 return finders.find(filename) is not None
100
100
101
101
102 class AbstractViewer:
102 class AbstractViewer:
103 def __init__(self, file, file_type, hash, url):
103 def __init__(self, file, file_type, id, url):
104 self.file = file
104 self.file = file
105 self.file_type = file_type
105 self.file_type = file_type
106 self.hash = hash
106 self.id = id
107 self.url = url
107 self.url = url
108 self.extension = get_extension(self.file.name)
108 self.extension = get_extension(self.file.name)
109
109
110 @staticmethod
110 @staticmethod
111 def supports(file_type):
111 def supports(file_type):
112 return True
112 return True
113
113
114 def get_view(self):
114 def get_view(self):
115 search_host = settings.get('External', 'ImageSearchHost')
115 search_host = settings.get('External', 'ImageSearchHost')
116 if search_host:
116 if search_host:
117 if search_host.endswith('/'):
117 if search_host.endswith('/'):
118 search_host = search_host[:-1]
118 search_host = search_host[:-1]
119 search_url = search_host + self.file.url
119 search_url = search_host + self.file.url
120 else:
120 else:
121 search_url = ''
121 search_url = ''
122
122
123 return ABSTRACT_VIEW.format(self.get_format_view(), self.file.url,
123 return ABSTRACT_VIEW.format(self.get_format_view(), self.file.url,
124 self.file_type, filesizeformat(self.file.size),
124 self.file_type, filesizeformat(self.file.size),
125 self.file_type, search_url, self.file.name)
125 self.file_type, search_url, self.file.name, self.id)
126
126
127 def get_format_view(self):
127 def get_format_view(self):
128 image_name = PLAIN_FILE_FORMATS.get(self.extension, self.extension)
128 image_name = PLAIN_FILE_FORMATS.get(self.extension, self.extension)
129 file_name = FILE_FILEFORMAT.format(image_name)
129 file_name = FILE_FILEFORMAT.format(image_name)
130
130
131 if file_exists(file_name):
131 if file_exists(file_name):
132 image = file_name
132 image = file_name
133 else:
133 else:
134 image = FILE_STUB_IMAGE
134 image = FILE_STUB_IMAGE
135
135
136 w, h = get_static_dimensions(image)
136 w, h = get_static_dimensions(image)
137
137
138 return ABSTRACT_FORMAT_VIEW.format(self.file.url, static(image), w, h)
138 return ABSTRACT_FORMAT_VIEW.format(self.file.url, static(image), w, h)
139
139
140
140
141 class VideoViewer(AbstractViewer):
141 class VideoViewer(AbstractViewer):
142 @staticmethod
142 @staticmethod
143 def supports(file_type):
143 def supports(file_type):
144 return file_type in FILE_TYPES_VIDEO
144 return file_type in FILE_TYPES_VIDEO
145
145
146 def get_format_view(self):
146 def get_format_view(self):
147 return VIDEO_FORMAT_VIEW.format(self.file.url)
147 return VIDEO_FORMAT_VIEW.format(self.file.url)
148
148
149
149
150 class AudioViewer(AbstractViewer):
150 class AudioViewer(AbstractViewer):
151 @staticmethod
151 @staticmethod
152 def supports(file_type):
152 def supports(file_type):
153 return file_type in FILE_TYPES_AUDIO
153 return file_type in FILE_TYPES_AUDIO
154
154
155 def get_format_view(self):
155 def get_format_view(self):
156 return AUDIO_FORMAT_VIEW.format(self.file.url)
156 return AUDIO_FORMAT_VIEW.format(self.file.url)
157
157
158
158
159 class SvgViewer(AbstractViewer):
159 class SvgViewer(AbstractViewer):
160 @staticmethod
160 @staticmethod
161 def supports(file_type):
161 def supports(file_type):
162 return file_type == FILE_TYPE_SVG
162 return file_type == FILE_TYPE_SVG
163
163
164 def get_format_view(self):
164 def get_format_view(self):
165 return SVG_FORMAT_VIEW.format(self.file.url, self.file.url)
165 return SVG_FORMAT_VIEW.format(self.file.url, self.file.url)
166
166
167
167
168 class ImageViewer(AbstractViewer):
168 class ImageViewer(AbstractViewer):
169 @staticmethod
169 @staticmethod
170 def supports(file_type):
170 def supports(file_type):
171 return file_type in FILE_TYPES_IMAGE
171 return file_type in FILE_TYPES_IMAGE
172
172
173 def get_format_view(self):
173 def get_format_view(self):
174 metadata = '{}, {}'.format(self.file.name.split('.')[-1],
174 metadata = '{}, {}'.format(self.file.name.split('.')[-1],
175 filesizeformat(self.file.size))
175 filesizeformat(self.file.size))
176
176
177 try:
177 try:
178 width, height = get_image_dimensions(self.file.path)
178 width, height = get_image_dimensions(self.file.path)
179 except Exception:
179 except Exception:
180 # If the image is a decompression bomb, treat it as just a regular
180 # If the image is a decompression bomb, treat it as just a regular
181 # file
181 # file
182 return super().get_format_view()
182 return super().get_format_view()
183
183
184 preview_path = self.file.path.replace('.', '.200x150.')
184 preview_path = self.file.path.replace('.', '.200x150.')
185 try:
185 try:
186 pre_width, pre_height = get_image_dimensions(preview_path)
186 pre_width, pre_height = get_image_dimensions(preview_path)
187 except Exception:
187 except Exception:
188 return super().get_format_view()
188 return super().get_format_view()
189
189
190 split = self.file.url.rsplit('.', 1)
190 split = self.file.url.rsplit('.', 1)
191 w, h = 200, 150
191 w, h = 200, 150
192 thumb_url = '%s.%sx%s.%s' % (split[0], w, h, split[1])
192 thumb_url = '%s.%sx%s.%s' % (split[0], w, h, split[1])
193
193
194 return IMAGE_FORMAT_VIEW.format(CSS_CLASS_THUMB,
194 return IMAGE_FORMAT_VIEW.format(CSS_CLASS_THUMB,
195 thumb_url,
195 thumb_url,
196 self.hash,
196 self.id,
197 str(pre_width),
197 str(pre_width),
198 str(pre_height), str(width), str(height),
198 str(pre_height), str(width), str(height),
199 full=self.file.url, image_meta=metadata)
199 full=self.file.url, image_meta=metadata)
200
200
201
201
202 class UrlViewer(AbstractViewer):
202 class UrlViewer(AbstractViewer):
203 @staticmethod
203 @staticmethod
204 def supports(file_type):
204 def supports(file_type):
205 return file_type is None
205 return file_type is None
206
206
207 def get_view(self):
207 def get_view(self):
208 return URL_VIEW.format(self.get_format_view(), get_domain(self.url))
208 return URL_VIEW.format(self.get_format_view(), get_domain(self.url))
209
209
210 def get_format_view(self):
210 def get_format_view(self):
211 protocol = self.url.split(':')[0]
211 protocol = self.url.split(':')[0]
212
212
213 domain = get_domain(self.url)
213 domain = get_domain(self.url)
214
214
215 if protocol in URL_PROTOCOLS:
215 if protocol in URL_PROTOCOLS:
216 url_image_name = URL_PROTOCOLS.get(protocol)
216 url_image_name = URL_PROTOCOLS.get(protocol)
217 elif domain:
217 elif domain:
218 url_image_name = self._find_image_for_domains(domain) or FILE_STUB_URL
218 url_image_name = self._find_image_for_domains(domain) or FILE_STUB_URL
219 else:
219 else:
220 url_image_name = FILE_STUB_URL
220 url_image_name = FILE_STUB_URL
221
221
222 image_path = 'images/{}.png'.format(url_image_name)
222 image_path = 'images/{}.png'.format(url_image_name)
223 image = static(image_path)
223 image = static(image_path)
224 w, h = get_static_dimensions(image_path)
224 w, h = get_static_dimensions(image_path)
225
225
226 return URL_FORMAT_VIEW.format(self.url, image, w, h)
226 return URL_FORMAT_VIEW.format(self.url, image, w, h)
227
227
228 @cached_result()
228 @cached_result()
229 def _find_image_for_domains(self, domain):
229 def _find_image_for_domains(self, domain):
230 """
230 """
231 Searches for the domain image for every domain level except top.
231 Searches for the domain image for every domain level except top.
232 E.g. for l3.example.co.uk it will search for l3.example.co.uk, then
232 E.g. for l3.example.co.uk it will search for l3.example.co.uk, then
233 example.co.uk, then co.uk
233 example.co.uk, then co.uk
234 """
234 """
235 levels = domain.split('.')
235 levels = domain.split('.')
236 while len(levels) > 1:
236 while len(levels) > 1:
237 domain = '.'.join(levels)
237 domain = '.'.join(levels)
238
238
239 filename = 'images/domains/{}.png'.format(domain)
239 filename = 'images/domains/{}.png'.format(domain)
240 if file_exists(filename):
240 if file_exists(filename):
241 return 'domains/' + domain
241 return 'domains/' + domain
242 else:
242 else:
243 del levels[0]
243 del levels[0]
244
244
@@ -1,220 +1,230 b''
1 /*
1 /*
2 @licstart The following is the entire license notice for the
2 @licstart The following is the entire license notice for the
3 JavaScript code in this page.
3 JavaScript code in this page.
4
4
5
5
6 Copyright (C) 2013 neko259
6 Copyright (C) 2013 neko259
7
7
8 The JavaScript code in this page is free software: you can
8 The JavaScript code in this page is free software: you can
9 redistribute it and/or modify it under the terms of the GNU
9 redistribute it and/or modify it under the terms of the GNU
10 General Public License (GNU GPL) as published by the Free Software
10 General Public License (GNU GPL) as published by the Free Software
11 Foundation, either version 3 of the License, or (at your option)
11 Foundation, either version 3 of the License, or (at your option)
12 any later version. The code is distributed WITHOUT ANY WARRANTY;
12 any later version. The code is distributed WITHOUT ANY WARRANTY;
13 without even the implied warranty of MERCHANTABILITY or FITNESS
13 without even the implied warranty of MERCHANTABILITY or FITNESS
14 FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
14 FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
15
15
16 As additional permission under GNU GPL version 3 section 7, you
16 As additional permission under GNU GPL version 3 section 7, you
17 may distribute non-source (e.g., minimized or compacted) forms of
17 may distribute non-source (e.g., minimized or compacted) forms of
18 that code without the copy of the GNU GPL normally required by
18 that code without the copy of the GNU GPL normally required by
19 section 4, provided you include this license notice and a URL
19 section 4, provided you include this license notice and a URL
20 through which recipients can access the Corresponding Source.
20 through which recipients can access the Corresponding Source.
21
21
22 @licend The above is the entire license notice
22 @licend The above is the entire license notice
23 for the JavaScript code in this page.
23 for the JavaScript code in this page.
24 */
24 */
25
25
26 var ITEM_VOLUME_LEVEL = 'volumeLevel';
26 var ITEM_VOLUME_LEVEL = 'volumeLevel';
27 var IMAGE_TYPES = ['image/png', 'image/jpg', 'image/jpeg', 'image/bmp', 'image/gif'];
27 var IMAGE_TYPES = ['image/png', 'image/jpg', 'image/jpeg', 'image/bmp', 'image/gif'];
28
28
29 /**
29 /**
30 * An email is a hidden file to prevent spam bots from posting. It has to be
30 * An email is a hidden file to prevent spam bots from posting. It has to be
31 * hidden.
31 * hidden.
32 */
32 */
33 function hideEmailFromForm() {
33 function hideEmailFromForm() {
34 $('.form-email').parent().parent().hide();
34 $('.form-email').parent().parent().hide();
35 }
35 }
36
36
37 /**
37 /**
38 * Highlight code blocks with code highlighter
38 * Highlight code blocks with code highlighter
39 */
39 */
40 function highlightCode(node) {
40 function highlightCode(node) {
41 node.find('pre code').each(function(i, e) {
41 node.find('pre code').each(function(i, e) {
42 hljs.highlightBlock(e);
42 hljs.highlightBlock(e);
43 });
43 });
44 }
44 }
45
45
46 function updateFavPosts(data) {
46 function updateFavPosts(data) {
47 var includePostBody = $('#fav-panel').is(":visible");
47 var includePostBody = $('#fav-panel').is(":visible");
48
48
49 var allNewPostCount = 0;
49 var allNewPostCount = 0;
50
50
51 if (includePostBody) {
51 if (includePostBody) {
52 var favoriteThreadPanel = $('#fav-panel');
52 var favoriteThreadPanel = $('#fav-panel');
53 favoriteThreadPanel.empty();
53 favoriteThreadPanel.empty();
54 }
54 }
55
55
56 $.each($.parseJSON(data), function (_, dict) {
56 $.each($.parseJSON(data), function (_, dict) {
57 var newPostCount = dict.new_post_count;
57 var newPostCount = dict.new_post_count;
58 allNewPostCount += newPostCount;
58 allNewPostCount += newPostCount;
59
59
60 if (includePostBody) {
60 if (includePostBody) {
61 var favThreadNode = $('<div class="post"></div>');
61 var favThreadNode = $('<div class="post"></div>');
62 favThreadNode.append($(dict.post_url));
62 favThreadNode.append($(dict.post_url));
63 favThreadNode.append(' ');
63 favThreadNode.append(' ');
64 favThreadNode.append($('<span class="title">' + dict.title + '</span>'));
64 favThreadNode.append($('<span class="title">' + dict.title + '</span>'));
65
65
66 if (newPostCount > 0) {
66 if (newPostCount > 0) {
67 favThreadNode.append(' (<a href="' + dict.newest_post_link + '">+' + newPostCount + "</a>)");
67 favThreadNode.append(' (<a href="' + dict.newest_post_link + '">+' + newPostCount + "</a>)");
68 }
68 }
69
69
70 favoriteThreadPanel.append(favThreadNode);
70 favoriteThreadPanel.append(favThreadNode);
71
71
72 addRefLinkPreview(favThreadNode[0]);
72 addRefLinkPreview(favThreadNode[0]);
73 }
73 }
74 });
74 });
75
75
76 var newPostCountNode = $('#new-fav-post-count');
76 var newPostCountNode = $('#new-fav-post-count');
77 if (allNewPostCount > 0) {
77 if (allNewPostCount > 0) {
78 newPostCountNode.text('(+' + allNewPostCount + ')');
78 newPostCountNode.text('(+' + allNewPostCount + ')');
79 newPostCountNode.show();
79 newPostCountNode.show();
80 } else {
80 } else {
81 newPostCountNode.hide();
81 newPostCountNode.hide();
82 }
82 }
83 }
83 }
84
84
85 function initFavPanel() {
85 function initFavPanel() {
86 var favPanelButton = $('#fav-panel-btn');
86 var favPanelButton = $('#fav-panel-btn');
87 if (favPanelButton.length > 0 && typeof SharedWorker != 'undefined') {
87 if (favPanelButton.length > 0 && typeof SharedWorker != 'undefined') {
88 var worker = new SharedWorker($('body').attr('data-update-script'));
88 var worker = new SharedWorker($('body').attr('data-update-script'));
89 worker.port.onmessage = function(e) {
89 worker.port.onmessage = function(e) {
90 updateFavPosts(e.data);
90 updateFavPosts(e.data);
91 };
91 };
92 worker.onerror = function(event){
92 worker.onerror = function(event){
93 throw new Error(event.message + " (" + event.filename + ":" + event.lineno + ")");
93 throw new Error(event.message + " (" + event.filename + ":" + event.lineno + ")");
94 };
94 };
95 worker.port.start();
95 worker.port.start();
96
96
97 $(favPanelButton).click(function() {
97 $(favPanelButton).click(function() {
98 var favPanel = $('#fav-panel');
98 var favPanel = $('#fav-panel');
99 favPanel.toggle();
99 favPanel.toggle();
100
100
101 worker.port.postMessage({ includePostBody: favPanel.is(':visible')});
101 worker.port.postMessage({ includePostBody: favPanel.is(':visible')});
102
102
103 return false;
103 return false;
104 });
104 });
105
105
106 $(document).on('keyup.removepic', function(e) {
106 $(document).on('keyup.removepic', function(e) {
107 if(e.which === 27) {
107 if(e.which === 27) {
108 $('#fav-panel').hide();
108 $('#fav-panel').hide();
109 }
109 }
110 });
110 });
111 }
111 }
112 }
112 }
113
113
114 function setVolumeLevel(level) {
114 function setVolumeLevel(level) {
115 localStorage.setItem(ITEM_VOLUME_LEVEL, level);
115 localStorage.setItem(ITEM_VOLUME_LEVEL, level);
116 }
116 }
117
117
118 function getVolumeLevel() {
118 function getVolumeLevel() {
119 var level = localStorage.getItem(ITEM_VOLUME_LEVEL);
119 var level = localStorage.getItem(ITEM_VOLUME_LEVEL);
120 if (level == null) {
120 if (level == null) {
121 level = 1.0;
121 level = 1.0;
122 }
122 }
123 return level
123 return level
124 }
124 }
125
125
126 function processVolumeUser(node) {
126 function processVolumeUser(node) {
127 if (!window.localStorage) return;
127 if (!window.localStorage) return;
128 node.prop("volume", getVolumeLevel());
128 node.prop("volume", getVolumeLevel());
129 node.on('volumechange', function(event) {
129 node.on('volumechange', function(event) {
130 setVolumeLevel(event.target.volume);
130 setVolumeLevel(event.target.volume);
131 $("video,audio").prop("volume", getVolumeLevel());
131 $("video,audio").prop("volume", getVolumeLevel());
132 });
132 });
133 }
133 }
134
134
135 /**
135 /**
136 * Add all scripts than need to work on post, when the post is added to the
136 * Add all scripts than need to work on post, when the post is added to the
137 * document.
137 * document.
138 */
138 */
139 function addScriptsToPost(post) {
139 function addScriptsToPost(post) {
140 addRefLinkPreview(post[0]);
140 addRefLinkPreview(post[0]);
141 highlightCode(post);
141 highlightCode(post);
142 processVolumeUser(post.find("video,audio"));
142 processVolumeUser(post.find("video,audio"));
143 }
143 }
144
144
145 /**
145 /**
146 * Fix compatibility issues with some rare browsers
146 * Fix compatibility issues with some rare browsers
147 */
147 */
148 function compatibilityCrutches() {
148 function compatibilityCrutches() {
149 if (window.operamini) {
149 if (window.operamini) {
150 $('#form textarea').each(function() { this.placeholder = ''; });
150 $('#form textarea').each(function() { this.placeholder = ''; });
151 }
151 }
152 }
152 }
153
153
154 function addContextMenu() {
154 function addContextMenu() {
155 $.contextMenu({
155 $.contextMenu({
156 selector: '.file-menu',
156 selector: '.file-menu',
157 trigger: 'left',
157 trigger: 'left',
158
158
159 build: function($trigger, e) {
159 build: function($trigger, e) {
160 var fileSearchUrl = $trigger.data('search-url');
160 var fileSearchUrl = $trigger.data('search-url');
161 var isImage = IMAGE_TYPES.indexOf($trigger.data('type')) > -1;
161 var isImage = IMAGE_TYPES.indexOf($trigger.data('type')) > -1;
162 var hasUrl = fileSearchUrl.length > 0;
162 var hasUrl = fileSearchUrl.length > 0;
163 var id = $trigger.data('id');
163 return {
164 return {
164 items: {
165 items: {
165 duplicates: {
166 duplicates: {
166 name: gettext('Duplicates search'),
167 name: gettext('Duplicates search'),
167 callback: function(key, opts) {
168 callback: function(key, opts) {
168 window.location = '/feed/?image=' + $trigger.data('filename');
169 window.location = '/feed/?image=' + $trigger.data('filename');
169 }
170 }
170 },
171 },
171 google: {
172 google: {
172 name: 'Google',
173 name: 'Google',
173 visible: isImage && hasUrl,
174 visible: isImage && hasUrl,
174 callback: function(key, opts) {
175 callback: function(key, opts) {
175 window.location = 'https://www.google.com/searchbyimage?image_url=' + fileSearchUrl;
176 window.location = 'https://www.google.com/searchbyimage?image_url=' + fileSearchUrl;
176 }
177 }
177 },
178 },
178 iqdb: {
179 iqdb: {
179 name: 'IQDB',
180 name: 'IQDB',
180 visible: isImage && hasUrl,
181 visible: isImage && hasUrl,
181 callback: function(key, opts) {
182 callback: function(key, opts) {
182 window.location = 'http://iqdb.org/?url=' + fileSearchUrl;
183 window.location = 'http://iqdb.org/?url=' + fileSearchUrl;
183 }
184 }
184 },
185 },
185 tineye: {
186 tineye: {
186 name: 'TinEye',
187 name: 'TinEye',
187 visible: isImage && hasUrl,
188 visible: isImage && hasUrl,
188 callback: function(key, opts) {
189 callback: function(key, opts) {
189 window.location = 'http://tineye.com/search?url=' + fileSearchUrl;
190 window.location = 'http://tineye.com/search?url=' + fileSearchUrl;
190 }
191 }
192 },
193 addAlias: {
194 name: gettext('Add local sticker'),
195 callback: function(key, opts) {
196 var alias = prompt(gettext('Input sticker name'));
197 if (alias) {
198 window.location = '/stickers/?action=add&name=' + alias + '&id=' + id;
199 }
200 }
191 }
201 }
192 },
202 }
193 };
203 };
194 }
204 }
195 });
205 });
196 }
206 }
197
207
198 $( document ).ready(function() {
208 $( document ).ready(function() {
199 hideEmailFromForm();
209 hideEmailFromForm();
200
210
201 $("a[href='#top']").click(function() {
211 $("a[href='#top']").click(function() {
202 $("html, body").animate({ scrollTop: 0 }, "slow");
212 $("html, body").animate({ scrollTop: 0 }, "slow");
203 return false;
213 return false;
204 });
214 });
205
215
206 addImgPreview();
216 addImgPreview();
207
217
208 addRefLinkPreview();
218 addRefLinkPreview();
209
219
210 highlightCode($(document));
220 highlightCode($(document));
211
221
212 initFavPanel();
222 initFavPanel();
213
223
214 var volumeUsers = $("video,audio");
224 var volumeUsers = $("video,audio");
215 processVolumeUser(volumeUsers);
225 processVolumeUser(volumeUsers);
216
226
217 addContextMenu();
227 addContextMenu();
218
228
219 compatibilityCrutches();
229 compatibilityCrutches();
220 });
230 });
@@ -1,20 +1,33 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load tz %}
4 {% load tz %}
5
5
6 {% block head %}
6 {% block head %}
7 <meta name="robots" content="noindex">
7 <meta name="robots" content="noindex">
8 <title>{% trans "Stickers" %} - {{ site_name }}</title>
8 <title>{% trans "Stickers" %} - {{ site_name }}</title>
9 {% endblock %}
9 {% endblock %}
10
10
11 {% block content %}
11 {% block content %}
12 <div id="posts-table">
12 <div id="posts-table">
13 {% for sticker in stickers %}
13 {% if local_stickers %}
14 <div class="gallery_image">
14 <h1>{% trans "Local stickers" %}</h1>
15 {{ sticker.attachment.get_view|safe }}
15 {% for sticker in local_stickers %}
16 <div>{{ sticker.name }}</div>
16 <div class="gallery_image">
17 </div>
17 {{ sticker.attachment.get_view|safe }}
18 {% endfor %}
18 <div>{{ sticker.name }}</div>
19 <div><a href="?action=remove&name={{ sticker.name }}">{% trans "Remove sticker" %}</a></div>
20 </div>
21 {% endfor %}
22 {% endif %}
23 {% if global_stickers %}
24 <h1>{% trans "Global stickers" %}</h1>
25 {% for sticker in global_stickers %}
26 <div class="gallery_image">
27 {{ sticker.attachment.get_view|safe }}
28 <div>{{ sticker.name }}</div>
29 </div>
30 {% endfor %}
31 {% endif %}
19 </div>
32 </div>
20 {% endblock %}
33 {% endblock %}
@@ -1,40 +1,41 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load tz %}
4 {% load tz %}
5
5
6 {% block head %}
6 {% block head %}
7 <meta name="robots" content="noindex">
7 <meta name="robots" content="noindex">
8 <title>{% trans 'Settings' %} - {{ site_name }}</title>
8 <title>{% trans 'Settings' %} - {{ site_name }}</title>
9 {% endblock %}
9 {% endblock %}
10
10
11 {% block content %}
11 {% block content %}
12 <div id="posts-table">
12 <div id="posts-table">
13 <p>
13 <p>
14 {% if moderator %}
14 {% if moderator %}
15 {% trans 'You are moderator.' %}
15 {% trans 'You are moderator.' %}
16 {% endif %}
16 {% endif %}
17 </p>
17 </p>
18 {% if hidden_tags %}
18 {% if hidden_tags %}
19 <p>{% trans 'Hidden tags:' %}
19 <p>{% trans 'Hidden tags:' %}
20 {% for tag in hidden_tags %}
20 {% for tag in hidden_tags %}
21 {{ tag.get_view|safe }}
21 {{ tag.get_view|safe }}
22 {% endfor %}
22 {% endfor %}
23 </p>
23 </p>
24 {% else %}
24 {% else %}
25 <p>{% trans 'No hidden tags.' %}</p>
25 <p>{% trans 'No hidden tags.' %}</p>
26 {% endif %}
26 {% endif %}
27 <p><a href="{% url 'stickers' %}">{% trans 'Stickers' %}</a></p>
27 </div>
28 </div>
28
29
29 <div class="post-form-w">
30 <div class="post-form-w">
30 <div class="post-form">
31 <div class="post-form">
31 <form method="post">{% csrf_token %}
32 <form method="post">{% csrf_token %}
32 {{ form.as_div }}
33 {{ form.as_div }}
33 <div class="form-submit">
34 <div class="form-submit">
34 <input type="submit" value="{% trans "Save" %}" />
35 <input type="submit" value="{% trans "Save" %}" />
35 </div>
36 </div>
36 </form>
37 </form>
37 </div>
38 </div>
38 </div>
39 </div>
39
40
40 {% endblock %}
41 {% endblock %}
@@ -1,315 +1,317 b''
1 import json
1 import json
2 import logging
2 import logging
3
3
4 from django.core import serializers
4 from django.core import serializers
5 from django.db import transaction
5 from django.db import transaction
6 from django.http import HttpResponse, HttpResponseBadRequest
6 from django.http import HttpResponse, HttpResponseBadRequest
7 from django.shortcuts import get_object_or_404
7 from django.shortcuts import get_object_or_404
8 from django.views.decorators.csrf import csrf_protect
8 from django.views.decorators.csrf import csrf_protect
9
9
10 from boards.abstracts.settingsmanager import get_settings_manager
10 from boards.abstracts.settingsmanager import get_settings_manager
11 from boards.forms import PostForm, PlainErrorList
11 from boards.forms import PostForm, PlainErrorList
12 from boards.mdx_neboard import Parser
12 from boards.mdx_neboard import Parser
13 from boards.models import Post, Thread, Tag, Attachment, TagAlias
13 from boards.models import Post, Thread, Tag, Attachment, TagAlias
14 from boards.models.attachment import AttachmentSticker
14 from boards.models.attachment import AttachmentSticker
15 from boards.models.thread import STATUS_ARCHIVE
15 from boards.models.thread import STATUS_ARCHIVE
16 from boards.models.user import Notification
16 from boards.models.user import Notification
17 from boards.utils import datetime_to_epoch
17 from boards.utils import datetime_to_epoch
18 from boards.views.thread import ThreadView
18 from boards.views.thread import ThreadView
19 from boards.models.attachment.viewers import FILE_TYPES_IMAGE
19 from boards.models.attachment.viewers import FILE_TYPES_IMAGE
20
20
21 __author__ = 'neko259'
21 __author__ = 'neko259'
22
22
23 PARAMETER_TRUNCATED = 'truncated'
23 PARAMETER_TRUNCATED = 'truncated'
24 PARAMETER_TAG = 'tag'
24 PARAMETER_TAG = 'tag'
25 PARAMETER_OFFSET = 'offset'
25 PARAMETER_OFFSET = 'offset'
26 PARAMETER_DIFF_TYPE = 'type'
26 PARAMETER_DIFF_TYPE = 'type'
27 PARAMETER_POST = 'post'
27 PARAMETER_POST = 'post'
28 PARAMETER_UPDATED = 'updated'
28 PARAMETER_UPDATED = 'updated'
29 PARAMETER_LAST_UPDATE = 'last_update'
29 PARAMETER_LAST_UPDATE = 'last_update'
30 PARAMETER_THREAD = 'thread'
30 PARAMETER_THREAD = 'thread'
31 PARAMETER_UIDS = 'uids'
31 PARAMETER_UIDS = 'uids'
32 PARAMETER_SUBSCRIBED = 'subscribed'
32 PARAMETER_SUBSCRIBED = 'subscribed'
33
33
34 DIFF_TYPE_HTML = 'html'
34 DIFF_TYPE_HTML = 'html'
35 DIFF_TYPE_JSON = 'json'
35 DIFF_TYPE_JSON = 'json'
36
36
37 STATUS_OK = 'ok'
37 STATUS_OK = 'ok'
38 STATUS_ERROR = 'error'
38 STATUS_ERROR = 'error'
39
39
40 logger = logging.getLogger(__name__)
40 logger = logging.getLogger(__name__)
41
41
42
42
43 @transaction.atomic
43 @transaction.atomic
44 def api_get_threaddiff(request):
44 def api_get_threaddiff(request):
45 """
45 """
46 Gets posts that were changed or added since time
46 Gets posts that were changed or added since time
47 """
47 """
48
48
49 thread_id = request.POST.get(PARAMETER_THREAD)
49 thread_id = request.POST.get(PARAMETER_THREAD)
50 uids_str = request.POST.get(PARAMETER_UIDS)
50 uids_str = request.POST.get(PARAMETER_UIDS)
51
51
52 if not thread_id or not uids_str:
52 if not thread_id or not uids_str:
53 return HttpResponse(content='Invalid request.')
53 return HttpResponse(content='Invalid request.')
54
54
55 uids = uids_str.strip().split(' ')
55 uids = uids_str.strip().split(' ')
56
56
57 opening_post = get_object_or_404(Post, id=thread_id)
57 opening_post = get_object_or_404(Post, id=thread_id)
58 thread = opening_post.get_thread()
58 thread = opening_post.get_thread()
59
59
60 json_data = {
60 json_data = {
61 PARAMETER_UPDATED: [],
61 PARAMETER_UPDATED: [],
62 PARAMETER_LAST_UPDATE: None, # TODO Maybe this can be removed already?
62 PARAMETER_LAST_UPDATE: None, # TODO Maybe this can be removed already?
63 }
63 }
64 posts = Post.objects.filter(thread=thread).exclude(uid__in=uids)
64 posts = Post.objects.filter(thread=thread).exclude(uid__in=uids)
65
65
66 diff_type = request.GET.get(PARAMETER_DIFF_TYPE, DIFF_TYPE_HTML)
66 diff_type = request.GET.get(PARAMETER_DIFF_TYPE, DIFF_TYPE_HTML)
67
67
68 for post in posts:
68 for post in posts:
69 json_data[PARAMETER_UPDATED].append(post.get_post_data(
69 json_data[PARAMETER_UPDATED].append(post.get_post_data(
70 format_type=diff_type, request=request))
70 format_type=diff_type, request=request))
71 json_data[PARAMETER_LAST_UPDATE] = str(thread.last_edit_time)
71 json_data[PARAMETER_LAST_UPDATE] = str(thread.last_edit_time)
72
72
73 settings_manager = get_settings_manager(request)
73 settings_manager = get_settings_manager(request)
74 json_data[PARAMETER_SUBSCRIBED] = str(settings_manager.thread_is_fav(opening_post))
74 json_data[PARAMETER_SUBSCRIBED] = str(settings_manager.thread_is_fav(opening_post))
75
75
76 # If the tag is favorite, update the counter
76 # If the tag is favorite, update the counter
77 settings_manager = get_settings_manager(request)
77 settings_manager = get_settings_manager(request)
78 favorite = settings_manager.thread_is_fav(opening_post)
78 favorite = settings_manager.thread_is_fav(opening_post)
79 if favorite:
79 if favorite:
80 settings_manager.add_or_read_fav_thread(opening_post)
80 settings_manager.add_or_read_fav_thread(opening_post)
81
81
82 return HttpResponse(content=json.dumps(json_data))
82 return HttpResponse(content=json.dumps(json_data))
83
83
84
84
85 @csrf_protect
85 @csrf_protect
86 def api_add_post(request, opening_post_id):
86 def api_add_post(request, opening_post_id):
87 """
87 """
88 Adds a post and return the JSON response for it
88 Adds a post and return the JSON response for it
89 """
89 """
90
90
91 opening_post = get_object_or_404(Post, id=opening_post_id)
91 opening_post = get_object_or_404(Post, id=opening_post_id)
92
92
93 logger.info('Adding post via api...')
93 logger.info('Adding post via api...')
94
94
95 status = STATUS_OK
95 status = STATUS_OK
96 errors = []
96 errors = []
97
97
98 if request.method == 'POST':
98 if request.method == 'POST':
99 form = PostForm(request.POST, request.FILES, error_class=PlainErrorList)
99 form = PostForm(request.POST, request.FILES, error_class=PlainErrorList)
100 form.session = request.session
100 form.session = request.session
101
101
102 if form.need_to_ban:
102 if form.need_to_ban:
103 # Ban user because he is suspected to be a bot
103 # Ban user because he is suspected to be a bot
104 # _ban_current_user(request)
104 # _ban_current_user(request)
105 status = STATUS_ERROR
105 status = STATUS_ERROR
106 if form.is_valid():
106 if form.is_valid():
107 post = ThreadView().new_post(request, form, opening_post,
107 post = ThreadView().new_post(request, form, opening_post,
108 html_response=False)
108 html_response=False)
109 if not post:
109 if not post:
110 status = STATUS_ERROR
110 status = STATUS_ERROR
111 else:
111 else:
112 logger.info('Added post #%d via api.' % post.id)
112 logger.info('Added post #%d via api.' % post.id)
113 else:
113 else:
114 status = STATUS_ERROR
114 status = STATUS_ERROR
115 errors = form.as_json_errors()
115 errors = form.as_json_errors()
116
116
117 response = {
117 response = {
118 'status': status,
118 'status': status,
119 'errors': errors,
119 'errors': errors,
120 }
120 }
121
121
122 return HttpResponse(content=json.dumps(response))
122 return HttpResponse(content=json.dumps(response))
123
123
124
124
125 def get_post(request, post_id):
125 def get_post(request, post_id):
126 """
126 """
127 Gets the html of a post. Used for popups. Post can be truncated if used
127 Gets the html of a post. Used for popups. Post can be truncated if used
128 in threads list with 'truncated' get parameter.
128 in threads list with 'truncated' get parameter.
129 """
129 """
130
130
131 post = get_object_or_404(Post, id=post_id)
131 post = get_object_or_404(Post, id=post_id)
132 truncated = PARAMETER_TRUNCATED in request.GET
132 truncated = PARAMETER_TRUNCATED in request.GET
133
133
134 return HttpResponse(content=post.get_view(truncated=truncated, need_op_data=True))
134 return HttpResponse(content=post.get_view(truncated=truncated, need_op_data=True))
135
135
136
136
137 def api_get_threads(request, count):
137 def api_get_threads(request, count):
138 """
138 """
139 Gets the JSON thread opening posts list.
139 Gets the JSON thread opening posts list.
140 Parameters that can be used for filtering:
140 Parameters that can be used for filtering:
141 tag, offset (from which thread to get results)
141 tag, offset (from which thread to get results)
142 """
142 """
143
143
144 if PARAMETER_TAG in request.GET:
144 if PARAMETER_TAG in request.GET:
145 tag_name = request.GET[PARAMETER_TAG]
145 tag_name = request.GET[PARAMETER_TAG]
146 if tag_name is not None:
146 if tag_name is not None:
147 tag = get_object_or_404(Tag, name=tag_name)
147 tag = get_object_or_404(Tag, name=tag_name)
148 threads = tag.get_threads().exclude(status=STATUS_ARCHIVE)
148 threads = tag.get_threads().exclude(status=STATUS_ARCHIVE)
149 else:
149 else:
150 threads = Thread.objects.exclude(status=STATUS_ARCHIVE)
150 threads = Thread.objects.exclude(status=STATUS_ARCHIVE)
151
151
152 if PARAMETER_OFFSET in request.GET:
152 if PARAMETER_OFFSET in request.GET:
153 offset = request.GET[PARAMETER_OFFSET]
153 offset = request.GET[PARAMETER_OFFSET]
154 offset = int(offset) if offset is not None else 0
154 offset = int(offset) if offset is not None else 0
155 else:
155 else:
156 offset = 0
156 offset = 0
157
157
158 threads = threads.order_by('-bump_time')
158 threads = threads.order_by('-bump_time')
159 threads = threads[offset:offset + int(count)]
159 threads = threads[offset:offset + int(count)]
160
160
161 opening_posts = []
161 opening_posts = []
162 for thread in threads:
162 for thread in threads:
163 opening_post = thread.get_opening_post()
163 opening_post = thread.get_opening_post()
164
164
165 # TODO Add tags, replies and images count
165 # TODO Add tags, replies and images count
166 post_data = opening_post.get_post_data(include_last_update=True)
166 post_data = opening_post.get_post_data(include_last_update=True)
167 post_data['status'] = thread.get_status()
167 post_data['status'] = thread.get_status()
168
168
169 opening_posts.append(post_data)
169 opening_posts.append(post_data)
170
170
171 return HttpResponse(content=json.dumps(opening_posts))
171 return HttpResponse(content=json.dumps(opening_posts))
172
172
173
173
174 # TODO Test this
174 # TODO Test this
175 def api_get_tags(request):
175 def api_get_tags(request):
176 """
176 """
177 Gets all tags or user tags.
177 Gets all tags or user tags.
178 """
178 """
179
179
180 # TODO Get favorite tags for the given user ID
180 # TODO Get favorite tags for the given user ID
181
181
182 tags = TagAlias.objects.all()
182 tags = TagAlias.objects.all()
183
183
184 term = request.GET.get('term')
184 term = request.GET.get('term')
185 if term is not None:
185 if term is not None:
186 tags = tags.filter(name__contains=term)
186 tags = tags.filter(name__contains=term)
187
187
188 tag_names = [tag.name for tag in tags]
188 tag_names = [tag.name for tag in tags]
189
189
190 return HttpResponse(content=json.dumps(tag_names))
190 return HttpResponse(content=json.dumps(tag_names))
191
191
192
192
193 def api_get_stickers(request):
193 def api_get_stickers(request):
194 term = request.GET.get('term')
194 term = request.GET.get('term')
195 if not term:
195 if not term:
196 return HttpResponseBadRequest()
196 return HttpResponseBadRequest()
197
197
198 stickers = AttachmentSticker.objects.filter(name__contains=term)
198 global_stickers = AttachmentSticker.objects.filter(name__contains=term)
199 local_stickers = [sticker for sticker in get_settings_manager(request).get_stickers() if term in sticker.name]
200 stickers = list(global_stickers) + local_stickers
199
201
200 image_dict = [{'thumb': sticker.attachment.get_thumb_url(),
202 image_dict = [{'thumb': sticker.attachment.get_thumb_url(),
201 'alias': sticker.name}
203 'alias': sticker.name}
202 for sticker in stickers]
204 for sticker in stickers]
203
205
204 return HttpResponse(content=json.dumps(image_dict))
206 return HttpResponse(content=json.dumps(image_dict))
205
207
206
208
207 # TODO The result can be cached by the thread last update time
209 # TODO The result can be cached by the thread last update time
208 # TODO Test this
210 # TODO Test this
209 def api_get_thread_posts(request, opening_post_id):
211 def api_get_thread_posts(request, opening_post_id):
210 """
212 """
211 Gets the JSON array of thread posts
213 Gets the JSON array of thread posts
212 """
214 """
213
215
214 opening_post = get_object_or_404(Post, id=opening_post_id)
216 opening_post = get_object_or_404(Post, id=opening_post_id)
215 thread = opening_post.get_thread()
217 thread = opening_post.get_thread()
216 posts = thread.get_replies()
218 posts = thread.get_replies()
217
219
218 json_data = {
220 json_data = {
219 'posts': [],
221 'posts': [],
220 'last_update': None,
222 'last_update': None,
221 }
223 }
222 json_post_list = []
224 json_post_list = []
223
225
224 for post in posts:
226 for post in posts:
225 json_post_list.append(post.get_post_data())
227 json_post_list.append(post.get_post_data())
226 json_data['last_update'] = datetime_to_epoch(thread.last_edit_time)
228 json_data['last_update'] = datetime_to_epoch(thread.last_edit_time)
227 json_data['posts'] = json_post_list
229 json_data['posts'] = json_post_list
228
230
229 return HttpResponse(content=json.dumps(json_data))
231 return HttpResponse(content=json.dumps(json_data))
230
232
231
233
232 def api_get_notifications(request, username):
234 def api_get_notifications(request, username):
233 last_notification_id_str = request.GET.get('last', None)
235 last_notification_id_str = request.GET.get('last', None)
234 last_id = int(last_notification_id_str) if last_notification_id_str is not None else None
236 last_id = int(last_notification_id_str) if last_notification_id_str is not None else None
235
237
236 posts = Notification.objects.get_notification_posts(usernames=[username],
238 posts = Notification.objects.get_notification_posts(usernames=[username],
237 last=last_id)
239 last=last_id)
238
240
239 json_post_list = []
241 json_post_list = []
240 for post in posts:
242 for post in posts:
241 json_post_list.append(post.get_post_data())
243 json_post_list.append(post.get_post_data())
242 return HttpResponse(content=json.dumps(json_post_list))
244 return HttpResponse(content=json.dumps(json_post_list))
243
245
244
246
245 def api_get_post(request, post_id):
247 def api_get_post(request, post_id):
246 """
248 """
247 Gets the JSON of a post. This can be
249 Gets the JSON of a post. This can be
248 used as and API for external clients.
250 used as and API for external clients.
249 """
251 """
250
252
251 post = get_object_or_404(Post, id=post_id)
253 post = get_object_or_404(Post, id=post_id)
252
254
253 json = serializers.serialize("json", [post], fields=(
255 json = serializers.serialize("json", [post], fields=(
254 "pub_time", "_text_rendered", "title", "text", "image",
256 "pub_time", "_text_rendered", "title", "text", "image",
255 "image_width", "image_height", "replies", "tags"
257 "image_width", "image_height", "replies", "tags"
256 ))
258 ))
257
259
258 return HttpResponse(content=json)
260 return HttpResponse(content=json)
259
261
260
262
261 def api_get_preview(request):
263 def api_get_preview(request):
262 raw_text = request.POST['raw_text']
264 raw_text = request.POST['raw_text']
263
265
264 parser = Parser()
266 parser = Parser()
265 return HttpResponse(content=parser.parse(parser.preparse(raw_text)))
267 return HttpResponse(content=parser.parse(parser.preparse(raw_text)))
266
268
267
269
268 def api_get_new_posts(request):
270 def api_get_new_posts(request):
269 """
271 """
270 Gets favorite threads and unread posts count.
272 Gets favorite threads and unread posts count.
271 """
273 """
272 posts = list()
274 posts = list()
273
275
274 include_posts = 'include_posts' in request.GET
276 include_posts = 'include_posts' in request.GET
275
277
276 settings_manager = get_settings_manager(request)
278 settings_manager = get_settings_manager(request)
277 fav_threads = settings_manager.get_fav_threads()
279 fav_threads = settings_manager.get_fav_threads()
278 fav_thread_ops = Post.objects.filter(id__in=fav_threads.keys())\
280 fav_thread_ops = Post.objects.filter(id__in=fav_threads.keys())\
279 .order_by('-pub_time').prefetch_related('thread')
281 .order_by('-pub_time').prefetch_related('thread')
280
282
281 ops = [{'op': op, 'last_id': fav_threads[str(op.id)]} for op in fav_thread_ops]
283 ops = [{'op': op, 'last_id': fav_threads[str(op.id)]} for op in fav_thread_ops]
282 if include_posts:
284 if include_posts:
283 new_post_threads = Thread.objects.get_new_posts(ops)
285 new_post_threads = Thread.objects.get_new_posts(ops)
284 if new_post_threads:
286 if new_post_threads:
285 thread_ids = {thread.id: thread for thread in new_post_threads}
287 thread_ids = {thread.id: thread for thread in new_post_threads}
286 else:
288 else:
287 thread_ids = dict()
289 thread_ids = dict()
288
290
289 for op in fav_thread_ops:
291 for op in fav_thread_ops:
290 fav_thread_dict = dict()
292 fav_thread_dict = dict()
291
293
292 op_thread = op.get_thread()
294 op_thread = op.get_thread()
293 if op_thread.id in thread_ids:
295 if op_thread.id in thread_ids:
294 thread = thread_ids[op_thread.id]
296 thread = thread_ids[op_thread.id]
295 new_post_count = thread.new_post_count
297 new_post_count = thread.new_post_count
296 fav_thread_dict['newest_post_link'] = thread.get_replies()\
298 fav_thread_dict['newest_post_link'] = thread.get_replies()\
297 .filter(id__gt=fav_threads[str(op.id)])\
299 .filter(id__gt=fav_threads[str(op.id)])\
298 .first().get_absolute_url(thread=thread)
300 .first().get_absolute_url(thread=thread)
299 else:
301 else:
300 new_post_count = 0
302 new_post_count = 0
301 fav_thread_dict['new_post_count'] = new_post_count
303 fav_thread_dict['new_post_count'] = new_post_count
302
304
303 fav_thread_dict['id'] = op.id
305 fav_thread_dict['id'] = op.id
304
306
305 fav_thread_dict['post_url'] = op.get_link_view()
307 fav_thread_dict['post_url'] = op.get_link_view()
306 fav_thread_dict['title'] = op.title
308 fav_thread_dict['title'] = op.title
307
309
308 posts.append(fav_thread_dict)
310 posts.append(fav_thread_dict)
309 else:
311 else:
310 fav_thread_dict = dict()
312 fav_thread_dict = dict()
311 fav_thread_dict['new_post_count'] = \
313 fav_thread_dict['new_post_count'] = \
312 Thread.objects.get_new_post_count(ops)
314 Thread.objects.get_new_post_count(ops)
313 posts.append(fav_thread_dict)
315 posts.append(fav_thread_dict)
314
316
315 return HttpResponse(content=json.dumps(posts))
317 return HttpResponse(content=json.dumps(posts))
@@ -1,25 +1,45 b''
1 from django.shortcuts import render
1 from django.shortcuts import render, redirect
2 from django.utils.decorators import method_decorator
2 from django.utils.decorators import method_decorator
3 from django.views.decorators.csrf import csrf_protect
3 from django.views.decorators.csrf import csrf_protect
4
4
5 from boards.models.attachment import AttachmentSticker
5 from boards.abstracts.settingsmanager import get_settings_manager
6 from boards.models.attachment import AttachmentSticker, Attachment
6 from boards.views.base import BaseBoardView
7 from boards.views.base import BaseBoardView
7
8
8 CONTEXT_STICKERS = 'stickers'
9 CONTEXT_GLOBAL_STICKERS = 'global_stickers'
10 CONTEXT_LOCAL_STICKERS = 'local_stickers'
9
11
10 TEMPLATE = 'boards/aliases.html'
12 TEMPLATE = 'boards/aliases.html'
11
13
12
14
13 class AliasesView(BaseBoardView):
15 class AliasesView(BaseBoardView):
14 @method_decorator(csrf_protect)
16 @method_decorator(csrf_protect)
15 def get(self, request, category=None):
17 def get(self, request, category=None):
18 result = self._process_creation(request)
19 if result:
20 return result
21
16 params = dict()
22 params = dict()
17
23
18 if category:
24 if category:
19 params[CONTEXT_STICKERS] = AttachmentSticker.objects.filter(
25 params[CONTEXT_GLOBAL_STICKERS] = AttachmentSticker.objects.filter(
20 name__startswith=(category + '/'))
26 name__startswith=(category + '/'))
21 else:
27 else:
22 params[CONTEXT_STICKERS] = AttachmentSticker.objects.all()
28 params[CONTEXT_GLOBAL_STICKERS] = AttachmentSticker.objects.all()
29 params[CONTEXT_LOCAL_STICKERS] = get_settings_manager(request).get_stickers()
23
30
24 return render(request, TEMPLATE, params)
31 return render(request, TEMPLATE, params)
25
32
33 def _process_creation(self, request):
34 action = request.GET.get('action')
35 if action == 'add' and 'name' in request.GET and 'id' in request.GET:
36 name = request.GET['name']
37 id = request.GET['id']
38 attachment = Attachment.objects.get(id=id)
39 get_settings_manager(request).add_attachment_alias(name, attachment)
40
41 return redirect('stickers')
42 if action == 'remove' and 'name' in request.GET:
43 name = request.GET['name']
44 get_settings_manager(request).remove_attachment_alias(name)
45 return redirect('stickers')
General Comments 0
You need to be logged in to leave comments. Login now