diff --git a/web/static/i18n/en-US.json b/web/static/i18n/en-US.json
index d78e6940..955c7339 100644
--- a/web/static/i18n/en-US.json
+++ b/web/static/i18n/en-US.json
@@ -1,7 +1,8 @@
{
"lang": {
"zhCN": "中文",
- "enUS": "English"
+ "enUS": "English",
+ "ruRU": "Русский"
},
"common": {
"ok": "OK",
diff --git a/web/static/i18n/ru-RU.json b/web/static/i18n/ru-RU.json
new file mode 100644
index 00000000..06f9aab1
--- /dev/null
+++ b/web/static/i18n/ru-RU.json
@@ -0,0 +1,4899 @@
+{
+ "lang": {
+ "zhCN": "中文",
+ "enUS": "English",
+ "ruRU": "Русский"
+ },
+ "common": {
+ "ok": "ОК",
+ "cancel": "Отмена",
+ "refresh": "Обновить",
+ "close": "Закрыть",
+ "edit": "Изменить",
+ "delete": "Удалить",
+ "remove": "Убрать",
+ "save": "Сохранить",
+ "loading": "Загрузка…",
+ "search": "Поиск",
+ "clearSearch": "Очистить поиск",
+ "noData": "Нет данных",
+ "confirm": "Подтвердить",
+ "copy": "Копировать",
+ "copied": "Скопировано",
+ "copyFailed": "Не удалось скопировать",
+ "view": "Открыть",
+ "actions": "Действия",
+ "loadFailed": "Ошибка загрузки",
+ "loadMore": "Загрузить ещё",
+ "untitled": "Без названия",
+ "justNow": "Только что",
+ "minutesAgo": "{{n}} мин назад",
+ "hoursAgo": "{{n}} ч назад",
+ "daysAgo": "{{n}} дн. назад"
+ },
+ "header": {
+ "title": "CyberStrikeAI",
+ "apiDocs": "Документация API",
+ "github": "GitHub",
+ "logout": "Выйти",
+ "language": "Язык интерфейса",
+ "backToDashboard": "К обзору",
+ "userMenu": "Меню пользователя",
+ "userMenuFor": "Меню пользователя: {{name}}",
+ "currentUser": "Текущий пользователь",
+ "unknownUser": "Неизвестный пользователь",
+ "scopeAll": "Все ресурсы",
+ "scopeAssigned": "Назначенные ресурсы",
+ "scopeOwn": "Свои ресурсы",
+ "scopeUnknown": "Неизвестная область",
+ "rolesCount": "Ролей: {{count}}",
+ "permissionsCount": "Прав: {{count}}",
+ "version": "Текущая версия",
+ "toggleSidebar": "Свернуть или развернуть боковую панель"
+ },
+ "theme": {
+ "system": "Системная",
+ "light": "Светлая",
+ "dark": "Тёмная",
+ "titleSystem": "Сейчас: системная тема. Нажмите, чтобы переключить на светлую.",
+ "titleLight": "Сейчас: светлая тема. Нажмите, чтобы переключить на тёмную.",
+ "titleDark": "Сейчас: тёмная тема. Нажмите, чтобы переключить на системную.",
+ "toggle": "Сменить тему"
+ },
+ "notifications": {
+ "title": "Уведомления",
+ "empty": "Нет новых событий",
+ "markAllRead": "Прочитать все",
+ "markSingleRead": "Прочитано",
+ "itemDefaultTitle": "Уведомление"
+ },
+ "login": {
+ "title": "Вход в CyberStrikeAI",
+ "titlePrefix": "Вход в",
+ "subtitle": "Войдите с учётной записью платформы. Администратор по умолчанию — admin.",
+ "usernameLabel": "Имя пользователя",
+ "usernamePlaceholder": "admin",
+ "passwordLabel": "Пароль",
+ "passwordPlaceholder": "Введите пароль",
+ "submit": "Войти"
+ },
+ "nav": {
+ "security": "Безопасность",
+ "toolGuard": "Блокировка вызовов",
+ "dashboard": "Обзор",
+ "chat": "Чат",
+ "assets": "Управление активами",
+ "assetOverview": "Обзор активов",
+ "assetLibrary": "Библиотека активов",
+ "infoCollect": "Разведка",
+ "tasks": "Задачи",
+ "projects": "Проекты",
+ "vulnerabilities": "Уязвимости",
+ "webshell": "Управление WebShell",
+ "chatFiles": "Управление файлами",
+ "mcp": "MCP",
+ "mcpMonitor": "Монитор MCP",
+ "mcpManagement": "Управление MCP",
+ "knowledge": "Знания",
+ "knowledgeRetrievalLogs": "История извлечения",
+ "knowledgeManagement": "Управление базой знаний",
+ "skills": "Навыки",
+ "skillsMonitor": "Монитор навыков",
+ "skillsManagement": "Управление навыками",
+ "agents": "Агенты",
+ "agentsManagement": "Управление агентами",
+ "roles": "Роли",
+ "rolesManagement": "Управление ролями",
+ "workflows": "Сценарии",
+ "settings": "Системные настройки",
+ "hitl": "Одобрение человеком",
+ "c2": "C2",
+ "c2Listeners": "Слушатели",
+ "c2Sessions": "Сессии",
+ "c2Tasks": "Задачи",
+ "c2Payloads": "Полезная нагрузка",
+ "c2Events": "События",
+ "c2Profiles": "Профили трафика",
+ "platformRbac": "Доступ к платформе"
+ },
+ "navGroups": {
+ "workbench": "Рабочая область",
+ "operations": "Операции безопасности",
+ "capabilities": "Возможности",
+ "administration": "Администрирование"
+ },
+ "dashboard": {
+ "title": "Обзор",
+ "refresh": "Обновить",
+ "refreshData": "Обновить данные",
+ "runningTasks": "Задачи в работе",
+ "runningConversations": "Диалоги в работе",
+ "vulnTotal": "Всего уязвимостей",
+ "toolCalls": "Вызовы инструментов",
+ "successRate": "Успешность инструментов",
+ "tokenUsage": "Расход токенов",
+ "tokenUsageSub": "За 7 дней {{calls}} вызовов · сегодня {{today}}",
+ "noTokenUsageYet": "Расхода пока нет",
+ "clickToViewTasks": "Открыть задачи",
+ "clickToViewChat": "Открыть диалоги",
+ "clickToViewVuln": "Открыть уязвимости",
+ "clickToViewMCP": "Открыть монитор MCP",
+ "accessOverviewTitle": "Обзор доступа",
+ "accessTabsAria": "C2 и WebShell",
+ "c2OverviewTitle": "Обзор C2",
+ "c2GoManage": "Открыть C2 →",
+ "webshellGoManage": "Открыть WebShell →",
+ "webshellConnections": "Активные подключения",
+ "webshellClickConnections": "К подключениям",
+ "c2ListenersRunning": "Слушатели работают",
+ "c2SessionsOnline": "Сессии онлайн",
+ "c2TasksPending": "Задачи в ожидании и в очереди",
+ "c2ClickListeners": "К слушателям",
+ "c2ClickSessions": "К сессиям",
+ "c2ClickTasks": "К задачам",
+ "severityDistribution": "Критичность уязвимостей",
+ "severityCritical": "Критическая",
+ "severityHigh": "Высокая",
+ "severityMedium": "Средняя",
+ "severityLow": "Низкая",
+ "severityInfo": "Инфо",
+ "totalVulns": "Всего уязвимостей",
+ "riskLevel": "Уровень риска",
+ "riskScore": "Взвешенный балл риска",
+ "riskSafe": "Безопасно",
+ "riskLow": "Низкий",
+ "riskMedium": "Средний",
+ "riskHigh": "Высокий",
+ "riskSevere": "Критический",
+ "latestFound": "Последние находки",
+ "noneYet": "Пока нет",
+ "runOverview": "Обзор запусков",
+ "batchQueues": "Пакетные очереди",
+ "pending": "В ожидании",
+ "executing": "Выполняется",
+ "completed": "Завершено",
+ "toolInvocations": "Вызовы инструментов",
+ "callsUnit": "вызовов",
+ "toolsUnit": "инструментов",
+ "knowledgeLabel": "База знаний",
+ "knowledgeItems": "элементов",
+ "categoriesUnit": "категорий",
+ "skillsLabel": "Навыки",
+ "skillUnit": "Навыки",
+ "quickLinks": "Быстрые ссылки",
+ "toolsExecCount": "Выполнения инструментов",
+ "ctaTitle": "Начните проверку безопасности",
+ "ctaSub": "Опишите цель в чате — ИИ поможет со сканированием и анализом уязвимостей",
+ "goToChat": "Перейти в чат",
+ "noTasks": "Нет задач",
+ "totalCount": "Всего {{count}}",
+ "notEnabled": "Отключено",
+ "enabled": "Включено",
+ "toConfigure": "Настроить",
+ "toUse": "Использовать",
+ "active": "Активно",
+ "highFreq": "Частые",
+ "noCallData": "Нет данных о вызовах",
+ "severityClickHint": "Нажмите, чтобы открыть",
+ "lastUpdated": "Обновлено",
+ "viewAll": "Все →",
+ "recentVulns": "Недавние уязвимости",
+ "recentFacts": "Недавние факты",
+ "noVulnYet": "Нет недавних уязвимостей",
+ "noFactsYet": "Нет недавних фактов",
+ "noFactsDesc": "В диалогах проекта агент записывает цели, находки и цепочки атак",
+ "createFirstProjectBtn": "Создать первый проект",
+ "factProjectMeta": "{{project}} · {{key}}",
+ "factsAcrossProjects_one": "{{count}} активный проект · {{facts}} фактов",
+ "factsAcrossProjects_other": "{{count}} активных проектов · {{facts}} фактов",
+ "capabilities": "Возможности",
+ "mcpTools": "Инструменты MCP",
+ "rolesLabel": "Роли",
+ "agentsLabel": "Агенты",
+ "webshellLabel": "WebShell",
+ "pendingCountLabel": "{{count}} в ожидании",
+ "highCountLabel": "Высоких {{count}}",
+ "toolsCountLabel_one": "{{count}} инструмент",
+ "toolsCountLabel_other": "{{count}} инструментов",
+ "failedNCalls_one": "{{count}} ошибка",
+ "failedNCalls_other": "{{count}} ошибок",
+ "noCompletedYet": "Завершённых результатов пока нет",
+ "noCallYet": "Вызовов пока нет",
+ "allClear": "Новых рисков нет",
+ "allIdle": "Система простаивает",
+ "executingNow": "Выполняется",
+ "healthyStatus": "В норме",
+ "normalStatus": "В целом в порядке",
+ "degradedStatus": "Требует внимания",
+ "alertTitle": "Обратите внимание",
+ "alertWarningTitle": "Требует внимания",
+ "alertDangerTitle": "Нужно действие",
+ "alertCriticalReason_one": "{{count}} открытая критическая уязвимость — проверьте сразу",
+ "alertCriticalReason_other": "{{count}} открытых критических уязвимостей — проверьте сразу",
+ "alertFailedReason_one": "Низкая успешность инструментов ({{count}} неудачный вызов) — проверьте монитор MCP",
+ "alertFailedReason_other": "Низкая успешность инструментов ({{count}} неудачных вызовов) — проверьте монитор MCP",
+ "alertHitlReason_one": "{{count}} запрос HITL ожидает одобрения — агент ждёт вашего решения",
+ "alertHitlReason_other": "{{count}} запросов HITL ожидают одобрения — агент ждёт вашего решения",
+ "alertMcpDownReason_one": "{{count}} внешний сервер MCP не работает — связанные инструменты недоступны",
+ "alertMcpDownReason_other": "{{count}} внешних серверов MCP не работают — связанные инструменты недоступны",
+ "alertDismiss": "Скрыть (в этой сессии)",
+ "openHighCountLabel": "Открытых высоких {{count}}",
+ "allHandled": "Высокая критичность обработана",
+ "viewVulns": "Открыть уязвимости",
+ "viewMonitor": "Открыть монитор",
+ "viewHitl": "Одобрить",
+ "viewMcpManagement": "Управление MCP",
+ "statusOpen": "Открыта",
+ "statusConfirmed": "Подтверждена",
+ "statusFixed": "Исправлена",
+ "statusFalsePositive": "Ложное срабатывание",
+ "statusIgnored": "Игнорируется",
+ "statusFilter": "Фильтр по статусу",
+ "allStatuses": "Все статусы",
+ "fixRate": "Доля исправленных",
+ "dataStale": "Данные могли устареть — обновите",
+ "recommendedActions": "Рекомендуемые действия",
+ "recommendedActionsHint": "По текущему состоянию",
+ "recoFixCritical_one": "Исправить {{count}} открытую критическую уязвимость",
+ "recoFixCritical_other": "Исправить {{count}} открытых критических уязвимостей",
+ "recoFixCriticalDesc": "Критические уязвимости устраняйте в первую очередь",
+ "recoApproveHitl_one": "Одобрить {{count}} запрос HITL",
+ "recoApproveHitl_other": "Одобрить {{count}} запросов HITL",
+ "recoApproveHitlDesc": "Агенту нужно ваше решение, чтобы продолжить",
+ "recoRestartMcp_one": "Проверить {{count}} остановленный внешний MCP",
+ "recoRestartMcp_other": "Проверить {{count}} остановленных внешних MCP",
+ "recoRestartMcpDesc": "Связанные инструменты недоступны, пока MCP не восстановится",
+ "recoCheckMonitor_one": "Разобрать {{count}} неудачный вызов инструмента",
+ "recoCheckMonitor_other": "Разобрать {{count}} неудачных вызовов инструментов",
+ "recoCheckMonitorDesc": "Подробности ошибок — в мониторе MCP",
+ "recoSetupMcp": "Настроить первый инструмент MCP",
+ "recoSetupMcpDesc": "Установите сервер MCP, чтобы агент мог вызывать эти возможности",
+ "recoStartScan": "Запустить сканирование из чата",
+ "recoStartScanDesc": "Опишите цель в чате — ИИ поможет выполнить проверку",
+ "recentEvents": "Недавние события",
+ "eventUntitled": "Событие",
+ "externalMcpServers": "Внешний MCP",
+ "mcpAllRunning": "Все работают",
+ "mcpPartialDown_one": "{{count}} остановлен",
+ "mcpPartialDown_other": "{{count}} остановлено",
+ "mcpAllDown": "Все остановлены",
+ "noVulnDesc": "Здесь недавние записи. Новые появятся, когда обнаружение в чате завершится",
+ "startScanBtn": "В чат для сканирования",
+ "factsAcrossProjects": "{{count}} активных проектов · {{facts}} фактов",
+ "toolsCountLabel": "{{count}} инструментов",
+ "failedNCalls": "{{count}} ошибок",
+ "alertCriticalReason": "{{count}} открытых критических уязвимостей — рекомендуется устранить сразу",
+ "alertFailedReason": "Низкая успешность инструментов ({{count}} ошибок) — проверьте монитор MCP",
+ "alertHitlReason": "{{count}} запросов HITL ожидают одобрения — агент ждёт вашего решения",
+ "alertMcpDownReason": "Не запущено внешних серверов MCP: {{count}}. Связанные инструменты недоступны",
+ "recoFixCritical": "Исправить открытые критические уязвимости: {{count}}",
+ "recoApproveHitl": "Одобрить запросы HITL: {{count}}",
+ "recoRestartMcp": "Проверить не запущенные внешние MCP: {{count}}",
+ "recoCheckMonitor": "Разобрать неудачные вызовы инструментов: {{count}}",
+ "mcpPartialDown": "Не запущено: {{count}}"
+ },
+ "projects": {
+ "title": "Проекты",
+ "showArchived": "Показывать архив",
+ "newProjectCta": "+ Новый проект",
+ "projectList": "Список проектов",
+ "searchProjectsPlaceholder": "Поиск проектов…",
+ "paginationShow": "Показано {{start}}-{{end}} из {{total}}",
+ "paginationRange": "{{start}}-{{end}}/{{total}}",
+ "paginationTotal": "Всего {{total}}",
+ "paginationPage": "{{page}}/{{total}}",
+ "paginationPerPage": "На странице",
+ "paginationPrev": "Назад",
+ "paginationNext": "Далее",
+ "selectOrCreateTitle": "Выберите или создайте проект",
+ "selectOrCreateHint": "У проекта общая доска фактов для диалогов; цель, среда, аутентификация и другие факты автоматически добавляются в привязанные диалоги.",
+ "createFirstProject": "Создать первый проект",
+ "defaultProjectName": "Проект",
+ "statusActive": "Активен",
+ "statusArchived": "В архиве",
+ "vulnerabilityManagement": "Управление уязвимостями",
+ "addFactCta": "+ Добавить факт",
+ "tabFacts": "Доска фактов",
+ "tabGraph": "Путь атаки",
+ "tabAssets": "Библиотека активов",
+ "tabConversations": "Привязанные диалоги",
+ "tabVulns": "Связанные уязвимости",
+ "tabSettings": "Настройки",
+ "boundAssetsHint": "Показаны только активы, привязанные к этому проекту. Нажмите цель, чтобы открыть подробности.",
+ "noBoundAssets": "К этому проекту не привязаны активы",
+ "assetCount": "{{count}} активов",
+ "factToolbarHint": "В индекс входят только ключ и краткое содержание (что, где и как проверить). Цепочку атаки и POC помещайте в тело; воспроизведение — через get_project_fact.",
+ "graphToolbarHint": "Стрелки совпадают с сохранёнными связями фактов (источник → цель). Слои узлов: цель → инфраструктура → находка → эксплуатация. Пунктир — предварительные связи.",
+ "graphView": "Вид",
+ "graphViewPath": "Путь атаки",
+ "graphViewFull": "Весь граф",
+ "graphSearchSr": "Поиск узлов",
+ "graphSearchPlaceholder": "Поиск узлов…",
+ "graphRefresh": "Обновить",
+ "graphCenter": "Центр",
+ "graphEmpty": "Данных графа пока нет. Добавьте связи у фактов находок и эксплуатации (discovered_on → target/*), чтобы построить путь.",
+ "graphEmptyTitle": "Соберите путь атаки",
+ "graphEmptyStep1": "Добавьте факты целей: домены, конечные точки, область",
+ "graphEmptyStep2": "Зафиксируйте находки и эксплуатацию со связями между фактами",
+ "graphEmptyStep3": "Добавляйте связи в режиме «Соединить» или при правке фактов",
+ "graphEmptyCta": "Добавить первый факт",
+ "graphStats": "Узлы: {{nodes}} | Рёбра: {{edges}}",
+ "graphStatsNodes": "Узлы",
+ "graphStatsEdges": "Рёбра",
+ "graphLegendNodes": "Узлы",
+ "graphLegendEdges": "Рёбра",
+ "graphLegendNodeTarget": "Цель",
+ "graphLegendNodeInfra": "Инфра",
+ "graphLegendNodeFinding": "Находка",
+ "graphLegendNodeVuln": "Уязвимость",
+ "graphLegendNodeExploit": "Эксплойт",
+ "graphLegendNodeMissing": "Отсутствует",
+ "graphLegendDiscovered": "discovered_on",
+ "graphLegendLeads": "leads_to",
+ "graphLegendExploits": "exploits",
+ "graphLegendTentative": "Предварительно (пунктир)",
+ "factLinksLabel": "Связи (откуда → этот факт)",
+ "factLinksPlaceholder": "discovered_on: target/primary_domain\nexploits: exploit/upload-rce",
+ "factLinksHint": "По одной строке: тип: source_fact_key (источник → этот факт). Частые типы: discovered_on, depends_on, leads_to, enables, exploits. При сохранении все связи заменяются.",
+ "factIncomingLinksLabel": "Входящие связи (только чтение)",
+ "factIncomingLinksHint": "Берутся из исходящих связей исходных фактов. Например, discovered_on находки к target/* видна как входящая у цели; исходящие связи правятся у исходного факта.",
+ "factIncomingLinksEmpty": "Нет входящих связей",
+ "graphEdgeFromSelf": "От этого узла",
+ "graphEdgeToSelf": "К этому узлу",
+ "linksColumn": "Связи",
+ "linkCountsTitle": "Число исходящих и входящих рёбер",
+ "graphConnect": "Соединить",
+ "graphConnectActive": "Соединение…",
+ "graphConnectPickTarget": "Источник {{source}} выбран — нажмите узел-цель",
+ "graphEdgeTypePrompt": "Тип ребра (discovered_on / leads_to / depends_on / enables / exploits)",
+ "graphConnectFailed": "Не удалось создать ребро",
+ "graphConnectSuccess": "Ребро создано",
+ "graphEdgesTitle": "Связи",
+ "graphEdgesHint": "Направление стрелки совпадает с базой и окном правки (источник → цель). Нажмите ребро, чтобы выделить его.",
+ "graphEdgesEmpty": "Связей пока нет",
+ "graphEdgeOutgoing": "Исходящая",
+ "graphEdgeIncoming": "Входящая",
+ "graphEdgeSynthetic": "Создано из связи факта; чтобы убрать, измените факт",
+ "confirmDeleteGraphEdge": "Удалить эту связь?",
+ "graphEdgeDeleteFailed": "Не удалось удалить ребро",
+ "graphEdgeDeleteSuccess": "Ребро удалено",
+ "graphDeleteEdge": "Удалить",
+ "viewVulnerability": "Открыть уязвимость",
+ "graphVulnSidebarHint": "Узел связанной уязвимости. Кнопка ниже открывает её в управлении уязвимостями.",
+ "promoteAttackChain": "Перенести цепочку",
+ "promoteAttackChainTitle": "Перенести цепочку атаки диалога в факты проекта",
+ "confirmPromoteAttackChain": "Перенести цепочку атаки этого диалога в проект? Факты и рёбра будут созданы или обновлены.",
+ "promoteAttackChainFailed": "Не удалось перенести",
+ "promoteAttackChainSuccess": "Перенесено: новых {{facts_created}} / обновлено {{facts_updated}} / рёбер {{edges_created}}",
+ "searchFactsSr": "Поиск фактов",
+ "searchFactsPlaceholder": "Ключ, краткое содержание, тело…",
+ "category": "Категория",
+ "all": "Все",
+ "confidence": "Достоверность",
+ "confidenceConfirmed": "Подтверждено",
+ "confidenceTentative": "Предварительно",
+ "confidenceDeprecated": "Устарело",
+ "displayOptions": "Отображение",
+ "sparseOnly": "Только неполные",
+ "hideDeprecated": "Скрыть устаревшие",
+ "summary": "Кратко",
+ "updated": "Обновлено",
+ "boundConversationsHint": "Диалоги, привязанные к этому проекту. Нажмите, чтобы открыть.",
+ "titleLabel": "Название",
+ "projectVulnSummaryHint": "Сводка уязвимостей этого проекта",
+ "searchVulnsSr": "Поиск уязвимостей",
+ "searchVulnsPlaceholder": "Название, описание, тип, цель…",
+ "noMatchingVulns": "Нет подходящих уязвимостей. Измените фильтры.",
+ "viewInVulnerabilityManagement": "Открыть в управлении уязвимостями",
+ "severity": "Критичность",
+ "status": "Статус",
+ "modalNewTitle": "Новый проект",
+ "modalNewSubtitle": "После создания привяжите диалоги — доска фактов будет общей",
+ "modalEditTitle": "Изменить проект",
+ "modalEditSubtitle": "Обновите название и описание проекта",
+ "projectName": "Название проекта",
+ "projectNamePlaceholder": "например, веб-тест заказчика А",
+ "projectDescription": "Описание проекта",
+ "projectDescriptionPlaceholder": "Заметки о заказчике и задаче, совместная работа…",
+ "createProject": "Создать проект",
+ "newProject": "Новый проект",
+ "chatSelectorButton": "После привязки проекта доска фактов общая для диалогов",
+ "selectProject": "Выбрать проект",
+ "noProject": "Без проекта",
+ "factBodyEnvTitle": "Факт среды",
+ "factBodyHasDetail": "Есть подробности",
+ "factBodySparseTitle": "Нет структуры цепочки атаки или POC",
+ "factBodySparse": "Неполно",
+ "factBodyReproducibleTitle": "Есть структура для воспроизведения",
+ "factBodyReproducible": "Воспроизводимо",
+ "factHintAttackSparse": "Факт цепочки атаки: заполните тело полностью (шаги, HTTP или команда, доказательства из ответа). Не ограничивайтесь выводом. Можно вставить шаблон цепочки атаки.",
+ "factHintAttackReady": "Факт цепочки атаки: тело нужно для воспроизведения при аудите. Сохраняйте исходные запрос и ответ и пошаговый ход.",
+ "factHintEnv": "Факт среды: укажите в теле источник доказательств. Для находок и эксплуатации используйте категорию finding, chain, exploit или poc.",
+ "confirmOverwriteBodyTemplate": "Заменить текущее тело шаблоном?",
+ "loadProjectsFailed": "Не удалось загрузить проекты",
+ "restoreTitle": "Восстановить как предварительный и вернуть в индекс доски",
+ "restore": "Восстановить",
+ "deprecateTitle": "Пометить как устаревший",
+ "deprecate": "Пометить устаревшим",
+ "editTitle": "Изменить поля",
+ "viewBodyTitle": "Показать тело целиком",
+ "details": "Подробности",
+ "deleteForeverTitle": "Удалить безвозвратно",
+ "noProjects": "Нет проектов",
+ "noMatchingProjects": "Нет подходящих проектов",
+ "pinned": "Закреплён",
+ "archived": "В архиве",
+ "statsFacts": "{{count}} фактов",
+ "statsVulns": "{{count}} уязвимостей",
+ "statsConversations": "{{count}} диалогов",
+ "statsSparse": "{{count}} неполных",
+ "projectNotFound": "Проект не найден",
+ "updatedPrefix": "Обновлено {{time}}",
+ "descExpand": "Показать всё",
+ "descCollapse": "Свернуть",
+ "descriptionLengthHint": "Только для команды: агент это не читает. Кратко, не более 4000 символов. Всё, что должен знать агент, помещайте на доску фактов.",
+ "noMatchingFacts": "Нет подходящих фактов. Измените фильтры.",
+ "noFacts": "Фактов пока нет. Нажмите «Добавить факт» или дайте агенту записать их.",
+ "relatedVulnIdTitle": "ID связанной уязвимости",
+ "noBoundConversations": "Привязанных диалогов пока нет. Выберите этот проект в диалоге, чтобы привязать.",
+ "untitledConversation": "Диалог без названия",
+ "open": "Открыть",
+ "unbindProjectTitle": "Отвязать проект",
+ "unbind": "Отвязать",
+ "unbindAssetConfirm": "Отвязать «{{target}}» от этого проекта? Актив не будет удалён.",
+ "unbindAssetDone": "Актив отвязан от проекта",
+ "confirmUnbindConversation": "Отвязать этот диалог от текущего проекта?",
+ "unbindFailed": "Не удалось отвязать",
+ "factMetaCategory": "Категория: {{value}}",
+ "factMetaConfidence": "Достоверность: {{value}}",
+ "factMetaUpdated": "Обновлено: {{time}}",
+ "factMetaRelatedVuln": "Связанная уязвимость: {{value}}",
+ "factMetaSourceConversation": "Исходный диалог: {{value}}",
+ "factMetaHasPrevious": "Есть предыдущая версия",
+ "emptyBody": "(пустое тело)",
+ "factSparseWarn": "Этот факт относится к цепочке атаки или эксплуатации, но в теле нет структуры для воспроизведения (шаги, HTTP или команда, запрос и ответ). Дополните его для воспроизведения при аудите.",
+ "factPreviousMeta": "В архиве с {{time}} · Кратко: {{summary}} · Достоверность: {{confidence}}",
+ "loadVulnerabilityListFailed": "Не удалось загрузить список уязвимостей",
+ "noVulnerabilitiesInProject": "В этом проекте пока нет уязвимостей. Создайте запись или дайте агенту зафиксировать её.",
+ "promptLinkFactToVuln": "Введите номер, чтобы связать факт «{{factKey}}»:\n\n{{lines}}",
+ "invalidIndex": "Неверный номер",
+ "linkFailed": "Не удалось связать",
+ "linkSuccess": "Уязвимость связана",
+ "promptConversationIdForVulnCreate": "Чтобы создать уязвимость, нужен ID диалога (можно исходного):",
+ "cancelledNoConversationId": "Отменено: не указан conversation_id",
+ "createVulnerabilityFailed": "Не удалось создать уязвимость",
+ "createVulnerabilityAndLinkSuccess": "Уязвимость создана и связана: {{value}}",
+ "confirmDeprecateFact": "Пометить факт {{factKey}} как устаревший?",
+ "operationFailed": "Операция не выполнена",
+ "confirmRestoreFact": "Восстановить факт {{factKey}}? Он снова попадёт в индекс доски со статусом «предварительный».",
+ "noVulnerabilityRecords": "В этом проекте нет записей об уязвимостях",
+ "viewRelatedFactsTitle": "Связанные факты",
+ "facts": "Факты",
+ "loadRelatedFactsFailed": "Не удалось загрузить связанные факты",
+ "noFactsForVulnerability": "У этой уязвимости пока нет связанных фактов. Свяжите её или создайте черновик из подробностей факта.",
+ "promptChooseFactByIndex": "С этой уязвимостью связано фактов: {{count}}. Введите номер для просмотра:\n{{lines}}",
+ "enterProjectName": "Введите название проекта",
+ "writePermissionDenied": "Эта учётная запись только для чтения: создавать и изменять проекты нельзя",
+ "saveFailed": "Не удалось сохранить",
+ "invalidJson": "Некорректный формат JSON",
+ "scopeNoteAuthorizedWebOnly": "Разрешено только тестирование уровня веб-приложения",
+ "invalidScopeJson": "Некорректный JSON области. Исправьте его или нажмите «Форматировать».",
+ "saved": "Сохранено",
+ "confirmArchiveProject": "После архивации проект по умолчанию скрыт из активного списка. Продолжить?",
+ "confirmRestoreProjectActive": "Вернуть в активные?",
+ "confirmDeleteProject": "Удалить этот проект? Факты будут удалены, диалоги отвязаны.",
+ "deleteFailed": "Не удалось удалить",
+ "addFact": "Добавить факт",
+ "saveFact": "Сохранить факт",
+ "editFact": "Изменить факт",
+ "saveChanges": "Сохранить",
+ "customCategoryOption": "{{value}} (своё)",
+ "selectProjectFirst": "Сначала выберите проект",
+ "loadFactFailed": "Не удалось загрузить факт",
+ "factKeySummaryRequired": "Нужны fact_key и краткое содержание",
+ "confirmSaveSparseFact": "Этот факт относится к цепочке атаки или эксплуатации, но в теле нет структуры для воспроизведения (шаги, HTTP или команда, запрос и ответ).\nВсё равно сохранить? Сначала лучше вставить шаблон цепочки атаки и заполнить POC.",
+ "confirmDeleteFact": "Удалить этот факт?",
+ "notUpdatedYet": "Ещё не обновлялось",
+ "clearStaleProjectBindingFailed": "Не удалось сбросить устаревшую привязку к проекту",
+ "noProjectDescription": "Нет привязки к проекту",
+ "noProjectsClickCreate": "Проектов пока нет. Нажмите «Новый проект» ниже.",
+ "sharedFactBoard": "Общая доска фактов",
+ "loadFailedRetry": "Не удалось загрузить, повторите позже",
+ "projectBound": "Проект привязан",
+ "projectUnbound": "Проект отвязан",
+ "updateProjectBindingFailed": "Не удалось обновить привязку к проекту",
+ "basicInfoTitle": "Основные сведения",
+ "basicInfoHint": "Название и описание видны в сведениях о проекте",
+ "settingsIntroTitle": "Настройки проекта",
+ "settingsIntroHint": "Метаданные проекта и граница полномочий агента. После сохранения сразу действуют для привязанных диалогов.",
+ "pinProject": "Закрепить проект (первым в списке)",
+ "pinProjectAction": "Закрепить проект",
+ "unpinProjectAction": "Открепить проект",
+ "pinFact": "Закрепить факт (выше в списке и в индексе доски)",
+ "editDescriptionPlaceholder": "Заметки о заказчике и задаче, контакты, совместная работа…",
+ "scopeTitle": "Область тестирования",
+ "scopeHint": "JSON границы полномочий агента и целевых активов",
+ "formatJson": "Форматировать",
+ "example": "Пример",
+ "scopeJsonLabel": "JSON области",
+ "scopeFootnote": "Доступны targets, exclude, notes и другие поля. Пусто — без ограничения области.",
+ "dangerZoneTitle": "Опасная зона",
+ "dangerZoneHint": "Архивные проекты скрыты, пока не включено «Показывать архив». Удаление безвозвратно стирает все факты.",
+ "archiveRestore": "Архив и восстановление",
+ "archiveProject": "В архив",
+ "editProject": "Изменить",
+ "renameProject": "Переименовать",
+ "restoreProjectActive": "Вернуть в активные",
+ "projectActions": "Действия с проектом",
+ "deleteProject": "Удалить проект",
+ "saveChangesHint": "Нажмите «Сохранить», чтобы отправить изменения на сервер",
+ "saveSettings": "Сохранить",
+ "factModalSubtitle": "Краткое содержание попадает в индекс доски. В теле — цепочка атаки и POC для воспроизведения при аудите, отдельно от записей уязвимостей.",
+ "relatedVulnIdLabel": "ID связанной уязвимости",
+ "optional": "Необязательно",
+ "factDetails": "Подробности факта",
+ "previousVersion": "Предыдущая версия",
+ "currentVersion": "Текущая версия",
+ "linkVulnerability": "Связать уязвимость",
+ "createVulnerabilityDraft": "Создать черновик уязвимости",
+ "generatedFromFact": "Создано из факта проекта {{factKey}}"
+ },
+ "chat": {
+ "toolExecBlocked": "Инструмент {{name}} заблокирован",
+ "newChat": "Новый диалог",
+ "newTask": "Новая задача",
+ "toggleConversationPanel": "Свернуть или развернуть список диалогов",
+ "searchHistory": "Поиск по истории...",
+ "projectFolders": "Проекты",
+ "projectFoldersLoadMoreRemaining": "Загрузить ещё, осталось проектов: {{count}}",
+ "projectPreviewLabel": "Сведения о проекте",
+ "projectPreviewStats": "{{total}} задач · открыто {{active}}",
+ "projectPreviewNoDescription": "Нет описания проекта",
+ "projectPreviewScope": "Область тестирования: {{scope}}",
+ "projectPreviewEdit": "Изменить проект",
+ "conversationPreviewJustNow": "Только что",
+ "conversationPreviewMinutes": "{{count}} мин",
+ "conversationPreviewHours": "{{count}} ч",
+ "conversationPreviewDays": "{{count}} дн.",
+ "conversationPreviewDateTime": "{{year}}-{{month}}-{{day}} {{hour}}:{{minute}}",
+ "conversationPreviewNoProject": "Без проекта",
+ "conversationPreviewDefaultMode": "По умолчанию",
+ "conversationPreviewUnread": "Непросмотренное обновление",
+ "conversationPreviewViewed": "Просмотрено",
+ "conversationPreviewConversation": "Диалог",
+ "returnToLatest": "К последнему сообщению",
+ "returnToLatestProcessDetail": "К последней итерации",
+ "taskProgressStep": "Шаг {{current}} из {{total}}",
+ "taskProgressOpen": "Ход задачи",
+ "taskProgressDetails": "Подробности хода задачи",
+ "taskProgressUnnamed": "Задача без названия",
+ "completedUnread": "Завершено, не просмотрено",
+ "newConversationInProject": "Новый диалог в этом проекте",
+ "newUnassignedConversation": "Новый диалог без проекта",
+ "conversationActions": "Действия с диалогом",
+ "renameConversationPrompt": "Введите новое название:",
+ "renameConversationTitle": "Переименовать диалог",
+ "renameConversationSubtitle": "Название обновится в папках проектов и в недавних диалогах",
+ "conversationTitleLabel": "Название диалога",
+ "conversationTitlePlaceholder": "Введите название диалога",
+ "recentConversations": "Недавние диалоги",
+ "toggleRecentConversations": "Развернуть или свернуть недавние диалоги",
+ "filterByProject": "Фильтр по проекту",
+ "filterAllProjects": "Все проекты",
+ "filterUnboundProjects": "Без привязки",
+ "filterProjectSearch": "Поиск проектов…",
+ "filterProjectSearchEmpty": "Нет подходящих проектов",
+ "filterProjectSearchHint": "Введите текст для поиска проектов",
+ "filterProjectSearchMore": "Введите текст, чтобы найти ещё проекты",
+ "filterProjectSearchLoading": "Поиск…",
+ "filterProjectSearchFailed": "Не удалось загрузить проекты. Повторите попытку.",
+ "projectConversationsTitle": "{{name}} · Диалоги",
+ "unboundConversationsTitle": "Диалоги без привязки",
+ "noProjectConversations": "В этом проекте нет диалогов",
+ "noUnboundConversations": "Нет диалогов без привязки",
+ "sortConversations": "Сортировка",
+ "sortByCreatedAt": "Время создания",
+ "sortByUpdatedAt": "Время изменения",
+ "batchManage": "Массовые действия",
+ "paginationShow": "Показано {{start}}-{{end}} из {{total}}",
+ "paginationRange": "{{start}}-{{end}}/{{total}}",
+ "paginationTotal": "Всего {{total}}",
+ "paginationPage": "{{page}}/{{total}}",
+ "paginationPerPage": "На странице",
+ "paginationPrev": "Назад",
+ "paginationNext": "Далее",
+ "attackChain": "Цепочка атаки",
+ "viewAttackChain": "Открыть цепочку атаки",
+ "selectRole": "Выбрать роль",
+ "defaultRole": "По умолчанию",
+ "inputPlaceholder": "Цель или команда… @ выбор инструментов",
+ "selectFile": "Выбрать файл",
+ "uploadFile": "Загрузить файл (несколько или перетаскивание)",
+ "readingAttachmentsDetail": "Чтение вложения {{current}}/{{total}} · {{name}} · {{percent}}%",
+ "uploadingAttachmentsDetail": "Загрузка вложений · готово {{done}}/{{total}} · всего {{percent}}%",
+ "waitingAttachmentsUpload": "Ожидание завершения загрузки вложений…",
+ "attachmentsUploadIncomplete": "Часть вложений не загрузилась. Удалите неудачные или выберите файлы снова перед отправкой.",
+ "attachmentUploading": "Загрузка…",
+ "attachmentUploadFailed": "Ошибка",
+ "attachmentUploadAlert": "Не удалось загрузить: {{name}}",
+ "send": "Отправить",
+ "loadingTools": "Загрузка инструментов...",
+ "noMatchTools": "Нет подходящих инструментов",
+ "penetrationTestDetail": "Ход выполнения задачи",
+ "expandDetail": "Развернуть подробности",
+ "turnElapsedRunning": "Обработка {{duration}}",
+ "turnElapsedComplete": "Заняло {{duration}}",
+ "turnElapsedCancelled": "Прервано · заняло {{duration}}",
+ "turnElapsedTimeout": "Тайм-аут · заняло {{duration}}",
+ "turnElapsedFailed": "Сбой · заняло {{duration}}",
+ "turnDurationSeconds": "{{seconds}} с",
+ "turnDurationMinutes": "{{minutes}} мин {{seconds}} с",
+ "turnDurationHours": "{{hours}} ч {{minutes}} мин",
+ "turnProcessAria": "{{state}}; развернуть или свернуть подробности выполнения",
+ "turnTokenUsageLabel": "{{tokens}} токенов",
+ "turnTokenUsageTitle": "Расход токенов: {{total}} (ввод {{prompt}}, вывод {{completion}}, кэш {{cached}}, рассуждение {{reasoning}}, вызовов {{calls}})",
+ "turnNumber": "Ход {{number}}",
+ "turnPending": "Обработка…",
+ "expandDetailLazyHint": "Развернуть подробности (итерации загружаются по щелчку)",
+ "loadingEarlierDetails": "Загрузка более ранних записей…",
+ "loadingLaterDetails": "Загрузка более новых записей…",
+ "viewToolDetail": "Подробнее",
+ "collapseToolDetail": "Свернуть",
+ "liveTimelinePruned": "Свёрнуты первые {{count}} записей живого хода. Полную запись можно смотреть постранично после завершения задачи.",
+ "liveTimelinePrunedSingleRound": "раунд main-agent {{n}}",
+ "liveTimelinePrunedRoundRange": "раунды main-agent {{from}}–{{to}}",
+ "toolExecutionsCount": "{{n}} запусков инструментов",
+ "collapseToolExecutions": "Свернуть запуски инструментов",
+ "toolExecutionDetailPending": "Подробности выполнения инструмента ещё не синхронизированы. Повторите попытку чуть позже.",
+ "noProcessDetail": "Нет подробностей хода (выполнение слишком быстрое или нет подробных событий)",
+ "copyMessageTitle": "Копировать сообщение",
+ "deleteTurnTitle": "Удалить этот ход",
+ "deleteTurnConfirm": "Удалить весь ход (сообщение пользователя и ответ ассистента)? Это нельзя отменить. Следующий ответ будет опираться только на оставшиеся сообщения; сохранённые снимки контекста будут очищены.",
+ "deleteTurnFailed": "Не удалось удалить ход",
+ "noHistoryConversations": "Истории диалогов пока нет",
+ "deleteConversationConfirm": "Удалить этот диалог? Сообщения не восстановить, но записанные уязвимости останутся в библиотеке уязвимостей.",
+ "renameFailed": "Не удалось переименовать",
+ "downloadConversationFailed": "Не удалось скачать диалог",
+ "viewAttackChainSelectConv": "Выберите диалог, чтобы открыть цепочку атаки",
+ "viewAttackChainCurrentConv": "Цепочка атаки текущего диалога",
+ "executeFailed": "Сбой выполнения",
+ "callOpenAIFailed": "Не удалось вызвать OpenAI",
+ "systemReadyMessage": "Система готова. Введите требования к проверке — система сама выполнит соответствующие тесты безопасности.",
+ "projectWelcomeMessage": "Текущий проект: {{project}}. Введите требования к проверке — система выполнит соответствующие тесты безопасности.",
+ "noProjectWelcomeMessage": "Проект не выбран. Введите требования к проверке — система выполнит соответствующие тесты безопасности.",
+ "projectWelcomeTitlePrefix": "Что проверить в ",
+ "projectWelcomeTitleSuffix": "?",
+ "noProjectWelcomeTitle": "Что проверить?",
+ "welcomeSubtitle": "Введите требования к проверке — система сама выполнит соответствующие тесты безопасности.",
+ "callNumber": "Вызов №{{n}}",
+ "iterationRound": "Итерация {{n}}",
+ "einoOrchestratorRound": "Оркестратор · раунд {{n}}",
+ "einoPlanExecuteRound": "Plan-Execute · раунд {{n}} · {{phase}}",
+ "planExecuteStreamPlanner": "Вывод планирования",
+ "planExecuteStreamExecutor": "Вывод выполнения",
+ "planExecuteStreamReplanning": "Вывод перепланирования",
+ "planExecuteStreamPhase": "Вывод фазы",
+ "einoSubAgentStep": "Субагент {{agent}} · шаг {{n}}",
+ "aiThinking": "Размышление ИИ",
+ "reasoningChain": "Ход рассуждения",
+ "planning": "Планирование",
+ "assistantStreamPhase": "Ответ ассистента",
+ "toolCallsDetected": "Обнаружено вызовов инструментов: {{count}}",
+ "callTool": "Вызов инструмента: {{name}} ({{index}}/{{total}})",
+ "toolExecComplete": "Инструмент {{name}} выполнен",
+ "toolExecFailed": "Сбой инструмента {{name}}",
+ "knowledgeRetrieval": "Поиск в базе знаний",
+ "knowledgeRetrievalTag": "Поиск в базе знаний",
+ "error": "Ошибка",
+ "streamNetworkErrorHint": "Соединение потеряно ({{detail}}). Длительная задача может ещё выполняться на сервере; проверьте выполняемые задачи сверху или обновите этот диалог позже.",
+ "streamEndedWithoutDone": "Соединение закрыто до сигнала завершения. Задача может ещё выполняться на сервере; проверьте выполняемые задачи сверху или обновите этот диалог.",
+ "taskCancelled": "Задача отменена",
+ "userInterruptContinueTitle": "⏸️ Прерывание пользователем и продолжение",
+ "unknownTool": "Неизвестный инструмент",
+ "einoAgentReplyTitle": "Ответ субагента",
+ "einoStreamErrorTitle": "⚠️ Поток Eino прерван ({{agent}})",
+ "einoStreamErrorMessage": "Не удалось прочитать поток; система повторит попытку или завершит работу согласно политике.",
+ "einoRunRetryTitle": "🔁 Повтор при временной ошибке",
+ "einoModelRetryTitle": "🔁 Повтор вызова модели",
+ "einoModelFailoverTitle": "🔀 Переключение на резервную модель",
+ "einoUsageSummaryTitle": "Сводка расхода токенов",
+ "einoUsageModelCalls": "Вызовы модели",
+ "einoUsagePromptTokens": "Токены запроса",
+ "einoUsageCompletionTokens": "Токены ответа",
+ "einoUsageTotalTokens": "Всего токенов",
+ "einoUsageCachedTokens": "Токены кэша",
+ "einoUsageReasoningTokens": "Токены рассуждения",
+ "einoEmptyResponseContinueTitle": "🔁 Автопродолжение (нет текста ассистента)",
+ "einoEmptyResponseContinueMessage": "Сессия завершилась без текста ассистента; продолжение по трассировке…",
+ "einoRunRetryPlan": "Повтор: попытка {{attempt}}/{{maxAttempts}}, ожидание {{backoffSec}} с",
+ "einoRunRetryReasonKind": "Тип причины",
+ "einoRunRetryErrorDetail": "Сведения об ошибке",
+ "einoRunRetryKind_rate_limit": "Превышен лимит запросов",
+ "einoRunRetryKind_retryable_http": "Повторяемая ошибка HTTP",
+ "einoRunRetryKind_upstream_server": "Ошибка вышестоящего сервера",
+ "einoRunRetryKind_http_error": "Ошибка HTTP",
+ "einoRunRetryKind_upstream_busy": "Вышестоящий сервис занят",
+ "einoRunRetryKind_network": "Сбой сетевого соединения",
+ "einoRunRetryKind_stream": "Сбой чтения потока",
+ "einoRunRetryKind_transient": "Временный сбой",
+ "iterationLimitReachedTitle": "⛔ Достигнут предел итераций",
+ "iterationLimitReachedMessage": "Достигнуто максимальное число итераций; автоматические итерации остановлены.",
+ "einoPendingOrphanedTitle": "🧹 Сверка вызовов инструментов",
+ "einoPendingOrphanedMessage": "Незакрытых вызовов инструментов: {{count}}. Они помечены как неуспешные и закрыты автоматически.",
+ "noDescription": "Нет описания",
+ "noResponseData": "Нет данных ответа",
+ "loading": "Загрузка...",
+ "loadFailed": "Не удалось загрузить: {{message}}",
+ "noAttackChainData": "Нет данных цепочки атаки",
+ "copyFailedManual": "Не удалось скопировать. Выделите текст и скопируйте вручную.",
+ "searching": "Поиск...",
+ "loadFailedRetry": "Не удалось загрузить, повторите попытку",
+ "dataFormatError": "Ошибка формата данных",
+ "progressInProgress": "Тест на проникновение выполняется...",
+ "executionFailed": "Сбой выполнения",
+ "penetrationTestComplete": "Тест на проникновение завершён",
+ "yesterday": "Вчера",
+ "historyGroupToday": "Сегодня",
+ "historyGroupLast7Days": "Последние 7 дней",
+ "historyGroupEarlier": "Ранее",
+ "agentModeSelectAria": "Выбор режима выполнения диалога",
+ "agentModePanelTitle": "Режим диалога",
+ "agentModeEinoSingle": "Eino, один агент (ADK)",
+ "agentModeEinoSingleHint": "Eino ChatModelAgent + Runner с инструментами MCP (/api/eino-agent)",
+ "agentModeDeep": "Deep (DeepAgent)",
+ "agentModeDeepHint": "Eino DeepAgent для сложных проверок безопасности, многоэтапного делегирования задач и синтеза",
+ "agentModePlanExecuteLabel": "Plan-Execute",
+ "agentModePlanExecuteHint": "План → выполнение → перепланирование (один исполнитель с инструментами)",
+ "agentModeSupervisorLabel": "Supervisor (маршрутизация к экспертам)",
+ "agentModeSupervisorHint": "Маршрутизация к экспертам: передача между несколькими специализированными субагентами",
+ "agentModeSingle": "Один агент",
+ "agentModeMulti": "Несколько агентов",
+ "agentModeSingleHint": "Один агент Eino ADK для диалога и вызова инструментов",
+ "agentModeMultiHint": "Готовая оркестрация Eino: Deep для сложных задач, Plan-Execute для структурированных циклов, Supervisor для маршрутизации к экспертам",
+ "reasoningModeLabel": "Рассуждение модели",
+ "reasoningEffortLabel": "Уровень рассуждения",
+ "reasoningModeDefault": "Как в системе",
+ "reasoningModeOff": "Выкл.",
+ "reasoningModeOn": "Вкл.",
+ "reasoningModeAuto": "Авто",
+ "reasoningEffortUnset": "Не задано",
+ "reasoningCompactLabel": "Рассуждение",
+ "reasoningCompactAria": "Открыть параметры рассуждения модели",
+ "reasoningPanelTitle": "Рассуждение модели",
+ "reasoningPanelHint": "Только для запросов Eino с одним агентом и с несколькими; совмещается со значениями по умолчанию в настройках.",
+ "sessionSettingsTitle": "Настройки сессии",
+ "sessionSettingsAria": "Открыть настройки сессии",
+ "sessionSettingsHint": "Параметры контроля человеком действуют только на следующие сообщения.",
+ "sessionShortcutAuditAgent": "Проверка агентом",
+ "modelSettingsAria": "Выбор канала ИИ, модели и параметров рассуждения",
+ "systemModelPickerTitle": "Выбор системной модели",
+ "systemModelField": "Модель",
+ "systemModelLoading": "Загрузка списка моделей…",
+ "systemModelLoaded": "Загружено моделей: {count}",
+ "systemModelCurrent": "Текущая",
+ "systemModelSaving": "Сохранение…",
+ "systemModelSaved": "Сохранено автоматически",
+ "reasoningSessionUpdated": "Рассуждение сессии обновлено",
+ "systemModelLoadFailed": "Не удалось получить модели",
+ "systemModelSaveFailed": "Не удалось сохранить модель",
+ "systemModelApplyFailed": "Не удалось применить модель",
+ "systemModelNeedApiKey": "Сначала укажите ключ API в системных настройках",
+ "systemModelRetry": "Повторить",
+ "sessionShortcutHuman": "Одобрение человеком",
+ "aiChannelLabel": "Канал ИИ",
+ "aiChannelDefault": "Канал по умолчанию",
+ "aiChannelDefaultShort": "Канал по умолчанию",
+ "reasoningSummaryFollow": "Система",
+ "reasoningSummaryDash": "—",
+ "agentModeOrchPlanExecute": "Plan-Exec",
+ "agentModeOrchSupervisor": "Supervisor",
+ "hitlTitle": "Контроль человеком",
+ "hitlCardSubtitle": "Одобрения и белый список",
+ "hitlReviewerLabel": "Проверяющий",
+ "hitlReviewerHuman": "Одобрение человеком",
+ "hitlReviewerAgent": "Агент аудита",
+ "hitlReviewerHint": "В любой момент переключайтесь между человеком и агентом аудита: правила и белый список те же. Выбор можно задать заранее, даже если HITL выключен.",
+ "hitlConfigTitle": "Параметры совместного режима",
+ "hitlModeLabel": "Режим",
+ "hitlModeOff": "Выкл.",
+ "hitlModeApproval": "Одобрение",
+ "hitlModeReviewEdit": "Проверка и правка",
+ "hitlSensitiveTools": "Чувствительные инструменты (через запятую)",
+ "hitlWhitelistTools": "Белый список инструментов (без одобрения, через запятую)",
+ "hitlWhitelistPlaceholder": "например, read_file, grep или по одному инструменту в строке (объединяется с глобальным белым списком в конфигурации)",
+ "hitlWhitelistHint": "Через запятую или с новой строки; показывается вместе с глобальным белым списком из конфигурации.",
+ "hitlApply": "Применить",
+ "hitlApplyOkSync": "Настройки HITL сохранены и переданы на сервер.",
+ "hitlApplyOkDefaultConfig": "Настройки HITL по умолчанию записаны в config.yaml и включены.",
+ "hitlApplyOkWhitelistYaml": "Белый список инструментов добавлен в config.yaml и действует. Настройки сессии сохраняются автоматически.",
+ "hitlApplyOkLocal": "Сохранено в этом браузере.",
+ "hitlApplyFail": "Не удалось синхронизировать с сервером",
+ "hitlTimeoutLabel": "Лимит ожидания одобрения",
+ "hitlTimeoutOneMinute": "1 минута",
+ "hitlTimeoutFiveMinutes": "5 минут",
+ "hitlTimeoutTenMinutes": "10 минут",
+ "hitlTimeoutUnlimited": "Без ограничения",
+ "hitlTimeoutHint": "Запросы без ответа автоматически отклоняются по истечении времени; на карточках одобрения виден отсчёт.",
+ "hitlStatusOff": "Контроль человеком: выкл.",
+ "rolePanelTitle": "Выбор роли"
+ },
+ "toolGuard": {
+ "invalidTestResponse": "Сервер вернул некорректный результат проверки",
+ "fieldTooLong": "Имена правил — до 200 байт UTF-8; шаблоны и сообщения — до 4096 байт UTF-8 каждое. Сократите текст и повторите.",
+ "tooManyRules": "Можно задать не более 100 правил блокировки.",
+ "title": "Блокировка вызовов",
+ "description": "Проверять имена и аргументы инструментов MCP до выполнения и останавливать совпавшие вызовы. Одобрения HITL и белые списки инструментов эти проверки не обходят.",
+ "reset": "Отменить изменения",
+ "save": "Сохранить и применить",
+ "policyTitle": "Политика блокировки",
+ "enableHint": "Проверка рисков до выполнения инструмента. Сохраните, чтобы применить изменения.",
+ "enabled": "Включить блокировку вызовов",
+ "scopeHint": "Правила проверяют только видимый текст вызова. Они не определяют владельца IP, цели перенаправлений и содержимое файлов, которое читают инструменты. Правила для доменов не покрывают каждый косвенный доступ.",
+ "rulesTitle": "Правила блокировки",
+ "rulesHint": "Включённые правила проверяются по порядку; первое совпадение задаёт сообщение блокировки. Шаблоны используют синтаксис RE2 и проверяются сервером при сохранении или тесте.",
+ "addRule": "Добавить правило",
+ "addDialogTitle": "Добавить правило блокировки",
+ "addDialogHint": "Настройте правило и проверьте его на примерах аргументов, прежде чем добавлять в список.",
+ "addToList": "Добавить в список",
+ "addToListHint": "После добавления правила выберите «Сохранить и применить» вверху страницы, чтобы активировать его.",
+ "cancelAdd": "Отмена",
+ "closeDialog": "Закрыть диалог добавления правила",
+ "singleTestTitle": "Проверить это правило",
+ "closeRuleTest": "Скрыть проверку правила",
+ "singleTestHint": "Проверьте текущий черновик отдельно до сохранения. Инструменты не выполняются.",
+ "testing": "Проверка…",
+ "testReadyHint": "Нажмите «Проверить совпадение», чтобы увидеть результат здесь.",
+ "testResultLabel": "Результат проверки",
+ "testChanged": "Содержимое изменилось. Запустите проверку снова.",
+ "ruleAdded": "Правило добавлено в список. Сохраните, чтобы применить его.",
+ "addingRule": "Проверка правила…",
+ "addFailed": "Не удалось добавить правило",
+ "messageHint": "В сообщениях доступны {match} (совпавший текст), {tool} (имя инструмента) и {rule} (имя правила).",
+ "testTitle": "Проверить все правила",
+ "testHint": "Проверяет текущую конфигурацию страницы, включая несохранённые изменения. Инструменты не выполняются.",
+ "test": "Проверить совпадение",
+ "testTool": "Имя инструмента",
+ "testArguments": "Аргументы инструмента (объект JSON)",
+ "loading": "Обработка…",
+ "unsaved": "Есть несохранённые изменения",
+ "savedState": "Синхронизировано с сервером",
+ "emptyRules": "Правил пока нет. Добавьте и сохраните правило, чтобы включить его проверки.",
+ "rule": "Правило",
+ "ruleEnabled": "Включено",
+ "deleteRule": "Удалить",
+ "ruleName": "Имя правила",
+ "pattern": "Шаблон (RE2)",
+ "message": "Сообщение блокировки",
+ "requestFailed": "Запрос не выполнен",
+ "invalidResponse": "Сервер вернул некорректную конфигурацию",
+ "loadFailed": "Не удалось загрузить конфигурацию блокировки",
+ "loadFirst": "Сначала загрузите конфигурацию блокировки",
+ "requiredFields": "Укажите имя и шаблон для каждого правила",
+ "saveSuccess": "Конфигурация блокировки вызовов сохранена. Дальнейшие вызовы инструментов будут использовать новую конфигурацию.",
+ "saveFailed": "Не удалось сохранить",
+ "defaultMessage": "Обнаружено {match}. Правило «{rule}» заблокировало этот вызов. Проверьте цель и операцию, прежде чем повторять.",
+ "blocked": "Правило совпало: этот вызов будет заблокирован",
+ "notBlocked": "Ни одно включённое правило не совпало",
+ "disabledResult": "Блокировка вызовов выключена: эта конфигурация не будет блокировать вызовы",
+ "matchedRule": "Совпавшее правило",
+ "matchedText": "Совпавший текст",
+ "matchedMessage": "Возвращённое сообщение блокировки",
+ "toolRequired": "Укажите имя инструмента для проверки",
+ "invalidArguments": "Аргументы инструмента должны быть корректным объектом JSON",
+ "testFailed": "Проверка не удалась",
+ "listHint": "Выберите правило, чтобы просмотреть или изменить. Правила проверяются в порядке списка.",
+ "helpTitle": "Синтаксис правил и область совпадения",
+ "testSummary": "Проверить общую блокировку с текущим порядком и состоянием правил",
+ "unnamedRule": "Правило без названия",
+ "expandRule": "Развернуть правило",
+ "collapseRule": "Свернуть правило",
+ "ruleOn": "Включено",
+ "ruleOff": "Отключено",
+ "ruleModified": "Изменено",
+ "ruleNew": "Новое",
+ "defaultPreview": "При совпадении используется сообщение блокировки по умолчанию",
+ "protectionOn": "Блокировка вызовов включена",
+ "protectionOff": "Блокировка вызовов выключена",
+ "ruleCount": "Включено правил: {{enabled}} из {{total}}",
+ "closeEditor": "Закрыть редактор",
+ "testEntry": "Проверить все правила",
+ "validateRule": "Проверить это правило",
+ "testAll": "Проверить все правила",
+ "testScopeAll": "Все правила",
+ "testScopeSingle": "Одно правило: {{name}}",
+ "testSingleHint": "Проверяет только это правило, без глобального переключателя и переключателей правил. Не сохраняет и не выполняет инструменты.",
+ "testAllHint": "Проверяет все правила в текущем порядке и с текущим состоянием. Не сохраняет и не выполняет инструменты.",
+ "singleMatched": "Это правило совпало",
+ "singleNotMatched": "Это правило не совпало",
+ "singleResultHint": "Это проверка одного правила. Фактическая блокировка также зависит от глобального переключателя, включённых правил и порядка совпадения."
+ },
+ "hitl": {
+ "pageTitle": "Одобрения HITL",
+ "pageReviewerLabel": "Текущий проверяющий",
+ "auditEngineLabel": "Движок одобрения",
+ "auditEngineOpenAI": "Протокол OpenAI",
+ "auditEngineJev": "TypeSafe Jev",
+ "colAuditEngine": "Движок одобрения",
+ "pageReviewerHint": "Действует для выбранного диалога. Без диалога сохраняется в config.yaml как значение по умолчанию для новых чатов. Применяется сразу.",
+ "pageReviewerSaved": "Проверяющий сохранён.",
+ "whitelistLabel": "Белый список инструментов (без одобрения)",
+ "whitelistHint": "По одному в строке или через запятую. Сохраняется в глобальный белый список config.yaml и применяется сразу (синхронизация с панелью чата).",
+ "whitelistSaved": "Белый список сохранён.",
+ "whitelistSaveFailed": "Не удалось сохранить белый список",
+ "strategyLabel": "Стратегия аудита (запрос)",
+ "strategyHint": "Инструменты из белого списка не требуют одобрения. Остальные оценивает модель по этому запросу, если выбран агент аудита.",
+ "strategyTabApproval": "Режим одобрения",
+ "strategyTabReviewEdit": "Режим проверки и правки",
+ "strategyHintApproval": "Инструменты из белого списка не требуют одобрения. В режиме одобрения агент аудита только одобряет или отклоняет.",
+ "strategyHintJev": "Активен TypeSafe Jev: текст стратегии ниже оценивается как структурированные вопросы. Встроенные правила для опасных действий остаются жёстким порогом. Jev не переписывает аргументы.",
+ "strategyHintReviewEdit": "В режиме проверки и правки агент аудита может сузить параметры через editedArguments перед одобрением. Отклоните запрос, если параметры нельзя безопасно поправить.",
+ "strategyReset": "Сбросить",
+ "strategySaved": "Стратегия аудита сохранена.",
+ "strategySaveFailed": "Не удалось сохранить стратегию аудита",
+ "tabPending": "Ожидают",
+ "tabLogs": "Журнал аудита",
+ "tabStrategy": "Стратегия аудита",
+ "tabWhitelist": "Белый список",
+ "pendingTitle": "Ожидают одобрения",
+ "auditReviewing": "Идёт автоматическая проверка",
+ "auditReviewEditing": "Автопроверка и правка",
+ "auditReviewExplanation": "Агент проверки рассматривает запрос по заданной стратегии. Выполнение начнётся только после одобрения.",
+ "auditApproved": "Агент аудита одобрил",
+ "auditEditedApproved": "Агент аудита изменил параметры и одобрил",
+ "auditRejected": "Агент аудита отклонил",
+ "waitingHumanApproval": "Ожидает одобрения",
+ "waitingHumanReview": "Ожидает проверки",
+ "humanApprovalExplanation": "Этот вызов инструмента нужно подтвердить перед запуском.",
+ "humanReviewExplanation": "Проверьте параметры и при необходимости измените их перед разрешением.",
+ "humanApproved": "Разрешено один раз",
+ "humanEditedApproved": "Параметры изменены и разрешены",
+ "humanRejected": "Отклонено при проверке",
+ "viewEditedArgs": "Изменённые параметры",
+ "reviewArgs": "Параметры (JSON)",
+ "commentOptional": "Комментарий (необязательно)",
+ "commentPlaceholder": "например, разрешить команду только для чтения",
+ "reject": "Отклонить",
+ "allowOnce": "Разрешить один раз",
+ "saveEditedAndAllow": "Сохранить правки и разрешить",
+ "waitingApprovalShort": "Ожидает одобрения",
+ "waitingApprovalCount": "Ожидают одобрения: {{count}}",
+ "approvalUrgencyUnlimited": "Срок одобрения не ограничен",
+ "approvalUrgencyMoreThanThree": "Ближайший срок — более 3 минут",
+ "approvalUrgencyMoreThanFive": "Ближайший срок — более 5 минут",
+ "approvalUrgencyThreeToFive": "Ближайший срок — через 3–5 минут",
+ "approvalUrgencyOneToThree": "Ближайший срок — через 1–3 минуты",
+ "approvalUrgencyWithinOne": "Ближайший срок — менее 1 минуты",
+ "requestGeneric": "Разрешить CyberStrikeAI вызвать {{tool}}?",
+ "requestVisitUrl": "Разрешить CyberStrikeAI открыть {{url}}?",
+ "requestVisitLongUrl": "Разрешить CyberStrikeAI открыть этот адрес?",
+ "requestModifyLongPath": "Разрешить CyberStrikeAI изменить этот файл?",
+ "requestBrowser": "Разрешить CyberStrikeAI использовать браузер?",
+ "requestCommand": "Разрешить CyberStrikeAI выполнить эту команду?",
+ "requestFile": "Разрешить CyberStrikeAI изменить {{path}}?",
+ "requestFiles": "Разрешить CyberStrikeAI изменить файлы?",
+ "toolTerminal": "Терминал",
+ "toolFiles": "Файлы",
+ "viewRequestDetails": "Подробности запроса",
+ "editRequestDetails": "Просмотр или правка параметров запроса",
+ "addApprovalComment": "Комментарий к одобрению (необязательно)",
+ "timeoutAutoReject": "По истечении отклоняется автоматически",
+ "timeoutUnlimited": "Без ограничения по времени",
+ "expiredAutoRejected": "Время истекло, идёт автоматическое отклонение…",
+ "taskClosedApprovalUnavailable": "Задача завершена, одобрение недоступно",
+ "taskInterrupted": "Задача прервана",
+ "interruptedApprovalCancelled": "Задача прервана, одобрение отменено",
+ "expiredRejected": "Время одобрения истекло, запрос отклонён",
+ "searchLabel": "Поиск",
+ "searchPlaceholder": "Инструмент, диалог, полезная нагрузка, комментарий…",
+ "searchApply": "Найти",
+ "filterDecision": "Решение",
+ "filterDecidedBy": "Проверяющий",
+ "filterAll": "Все",
+ "decisionApprove": "Одобрить",
+ "decisionReject": "Отклонить",
+ "reviewerHuman": "Человек",
+ "reviewerAgent": "Агент аудита",
+ "reviewerSystem": "Система",
+ "reviewerManual": "Вручную",
+ "logCreate": "Новая запись",
+ "logModalTitle": "Запись аудита",
+ "logModalEdit": "Изменить запись аудита",
+ "fieldConversation": "ID диалога",
+ "fieldTool": "Имя инструмента",
+ "fieldComment": "Комментарий",
+ "fieldPayload": "Полезная нагрузка (JSON)",
+ "fieldUserMessage": "Сообщение пользователя",
+ "fieldThinking": "Размышление",
+ "fieldReasoning": "Цепочка рассуждений",
+ "fieldPlanning": "План",
+ "colId": "ID",
+ "colTool": "Инструмент",
+ "colConversation": "Диалог",
+ "colDecision": "Решение",
+ "colDecidedBy": "Проверяющий",
+ "colContext": "Контекст",
+ "colTime": "Время",
+ "colActions": "Действия",
+ "viewDetail": "Подробнее",
+ "logModalView": "Подробности записи аудита",
+ "fieldExecutionResult": "Результат выполнения",
+ "executionSuccess": "успех",
+ "executionFailed": "ошибка",
+ "edit": "Изменить",
+ "delete": "Удалить",
+ "logsEmpty": "Нет записей аудита",
+ "logsEmptyHint": "Записи создаются автоматически при одобрении или отклонении запросов HITL.",
+ "pageInfo": "Всего {{total}}",
+ "prevPage": "Назад",
+ "nextPage": "Далее",
+ "conversationRequired": "Укажите ID диалога",
+ "toolRequired": "Укажите имя инструмента",
+ "saveFailed": "Не удалось сохранить",
+ "deleteConfirm": "Удалить эту запись аудита?",
+ "deleteFailed": "Не удалось удалить",
+ "retentionHint": "Записи аудита хранятся {{days}} дн., затем удаляются автоматически.",
+ "selectedCount": "Выбрано: {{count}}",
+ "selectAll": "Выбрать все",
+ "deselectAll": "Снять выбор",
+ "batchDelete": "Удалить выбранные",
+ "batchDeleteConfirm": "Удалить выбранные записи аудита ({{count}})? Это действие нельзя отменить.",
+ "batchDeleteSuccess": "Удалено записей аудита: {{count}}",
+ "batchDeleteFailed": "Не удалось удалить выбранные",
+ "clearAll": "Очистить всё",
+ "clearAllConfirm": "Очистить записи аудита по текущим фильтрам ({{count}})? Это действие нельзя отменить.",
+ "clearAllConfirmNoFilter": "Фильтры не заданы. Будут удалены все записи аудита ({{count}}). Это действие нельзя отменить. Продолжить?",
+ "clearAllSuccess": "Очищено записей: {{count}}",
+ "clearAllFailed": "Не удалось очистить",
+ "selectLogsFirst": "Сначала выберите записи для удаления",
+ "loading": "Загрузка...",
+ "emptyState": "Нет запросов на одобрение",
+ "dismiss": "Скрыть",
+ "conversationLabel": "Диалог:",
+ "reviewEditHelp": "Режим проверки и правки: укажите объект JSON, чтобы переопределить аргументы инструмента. Пример: {\"command\":\"ls -la\"}",
+ "approvalHelp": "Режим одобрения: только одобрить или отклонить, правка аргументов отключена.",
+ "commentHelp": "Комментарий (необязательно): кратко укажите причину.",
+ "approve": "Одобрить",
+ "loadFailed": "Не удалось загрузить",
+ "invalidJson": "Некорректные аргументы JSON",
+ "submitFailedPrefix": "Не удалось отправить:"
+ },
+ "progress": {
+ "callingAI": "Вызов модели ИИ...",
+ "callingTool": "Вызов инструмента: {{name}}",
+ "lastIterSummary": "Последняя итерация: сводка и следующие шаги...",
+ "summaryDone": "Сводка готова",
+ "generatingFinalReply": "Формирование итогового ответа...",
+ "maxIterSummary": "Достигнут предел итераций, формируется сводка...",
+ "analyzingRequestShort": "Анализ запроса...",
+ "analyzingRequestPlanning": "Анализ запроса и план стратегии проверки...",
+ "startingEinoDeepAgent": "Запуск Eino DeepAgent...",
+ "startingEinoMultiAgent": "Запуск многоагентного режима Eino...",
+ "einoAgent": "Агент Eino: {{name}}",
+ "peAgentPlanner": "Планировщик",
+ "peAgentExecutor": "Исполнитель",
+ "peAgentReplanning": "Перепланировщик"
+ },
+ "timeline": {
+ "blocked": "Заблокировано",
+ "params": "Параметры:",
+ "executionResult": "Результат выполнения:",
+ "executionId": "ID выполнения:",
+ "noResult": "Нет результата",
+ "running": "Выполняется...",
+ "backgroundRunning": "Выполняется в фоне",
+ "completed": "Завершено",
+ "execFailed": "Выполнение не удалось",
+ "resultMissing": "Запись результата отсутствует",
+ "toolSucceeded": "Успешно",
+ "toolFailed": "Ошибка"
+ },
+ "tasks": {
+ "title": "Управление задачами",
+ "stopTask": "Остановить задачу",
+ "interruptModalTitle": "Прервать текущий шаг",
+ "interruptReasonLabel": "Примечание к прерыванию",
+ "interruptModalHint": "Если инструмент выполняется: как «Остановить инструмент» в мониторе MCP — останавливается только этот вызов, выполнение продолжается; примечание можно добавить к результату инструмента (USER INTERRUPT NOTE). Если инструмент не выполняется (модель только думает или передаёт поток): «Прервать и продолжить» тоже работает — текущий вывод ставится на паузу, примечание добавляется в контекст, и выполнение возобновляется автоматически; на шкале прогресса появляется запись «Прерывание пользователя и продолжение». Используйте это вместо полной остановки, если нужно только скорректировать ход; «Остановить полностью» завершает всю задачу.",
+ "interruptReasonPlaceholder": "например, инструмент слишком медленный — пропустить и кратко изложить…",
+ "interruptReasonRequired": "Введите короткое примечание, чтобы модель могла продолжить с его учётом.",
+ "interruptSubmitting": "Отправка...",
+ "interruptConfirmContinue": "Прервать и продолжить",
+ "interruptHardStop": "Остановить полностью",
+ "interruptModalClose": "Закрыть",
+ "userInterruptTimelineTitle": "Примечание пользователя (продолжение)",
+ "collapseDetail": "Свернуть подробности",
+ "newTask": "Новая задача",
+ "autoRefresh": "Автообновление",
+ "historyHint": "Подсказка: доступна история завершённых задач. Включите «Показать историю», чтобы открыть её.",
+ "statusRunning": "Выполняется",
+ "statusCleanupUnconfirmed": "Удалённая отмена не подтверждена",
+ "statusCleaning": "Очистка",
+ "statusCleanupFailed": "Очистка не удалась (повтор)",
+ "statusCancelling": "Отмена",
+ "statusFailed": "Ошибка",
+ "statusTimeout": "Тайм-аут",
+ "statusCancelled": "Отменено",
+ "statusCompleted": "Завершено",
+ "historyBadge": "История",
+ "duration": "Длительность",
+ "completedAt": "Завершено",
+ "startedAt": "Начато",
+ "clickToCopy": "Нажмите, чтобы скопировать",
+ "unnamedTask": "Задача без названия",
+ "unknown": "Неизвестно",
+ "unknownTime": "Время неизвестно",
+ "clearHistoryConfirm": "Очистить всю историю задач?",
+ "cancelTaskFailed": "Не удалось отменить задачу",
+ "cancelFailed": "Не удалось отменить",
+ "taskInfoNotSynced": "Данные задачи ещё не синхронизированы, повторите позже.",
+ "loadActiveTasksFailed": "Не удалось загрузить активные задачи",
+ "cannotGetTaskStatus": "Не удалось получить статус задачи",
+ "copiedToast": "Скопировано",
+ "cancelling": "Отмена...",
+ "enterTaskPrompt": "Введите хотя бы одну задачу",
+ "noValidTask": "Нет допустимых задач",
+ "createBatchQueueFailed": "Не удалось создать очередь пакетных задач",
+ "noBatchQueues": "Сейчас нет очередей пакетных задач",
+ "recentCompletedTasks": "Недавно завершённые задачи (за 24 часа)",
+ "clearHistory": "Очистить историю",
+ "cancelTask": "Отменить задачу",
+ "viewConversation": "Открыть диалог",
+ "viewVulnerabilities": "Открыть уязвимости",
+ "viewVulnerabilitiesQueueTitle": "Открыть уязвимости: управление с фильтром по этой очереди",
+ "runSingleTask": "Запустить задачу",
+ "confirmRunSingleTask": "Запустить только эту задачу? После завершения очередь будет приостановлена и не перейдёт к остальным ожидающим.",
+ "runSingleTaskFailed": "Не удалось запустить задачу",
+ "runSingleTaskUnavailable": "Недоступно, пока выполняется очередь или задача",
+ "runSingleTaskUnavailableSelf": "Эта задача выполняется",
+ "runSingleTaskUnavailableQueue": "Очередь выполняется; приостановите её, чтобы запустить другую задачу отдельно",
+ "conversationIdLabel": "ID диалога",
+ "statusPending": "Ожидает",
+ "statusPaused": "Пауза",
+ "statusCronCycleIdle": "Раунд завершён · цикл по расписанию",
+ "statusCronRunning": "Выполняется · очередь cron",
+ "cronNextRunLine": "Следующий запуск: {{time}}",
+ "cronRoundDoneProgressHint": "Очередь cron: подзадачи завершены; следующий раунд начнётся по расписанию",
+ "cronRunningProgressHint": "Текущий раунд выполняется; следующий полный цикл — по cron и времени следующего запуска",
+ "cronPendingScheduled": "Запланировано cron · далее {{time}}",
+ "cronPendingProgressNote": "Запустится по расписанию или нажмите «Старт», чтобы выполнить раунд сейчас",
+ "cronRecurringCallout": "Очереди cron начинают новый раунд в каждое запланированное время. Отключите «Разрешить автозапуск cron», чтобы остановить цикл.",
+ "confirmCancelTasks": "Отменить выбранные задачи ({{n}})?",
+ "batchCancelResultPartial": "Пакетная отмена: успешно {{success}}, с ошибкой {{fail}}",
+ "batchCancelResultSuccess": "Отменено задач: {{n}}",
+ "taskCount": "Задач: {{count}}",
+ "queueIdLabel": "ID очереди",
+ "createdTimeLabel": "Создано",
+ "totalLabel": "Всего",
+ "pendingLabel": "Ожидает",
+ "runningLabel": "Выполняется",
+ "completedLabel": "Завершено",
+ "failedLabel": "Ошибка",
+ "cancelledLabel": "Отменено",
+ "loadingTasks": "Загрузка...",
+ "loadFailedRetry": "Ошибка загрузки",
+ "loadTaskListFailed": "Не удалось загрузить список задач",
+ "getQueueDetailFailed": "Не удалось загрузить сведения об очереди",
+ "startBatchQueueFailed": "Не удалось запустить пакетную очередь",
+ "pauseQueueFailed": "Не удалось приостановить очередь",
+ "pauseQueueConfirm": "Приостановить эту пакетную очередь? Текущая задача будет остановлена; остальные останутся в ожидании.",
+ "deleteQueueConfirm": "Удалить эту пакетную очередь? Это нельзя отменить.",
+ "deleteQueueFailed": "Не удалось удалить пакетную очередь",
+ "rerunQueueConfirm": "Перезапустить эту пакетную очередь? Все задачи будут сброшены в ожидание и выполнены заново.",
+ "rerunQueueFailed": "Не удалось перезапустить пакетную очередь",
+ "batchQueueTitle": "Очередь пакетных задач",
+ "batchQueueUntitled": "Очередь без названия",
+ "resumeExecute": "Продолжить",
+ "taskIncomplete": "Данные задачи неполные",
+ "cannotGetTaskMessageInput": "Не удалось получить текст задачи",
+ "taskMessageRequired": "Укажите текст задачи",
+ "saveTaskFailed": "Не удалось сохранить задачу",
+ "queueInfoMissing": "Сведения об очереди не найдены",
+ "addTaskFailed": "Не удалось добавить задачу",
+ "confirmDeleteTask": "Удалить эту задачу?\n\nЗадача: {{message}}\n\nЭто нельзя отменить.",
+ "deleteTaskFailed": "Не удалось удалить задачу",
+ "paginationShow": "Показано {{start}}–{{end}} из {{total}}",
+ "paginationPerPage": "На странице",
+ "paginationFirst": "Первая",
+ "paginationPrev": "Назад",
+ "paginationNext": "Далее",
+ "paginationLast": "Последняя",
+ "paginationPage": "Стр. {{current}} / {{total}}",
+ "deleteQueue": "Удалить очередь",
+ "retry": "Повторить",
+ "noMatchingTasks": "Нет подходящих задач",
+ "updateTaskFailed": "Не удалось обновить задачу",
+ "durationSeconds": "с",
+ "durationMinutes": "мин",
+ "durationHours": "ч"
+ },
+ "infoCollect": {
+ "enterFofaQuery": "Введите синтаксис запроса FOFA",
+ "querying": "Запрос...",
+ "queryFailed": "Запрос не выполнен",
+ "enterNaturalLanguage": "Введите описание на естественном языке",
+ "cancelParse": "Отменить разбор",
+ "clickToCancelParse": "Нажмите, чтобы отменить разбор ИИ",
+ "parseToFofa": "Разобрать естественный язык в запрос FOFA",
+ "parseResultEmpty": "Результат разбора пуст: добавьте или измените запрос FOFA во всплывающем окне",
+ "queryPlaceholder": "например, app=\"Apache\" && country=\"CN\"",
+ "selectAll": "Выбрать все или ничего",
+ "selectRow": "Выбрать строку",
+ "copyTarget": "Копировать цель",
+ "sendToChat": "Отправить в чат (можно править; Ctrl/Cmd+щелчок — отправить сразу)",
+ "noTargetToCopy": "Нет цели для копирования",
+ "targetCopied": "Цель скопирована",
+ "manualCopyHint": "Не удалось скопировать, скопируйте вручную: ",
+ "cannotInferTarget": "Не удалось определить цель сканирования по строке (в полях нужны host/ip/port/domain)",
+ "noSendMessage": "sendMessage() не найдена, обновите страницу и повторите",
+ "filledToInput": "Подставлено в поле чата, отредактируйте и отправьте",
+ "noExportResult": "Нет результатов для экспорта",
+ "xlsxNotLoaded": "Библиотека XLSX не загружена, обновите страницу и повторите",
+ "noResults": "Нет результатов",
+ "selectRowsFirst": "Сначала выберите строки для сканирования",
+ "noScanTarget": "По выбранному не удалось определить цели сканирования (в полях нужны host/ip/port/domain)",
+ "batchScanFailed": "Пакетное сканирование не удалось",
+ "batchQueueCreated": "Очередь пакетного сканирования создана",
+ "field": "Поле",
+ "cellValueLength": "Символов: {{count}}",
+ "parsePending": "Разбор ИИ...",
+ "parsePendingClickCancel": "Разбор ИИ... (нажмите кнопку, чтобы отменить)",
+ "parseSlow": "Разбор ИИ занимает больше времени, обработка продолжается…",
+ "parseDone": "Разбор ИИ завершён",
+ "parseCancelled": "Разбор ИИ отменён",
+ "parseFailed": "Разбор ИИ не удался: ",
+ "parseResultTitle": "Результат разбора ИИ",
+ "naturalLanguageLabel": "Естественный язык",
+ "fofaQueryEditable": "Запрос FOFA (можно править)",
+ "confirmBeforeQuery": "Перед запуском проверьте синтаксис и область запроса.",
+ "reminder": "Напоминание",
+ "explanation": "Пояснение",
+ "actions": "Действия",
+ "batchScanTitle": "Пакетное сканирование FOFA",
+ "queueCreatedSkipped": "Очередь создана (пропущено строк: {{n}}, нет цели)",
+ "createQueueFailed": "Не удалось создать пакетную очередь",
+ "loading": "Загрузка...",
+ "none": "Нет",
+ "truncated": "обрезано",
+ "resultsMeta": "Всего {{total}} · на странице {{count}} · page={{page}} · size={{size}}",
+ "providerReturnedFewer": "Поставщик вернул строк {{count}}/{{expected}}",
+ "parseModalCancel": "Отмена",
+ "parseModalApply": "Подставить в запрос",
+ "parseModalApplyRun": "Подставить и выполнить"
+ },
+ "assets": {
+ "overviewTitle": "Обзор активов",
+ "libraryTitle": "Библиотека активов",
+ "viewLibrary": "Открыть библиотеку активов",
+ "totalAssets": "Всего активов",
+ "totalAssetsHint": "Активы на постоянном мониторинге",
+ "ipCount": "IP-адреса",
+ "domainCount": "Домены",
+ "portCount": "Порты",
+ "recentCount": "Обнаружено за 7 дней",
+ "recentWindow": "7 дней",
+ "recentShare": "{{percent}}% всех текущих активов",
+ "protocolDistribution": "Распределение протоколов",
+ "protocolDistributionHint": "Состав экспозиции по выявленным службам",
+ "protocolKinds": "{{count}} протоколов",
+ "topProtocol": "Основной протокол",
+ "topProtocolShare": "Доля основного протокола",
+ "protocolComposition": "Состав протоколов",
+ "protocolRank": "Место",
+ "protocolName": "Протокол",
+ "assetRatio": "Доля активов",
+ "assetAmount": "Количество",
+ "ratio": "Доля",
+ "assetCountUnit": "{{count}} активов",
+ "postureChanges": "Изменения состояния",
+ "postureChangesHint": "Динамика активов и рисков за выбранный период",
+ "periodSelection": "Период",
+ "days7": "7 дней",
+ "days30": "30 дней",
+ "days90": "90 дней",
+ "assetTrend": "Динамика активов",
+ "assetTrendHint": "Новые обнаружения и неактивные активы",
+ "addedAssets": "Добавленные активы",
+ "inactiveAssets": "Неактивные активы",
+ "riskTrend": "Динамика выявления рисков",
+ "riskTrendHint": "Новые уязвимости и находки критического и высокого уровня",
+ "discoveredRisks": "Новые уязвимости",
+ "highRisks": "Критические и высокие",
+ "scanCoverage": "Охват сканированием",
+ "scanCoverageHint": "Активы без сканирования или с просроченной проверкой",
+ "overallCoverage": "Общий охват",
+ "scannedAssets": "Просканированные активы",
+ "scannedAssetsHint": "Просканированы хотя бы раз",
+ "recentlyScanned": "Охвачено за 30 дней",
+ "neverScanned": "Без сканирования",
+ "neverScannedHint": "В приоритете на сканирование",
+ "staleScans": "Не сканировались 30+ дней",
+ "staleScansHint": "Результаты сканирования могут устареть",
+ "coverageOfTotal": "{{percent}}% всех активов",
+ "coverageMeta": "{{scanned}} / {{total}} охвачено",
+ "addAsset": "+ Добавить актив",
+ "bulkImport": "Массовый импорт",
+ "bulkImportTitle": "Массовый импорт активов",
+ "bulkImportSubtitle": "Заполните шаблон, загрузите файл и проверьте результат перед импортом",
+ "downloadTemplate": "Скачать шаблон",
+ "downloadTemplateHint": "Рекомендуется XLSX; CSV удобен для выгрузки из других систем",
+ "downloadXlsx": "Скачать шаблон XLSX",
+ "downloadCsv": "Скачать шаблон CSV",
+ "uploadFile": "Загрузить файл",
+ "uploadFileHint": "Поддерживаются .xlsx и .csv, до 100 000 строк и 100 МБ",
+ "chooseOrDropFile": "Выберите файл или перетащите его сюда",
+ "fileNotSelected": "Файл не выбран",
+ "dataPreview": "Предпросмотр данных",
+ "rowNumber": "Строка",
+ "validationResult": "Проверка",
+ "validationPassed": "Допустимо",
+ "importValidRows": "Импортировать допустимые строки",
+ "importValidRowsCount": "Импортировать допустимые строки: {{count}}",
+ "importPreviewSummary": "{{total}} строк: {{valid}} допустимых, {{invalid}} требуют внимания",
+ "importBackendRowError": "Строка Excel {{row}} (отправленный допустимый актив №{{index}}): {{message}}",
+ "previewLimited": "Показаны первые 100 строк; будут обработаны все {{count}} строк",
+ "fileTypeInvalid": "Поддерживаются только файлы .xlsx и .csv",
+ "fileTooLarge": "Размер файла не должен превышать 100 МБ",
+ "spreadsheetUnavailable": "Не удалось загрузить компонент таблиц; обновите страницу и повторите",
+ "fileParseFailed": "Не удалось разобрать файл; убедитесь, что он корректен и не повреждён",
+ "fileHasNoData": "В файле нет строк для импорта",
+ "headerInvalid": "Столбцы шаблона не распознаны; используйте скачанный шаблон",
+ "tooManyRows": "Число строк не должно превышать {{count}}",
+ "importTargetRequired": "Не указан адрес цели",
+ "importStatusInvalid": "Статус должен быть active или inactive",
+ "importProjectNotFound": "Проект не существует или недоступен: {{project}}",
+ "importTagsInvalid": "Не больше 30 тегов, до 64 символов в каждом",
+ "importFieldTooLong": "Поле {{field}} слишком длинное",
+ "duplicateFileRow": "Дублирует строку {{row}}",
+ "bulkImportDone": "Импорт завершён: создано {{created}}, обновлено {{updated}}, пропущено {{skipped}}",
+ "templateGuideTitle": "Инструкция по массовому импорту активов",
+ "templateGuideRequired": "Обязательно: target либо хотя бы одно из host / ip / domain",
+ "templateGuideTarget": "Примеры target: https://example.com:443, example.com, 1.1.1.1:22",
+ "templateGuideProject": "project — имя или ID существующего проекта; пустое значение означает без проекта",
+ "templateGuideTags": "Теги через запятую, не больше 30",
+ "templateGuideStatus": "status принимает active / inactive, по умолчанию active",
+ "templateGuideLimit": "Вводите данные на листе Assets; до 100 000 строк",
+ "instructionsSheet": "Инструкция",
+ "addAssetTitle": "Добавить актив",
+ "editAssetTitle": "Изменить актив",
+ "editorSubtitle": "Укажите одну цель; остальные сведения разберутся автоматически или их можно добавить позже",
+ "assetAddress": "Адрес актива",
+ "assetAddressHint": "Принимается любая непустая цель; распознанные URL, домены, IP и порты разбираются автоматически",
+ "targetWhitespace": "Адрес актива не может содержать пробелы",
+ "targetInvalid": "Введите корректный URL, домен или IP-адрес",
+ "hostInvalid": "Полный URL некорректен или содержит учётные данные",
+ "ipInvalid": "Некорректный IP-адрес",
+ "domainInvalid": "Некорректный домен",
+ "protocolInvalid": "Некорректный протокол",
+ "portInvalid": "Порт должен быть от 1 до 65535",
+ "connectionInfo": "Подключение",
+ "connectionInfoHint": "Разбирается из адреса актива автоматически; при необходимости измените",
+ "fingerprintInfo": "Идентификация",
+ "fingerprintInfoHint": "Добавьте отпечатки страницы и службы, чтобы упростить поиск",
+ "locationAndStatus": "Местоположение и статус",
+ "locationAndStatusHint": "Укажите расположение актива и текущую доступность",
+ "hostUrl": "Полный URL / хост",
+ "pageTitle": "Заголовок страницы",
+ "serverProduct": "Служба / продукт",
+ "tagsPlaceholder": "Enter или запятая, чтобы добавить",
+ "tagsHint": "Классификация по среде, бизнесу или владельцу",
+ "removeTag": "Удалить тег {{tag}}",
+ "addAssetAction": "Добавить актив",
+ "discardChanges": "Отменить несохранённые изменения?",
+ "createdSuccessfully": "Актив добавлен",
+ "updatedSuccessfully": "Актив обновлён",
+ "duplicateMerged": "Актив уже существовал; сведения безопасно объединены",
+ "assetSkipped": "Актив не сохранён; возможно, он существует без права на изменение",
+ "searchPlaceholder": "Поиск по хосту, IP, домену, заголовку, службе или тегу",
+ "allStatuses": "Все статусы",
+ "allProjects": "Все проекты",
+ "unboundProject": "Не привязан",
+ "project": "Проект",
+ "archived": "В архиве",
+ "statusActive": "Активен",
+ "statusInactive": "Неактивен",
+ "target": "Цель",
+ "service": "Служба",
+ "title": "Заголовок / отпечаток",
+ "location": "Местоположение",
+ "country": "Страна / регион",
+ "province": "Регион / штат",
+ "city": "Город",
+ "source": "Источник",
+ "lastSeen": "Последняя активность",
+ "status": "Статус",
+ "domain": "Домен",
+ "port": "Порт",
+ "protocol": "Протокол",
+ "server": "Отпечаток сервера",
+ "tags": "Теги (через запятую)",
+ "tagsLabel": "Теги",
+ "detailTitle": "Сведения об активе",
+ "sourceQuery": "Исходный запрос",
+ "firstSeen": "Первое обнаружение",
+ "totalMeta": "{{count}} активов",
+ "targetRequired": "Введите адрес актива",
+ "loadFailed": "Не удалось загрузить активы",
+ "saveFailed": "Не удалось сохранить актив",
+ "saved": "Актив сохранён",
+ "deleteConfirm": "Удалить этот актив?",
+ "deleteFailed": "Не удалось удалить актив",
+ "importSelected": "Импортировать выбранное",
+ "importOne": "Добавить в библиотеку активов",
+ "selectFirst": "Сначала выберите результаты для импорта",
+ "noValidImportTarget": "Ни у одного выбранного результата нет допустимой цели актива",
+ "importFailed": "Не удалось импортировать активы",
+ "importDone": "Создано {{created}}, обновлено {{updated}}",
+ "importDoneWithInvalid": "Создано {{created}}, обновлено {{updated}}, пропущено результатов без допустимых целей: {{invalid}}",
+ "selectPage": "Выбрать эту страницу",
+ "selectAsset": "Выбрать актив",
+ "selectedCount": "Выбрано: {{count}}",
+ "clearSelection": "Снять выбор",
+ "selectAllResults": "Выбрать все {{count}}",
+ "allResultsSelected": "Выбраны все подходящие активы: {{count}}",
+ "bindProject": "Привязать проект",
+ "bindProjectTitle": "Привязать проект",
+ "chooseProject": "Выберите проект",
+ "selectProjectRequired": "Выберите проект для привязки",
+ "bindProjectCount": "Обновить активы: {{count}}",
+ "bindProjectHint": "Все выбранные активы будут привязаны к этому проекту.",
+ "confirmBinding": "Подтвердить",
+ "bindProjectDone": "Привязано активов: {{count}}",
+ "bindProjectFailed": "Не удалось привязать проект",
+ "sendToChat": "Отправить в чат",
+ "sendToChatShort": "Скан",
+ "createScanTask": "Создать задачи сканирования",
+ "scanTitle": "Сканировать активы",
+ "scanAssetCount": "{{count}} активов",
+ "userPrompt": "Запрос пользователя",
+ "promptHint": "Подстановки: {{asset_id}}, {{target}}, {{host}}, {{ip}}, {{domain}}, {{port}}. В режиме задач создаётся одна задача на актив.",
+ "executeNow": "Запустить сразу после создания",
+ "confirmSend": "Отправить",
+ "confirmCreate": "Создать задачи",
+ "defaultScanPrompt": "Выполните авторизованное сканирование безопасности {{target}} (ID актива: {{asset_id}}). Сохраните подтверждённые находки через record_vulnerability, затем вызовите complete_asset_scan(id={{asset_id}}), чтобы обновить время последнего сканирования и число связанных уязвимостей.",
+ "selectAssetsFirst": "Сначала выберите активы",
+ "promptRequired": "Введите запрос пользователя",
+ "scanSubmitFailed": "Не удалось отправить сканирование",
+ "scanTaskLinkFailed": "Задачи созданы, но привязка к активам не удалась",
+ "scanConversationTitle": "Сканирование активов: {{targets}}",
+ "scanQueueTitle": "Пакетное сканирование активов",
+ "lastScan": "Последнее сканирование",
+ "relatedVulnerabilities": "Связанные находки",
+ "riskLevel": "Риск",
+ "riskCritical": "Критический",
+ "riskHigh": "Высокий",
+ "riskMedium": "Средний",
+ "riskLow": "Низкий",
+ "riskInfo": "Инфо",
+ "riskNormal": "Норма",
+ "riskUnassessed": "Не оценён",
+ "advancedFilters": "Расширенные фильтры",
+ "resetFilters": "Сбросить",
+ "allRisks": "Все уровни риска",
+ "minVulnerabilityCount": "Минимум находок",
+ "scanState": "Статус сканирования",
+ "allScanStates": "Все",
+ "neverScannedFilter": "Без сканирования",
+ "overdue30": "Не сканировались 30 дней",
+ "overdue60": "Не сканировались 60 дней",
+ "overdue90": "Не сканировались 90 дней",
+ "scannedFilter": "Просканированы",
+ "responsiblePerson": "Владелец",
+ "department": "Подразделение",
+ "ownership": "Владение",
+ "unassigned": "Не назначен",
+ "businessSystem": "Бизнес-система",
+ "environment": "Среда",
+ "criticality": "Критичность",
+ "allEnvironments": "Все среды",
+ "environmentProduction": "Продакшен",
+ "environmentStaging": "Стейджинг",
+ "environmentTesting": "Тестирование",
+ "environmentDevelopment": "Разработка",
+ "environmentOther": "Другое",
+ "allCriticalities": "Все уровни критичности",
+ "criticalityCritical": "Критически важная",
+ "criticalityHigh": "Высокая",
+ "criticalityMedium": "Средняя",
+ "criticalityLow": "Низкая",
+ "firstSeenStart": "Первое обнаружение с",
+ "firstSeenEnd": "Первое обнаружение по",
+ "lastSeenStart": "Последняя активность с",
+ "lastSeenEnd": "Последняя активность по",
+ "sort": "Сортировка",
+ "sortLastSeenDesc": "Последняя активность (сначала новые)",
+ "sortLastScanAsc": "Наиболее просроченное сканирование",
+ "sortLastScanDesc": "Недавно сканированные",
+ "sortRiskDesc": "Риск (по убыванию)",
+ "sortVulnerabilityDesc": "Находки (сначала больше)",
+ "sortFirstSeenDesc": "Первое обнаружение (сначала новые)",
+ "sortHostAsc": "Имя цели",
+ "sortPortAsc": "Порт",
+ "savedViews": "Сохранённые представления",
+ "saveCurrentView": "Сохранить текущее представление",
+ "deleteView": "Удалить представление",
+ "bulkEdit": "Массовое изменение",
+ "moreActions": "Ещё",
+ "export": "Экспорт",
+ "exportCsv": "Экспорт CSV",
+ "exportXlsx": "Экспорт XLSX",
+ "mergeDuplicates": "Объединить дубликаты",
+ "mergeRequiresMultiple": "Выберите не менее двух дублирующихся активов",
+ "batchDelete": "Массовое удаление…",
+ "responsibilityBusiness": "Владение и бизнес-контекст",
+ "responsibilityBusinessHint": "Владение и бизнес-приоритет определяют операции и ранжирование рисков"
+ },
+ "vulnerability": {
+ "title": "Управление уязвимостями",
+ "addVuln": "Добавить уязвимость",
+ "editVuln": "Изменить уязвимость",
+ "loadFailed": "Не удалось загрузить уязвимости",
+ "deleteConfirm": "Удалить эту уязвимость?"
+ },
+ "webshell": {
+ "title": "Управление WebShell",
+ "addConnection": "Добавить подключение",
+ "connections": "Подключения",
+ "noConnections": "Нет подключений. Нажмите «Добавить подключение».",
+ "selectOrAdd": "Выберите подключение в списке или добавьте новое подключение WebShell.",
+ "url": "URL оболочки",
+ "urlPlaceholder": "http(s)://target.com/shell.php",
+ "password": "Пароль / ключ",
+ "passwordPlaceholder": "например, пароль подключения IceSword/AntSword",
+ "method": "Метод запроса",
+ "methodPost": "POST",
+ "methodGet": "GET",
+ "type": "Тип оболочки",
+ "typePhp": "PHP",
+ "typeAsp": "ASP",
+ "typeAspx": "ASPX",
+ "typeJsp": "JSP",
+ "typeCustom": "Свой",
+ "cmdParam": "Имя параметра команды",
+ "cmdParamPlaceholder": "Пусто — cmd; например xxx для xxx=command",
+ "encoding": "Кодировка ответа",
+ "encodingAuto": "Авто",
+ "encodingUtf8": "UTF-8",
+ "encodingGbk": "GBK (упрощённый китайский, Windows)",
+ "encodingGb18030": "GB18030",
+ "encodingHint": "Если на цели с упрощённым китайским Windows вывод искажён, выберите GBK или GB18030.",
+ "os": "ОС цели",
+ "osAuto": "Авто (по типу оболочки)",
+ "osLinux": "Linux / Unix",
+ "osWindows": "Windows",
+ "osHint": "Задаёт команды файлового менеджера и загрузки: Linux или Windows. Для PHP/JSP на Windows выберите Windows.",
+ "remark": "Примечание",
+ "remarkPlaceholder": "Понятное имя подключения",
+ "deleteConfirm": "Удалить это подключение?",
+ "editConnection": "Изменить",
+ "editConnectionTitle": "Изменить подключение",
+ "tabTerminal": "Виртуальный терминал",
+ "tabFileManager": "Файловый менеджер",
+ "tabAiAssistant": "ИИ-помощник",
+ "tabDbManager": "База данных",
+ "tabMemo": "Заметки",
+ "dbType": "Тип базы данных",
+ "dbHost": "Хост",
+ "dbPort": "Порт",
+ "dbUsername": "Пользователь",
+ "dbPassword": "Пароль",
+ "dbName": "Имя базы",
+ "dbSqlitePath": "Путь к файлу SQLite",
+ "dbSqlPlaceholder": "Введите SQL, например SELECT version();",
+ "dbRunSql": "Выполнить SQL",
+ "dbTest": "Проверить подключение",
+ "dbOutput": "Вывод",
+ "dbNoConn": "Сначала выберите подключение WebShell",
+ "dbSqlRequired": "Введите SQL",
+ "dbRunning": "Команда базы выполняется, подождите",
+ "dbCliHint": "Если команда не найдена, сначала установите на цели mysql, psql, sqlite3 или sqlcmd",
+ "dbExecFailed": "Не удалось выполнить команду базы",
+ "dbSchema": "Схема базы",
+ "dbLoadSchema": "Загрузить схему",
+ "dbNoSchema": "Схемы пока нет. Нажмите «Загрузить схему»",
+ "dbSelectTableHint": "Нажмите таблицу, чтобы раскрыть столбцы и собрать SQL",
+ "dbNoColumns": "Нет данных о столбцах",
+ "dbResultTable": "Таблица результатов",
+ "dbClearSql": "Очистить SQL",
+ "dbTemplateSql": "Шаблон SQL",
+ "dbRows": "строк",
+ "dbColumns": "столбцов",
+ "dbSchemaFailed": "Не удалось загрузить схему",
+ "dbSchemaLoaded": "Схема загружена",
+ "dbAddProfile": "Добавить подключение",
+ "dbExecSuccess": "SQL выполнен",
+ "dbNoOutput": "Выполнено (нет вывода)",
+ "dbRenameProfile": "Переименовать",
+ "dbDeleteProfile": "Удалить подключение",
+ "dbDeleteProfileConfirm": "Удалить этот профиль подключения к базе?",
+ "dbProfileNamePrompt": "Введите имя профиля",
+ "dbProfiles": "Подключения к базам",
+ "aiSystemReadyMessage": "Система готова. Опишите, что нужно проверить, — система выполнит соответствующие тесты безопасности.",
+ "aiNewConversation": "Новый диалог",
+ "aiPreviousConversation": "Предыдущий диалог",
+ "aiDeleteConversation": "Удалить диалог",
+ "aiDeleteConversationConfirm": "Удалить этот диалог?",
+ "aiPlaceholder": "например, список файлов в текущем каталоге",
+ "aiSend": "Отправить",
+ "aiMemo": "Заметки",
+ "aiMemoPlaceholder": "Ключевые команды, идеи проверок и шаги воспроизведения...",
+ "aiMemoClear": "Очистить",
+ "aiMemoSaving": "Сохранение...",
+ "aiMemoSaved": "Сохранено локально",
+ "quickCommands": "Быстрые команды",
+ "downloadFile": "Скачать",
+ "terminalWelcome": "Виртуальный терминал WebShell — введите команду и нажмите Enter (Ctrl+L очищает)",
+ "filePath": "Текущий путь",
+ "listDir": "Список каталога",
+ "readFile": "Читать",
+ "editFile": "Изменить",
+ "deleteFile": "Удалить",
+ "saveFile": "Сохранить",
+ "cancelEdit": "Отмена",
+ "parentDir": "Родительский каталог",
+ "execError": "Ошибка выполнения",
+ "testConnectivity": "Проверить связь",
+ "testSuccess": "Связь есть, оболочка доступна",
+ "testFailed": "Проверка связи не удалась",
+ "testNoExpectedOutput": "Оболочка ответила, но ожидаемый вывод не найден. Проверьте пароль и имя параметра команды.",
+ "clearScreen": "Очистить",
+ "copyTerminalLog": "Копировать журнал",
+ "terminalIdle": "Ожидание",
+ "terminalRunning": "Выполняется",
+ "terminalCopyOk": "Журнал скопирован",
+ "terminalCopyFail": "Не удалось скопировать",
+ "terminalNewWindow": "Новый терминал",
+ "terminalWindowPrefix": "Терминал",
+ "running": "Выполняется…",
+ "waitFinish": "Дождитесь завершения текущей команды",
+ "newDir": "Новый каталог",
+ "rename": "Переименовать",
+ "upload": "Загрузить",
+ "newFile": "Новый файл",
+ "filterPlaceholder": "Фильтр по имени",
+ "batchDelete": "Удалить выбранные",
+ "batchDownload": "Скачать выбранные",
+ "refresh": "Обновить",
+ "selectAll": "Выбрать все",
+ "searchPlaceholder": "Поиск подключений...",
+ "noMatchConnections": "Нет подходящих подключений",
+ "breadcrumbHome": "Корень",
+ "dirTree": "Дерево каталогов",
+ "back": "Назад",
+ "moreActions": "Ещё",
+ "batchProbe": "Пакетная проверка",
+ "probeRunning": "Проверка",
+ "probeOnline": "В сети",
+ "probeOffline": "Не в сети",
+ "probeNoConnections": "Нет подключений для проверки",
+ "colModifiedAt": "Изменён",
+ "colPerms": "Права",
+ "colOwner": "Владелец",
+ "colGroup": "Группа",
+ "colType": "Тип",
+ "dbProfileName": "Имя подключения"
+ },
+ "mcp": {
+ "monitorTitle": "Монитор состояния MCP",
+ "execStats": "Статистика выполнения",
+ "latestExecutions": "Последние выполнения",
+ "toolSearch": "Поиск инструментов",
+ "toolSearchPlaceholder": "Имя инструмента...",
+ "statusFilter": "Фильтр статуса",
+ "filterAll": "Все",
+ "filterEnabled": "Включено",
+ "filterDisabled": "Отключено",
+ "selectedCount": "Выбрано: {{count}}",
+ "selectAll": "Выбрать все",
+ "deselectAll": "Снять выбор",
+ "deleteSelected": "Удалить выбранные",
+ "deleteExecConfirm": "Удалить эту запись выполнения?",
+ "batchDeleteFailed": "Пакетное удаление не удалось",
+ "managementTitle": "Управление MCP",
+ "addExternal": "Добавить внешний MCP",
+ "toolConfig": "Конфигурация инструментов MCP",
+ "saveToolConfig": "Сохранить конфигурацию",
+ "alwaysVisibleLabel": "Закреплено",
+ "alwaysVisibleHint": "Всегда показывать в результатах поиска инструментов",
+ "alwaysVisibleBuiltinLabel": "Встроенное по умолчанию",
+ "alwaysVisibleBuiltinHint": "Встроенный инструмент сервера закреплён по умолчанию, отключить его нельзя",
+ "externalConfig": "Конфигурация внешнего MCP",
+ "loadingTools": "Загрузка инструментов...",
+ "loadToolsTimeout": "Тайм-аут загрузки инструментов. Внешний MCP может отвечать медленно. Нажмите «Обновить», чтобы повторить, или проверьте подключение.",
+ "loadToolsFailed": "Не удалось загрузить инструменты",
+ "noTools": "Нет инструментов",
+ "externalBadge": "Внешний",
+ "externalFrom": "Внешний ({{name}})",
+ "externalToolFrom": "Внешний MCP — источник: {{name}}",
+ "clickToViewTools": "Нажмите, чтобы открыть инструменты {{name}}",
+ "filterBySource": "Источник: {{name}}",
+ "clearSourceFilter": "Сбросить фильтр источника",
+ "noDescription": "Нет описания",
+ "paginationInfo": "{{start}}–{{end}} из {{total}} инструментов",
+ "perPage": "На странице:",
+ "firstPage": "Первая",
+ "prevPage": "Назад",
+ "nextPage": "Далее",
+ "lastPage": "Последняя",
+ "pageInfo": "Стр. {{page}} из {{total}}",
+ "currentPageEnabled": "Включено на этой странице",
+ "totalEnabled": "Всего включено",
+ "toolsConfigSaved": "Конфигурация инструментов сохранена",
+ "saveToolsConfigFailed": "Не удалось сохранить конфигурацию инструментов",
+ "getConfigFailed": "Не удалось получить конфигурацию",
+ "noExternalMCP": "Внешний MCP не настроен",
+ "clickToAddExternal": "Нажмите «Добавить внешний MCP», чтобы настроить",
+ "connected": "Подключено",
+ "connecting": "Подключение...",
+ "connectionFailed": "Ошибка подключения",
+ "disabled": "Отключено",
+ "disconnected": "Нет связи",
+ "stopConnection": "Остановить подключение",
+ "startConnection": "Запустить подключение",
+ "stop": "Стоп",
+ "start": "Старт",
+ "editConfig": "Изменить конфигурацию",
+ "deleteConfig": "Удалить конфигурацию",
+ "transportMode": "Транспорт",
+ "toolCount": "Число инструментов",
+ "description": "Описание",
+ "timeout": "Тайм-аут",
+ "command": "Команда",
+ "addExternalMCP": "Добавить внешний MCP",
+ "editExternalMCP": "Изменить внешний MCP",
+ "jsonEmpty": "JSON не может быть пустым",
+ "jsonError": "Ошибка формата JSON",
+ "configMustBeObject": "Ошибка конфигурации: нужен объект JSON, где ключ — имя",
+ "configNeedOne": "Ошибка конфигурации: нужен хотя бы один элемент",
+ "configNameEmpty": "Ошибка конфигурации: имя не может быть пустым",
+ "configMustBeObj": "Ошибка конфигурации: «{{name}}» должен быть объектом",
+ "configNeedCommand": "Ошибка конфигурации: «{{name}}» требует command (stdio) или url (http/sse)",
+ "configStdioNeedCommand": "Ошибка конфигурации: «{{name}}» в режиме stdio требует command",
+ "configHttpNeedUrl": "Ошибка конфигурации: «{{name}}» в режиме http требует url",
+ "configSseNeedUrl": "Ошибка конфигурации: «{{name}}» в режиме sse требует url",
+ "configEditMustContainName": "Ошибка конфигурации: в режиме правки JSON должен содержать имя «{{name}}»",
+ "saveSuccess": "Сохранено",
+ "deleteSuccess": "Удалено",
+ "deleteExternalConfirm": "Удалить внешний MCP «{{name}}»?",
+ "operationFailed": "Операция не выполнена",
+ "connectionFailedCheck": "Ошибка подключения. Проверьте конфигурацию и сеть.",
+ "connectionTimeout": "Тайм-аут подключения. Проверьте конфигурацию и сеть.",
+ "totalCount": "Всего",
+ "enabledCount": "Включено",
+ "disabledCount": "Отключено",
+ "connectedCount": "Подключено",
+ "toolsCountValue": "🔧 Инструментов: {{count}}",
+ "connectionErrorLabel": "Ошибка подключения:",
+ "secondsUnit": "с",
+ "urlLabel": "URL",
+ "loadExternalMCPFailed": "Ошибка загрузки"
+ },
+ "settings": {
+ "title": "Системные настройки",
+ "nav": {
+ "basic": "Основные",
+ "hitl": "Человек в контуре",
+ "knowledge": "База знаний",
+ "c2": "C2",
+ "robots": "Боты",
+ "terminal": "Терминал",
+ "security": "Безопасность",
+ "rbac": "Права платформы",
+ "audit": "Журнал аудита",
+ "storage": "Очистка хранилища",
+ "infocollect": "Управление активами"
+ },
+ "infocollect": {
+ "title": "Управление активами"
+ },
+ "hitl": {
+ "title": "Человек в контуре",
+ "approvalTitle": "Одобрение",
+ "defaultReviewer": "Проверяющий по умолчанию",
+ "defaultReviewerHint": "Используется, если диалог не выбран, и для новых диалогов. Панель чата может задать другое значение.",
+ "auditModelTitle": "Модель агента аудита",
+ "auditBackend": "Движок одобрения",
+ "auditBackendHint": "Одно из двух: модель чата, совместимая с OpenAI, возвращает JSON по запросу, либо TypeSafe Jev принимает структурированное решение разрешить или запретить (без правки аргументов).",
+ "auditBackendOpenAI": "Модель, совместимая с OpenAI",
+ "auditBackendTypeSafe": "TypeSafe Jev",
+ "auditModelReuseMain": "Как у основной модели",
+ "auditModelBaseUrlPlaceholder": "Пусто — основной Base URL",
+ "auditModelApiKeyPlaceholder": "Пусто — основной API Key",
+ "auditModelName": "Модель одобрения",
+ "auditModelNamePlaceholder": "Пусто — основная модель; лучше компактная",
+ "auditModelHint": "Только для одобрений агента аудита. Ручное одобрение модель не вызывает.",
+ "auditModelTypeSafeHint": "Jev использует TypeSafe System One, а не протокол OpenAI. API-ключ обязателен и не берётся из основной модели. Пустое имя модели — jev-latest. В режиме проверки и правки Jev только разрешает или запрещает и не переписывает аргументы.",
+ "auditModelTypeSafeBaseUrlPlaceholder": "Пусто — https://api.typesafe.ai",
+ "auditModelTypeSafeApiKeyPlaceholder": "API-ключ TypeSafe (обязателен, не из основной модели)",
+ "auditModelTypeSafeNamePlaceholder": "Пусто — jev-latest",
+ "testAuditModel": "Проверить модель аудита",
+ "testTypeSafeFillRequired": "Сначала укажите API-ключ TypeSafe",
+ "retentionDays": "Срок хранения закрытых записей аудита, дни",
+ "retentionDaysHint": "0 — хранить бессрочно; пусто — 90 дней.",
+ "toolWhitelist": "Белый список инструментов без одобрения",
+ "toolWhitelistPlaceholder": "read_file, list_dir, glob",
+ "toolWhitelistHint": "По одному в строке или через запятую. Инструменты из списка не требуют одобрения.",
+ "auditAgentTitle": "Стратегия агента аудита",
+ "auditPromptApproval": "Запрос для режима одобрения",
+ "auditPromptHint": "Пусто — стратегия сервера по умолчанию.",
+ "auditPromptTypeSafeHint": "Сейчас выбран TypeSafe Jev: свой текст стратегии оценивается как структурированные вопросы. Встроенные правила для опасных действий остаются жёстким порогом. Jev не переписывает аргументы.",
+ "auditPromptReviewEdit": "Запрос для режима проверки и правки",
+ "auditPromptReviewEditHint": "В режиме проверки и правки можно одобрить с суженными editedArguments."
+ },
+ "knowledge": {
+ "title": "База знаний"
+ },
+ "c2": {
+ "title": "C2",
+ "sectionTitle": "Встроенный C2",
+ "enableLabel": "Включить встроенный C2 (слушатели, сессии, полезные нагрузки, инструменты MCP)",
+ "enableHint": "Если выключено, слушатели не запускаются и инструменты MCP для C2 не регистрируются, панель C2 скрыта. Удобно для локального чата и базы знаний. Нажмите «Применить», чтобы сохранить."
+ },
+ "robots": {
+ "title": "Настройки ботов",
+ "description": "Настройте ботов WeChat (iLink), WeCom, DingTalk и Lark, чтобы общаться с CyberStrikeAI с телефона, не открывая веб-интерфейс.",
+ "managerTitle": "Управление ботами",
+ "managerDesc": "Сначала выберите тип бота, затем заполните только поля этой платформы.",
+ "newBot": "Новый бот",
+ "configure": "Настроить",
+ "statusNotConfigured": "Не настроен",
+ "statusConfigured": "Настроен",
+ "statusEnabled": "Включён",
+ "emptyTitle": "Выберите бота для настройки",
+ "emptyDesc": "Выберите платформу выше или создайте бота кнопкой «Новый бот».",
+ "createTitle": "Новый бот",
+ "createDesc": "Выберите тип бота для его настройки. В этой версии одна встроенная конфигурация на платформу.",
+ "wechat": {
+ "title": "WeChat / iLink",
+ "subtitle": "Привяжите личный WeChat по QR-коду и общайтесь с CyberStrikeAI с телефона",
+ "statusIdle": "Не привязан",
+ "statusBound": "Подключён",
+ "statusScanning": "Привязка…",
+ "step1": "Создать QR",
+ "step2": "Сканировать в WeChat",
+ "step3": "Подтвердить",
+ "enabled": "Включить бота WeChat",
+ "bindButton": "Создать QR-код и привязать",
+ "bindHint": "Отсканируйте в WeChat для подтверждения. Настройки сохраняются автоматически.",
+ "qrLoading": "Создание QR-кода…",
+ "verifyCodeLabel": "Код на телефоне (только если запросит WeChat)",
+ "rebindButton": "Привязать снова",
+ "boundBotId": "ID привязанного бота: ",
+ "verifyCodeSubmit": "Отправить",
+ "advanced": "Дополнительно",
+ "baseUrl": "Базовый URL API",
+ "botType": "Тип бота",
+ "botAgent": "Агент бота",
+ "ilinkBotId": "ID бота iLink (после привязки)",
+ "boundSuccess": "Привязка выполнена. Бот WeChat включён.",
+ "alreadyBound": "Этот аккаунт WeChat уже привязан.",
+ "openLink": "QR не виден? Откройте ссылку в WeChat на телефоне"
+ },
+ "wecom": {
+ "title": "WeCom",
+ "subtitle": "HTTP-callback для ботов внутренних приложений",
+ "enabled": "Включить бота WeCom",
+ "token": "Token",
+ "tokenPlaceholder": "Token",
+ "encodingAesKey": "EncodingAESKey",
+ "encodingAesKeyPlaceholder": "EncodingAESKey (пусто — без шифрования)",
+ "corpId": "CorpID",
+ "corpIdPlaceholder": "Corp ID",
+ "secret": "Secret",
+ "secretPlaceholder": "App Secret",
+ "agentId": "AgentID",
+ "agentIdPlaceholder": "App AgentId"
+ },
+ "dingtalk": {
+ "title": "DingTalk",
+ "subtitle": "Бот внутреннего приложения, подключение Stream",
+ "enabled": "Включить бота DingTalk",
+ "clientIdLabel": "Client ID (AppKey)",
+ "clientIdPlaceholder": "App Key DingTalk",
+ "clientSecretLabel": "Client Secret",
+ "clientSecretPlaceholder": "App Secret DingTalk",
+ "streamHint": "Включите возможности бота и настройте потоковый доступ на открытой платформе."
+ },
+ "lark": {
+ "title": "Lark",
+ "subtitle": "Корпоративный бот для личных чатов и упоминаний в группах",
+ "enabled": "Включить бота Lark",
+ "appIdLabel": "App ID",
+ "appIdPlaceholder": "App ID Lark/Feishu",
+ "appSecretLabel": "App Secret",
+ "appSecretPlaceholder": "App Secret Lark/Feishu",
+ "verifyTokenLabel": "Verify Token (необязательно)",
+ "verifyTokenPlaceholder": "Verification Token подписки на события"
+ },
+ "telegram": {
+ "title": "Telegram",
+ "subtitle": "Long polling Bot API; личный чат и @ в группе",
+ "enabled": "Включить бота Telegram",
+ "botToken": "Токен бота",
+ "botTokenHint": "Приём через long poll getUpdates; публичный URL callback не нужен",
+ "botUsername": "Имя пользователя бота (необязательно)",
+ "allowGroup": "Групповой чат (отвечать только на @)"
+ },
+ "slack": {
+ "title": "Slack",
+ "subtitle": "Socket Mode; публичный URL callback не нужен",
+ "enabled": "Включить бота Slack",
+ "botToken": "Токен бота (xoxb-)",
+ "appToken": "Токен приложения (xapp-)",
+ "appTokenHint": "Для Socket Mode нужна область connections:write"
+ },
+ "discord": {
+ "title": "Discord",
+ "subtitle": "Gateway WebSocket; личные сообщения и @ на сервере",
+ "enabled": "Включить бота Discord",
+ "botToken": "Токен бота",
+ "botTokenHint": "Создайте бота в Developer Portal и включите Message Content Intent",
+ "allowGuild": "Каналы сервера (отвечать только на @)"
+ },
+ "qq": {
+ "title": "Бот QQ",
+ "subtitle": "WebSocket открытой платформы QQ; C2C и @ в группе",
+ "enabled": "Включить бота QQ",
+ "appId": "App ID",
+ "clientSecret": "Client Secret",
+ "secretHint": "С открытой платформы ботов QQ. Перед публикацией используйте песочницу",
+ "sandbox": "Песочница"
+ }
+ },
+ "apply": {
+ "button": "Применить конфигурацию",
+ "loadFailed": "Не удалось загрузить конфигурацию",
+ "fillRequired": "Заполните все обязательные поля (отмечены *)",
+ "applyFailed": "Не удалось применить конфигурацию",
+ "applySuccess": "Конфигурация применена"
+ },
+ "security": {
+ "changePassword": "Сменить пароль",
+ "fillPasswordHint": "Укажите текущий и новый пароль. Новый пароль — не короче 8 символов и должен совпасть при повторе.",
+ "changePasswordFailed": "Не удалось сменить пароль",
+ "passwordUpdated": "Пароль обновлён. Войдите снова с новым паролем."
+ }
+ },
+ "auth": {
+ "sessionExpired": "Сессия истекла, войдите снова",
+ "unauthorized": "Нет авторизации",
+ "forbidden": "Недостаточно прав",
+ "requestFailed": "Запрос не выполнен",
+ "enterPassword": "Введите пароль",
+ "loginFailedCheck": "Вход не выполнен, проверьте имя пользователя или пароль",
+ "loginFailedRetry": "Вход не выполнен, повторите позже",
+ "loggedOut": "Вы вышли"
+ },
+ "knowledge": {
+ "title": "Управление базой знаний",
+ "retrievalLogs": "История извлечения",
+ "totalItems": "Всего элементов",
+ "categories": "Категории",
+ "addKnowledge": "Добавить знание",
+ "buildIndex": "Построить индекс",
+ "buildIndexConfirm": "Построить векторы для элементов базы знаний, которые ещё не проиндексированы?",
+ "rebuildIndexFull": "Полная пересборка",
+ "rebuildIndexFullConfirm": "Переиндексировать все элементы базы знаний? Это может занять время.",
+ "deleteItemConfirm": "Удалить этот элемент базы знаний?",
+ "notEnabledTitle": "База знаний не включена",
+ "notEnabledHint": "Включите извлечение из базы знаний в системных настройках.",
+ "goToSettings": "К настройкам"
+ },
+ "roles": {
+ "title": "Управление ролями",
+ "createRole": "Создать роль",
+ "searchPlaceholder": "Поиск ролей...",
+ "deleteConfirm": "Удалить эту роль?",
+ "loadFailed": "Не удалось загрузить роли",
+ "noDescription": "Нет описания",
+ "defaultRoleDescription": "Роль по умолчанию, без дополнительного пользовательского промпта, использует MCP по умолчанию",
+ "noMatchingRoles": "Нет подходящих ролей",
+ "noRoles": "Нет ролей",
+ "enabled": "Включено",
+ "disabled": "Отключено",
+ "noDescriptionShort": "Нет описания"
+ },
+ "skills": {
+ "title": "Управление навыками",
+ "monitorTitle": "Монитор навыков",
+ "createSkill": "Создать навык",
+ "callStats": "Статистика вызовов",
+ "addSkill": "Добавить навык",
+ "editSkill": "Изменить навык",
+ "loadListFailed": "Не удалось загрузить список навыков",
+ "noSkills": "Нет навыков. Нажмите «Создать навык», чтобы добавить первый.",
+ "noMatch": "Нет подходящих навыков",
+ "searchFailed": "Поиск не удался",
+ "refreshed": "Обновлено",
+ "loadDetailFailed": "Не удалось загрузить сведения о навыке",
+ "viewFailed": "Не удалось открыть навык",
+ "saving": "Сохранение...",
+ "saveFailed": "Не удалось сохранить навык",
+ "deleteFailed": "Не удалось удалить навык",
+ "loadStatsFailed": "Не удалось загрузить данные монитора навыков",
+ "clearStatsConfirm": "Очистить всю статистику навыков? Это нельзя отменить.",
+ "statsCleared": "Статистика навыков очищена",
+ "clearStatsFailed": "Не удалось очистить статистику",
+ "noDescription": "Нет описания",
+ "viewSkillTitle": "Навык: {{name}}",
+ "descriptionLabel": "Описание:",
+ "pathLabel": "Путь:",
+ "modTimeLabel": "Изменён:",
+ "contentLabel": "Содержимое:",
+ "cardVersion": "v{{version}}",
+ "cardScripts": "Скриптов: {{count}}",
+ "cardFiles": "Файлов: {{count}}",
+ "versionLabel": "Версия:",
+ "scriptsHeading": "Скрипты:",
+ "summaryHint": "(сводка — при необходимости загрузите полный текст)",
+ "loadFullBody": "Загрузить полный текст",
+ "loadFullFailed": "Не удалось загрузить полный текст навыка",
+ "nameRequired": "Укажите имя навыка",
+ "contentRequired": "Укажите содержимое навыка",
+ "nameInvalid": "Только строчные буквы, цифры и дефисы (правила имён Agent Skills)",
+ "descriptionRequired": "Нужно описание (записывается во front matter SKILL.md)",
+ "saveSuccess": "Навык обновлён",
+ "createdSuccess": "Навык создан",
+ "deleteConfirm": "Удалить навык «{{name}}»? Это нельзя отменить.",
+ "deleteConfirmWithRoles": "Удалить навык «{{name}}»?\n\n⚠️ Этот навык сейчас привязан к ролям ({{count}}):\n{{roles}}\n\nПосле удаления система автоматически уберёт этот навык из этих ролей.\n\nЭто нельзя отменить. Продолжить?",
+ "deleteSuccess": "Навык удалён",
+ "deleteSuccessWithRoles": "Навык удалён и автоматически убран из ролей ({{count}}): {{roles}}",
+ "loadFailedShort": "Ошибка загрузки",
+ "totalSkillsCount": "Всего навыков",
+ "totalCallsCount": "Всего вызовов",
+ "successfulCalls": "Успешные вызовы",
+ "failedCalls": "Неуспешные вызовы",
+ "successRate": "Доля успеха",
+ "skillName": "Имя навыка",
+ "totalCalls": "Всего вызовов",
+ "success": "Успех",
+ "failure": "Ошибка",
+ "lastCallTime": "Время последнего вызова",
+ "noCallRecords": "Записей вызовов навыков пока нет",
+ "loadStatsErrorShort": "Не удалось загрузить статистику",
+ "loadCallStatsError": "Не удалось загрузить статистику вызовов"
+ },
+ "apiDocs": {
+ "pageTitle": "Документация API - CyberStrikeAI",
+ "title": "Документация API",
+ "subtitle": "Документация API платформы CyberStrikeAI с проверкой запросов",
+ "authTitle": "Аутентификация API",
+ "authAllNeedToken": "Для всех конечных точек API нужна аутентификация по токену.",
+ "authGetToken": "1. Получение токена:",
+ "authGetTokenDesc": "После входа в интерфейсе токен сохраняется автоматически. Его также можно получить так:",
+ "authUseToken": "2. Использование токена:",
+ "authUseTokenDesc": "Добавьте заголовок Authorization:",
+ "authTip": "💡 Страница сама подставит токен текущего входа; вводить его не нужно.",
+ "tokenDetected": "✓ Токен найден — конечные точки API можно проверить сразу",
+ "tokenNotDetected": "⚠ Токен не найден — сначала войдите в интерфейсе и обновите страницу. При проверке добавьте в заголовок запроса Authorization: Bearer token",
+ "sidebarGroupTitle": "Группы API",
+ "allApis": "Все API",
+ "loading": "Загрузка...",
+ "loadingDesc": "Загрузка документации API",
+ "errorLoginRequired": "Чтобы открыть документацию API, сначала войдите в интерфейсе и обновите страницу.",
+ "errorLoadSpec": "Не удалось загрузить спецификацию API: ",
+ "errorLoadFailed": "Не удалось загрузить документацию API: ",
+ "errorSpecInvalid": "Недопустимый формат спецификации API",
+ "loadFailed": "Ошибка загрузки",
+ "backToLogin": "Ко входу",
+ "noApis": "Нет API",
+ "noEndpointsInGroup": "В этой группе нет конечных точек API",
+ "sectionDescription": "Описание",
+ "viewDetailDesc": "Показать подробности",
+ "hideDetailDesc": "Скрыть подробности",
+ "noDescription": "Нет описания",
+ "sectionParams": "Параметры",
+ "paramName": "Параметр",
+ "type": "Тип",
+ "description": "Описание",
+ "required": "Обязательный",
+ "optional": "Необязательный",
+ "sectionRequestBody": "Тело запроса",
+ "example": "Пример",
+ "exampleJson": "Пример JSON:",
+ "sectionResponse": "Ответ",
+ "testSection": "Проверка",
+ "requestBodyJson": "Тело запроса (JSON)",
+ "queryParams": "Параметры запроса:",
+ "sendRequest": "Отправить запрос",
+ "copyCurl": "Копировать cURL",
+ "clearResult": "Очистить результат",
+ "copyCurlTitle": "Копировать команду cURL",
+ "clearResultTitle": "Очистить результат проверки",
+ "sendingRequest": "Отправка запроса...",
+ "errorPathParamRequired": "Параметр пути {{name}} обязателен",
+ "errorQueryParamRequired": "Параметр запроса {{name}} обязателен",
+ "errorTokenRequired": "Токен не найден. Войдите в интерфейсе и обновите страницу или добавьте в заголовок запроса Authorization: Bearer your_token",
+ "errorJsonInvalid": "Недопустимый JSON в теле запроса: ",
+ "requestFailed": "Запрос не выполнен: ",
+ "copied": "Скопировано",
+ "curlCopied": "Команда curl скопирована в буфер обмена.",
+ "copyFailedManual": "Не удалось скопировать, скопируйте вручную:\n\n",
+ "curlGenFailed": "Не удалось сформировать команду cURL: ",
+ "requestBodyPlaceholder": "Введите тело запроса в формате JSON",
+ "tags": {
+ "auth": "Аутентификация",
+ "conversationManagement": "Управление диалогами",
+ "conversationInteraction": "Взаимодействие с диалогом",
+ "batchTasks": "Пакетные задачи",
+ "vulnerabilityManagement": "Управление уязвимостями",
+ "roleManagement": "Управление ролями",
+ "skillsManagement": "Управление навыками",
+ "monitoring": "Мониторинг",
+ "configManagement": "Управление конфигурацией",
+ "externalMCPManagement": "Управление внешними MCP",
+ "attackChain": "Цепочка атаки",
+ "knowledgeBase": "База знаний",
+ "mcp": "MCP",
+ "fofaRecon": "Разведка FOFA",
+ "terminal": "Терминал",
+ "webshellManagement": "Управление WebShell",
+ "chatUploads": "Загрузки чата",
+ "robotIntegration": "Интеграция роботов",
+ "markdownAgents": "Агенты Markdown",
+ "projectManagement": "Управление проектами",
+ "assetManagement": "Управление активами"
+ },
+ "summary": {
+ "login": "Вход пользователя",
+ "logout": "Выход пользователя",
+ "changePassword": "Сменить пароль",
+ "validateToken": "Проверить токен",
+ "createConversation": "Создать диалог",
+ "listConversations": "Список диалогов",
+ "getConversationDetail": "Получить сведения о диалоге",
+ "updateConversation": "Обновить диалог",
+ "deleteConversation": "Удалить диалог",
+ "getConversationResult": "Получить результат диалога",
+ "sendMessageNonStream": "Отправить сообщение и получить ответ ИИ (без потока)",
+ "sendMessageStream": "Отправить сообщение и получить ответ ИИ (поток)",
+ "cancelTask": "Отменить задачу",
+ "listRunningTasks": "Список выполняемых задач",
+ "listCompletedTasks": "Список завершённых задач",
+ "createBatchQueue": "Создать очередь пакетных задач",
+ "listBatchQueues": "Список очередей пакетных задач",
+ "getBatchQueue": "Получить очередь пакетных задач",
+ "deleteBatchQueue": "Удалить очередь пакетных задач",
+ "startBatchQueue": "Запустить очередь пакетных задач",
+ "pauseBatchQueue": "Приостановить очередь пакетных задач",
+ "addTaskToQueue": "Добавить задачу в очередь",
+ "sqlInjectionScan": "Сканирование на SQL-инъекции",
+ "portScan": "Сканирование портов",
+ "updateBatchTask": "Обновить пакетную задачу",
+ "deleteBatchTask": "Удалить пакетную задачу",
+ "listVulnerabilities": "Список уязвимостей",
+ "createVulnerability": "Создать уязвимость",
+ "getVulnerabilityStats": "Получить статистику уязвимостей",
+ "getVulnerability": "Получить уязвимость",
+ "updateVulnerability": "Обновить уязвимость",
+ "deleteVulnerability": "Удалить уязвимость",
+ "listRoles": "Список ролей",
+ "createRole": "Создать роль",
+ "getRole": "Получить роль",
+ "updateRole": "Обновить роль",
+ "deleteRole": "Удалить роль",
+ "getAvailableSkills": "Получить список доступных навыков",
+ "listSkills": "Список навыков",
+ "createSkill": "Создать навык",
+ "getSkillStats": "Получить статистику навыка",
+ "clearSkillStats": "Очистить статистику навыка",
+ "getSkill": "Получить навык",
+ "updateSkill": "Обновить навык",
+ "deleteSkill": "Удалить навык",
+ "getBoundRoles": "Получить привязанные роли",
+ "clearSkillStatsAlt": "Очистить статистику навыка",
+ "getMonitorInfo": "Получить данные мониторинга",
+ "getExecutionRecords": "Получить записи выполнения",
+ "deleteExecutionRecord": "Удалить запись выполнения",
+ "batchDeleteExecutionRecords": "Удалить записи выполнения пакетом",
+ "getStats": "Получить статистику",
+ "getConfig": "Получить конфигурацию",
+ "updateConfig": "Обновить конфигурацию",
+ "getToolConfig": "Получить конфигурацию инструмента",
+ "applyConfig": "Применить конфигурацию",
+ "listExternalMCP": "Список внешних MCP",
+ "getExternalMCPStats": "Получить статистику внешних MCP",
+ "getExternalMCP": "Получить внешний MCP",
+ "addOrUpdateExternalMCP": "Добавить или обновить внешний MCP",
+ "stdioModeConfig": "Конфигурация режима stdio",
+ "sseModeConfig": "Конфигурация режима SSE",
+ "deleteExternalMCP": "Удалить внешний MCP",
+ "startExternalMCP": "Запустить внешний MCP",
+ "stopExternalMCP": "Остановить внешний MCP",
+ "getAttackChain": "Получить цепочку атаки",
+ "regenerateAttackChain": "Пересоздать цепочку атаки",
+ "pinConversation": "Закрепить диалог",
+ "getCategories": "Получить категории",
+ "listKnowledgeItems": "Список записей базы знаний",
+ "createKnowledgeItem": "Создать запись базы знаний",
+ "getKnowledgeItem": "Получить запись базы знаний",
+ "updateKnowledgeItem": "Обновить запись базы знаний",
+ "deleteKnowledgeItem": "Удалить запись базы знаний",
+ "getIndexStatus": "Получить статус индекса",
+ "startKnowledgeIndex": "Построить индекс",
+ "scanKnowledgeBase": "Сканировать базу знаний",
+ "searchKnowledgeBase": "Искать в базе знаний",
+ "basicSearch": "Базовый поиск",
+ "searchByRiskType": "Поиск по типу риска",
+ "getRetrievalLogs": "Получить журналы извлечения",
+ "deleteRetrievalLog": "Удалить запись извлечения",
+ "mcpEndpoint": "Конечная точка MCP",
+ "listAllTools": "Список всех инструментов",
+ "invokeTool": "Вызвать инструмент",
+ "initConnection": "Инициализировать подключение",
+ "successResponse": "Успешный ответ",
+ "errorResponse": "Ответ с ошибкой",
+ "deleteConversationTurn": "Удалить реплику диалога",
+ "getMessageProcessDetails": "Получить сведения об обработке сообщения",
+ "rerunBatchQueue": "Повторить очередь пакетных задач",
+ "updateBatchQueueMetadata": "Обновить метаданные очереди",
+ "updateBatchQueueSchedule": "Обновить расписание очереди",
+ "setBatchQueueScheduleEnabled": "Переключить автозапуск по cron",
+ "fofaSearch": "Поиск FOFA",
+ "fofaParse": "Преобразовать естественный язык в синтаксис FOFA",
+ "testOpenAI": "Проверить подключение к API OpenAI",
+ "terminalRun": "Выполнить команду терминала",
+ "terminalRunStream": "Выполнить команду терминала (поток)",
+ "terminalWS": "Терминал WebSocket",
+ "listWebshellConnections": "Список подключений WebShell",
+ "createWebshellConnection": "Создать подключение WebShell",
+ "updateWebshellConnection": "Обновить подключение WebShell",
+ "deleteWebshellConnection": "Удалить подключение WebShell",
+ "getWebshellConnectionState": "Получить состояние подключения",
+ "saveWebshellConnectionState": "Сохранить состояние подключения",
+ "getWebshellAIHistory": "Получить историю чата ИИ",
+ "listWebshellAIConversations": "Список диалогов ИИ",
+ "webshellExec": "Выполнить команду WebShell",
+ "webshellFileOp": "Файловая операция WebShell",
+ "listChatUploads": "Список загрузок",
+ "exportChatUploads": "Экспортировать загрузки",
+ "uploadChatFile": "Загрузить файл",
+ "deleteChatUpload": "Удалить загрузку",
+ "downloadChatUpload": "Скачать загрузку",
+ "getChatUploadContent": "Получить текстовое содержимое файла",
+ "putChatUploadContent": "Записать текстовое содержимое файла",
+ "mkdirChatUpload": "Создать каталог загрузки",
+ "renameChatUpload": "Переименовать загрузку",
+ "wecomCallbackVerify": "Проверка обратного вызова WeCom",
+ "wecomCallbackMessage": "Обратный вызов сообщения WeCom",
+ "dingtalkCallback": "Обратный вызов сообщения DingTalk",
+ "larkCallback": "Обратный вызов сообщения Lark",
+ "testRobot": "Проверить обработку сообщения робота",
+ "listMarkdownAgents": "Список агентов Markdown",
+ "createMarkdownAgent": "Создать агента Markdown",
+ "getMarkdownAgent": "Получить сведения об агенте Markdown",
+ "updateMarkdownAgent": "Обновить агента Markdown",
+ "deleteMarkdownAgent": "Удалить агента Markdown",
+ "listSkillPackageFiles": "Список файлов пакета навыка",
+ "getSkillPackageFile": "Получить содержимое файла пакета навыка",
+ "putSkillPackageFile": "Записать файл пакета навыка",
+ "batchGetToolNames": "Получить имена инструментов пакетом",
+ "getKnowledgeStats": "Получить статистику базы знаний",
+ "listProjects": "Список проектов",
+ "createProject": "Создать проект",
+ "getProject": "Получить проект",
+ "updateProject": "Обновить проект",
+ "deleteProject": "Удалить проект",
+ "listProjectFacts": "Список фактов или факт по ключу",
+ "upsertProjectFact": "Создать или обновить факт",
+ "getProjectFactGraph": "Получить граф пути атаки по фактам проекта",
+ "listProjectFactEdges": "Список всех связей фактов проекта",
+ "createProjectFactEdge": "Добавить связь факта",
+ "deleteProjectFactEdge": "Удалить связь факта",
+ "promoteAttackChainToProject": "Перенести цепочку атаки диалога в граф фактов проекта",
+ "importAssets": "Массово импортировать активы"
+ },
+ "response": {
+ "getSuccess": "Успешно",
+ "unauthorized": "Не авторизован",
+ "unauthorizedToken": "Не авторизован, нужен действительный токен",
+ "createSuccess": "Успешно создано",
+ "badRequest": "Некорректный запрос",
+ "conversationNotFound": "Диалог не найден",
+ "conversationOrResultNotFound": "Диалог или результат не найден",
+ "badRequestTaskEmpty": "Некорректный запрос (например, задача пуста)",
+ "badRequestConfig": "Некорректный запрос (например, недопустимая конфигурация или нет обязательных полей)",
+ "badRequestQueryEmpty": "Некорректный запрос (например, запрос пуст)",
+ "methodNotAllowed": "Метод не поддерживается (только POST)",
+ "loginSuccess": "Вход выполнен",
+ "invalidPassword": "Неверный пароль",
+ "logoutSuccess": "Выход выполнен",
+ "passwordChanged": "Пароль изменён",
+ "tokenValid": "Токен действителен",
+ "tokenInvalid": "Токен недействителен или истёк",
+ "conversationCreated": "Диалог создан",
+ "internalError": "Внутренняя ошибка сервера",
+ "updateSuccess": "Успешно обновлено",
+ "deleteSuccess": "Успешно удалено",
+ "queueNotFound": "Очередь не найдена",
+ "startSuccess": "Успешно запущено",
+ "pauseSuccess": "Успешно приостановлено",
+ "addSuccess": "Успешно добавлено",
+ "taskNotFound": "Задача не найдена",
+ "cancelSubmitted": "Запрос на отмену отправлен",
+ "noRunningTask": "Нет выполняемой задачи",
+ "messageSent": "Сообщение отправлено, ответ ИИ получен",
+ "streamResponse": "Потоковый ответ (Server-Sent Events)",
+ "badRequestOrDeleteFailed": "Некорректный запрос или удаление не выполнено",
+ "paramError": "Недопустимые параметры",
+ "onlyCompletedOrCancelledCanRerun": "Повторить можно только завершённые или отменённые очереди",
+ "badRequestOrQueueRunning": "Некорректный запрос или очередь выполняется",
+ "setSuccess": "Успешно задано",
+ "searchSuccess": "Поиск выполнен",
+ "parseSuccess": "Разбор выполнен",
+ "testResult": "Результат проверки",
+ "executionDone": "Выполнение завершено",
+ "sseEventStream": "Поток событий SSE",
+ "wsEstablished": "Соединение WebSocket установлено",
+ "fileDownload": "Скачивание файла",
+ "fileNotFound": "Файл не найден",
+ "writeSuccess": "Успешно записано",
+ "renameSuccess": "Успешно переименовано",
+ "wecomVerifySuccess": "Проверка выполнена, возвращён расшифрованный echostr",
+ "processSuccess": "Успешно обработано",
+ "agentNotFound": "Агент не найден",
+ "saveSuccess": "Успешно сохранено",
+ "operationResult": "Результат операции",
+ "executionResult": "Результат выполнения",
+ "connectionNotFound": "Подключение не найдено",
+ "projectList": "Список проектов",
+ "projectDetail": "Сведения о проекте",
+ "projectFactList": "Список фактов или один факт (может включать link_counts / outgoing_links)",
+ "success": "Успешно",
+ "factGraphNodesEdges": "узлы + связи",
+ "edgeList": "Список связей",
+ "edgeCreated": "Связь создана",
+ "promoteAttackChainResult": "Результат переноса (факты/связи/граф)",
+ "assetImportCompleted": "Импорт завершён",
+ "assetImportValidationFailed": "Не пройдена проверка числа активов или полей",
+ "assetImportForbidden": "Нет права asset:write или доступа к указанному проекту",
+ "assetImportTransactionFailed": "Транзакция импорта не выполнена"
+ }
+ },
+ "mcpMonitor": {
+ "toolRowNoCompletedAriaLabel": "{{name}}, вызовов: {{total}}, завершённых результатов нет; открыть записи выполнения",
+ "rateExcludesBlocked": "Доля успеха учитывает успешные и неуспешные вызовы, без заблокированных и остановленных",
+ "timelineBlockedLegend": "Заблокировано",
+ "blockedCount": "Заблокировано {{n}}",
+ "statusBlocked": "Заблокировано",
+ "deselectAll": "Снять выбор",
+ "statusPending": "Ожидает",
+ "statusQueued": "В очереди",
+ "statusSuccess": "Успешно",
+ "statusCompleted": "Завершено",
+ "statusRunning": "Выполняется",
+ "statusFailed": "Ошибка",
+ "statusHardTimeout": "Тайм-аут",
+ "statusCancelled": "Отменено",
+ "statusOrphaned": "Осиротевший",
+ "terminateExecution": "Остановить",
+ "loading": "Загрузка...",
+ "noStatsData": "Нет статистики",
+ "noExecutions": "Нет записей выполнения",
+ "emptyHint": "Записи появятся здесь после вызова инструментов MCP в чате или задачах",
+ "noRecordsWithFilter": "Нет записей по текущему фильтру",
+ "paginationInfo": "Показано {{start}}–{{end}} из {{total}}",
+ "perPageLabel": "На странице",
+ "firstPage": "Первая",
+ "prevPage": "Назад",
+ "nextPage": "Далее",
+ "lastPage": "Последняя",
+ "pageInfo": "Стр. {{page}} из {{total}}",
+ "loadStatsError": "Не удалось загрузить статистику",
+ "loadExecutionsError": "Не удалось загрузить записи выполнения",
+ "totalCalls": "Всего вызовов",
+ "successFailed": "Успешно {{success}} / ошибок {{failed}}",
+ "successRate": "Доля успеха",
+ "statsFromAllTools": "По всем вызовам инструментов",
+ "lastCall": "Последний вызов",
+ "lastRefreshTime": "Обновлено",
+ "noCallsYet": "Вызовов ещё нет",
+ "noCompletedYet": "Завершённых результатов ещё нет",
+ "unknownTool": "Неизвестный инструмент",
+ "successFailedRate": "Успешно {{success}} / ошибок {{failed}} · доля успеха {{rate}}%",
+ "topToolsTitle": "Топ-{{n}} инструментов по вызовам",
+ "toolRankingTitle": "Рейтинг вызовов инструментов",
+ "toolStatsTitle": "Статистика инструментов",
+ "toolStatsHint": "Нажмите сегмент или строку, чтобы отфильтровать записи ниже; наведите курсор для подсветки",
+ "scopeCumulative": "За всё время",
+ "scopeTimeline": "Период тренда",
+ "filterActive": "Фильтр: {{tool}}",
+ "kpiScopeNote": "Итоги за всё время",
+ "columnCalls": "Вызовы",
+ "columnShare": "Доля",
+ "columnSuccessRate": "Доля успеха",
+ "rankingSummary": "Топ-{{n}} {{pct}}% · всего вызовов {{total}}",
+ "barVolumeLegend": "Длина столбца: относительный объём вызовов; зелёный/красный: доля успеха и ошибок",
+ "clickToFilterTool": "Нажмите строку, чтобы отфильтровать записи ниже",
+ "toolRowAriaLabel": "{{name}}, вызовов: {{total}}, доля успеха {{rate}}%. Нажмите, чтобы отфильтровать записи.",
+ "successRateAria": "Доля успеха {{rate}}%",
+ "filterByToolTitle": "Фильтр: {{tool}}",
+ "clearToolFilter": "Сбросить фильтр инструмента",
+ "successCount": "Успешно {{n}}",
+ "failedCount": "Ошибок {{n}}",
+ "neutralCount": "Остановлено {{n}}",
+ "rateHealthy": "Работает стабильно",
+ "rateWarning": "Есть ошибки",
+ "rateCritical": "Высокая доля ошибок",
+ "statsSubtitle": "Обновлено {{time}} · инструментов: {{count}}",
+ "retentionHint": "Записи выполнения хранятся {{days}} дн., затем удаляются автоматически.",
+ "timelineTitle": "Тренд вызовов",
+ "timelineHint": "Все инструменты вместе (без разбивки)",
+ "timelineRange24h": "24 ч",
+ "timelineRange7d": "7 дн.",
+ "timelineRange30d": "30 дн.",
+ "timelineSummary": "Вызовов за период: {{total}} · пик {{peak}}",
+ "timelineSparseHint": "Большинство интервалов пусты; пик {{peak}} вызовов в {{peakTime}}",
+ "timelineNoData": "За этот период вызовов нет",
+ "timelineLoading": "Загрузка тренда…",
+ "timelineEmptyHint": "Смените период или вызовите инструменты MCP в чате или задачах",
+ "timelineLoadError": "Не удалось загрузить тренд вызовов",
+ "timelineTotalLegend": "Всего вызовов",
+ "timelineFailedLegend": "Ошибки",
+ "timelineTooltip": "{{time}}: вызовов {{total}} (ошибок {{failed}}, заблокировано {{blocked}})",
+ "distTitle": "Распределение вызовов",
+ "distLegend": "Площадь сектора — доля всех вызовов",
+ "distClickHint": "Нажмите сегмент, чтобы отфильтровать записи",
+ "distHeaderHint": "Всего вызовов: {{n}}",
+ "distSegmentAria": "{{name}}, {{pct}}% вызовов, {{calls}} раз",
+ "distOthersNoFilter": "Остальные инструменты нельзя отфильтровать по отдельности",
+ "distTotalCalls": "Всего вызовов: {{n}}",
+ "distTop6Share": "Доля топ-{{n}} от всех вызовов",
+ "distOthers": "Другие инструменты",
+ "distCallsUnit": "Вызовов: {{n}}",
+ "riskTitle": "Оповещения об ошибках",
+ "riskNone": "Недавних ошибок нет",
+ "riskItem": "{{name}}: ошибок {{failed}} / {{total}}",
+ "selectedToolTitle": "Активный фильтр",
+ "selectedToolEmpty": "Выберите инструмент слева, чтобы отфильтровать записи ниже",
+ "selectedToolStats": "Вызовов {{total}} · успешно {{success}} · ошибок {{failed}} · доля успеха {{rate}}%",
+ "columnTool": "Инструмент",
+ "columnStatus": "Статус",
+ "columnStartTime": "Начало",
+ "columnDuration": "Длительность",
+ "columnActions": "Действия",
+ "viewDetail": "Подробнее",
+ "delete": "Удалить",
+ "deleteExecTitle": "Удалить эту запись выполнения",
+ "deleteExecConfirmSingle": "Удалить эту запись выполнения? Это нельзя отменить.",
+ "deleteExecFailed": "Не удалось удалить запись выполнения",
+ "execDeleted": "Запись выполнения удалена",
+ "selectExecFirst": "Сначала выберите записи выполнения для удаления",
+ "batchDeleteConfirm": "Удалить выбранные записи выполнения ({{count}})? Это нельзя отменить.",
+ "batchDeleteSuccess": "Удалено записей выполнения: {{count}}",
+ "unknown": "Неизвестно",
+ "durationSeconds": "{{n}} с",
+ "durationMinutes": "{{minutes}} мин {{seconds}} с",
+ "durationMinutesOnly": "{{minutes}} мин",
+ "durationHours": "{{hours}} ч {{minutes}} мин",
+ "durationHoursOnly": "{{hours}} ч"
+ },
+ "knowledgePage": {
+ "totalContent": "Всего содержимого",
+ "categoryFilter": "Фильтр категории",
+ "all": "Все",
+ "searchPlaceholder": "Поиск в базе знаний...",
+ "loading": "Загрузка..."
+ },
+ "retrievalLogs": {
+ "totalRetrievals": "Всего извлечений",
+ "successRetrievals": "Успешно",
+ "successRate": "Доля успеха",
+ "retrievedItems": "Получено элементов",
+ "conversationId": "ID диалога",
+ "messageId": "ID сообщения",
+ "filter": "Фильтр",
+ "optionalConversation": "Необязательно: фильтр по диалогу",
+ "optionalMessage": "Необязательно: фильтр по сообщению",
+ "loading": "Загрузка...",
+ "noRecords": "Записей извлечения пока нет",
+ "noQuery": "Нет текста запроса",
+ "itemsUnit": "эл.",
+ "hasResults": "Есть результаты",
+ "noResults": "Нет результатов",
+ "clickToCopy": "Нажмите, чтобы скопировать",
+ "retrievalResult": "Результат извлечения",
+ "foundCount": "Найдено связанных элементов базы знаний: {{count}}",
+ "foundUnknown": "Найдены связанные знания (число неизвестно)",
+ "noMatch": "Нет подходящих элементов базы знаний",
+ "retrievedItemsLabel": "Полученные элементы базы знаний:",
+ "viewDetails": "Подробнее",
+ "loadError": "Не удалось загрузить журналы извлечения",
+ "detailError": "Не удалось получить сведения об извлечении",
+ "deleteError": "Не удалось удалить запись журнала извлечения",
+ "detailsTitle": "Сведения об извлечении",
+ "queryInfo": "Сведения о запросе",
+ "queryContent": "Текст запроса:",
+ "retrievalInfo": "Сведения об извлечении",
+ "riskType": "Тип риска",
+ "retrievalTime": "Время извлечения",
+ "noItemDetails": "Сведения об элементе базы знаний не найдены",
+ "noContentPreview": "Нет предпросмотра содержимого",
+ "untitled": "Без названия",
+ "uncategorized": "Без категории",
+ "relatedInfo": "Связанные сведения",
+ "itemsCount": "Элементов базы знаний: {{count}}",
+ "deleteConfirm": "Удалить эту запись извлечения?"
+ },
+ "infoCollectPage": {
+ "title": "Разведка",
+ "reset": "Сбросить",
+ "confirm": "ОК",
+ "fofaQuerySyntax": "Синтаксис запроса FOFA",
+ "naturalLanguage": "Естественный язык (ИИ разбирает в FOFA)",
+ "returnCount": "Число результатов",
+ "pageNum": "Страница",
+ "returnFields": "Поля ответа (через запятую)",
+ "queryResults": "Результаты запроса",
+ "selectedRows": "Выбрано: {{count}}",
+ "selectedRowsZero": "Выбрано: 0",
+ "columns": "Столбцы",
+ "exportCsv": "Экспорт CSV",
+ "exportJson": "Экспорт JSON",
+ "exportXlsx": "Экспорт XLSX",
+ "batchScan": "Пакетное сканирование",
+ "showColumns": "Показать столбцы",
+ "columnsPanelAll": "Выбрать все",
+ "columnsPanelNone": "Снять выбор",
+ "columnsPanelClose": "Закрыть",
+ "formHint": "Синтаксис запросов — в документации FOFA; поддерживаются && / || / ().",
+ "parseBtn": "Разбор ИИ",
+ "parseHint": "Результат откроется во всплывающем окне для правки перед запросом.",
+ "minFields": "Минимум полей",
+ "webCommon": "Веб, обычный",
+ "intelEnhanced": "Расширенная разведка",
+ "presetApache": "Apache + Китай",
+ "presetLogin": "Страница входа + Китай",
+ "presetDomain": "По домену",
+ "presetIp": "По IP",
+ "queryPresetsAria": "Пресеты запросов FOFA",
+ "fieldsPresetsAria": "Пресеты полей FOFA",
+ "resultsToolbarAria": "Панель результатов",
+ "fillExample": "Подставить пример",
+ "parseBtnTitle": "Разобрать естественный язык в запрос FOFA",
+ "minFieldsTitle": "Для быстрого экспорта",
+ "webCommonTitle": "Для просмотра и фильтрации",
+ "intelEnhancedTitle": "Больше отпечатков и разведданных",
+ "fullLabel": "full",
+ "nlPlaceholder": "например, сайты Apache в штате Миссури, США, в заголовке есть Home",
+ "showHideColumns": "Показать или скрыть столбцы",
+ "exportCsvTitle": "Экспортировать результаты в CSV (UTF-8)",
+ "exportJsonTitle": "Экспортировать результаты в JSON",
+ "exportXlsxTitle": "Экспортировать результаты в Excel",
+ "batchScanTitle": "Создать очередь пакетных задач из выбранных строк"
+ },
+ "chatFilesPage": {
+ "title": "Управление файлами",
+ "intro": "Здесь появляются файлы, загруженные в чате. Перетащите файлы в список ниже или нажмите «Загрузить», чтобы выбрать файлы (можно несколько). Нажмите «Копировать путь», чтобы скопировать абсолютный путь на сервере и вставить его в диалог — модель сможет сослаться на файл.",
+ "export": "Экспортировать текущие результаты",
+ "exportStarted": "Начат экспорт текущих файлов",
+ "exportEmpty": "Нет файлов для экспорта",
+ "upload": "Загрузить",
+ "conversationFilter": "ID диалога",
+ "conversationPlaceholder": "Пусто — все",
+ "projectFilter": "ID проекта",
+ "projectPlaceholder": "Пусто — все",
+ "searchName": "Имя файла",
+ "searchNamePlaceholder": "Фильтр по имени файла",
+ "sourceFilter": "Источник",
+ "sourceAll": "Все",
+ "sourceUpload": "Загрузки из чата",
+ "sourceReduction": "Результаты инструментов",
+ "sourceWorkspace": "Файлы рабочей области",
+ "sourceConversationArtifact": "Артефакты диалога",
+ "groupBy": "Группировать",
+ "groupNone": "Нет (плоский список)",
+ "groupByDate": "По дате",
+ "groupByConversation": "По диалогу",
+ "groupByProject": "По проекту",
+ "groupByFolder": "По папке (навигация по пути)",
+ "projectUnbound": "Проект не привязан",
+ "paginationInfo": "Показано {{start}}–{{end}} из {{total}} файлов",
+ "paginationPage": "Стр. {{page}} из {{totalPages}}",
+ "pageSize": "На странице",
+ "prevPage": "Назад",
+ "nextPage": "Далее",
+ "browseRoot": "Файлы",
+ "treeUploadsRoot": "Загрузки из чата",
+ "treeReductionRoot": "Результаты инструментов",
+ "treeWorkspaceRoot": "Файлы рабочей области",
+ "treeArtifactsRoot": "Артефакты диалога",
+ "browseUp": "Вверх",
+ "enterFolderTitle": "Открыть папку",
+ "copyFolderPathTitle": "Копировать путь папки",
+ "folderPathCopied": "Путь папки скопирован — вставьте в чат при необходимости",
+ "folderEmpty": "Эта папка пуста",
+ "confirmDeleteFolder": "Удалить эту папку и всё её содержимое? Это нельзя отменить.",
+ "folderRemovedStale": "Этой папки больше нет на сервере; список обновлён.",
+ "deleteFolderTitle": "Удалить папку",
+ "uploadToFolderTitle": "Загрузить файл в эту папку",
+ "newFolderButton": "Новая папка",
+ "newFolderTitle": "Новая папка",
+ "newFolderLocation": "Расположение",
+ "newFolderNameLabel": "Имя папки",
+ "newFolderNamePlaceholder": "Только имя, без косых черт",
+ "mkdirOk": "Папка создана",
+ "mkdirExists": "Файл или папка с таким именем уже есть",
+ "mkdirInvalidName": "Недопустимое имя: не может быть пустым, содержать / или \\, а также быть . или ..",
+ "colSubPath": "Подпапка",
+ "folderRoot": "(корень)",
+ "groupCount": "Файлов: {{count}}",
+ "convManual": "Ручная загрузка",
+ "convNew": "Новый чат",
+ "colDate": "Дата",
+ "colConversation": "Диалог",
+ "colName": "Имя",
+ "colSize": "Размер",
+ "colModified": "Изменён",
+ "colActions": "Действия",
+ "copyPath": "Копировать путь",
+ "copyPathTitle": "Копировать абсолютный путь на сервере; вставьте в чат, чтобы сослаться на этот файл",
+ "pathCopied": "Путь скопирован — вставьте его в чат",
+ "uploadOkHint": "Загружено. Нажмите «Копировать путь», чтобы скопировать абсолютный путь.",
+ "uploadingFile": "Загрузка {{name}} · {{percent}}%",
+ "moreActions": "Ещё: открыть чат, изменить, переименовать, удалить",
+ "download": "Скачать",
+ "edit": "Изменить",
+ "rename": "Переименовать",
+ "openChat": "Открыть чат",
+ "confirmDelete": "Удалить этот файл?",
+ "editTitle": "Изменить файл",
+ "renameTitle": "Переименовать",
+ "renameCurrentFile": "Текущий файл",
+ "newFileName": "Новое имя файла",
+ "empty": "Загрузок из чата пока нет",
+ "errorLoad": "Не удалось загрузить",
+ "editBinaryHint": "Двоичные файлы (изображения, архивы и т. п.) нельзя править здесь как текст. Скачайте и откройте локально.",
+ "editUnavailable": "Н/Д",
+ "editTooLarge": "Файл больше 2 МБ, править его здесь нельзя. Скачайте и измените локально.",
+ "errorGeneric": "Что-то пошло не так. Повторите попытку."
+ },
+ "vulnerabilityPage": {
+ "alertTitle": "Оповещения робота об уязвимостях",
+ "alertDescription": "Отправлять новые уязвимости роботам, привязанным к этой учётной записи, с фильтром по серьёзности.",
+ "alertHint": "Отправлять новые уязвимости не ниже выбранной серьёзности на привязанные учётные записи роботов.",
+ "alertConfiguredNotBound": "{{platforms}} включено, но эта веб-учётная запись не привязана к получателю.",
+ "alertNoBinding": "Нет включённого и привязанного робота для исходящих сообщений. Настройки всё равно сохранены.",
+ "alertBindAction": "Привязать учётную запись получателя",
+ "alertMinimum": "Минимальная серьёзность",
+ "alertEnabled": "Включить оповещения",
+ "alertSaved": "Настройки оповещений об уязвимостях сохранены",
+ "alertSaveFailed": "Не удалось сохранить настройки оповещений об уязвимостях",
+ "statTotal": "Всего",
+ "statClickAll": "Показать все (сбросить фильтр серьёзности)",
+ "statClickFilter": "Нажмите, чтобы фильтровать по этой серьёзности; ещё раз — чтобы сбросить",
+ "advancedFilters": "Расширенные фильтры",
+ "moreFilters": "Ещё фильтры",
+ "applyFilters": "Применить",
+ "clearAdvanced": "Очистить",
+ "clearAll": "Сбросить всё",
+ "activeFilters": "Активные фильтры",
+ "chipRemove": "Убрать",
+ "filter": "Фильтр",
+ "clear": "Очистить",
+ "vulnId": "ID уязвимости",
+ "conversationId": "ID диалога",
+ "taskOrQueueId": "ID задачи / очереди",
+ "filterTaskOrQueue": "Фильтр по ID задачи или очереди",
+ "conversationTag": "Метка диалога",
+ "filterConversationTag": "Фильтр по метке диалога",
+ "taskTag": "Метка задачи",
+ "filterTaskTag": "Фильтр по метке задачи",
+ "severity": "Серьёзность",
+ "status": "Статус",
+ "statusOpen": "Открыта",
+ "statusConfirmed": "Подтверждена",
+ "statusFixed": "Исправлена",
+ "statusFalsePositive": "Ложное срабатывание",
+ "statusIgnored": "Проигнорирована",
+ "statusChangeLabel": "Изменить статус",
+ "statusUpdateFailed": "Не удалось обновить статус",
+ "searchVulnId": "Поиск по ID уязвимости",
+ "searchKeyword": "Поиск по названию, описанию, типу, цели…",
+ "searchKeywordShort": "Ключевое слово",
+ "filterExactId": "Точный ID уязвимости",
+ "filterEnterHint": "Нажмите Enter для фильтра",
+ "filterConversation": "Фильтр по диалогу",
+ "loading": "Загрузка...",
+ "loadListFailed": "Не удалось загрузить",
+ "noRecords": "Нет записей об уязвимостях",
+ "batchExport": "Пакетный экспорт",
+ "batchDelete": "Пакетное удаление",
+ "batchDeleteNoResults": "Нет уязвимостей по текущим фильтрам для удаления",
+ "batchDeleteConfirm": "Удалить записи об уязвимостях по текущим фильтрам ({{count}})? Это нельзя отменить.",
+ "batchDeleteConfirmAll": "Фильтры не заданы. Будут удалены все записи об уязвимостях ({{count}}). Это нельзя отменить. Продолжить?",
+ "batchDeleteSuccess": "Удалено записей об уязвимостях: {{count}}",
+ "batchDeleteFailed": "Пакетное удаление не удалось",
+ "downloadMarkdownTitle": "Скачать Markdown",
+ "exportNoResults": "Нет уязвимостей по текущим фильтрам",
+ "exportStarted": "Начата загрузка файлов: {{count}}",
+ "exportFailed": "Экспорт не удался",
+ "saveRequiredFields": "Заполните ID диалога, название, описание, серьёзность, тип, цель, шаги воспроизведения, доказательства/POC, влияние и меры устранения",
+ "saveFailed": "Не удалось сохранить",
+ "fetchFailed": "Не удалось получить уязвимость",
+ "deleteFailed": "Не удалось удалить",
+ "detailVulnId": "ID уязвимости",
+ "detailType": "Тип",
+ "detailTarget": "Цель",
+ "detailProject": "Проект",
+ "projectUnbound": "Без проекта",
+ "allProjects": "Все проекты",
+ "filterByProject": "Фильтр по проекту",
+ "projectBindHint": "После привязки агенты смогут видеть эту находку в области проекта.",
+ "projectBindFailed": "Не удалось обновить привязку к проекту",
+ "projectBindOk": "Привязка к проекту обновлена",
+ "detailConversationId": "ID диалога",
+ "openConversation": "Открыть диалог",
+ "detailTaskId": "ID задачи",
+ "detailTaskQueueId": "ID очереди задач",
+ "detailConversationTag": "Метка диалога",
+ "detailTaskTag": "Метка задачи",
+ "detailPreconditions": "Предусловия",
+ "detailReproductionSteps": "Шаги воспроизведения",
+ "detailEvidence": "Доказательства / POC",
+ "detailImpact": "Влияние",
+ "detailRecommendation": "Устранение",
+ "detailRetestNotes": "Метод повторной проверки",
+ "downloadOkTitle": "Скачано",
+ "exportFailedMessage": "Экспорт не удался",
+ "downloadFailed": "Загрузка не удалась"
+ },
+ "tasksPage": {
+ "statusFilter": "Фильтр статуса",
+ "statusPending": "Ожидает",
+ "statusPaused": "Пауза",
+ "statusCancelled": "Отменено",
+ "searchQueuePlaceholder": "Поиск по ID очереди, названию или времени создания",
+ "searchKeywordPlaceholder": "Введите ключевое слово..."
+ },
+ "skillsPage": {
+ "clearStats": "Очистить статистику",
+ "clearStatsTitle": "Очистить всю статистику",
+ "skillsCallStats": "Статистика вызовов навыков",
+ "searchPlaceholder": "Поиск навыков...",
+ "overviewTotal": "Навыки",
+ "overviewCurrent": "Эта страница",
+ "overviewFiles": "Файлы",
+ "overviewScripts": "Скрипты",
+ "loading": "Загрузка...",
+ "paginationShow": "Показано {{start}}–{{end}} из {{total}}",
+ "perPageLabel": "На странице",
+ "firstPage": "Первая",
+ "prevPage": "Назад",
+ "pageOf": "Стр. {{current}} / {{total}}",
+ "nextPage": "Далее",
+ "lastPage": "Последняя"
+ },
+ "agentsPage": {
+ "title": "Управление агентами",
+ "create": "Новый агент",
+ "hint": "Агенты — файлы .md в agents_dir (front matter и тело как системный промпт). Оркестратор — координатор Deep и не показывается как подагент задачи.",
+ "dirLabel": "Каталог",
+ "loading": "Загрузка...",
+ "empty": "Подагентов Markdown пока нет. Нажмите «Новый агент», чтобы создать.",
+ "noDesc": "Нет описания",
+ "loadFailed": "Не удалось загрузить список",
+ "loadOneFailed": "Не удалось загрузить агента",
+ "createTitle": "Новый агент",
+ "editTitle": "Изменить агента",
+ "filename": "Имя файла (.md)",
+ "filenamePlaceholder": "например, code-reviewer.md",
+ "fieldRole": "Тип",
+ "roleSub": "Подагент",
+ "roleOrchestrator": "Оркестратор (Deep)",
+ "roleHint": "Оркестраторы зависят от режима: Deep → orchestrator.md (или один .md с kind: orchestrator); plan_execute → orchestrator-plan-execute.md; supervisor → orchestrator-supervisor.md. Не больше одного каждого вида. Пустое тело заменяется на multi_agent.orchestrator_instruction / orchestrator_instruction_plan_execute / orchestrator_instruction_supervisor или встроенные значения по умолчанию (PE/SV не используют orchestrator_instruction режима Deep).",
+ "badgeOrchestrator": "Оркестратор",
+ "badgeSub": "Подагент",
+ "filenameInvalid": "Имя файла должно заканчиваться на .md и содержать только буквы, цифры и ._-",
+ "fieldId": "ID агента (необязательно; если пусто — из имени)",
+ "fieldName": "Отображаемое имя",
+ "namePlaceholder": "Code Reviewer",
+ "fieldDesc": "Описание",
+ "descPlaceholder": "Когда оркестратору следует делегировать этому агенту",
+ "fieldTools": "Инструменты (через запятую; те же ключи, что у инструментов роли)",
+ "fieldBindRole": "Привязать роль (необязательно)",
+ "fieldMaxIter": "Макс. итераций (0 = из настроек → agent.max_iterations)",
+ "fieldInstruction": "Системный промпт (тело Markdown)",
+ "instructionPlaceholder": "Вы специализированный агент...",
+ "nameRequired": "Укажите отображаемое имя",
+ "instructionRequired": "Укажите тело системного промпта",
+ "saveOk": "Сохранено",
+ "createOk": "Создано",
+ "saveFailed": "Не удалось сохранить",
+ "deleteConfirm": "Удалить {{name}}?",
+ "deleteOk": "Удалено",
+ "deleteFailed": "Не удалось удалить"
+ },
+ "settingsBasic": {
+ "basicTitle": "Основные настройки",
+ "openaiConfig": "Настройки канала ИИ",
+ "aiChannelCurrent": "Текущий канал",
+ "aiChannelSave": "Сохранить изменения",
+ "aiChannelSetDefault": "Сделать основным",
+ "aiChannelNew": "Новый",
+ "aiChannelCopy": "Копировать",
+ "aiChannelDelete": "Удалить",
+ "aiChannelHint": "Переключайте сохранённые каналы в списке. Сохранение записывает ai.channels; канал по умолчанию используется в новых чатах и задачах без явного канала.",
+ "aiChannelSavedList": "Сохранённые каналы",
+ "aiChannelListAria": "Список каналов ИИ",
+ "aiChannelEditing": "Редактирование",
+ "aiChannelFormContext": "Редактируется выбранный канал",
+ "aiChannelFormContextHint": "Сохранение формы обновляет настройки этого канала.",
+ "aiChannelBulkProbe": "Проверить все",
+ "aiChannelBulkProbeTitle": "Проверить доступность всех заполненных каналов",
+ "aiChannelSelectAria": "Выбрать {name}",
+ "aiChannelDefaultBadge": "По умолчанию",
+ "aiChannelReady": "Готов",
+ "aiChannelReadyWithLatency": "Готов{latency}",
+ "aiChannelComplete": "Заполнен",
+ "aiChannelDraft": "Не заполнен",
+ "aiChannelDefaultMeta": "Канал по умолчанию",
+ "aiChannelCustomMeta": "Пользовательский канал",
+ "aiChannelOpenAICompat": "Совместим с OpenAI",
+ "aiChannelModelMissing": "Нет модели",
+ "aiChannelName": "Имя канала",
+ "aiChannelConnectionSection": "Подключение",
+ "aiChannelConnectionHint": "Сначала укажите провайдера, конечную точку, ключ и модель. Проверка подключения использует эти значения.",
+ "aiChannelModelSection": "Модель и лимиты",
+ "aiChannelModelHint": "Имя модели задаёт маршрутизацию. Лимиты токенов ограничивают контекст и один ответ.",
+ "aiChannelLimitsSection": "Лимиты",
+ "aiChannelLimitsHint": "Лимиты токенов ограничивают окно контекста и один ответ.",
+ "aiChannelUntitled": "Новый канал",
+ "aiChannelDeleteConfirm": "Удалить канал ИИ «{name}»?",
+ "aiChannelSaved": "Канал сохранён",
+ "aiChannelDefaultSaved": "Канал по умолчанию сохранён",
+ "aiChannelCount": "Сохранено каналов: {count}",
+ "aiChannelSaving": "Сохранение канала...",
+ "aiChannelNewUnsaved": "Новый канал ещё не сохранён. Заполните его и нажмите «Сохранить изменения».",
+ "aiChannelCopyUnsaved": "Копия канала ещё не сохранена. Проверьте её и нажмите «Сохранить изменения».",
+ "aiChannelDeleted": "Канал удалён",
+ "aiChannelProbeNoComplete": "Нет заполненного канала для проверки. Сначала укажите базовый URL, ключ API и модель.",
+ "aiChannelProbing": "Проверка каналов: {count}...",
+ "aiChannelProbeDone": "Проверка завершена: готово {ok}/{total}",
+ "apiProvider": "Провайдер API",
+ "providerOpenAI": "OpenAI / API, совместимый с OpenAI",
+ "providerClaude": "Claude (Anthropic Messages API)",
+ "visionProviderReuseOpenAI": "Использовать канал ИИ по умолчанию",
+ "fofaConfig": "Настройки FOFA",
+ "fofaConfigHint": "Для обнаружения и импорта активов; достаточно ключа API.",
+ "agentConfig": "Настройки агента",
+ "knowledgeConfig": "Настройки базы знаний",
+ "baseUrl": "Базовый URL",
+ "apiKey": "Ключ API",
+ "model": "Модель",
+ "openaiBaseUrlPlaceholder": "https://api.openai.com/v1",
+ "openaiApiKeyPlaceholder": "Введите ключ API OpenAI",
+ "modelPlaceholder": "gpt-4",
+ "fetchModels": "Загрузить список",
+ "modelsListFetching": "Загрузка списка моделей...",
+ "modelsListSelectPlaceholder": "Выберите модель",
+ "modelsListSuccess": "Загружено моделей: {count} — выберите в списке справа или введите в поле",
+ "modelsListFailed": "Не удалось загрузить список моделей",
+ "modelsListNeedApiKey": "Сначала введите ключ API",
+ "modelsListClaudeHint": "Claude не поддерживает автозагрузку списка моделей; введите имя модели вручную",
+ "maxTotalTokens": "Макс. токенов контекста",
+ "maxTotalTokensPlaceholder": "120000",
+ "maxTotalTokensHint": "Общий лимит для сжатия памяти и построения цепочки атаки. По умолчанию: 120000",
+ "maxCompletionTokens": "Макс. токенов ответа",
+ "maxCompletionTokensPlaceholder": "32768",
+ "maxCompletionTokensHint": "Максимум токенов одного ответа модели. По умолчанию: 32768",
+ "openaiReasoningTitle": "Настройки рассуждения",
+ "openaiReasoningHint": "Настройки рассуждения этого канала ИИ по умолчанию; настройки сессии чата могут их переопределить.",
+ "openaiReasoningProfile": "Профиль передачи",
+ "openaiReasoningAllowClient": "Разрешить странице чата переопределять параметры рассуждения",
+ "fofaBaseUrlPlaceholder": "https://fofa.info/api/v1/search/all (необязательно)",
+ "fofaBaseUrlHint": "Оставьте пустым для значения по умолчанию.",
+ "fofaApiKeyPlaceholder": "Введите ключ API FOFA",
+ "fofaApiKeyHint": "Хранится только в конфигурации сервера (config.yaml).",
+ "maxIterations": "Макс. итераций",
+ "iterationsPlaceholder": "30",
+ "toolWaitTimeoutSeconds": "Ожидание инструмента, с",
+ "toolWaitTimeoutSecondsHint": "Лимит ожидания инструментов за ход. По его достижении агент получает execution_id, а исполнитель продолжает работу; для продолжения ожидания вызовите wait_tool_execution. 0 — ждать до завершения.",
+ "externalMcpConcurrencyServer": "Лимит параллелизма на MCP",
+ "externalMcpConcurrencyServerHint": "Одновременные запуски инструментов на внешний сервер MCP. 0 — значение по умолчанию 2; -1 снимает лимит.",
+ "externalMcpConcurrencyTotal": "Общий параллелизм внешних MCP",
+ "externalMcpConcurrencyTotalHint": "Всего одновременных запусков инструментов внешних MCP. 0 — значение по умолчанию 16; -1 снимает лимит.",
+ "externalMcpCircuitThreshold": "Порог сбоев размыкания MCP",
+ "externalMcpCircuitThresholdHint": "Временно размыкает цепь после стольких подряд сбоев одного сервера MCP. 0 — значение по умолчанию 3; -1 отключает размыкание.",
+ "externalMcpCircuitCooldown": "Пауза размыкания MCP, с",
+ "externalMcpCircuitCooldownHint": "Сколько сервер остаётся в разомкнутом состоянии. 0 — 60 секунд по умолчанию.",
+ "enableMultiAgent": "Включить мультиагент Eino",
+ "enableMultiAgentHint": "После включения на странице чата доступен мультиагентный режим; субагенты задаются в multi_agent.sub_agents или каталоге agents/. Оркестрация настраивается ниже.",
+ "multiAgentOrchestration": "Оркестрация мультиагента",
+ "multiAgentOrchestrationHint": "deep = DeepAgent + task; plan_execute = plan / execute / replan; supervisor = маршрутизация экспертов (transfer, отдельные сценарии). Применяется после сохранения и применения.",
+ "multiAgentOrchDeep": "deep — DeepAgent (субагенты task)",
+ "multiAgentOrchPlanExecute": "plan_execute — plan / execute / replan",
+ "multiAgentOrchSupervisor": "supervisor — маршрутизация экспертов (transfer)",
+ "multiAgentPeLoop": "Лимит внешнего цикла plan_execute",
+ "multiAgentPeLoopPlaceholder": "0 — значение Eino по умолчанию (10)",
+ "multiAgentPeLoopHint": "Только для plan_execute; максимум циклов execute↔replan.",
+ "multiAgentRobotMode": "Режим диалога ботов по умолчанию",
+ "multiAgentRobotModeHint": "Режим выполнения сообщений ботов WeCom / DingTalk / Lark. Deep / Plan-Execute / Supervisor требуют включённого мультиагента.",
+ "agentAdvanced": "Дополнительные настройки",
+ "einoModelRetryMaxRetries": "Лимит встроенных повторов модели Eino",
+ "einoModelRetryMaxRetriesHint": "Встроенные попытки ModelRetry у ChatModelAgent. 0 — значение сервера по умолчанию 4. Покрывает 429/5xx, сетевые сбои и пустой ответ модели.",
+ "einoModelRetryMaxBackoffSec": "Макс. пауза встроенного повтора Eino, с",
+ "einoModelRetryMaxBackoffSecHint": "Верхняя граница экспоненциальной паузы встроенного повтора модели. 0 — 30 секунд по умолчанию.",
+ "einoModelFailoverChannels": "Каналы переключения модели Eino",
+ "einoModelFailoverChannelsPlaceholder": "backup-openai\nbackup-claude",
+ "einoModelFailoverChannelsHint": "Когда основной канал исчерпает встроенные повторы, переключение идёт по этим ID из ai.channels по порядку. По одному в строке; запятые тоже допустимы.",
+ "einoModelFailoverMaxRetries": "Лимит переключений модели Eino",
+ "einoModelFailoverMaxRetriesHint": "0 — перебрать все настроенные резервные каналы. Положительное значение ограничивает число попыток.",
+ "userLedgerMaxRunes": "Общий лимит символов журнала ввода",
+ "userLedgerMaxRunesHint": "Общий лимит исходного журнала ввода пользователя, добавляемого после сжатия; 0 — значение сервера по умолчанию. Исходные сообщения в БД не обрезаются.",
+ "userLedgerEntryMaxRunes": "Лимит символов ввода на сообщение",
+ "userLedgerEntryMaxRunesHint": "Видимый модели лимит каждого сообщения пользователя в журнале; 0 — значение сервера по умолчанию и не влияет на оригиналы в БД.",
+ "latestUserMaxRunes": "Лимит символов последнего ввода",
+ "latestUserMaxRunesHint": "Рабочий лимит текущего сообщения пользователя для модели; слишком длинный ввод сохраняется, а в контекст попадает только предпросмотр. 0 — значение сервера по умолчанию.",
+ "latestUserHeadRunes": "Начало предпросмотра последнего ввода",
+ "latestUserHeadRunesHint": "Символы начала слишком длинного текущего ввода, видимые модели.",
+ "latestUserTailRunes": "Конец предпросмотра последнего ввода",
+ "latestUserTailRunesHint": "Символы конца слишком длинного текущего ввода, видимые модели.",
+ "multiAgentBatchUse": "Мультиагент для пакетных очередей задач",
+ "multiAgentBatchUseHint": "Если включено, каждая подзадача очереди в управлении задачами выполняется через Eino DeepAgent (нужен мультиагент).",
+ "enableKnowledge": "Включить поиск по базе знаний",
+ "knowledgeBasePath": "Путь к базе знаний",
+ "knowledgeBasePathPlaceholder": "knowledge_base",
+ "knowledgeBasePathHint": "Относительно каталога файла конфигурации",
+ "embeddingConfig": "Настройки эмбеддингов",
+ "provider": "Провайдер",
+ "embeddingBaseUrlPlaceholder": "Оставьте пустым, чтобы использовать base_url OpenAI",
+ "embeddingApiKeyPlaceholder": "Оставьте пустым, чтобы использовать api_key OpenAI",
+ "modelName": "Имя модели",
+ "embeddingModelPlaceholder": "text-embedding-v4",
+ "retrievalConfig": "Настройки поиска",
+ "topK": "Результатов Top-K",
+ "topKPlaceholder": "5",
+ "topKHint": "Сколько результатов Top-K возвращать",
+ "similarityThreshold": "Порог сходства",
+ "similarityPlaceholder": "0.7",
+ "similarityHint": "Результаты ниже этого значения отбрасываются (0–1)",
+ "subIndexFilter": "Фильтр подиндекса (необязательно)",
+ "subIndexFilterPlaceholder": "например prod; должен совпадать с тегом sub_indexes индексации",
+ "subIndexFilterHint": "Пусто — без фильтра. Если задано, остаются строки, у которых sub_indexes содержит этот тег (старые строки с пустым sub_indexes тоже подходят).",
+ "ragPipelineHeader": "Конвейер RAG (MultiQuery + Rerank)",
+ "ragPipelineHint": "MultiQuery и rerank всегда включены: переформулировка запроса LLM → предварительная выборка и слияние векторов → HTTP rerank → дедупликация и обрезка по бюджету.",
+ "multiQueryMaxQueries": "Лимит вариантов переформулировки MultiQuery",
+ "multiQueryMaxQueriesPlaceholder": "4",
+ "multiQueryMaxQueriesHint": "Максимум вариантов поиска от LLM, включая перефразирование исходного запроса. Рекомендуется 3–4, максимум 8.",
+ "rerankProvider": "Провайдер Rerank",
+ "rerankProviderAuto": "Авто (определять по базовому URL)",
+ "rerankProviderCohere": "API, совместимый с Cohere",
+ "rerankProviderHint": "DashScope использует gte-rerank; другие совместимые конечные точки — /v1/rerank. Оставьте пустым, чтобы определить по базовому URL ниже.",
+ "rerankModel": "Модель Rerank (необязательно)",
+ "rerankModelPlaceholder": "Пусто: DashScope→gte-rerank, Cohere→rerank-multilingual-v3.0",
+ "rerankBaseUrl": "Базовый URL Rerank (необязательно)",
+ "rerankBaseUrlPlaceholder": "Оставьте пустым, чтобы использовать base_url эмбеддингов / OpenAI",
+ "rerankApiKey": "Ключ API Rerank (необязательно)",
+ "rerankApiKeyPlaceholder": "Оставьте пустым, чтобы использовать api_key эмбеддингов / OpenAI",
+ "rerankApiKeyHint": "При сбое rerank сохраняется порядок слияния; поиск продолжает работать.",
+ "postRetrieveHeader": "После поиска (дедупликация / бюджет)",
+ "postRetrieveDedupeAuto": "Результаты всегда дедуплицируются по нормализованному тексту (пробелы в теле схлопываются). Настройка не нужна.",
+ "prefetchTopK": "Кандидаты предварительной выборки (векторный этап)",
+ "prefetchTopKPlaceholder": "20",
+ "prefetchTopKHint": "Векторные кандидаты на вариант MultiQuery; 0 — встроенное max(top_k×4, 20) (максимум 200).",
+ "maxContextChars": "Макс. возвращаемых символов (Unicode)",
+ "maxContextCharsPlaceholder": "0",
+ "maxContextCharsHint": "0 — без ограничения; целые фрагменты сохраняются по рангу, пока не превышен бюджет.",
+ "maxContextTokens": "Макс. возвращаемых токенов",
+ "maxContextTokensPlaceholder": "0",
+ "maxContextTokensHint": "0 — без ограничения; оценка tiktoken (имя модели эмбеддингов, иначе cl100k_base).",
+ "indexConfig": "Настройки индекса",
+ "chunkStrategy": "Стратегия фрагментации",
+ "chunkStrategyMarkdownRecursive": "Заголовки Markdown, затем рекурсивно (рекомендуется)",
+ "chunkStrategyRecursive": "Только рекурсивно",
+ "chunkStrategyHint": "Как в конвейерах Eino: заголовки Markdown и рекурсия для документов; для обычного текста достаточно рекурсии.",
+ "requestTimeoutSeconds": "Таймаут HTTP эмбеддингов (секунды)",
+ "requestTimeoutPlaceholder": "120",
+ "requestTimeoutHint": "0 — таймаут HTTP-клиента эмбеддингов по умолчанию 120 с.",
+ "batchSize": "Размер пакета эмбеддингов",
+ "batchSizePlaceholder": "64",
+ "batchSizeHint": "Максимум текстов в одном запросе эмбеддингов (индексатор SQLite так же группирует запись).",
+ "preferSourceFile": "При индексации предпочитать файл на диске (Eino FileLoader)",
+ "preferSourceFileHint": "Если включено, содержимое берётся из file_path; при ошибке загрузки используется содержимое БД.",
+ "subIndexes": "Подиндексы Eino (через запятую)",
+ "subIndexesPlaceholder": "например prod, knowledge",
+ "subIndexesHint": "Передаётся в indexer.WithSubIndexes; сохраняется в столбце sub_indexes.",
+ "chunkSize": "Размер фрагмента",
+ "chunkSizePlaceholder": "512",
+ "chunkSizeHint": "Максимум токенов во фрагменте (по умолчанию 512)",
+ "chunkOverlap": "Перекрытие фрагментов",
+ "chunkOverlapPlaceholder": "50",
+ "chunkOverlapHint": "Перекрытие фрагментов в токенах (по умолчанию 50)",
+ "maxChunksPerItem": "Макс. фрагментов на элемент",
+ "maxChunksPlaceholder": "0",
+ "maxChunksHint": "Максимум фрагментов на элемент базы знаний (0 — без лимита)",
+ "maxRpm": "Макс. RPM",
+ "maxRpmPlaceholder": "0",
+ "maxRpmHint": "Максимум запросов в минуту (0 — без лимита)",
+ "rateLimitDelay": "Пауза ограничения (мс)",
+ "rateLimitPlaceholder": "300",
+ "rateLimitHint": "Пауза между запросами (мс); 0 — без лимита",
+ "maxRetries": "Макс. повторов",
+ "maxRetriesPlaceholder": "3",
+ "maxRetriesHint": "Повторы при ограничении частоты или ошибке сервера",
+ "retryDelay": "Пауза повтора (мс)",
+ "retryDelayPlaceholder": "1000",
+ "retryDelayHint": "Пауза между повторами (мс)",
+ "visionConfig": "Анализ изображений (analyze_image)",
+ "visionEnabled": "Включить инструмент анализа изображений analyze_image",
+ "visionEnabledHint": "При включении регистрируется инструмент MCP; изображения уходят только в один вызов VL; в контексте агента остаются текстовые сводки. Чтобы применить, сохраните и примените.",
+ "visionBaseUrlPlaceholder": "Оставьте пустым, чтобы использовать базовый URL OpenAI",
+ "visionApiKeyPlaceholder": "Оставьте пустым, чтобы использовать ключ API OpenAI",
+ "visionModel": "Модель анализа изображений",
+ "visionModelPlaceholder": "qwen-vl-max",
+ "visionModelRequired": "При включённом анализе изображений нужно имя модели",
+ "visionAdvanced": "Дополнительно: предобработка и лимиты",
+ "visionMaxImageBytes": "Макс. размер исходного файла (байт)",
+ "visionMaxDimension": "Макс. пикселей по длинной стороне",
+ "visionJpegQuality": "Качество JPEG",
+ "visionMaxPayloadBytes": "Макс. размер полезной нагрузки API (байт)",
+ "visionSkipPreprocessBytes": "Без обработки ниже (байт)",
+ "visionSkipPreprocessHint": "0 — всегда сжимать в JPEG; также должны соблюдаться лимиты длинной стороны и полезной нагрузки.",
+ "visionDetail": "Детализация изображения",
+ "visionTimeout": "Таймаут (секунды)",
+ "visionTestFillRequired": "Укажите модель анализа изображений и убедитесь, что ключ API доступен (или используется OpenAI)",
+ "testConnection": "Проверить подключение",
+ "testFillRequired": "Сначала укажите базовый URL, ключ API и модель",
+ "testing": "Проверка подключения...",
+ "testSuccess": "Подключение успешно",
+ "testFailed": "Подключение не удалось",
+ "testError": "Ошибка проверки",
+ "testErrorInvalidApiKey": "Ключ API недействителен или не авторизован. Проверьте правильность ключа.",
+ "testErrorUnauthorized": "Аутентификация не удалась. Проверьте ключ API.",
+ "testErrorForbidden": "Запрос отклонён. Проверьте права учётной записи или доступ к модели.",
+ "testErrorModelUnavailable": "Модель недоступна или имя модели указано неверно.",
+ "testErrorBaseUrl": "Базовый URL недоступен. Проверьте правильность конечной точки."
+ },
+ "settingsTerminal": {
+ "title": "Терминал",
+ "description": "Запуск команд на сервере для сопровождения и отладки. Команды выполняются на сервере; избегайте конфиденциальных и разрушительных операций.",
+ "terminalTab": "Терминал {{n}}",
+ "welcomeLine": "Терминал CyberStrikeAI — реальная сессия оболочки; вводите команды напрямую. Ctrl+L очищает экран",
+ "sessionClosed": "[Сессия закрыта]",
+ "connectionError": "[Ошибка подключения терминала]",
+ "connectFailed": "[Не удалось подключиться к службе терминала: {{msg}}]",
+ "closeTabTitle": "Закрыть",
+ "containerClickTitle": "Нажмите здесь, затем вводите команды",
+ "xtermNotLoaded": "Не удалось загрузить xterm.js. Обновите страницу или проверьте сеть.",
+ "close": "×",
+ "newTerminal": "+"
+ },
+ "settingsStorage": {
+ "title": "Очистка хранилища",
+ "subtitle": "Просмотр и освобождение места, занятого рабочими файлами: каталоги агента, обрезанный вывод инструментов, артефакты C2, вложения диалогов. Удаление необратимо — сначала сделайте предпросмотр.",
+ "diskUsed": "Занято на диске",
+ "runtimeTotal": "Рабочие данные",
+ "reclaimable": "Можно освободить",
+ "inodes": "Использование inode",
+ "inodesHint": "Сканирование может исчерпать inode большим числом мелких файлов",
+ "refresh": "Пересканировать",
+ "preview": "Предпросмотр",
+ "cleanNow": "Очистить",
+ "colCategory": "Категория",
+ "colRoot": "Каталог",
+ "colUnits": "Объекты",
+ "colSize": "Размер",
+ "colReclaimable": "Можно освободить",
+ "colRetention": "Хранить, дней",
+ "colEnabled": "Включено",
+ "loading": "Загрузка...",
+ "policyTitle": "Автоматическая очистка",
+ "autoClean": "Включить очистку по расписанию",
+ "intervalMinutes": "Интервал (минуты)",
+ "orphanGraceDays": "Льгота для осиротевших каталогов (дни)",
+ "activeGraceHours": "Защита активных сессий (часы)",
+ "policyHint": "Срок 0 отключает очистку по возрасту, но каталоги уже удалённых диалогов всё равно освобождаются. Сессии с недавней активностью не трогаются. Автоочистка по умолчанию выключена, поэтому обновление само ничего не удаляет.",
+ "save": "Сохранить политику",
+ "loadMetaFailed": "Не удалось загрузить политику очистки",
+ "loadStatusFailed": "Не удалось загрузить данные о месте",
+ "cleanupFailed": "Запрос очистки не выполнен",
+ "unavailable": "Недоступно",
+ "free": "свободно",
+ "itemsUnit": "объектов",
+ "daysUnit": "дн.",
+ "noCategories": "Нет категорий для очистки",
+ "orphanSuffix": "включая осиротевшие",
+ "skippedActive": "пропущены активные",
+ "defaultRetention": "По умолчанию",
+ "unused": "Не используется",
+ "scannedAt": "Сканирование",
+ "previewTitle": "Предпросмотр (ничего не удалено)",
+ "cleanDone": "Очистка завершена",
+ "nothingToClean": "Сейчас нет файлов, подходящих под политику очистки.",
+ "skippedActiveNote": "Пропущены сессии с недавней активностью: ",
+ "failed": "Операция не выполнена",
+ "confirmClean": "Будет безвозвратно удалено около {size} ({count} объектов) рабочих файлов. Сначала выполните «Предпросмотр». Продолжить?",
+ "saveFailed": "Не удалось сохранить",
+ "saved": "Политика очистки сохранена",
+ "cat.workspace": "Рабочий каталог агента",
+ "catHint.workspace": "Загрузки и результаты сканирования агента (tmp/workspace), отдельно для проекта и диалога.",
+ "cat.reduction": "Обрезанный вывод инструментов",
+ "catHint.reduction": "Файлы усечённого вывода слишком больших инструментов (tmp/reduction): один файл на запуск, данные производные.",
+ "cat.conversation_artifacts": "Артефакты диалогов",
+ "catHint.conversation_artifacts": "Транскрипты суммаризации и журнал слишком длинного ввода (data/conversation_artifacts).",
+ "cat.plantask": "Доски планов",
+ "catHint.plantask": "Доски планов мультиагента Eino (skills/.eino/plantask), данные производные.",
+ "cat.c2_artifacts": "Артефакты C2",
+ "catHint.c2_artifacts": "Снимки экрана C2, загрузки, переданные файлы и сгенерированные payload (tmp/c2). Очистка payload делает их ссылки недействительными.",
+ "cat.chat_uploads": "Вложения диалогов",
+ "catHint.chat_uploads": "Файлы, загруженные в диалогах (chat_uploads/<дата>/<диалог>).",
+ "cat.workflow_checkpoints": "Контрольные точки сценариев",
+ "catHint.workflow_checkpoints": "Контрольные точки запусков сценариев (data/workflow-checkpoints), нужны только для продолжения прерванного запуска.",
+ "cat.diagnostic_logs": "Диагностические журналы",
+ "catHint.diagnostic_logs": "Журналы диагностики с ротацией по дням (log/diagnostic-*.log)."
+ },
+ "settingsAudit": {
+ "title": "Журнал аудита",
+ "description": "Действия администратора платформы (вход, конфигурация, удаления). Не записывает содержимое чата, отдельные команды терминала и WebShell и вызовы инструментов.",
+ "filterCategory": "Категория",
+ "filterAction": "Действие",
+ "filterEvent": "Тип события",
+ "filterAllCategories": "Все категории",
+ "filterAllActions": "Все действия",
+ "filterCascadeHint": "Выберите категорию, чтобы фильтровать по действию",
+ "filterResult": "Результат",
+ "pageSize": "На странице",
+ "statTotal": "Всего по фильтру",
+ "statSuccess": "Успешно",
+ "statFailures": "Ошибки",
+ "statRecent7d": "За 7 дней",
+ "retentionHint": "Записи аудита хранятся {{days}} дн., затем удаляются автоматически.",
+ "disabledHint": "Ведение аудита отключено: новые действия не записываются.",
+ "filterSince": "С",
+ "filterUntil": "По",
+ "filterTimeZone": "Часовой пояс: {{tz}} (фильтр по локальному времени браузера)",
+ "datetimePlaceholder": "Выберите дату и время",
+ "timePresets": "Быстрый период",
+ "preset15m": "15 минут",
+ "preset1h": "1 час",
+ "preset24h": "24 часа",
+ "preset7d": "7 дней",
+ "presetToday": "Сегодня",
+ "pickerHour": "Час",
+ "pickerMinute": "Мин",
+ "pickerClear": "Очистить",
+ "pickerToday": "Сегодня",
+ "pickerConfirm": "ОК",
+ "filterQuery": "Ключевое слово",
+ "filterQueryPlaceholder": "Сообщение / ID ресурса / действие",
+ "colTime": "Время",
+ "colMessage": "Сообщение",
+ "colCategory": "Категория",
+ "colAction": "Действие",
+ "colResult": "Результат",
+ "colIp": "IP",
+ "colResource": "ID ресурса",
+ "cat": {
+ "auth": "Авторизация",
+ "config": "Конфигурация",
+ "terminal": "Терминал",
+ "c2": "C2",
+ "webshell": "WebShell",
+ "knowledge": "База знаний",
+ "conversation": "Диалог",
+ "vulnerability": "Уязвимость",
+ "asset": "Актив",
+ "externalMcp": "Внешний MCP",
+ "task": "Задачи",
+ "tool": "Инструменты",
+ "file": "Файлы",
+ "hitl": "HITL",
+ "role": "Роли",
+ "skill": "Навыки",
+ "agent": "Субагенты"
+ },
+ "act": {
+ "login": "Вход",
+ "logout": "Выход",
+ "login_failed": "Неудачный вход",
+ "password_change": "Смена пароля",
+ "change_password": "Сменить пароль",
+ "apply": "Применить конфигурацию",
+ "update": "Обновить",
+ "exec": "Выполнение в терминале",
+ "exec_stream": "Поток терминала",
+ "listener_create": "Создать слушатель",
+ "listener_delete": "Удалить слушатель",
+ "listener_start": "Запустить слушатель",
+ "listener_stop": "Остановить слушатель",
+ "session_delete": "Удалить сессию",
+ "task_create": "Создать задачу",
+ "task_cancel": "Отменить задачу",
+ "task_delete": "Удалить задачу",
+ "connection_create": "Создать подключение",
+ "connection_delete": "Удалить подключение",
+ "item_delete": "Удалить элемент базы знаний",
+ "index_build": "Построить индекс базы знаний",
+ "index_rebuild_full": "Полная пересборка индекса базы знаний",
+ "delete": "Удалить",
+ "delete_turn": "Удалить ход",
+ "create": "Создать",
+ "upsert": "Обновить внешний MCP",
+ "create_queue": "Создать пакетную очередь",
+ "start_queue": "Запустить пакетную очередь",
+ "delete_queue": "Удалить пакетную очередь",
+ "pause_queue": "Приостановить пакетную очередь",
+ "rerun_queue": "Повторить пакетную очередь",
+ "delete_batch_task": "Удалить подзадачу пакета",
+ "execution_delete": "Удалить выполнение",
+ "execution_delete_batch": "Удалить выполнения пакетом",
+ "upload": "Загрузить",
+ "decision": "Решение HITL",
+ "markdown_create": "Создать субагента",
+ "markdown_update": "Обновить субагента",
+ "markdown_delete": "Удалить субагента"
+ },
+ "openResource": "Открыть связанный ресурс",
+ "openResourceChat": "Открыть связанный ресурс (чат)",
+ "resourceIdLabel": "ID ресурса",
+ "resourceRemoved": "(ресурса больше нет)",
+ "filterAll": "Все",
+ "filterBtn": "Фильтр",
+ "resetBtn": "Сбросить",
+ "exportBtn": "Экспорт",
+ "exportJson": "Экспорт JSON",
+ "export": "Экспорт JSON",
+ "exportCsv": "Экспорт CSV",
+ "exportDone": "Экспорт завершён",
+ "loading": "Загрузка...",
+ "empty": "Нет записей аудита",
+ "result": {
+ "success": "успех",
+ "failure": "ошибка"
+ },
+ "msg": {
+ "auth": {
+ "login": "Вход выполнен",
+ "login_failed": "Вход не выполнен: неверный пароль",
+ "logout": "Выход выполнен",
+ "change_password": "Пароль входа изменён",
+ "change_password_failed": "Не удалось сменить пароль: текущий пароль неверен"
+ },
+ "config": {
+ "apply": "Конфигурация применена",
+ "update": "Конфигурация в памяти обновлена",
+ "apply_fail_kb_init": "Не удалось применить конфигурацию: инициализация базы знаний",
+ "apply_fail_kb_reinit": "Не удалось применить конфигурацию: повторная инициализация базы знаний",
+ "apply_fail_c2": "Не удалось применить конфигурацию: C2"
+ },
+ "conversation": {
+ "create": "Диалог создан",
+ "delete": "Диалог удалён",
+ "delete_turn": "Ход диалога удалён"
+ },
+ "c2": {
+ "listener_create": "Слушатель C2 создан",
+ "listener_delete": "Слушатель C2 удалён",
+ "listener_start": "Слушатель C2 запущен",
+ "listener_stop": "Слушатель C2 остановлен",
+ "session_delete": "Сессия C2 удалена",
+ "task_create": "Задача C2 создана",
+ "task_cancel": "Задача C2 отменена",
+ "task_delete": "Задачи C2 удалены (пакет)"
+ },
+ "webshell": {
+ "connection_create": "Подключение WebShell создано",
+ "connection_delete": "Подключение WebShell удалено"
+ },
+ "knowledge": {
+ "item_delete": "Элемент базы знаний удалён",
+ "index_build": "Индекс базы знаний построен",
+ "index_rebuild_full": "Индекс базы знаний полностью пересобран"
+ },
+ "vulnerability": {
+ "create": "Запись об уязвимости создана",
+ "update": "Запись об уязвимости обновлена",
+ "delete": "Запись об уязвимости удалена",
+ "delete_batch": "Записи об уязвимостях удалены (пакет)"
+ },
+ "external_mcp": {
+ "upsert": "Конфигурация внешнего MCP обновлена",
+ "delete": "Конфигурация внешнего MCP удалена"
+ },
+ "task": {
+ "create_queue": "Пакетная очередь задач создана",
+ "start_queue": "Пакетная очередь задач запущена",
+ "delete_queue": "Пакетная очередь задач удалена",
+ "pause_queue": "Пакетная очередь задач приостановлена",
+ "rerun_queue": "Пакетная очередь задач повторена",
+ "delete_batch_task": "Подзадача пакета удалена"
+ },
+ "tool": {
+ "execution_delete": "Запись выполнения инструмента удалена",
+ "execution_delete_batch": "Записи выполнения инструментов удалены (пакет)"
+ },
+ "file": {
+ "upload": "Вложение чата загружено",
+ "delete": "Вложение чата удалено"
+ },
+ "hitl": {
+ "decision": "Решение об одобрении HITL"
+ },
+ "role": {
+ "create": "Роль создана",
+ "update": "Роль обновлена",
+ "delete": "Роль удалена"
+ },
+ "skill": {
+ "create": "Навык создан",
+ "update": "Навык обновлён",
+ "delete": "Навык удалён"
+ },
+ "agent": {
+ "markdown_create": "Субагент Markdown создан",
+ "markdown_update": "Субагент Markdown обновлён",
+ "markdown_delete": "Субагент Markdown удалён"
+ }
+ },
+ "paginationShow": "{{start}}-{{end}} из {{total}}",
+ "detailTitle": "Подробности аудита",
+ "detailTime": "Время",
+ "detailCategory": "Категория",
+ "detailResult": "Результат",
+ "detailMessage": "Сообщение",
+ "detailSession": "Сессия"
+ },
+ "settingsSecurity": {
+ "changePasswordTitle": "Сменить пароль",
+ "changePasswordDesc": "После смены пароля войдите снова с новым паролем.",
+ "currentPassword": "Текущий пароль",
+ "currentPasswordPlaceholder": "Введите текущий пароль",
+ "newPassword": "Новый пароль",
+ "newPasswordPlaceholder": "Новый пароль (не менее 8 символов)",
+ "confirmPassword": "Подтвердите новый пароль",
+ "confirmPasswordPlaceholder": "Введите новый пароль ещё раз",
+ "clear": "Очистить",
+ "changePasswordBtn": "Сменить пароль"
+ },
+ "settingsRobotsExtra": {
+ "botCommandsTitle": "Команды бота",
+ "botCommandsEntryDesc": "Команды робота для личности, авторизации, диалогов, ролей, режимов, проектов и диагностики безопасности.",
+ "viewAllCommands": "Все команды",
+ "botCommandsDesc": "В чате можно отправлять следующие команды (поддерживаются китайский и английский):",
+ "botCmdCategoryGeneral": "Общие",
+ "botCmdCategoryIdentity": "Личность и авторизация",
+ "botCmdCategoryConversation": "Диалог",
+ "botCmdCategoryRole": "Роль",
+ "botCmdCategoryMode": "Режим",
+ "botCmdCategoryDiagnostics": "Диагностика и безопасность",
+ "botCmdCategoryProject": "Проект",
+ "botCmdHelp": "Показать эту справку",
+ "botCmdIdentity": "Показать отправителя платформы, режим авторизации и действующую личность RBAC",
+ "botCmdBindUser": "Привязать эту личность платформы к пользователю RBAC (только режим привязки пользователя)",
+ "botCmdUnbindUser": "Снять привязку этой личности платформы (только режим привязки пользователя; нужно подтверждение)",
+ "botAuthHint": "Режим служебной учётной записи не принимает команды привязки и отвязки; принимаются только отправители из настроенного списка.",
+ "botCmdList": "Список диалогов",
+ "botCmdSwitch": "Перейти к диалогу",
+ "botCmdNew": "Начать новый диалог",
+ "botCmdClear": "Очистить контекст",
+ "botCmdStatus": "Показать текущий диалог, режим, роль и проект",
+ "botCmdTask": "Показать статус текущей задачи и прошедшее время",
+ "botCmdRename": "Переименовать текущий диалог",
+ "botCmdPermissions": "Показать действующие бизнес-права",
+ "botCmdDoctor": "Проверить состояние ключевой конфигурации",
+ "botCmdConfirmation": "Подтвердить или отменить рискованное действие",
+ "botCmdStop": "Остановить выполняемую задачу",
+ "botCmdRoles": "Список ролей",
+ "botCmdRole": "Сменить роль",
+ "botCmdModes": "Список режимов диалога и текущий выбор",
+ "botCmdMode": "Сменить режим диалога",
+ "botCmdDelete": "Удалить диалог (нужно подтверждение)",
+ "botCmdVersion": "Показать версию",
+ "botCmdProjects": "Список проектов",
+ "botCmdNewProject": "Создать проект и привязать текущий диалог",
+ "botCmdBindProject": "Привязать текущий диалог к проекту",
+ "botCmdUnbindProject": "Отвязать проект от текущего диалога",
+ "botCommandsFooter": "Иначе текст отправляется ИИ с текущими правами RBAC привязанного пользователя или служебной учётной записи."
+ },
+ "mcpDetailModal": {
+ "blockReason": "Причина блокировки",
+ "title": "Сведения о вызове инструмента",
+ "execInfo": "Сведения о выполнении",
+ "tool": "Инструмент",
+ "status": "Статус",
+ "statusPending": "Ожидает",
+ "statusRunning": "Выполняется",
+ "statusCompleted": "Завершено",
+ "statusFailed": "Ошибка",
+ "statusCancelled": "Отменено",
+ "unknown": "Неизвестно",
+ "getDetailFailed": "Не удалось получить сведения",
+ "runningNoResponseYet": "Вывода пока нет; инструмент может ещё выполняться. Если он завис, нажмите «Остановить инструмент» ниже — остановится только этот вызов.",
+ "abortTitle": "Управление выполнением",
+ "abortHint": "Останавливает только этот вызов инструмента. Диалог или многошаговая задача продолжаются (в отличие от остановки всей задачи).",
+ "abortBtn": "Остановить инструмент",
+ "abortConfirm": "Остановить этот вызов инструмента? Диалог или итеративная задача не будут отменены.",
+ "abortSuccess": "Запрошена отмена; статус обновится, когда инструмент вернёт результат.",
+ "abortFailed": "Не удалось остановить инструмент",
+ "abortNoteModalTitle": "Остановить инструмент с примечанием",
+ "abortNoteModalHint": "Необязательно: почему вы остановили вызов или как модели продолжать. Модель сначала видит вывод инструмента, затем помеченный блок (USER INTERRUPT NOTE — это не сырой вывод инструмента), затем ваш текст. Оставьте пустым для обычной остановки.",
+ "abortNoteLabel": "Примечание (необязательно)",
+ "abortNotePlaceholder": "например, вывода достаточно — не ждать и продолжить…",
+ "abortNoteSubmit": "Остановить инструмент",
+ "abortNoteClose": "Отмена",
+ "execSuccessNoContent": "Выполнение успешно, отображаемого содержимого нет.",
+ "time": "Время",
+ "executionId": "ID выполнения",
+ "requestParams": "Параметры запроса",
+ "copyJson": "Копировать JSON",
+ "responseResult": "Ответ",
+ "copyContent": "Копировать содержимое",
+ "correctInfo": "Верные сведения",
+ "rawResponse": "Исходный ответ",
+ "errorInfo": "Сведения об ошибке",
+ "copyError": "Копировать ошибку",
+ "contentTruncated": "… (показано не полностью; полный файл — read_file по пути в persisted-output)"
+ },
+ "attackChainModal": {
+ "title": "Цепочка атаки",
+ "regenerate": "Сформировать заново",
+ "regenerateTitle": "Сформировать цепочку атаки заново (включая последний диалог)",
+ "exportPng": "Экспорт PNG",
+ "exportSvg": "Экспорт SVG",
+ "refreshTitle": "Обновить текущую цепочку атаки",
+ "nodesEdges": "Узлы: {{nodes}} | Рёбра: {{edges}}",
+ "searchPlaceholder": "Поиск узлов...",
+ "allTypes": "Все типы",
+ "target": "Цель",
+ "action": "Действие",
+ "vulnerability": "Уязвимость",
+ "allRisks": "Все риски",
+ "highRisk": "Высокий (80–100)",
+ "mediumHighRisk": "Повышенный (60–79)",
+ "mediumRisk": "Средний (40–59)",
+ "lowRisk": "Низкий (0–39)",
+ "resetFilter": "Сбросить фильтр",
+ "loading": "Загрузка...",
+ "riskLevel": "Уровень риска",
+ "lineMeaning": "Значение линий",
+ "blueLine": "Синяя: действие обнаруживает уязвимость",
+ "redLine": "Красная: даёт возможность или вносит вклад",
+ "grayLine": "Серая: логический порядок",
+ "nodeDetails": "Сведения об узле",
+ "closeDetails": "Закрыть сведения"
+ },
+ "externalMcpModal": {
+ "configJson": "JSON конфигурации",
+ "formatLabel": "Формат:",
+ "formatDesc": "Объект JSON; ключ = имя конфигурации, значение = конфигурация. Состояние задаётся кнопками «Старт» и «Стоп». Поддерживается синтаксис переменных окружения ${VAR} и ${VAR:-default}.",
+ "configExample": "Пример конфигурации:",
+ "stdioMode": "Режим stdio:",
+ "httpMode": "Режим HTTP:",
+ "sseMode": "Режим SSE:",
+ "placeholder": "{\n \"my-server\": {\n \"command\": \"python3\",\n \"args\": [\"${HOME}/mcp/server.py\"],\n \"env\": { \"API_KEY\": \"${API_KEY}\" },\n \"timeout\": 300\n }\n}",
+ "exampleStdio": "{\n \"my-server\": {\n \"command\": \"python3\",\n \"args\": [\"${HOME}/mcp/server.py\"],\n \"env\": { \"API_KEY\": \"${API_KEY}\", \"LOG_LEVEL\": \"${LOG_LEVEL:-INFO}\" },\n \"timeout\": 300\n }\n}",
+ "exampleHttp": "{\n \"remote-mcp\": {\n \"type\": \"http\",\n \"url\": \"https://mcp.example.com/mcp\",\n \"headers\": { \"Authorization\": \"Bearer ${MCP_TOKEN}\" }\n }\n}",
+ "exampleSse": "{\n \"sse-mcp\": {\n \"type\": \"sse\",\n \"url\": \"http://127.0.0.1:8081/mcp/sse\"\n }\n}",
+ "exampleDescription": "Пример описания",
+ "formatJson": "Форматировать JSON",
+ "loadExample": "Загрузить пример"
+ },
+ "skillModal": {
+ "addSkill": "Добавить навык",
+ "editSkill": "Изменить навык",
+ "skillName": "Имя навыка",
+ "skillNamePlaceholder": "например, sql-injection-testing",
+ "skillNameHint": "Строчные буквы, цифры, дефисы (имя Agent Skills)",
+ "description": "Описание",
+ "descriptionPlaceholder": "Краткое описание",
+ "descriptionHint": "Соответствует полю description в YAML файла SKILL.md (при создании и правке SKILL.md)",
+ "packageFiles": "Файлы пакета",
+ "packageFilesHint": "Нажмите файл, чтобы изменить; папки — только подписи, их нельзя открыть",
+ "folderHint": "Папка (не редактируется)",
+ "clickToEdit": "Нажмите, чтобы изменить этот файл",
+ "editingFile": "Редактирование",
+ "newFile": "Новый файл",
+ "newFilePlaceholder": "Относительный путь, например FORMS.md или scripts/extra.sh",
+ "newFilePathRequired": "Укажите путь нового файла",
+ "newFilePathInvalid": "Недопустимый путь (без .. и абсолютных путей)",
+ "noPackageFiles": "Файлы не указаны",
+ "unsavedSwitch": "Есть несохранённые изменения. Всё равно переключить файл?",
+ "contentLabel": "Содержимое",
+ "contentPlaceholder": "Измените выбранный файл…",
+ "contentPlaceholderAdd": "Только тело SKILL.md (front matter создаётся автоматически)…",
+ "bodyHintEdit": "Для SKILL.md это только тело (без заголовка ---); при сохранении оно объединяется с именем и описанием в стандартный SKILL.md.",
+ "contentHintAdd": "Создаёт стандартный SKILL.md (YAML front matter и тело). Папки открытых навыков можно класть в skills/ как есть.",
+ "contentHint": "См. формат Claude Agent Skills"
+ },
+ "knowledgeItemModal": {
+ "addKnowledge": "Добавить знание",
+ "editKnowledge": "Изменить знание",
+ "category": "Категория (тип риска)",
+ "categoryPlaceholder": "например, SQL-инъекция",
+ "title": "Название",
+ "titlePlaceholder": "Название элемента базы знаний",
+ "contentLabel": "Содержимое (Markdown)",
+ "contentPlaceholder": "Введите содержимое в Markdown..."
+ },
+ "batchManageModal": {
+ "title": "Управление диалогами · всего {{count}}",
+ "searchPlaceholder": "Поиск в истории",
+ "conversationName": "Название диалога",
+ "project": "Проект",
+ "noProject": "Без проекта",
+ "unknownProject": "Неизвестный проект",
+ "filterByProject": "Фильтр по проекту",
+ "lastTime": "Последняя активность",
+ "action": "Действие",
+ "selectAll": "Выбрать все",
+ "deleteSelected": "Удалить выбранные",
+ "confirmDeleteNone": "Выберите хотя бы один диалог для удаления",
+ "confirmDeleteN": "Удалить выбранные диалоги ({{count}})? Сообщения чата восстановить нельзя, но записанные уязвимости останутся в библиотеке уязвимостей.",
+ "deleteFailed": "Не удалось удалить",
+ "unnamedConversation": "Диалог без названия"
+ },
+ "contextMenu": {
+ "viewAttackChain": "Открыть цепочку атаки",
+ "viewVulnerabilities": "Открыть уязвимости",
+ "downloadMarkdown": "Скачать Markdown",
+ "downloadMarkdownSummary": "Сводка",
+ "downloadMarkdownFull": "Полностью",
+ "rename": "Переименовать",
+ "pinConversation": "Закрепить диалог",
+ "unpinConversation": "Открепить",
+ "batchManage": "Пакетное управление",
+ "deleteConversation": "Удалить диалог"
+ },
+ "batchImportModal": {
+ "hitlInherit": "Как в глобальных настройках",
+ "hitlOff": "Без одобрения",
+ "hitlHuman": "Одобрение человеком",
+ "hitlAgent": "Одобрение агентом",
+ "hitlReviewEdit": "Проверка и правка агентом",
+ "hitlPolicy": "Одобрение инструментов",
+ "hitlHint": "Применяется ко всем задачам этой очереди, включая добавленные позже. Используются глобальные белый список, стратегия аудита и тайм-аут.",
+ "hitlSubtaskHint": "Новые задачи используют настройку одобрения инструментов очереди. Измените её в сведениях об очереди.",
+ "title": "Новая задача",
+ "queueTitle": "Название очереди",
+ "queueTitlePlaceholder": "Введите название очереди (необязательно, для поиска и фильтра)",
+ "queueTitleHint": "Задайте название очереди пакетных задач, чтобы позже её было проще найти и вести.",
+ "role": "Роль",
+ "defaultRole": "По умолчанию",
+ "roleHint": "Выберите роль; все задачи выполнятся с её конфигурацией (промпт и инструменты).",
+ "project": "Проект",
+ "projectNone": "(Не привязан)",
+ "projectHint": "Можно привязать эту очередь к проекту; оставьте пустым, чтобы не привязывать.",
+ "agentMode": "Режим агента",
+ "agentModeSingle": "Один агент (Eino ADK)",
+ "agentModeMulti": "Несколько агентов (Eino)",
+ "agentModeHint": "Как в чате: один агент Eino (ADK) либо Deep / Plan-Execute / Supervisor (последние три требуют multi_agent.enabled).",
+ "concurrency": "Параллельность",
+ "concurrencyHint": "Число подзадач параллельно (1–8). По умолчанию 1 — последовательно; для задач со сканированием используйте 1–2.",
+ "scheduleMode": "Режим расписания",
+ "scheduleModeManual": "Вручную",
+ "scheduleModeCron": "Выражение cron",
+ "scheduleModeHint": "«Вручную» — для разового запуска; cron — для повторов. Сначала проверьте задачи вручную.",
+ "cronExpr": "Выражение cron",
+ "cronExprPlaceholder": "например, 0 */2 * * * (каждые 2 часа)",
+ "cronExprHint": "Стандартный cron из 5 полей: минута час день месяц день недели. Пример: `0 2 * * *` — каждый день в 02:00.",
+ "cronExprRequired": "Укажите выражение cron, если выбрано расписание cron",
+ "cronExprInvalid": "Неверный формат выражения cron. Нужно 5 полей (минута час день месяц день недели), например: 0 */2 * * *",
+ "executeNow": "Запустить сразу после создания",
+ "executeNowHint": "По умолчанию выключено. Если выключено, очередь остаётся в ожидании, и её можно запустить вручную позже.",
+ "tasksList": "Список задач (одна задача на строку)",
+ "tasksListPlaceholder": "Введите список задач, по одной на строку",
+ "tasksListPlaceholderExample": "Введите список задач, по одной на строку, например:\nПросканировать открытые порты 192.168.1.1\nПроверить, есть ли SQL-инъекция на https://example.com\nПеречислить поддомены example.com",
+ "tasksListHint": "Одна команда задачи на строку; система выполняет их через пул параллельности. Пустые строки игнорируются.",
+ "tasksListHintFull": "Подсказка: одна команда задачи на строку; система выполняет их через пул параллельности. Пустые строки игнорируются.",
+ "createQueue": "Создать очередь"
+ },
+ "batchQueueDetailModal": {
+ "title": "Сведения о пакетной очереди",
+ "addTask": "Добавить задачу",
+ "startExecute": "Старт",
+ "startExecuteNow": "Запустить сейчас (один раунд)",
+ "startExecuteNowConfirm": "Это очередь cron. «Старт» выполнит текущий раунд сразу, не дожидаясь следующего времени cron. Продолжить?",
+ "pauseQueue": "Приостановить очередь",
+ "rerunQueue": "Перезапустить",
+ "deleteQueue": "Удалить очередь",
+ "queueTitle": "Название задачи",
+ "role": "Роль",
+ "defaultRole": "По умолчанию",
+ "queueId": "ID очереди",
+ "status": "Статус",
+ "createdAt": "Создано",
+ "startedAt": "Начато",
+ "nextRunAt": "Следующий запуск",
+ "scheduleCronAuto": "Разрешить автозапуск cron",
+ "scheduleCronAutoHint": "Если выключено, выражение cron сохраняется, но очередь не запускается по расписанию; для ручного запуска используйте «Старт».",
+ "editSchedule": "Изменить расписание",
+ "editScheduleTitle": "Изменить конфигурацию расписания",
+ "editScheduleSuccess": "Расписание обновлено",
+ "editScheduleError": "Не удалось обновить расписание",
+ "editMetadata": "Изменить сведения",
+ "lastScheduleTriggerAt": "Последний запуск по расписанию",
+ "lastScheduleError": "Последняя ошибка расписания",
+ "lastRunError": "Сводка последней ошибки запуска",
+ "cronSchedulePausedBadge": "Расписание на паузе",
+ "scheduleToggleFailed": "Не удалось обновить переключатель расписания",
+ "completedAt": "Завершено",
+ "taskTotal": "Всего задач",
+ "concurrency": "Параллельность",
+ "concurrencyEditHint": "Нажмите, чтобы изменить. Нельзя менять, пока очередь выполняется.",
+ "taskList": "Список задач",
+ "startLabel": "Старт",
+ "completeLabel": "Завершение",
+ "errorLabel": "Ошибка",
+ "resultLabel": "Результат",
+ "technicalDetails": "Технические сведения (ID, время, расписание)"
+ },
+ "editBatchTaskModal": {
+ "title": "Изменить задачу",
+ "taskMessage": "Текст задачи",
+ "taskMessagePlaceholder": "Введите текст задачи"
+ },
+ "addBatchTaskModal": {
+ "title": "Добавить задачу",
+ "taskMessage": "Текст задачи",
+ "taskMessagePlaceholder": "Введите текст задачи",
+ "add": "Добавить"
+ },
+ "vulnerabilityModal": {
+ "project": "Проект",
+ "projectNone": "(Не привязан)",
+ "projectHint": "Привязанные находки попадают в list_vulnerabilities этого проекта; оставьте пустым, чтобы по возможности определить проект по диалогу.",
+ "conversationId": "ID диалога",
+ "conversationIdPlaceholder": "Введите ID диалога",
+ "conversationTag": "Метка диалога",
+ "conversationTagPlaceholder": "например, проверка A, еженедельный отчёт",
+ "taskTag": "Метка задачи",
+ "taskTagPlaceholder": "например, пакетное сканирование Q2, повторная проверка",
+ "title": "Название",
+ "titlePlaceholder": "/api/login уязвим к SQL-инъекции",
+ "description": "Описание",
+ "descriptionPlaceholder": "Кратко опишите суть, точку срабатывания, наблюдаемое аномальное поведение и почему это можно эксплуатировать.",
+ "severity": "Серьёзность",
+ "pleaseSelect": "Выберите",
+ "severityCritical": "Критическая",
+ "severityHigh": "Высокая",
+ "severityMedium": "Средняя",
+ "severityLow": "Низкая",
+ "severityInfo": "Информация",
+ "status": "Статус",
+ "statusOpen": "Открыта",
+ "statusConfirmed": "Подтверждена",
+ "statusFixed": "Исправлена",
+ "statusFalsePositive": "Ложное срабатывание",
+ "statusIgnored": "Проигнорирована",
+ "type": "Тип уязвимости",
+ "typePlaceholder": "например, SQL-инъекция, XSS, CSRF",
+ "target": "Цель",
+ "targetPlaceholder": "Укажите точно: URL, IP:порт, путь конечной точки и имя параметра.",
+ "preconditions": "Предусловия",
+ "preconditionsPlaceholder": "Состояние входа, права, учётная запись, заголовки и cookie, нужные данные, среда и версия; если не требуется — напишите «нет».",
+ "reproductionSteps": "Шаги воспроизведения",
+ "reproductionStepsPlaceholder": "Пронумеруйте шаги и укажите точку входа, параметр, полезную нагрузку, команду и точку наблюдения.",
+ "evidence": "Доказательства / POC",
+ "evidencePlaceholder": "Исходные запрос и ответ, команда curl или инструмента, заметки к снимкам, журналы, записи DNSLog и callback, результаты базы данных, пути файлов, метки времени и т. п.",
+ "impact": "Влияние",
+ "impactPlaceholder": "Опишите подтверждённое реальное влияние, например какие данные можно прочитать или изменить.",
+ "recommendation": "Рекомендация",
+ "recommendationPlaceholder": "Опишите конкретное исправление и критерии повторной проверки.",
+ "retestNotes": "Метод повторной проверки",
+ "retestNotesPlaceholder": "Как проверить исправление, включая ожидаемый код состояния, сообщение об ошибке или результат контроля доступа."
+ },
+ "vulnerabilityMd": {
+ "headingBasic": "Основные сведения",
+ "labelId": "ID уязвимости",
+ "labelSeverity": "Серьёзность",
+ "labelStatus": "Статус",
+ "labelType": "Тип",
+ "labelTarget": "Цель",
+ "labelConversationId": "ID диалога",
+ "labelTaskId": "ID задачи",
+ "labelTaskQueueId": "ID очереди задач",
+ "labelConversationTag": "Метка диалога",
+ "labelTaskTag": "Метка задачи",
+ "labelCreated": "Создано",
+ "labelUpdated": "Обновлено",
+ "headingDescription": "Описание",
+ "headingPreconditions": "Предусловия",
+ "headingReproductionSteps": "Шаги воспроизведения",
+ "headingEvidence": "Доказательства / POC",
+ "headingImpact": "Влияние",
+ "headingRecommendation": "Устранение",
+ "headingRetestNotes": "Метод повторной проверки"
+ },
+ "roleModal": {
+ "addRole": "Добавить роль",
+ "editRole": "Изменить роль",
+ "roleName": "Имя роли",
+ "roleNamePlaceholder": "Введите имя роли",
+ "roleDescription": "Описание роли",
+ "roleDescriptionPlaceholder": "Введите описание роли",
+ "roleIcon": "Значок роли",
+ "roleIconPlaceholder": "Введите эмодзи, например 🏆",
+ "roleIconHint": "Эмодзи в списке выбора роли.",
+ "userPrompt": "Пользовательский промпт",
+ "userPromptPlaceholder": "Добавляется перед сообщением пользователя...",
+ "userPromptHint": "Этот промпт добавляется перед сообщением пользователя и направляет ИИ. Системный промпт он не меняет.",
+ "relatedTools": "Связанные инструменты (необязательно)",
+ "defaultRoleToolsTitle": "Роль по умолчанию использует все инструменты",
+ "defaultRoleToolsDesc": "Роль по умолчанию использует все инструменты, включённые в управлении MCP.",
+ "searchToolsPlaceholder": "Поиск инструментов...",
+ "loadingTools": "Загрузка инструментов...",
+ "relatedToolsHint": "Фильтр «Связано / Не связано» выше учитывает флажки этой роли. Включение и выключение MCP задаётся в управлении MCP.",
+ "enableRole": "Включить эту роль",
+ "selectAll": "Выбрать все",
+ "deselectAll": "Снять выбор",
+ "roleNameRequired": "Укажите имя роли",
+ "roleNotFound": "Роль не найдена",
+ "firstRoleNoToolsHint": "Первая роль без выбранных инструментов по умолчанию использует все инструменты.",
+ "filterRoleAll": "Все",
+ "filterRoleOn": "Связано с ролью",
+ "filterRoleOff": "Не связано",
+ "statsPageLinked": "Отмечено на странице: {{current}} / {{total}}",
+ "statsPageLinkedTitle": "Флажок связывает инструмент с этой ролью; это не включение MCP",
+ "statsRoleLinked": "Отмечено для роли: {{current}} / {{max}}",
+ "statsRoleLinkedTitle": "Числитель: отмеченные инструменты (только включённые в MCP). Знаменатель: число включённых в MCP (как фильтр в управлении MCP)",
+ "statsRoleLinkedNoMax": "Отмечено для роли: {{current}} (переключите фильтр на «Все», уберите поиск и загрузите один раз, чтобы синхронизировать предел)",
+ "statsRoleLinkedNoMaxTitle": "Общее число включённых в MCP ещё не в кэше",
+ "statsRoleUsesAll": "Политика: все включённые в MCP ({{mcpOn}}) · всего в каталоге {{all}} (включая выключенные в MCP)",
+ "statsRoleUsesAllTitle": "Совпадает с числом «включено» в управлении MCP; явного списка инструментов нет",
+ "statsListScopeAll": "Список: все {{n}}",
+ "statsListScopeRoleOn": "Список: связано с этой ролью {{n}}",
+ "statsListScopeRoleOff": "Список: не связано с этой ролью {{n}}",
+ "usingAllEnabledTools": "(Используются все включённые инструменты)",
+ "loadToolsFailed": "Не удалось загрузить инструменты",
+ "cannotDeleteDefaultRole": "Нельзя удалить роль по умолчанию",
+ "usingAllTools": "Использовать все инструменты",
+ "andNMore": " и ещё {{count}}",
+ "toolsLabel": "Инструменты:",
+ "noTools": "Нет инструментов",
+ "paginationShow": "{{start}}–{{end}} из {{total}} инструментов",
+ "paginationSearch": " (поиск: «{{keyword}}»)",
+ "firstPage": "Первая",
+ "prevPage": "Назад",
+ "pageOf": "Стр. {{page}} / {{total}}",
+ "nextPage": "Далее",
+ "lastPage": "Последняя",
+ "mcpDisabledBadge": "MCP выкл.",
+ "mcpDisabledBadgeTitle": "Выключено в управлении MCP; флажок только задаёт связь с ролью — включите в MCP, чтобы выполнять",
+ "roleFilterOnBanner": "Эти инструменты отмечены и связаны с этой ролью (независимо от общего включения MCP).",
+ "roleFilterOffBanner": "Эти инструменты не отмечены и не связаны с этой ролью.",
+ "checkboxLinkTitle": "Отметьте, чтобы связать этот инструмент с этой ролью",
+ "bindWorkflow": "Привязать сценарий",
+ "bindWorkflowHint": "Если выбран сценарий, диалоги с этой ролью запускают его автоматически; поля сценария свободно задаются в JSON графа.",
+ "workflowPolicy": "Политика запуска сценария",
+ "workflowPolicyAuto": "Автозапуск",
+ "workflowPolicyOff": "Выкл.",
+ "noWorkflowBind": "Без сценария",
+ "workflowDisabledSuffix": " (отключён)"
+ },
+ "workflows": {
+ "title": "Сценарии",
+ "newGraph": "Новый сценарий",
+ "processLibrary": "Сценарии",
+ "nodeLibrary": "Библиотека узлов",
+ "emptyList": "Сценариев пока нет",
+ "statusEnabled": "Включён",
+ "statusDisabled": "Выключен",
+ "metaId": "ID",
+ "metaName": "Название",
+ "metaDescription": "Описание",
+ "metaEnabled": "Включён",
+ "metaIdHint": "После создания изменить нельзя; используется в API и при привязке ролей",
+ "metaModalTitle": "Сведения о сценарии",
+ "editMeta": "Изменить",
+ "untitled": "Сценарий без названия",
+ "toggleEnabled": "Включить или выключить",
+ "metaEnabledHint": "Выключенные сценарии роли не запускают автоматически",
+ "enabledUpdated": "Состояние включения обновлено",
+ "enabledUpdateFailed": "Не удалось обновить состояние включения",
+ "namePlaceholder": "Базовое веб-сканирование",
+ "descriptionPlaceholder": "Необязательно",
+ "toolbarLabel": "Инструменты правки сценария",
+ "canvasTools": "Инструменты холста",
+ "moreActions": "Ещё",
+ "deleteWorkflow": "Удалить сценарий",
+ "ai": {
+ "open": "Создать на естественном языке",
+ "title": "Создание сценария на естественном языке",
+ "subtitle": "ИИ готовит черновик для правки и не сохраняет и не запускает его автоматически.",
+ "promptLabel": "Запрос на сценарий",
+ "promptPlaceholder": "Пример: непрерывно отслеживать поддомены, сертификаты и открытые страницы домена и формировать отчёт при появлении новых активов",
+ "examplesLabel": "Примеры запросов",
+ "exampleDomain": "Мониторинг домена",
+ "exampleReport": "Одобрение отчёта",
+ "exampleTriage": "Разбор активов",
+ "includeObjective": "Также сформировать конфигурацию цели",
+ "allowSchedule": "Разрешить предложения запуска по расписанию",
+ "allowHighRisk": "Разрешить черновики узлов высокого риска",
+ "allowFallback": "Разрешить детерминированный запасной вариант при сбое ИИ",
+ "promptRequired": "Сначала опишите запрос на сценарий.",
+ "generateFailed": "Не удалось создать",
+ "generatedWithIssues": "В черновике остались ошибки проверки. Измените запрос и повторите попытку.",
+ "generate": "Создать черновик",
+ "generating": "Создание…",
+ "apply": "Показать на холсте",
+ "rendering": "Отрисовка…",
+ "applied": "Черновик сценария создан. Проверьте его перед сохранением.",
+ "canvasRendering": "ИИ собирает холст…",
+ "generatorAI": "Канал ИИ",
+ "generatorLLM": "Создано моделью",
+ "generatorServer": "Серверный генератор черновиков",
+ "generatorFallback": "Детерминированный запасной вариант",
+ "generatorUnknown": "Неизвестный генератор",
+ "llmTitle": "Создано каналом ИИ платформы по умолчанию и проверено по структуре",
+ "serverTitle": "Создано на сервере и прошло структурный аудит",
+ "fallbackTitle": "Использован детерминированный запасной вариант",
+ "serverFallbackNotice": "Генерация моделью недоступна: {{reason}}",
+ "preview": "Предпросмотр потока",
+ "resultNodes": "Узлы",
+ "resultEdges": "Связи",
+ "resultRepairs": "Раунды исправления",
+ "resultCapabilities": "Возможности узлов",
+ "resultTools": "Возможности инструментов",
+ "resultRisk": "Риск",
+ "resultSaveState": "Можно сохранить",
+ "yes": "Да",
+ "no": "Нет",
+ "missingFields": "Не хватает настроек",
+ "riskWarnings": "Предупреждения о риске",
+ "assumptions": "Допущения",
+ "capabilityTrace": "Цепочка возможностей",
+ "nodeGenerated": "Создано ИИ",
+ "nodeNeedsInput": "Нужен ввод",
+ "riskLow": "Низкий",
+ "riskMedium": "Средний",
+ "riskHigh": "Высокий",
+ "steps": {
+ "understand": "Разбор запроса",
+ "match": "Подбор инструментов",
+ "draft": "Создание узлов",
+ "audit": "Аудит безопасности"
+ },
+ "examples": {
+ "domainMonitor": "Непрерывно отслеживать поддомены, сертификаты и открытые страницы домена и формировать отчёт при появлении новых активов",
+ "reportReview": "Каждый день собирать сведения об угрозах, формировать отчёт и отправлять его ответственному за безопасность на одобрение",
+ "assetTriage": "Выделить активы высокого риска из результатов сканирования, убрать дубликаты и выдать рекомендации по устранению"
+ }
+ },
+ "audit": {
+ "title": "Аудит черновика",
+ "ready": "В этом черновике нет блокирующих пунктов",
+ "review": "Проверьте перед сохранением или запуском",
+ "aiNodes": "Узлы ИИ",
+ "needsInput": "Нужен ввод",
+ "highRisk": "Высокий риск",
+ "validation": "Ошибки структуры",
+ "nodeIssues": "Замечания по узлам",
+ "validationIssues": "Проверки структуры",
+ "saveConfirm": "В черновике не хватает настроек или есть узлы высокого риска. Всё равно сохранить как черновик?"
+ },
+ "package": {
+ "importLocal": "Импортировать локальный пакет",
+ "export": "Экспорт",
+ "title": "Импорт локального пакета",
+ "preflightHint": "Проверьте содержимое пакета и конфликты, прежде чем одобрить импорт",
+ "safeBadge": "Безопасная предпроверка",
+ "stepInspection": "Загрузка и предпроверка",
+ "stepInspectionHint": "Проверить формат, содержимое и версию",
+ "stepImport": "Устранить конфликт и импортировать",
+ "stepImportHint": "Выбрать стратегию и завершить импорт",
+ "stepsLabel": "Шаги импорта",
+ "packageEyebrow": "Локальный пакет сценария",
+ "packageTitle": "Выберите пакет для импорта",
+ "decisionEyebrow": "Стратегия импорта",
+ "pending": "Ожидает",
+ "ready": "Готово",
+ "footerNote": "Существующие сценарии не изменятся, пока вы не подтвердите",
+ "selectFile": "Выбрать локальный пакет",
+ "dropTitle": "Перетащите локальный пакет сюда",
+ "dropOr": "или",
+ "removeFile": "Убрать файл",
+ "noFile": "Файл не выбран",
+ "fileHint": "Только один пакет .csapkg.zip размером до 10 МиБ.",
+ "inspect": "Загрузить и выполнить предпроверку",
+ "inspecting": "Идёт безопасная предпроверка…",
+ "resolutionTitle": "Решение",
+ "confirmImport": "Подтвердить импорт",
+ "overwriteTitle": "Перезаписать локальный сценарий?",
+ "overwriteWarning": "Будут заменены название, описание, граф и состояние включения. Откат в этой версии недоступен.",
+ "overwriteCheck": "Подтверждаю перезапись текущего локального сценария",
+ "overwriteConfirm": "Перезаписать и импортировать",
+ "errors": {
+ "fileRequired": "Перед предпроверкой выберите локальный пакет сценария.",
+ "fileTooLarge": "Файл превышает лимит 10 МиБ. Выберите пакет меньшего размера.",
+ "invalidArchive": "Локальный пакет не является допустимым Zip-архивом, предпроверка невозможна.",
+ "unsupportedFormat": "Этот формат или версия локального пакета не поддерживается.",
+ "invalidManifest": "Манифест локального пакета недопустим, предпроверка невозможна.",
+ "checksumMismatch": "Проверка локального пакета не пройдена: файл мог быть повреждён или изменён.",
+ "multipleWorkflows": "В этой версии поддерживаются локальные пакеты только с одним сценарием.",
+ "workflowInvalid": "Определение сценария в локальном пакете недопустимо, предпроверка невозможна.",
+ "inspectionNotFound": "Запись предпроверки больше не существует. Загрузите пакет и выполните предпроверку снова.",
+ "inspectionExpired": "Срок предпроверки истёк. Загрузите пакет и выполните предпроверку снова.",
+ "inspectionConsumed": "Эта предпроверка уже использована при импорте и не может быть применена повторно.",
+ "conflictChanged": "Локальный сценарий изменился после предпроверки. Перед импортом выполните предпроверку снова.",
+ "idConflict": "Выбранное решение больше не соответствует текущему конфликту. Выполните предпроверку снова.",
+ "invalidAction": "Решение импорта недопустимо. Выберите его снова.",
+ "invalidRenameId": "Введите свободный новый ID сценария.",
+ "overwriteConfirmationRequired": "Подтвердите перезапись, прежде чем заменять локальный сценарий.",
+ "idempotencyKeyRequired": "Браузер не может создать идентификатор запроса импорта. Обновите страницу и повторите попытку.",
+ "idempotencyKeyReused": "Состояние запроса импорта недопустимо. Выполните предпроверку снова и повторите попытку.",
+ "importFailed": "Импорт не выполнен; локальный сценарий не изменён.",
+ "exportFailed": "Экспорт не выполнен. Повторите попытку позже.",
+ "workflowNotFound": "Сценарий не найден, экспорт невозможен.",
+ "generic": "Операция не выполнена. Повторите попытку позже.",
+ "serviceUnavailable": "Служба импорта не готова. Обновите страницу и повторите попытку."
+ },
+ "conflict": {
+ "identical": "Найден локальный сценарий с тем же ID и тем же содержимым; импорт будет пропущен.",
+ "idConflict": "Найден локальный сценарий с тем же ID, но другим содержимым. По умолчанию сохраняется локальная версия.",
+ "none": "Локальный сценарий с таким ID не найден. Можно создать новый сценарий."
+ },
+ "summary": {
+ "title": "Результат предпроверки",
+ "passed": "Проверки пройдены",
+ "workflowName": "Название сценария",
+ "unnamedWorkflow": "Сценарий без названия",
+ "sourceId": "ID исходного сценария",
+ "sourceRevision": "Исходная ревизия",
+ "graphSize": "Размер графа",
+ "graphSizeValue": "{{nodes}} узлов · {{edges}} связей",
+ "contentHash": "Хеш содержимого",
+ "expiresAt": "Срок предпроверки"
+ },
+ "resolution": {
+ "needInspection": "Сначала выберите локальный пакет и выполните предпроверку.",
+ "createTitle": "Можно создать",
+ "createHint": "Импорт создаст новый локальный сценарий.",
+ "identicalTitle": "Дубликат не нужен",
+ "identicalHint": "Такое содержимое уже есть. Повторный импорт не нужен.",
+ "keepExisting": "Оставить локальную версию",
+ "keepExistingHint": "Текущий локальный сценарий не меняется; запись импорта сохраняется.",
+ "revealRiskChoices": "Изменить решение",
+ "overwrite": "Перезаписать локальную версию",
+ "overwriteHint": "Заменить название, описание, граф и состояние включения; потребуется повторное подтверждение.",
+ "rename": "Сохранить с новым ID",
+ "renameHint": "Оставить текущий локальный сценарий и создать копию с новым ID.",
+ "newWorkflowId": "Новый ID сценария",
+ "newWorkflowIdPlaceholder": "Например: web-scan-basic-copy",
+ "continueOverwrite": "К подтверждению перезаписи",
+ "confirmRename": "Подтвердить сохранение с новым ID",
+ "confirmCreate": "Подтвердить создание",
+ "confirmComplete": "Подтвердить завершение"
+ },
+ "result": {
+ "created": "Импорт завершён: создан новый локальный сценарий.",
+ "overwritten": "Импорт завершён: локальный сценарий перезаписан.",
+ "renamed": "Импорт завершён: локальный сценарий создан с новым ID.",
+ "keptExisting": "Локальный сценарий оставлен без изменений.",
+ "skippedIdentical": "Такое локальное содержимое уже есть; импорт пропущен.",
+ "complete": "Импорт завершён.",
+ "completedAction": "Импорт завершён"
+ },
+ "exportSelectSaved": "Перед экспортом выберите сохранённый сценарий."
+ },
+ "connect": "Связать",
+ "connecting": "Связывание",
+ "deleteSelected": "Удалить выбранное",
+ "autoLayout": "Авторазмещение",
+ "dryRun": "Пробный запуск",
+ "canvasEmpty": "Перетащите узлы слева на холст или создайте сценарий на естественном языке",
+ "properties": "Свойства",
+ "nodeProperties": "Свойства узла",
+ "edgeProperties": "Свойства связи",
+ "deleteNode": "Удалить узел",
+ "deleteEdge": "Удалить связь",
+ "propertyEmpty": "Выберите узел или связь, чтобы изменить свойства",
+ "propLabel": "Название",
+ "propType": "Тип",
+ "customFields": "Дополнительные поля",
+ "addField": "Добавить поле",
+ "noCustomFields": "Нет дополнительных полей",
+ "nodes": {
+ "start": "Старт",
+ "tool": "Инструмент",
+ "agent": "Агент",
+ "condition": "Условие",
+ "hitl": "Одобрение",
+ "output": "Вывод",
+ "end": "Конец",
+ "default": "Узел"
+ },
+ "edges": {
+ "yes": "Да",
+ "no": "Нет"
+ },
+ "config": {
+ "inputKeys": "Входные переменные",
+ "mcpTool": "Инструмент MCP",
+ "selectTool": "Выберите инструмент",
+ "searchTool": "Поиск инструментов…",
+ "noToolsFound": "Нет подходящих инструментов",
+ "noToolsAvailable": "Нет доступных инструментов",
+ "toolDisabled": " (выключен)",
+ "argumentsTemplate": "Шаблон аргументов",
+ "argumentsStatic": "Аргументы инструмента (JSON)",
+ "timeoutSeconds": "Таймаут (секунды)",
+ "optional": "Необязательно",
+ "agentMode": "Режим агента",
+ "inputSource": "Источник ввода",
+ "inputBinding": "Привязка поля ввода",
+ "inputBindingHint": "from = источник данных, field = имя поля (например, output, message)",
+ "nodeInstruction": "Инструкция узла",
+ "instructionPlaceholder": "Опишите, что должен сделать этот узел",
+ "outputKey": "Имя выходной переменной",
+ "conditionExpression": "Выражение условия",
+ "conditionHint": "Узел вычисляет matched (true/false); исходящие связи задают ветви: первая — «Да», вторая — «Нет». На связи также можно указать {{previous.matched}} == \"true\".",
+ "conditionGuideTitle": "Доступный синтаксис",
+ "conditionGuideVars": "Переменные: {{previous.output}}, {{previous.matched}}, {{inputs.message}}, {{outputs.name}}, {{nodeId.output}}",
+ "conditionGuideOps": "Операторы: ==, !=, >, >=, <, <=, contains, matches, &&, ||",
+ "conditionGuideJson": "Вложенные поля: jsonpath(value, \"$.status\") или jq(value, \".severity\")",
+ "edgeCondition": "Условие связи",
+ "edgeBranch": "Ветвь условия",
+ "selectBranch": "Выбрать",
+ "edgeConditionHintCondition": "{{previous.matched}} == \"true\" (Да) или == \"false\" (Нет)",
+ "edgeConditionHintExample": "например, {{previous.output}} == \"ok\"",
+ "edgeBranchHint": "Первая связь от узла условия по умолчанию ведёт в ветвь «Да», вторая — в «Нет»; условия можно задать здесь.",
+ "joinStrategy": "Стратегия объединения",
+ "joinStrategyHint": "Если в узел входят несколько предыдущих, выберите, как собрать previous: объединить все, последний по порядку на холсте, первый непустой или прервать при ошибке.",
+ "hitlPrompt": "Текст одобрения",
+ "hitlPromptPlaceholder": "Одобрить продолжение",
+ "hitlReviewer": "Проверяющий",
+ "hitlInteractiveHint": "Выполнение останавливается на этом узле. Чтобы продолжить, одобрите или отклоните шаг через API или панель мониторинга.",
+ "promptBinding": "Привязка поля запроса",
+ "promptBindingHint": "Если текст пуст, текст одобрения берётся из привязанного поля",
+ "outputSource": "Источник переменной",
+ "sourceBinding": "Привязка поля вывода",
+ "sourceBindingHint": "Значение привязанного поля записывается в выходную переменную; заданное ниже статическое значение его заменяет",
+ "staticValue": "Статическое значение вывода",
+ "resultBinding": "Привязка итога",
+ "resultBindingHint": "Поле, которое показывается в итоге конечного узла",
+ "endTemplate": "Шаблон итога"
+ },
+ "defaultHitlPrompt": "Подтвердите, нужно ли продолжать этот шаг",
+ "nodeFallback": "Узел {{n}}",
+ "loadFailed": "Не удалось загрузить сценарии",
+ "saveFailed": "Не удалось сохранить сценарий",
+ "dryRunPrompt": "Сообщение для пробного запуска",
+ "dryRunPromptHint": "Это сообщение передаётся узлу «Старт» как вход message.",
+ "dryRunFailed": "Пробный запуск не выполнен",
+ "dryRunDone": "Пробный запуск завершён",
+ "dryRunTrace": "Трассировка пробного запуска",
+ "dryRunNoTrace": "Нет трассировки",
+ "deleteFailed": "Не удалось удалить сценарий",
+ "saved": "Сценарий сохранён",
+ "deleted": "Сценарий удалён",
+ "idNameRequired": "Нужны ID и название сценария",
+ "selectToDelete": "Выберите сценарий для удаления",
+ "confirmDelete": "Удалить сценарий {{id}}?",
+ "duplicateEdge": "Между этими узлами уже есть связь",
+ "connectModeOn": "Режим связи: нажмите исходный узел, затем целевой",
+ "connectModeOff": "Режим связи выключен",
+ "validation": {
+ "needStart": "Нужен хотя бы один узел «Старт»",
+ "needOutput": "Нужен хотя бы один узел «Вывод»",
+ "edgeSelfLoop": "Связь {{id}} не может указывать на саму себя",
+ "edgeSourceMissing": "Исходный узел связи {{id}} не существует",
+ "edgeTargetMissing": "Целевой узел связи {{id}} не существует",
+ "startIncoming": "У узла «Старт» {{label}} не должно быть входящих связей",
+ "outputOutgoing": "У узла «Вывод» {{label}} не должно быть исходящих связей",
+ "toolNeedsMcp": "Для узла инструмента {{label}} нужен инструмент MCP",
+ "conditionNeedsExpr": "Для узла условия {{label}} нужно выражение условия",
+ "conditionNeedsOutEdge": "Узлу условия {{label}} нужна хотя бы одна исходящая связь (ветвь «Да» или «Нет»)",
+ "conditionTooManyEdges": "У узла условия {{label}} должно быть не более двух исходящих связей («Да» и «Нет»); для третьей и следующих задайте условия связей",
+ "conditionBranchLabel": "Исходящие связи узла условия {{label}} нужно пометить «Да» или «Нет»",
+ "conditionBranchDuplicate": "У узла условия {{label}} повторяются ветви",
+ "nodeNeedsIncoming": "Узлу {{label}} нужна входящая связь",
+ "nodeNeedsOutgoing": "Узлу {{label}} нужна исходящая связь",
+ "nodeUnreachable": "До узла {{label}} нельзя дойти от «Старт»",
+ "graphCycle": "В сценарии есть цикл; граф должен быть без циклов",
+ "serverFailed": "Серверная проверка графа не пройдена",
+ "outputNeedsKey": "Для узла вывода {{label}} нужно имя выходной переменной"
+ }
+ },
+ "c2": {
+ "clipboardCopied": "Скопировано в буфер",
+ "common": {
+ "justNow": "Только что",
+ "minutesAgo": "{{n}} мин. назад",
+ "hoursAgo": "{{n}} ч. назад"
+ },
+ "fmt": {
+ "durationMs": "{{n}} мс",
+ "durationSec": "{{n}} с",
+ "durationMin": "{{n}} мин"
+ },
+ "files": {
+ "parent": "Вверх",
+ "refresh": "Обновить",
+ "upload": "Загрузить",
+ "uploading": "Загрузка {{name}} · {{percent}}%",
+ "uploadOk": "Загружено",
+ "uploadQueued": "Задача загрузки поставлена в очередь",
+ "uploadPendingApproval": "Задача загрузки ожидает одобрения HITL",
+ "uploadUnsupported": "Загрузка для этой сессии не поддерживается",
+ "uploadCurlBeacon": "Маяки Curl не загружают файлы; используйте HTTP Beacon",
+ "uploadTcpShell": "Это TCP reverse shell (bash/nc): только команды и скачивание. Для загрузки переподключитесь: (1) скомпилированным маяком CSB1 на том же слушателе или (2) маяком HTTP/HTTPS Beacon.",
+ "uploadTcpReverse": "Это TCP reverse shell (bash/nc): только команды и скачивание. Для загрузки переподключитесь: (1) скомпилированным маяком CSB1 на том же слушателе или (2) маяком HTTP/HTTPS Beacon.",
+ "loading": "Загрузка…",
+ "timeout": "Время загрузки файлов истекло",
+ "emptyDir": "Пустой каталог",
+ "colName": "Имя",
+ "colSize": "Размер",
+ "colMode": "Режим",
+ "colActions": "Действия",
+ "open": "Открыть",
+ "download": "Скачать",
+ "downloadOk": "Скачано",
+ "failed": "Ошибка"
+ },
+ "listeners": {
+ "title": "Управление слушателями",
+ "create": "Создать слушатель",
+ "name": "Имя",
+ "type": "Тип",
+ "bindHost": "Адрес привязки",
+ "bindPort": "Порт привязки",
+ "status": "Статус",
+ "remark": "Примечание",
+ "actions": "Действия",
+ "start": "Запустить",
+ "stop": "Остановить",
+ "delete": "Удалить",
+ "edit": "Изменить",
+ "running": "Работает",
+ "stopped": "Остановлен",
+ "placeholderName": "Имя слушателя",
+ "placeholderHost": "По умолчанию 127.0.0.1",
+ "placeholderPort": "Номер порта",
+ "placeholderRemark": "Примечание, необязательно",
+ "emptyTitle": "Слушателей пока нет",
+ "emptyHint": "Создайте первый слушатель C2 кнопкой ниже",
+ "headerCreateBtn": "+ Создать слушатель",
+ "modalCreateTitle": "Создать слушатель",
+ "placeholderNameExample": "напр. http-beacon-01",
+ "bindHintExternal": "0.0.0.0 — доступ извне",
+ "callbackHost": "Хост обратного вызова (необязательно)",
+ "callbackHostHint": "Публичный IP или имя хоста для полезных нагрузок и маяков; отдельно от адреса привязки. Если пусто, при генерации полезной нагрузки используется адрес привязки или автоопределение.",
+ "allowLegacyShell": "Разрешить незашифрованный классический reverse shell (только лаборатория)",
+ "allowLegacyShellHint": "По умолчанию выключено. Если включено, открытые TCP-подключения bash/nc регистрируют сессии и уязвимы для сканеров из интернета; в рабочей среде используйте зашифрованные сборки Beacon.",
+ "malleableProfile": "Malleable Profile",
+ "malleableProfileHint": "Необязательно. Заголовки ответов HTTP/HTTPS Beacon и маскировка трафика. Чтобы применить изменения, остановите слушатель и запустите его снова.",
+ "malleableProfileNone": "Нет",
+ "malleableProfileNonHttpHint": "Этот тип слушателя не использует Malleable Profile. Профиль можно привязать заранее, если позже переключитесь на HTTP/HTTPS Beacon.",
+ "malleableProfileEmptyListHint": "Сохранённых профилей пока нет. Создайте профиль в разделе C2 → Маскировка трафика / Malleable Profile и выберите его здесь.",
+ "placeholderRemarkLong": "Примечание, необязательно",
+ "editTitle": "Изменить слушатель",
+ "startedAt": "Запущен {{time}}",
+ "startedAtPrefix": "Запущен",
+ "statusError": "Ошибка",
+ "bindEndpoint": "Адрес прослушивания",
+ "callbackShort": "Обратный вызов",
+ "profileBadgeTitle": "Привязан Malleable Profile",
+ "confirmDelete": "Удалить этот слушатель? Все связанные сессии и задачи будут удалены.",
+ "toastFillRequired": "Заполните все обязательные поля",
+ "toastCreated": "Слушатель создан",
+ "toastStarted": "Слушатель запущен",
+ "toastStopped": "Слушатель остановлен",
+ "toastDeleted": "Слушатель удалён",
+ "toastUpdated": "Слушатель обновлён",
+ "loadingProfiles": "Загрузка Malleable Profile…",
+ "toastProfilesLoadFailed": "Не удалось загрузить Malleable Profile",
+ "submitCreate": "Создать",
+ "typeLabels": {
+ "http_beacon": "HTTP Beacon",
+ "https_beacon": "HTTPS Beacon",
+ "tcp_reverse": "TCP Reverse",
+ "websocket": "WebSocket"
+ }
+ },
+ "sessions": {
+ "title": "Управление сессиями",
+ "hostname": "Имя хоста",
+ "username": "Пользователь",
+ "os": "ОС",
+ "arch": "Арх.",
+ "ip": "IP-адрес",
+ "status": "Статус",
+ "active": "Активна",
+ "sleeping": "Сон",
+ "dead": "Нет связи",
+ "isAdmin": "Админ",
+ "pid": "PID",
+ "sleep": "Интервал сна",
+ "jitter": "Джиттер",
+ "firstSeen": "Первый контакт",
+ "lastCheckIn": "Последний отклик",
+ "selectSession": "Выбор сессии",
+ "terminal": "Терминал",
+ "files": "Файлы",
+ "tasks": "Задачи",
+ "info": "Сведения",
+ "execute": "Выполнить команду",
+ "kill": "Завершить сессию",
+ "cardDeleteSession": "Удалить",
+ "btnSleep": "Сон",
+ "emptyTitle": "Нет активных сессий",
+ "emptyHint": "Запустите слушатель и выполните полезную нагрузку на цели",
+ "unknownHost": "неизвестно",
+ "rootBadge": "ROOT",
+ "curlBeaconTitle": "Облегчённый маяк Curl",
+ "curlBeaconBody": "Эта сессия создана однострочником Curl: только heartbeat и задачи.\nИнтерактивный терминал и файлы требуют скомпилированного бинарника Beacon или слушателя TCP reverse.",
+ "infoSessionId": "ID сессии",
+ "infoImplantUuid": "UUID импланта",
+ "infoHostname": "Имя хоста",
+ "infoUsername": "Пользователь",
+ "infoOs": "ОС",
+ "infoArch": "Арх.",
+ "infoPid": "PID",
+ "infoProcess": "Процесс",
+ "infoAdmin": "Админ",
+ "infoInternalIp": "Внутренний IP",
+ "infoSleep": "Сон",
+ "infoSleepLine": "{{sec}} с (джиттер {{jitter}}%)",
+ "infoFirstSeen": "Первый контакт",
+ "infoLastCheckin": "Последний отклик",
+ "infoNote": "Заметка",
+ "infoNoteEmpty": "Нет заметок",
+ "infoCopy": "Копировать",
+ "noteEdit": "Изменить",
+ "notePlaceholder": "Заметка, чтобы опознать эту сессию…",
+ "toastNoteSaved": "Заметка сохранена",
+ "toastNoteTooLong": "Не больше 2000 символов",
+ "infoSectionIdentity": "Идентификация",
+ "infoSectionSystem": "Система",
+ "infoSectionNetwork": "Сеть и маяк",
+ "infoSectionTimeline": "Хронология",
+ "infoSectionNote": "Заметки",
+ "adminYes": "Да",
+ "adminNo": "Нет",
+ "promptSleepSeconds": "Интервал сна (секунды)",
+ "promptJitterPercent": "Джиттер, % (0–100)",
+ "sleepModalHint": "Сохраняется на сервере и ставит в очередь задачу сна. Имплант применит её при следующем опросе задач; следующие отклики сохранят эти параметры.",
+ "sleepModalTitle": "Интервал маяка",
+ "sleepModalCurrent": "Сейчас {{sec}} с · джиттер {{jitter}}%",
+ "sleepModalPreview": "Ориентир {{min}}–{{max}} с",
+ "sleepModalPresets": "Шаблоны",
+ "toastSleepInvalid": "Интервал сна — не меньше 1 с",
+ "toastSleepUpdated": "Параметры сна обновлены",
+ "confirmExitSession": "Отправить этой сессии команду выхода?",
+ "confirmDeleteSession": "Удалить эту сессию и связанные задачи и файлы с сервера? (Импланту команда выхода не отправляется; чтобы завершить агент, используйте «Завершить сессию».)",
+ "toastExitSent": "Команда выхода отправлена",
+ "toastSessionDeleted": "Запись сессии удалена",
+ "terminalWelcome": "Терминал CyberStrikeAI C2 — Enter: выполнить; ↑↓: история; Ctrl+L: очистить; Ctrl+C: отменить ввод",
+ "termStatusReady": "Готов",
+ "termStatusExec": "Выполнение…",
+ "termStatusErr": "Ошибка",
+ "termStatusTimeout": "Тайм-аут",
+ "termNoSession": "Ошибка: сессия не выбрана",
+ "termWaitTimeout": "[Время ожидания результата истекло]",
+ "termCleared": "Терминал очищен",
+ "termNoSelection": "Текст не выделен",
+ "termWaitFinish": "Дождитесь завершения текущей команды",
+ "termCtrlC": "Удалённое прерывание в этой версии не поддерживается",
+ "termQueued": "[Команда в очереди — выполнится после текущей задачи]",
+ "clearTerminal": "Очистить",
+ "batchDelete": "Удалить выбранные",
+ "deleteFiltered": "Удалить отфильтрованные",
+ "selectAll": "Выбрать все",
+ "filterAllStatus": "Все статусы",
+ "filterAllListeners": "Все слушатели",
+ "filterSearchPlaceholder": "Имя хоста, пользователь или IP",
+ "filterApply": "Фильтр",
+ "filterReset": "Сбросить",
+ "filterSuspicious": "Вероятные ложные срабатывания",
+ "filterCount": "Всего {{n}}, выбрано {{selected}}",
+ "emptyFilter": "Нет сессий по текущим фильтрам",
+ "listEmpty": "Нет сессий",
+ "selectPromptTitle": "Выберите сессию",
+ "selectPromptHint": "Нажмите сессию в списке слева, чтобы открыть терминал, файлы и задачи.",
+ "confirmBatchDelete": "Удалить выбранные сессии ({{n}})? Связанные задачи и записи файлов будут удалены.",
+ "confirmDeleteFiltered": "Удалить все сессии из текущей выборки ({{n}})?",
+ "toastSelectFirst": "Выберите хотя бы одну сессию",
+ "toastBatchDeleted": "Удалено сессий: {{n}}"
+ },
+ "tasks": {
+ "title": "Управление задачами",
+ "taskId": "ID задачи",
+ "type": "Тип",
+ "status": "Статус",
+ "result": "Результат",
+ "error": "Ошибка",
+ "duration": "Длительность",
+ "createdAt": "Создано",
+ "queued": "В очереди",
+ "sent": "Отправлена",
+ "running": "Выполняется",
+ "success": "Успешно",
+ "failed": "Сбой",
+ "cancelled": "Отменена",
+ "viewResult": "Результат",
+ "cancel": "Отменить задачу",
+ "refresh": "Обновить задачи",
+ "pending": "Ожидание",
+ "emptyAll": "Задач пока нет",
+ "emptySession": "Для этой сессии задач нет",
+ "sessionTaskHistory": "История задач",
+ "sessionTaskCount": "Задач: {{n}}",
+ "colTask": "Задача",
+ "colSession": "Сессия",
+ "colType": "Тип",
+ "colCommand": "Команда",
+ "colStatus": "Статус",
+ "colDuration": "Длительность",
+ "colCreated": "Создано",
+ "colActions": "Действия",
+ "view": "Просмотр",
+ "cancelBtn": "Отмена",
+ "modalTitle": "Сведения о задаче",
+ "labelId": "ID",
+ "labelSession": "Сессия",
+ "labelType": "Тип",
+ "labelCommand": "Команда",
+ "labelPayload": "Полезная нагрузка",
+ "labelStatus": "Статус",
+ "labelCreated": "Создано",
+ "labelSent": "Отправлено",
+ "labelCompleted": "Завершено",
+ "labelDuration": "Длительность",
+ "labelError": "Ошибка",
+ "labelResult": "Вывод",
+ "toastCancelled": "Задача отменена",
+ "batchDelete": "Удалить выбранные",
+ "selectAll": "Выбрать все на странице",
+ "deleteOne": "Удалить задачу",
+ "deleteBtn": "Удалить",
+ "confirmDeleteOne": "Удалить эту запись задачи с сервера? Это нельзя отменить.",
+ "confirmBatchDelete": "Удалить выбранные записи задач ({{n}})? Это нельзя отменить.",
+ "toastSelectFirst": "Сначала выберите задачи",
+ "toastDeleted": "Удалено задач: {{n}}",
+ "paginationShow": "{{start}}-{{end}} из {{total}}",
+ "paginationPerPage": "На странице",
+ "paginationFirst": "Первая",
+ "paginationPrev": "Назад",
+ "paginationPage": "Стр. {{current}} / {{total}}",
+ "paginationNext": "Далее",
+ "paginationLast": "Последняя",
+ "statTotal": "Отфильтровано",
+ "timePresets": "Быстро",
+ "preset15m": "15 минут",
+ "preset1h": "1 час",
+ "preset24h": "24 часа",
+ "preset7d": "7 дней",
+ "presetAll": "Все",
+ "filterAllStatuses": "Все статусы",
+ "filterAllTypes": "Все типы",
+ "filterSession": "ID сессии",
+ "filterSessionPlaceholder": "s_xxxxxxxx",
+ "applyFilters": "Применить",
+ "resetFilters": "Сбросить"
+ },
+ "payloads": {
+ "386": "386",
+ "title": "Генератор полезной нагрузки",
+ "oneliner": "Однострочник",
+ "build": "Сборка маяка",
+ "listener": "Слушатель",
+ "kind": "Вариант",
+ "host": "Хост обратного вызова",
+ "generate": "Сгенерировать",
+ "copy": "Копировать",
+ "copied": "Скопировано",
+ "os": "Целевая ОС",
+ "arch": "Целевая архитектура",
+ "buildBtn": "Собрать",
+ "building": "Сборка...",
+ "download": "Скачать",
+ "linux": "Linux",
+ "windows": "Windows",
+ "darwin": "macOS",
+ "amd64": "AMD64",
+ "arm64": "ARM64",
+ "arm": "ARM",
+ "onelinerDesc": "Однострочный reverse shell для цели (Bash / Python / PowerShell / Curl).",
+ "buildDesc": "Кросс-компиляция полного бинарника Beacon для Linux / Windows / macOS.",
+ "hostOptional": "Хост обратного вызова (необязательно)",
+ "placeholderListenerHost": "Пусто: хост обратного вызова слушателя, иначе адрес привязки",
+ "generateOnelinerBtn": "Сгенерировать однострочник",
+ "buildBeaconBtn": "Собрать маяк",
+ "loopbackBeaconWarning": "127.0.0.1 работает, только если маяк и C2 на одном хосте и в одном сетевом пространстве имён (простой локальный тест). Если C2 в Docker, а маяк на хосте, 127.0.0.1 указывает на loopback хоста, а не контейнера: укажите опубликованный порт и LAN-IP хоста или host.docker.internal. Для удалённых целей привяжите 0.0.0.0 и пересоберите.",
+ "noBeaconListenersTcpOnly": "tcp_reverse поддерживает скомпилированный Beacon (TCP и кадрирование CSB1); классический shell — через однострочник выше",
+ "noListenersOption": "Нет слушателей",
+ "noKindOption": "Нет вариантов",
+ "toastLoadListenersFail": "Не удалось загрузить слушатели: {{msg}}",
+ "toastPickListener": "Сначала выберите слушатель",
+ "toastOnelinerFail": "Не удалось сгенерировать однострочник: {{msg}}",
+ "toastBuildFail": "Не удалось собрать маяк: {{msg}}",
+ "toastBuildSuccess": "Сборка выполнена: {{bytes}} байт",
+ "buildSuccessTitle": "Сборка выполнена",
+ "buildMetaOsArch": "ОС: {{os}} | Арх.: {{arch}}",
+ "buildSize": "Размер: {{bytes}} байт",
+ "clickToCopyTitle": "Нажмите, чтобы скопировать",
+ "toastDownloadQueued": "Задача скачивания поставлена в очередь"
+ },
+ "events": {
+ "title": "Аудит событий",
+ "level": "Уровень",
+ "category": "Категория",
+ "message": "Сообщение",
+ "time": "Время",
+ "info": "Инфо",
+ "warn": "Предупреждение",
+ "critical": "Критический",
+ "listener": "Слушатель",
+ "session": "Сессия",
+ "task": "Задача",
+ "payload": "Полезная нагрузка",
+ "opsec": "OPSEC",
+ "refresh": "Обновить",
+ "empty": "Событий пока нет",
+ "batchDelete": "Удалить выбранные",
+ "selectAll": "Выбрать все на странице",
+ "deleteOne": "Удалить",
+ "confirmDeleteOne": "Удалить это событие? Это нельзя отменить.",
+ "confirmBatchDelete": "Удалить выбранные события ({{n}})? Это нельзя отменить.",
+ "toastSelectFirst": "Сначала выберите события",
+ "toastDeleted": "Удалено событий: {{n}}",
+ "paginationShow": "{{start}}-{{end}} из {{total}}",
+ "paginationPerPage": "На странице",
+ "paginationFirst": "Первая",
+ "paginationPrev": "Назад",
+ "paginationPage": "Стр. {{current}} / {{total}}",
+ "paginationNext": "Далее",
+ "paginationLast": "Последняя",
+ "statTotal": "Отфильтровано",
+ "timePresets": "Быстро",
+ "preset15m": "15 минут",
+ "preset1h": "1 час",
+ "preset24h": "24 часа",
+ "preset7d": "7 дней",
+ "presetAll": "Все",
+ "filterAllLevels": "Все уровни",
+ "filterAllCategories": "Все категории",
+ "filterSession": "ID сессии",
+ "filterSessionPlaceholder": "s_xxxxxxxx",
+ "applyFilters": "Применить",
+ "resetFilters": "Сбросить",
+ "colSession": "Сессия",
+ "colTask": "Задача",
+ "colId": "ID события",
+ "detailTitle": "Сведения о событии",
+ "detailData": "Дополнительные данные"
+ },
+ "profiles": {
+ "title": "Malleable Profile",
+ "name": "Имя",
+ "userAgent": "User-Agent",
+ "uris": "Пути URI",
+ "headers": "Заголовки",
+ "jitter": "Диапазон джиттера",
+ "create": "Создать профиль",
+ "createBtn": "+ Создать профиль",
+ "delete": "Удалить",
+ "empty": "Профилей пока нет",
+ "defaultValue": "По умолчанию",
+ "modalCreateTitle": "Создать Malleable Profile",
+ "profileNameLabel": "Имя профиля",
+ "placeholderProfileName": "напр. cdn-fronting",
+ "hintUa": "Свой User-Agent для HTTP-запросов Beacon",
+ "labelBeaconUris": "URI Beacon (по одному в строке)",
+ "hintUris": "Пути URI, по которым маяк выходит на связь",
+ "labelJitterMin": "Джиттер, мин. (мс)",
+ "labelJitterMax": "Джиттер, макс. (мс)",
+ "labelRespHeaders": "Свои заголовки ответа (JSON)",
+ "hintHeaders": "Заголовки ответа HTTP, имитирующие обычный сервер",
+ "toastNameRequired": "Укажите имя профиля",
+ "toastInvalidHeadersJson": "Некорректный JSON в заголовках ответа",
+ "toastCreated": "Профиль создан",
+ "toastDeleted": "Профиль удалён",
+ "confirmDelete": "Удалить этот профиль?",
+ "submitCreate": "Создать"
+ }
+ },
+ "rbac": {
+ "title": "Доступ к платформе",
+ "pageSubtitle": "Участники, роли и доступные данные в одном месте.",
+ "createUser": "Добавить участника",
+ "memberManagement": "Участники",
+ "memberManagementHint": "Назначение ролей и области данных участникам",
+ "roleManagement": "Роли",
+ "roleManagementHint": "Наборы прав и видимость по умолчанию",
+ "users": "Пользователи",
+ "usersHint": "Выберите учётную запись, чтобы управлять ролями и областью ресурсов.",
+ "searchUsers": "Имя пользователя или отображаемое имя",
+ "metricUsers": "Учётные записи",
+ "metricEnabled": "Включено",
+ "metricRoles": "Роли",
+ "metricAssignments": "Выдачи ресурсов",
+ "metricUsersPill": "учётные записи",
+ "metricEnabledPill": "включённые учётные записи",
+ "metricRolesPill": "роли",
+ "metricAssignmentsPill": "выдачи",
+ "selectUser": "Выберите пользователя",
+ "selectUserHint": "Выберите пользователя платформы, чтобы изменить роли, статус и выдачи ресурсов.",
+ "createRole": "Новая роль",
+ "tabRoles": "Роли и права",
+ "tabAssignments": "Выдачи ресурсов",
+ "tabAudit": "История изменений",
+ "roleMembership": "Роли участника",
+ "roleMembershipHint": "Роли определяют допустимые действия, область ресурсов — видимые данные.",
+ "unsavedChanges": "Есть несохранённые изменения",
+ "saveRoleChanges": "Сохранить роли",
+ "resourceType": "Тип ресурса",
+ "resourceId": "ID ресурса",
+ "resourceIdPlaceholder": "Введите ID ресурсов через запятую",
+ "searchResource": "Поиск реальных ресурсов",
+ "searchResourcePlaceholder": "Поиск по имени или ID",
+ "assignSelected": "Выдать выбранное",
+ "advancedAssignment": "Дополнительно: указать ID",
+ "advancedAssignmentHint": "Для известных ID из журналов или API. При отправке существование ресурса всё равно проверяется.",
+ "availableResources": "Доступные ресурсы",
+ "availableResourcesHint": "Выбор на странице сохраняется при перелистывании",
+ "currentAssignments": "Текущие явные выдачи",
+ "allResourceTypes": "Все типы",
+ "searchAssignments": "Имя или ID выданного ресурса",
+ "items": "эл.",
+ "assignmentHelp": "Явные выдачи нужны только ролям «Назначенные ресурсы». Выдача проекта также охватывает диалоги и уязвимости этого проекта.",
+ "assignResource": "Выдать",
+ "roleCatalog": "Каталог ролей",
+ "roleCatalogHint": "Сначала задайте права и область ресурсов, затем назначьте роль участникам.",
+ "searchRoles": "Поиск ролей или прав",
+ "filterRoles": "Фильтр ролей",
+ "userModalTitle": "Пользователь платформы",
+ "username": "Имя пользователя",
+ "displayName": "Отображаемое имя",
+ "password": "Пароль",
+ "passwordPlaceholder": "При изменении оставьте пустым, чтобы не менять",
+ "enabled": "Включить учётную запись",
+ "roles": "Роли",
+ "roleModalTitle": "Роль платформы",
+ "roleName": "Имя роли",
+ "description": "Описание",
+ "scope": "Область ресурсов",
+ "permissions": "Права",
+ "searchPermissions": "Поиск прав, напр. project:read",
+ "selectVisible": "Выбрать видимые",
+ "clearVisible": "Снять видимые",
+ "deleteUserConfirm": "Удалить этого пользователя платформы? Активные сессии будут сразу отозваны.",
+ "deleteBuiltinUser": "Встроенного администратора удалить нельзя",
+ "deleteRoleConfirm": "Удалить эту роль платформы?",
+ "scopeHint": "Область ресурсов и права на операции вместе определяют фактический доступ.",
+ "builtinAccount": "Встроенная учётная запись",
+ "statusEnabled": "Включена",
+ "statusDisabled": "Отключена",
+ "noRoleAssigned": "Роль не назначена",
+ "noExplicitGrant": "Нет явных выдач",
+ "notConfigured": "Не настроено",
+ "noRoleDescription": "Нет описания роли",
+ "noDescription": "Нет описания",
+ "systemBuiltin": "Системная роль",
+ "customRole": "Своя",
+ "operationPermissions": "Права на операции",
+ "assignedMembers": "Участники",
+ "viewPermissions": "Смотреть права",
+ "effectivePermissions": "Действующие права",
+ "effectiveScope": "Действующая область",
+ "revoke": "Отозвать",
+ "retry": "Повторить",
+ "alreadyAssigned": "Назначено",
+ "loadingResources": "Загрузка реальных ресурсов…",
+ "viewSystemRole": "Просмотр системной роли",
+ "editRole": "Изменить роль",
+ "resourceCount": "Ресурсов: {{count}}",
+ "permissionCount": "Прав: {{count}}",
+ "userCount": "Пользователей: {{count}}",
+ "confirmDiscardSwitchUser": "Изменения ролей не сохранены. Отменить их и переключить участника?",
+ "confirmDiscardEditUser": "Изменения ролей не сохранены. Отменить их и изменить профиль участника?",
+ "confirmDiscardSwitchTab": "Изменения ролей не сохранены. Отменить их и переключить вкладку?",
+ "confirmDiscardResourceType": "Смена типа ресурса сбросит текущий выбор. Продолжить?",
+ "pendingGrants": "К выдаче",
+ "clearSelection": "Снять выбор",
+ "confirmGrant": "Подтвердить выдачу",
+ "removePending": "Убрать",
+ "selectedCount": "Выбрано: {{count}}",
+ "noRoleNoticeTitle": "Роль не назначена",
+ "noRoleNoticeHint": "Назначьте роль, прежде чем настраивать выдачи ресурсов. Без роли доступны только собственные данные, большинство функций недоступно.",
+ "assignRolesNow": "Назначить роли",
+ "viewPermissionsDetail": "Подробнее",
+ "hidePermissions": "Скрыть",
+ "effectivePermissionsDetail": "Состав действующих прав",
+ "editUserRolesHint": "Роли изменяются в карточке участника, вкладка «Роли и права».",
+ "auditTitle": "История изменений прав",
+ "auditHint": "События аудита по доступу этого участника к платформе.",
+ "unknownActor": "Неизвестный инициатор",
+ "metricUserContext": "Участник: {{name}} · ролей: {{roles}} · выдач: {{grants}}",
+ "currentMemberLabel": "Выбранный участник",
+ "auditPageSizeLabel": "Строк на странице",
+ "auditPageSize20": "20 / стр.",
+ "auditPageSize50": "50 / стр.",
+ "auditPageSize100": "100 / стр.",
+ "auditFilters": {
+ "filterBarLabel": "Фильтры истории изменений",
+ "actionLabel": "Фильтр по действию",
+ "allActions": "Все действия",
+ "actorPlaceholder": "Имя инициатора",
+ "resourceTypeLabel": "Фильтр по типу ресурса",
+ "allResourceTypes": "Все типы ресурсов"
+ },
+ "unknownTarget": "Неизвестный участник",
+ "auditColumns": {
+ "action": "Действие",
+ "actor": "Инициатор",
+ "target": "Участник",
+ "resourceType": "Тип ресурса",
+ "resourceId": "ID ресурса",
+ "time": "Время"
+ },
+ "deleteUserConfirmDetailed": "Удалить участника {{name}} (@{{username}})?\n\nБудут отозваны активные сессии, а также привязки ролей ({{roles}}) и выдачи ресурсов ({{grants}}).",
+ "auditActions": {
+ "create_user": "Создание пользователя платформы",
+ "update_user": "Обновление пользователя платформы",
+ "delete_user": "Удаление пользователя платформы",
+ "create_role": "Создание роли платформы",
+ "update_role": "Обновление роли платформы",
+ "delete_role": "Удаление роли платформы",
+ "assign_resource": "Выдача доступа к ресурсу",
+ "delete_resource_assignment": "Отзыв выдачи ресурса"
+ },
+ "copyId": "Копировать ID",
+ "copiedId": "Скопировано",
+ "resourceIdLabel": "ID",
+ "resourceDetailProject": "Проект {{id}}",
+ "pagination": {
+ "first": "Первая",
+ "previous": "Назад",
+ "next": "Далее",
+ "last": "Последняя",
+ "rowsPerPage": "Строк на странице",
+ "recordRange": "{{start}}-{{end}} из {{total}}",
+ "pageIndicator": "Стр. {{page}} / {{pages}}",
+ "resourcePage": "Стр. {{page}}",
+ "pageSummary": "Стр. {{page}} / {{pages}} · {{total}}",
+ "emptySummary": "0"
+ },
+ "resourceTypes": {
+ "user": "Пользователь платформы",
+ "project": "Проект",
+ "conversation": "Диалог",
+ "vulnerability": "Уязвимость",
+ "webshell": "Подключение WebShell",
+ "batch_task": "Пакетная задача",
+ "c2_listener": "Слушатель C2"
+ },
+ "scopes": {
+ "all": {
+ "label": "Все ресурсы",
+ "hint": "Доступ ко всем данным платформы",
+ "option": "Все ресурсы — доступ ко всем данным платформы"
+ },
+ "assigned": {
+ "label": "Назначенные ресурсы",
+ "hint": "Только данные, явно выданные администратором",
+ "option": "Назначенные ресурсы — только данные, выданные администратором"
+ },
+ "own": {
+ "label": "Созданные мной",
+ "hint": "Только данные, созданные участником",
+ "option": "Созданные мной — только собственные данные"
+ }
+ },
+ "systemRoles": {
+ "admin": {
+ "name": "Администратор",
+ "description": "Полный доступ к администрированию платформы"
+ },
+ "operator": {
+ "name": "Оператор",
+ "description": "Повседневные сценарии безопасности без управления учётными записями и основной конфигурацией"
+ },
+ "auditor": {
+ "name": "Аудитор",
+ "description": "Только чтение аудита, мониторинга и активов"
+ },
+ "viewer": {
+ "name": "Только чтение",
+ "description": "Только чтение явно выданных ресурсов"
+ }
+ },
+ "empty": {
+ "noMatchingUsers": "Нет подходящих участников",
+ "noUsers": "Участников платформы пока нет",
+ "tryAnotherKeyword": "Попробуйте другой запрос",
+ "addFirstUser": "Создайте первого участника кнопкой «Добавить участника»",
+ "noMatchingRoles": "Нет подходящих ролей",
+ "adjustRoleSearch": "Измените поиск или создайте свою роль",
+ "noRoles": "Ролей нет",
+ "selectUserFirst": "Сначала выберите участника",
+ "noAssignments": "Выдач ресурсов нет",
+ "addAssignmentHint": "Выберите ресурсы слева, затем нажмите «Подтвердить выдачу» выше",
+ "noMatchingAssignments": "Нет подходящих выдач",
+ "adjustAssignmentFilter": "Измените тип ресурса или запрос",
+ "noMatchingResources": "Нет подходящих ресурсов",
+ "noMatchingPermissions": "Нет подходящих прав",
+ "noEffectivePermissions": "Действующих прав нет",
+ "assignRoleFirst": "Назначьте роль, чтобы увидеть права",
+ "noAuditLogs": "Истории изменений нет",
+ "noAuditLogsHint": "Здесь появятся изменения ролей и выдач этого участника"
+ },
+ "messages": {
+ "roleChangesSaved": "Изменения ролей сохранены и уже действуют",
+ "grantPartial": "Добавлено выдач: {{created}}; уже были или дубли: {{skipped}}",
+ "grantSuccess": "Выдано ресурсов ({{resource}}): {{count}}"
+ },
+ "errors": {
+ "loadFailed": "Ошибка загрузки",
+ "loadDataFailed": "Не удалось загрузить данные управления доступом",
+ "loadResourcesFailed": "Не удалось загрузить ресурсы",
+ "saveFailed": "Ошибка сохранения",
+ "saveUserFailed": "Не удалось сохранить участника",
+ "saveRoleFailed": "Не удалось сохранить роль",
+ "deleteFailed": "Ошибка удаления",
+ "deleteUserFailed": "Не удалось удалить участника",
+ "deleteRoleFailed": "Не удалось удалить роль",
+ "enterResourceId": "Введите хотя бы один ID ресурса",
+ "tooManyResources": "За раз можно выдать не больше 100 ресурсов",
+ "batchGrantFailed": "Пакетная выдача не выполнена",
+ "grantFailed": "Не удалось выдать",
+ "revokeFailed": "Не удалось отозвать",
+ "loadAuditFailed": "Не удалось загрузить историю аудита"
+ },
+ "permissionModules": {
+ "agent": "Запуск агентов",
+ "agents": "Описания агентов",
+ "attackchain": "Цепочки атак",
+ "audit": "Аудит",
+ "auth": "Безопасность учётной записи",
+ "c2": "C2",
+ "chat": "Диалоги",
+ "config": "Конфигурация системы",
+ "dashboard": "Обзор",
+ "files": "Файлы",
+ "fofa": "FOFA",
+ "hitl": "Человек в контуре",
+ "knowledge": "База знаний",
+ "mcp": "MCP",
+ "monitor": "Монитор выполнения",
+ "notification": "Уведомления",
+ "openapi": "OpenAPI",
+ "project": "Проекты",
+ "rbac": "Доступ к платформе",
+ "robot": "Роботы",
+ "roles": "Роли тестирования",
+ "skills": "Навыки",
+ "tasks": "Задачи",
+ "terminal": "Терминал",
+ "vulnerability": "Уязвимости",
+ "webshell": "WebShell",
+ "workflow": "Сценарии"
+ },
+ "permissionDescriptions": {
+ "auth": {
+ "self": "Управлять своей сессией и паролем"
+ },
+ "dashboard": {
+ "read": "Просматривать сводки на обзоре"
+ },
+ "chat": {
+ "read": "Просматривать диалоги",
+ "write": "Создавать и изменять диалоги",
+ "delete": "Удалять диалоги и ходы"
+ },
+ "agent": {
+ "execute": "Запускать ИИ-агентов и сценарии"
+ },
+ "hitl": {
+ "read": "Просматривать очереди и журналы HITL",
+ "write": "Одобрять, отклонять и настраивать запросы HITL"
+ },
+ "tasks": {
+ "read": "Просматривать очереди задач",
+ "write": "Создавать и запускать очереди задач",
+ "delete": "Удалять очереди задач"
+ },
+ "project": {
+ "read": "Просматривать проекты и факты проекта",
+ "write": "Создавать и изменять проекты и факты проекта",
+ "delete": "Удалять проекты и факты проекта"
+ },
+ "vulnerability": {
+ "read": "Просматривать уязвимости",
+ "write": "Создавать и изменять уязвимости",
+ "delete": "Удалять уязвимости"
+ },
+ "webshell": {
+ "read": "Просматривать подключения WebShell",
+ "write": "Управлять подключениями WebShell и работать с ними",
+ "delete": "Удалять подключения WebShell"
+ },
+ "c2": {
+ "read": "Просматривать слушатели, сессии, задачи, события и профили C2",
+ "write": "Управлять слушателями, сессиями, задачами, полезными нагрузками, файлами и профилями C2",
+ "delete": "Удалять объекты C2"
+ },
+ "mcp": {
+ "read": "Просматривать состояние MCP и внешнюю конфигурацию MCP",
+ "write": "Управлять внешними серверами MCP и вызывать конечную точку MCP"
+ },
+ "knowledge": {
+ "read": "Просматривать базу знаний и журналы поиска",
+ "write": "Создавать, изменять, индексировать и сканировать базу знаний",
+ "delete": "Удалять элементы базы знаний и журналы поиска"
+ },
+ "skills": {
+ "read": "Просматривать навыки и статистику",
+ "write": "Создавать и изменять навыки",
+ "delete": "Удалять навыки и статистику"
+ },
+ "agents": {
+ "read": "Просматривать агентов Markdown",
+ "write": "Создавать и изменять агентов Markdown",
+ "delete": "Удалять агентов Markdown"
+ },
+ "roles": {
+ "read": "Просматривать роли тестирования ИИ",
+ "write": "Создавать и изменять роли тестирования ИИ",
+ "delete": "Удалять роли тестирования ИИ"
+ },
+ "workflow": {
+ "read": "Просматривать определения и запуски сценариев",
+ "write": "Создавать, изменять, проверять, запускать и возобновлять сценарии",
+ "delete": "Удалять сценарии"
+ },
+ "config": {
+ "read": "Просматривать конфигурацию системы",
+ "write": "Изменять и применять конфигурацию системы"
+ },
+ "terminal": {
+ "execute": "Выполнять команды в терминале"
+ },
+ "audit": {
+ "read": "Просматривать и экспортировать журналы аудита",
+ "delete": "Удалять журналы аудита"
+ },
+ "rbac": {
+ "read": "Просматривать пользователей, роли платформы, права и выдачи",
+ "write": "Управлять пользователями, ролями платформы, правами и выдачами"
+ },
+ "notification": {
+ "read": "Просматривать уведомления",
+ "write": "Отмечать уведомления прочитанными"
+ },
+ "robot": {
+ "read": "Просматривать статус привязки роботов",
+ "write": "Управлять привязками роботов и проверять обратные вызовы"
+ },
+ "files": {
+ "read": "Просматривать файлы, загруженные в чат",
+ "write": "Загружать, изменять и переименовывать файлы чата",
+ "delete": "Удалять файлы чата"
+ },
+ "attackchain": {
+ "read": "Просматривать цепочки атак",
+ "write": "Пересоздавать цепочки атак"
+ },
+ "fofa": {
+ "execute": "Выполнять поиск FOFA и разбирать запросы"
+ },
+ "openapi": {
+ "read": "Читать агрегированные результаты OpenAPI"
+ },
+ "monitor": {
+ "read": "Просматривать монитор выполнения",
+ "write": "Отменять отслеживаемые выполнения",
+ "delete": "Удалять записи монитора выполнения"
+ }
+ }
+ }
+}
diff --git a/web/static/i18n/zh-CN.json b/web/static/i18n/zh-CN.json
index 5c20931a..cbe7cbda 100644
--- a/web/static/i18n/zh-CN.json
+++ b/web/static/i18n/zh-CN.json
@@ -1,7 +1,8 @@
{
"lang": {
"zhCN": "中文",
- "enUS": "English"
+ "enUS": "English",
+ "ruRU": "Русский"
},
"common": {
"ok": "确定",
diff --git a/web/static/js/audit-datetime-picker.js b/web/static/js/audit-datetime-picker.js
index ef2fad23..e82b08df 100644
--- a/web/static/js/audit-datetime-picker.js
+++ b/web/static/js/audit-datetime-picker.js
@@ -17,7 +17,9 @@
function pickerLocale() {
if (typeof auditLocale === 'function') return auditLocale();
+ if (typeof window.uiLocale === 'function') return window.uiLocale();
if (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) return 'zh-CN';
+ if (typeof window.__locale === 'string' && window.__locale.startsWith('ru')) return 'ru-RU';
return 'en-US';
}
@@ -87,6 +89,9 @@
if (loc.startsWith('zh')) {
return ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d'];
}
+ if (loc.startsWith('ru')) {
+ return ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'];
+ }
return ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
}
diff --git a/web/static/js/audit.js b/web/static/js/audit.js
index c2d41327..4d54d857 100644
--- a/web/static/js/audit.js
+++ b/web/static/js/audit.js
@@ -91,8 +91,11 @@ function auditResultLabel(result) {
}
function auditLocale() {
+ if (typeof window.uiLocale === 'function') return window.uiLocale();
if (typeof window.__locale === 'string' && window.__locale.length) {
- return window.__locale.startsWith('zh') ? 'zh-CN' : 'en-US';
+ if (window.__locale.startsWith('zh')) return 'zh-CN';
+ if (window.__locale.startsWith('ru')) return 'ru-RU';
+ return 'en-US';
}
return (typeof navigator !== 'undefined' && navigator.language) ? navigator.language : 'en-US';
}
diff --git a/web/static/js/chat.js b/web/static/js/chat.js
index ce7744fb..846ef0c4 100644
--- a/web/static/js/chat.js
+++ b/web/static/js/chat.js
@@ -3648,9 +3648,9 @@ function addMessage(role, content, mcpExecutionIds = null, progressId = null, cr
} else {
messageTime = new Date();
}
- const msgTimeLocale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US';
+ const msgTimeLocale = (typeof window.uiLocale === 'function' ? window.uiLocale() : 'en-US');
const msgTimeOpts = { hour: '2-digit', minute: '2-digit' };
- if (msgTimeLocale === 'zh-CN') msgTimeOpts.hour12 = false;
+ if (msgTimeLocale === 'zh-CN' || msgTimeLocale === 'ru-RU') msgTimeOpts.hour12 = false;
timeDiv.textContent = messageTime.toLocaleTimeString(msgTimeLocale, msgTimeOpts);
try {
timeDiv.dataset.messageTime = messageTime.toISOString();
@@ -5597,7 +5597,7 @@ function renderMCPDetailModal(exec) {
try {
statusEl.dataset.detailStatus = normalizedStatus;
} catch (e) { /* ignore */ }
- const detailTimeLocale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US';
+ const detailTimeLocale = (typeof window.uiLocale === 'function' ? window.uiLocale() : 'en-US');
const detailTimeEl = document.getElementById('detail-time');
if (detailTimeEl) {
detailTimeEl.textContent = exec.startTime
@@ -6257,13 +6257,13 @@ function formatConversationTimestamp(dateObj, todayStart, yesterdayStart) {
const referenceToday = todayStart || new Date(now.getFullYear(), now.getMonth(), now.getDate());
const referenceYesterday = yesterdayStart || new Date(referenceToday.getTime() - 24 * 60 * 60 * 1000);
const messageDate = new Date(dateObj.getFullYear(), dateObj.getMonth(), dateObj.getDate());
- const fmtLocale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US';
+ const fmtLocale = (typeof window.uiLocale === 'function' ? window.uiLocale() : 'en-US');
const yesterdayLabel = typeof window.t === 'function' ? window.t('chat.yesterday') : '昨天';
const timeOnlyOpts = { hour: '2-digit', minute: '2-digit' };
const dateTimeOpts = { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' };
const fullDateOpts = { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' };
- if (fmtLocale === 'zh-CN') {
+ if (fmtLocale === 'zh-CN' || fmtLocale === 'ru-RU') {
timeOnlyOpts.hour12 = false;
dateTimeOpts.hour12 = false;
fullDateOpts.hour12 = false;
@@ -10565,7 +10565,7 @@ function formatConversationDateForMarkdown(value) {
if (!value) return '';
const d = new Date(value);
if (isNaN(d.getTime())) return '';
- const locale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US';
+ const locale = (typeof window.uiLocale === 'function' ? window.uiLocale() : 'en-US');
return d.toLocaleString(locale, {
year: 'numeric',
month: '2-digit',
@@ -11066,9 +11066,9 @@ function closeBatchManageModal() {
// 语言切换时刷新当前聊天页内的时间与动态文案(消息时间、执行流程时间由 monitor 的 refreshProgressAndTimelineI18n 处理)
function refreshChatPanelI18n() {
- const locale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US';
+ const locale = (typeof window.uiLocale === 'function' ? window.uiLocale() : 'en-US');
const timeOpts = { hour: '2-digit', minute: '2-digit' };
- if (locale === 'zh-CN') timeOpts.hour12 = false;
+ if (locale === 'zh-CN' || locale === 'ru-RU') timeOpts.hour12 = false;
const t = typeof window.t === 'function' ? window.t : function (k) { return k; };
const messagesEl = document.getElementById('chat-messages');
diff --git a/web/static/js/hitl.js b/web/static/js/hitl.js
index 81d92c67..3838d477 100644
--- a/web/static/js/hitl.js
+++ b/web/static/js/hitl.js
@@ -116,8 +116,11 @@ function hitlPaginationT(key, opts, fallback) {
}
function hitlLocale() {
+ if (typeof window.uiLocale === 'function') return window.uiLocale();
if (typeof window.__locale === 'string' && window.__locale.length) {
- return window.__locale.startsWith('zh') ? 'zh-CN' : 'en-US';
+ if (window.__locale.startsWith('zh')) return 'zh-CN';
+ if (window.__locale.startsWith('ru')) return 'ru-RU';
+ return 'en-US';
}
return (typeof navigator !== 'undefined' && navigator.language) ? navigator.language : 'en-US';
}
diff --git a/web/static/js/i18n.js b/web/static/js/i18n.js
index 5c5cb18f..b403db48 100644
--- a/web/static/js/i18n.js
+++ b/web/static/js/i18n.js
@@ -26,6 +26,9 @@
if (navLang.startsWith('zh')) {
return 'zh-CN';
}
+ if (navLang.startsWith('ru')) {
+ return 'ru-RU';
+ }
if (navLang.startsWith('en')) {
return 'en-US';
}
@@ -124,6 +127,8 @@
const lang = (i18next.language || DEFAULT_LANG).toLowerCase();
if (lang.indexOf('zh') === 0) {
label.textContent = i18next.t('lang.zhCN');
+ } else if (lang.indexOf('ru') === 0) {
+ label.textContent = i18next.t('lang.ruRU');
} else {
label.textContent = i18next.t('lang.enUS');
}
@@ -154,6 +159,9 @@
const current = i18next.language || DEFAULT_LANG;
if (lang === current) return;
await loadLanguageResources(lang);
+ if (lang === 'ru-RU') {
+ await loadLanguageResources('en-US');
+ }
await i18next.changeLanguage(lang);
try {
localStorage.setItem(STORAGE_KEY, lang);
@@ -183,12 +191,18 @@
const initialLang = detectInitialLang();
await i18next.init({
lng: initialLang,
- fallbackLng: DEFAULT_LANG,
+ fallbackLng: {
+ 'ru-RU': ['en-US', 'zh-CN'],
+ 'default': [DEFAULT_LANG]
+ },
debug: false,
resources: {}
});
await loadLanguageResources(initialLang);
+ if (initialLang === 'ru-RU') {
+ await loadLanguageResources('en-US');
+ }
applyTranslations(document);
updateLangLabel();
if (typeof window.refreshThemeToggleLabel === 'function') {
@@ -203,6 +217,12 @@
if (typeof i18next === 'undefined') return key;
return i18next.t(key, opts);
};
+ window.uiLocale = function () {
+ const lang = String((window.__locale || (typeof i18next !== 'undefined' && i18next.language) || '')).toLowerCase();
+ if (lang.indexOf('zh') === 0) return 'zh-CN';
+ if (lang.indexOf('ru') === 0) return 'ru-RU';
+ return 'en-US';
+ };
window.changeLanguage = changeLanguage;
window.applyTranslations = applyTranslations;
diff --git a/web/static/js/monitor.js b/web/static/js/monitor.js
index e3630b6d..62f7c661 100644
--- a/web/static/js/monitor.js
+++ b/web/static/js/monitor.js
@@ -77,11 +77,17 @@ if (typeof window !== 'undefined') {
// 当前界面语言对应的 BCP 47 标签(与时间格式化一致)
function getCurrentTimeLocale() {
+ if (typeof window.uiLocale === 'function') return window.uiLocale();
if (typeof window.__locale === 'string' && window.__locale.length) {
- return window.__locale.startsWith('zh') ? 'zh-CN' : 'en-US';
+ if (window.__locale.startsWith('zh')) return 'zh-CN';
+ if (window.__locale.startsWith('ru')) return 'ru-RU';
+ return 'en-US';
}
if (typeof i18next !== 'undefined' && i18next.language) {
- return (i18next.language || '').startsWith('zh') ? 'zh-CN' : 'en-US';
+ const lang = String(i18next.language || '');
+ if (lang.startsWith('zh')) return 'zh-CN';
+ if (lang.startsWith('ru')) return 'ru-RU';
+ return 'en-US';
}
return 'zh-CN';
}
@@ -90,7 +96,7 @@ function getCurrentTimeLocale() {
function getTimeFormatOptions() {
const loc = getCurrentTimeLocale();
const base = { hour: '2-digit', minute: '2-digit', second: '2-digit' };
- if (loc === 'zh-CN') {
+ if (loc === 'zh-CN' || loc === 'ru-RU') {
base.hour12 = false;
}
return base;
@@ -7812,7 +7818,7 @@ function buildMcpTimelineSvg(points, rangeKey) {
const maxVal = Math.max(1, ...points.map((p) => p.total || 0));
const hasFailed = points.some((p) => (p.failed || 0) > 0);
const hasBlocked = points.some((p) => (p.blocked || 0) > 0);
- const locale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US';
+ const locale = (typeof window.uiLocale === 'function' ? window.uiLocale() : 'en-US');
const barGap = points.length > 48 ? 1 : 2;
const barW = Math.max(1.6, Math.min(8, (plotW / Math.max(1, points.length)) - barGap));
@@ -8030,7 +8036,7 @@ function buildTimelineSparseHint(points, timeline) {
if (nonZeroRatio > 0.3 && !peakNearEnd) return '';
const rangeKey = timeline.range || getMcpMonitorTimelineRange();
- const locale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US';
+ const locale = (typeof window.uiLocale === 'function' ? window.uiLocale() : 'en-US');
const peakTime = timeline.summary.peakAt
? formatMcpTimelineLabel(timeline.summary.peakAt, rangeKey, locale)
: formatMcpTimelineLabel(points[peakIdx].t, rangeKey, locale);
@@ -8040,7 +8046,7 @@ function buildTimelineSparseHint(points, timeline) {
function renderMcpTimelineActiveMoments(points, rangeKey) {
if (!Array.isArray(points) || points.length === 0) return '';
- const locale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US';
+ const locale = (typeof window.uiLocale === 'function' ? window.uiLocale() : 'en-US');
const active = points
.map((p, i) => ({ ...p, i }))
.filter((p) => (p.total || 0) > 0)
@@ -8590,7 +8596,7 @@ function renderMcpStatsStackedBar(success, failed) {
function updateMonitorStatsSubtitle(lastFetchedAt, toolCount, retentionDays) {
const subtitle = document.getElementById('monitor-stats-subtitle');
if (!subtitle) return;
- const locale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US';
+ const locale = (typeof window.uiLocale === 'function' ? window.uiLocale() : 'en-US');
const timeText = lastFetchedAt
? (lastFetchedAt.toLocaleString ? lastFetchedAt.toLocaleString(locale) : String(lastFetchedAt))
: '—';
@@ -8987,7 +8993,7 @@ function renderMonitorStats(summary = null, topTools = [], lastFetchedAt = null)
const hasCalls = effectiveTotal > 0;
const successRateNum = hasCalls ? (totals.success / effectiveTotal) * 100 : 0;
const successRate = hasCalls ? successRateNum.toFixed(1) : '-';
- const locale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US';
+ const locale = (typeof window.uiLocale === 'function' ? window.uiLocale() : 'en-US');
const noCallsYet = mcpMonitorT('noCallsYet') || monitorFallback('暂无调用', 'No calls yet');
const noCompletedYet = mcpMonitorT('noCompletedYet') || monitorFallback('暂无有效完成', 'No completed outcomes yet');
const lastCallText = totals.lastCallTime
@@ -9081,7 +9087,7 @@ function renderMonitorExecutions(executions = [], statusFilter = 'all') {
hard_timeout: 'statusHardTimeout',
orphaned: 'statusOrphaned'
};
- const locale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : undefined;
+ const locale = (typeof window.uiLocale === 'function') ? window.uiLocale() : undefined;
const rowEntries = executions
.map(exec => {
const status = getToolExecutionDisplayStatus(exec);
diff --git a/web/static/js/vulnerability.js b/web/static/js/vulnerability.js
index 1c50786d..329f3501 100644
--- a/web/static/js/vulnerability.js
+++ b/web/static/js/vulnerability.js
@@ -8,11 +8,11 @@ function vulnT(key, opts) {
}
function vulnDateLocale() {
+ if (typeof window.uiLocale === 'function') return window.uiLocale();
try {
const lang = (window.__locale || '').toLowerCase();
- if (lang.indexOf('zh') === 0) {
- return 'zh-CN';
- }
+ if (lang.indexOf('zh') === 0) return 'zh-CN';
+ if (lang.indexOf('ru') === 0) return 'ru-RU';
} catch (e) { /* ignore */ }
return 'en-US';
}
diff --git a/web/templates/api-docs.html b/web/templates/api-docs.html
index 30eef14d..f1c8b9fa 100644
--- a/web/templates/api-docs.html
+++ b/web/templates/api-docs.html
@@ -984,6 +984,7 @@