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 @@ diff --git a/web/templates/index.html b/web/templates/index.html index ed2e52a9..bd66f4ec 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -115,6 +115,7 @@