diff --git a/hypn0/hypn0_site/forms.py b/hypn0/hypn0_site/forms.py
index fc36d9f..8daefba 100644
--- a/hypn0/hypn0_site/forms.py
+++ b/hypn0/hypn0_site/forms.py
@@ -6,25 +6,25 @@ class HalftoneGenerateForm(forms.Form):
Форма валидации входных параметров для генерации гипнотического SVG.
"""
SHAPE_CHOICES = [
- ("circle", "Круг"),
- ("ring", "Кольцо"),
- ("triangle", "Треугольник"),
- ("square", "Квадрат"),
- ("diamond", "Ромб"),
- ("hexagon", "Шестиугольник"),
- ("star", "Звезда"),
- ("cross", "Крестик"),
- ("line", "Линия"),
- ("snowflake", "Снежинка"),
- ("binary", "Матрица (0/1)"),
- ("heart", "Сердечко"),
+ ("circle", "Circle"),
+ ("ring", "Ring"),
+ ("triangle", "Triangle"),
+ ("square", "Square"),
+ ("diamond", "Diamond"),
+ ("hexagon", "Hexagon"),
+ ("star", "Star"),
+ ("cross", "Cross"),
+ ("line", "Line"),
+ ("snowflake", "Snowflake"),
+ ("binary", "Binary (0/1)"),
+ ("heart", "Heart"),
]
image = forms.ImageField(
required=True,
error_messages={
- "required": "Покорми Гипножабу изображением!",
- "invalid_image": "Гипножаба не может переварить этот файл. Нужна картинка (PNG, JPG, WEBP).",
+ "required": "Feed the Hypnotoad with an image!",
+ "invalid_image": "Hypnotoad cannot digest this file. Image required (PNG, JPG, WEBP).",
},
)
shape = forms.ChoiceField(
diff --git a/hypn0/hypn0_site/models.py b/hypn0/hypn0_site/models.py
index d122804..2ab3345 100644
--- a/hypn0/hypn0_site/models.py
+++ b/hypn0/hypn0_site/models.py
@@ -596,13 +596,13 @@ class TbBlogPost(models.Model):
def blog_nav_title(self) -> str:
"""Детерминированный выбор названия раздела блога для навигации на основе ID статьи."""
nav_titles = (
- "Блог Мозгоклюя",
- "Записки Гипножабы",
- "Дневник Мозговых Перцепторов",
- "Хроники психо-резонанса",
- "Мемуары астрального архива",
- "Манифесты волновой матрицы",
- "Бортовой журнал транс-погружения",
+ "Brain Slug Blog",
+ "Hypnotoad Field Notes",
+ "Neural Perceptor Diary",
+ "Psycho-Resonance Chronicles",
+ "Astral Archive Memoirs",
+ "Wave Matrix Manifestos",
+ "Deep Trance Flight Log",
)
if not self.id:
return nav_titles[0]
diff --git a/hypn0/hypn0_site/services/naming.py b/hypn0/hypn0_site/services/naming.py
index fd92d57..f9e61a0 100644
--- a/hypn0/hypn0_site/services/naming.py
+++ b/hypn0/hypn0_site/services/naming.py
@@ -47,7 +47,7 @@ HYPNO_GENITIVES = [
HYPNO_POSTFIXES = [
"#42", "v3.14", "(Ultra)", "(Phase 7)", "(Alpha)", "(Omega)", "(Protocol 09)", "(Revision 14)",
"(Extract)", "(Layer 9)", "(Ward 106)", "(Hangar 18)", "(Unfinished Opus)", "(Secret Project)",
- "[Classified]", "(DOOP-66)", "(Sub-Level 4)", "(MK-Ultra)", "С90"
+ "[Classified]", "(DOOP-66)", "(Sub-Level 4)", "(MK-Ultra)", "C90"
]
diff --git a/hypn0/hypn0_site/tests.py b/hypn0/hypn0_site/tests.py
index 433cd2a..ccc0911 100644
--- a/hypn0/hypn0_site/tests.py
+++ b/hypn0/hypn0_site/tests.py
@@ -232,7 +232,7 @@ class HalftoneViewTests(BaseMediaTestCase):
self.assertContains(response, 'X-CSRFToken')
self.assertContains(response, "handleFileChange")
self.assertContains(response, "imagePreviewUrl")
- self.assertContains(response, "Гипножаба облизывается на:")
+ self.assertContains(response, "Hypnotoad salivates over:")
# Проверяем, что csrf_token не пустой в hx-headers
content = response.content.decode("utf-8")
self.assertNotIn('hx-headers=\'{"X-CSRFToken": ""}\'', content)
@@ -291,7 +291,7 @@ class HalftoneViewTests(BaseMediaTestCase):
def test_generate_post_no_image(self):
response = self.client.post(reverse("hypn0_site:generate"), data={"shape": "circle"})
self.assertEqual(response.status_code, 200)
- self.assertContains(response, "Покорми Гипножабу")
+ self.assertContains(response, "Feed the Hypnotoad")
def test_generate_post_success(self):
buf = io.BytesIO()
@@ -366,8 +366,8 @@ class PublishViewTests(BaseMediaTestCase):
},
)
self.assertEqual(response.status_code, 200)
- self.assertContains(response, "Мозговые слизняки протестуют!")
- self.assertContains(response, "Подчиниться и опубликовать")
+ self.assertContains(response, "Brain Slugs are rioting!")
+ self.assertContains(response, "Submit and Publish")
self.assertEqual(TbHypn0Item.objects.count(), 0)
def test_publish_with_cookie_success(self):
@@ -389,8 +389,8 @@ class PublishViewTests(BaseMediaTestCase):
},
)
self.assertEqual(response.status_code, 200)
- self.assertContains(response, "Уже в галерее транса")
- self.assertContains(response, "идет прорастание психо-паутины...")
+ self.assertContains(response, "In the Trance Gallery")
+ self.assertContains(response, "psychic neural web expanding…")
# Проверяем запись в БД
self.assertEqual(TbHypn0Item.objects.count(), 1)
@@ -419,7 +419,7 @@ class PublishViewTests(BaseMediaTestCase):
data={"svg_content": ""},
)
self.assertEqual(response.status_code, 200)
- self.assertContains(response, "Нет данных SVG для публикации")
+ self.assertContains(response, "No SVG data for publication")
self.assertEqual(TbHypn0Item.objects.count(), 0)
@@ -522,11 +522,11 @@ class GalleryDetailAndDownloadTests(BaseMediaTestCase):
self.assertEqual(response.status_code, 200)
self.assertContains(response, "Астральный Транс Сознания #42")
self.assertContains(response, f"#{self.item.s_hash_id}")
- self.assertContains(response, "Векторная анатомия")
- self.assertContains(response, "Синтез психо-сетки")
- self.assertContains(response, "Астральный паспорт")
- self.assertContains(response, "Копировать SVG-код")
- self.assertContains(response, "Скачать .svg")
+ self.assertContains(response, "Vector Anatomy")
+ self.assertContains(response, "Psych-Grid Synthesis")
+ self.assertContains(response, "Astral Passport")
+ self.assertContains(response, "Copy SVG code")
+ self.assertContains(response, "Download .svg")
# Проверка инкремента просмотров
self.item.refresh_from_db()
@@ -557,7 +557,7 @@ class GalleryDetailAndDownloadTests(BaseMediaTestCase):
client = Client()
response = client.post(reverse("hypn0_site:gallery_vote", kwargs={"hash_id": self.item.s_hash_id}))
self.assertEqual(response.status_code, 200)
- self.assertContains(response, "Подчинитесь воле Гипножабы!")
+ self.assertContains(response, "Surrender to Hypnotoad!")
class GalleryRandomAndNavigationTests(BaseMediaTestCase):
@@ -699,8 +699,8 @@ class UnconsciousMatrixTests(BaseMediaTestCase):
item = self.items[0]
response = self.client.get(reverse("hypn0_site:gallery_detail", kwargs={"hash_id": item.s_hash_id}))
self.assertEqual(response.status_code, 200)
- self.assertContains(response, "Матрица бессознательного")
- self.assertContains(response, "5 узлов")
+ self.assertContains(response, "Matrix of the Unconscious")
+ self.assertContains(response, "5 nodes")
self.assertContains(response, f"#{item.s_hash_id}")
self.assertContains(response, "btn-nav-prev")
self.assertContains(response, "btn-nav-next")
@@ -782,7 +782,7 @@ class GalleryFreshFloorTests(BaseMediaTestCase):
response = self.client.get(reverse("hypn0_site:index"))
self.assertEqual(response.status_code, 200)
- self.assertContains(response, "Плеск бессознательного")
+ self.assertContains(response, "Incubator of the Unconscious")
self.assertContains(response, "FRESH STREAM")
self.assertContains(response, f"gallery-card-{item.s_hash_id}")
self.assertContains(response, f"¤{item.s_hash_id}")
@@ -935,12 +935,12 @@ class GalleryArchiveTests(BaseMediaTestCase):
url = reverse("hypn0_site:gallery_archive")
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
- self.assertContains(response, "Галерея транса")
+ self.assertContains(response, "Trance Gallery")
self.assertContains(response, "ARCHIVE")
- self.assertContains(response, "Все волны")
- self.assertContains(response, "Инкубатор")
- self.assertContains(response, "Одобрено")
- self.assertContains(response, "Золотой фонд")
+ self.assertContains(response, "All waves")
+ self.assertContains(response, "Incubator")
+ self.assertContains(response, "Approved")
+ self.assertContains(response, "Hall of Fame")
# Все 3 картины присутствуют в выдаче
self.assertContains(response, f"gallery-card-{self.item_fresh.s_hash_id}")
self.assertContains(response, f"gallery-card-{self.item_curated.s_hash_id}")
@@ -1013,21 +1013,21 @@ class GalleryArchiveTests(BaseMediaTestCase):
resp_p1 = self.client.get(url, data={"floor": "all", "sort": "likes"})
self.assertEqual(resp_p1.status_code, 200)
self.assertEqual(len(resp_p1.context["page_obj"]), 16)
- self.assertContains(resp_p1, "Фаза 1 из 2")
- self.assertContains(resp_p1, "
Галерея транса — Каталог анимированных картин | HypnoSVG")
+ self.assertContains(resp_p1, "Phase 1 of 2")
+ self.assertContains(resp_p1, "Trance Gallery — Animated SVG Catalog | HypnoSVG")
# Проверяем сохранение query-параметров в ссылках пагинатора
self.assertContains(resp_p1, "floor=all&sort=likes&page=2")
resp_p2 = self.client.get(url, data={"floor": "all", "sort": "likes", "page": 2})
self.assertEqual(resp_p2.status_code, 200)
self.assertEqual(len(resp_p2.context["page_obj"]), 2)
- self.assertContains(resp_p2, "Фаза 2 из 2")
- self.assertContains(resp_p2, "Галерея транса — Каталог анимированных картин (Фаза 2) | HypnoSVG")
+ self.assertContains(resp_p2, "Phase 2 of 2")
+ self.assertContains(resp_p2, "Trance Gallery — Animated SVG Catalog (Phase 2) | HypnoSVG")
self.assertContains(resp_p2, "floor=all&sort=likes&page=1")
# Проверяем Title при фильтрации по этажу
resp_curated = self.client.get(url, data={"floor": "curated"})
- self.assertContains(resp_curated, "Одобрено Мозговым Слизнем | HypnoSVG")
+ self.assertContains(resp_curated, "Approved by Brain Slug | HypnoSVG")
class RescoreCommandTests(BaseMediaTestCase):
@@ -1311,12 +1311,12 @@ class BlogPostModelAndAdminTests(BaseMediaTestCase):
# Проверяем, что отображается один из динамических заголовков секции
content = response.content.decode("utf-8")
possible_headings = (
- "Сигналы из параллельного континуума",
- "Психо-артефакты ноосферы",
- "Заметки астрального консилиума",
- "Рефлекторные отклики мозговых слизней",
- "Публикации из глубин подсознания",
- "Материалы хроник гипноза",
+ "Signals from Parallel Continuum",
+ "Noosphere Psycho-Artifacts",
+ "Astral Council Field Notes",
+ "Reflexive Responses of Brain Slugs",
+ "Dispatches from Subconscious Depths",
+ "Materials from Hypno Chronicles",
)
self.assertTrue(any(heading in content for heading in possible_headings))
self.assertContains(response, "Хроники гипно-транса: Урок 1")
@@ -1345,7 +1345,7 @@ class BlogPostModelAndAdminTests(BaseMediaTestCase):
# Проверка 1-й страницы ленты блога
response = self.client.get("/blog")
self.assertEqual(response.status_code, 200)
- self.assertContains(response, "Хроники гипноза")
+ self.assertContains(response, "Hypno Chronicles")
self.assertContains(response, "Тестовая депеша 11")
self.assertNotContains(response, "Черновик ноосферы")
self.assertContains(response, "page=2")
@@ -1388,7 +1388,7 @@ class BlogPostModelAndAdminTests(BaseMediaTestCase):
resp_parent = self.client.get(f"/blog/{series_parent.slug}")
self.assertEqual(resp_parent.status_code, 200)
self.assertContains(resp_parent, "Цикл: Основы векторного гипноза")
- self.assertContains(resp_parent, "Материалы и главы этого цикла")
+ self.assertContains(resp_parent, "Chapters in This Series")
self.assertContains(resp_parent, "Глава 1: Первичная модуляция")
self.assertContains(resp_parent, "Глава 2: Фазовый сдвиг матрицы")
@@ -1396,31 +1396,31 @@ class BlogPostModelAndAdminTests(BaseMediaTestCase):
resp_ch1 = self.client.get(f"/blog/{ch1.slug}")
self.assertEqual(resp_ch1.status_code, 200)
self.assertContains(resp_ch1, "Глава 1: Первичная модуляция")
- self.assertContains(resp_ch1, "Материал входит в серию:")
+ self.assertContains(resp_ch1, "Part of the series:")
self.assertContains(resp_ch1, self.item.s_title)
self.assertContains(resp_ch1, f"#{self.item.s_hash_id}")
self.assertContains(resp_ch1, f"/gallery/{self.item.s_hash_id}")
- self.assertContains(resp_ch1, "Скачать SVG")
- self.assertContains(resp_ch1, "Вес:")
- self.assertContains(resp_ch1, "Следующая глава цикла")
+ self.assertContains(resp_ch1, "Download SVG")
+ self.assertContains(resp_ch1, "Weight:")
+ self.assertContains(resp_ch1, "Next chapter in series")
self.assertContains(resp_ch1, "Глава 2: Фазовый сдвиг матрицы")
# Проверка хлебных крошек
- self.assertContains(resp_ch1, 'aria-label="Хлебные крошки"')
+ self.assertContains(resp_ch1, 'aria-label="Breadcrumbs"')
self.assertContains(resp_ch1, ch1.blog_nav_title)
- self.assertContains(resp_ch1, "Глава 1 из 2")
+ self.assertContains(resp_ch1, "Chapter 1 of 2")
self.assertContains(resp_ch1, 'aria-current="page"')
# Проверка JSON-LD BreadcrumbList
self.assertContains(resp_ch1, '"@type": "BreadcrumbList"')
- self.assertContains(resp_ch1, '"name": "Главная"')
- self.assertContains(resp_ch1, '"name": "Хроники гипноза"')
+ self.assertContains(resp_ch1, '"name": "Home"')
+ self.assertContains(resp_ch1, '"name": "Hypno Chronicles"')
self.assertContains(resp_ch1, series_parent.s_title)
# 3. Проверка второй главы (ссылка назад на главу 1)
resp_ch2 = self.client.get(f"/blog/{ch2.slug}")
self.assertEqual(resp_ch2.status_code, 200)
- self.assertContains(resp_ch2, "Предыдущая глава цикла")
+ self.assertContains(resp_ch2, "Previous chapter in series")
self.assertContains(resp_ch2, "Глава 1: Первичная модуляция")
# 4. Проверка доступа по числовому ID
@@ -1437,10 +1437,10 @@ class BlogPostModelAndAdminTests(BaseMediaTestCase):
"""Проверка отдачи кастомной страницы 404 в режиме DEBUG=False."""
response = self.client.get("/totally-non-existent-hypno-route-404")
self.assertEqual(response.status_code, 404)
- self.assertContains(response, "Гипноузел не обнаружен", status_code=404)
- self.assertContains(response, "СБОЙ МАТРИЦЫ // КОД? 404", status_code=404)
+ self.assertContains(response, "Hypno-Node Not Found", status_code=404)
+ self.assertContains(response, "MATRIX ANOMALY // CODE 404", status_code=404)
self.assertContains(response, "return-btn", status_code=404)
- self.assertContains(response, "Стираем остатки несуществующей страницы", status_code=404)
+ self.assertContains(response, "Erasing traces of the phantom page", status_code=404)
@override_settings(DEBUG=True)
def test_debug_error_preview_routing(self):
@@ -1448,23 +1448,23 @@ class BlogPostModelAndAdminTests(BaseMediaTestCase):
# 1. Индексный список страниц ошибок
resp_list = self.client.get("/_error/")
self.assertEqual(resp_list.status_code, 200)
- self.assertContains(resp_list, "Центр отладки страниц ошибок")
+ self.assertContains(resp_list, "Error Pages Debug Center")
self.assertContains(resp_list, "/_error/404")
- self.assertContains(resp_list, "готов к просмотру")
+ self.assertContains(resp_list, "ready to preview")
# 2. Проверка каждого кода ошибки
expected_checks = [
- ("400", 400, "Сбой ментального протокола"),
- ("401", 401, "Требуется ментальная идентификация"),
- ("403", 403, "Отказано бюрократом 24-го уровня"),
- ("404", 404, "Гипноузел не обнаружен"),
- ("413", 413, "Перегрузка сенсорных каналов"),
- ("429", 429, "Слишком много мыслей в секунду"),
- ("500", 500, "Критический перегрев неокортекса"),
- ("502", 502, "Контейнер потерял сознание"),
- ("503", 503, "Плановый сеанс гипнотерапии"),
- ("504", 504, "Разрыв астральной связи"),
- ("under_reconstruction", 200, "Отдел мозга на реконструкции"),
+ ("400", 400, "Perceptual Protocol Mismatch"),
+ ("401", 401, "Subconscious Identity Required"),
+ ("403", 403, "Cognitive Lockdown Active"),
+ ("404", 404, "Hypno-Node Not Found"),
+ ("413", 413, "Sensory Channel Saturation"),
+ ("429", 429, "Too Many Thoughts per Second"),
+ ("500", 500, "Introspection Neural Lock"),
+ ("502", 502, "Uplink to Hippocampus Lost"),
+ ("503", 503, "Scheduled Hypnotherapy in Progress"),
+ ("504", 504, "Astral Synapse Timed Out"),
+ ("under_reconstruction", 200, "Brain Sector Under Reconstruction"),
]
for code, expected_status, text_fragment in expected_checks:
@@ -1478,12 +1478,12 @@ class BlogPostModelAndAdminTests(BaseMediaTestCase):
# 3. Предпросмотр с суффиксом .html
resp_404_html = self.client.get("/_error/404.html")
self.assertEqual(resp_404_html.status_code, 404)
- self.assertContains(resp_404_html, "Гипноузел не обнаружен", status_code=404)
+ self.assertContains(resp_404_html, "Hypno-Node Not Found", status_code=404)
# 4. Несуществующий шаблон ошибки в dev-режиме
resp_non_existent = self.client.get("/_error/non-existent-code-999")
self.assertEqual(resp_non_existent.status_code, 404)
- self.assertContains(resp_non_existent, "Шаблон для ошибки 'non-existent-code-999' не найден", status_code=404)
+ self.assertContains(resp_non_existent, "Template for error 'non-existent-code-999' not found", status_code=404)
def test_custom_django_error_handlers(self):
"""Проверка прямого вызова стандартных Django-обработчиков ошибок."""
@@ -1495,22 +1495,22 @@ class BlogPostModelAndAdminTests(BaseMediaTestCase):
req400 = factory.get("/bad-request")
resp400 = error_400(req400)
self.assertEqual(resp400.status_code, 400)
- self.assertIn("Сбой ментального протокола", resp400.content.decode("utf-8"))
+ self.assertIn("Perceptual Protocol Mismatch", resp400.content.decode("utf-8"))
req403 = factory.get("/forbidden")
resp403 = error_403(req403)
self.assertEqual(resp403.status_code, 403)
- self.assertIn("Отказано бюрократом 24-го уровня", resp403.content.decode("utf-8"))
+ self.assertIn("Cognitive Lockdown Active", resp403.content.decode("utf-8"))
req404 = factory.get("/not-found")
resp404 = error_404(req404)
self.assertEqual(resp404.status_code, 404)
- self.assertIn("Гипноузел не обнаружен", resp404.content.decode("utf-8"))
+ self.assertIn("Hypno-Node Not Found", resp404.content.decode("utf-8"))
req500 = factory.get("/server-error")
resp500 = error_500(req500)
self.assertEqual(resp500.status_code, 500)
- self.assertIn("Критический перегрев неокортекса", resp500.content.decode("utf-8"))
+ self.assertIn("Introspection Neural Lock", resp500.content.decode("utf-8"))
def test_s_title_plain_property(self):
"""Проверка очистки HTML-тегов и мнемоник в s_title_plain для моделей TbHypn0Item и TbBlogPost."""
diff --git a/hypn0/hypn0_site/views.py b/hypn0/hypn0_site/views.py
index 718afa4..c1caac0 100644
--- a/hypn0/hypn0_site/views.py
+++ b/hypn0/hypn0_site/views.py
@@ -196,14 +196,14 @@ def debug_error_preview(request: HttpRequest, code: str = "") -> HttpResponse:
break
except TemplateDoesNotExist:
pass
- status_mark = "готов к просмотру" if exists else "шаблон не создан"
+ status_mark = "ready to preview" if exists else "template not found"
links.append(f"/{c} ({status_mark})")
html = f"""
-
+
- Dev: Предпросмотр страниц ошибок // HypnoSVG
+ Dev: Error Pages Preview // HypnoSVG
+ {{ active_svg|safe }}
+
+
+ {% elif post.k_item.file_svg %}
+
+ {% endif %}
+
+
+
+
+
+
+ {{ post.k_item.s_title|safe }}
+
+
+
+
+ {{ post.k_item.get_i_level_display }}
+
+
+
+
+
+ {% if svg_stats.total_oscillators %}
+
+ Oscillators:
+ {{ svg_stats.total_oscillators }}
+
+ {% endif %}
+ {% if svg_stats.viewbox %}
+
+ viewBox:
+ {{ svg_stats.viewbox }}
+
+ {% endif %}
+ {% if svg_stats.total_kb %}
+
+ Weight:
+ {{ svg_stats.total_kb }} KB
+
+ {% endif %}
+
+
+
+ {{ post.k_item.i_views_count }}
+
+
+
+
+
+ {{ post.k_item.i_likes_count }}
+
+
+
+
+
+
+
+
+ {% elif post.f_cover_img %}{# 2. ЕСТЬ ТОЛЬКО ОБЛОЖКА #}
+
+

+
+ {% else %}{# 3. НЕТ НИ SVG, НИ ОБЛОЖКИ — ДЕФОЛТНАЯ ЗАГЛУШКА #}
+
+

+
No visual transmitted
+
+ {% endif %}
+
+
+
+
+
+
+ {{ post.s_title|safe }}
+
+
+
+
+
+
+
+
+
+
+
+ {% if post.s_teaser %}
+
+ {{ post.s_teaser|safe }}
+
+ {% endif %}
+
+
+
+
+ {{ post.s_content|safe }}
+
+
+
+
+ {% endif %}
+
{% if series_posts and not series_parent %}
@@ -292,7 +442,7 @@
Chapter {{ child.i_order }}
-
+