Files
guarddog-nexus/guarddog_nexus/i18n.py
Marker689 d33411b719 feat: фаза 3 — i18n RU/EN, /metrics, AI disclaimer, initiator+IP, LLM очередь
3.3 i18n: модуль с RU/EN словарями, LangMiddleware (cookie+query),
     Jinja-фильтр t(), переключатель EN/RU в nav, перевод ключевых
     строк интерфейса
3.5 /metrics: Prometheus-совместимый endpoint (scans_total,
     scans_flagged, findings_total, by_status, by_ecosystem,
     last_scan_timestamp)
3.2 AI disclaimer: сноска под каждым LLM-вердиктом (.llm-disclaimer)
3.4 LLM очередь: asyncio.Semaphore(LLM_MAX_CONCURRENT_ANALYSES)
3.1 initiator + source_ip: поля в Scan, захват из webhook payload,
     показ в scan_detail + API
3.6 UI: убран stat-minibar и heatmap с дашборда
2026-05-10 07:37:22 +03:00

107 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Internationalisation — RU / EN dictionaries and helpers."""
_STRINGS = {
"nav_dashboard": {"en": "Dashboard", "ru": "Дашборд"},
"nav_scans": {"en": "Scans", "ru": "Сканы"},
"nav_packages": {"en": "Packages", "ru": "Пакеты"},
"title_dashboard": {"en": "GuardDog Nexus", "ru": "GuardDog Nexus"},
"title_scans": {"en": "Scans — GuardDog Nexus", "ru": "Сканы — GuardDog Nexus"},
"title_packages": {"en": "Packages — GuardDog Nexus", "ru": "Пакеты — GuardDog Nexus"},
"title_scan_detail": {"en": "Scan #{} — GuardDog Nexus", "ru": "Скан #{} — GuardDog Nexus"},
"title_package_detail": {"en": "{} v{} — GuardDog Nexus", "ru": "{} v{} — GuardDog Nexus"},
"heading_scans": {"en": "Scans", "ru": "Сканы"},
"heading_packages": {"en": "Packages", "ru": "Пакеты"},
"heading_dashboard": {"en": "Dashboard", "ru": "Дашборд"},
"heading_latest_flagged": {"en": "Latest Flagged", "ru": "Последние флаги"},
"heading_latest_scans": {"en": "Latest Scans", "ru": "Последние сканы"},
"heading_findings": {"en": "Findings", "ru": "Находки"},
"heading_findings_count": {"en": "Findings ({})", "ru": "Находки ({})"},
"heading_scans_count": {"en": "Scans ({})", "ru": "Сканы ({})"},
"col_id": {"en": "ID", "ru": "ID"},
"col_package": {"en": "Package", "ru": "Пакет"},
"col_version": {"en": "Version", "ru": "Версия"},
"col_repo": {"en": "Repo", "ru": "Репозиторий"},
"col_repository": {"en": "Repository", "ru": "Репозиторий"},
"col_status": {"en": "Status", "ru": "Статус"},
"col_findings": {"en": "Findings", "ru": "Находки"},
"col_time": {"en": "Time", "ru": "Время"},
"col_name": {"en": "Name", "ru": "Имя"},
"col_ecosystem": {"en": "Ecosystem", "ru": "Экосистема"},
"col_flagged": {"en": "Flagged", "ru": "Флаг"},
"col_last_scan": {"en": "Last Scan", "ru": "Посл. скан"},
"filter_search": {"en": "Search packages...", "ru": "Поиск пакетов..."},
"filter_all_statuses": {"en": "All Statuses", "ru": "Все статусы"},
"filter_pending": {"en": "Pending", "ru": "Ожидает"},
"filter_scanning": {"en": "Scanning", "ru": "Сканируется"},
"filter_completed": {"en": "Completed", "ru": "Завершён"},
"filter_failed": {"en": "Failed", "ru": "Ошибка"},
"btn_show_all": {"en": "Show all", "ru": "Показать все"},
"btn_flagged_only": {"en": "Flagged only", "ru": "Только флаги"},
"btn_export_csv": {"en": "Export CSV", "ru": "Экспорт CSV"},
"btn_analyze_llm": {"en": "Analyze with LLM", "ru": "Анализ через LLM"},
"btn_copy": {"en": "Copy", "ru": "Копировать"},
"btn_prev": {"en": "Prev", "ru": "Назад"},
"btn_next": {"en": "Next", "ru": "Вперёд"},
"scan_info_package": {"en": "Package", "ru": "Пакет"},
"scan_info_version": {"en": "Version", "ru": "Версия"},
"scan_info_ecosystem": {"en": "Ecosystem", "ru": "Экосистема"},
"scan_info_repository": {"en": "Repository", "ru": "Репозиторий"},
"scan_info_status": {"en": "Status", "ru": "Статус"},
"scan_info_sha256": {"en": "SHA256", "ru": "SHA256"},
"scan_info_started": {"en": "Started", "ru": "Начат"},
"scan_info_finished": {"en": "Finished", "ru": "Завершён"},
"scan_info_initiated": {"en": "Initiated by", "ru": "Инициатор"},
"scan_info_source_ip": {"en": "Source IP", "ru": "IP-адрес"},
"scan_info_error": {"en": "Error", "ru": "Ошибка"},
"empty_no_scans": {
"en": "No scans yet — scans will appear here once packages are processed.",
"ru": "Сканов пока нет — появятся после обработки пакетов.",
},
"empty_no_packages": {
"en": "No packages yet — packages will appear here once scans are processed.",
"ru": "Пакетов пока нет — появятся после сканирования.",
},
"empty_no_findings": {
"en": "No findings — package looks clean.",
"ru": "Находок нет — пакет чистый.",
},
"view_all_scans": {"en": "View all scans →", "ru": "Все сканы →"},
"refresh_hint": {
"en": "Last refresh: {} (auto every 30s)",
"ru": "Обновлено: {} (авто каждые 30с)",
},
"llm_disabled": {"en": "LLM analysis is disabled", "ru": "LLM-анализ отключён"},
"llm_failed": {"en": "LLM analysis failed", "ru": "LLM-анализ не удался"},
"llm_not_found": {"en": "Finding not found", "ru": "Находка не найдена"},
"llm_disclaimer": {
"en": "⚠ AI-generated analysis — may contain inaccuracies. "
"Always verify findings before taking action.",
"ru": "⚠ Анализ сгенерирован AI — может содержать неточности. "
"Всегда проверяйте находки перед принятием мер.",
},
"breadcrumb_home": {"en": "Home", "ru": "Главная"},
"breadcrumb_scans": {"en": "Scans", "ru": "Сканы"},
"breadcrumb_packages": {"en": "Packages", "ru": "Пакеты"},
"breadcrumb_dashboard": {"en": "Dashboard", "ru": "Дашборд"},
"page_of": {"en": "of", "ru": "из"},
"total_scans": {"en": "{} total scans", "ru": "{} всего сканов"},
"total_packages": {"en": "{} total packages", "ru": "{} всего пакетов"},
"scan_detail_title": {"en": "Scan #{}", "ru": "Скан #{}"},
"status_pending": {"en": "pending", "ru": "ожидает"},
"status_scanning": {"en": "scanning", "ru": "сканируется"},
"status_completed": {"en": "completed", "ru": "завершён"},
"status_failed": {"en": "failed", "ru": "ошибка"},
}
LANGUAGES = {"en": "English", "ru": "Русский"}
DEFAULT_LANG = "en"
def t(key: str, lang: str = DEFAULT_LANG, **kwargs) -> str:
"""Look up a string in the given language, with optional formatting."""
entry = _STRINGS.get(key, {})
text = entry.get(lang) or entry.get(DEFAULT_LANG, key)
if kwargs:
return text.format(**kwargs)
return text