Files
hermes-agent/apps/desktop/src/i18n/ru.ts
T
Teknium 6d89c1afde feat(plugins): pin a plugin to a commit from Desktop, and show pins in every list
Only the CLI could install a plugin at an exact commit (`--ref <sha>`); the
Desktop "Install from Git" dialog, the TUI gateway `plugins.manage install`
method and the dashboard `/agent-plugins/install` endpoint all called
`_install_plugin_core` without a ref, so a team that wanted everyone on the
same private-plugin commit had to leave the app for a terminal.

- `dashboard_install_plugin(ref=)` threads the pin to `_install_plugin_core`;
  the 40-hex validation and HEAD verification are unchanged. The gateway
  method and dashboard body accept `ref`.
- Desktop dialog gains an optional "Pin to commit" field (custom sources only,
  client-side 40-hex check disables Install on anything shorter).
- `plugins.manage list` rows carry `pinned_sha`; the Desktop plugins tab shows
  a `pinned @ <sha8>` badge and `hermes plugins list` prints `git pinned@<sha8>`
  in Source, so a team can eyeball that everyone runs the same commit.
2026-09-10 05:09:15 -07:00

3845 lines
249 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { defineFieldCopy } from '@/app/settings/field-copy'
import { defineLocale } from './define-locale'
// RU_PLURAL: (count, one, few, many) — русские формы сущ. падежа
// RU_NOUN: (count, one, few, many) — формы род. множественного
// count может быть number или string (часть подписей en.ts передаёт строки)
const ruNum = (count: number | string) => (typeof count === 'string' ? Number(count) || 0 : count)
const RU_PLURAL = (count: number | string, one: string, few: string, many: string) => {
const c = ruNum(count)
const n = Math.abs(c) % 10
const nn = Math.abs(c) % 100
return n === 1 && nn !== 11 ? one : n >= 2 && n <= 4 && (nn < 12 || nn > 14) ? few : many
}
const RU_NOUN = (count: number | string, one: string, few: string, many: string) => {
const c = ruNum(count)
const n = Math.abs(c) % 10
const nn = Math.abs(c) % 100
return n === 1 && nn !== 11 ? one : n >= 2 && n <= 4 && (nn < 12 || nn > 14) ? few : many
}
export const ru = defineLocale({
sessionImport: {
title: 'Продолжить из другого приложения',
subtitle: 'Перенесите разговор в Hermes и продолжите с того места, где остановились.',
action: 'Импортировать сессию',
readingFrom: 'Читаем с',
connectedComputer: 'подключённого компьютера',
destination: 'Импорт в',
all: 'Все',
search: 'Поиск по загруженным сессиям',
scanning: 'Поиск разговоров',
scanError: 'Не удалось найти сессии',
scanHelp: 'Проверьте подключение к серверу и повторите попытку. Старому серверу может требоваться обновление.',
empty: 'Разговоров пока нет',
emptyHelp: 'Здесь появятся сессии Claude Code и Codex с этого сервера.',
noMatches: 'Совпадений нет',
searchHelp: 'Попробуйте другой заголовок или папку либо загрузите ещё сессии.',
skipped: 'Некоторые журналы пусты, недоступны или слишком велики для просмотра.',
more: 'Загрузить ещё сессии',
messages: 'сообщений',
choose: 'Разговор, который стоит продолжить',
chooseHelp: 'Выберите сессию, чтобы прочитать историю перед импортом в Hermes.',
previewLoading: 'Открываем просмотр',
previewError: 'Просмотр недоступен',
previewHelp: 'Исходный файл мог переместиться или измениться. Обновите список и повторите попытку.',
previewLimit: 'Просмотр сокращён для удобства чтения. Импортируется весь разговор.',
you: 'Вы',
snapshot: 'Этот разговор уже есть в Hermes. Откройте существующую копию, чтобы продолжить.',
copyNotice:
'Копируется текст разговора. Исходные файлы не меняются. Вывод инструментов и рассуждения не переносятся.',
importing: 'Импорт…',
open: 'Открыть в Hermes',
continue: 'Продолжить в Hermes',
importError: 'Не удалось импортировать разговор.'
},
common: {
apply: 'Применить',
back: 'Назад',
save: 'Сохранить',
saving: 'Сохранение…',
cancel: 'Отмена',
change: 'Изменить',
choose: 'Выбрать',
clear: 'Очистить',
close: 'Закрыть',
collapse: 'Свернуть',
confirm: 'Подтвердить',
connect: 'Подключить',
connecting: 'Подключение',
continue: 'Продолжить',
copied: 'Скопировано',
copy: 'Копировать',
copyFailed: 'Не удалось скопировать',
delete: 'Удалить',
docs: 'Документация',
done: 'Готово',
error: 'Ошибка',
expand: 'Развернуть',
failed: 'Ошибка',
formatJson: 'Форматировать JSON',
free: 'Бесплатно',
loading: 'Загрузка…',
notSet: 'Не задано',
refresh: 'Обновить',
remove: 'Убрать',
replace: 'Заменить',
retry: 'Повторить',
run: 'Запустить',
send: 'Отправить',
set: 'Установить',
skip: 'Пропустить',
update: 'Обновить',
tryHint: term => `Попробуйте «${term}»`,
on: 'Вкл',
off: 'Выкл'
},
fileMenu: {
revealFinder: 'Показать в Finder',
revealExplorer: 'Показать в Проводнике',
revealFileManager: 'Открыть содержащую папку',
revealInSidebar: 'Показать в дереве файлов',
copyPath: 'Копировать путь',
copyRelativePath: 'Копировать относительный путь',
download: 'Скачать',
downloadSaved: 'Сохранено',
downloadFailed: 'Не удалось скачать',
rename: 'Переименовать…',
delete: 'Удалить',
renameTitle: 'Переименование',
renameLabel: 'Новое имя',
deleteTitle: name => `Удалить ${name}?`,
deleteBody: 'Элемент будет перемещён в корзину — его можно восстановить оттуда.',
pathCopied: 'Путь скопирован'
},
boot: {
ready: 'Hermes Desktop готов',
desktopBootFailedWithMessage: message => `Не удалось запустить приложение: ${message}`,
steps: {
connectingGateway: 'Подключение к шлюзу',
loadingSettings: 'Загрузка настроек Hermes',
loadingSessions: 'Загрузка последних сеансов',
retryingRemoteBackend: 'Переподключение к удалённому бэкенду Hermes…',
startingDesktopConnection: 'Запуск подключения приложения',
startingHermesDesktop: 'Запуск Hermes Desktop…'
},
errors: {
backgroundExited: 'Фоновый процесс Hermes завершён.',
backgroundExitedDuringStartup: 'Фоновый процесс Hermes завершился при запуске.',
backendStopped: 'Бэкенд остановлен',
desktopBootFailed: 'Не удалось запустить приложение',
gatewayConnectionLost: 'Соединение с шлюзом потеряно',
gatewaySignInRequired: 'Требуется вход в шлюз',
ipcBridgeUnavailable: 'IPC-мост приложения недоступен.'
},
failure: {
title: 'Hermes не удалось запустить',
description:
'Фоновый шлюз не запустился. Попробуйте один из шагов восстановления ниже. Ничто из этого не удаляет ваши чаты и настройки.',
remoteTitle: 'Требуется вход в удалённый шлюз',
remoteDescription:
'Сессия удалённого шлюза истекла. Войдите снова, чтобы переподключиться. Ничто из этого не удаляет ваши чаты и настройки.',
retry: 'Повторить',
repairInstall: 'Восстановить установку',
useLocalGateway: 'Использовать локальный шлюз',
gatewaySettings: 'Настройки шлюза',
back: 'Назад',
openLogs: 'Открыть журналы',
repairHint: 'Восстановление перезапускает установщик — на чистой машине это может занять несколько минут.',
remoteSignInHint: signInLabel =>
`Выход из сохранённой сессии удалённого браузера, затем открытие ${signInLabel}. Чтобы перейти на встроенный бэкенд, используйте локальный шлюз.`,
signOutAndSignIn: 'Выйти и войти',
remoteFailureHint: 'Проверьте URL шлюза и вход в настройках шлюза или переключитесь на локальный шлюз.',
hideRecentLogs: 'Скрыть недавние журналы',
showRecentLogs: 'Показать недавние журналы',
signedInTitle: 'Вы вошли в систему',
signedInMessage: 'Переподключение к удалённому шлюзу…',
signInIncompleteTitle: 'Вход не завершён',
signInIncompleteMessage: 'Окно входа закрылось до завершения аутентификации.',
signInFailed: 'Не удалось войти',
signInToRemoteGateway: 'Войти в удалённый шлюз',
signInWithProvider: provider => `Войти через ${provider}`,
identityProvider: 'вашему провайдеру аутентификации'
}
},
notifications: {
region: 'Уведомления',
hide: 'Скрыть',
show: 'Показать',
more: count =>
`Ещё ${count} ${count % 10 === 1 && count % 100 !== 11 ? 'уведомление' : count % 10 >= 2 && count % 10 <= 4 && (count % 100 < 12 || count % 100 > 14) ? 'уведомления' : 'уведомлений'}`,
clearAll: 'Очистить всё',
dismiss: 'Закрыть уведомление',
details: 'Подробности',
copyDetail: 'Копировать подробность',
copyDetailFailed: 'Не удалось скопировать подробность уведомления',
backendOutOfDateTitle: 'Устаревший бэкенд',
backendOutOfDateMessage:
'Ваш бэкенд Hermes старше этой сборки приложения и может работать некорректно. Обновите их, чтобы они совпали.',
installMethodUnsupportedTitle: 'Неподдерживаемый способ установки',
updateHermes: 'Обновить Hermes',
updateReadyTitle: 'Обновление готово',
updateReadyMessage: count =>
`Доступно ${count} ${count % 10 === 1 && count % 100 !== 11 ? 'новое изменение' : count % 10 >= 2 && count % 10 <= 4 && (count % 100 < 12 || count % 100 > 14) ? 'новых изменения' : 'новых изменений'}.`,
updateReadyMessageUnknown: 'Доступно новое обновление.',
seeWhatsNew: 'Смотреть, что нового',
mcp: {
needsAuthTitle: 'Серверу MCP требуется повторная аутентификация',
needsAuthMessage: name => `Для MCP ${name} требуется повторная аутентификация.`,
errorTitle: 'Сервер MCP недоступен',
errorMessage: name => `MCP ${name} не прошёл проверку работоспособности.`,
signIn: 'Войти',
view: 'Просмотр'
},
errors: {
elevenLabsNeedsKey: 'Для STT ElevenLabs нужен ELEVENLABS_API_KEY.',
elevenLabsRejectedKey: 'ElevenLabs отклонил API-ключ (401).',
diskFull: 'Диск заполнен — освободите место и повторите.',
gatewayAuthFailed: 'Аутентификация шлюза не удалась — проверьте API_SERVER_KEY.',
methodNotAllowed:
'Бэкенд приложения отклонил запрос (405 Method Not Allowed). Попробуйте перезапустить Hermes Desktop.',
microphonePermission: 'Доступ к микрофону запрещён.',
openaiRejectedApiKey: 'OpenAI отклонил API-ключ.',
openaiRejectedApiKeyWithStatus: status => `OpenAI отклонил API-ключ (${status} invalid_api_key).`,
openaiTtsNeedsKey: 'Для TTS OpenAI нужен VOICE_TOOLS_OPENAI_KEY или OPENAI_API_KEY.'
},
voice: {
configureSpeechToText: 'Настройте распознавание речи, чтобы использовать голосовой режим.',
couldNotStartSession: 'Не удалось начать голосовой сеанс',
microphoneAccessDenied: 'Доступ к микрофону запрещён.',
microphoneConstraintsUnsupported: 'Ограничения микрофона не поддерживаются этим устройством.',
microphoneFailed: 'Ошибка микрофона',
microphoneInUse: 'Микрофон уже используется другим приложением.',
microphonePermissionDenied: 'Доступ к микрофону запрещён.',
microphoneStartFailed: 'Не удалось начать запись с микрофона.',
microphoneUnsupported: 'Это окружение не поддерживает запись с микрофона.',
noMicrophone: 'Микрофон не найден.',
noSpeechDetected: 'Речь не обнаружена',
playbackFailed: 'Не удалось воспроизвести голос',
recordingFailed: 'Не удалось записать голос',
sayStopToEnd: phrase => `Скажите «${phrase}», чтобы закончить голосовой чат.`,
transcriptionFailed: 'Не удалось расшифровать речь',
transcriptionUnavailable: 'Расшифровка речи пока недоступна.',
tryRecordingAgain: 'Попробуйте записать ещё раз.',
unavailable: 'Голос недоступен'
},
native: {
approvalTitle: 'Требуется одобрение',
approveAction: 'Одобрить',
rejectAction: 'Отклонить',
inputTitle: 'Требуется ввод',
inputBody: 'Hermes ожидает ваш ответ.',
turnDoneTitle: 'Hermes завершил',
turnDoneBody: '',
turnErrorTitle: 'Ход не удался',
backgroundDoneTitle: 'Фоновая задача завершена',
backgroundFailedTitle: 'Фоновая задача не удалась',
creditsTitle: 'Кредиты'
}
},
remoteDisplayBanner: {
message: reason =>
`Включён программный рендеринг — обнаружен удалённый дисплей (${reason}). GPU-ускорение отключено, чтобы избежать мерцания.`
},
billingBlock: {
titleNous: 'Кредиты Nous закончились',
titleProvider: provider => `Кредиты закончились — ${provider}`,
fallbackMessage: 'В вашем аккаунте закончились кредиты. Пополните баланс, чтобы продолжить.',
openBilling: 'Открыть биллинг',
addCredits: 'Пополнить кредиты',
dismiss: 'Скрыть'
},
titlebar: {
hideSidebar: 'Скрыть боковую панель',
showSidebar: 'Показать боковую панель',
search: 'Поиск',
searchTitle: 'Поиск сеансов, видов и действий',
swapSidebarSides: 'Поменять панели местами',
hideRightSidebar: 'Скрыть правую панель',
showRightSidebar: 'Показать правую панель',
unreadSessions: count =>
count === 1
? '1 непрочитанный сеанс'
: `${count} ${count % 10 === 1 && count % 100 !== 11 ? 'непрочитанный сеанс' : count % 10 >= 2 && count % 10 <= 4 && (count % 100 < 12 || count % 100 > 14) ? 'непрочитанных сеанса' : 'непрочитанных сеансов'}`,
muteHaptics: 'Выключить вибрацию',
unmuteHaptics: 'Включить вибрацию',
openSettings: 'Открыть настройки',
openStarmap: 'Открыть граф памяти',
enterHud: 'Режим HUD',
exitHud: 'Выйти из режима HUD',
layoutEditor: 'Редактор раскладки',
layoutEditorTitle: mod => `Редактор раскладки — ${mod}-клик сбрасывает раскладку`
},
keybinds: {
title: 'Горячие клавиши',
subtitle: open => `Нажмите на сочетание, чтобы переназначить · ${open} снова открывает эту панель.`,
search: 'Поиск горячих клавиш…',
rebind: 'Переназначить',
reset: 'Сбросить по умолчанию',
resetAll: 'Сбросить всё',
pressKey: 'Нажмите клавишу…',
set: 'задано',
conflictWith: label => `Также назначено на «${label}»`,
categories: {
composer: 'Композер',
profiles: 'Профили',
session: 'Сеанс',
navigation: 'Навигация',
view: 'Вид'
},
actions: {
'keybinds.openPanel': 'Открыть горячие клавиши',
'nav.commandPalette': 'Открыть палитру команд',
'nav.commandCenter': 'Открыть центр команд',
'nav.settings': 'Открыть настройки',
'nav.profiles': 'Открыть профили',
'nav.skills': 'Открыть навыки',
'nav.messaging': 'Открыть мессенджеры',
'nav.artifacts': 'Открыть артефакты',
'nav.cron': 'Открыть запланированные задачи',
'nav.agents': 'Открыть агенты',
'session.new': 'Новый сеанс',
'session.newTab': 'Новая вкладка сеанса',
'session.newWindow': 'Новое окно',
'session.next': 'Следующий сеанс',
'session.prev': 'Предыдущий сеанс',
'session.slot.1': 'Переключить на недавний сеанс 1',
'session.slot.2': 'Переключить на недавний сеанс 2',
'session.slot.3': 'Переключить на недавний сеанс 3',
'session.slot.4': 'Переключить на недавний сеанс 4',
'session.slot.5': 'Переключить на недавний сеанс 5',
'session.slot.6': 'Переключить на недавний сеанс 6',
'session.slot.7': 'Переключить на недавний сеанс 7',
'session.slot.8': 'Переключить на недавний сеанс 8',
'session.slot.9': 'Переключить на недавний сеанс 9',
'session.focusSearch': 'Поиск по сеансам',
'session.togglePin': 'Закрепить / открепить текущий сеанс',
'session.archive': 'В архив текущий сеанс',
'workspace.newWorktree': 'Новый worktree',
'workspace.openFolder': 'Открыть папку как проект',
'composer.focus': 'Сфокусировать композер',
'composer.modelPicker': 'Открыть выбор модели',
'composer.voice': 'Начать / остановить голосовой диалог',
'view.toggleSidebar': 'Показать / скрыть панель сеансов',
'view.toggleRightSidebar': 'Показать / скрыть браузер файлов',
'view.toggleReview': 'Показать / скрыть панель ревью',
'view.toggleStatusbar': 'Показать / скрыть строку состояния',
'view.toggleTabStrip': 'Показать / скрыть вкладки',
'view.showFiles': 'Показать браузер файлов',
'view.showBrowser': 'Открыть браузер',
'view.toggleHud': 'Включить / выключить режим HUD',
'hud.snapToPointer': 'Переместить HUD под курсор (глобально, пока HUD открыт)',
'view.showTerminal': 'Показать / скрыть терминал',
'view.newTerminal': 'Новый терминал',
'view.nextTerminal': 'Следующий терминал',
'view.prevTerminal': 'Предыдущий терминал',
'view.closeTerminal': 'Закрыть терминал',
'view.terminalSelection': 'Отправить выделенное из терминала в композер',
'view.terminalCopy': 'Копировать выделенное из терминала',
'view.terminalPaste': 'Вставить в терминал',
'view.closeTab': 'Закрыть вкладку',
'view.reopenTab': 'Восстановить закрытую вкладку',
'view.flipPanes': 'Поменять панели местами',
'view.findInPage': 'Найти на странице',
'view.findNext': 'Следующее вхождение',
'view.findPrevious': 'Предыдущее вхождение',
'appearance.toggleMode': 'Сменить светлую / тёмную тему',
'profile.default': 'Переключить на профиль по умолчанию',
'profile.switch.1': 'Переключить на профиль 1',
'profile.switch.2': 'Переключить на профиль 2',
'profile.switch.3': 'Переключить на профиль 3',
'profile.switch.4': 'Переключить на профиль 4',
'profile.switch.5': 'Переключить на профиль 5',
'profile.switch.6': 'Переключить на профиль 6',
'profile.switch.7': 'Переключить на профиль 7',
'profile.switch.8': 'Переключить на профиль 8',
'profile.switch.9': 'Переключить на профиль 9',
'profile.switch.10': 'Переключить на профиль 10',
'profile.switch.11': 'Переключить на профиль 11',
'profile.switch.12': 'Переключить на профиль 12',
'profile.switch.13': 'Переключить на профиль 13',
'profile.switch.14': 'Переключить на профиль 14',
'profile.switch.15': 'Переключить на профиль 15',
'profile.switch.16': 'Переключить на профиль 16',
'profile.switch.17': 'Переключить на профиль 17',
'profile.switch.18': 'Переключить на профиль 18',
'profile.next': 'Следующий профиль',
'profile.prev': 'Предыдущий профиль',
'profile.toggleAll': 'Показать все профили',
'profile.create': 'Создать профиль',
'composer.send': 'Отправить сообщение',
'composer.newline': 'Вставить перенос строки',
'composer.steer': 'Направить текущий ход',
'composer.queue': 'Поставить сообщение в очередь',
'composer.sendQueued': 'Отправить следующий ход из очереди',
'composer.mention': 'Ссылка на файлы, папки, URL',
'composer.slash': 'Палитра slash-команд',
'composer.help': 'Быстрая справка',
'composer.history': 'Переключить поповер / историю',
'composer.cancel': 'Закрыть поповер · отменить запуск'
}
},
findInPage: {
next: 'Следующее вхождение',
previous: 'Предыдущее вхождение'
},
language: {
label: 'Язык',
description: 'Выберите язык интерфейса приложения.',
saving: 'Сохранение языка…',
saveError: 'Не удалось обновить язык',
switchTo: 'Сменить язык',
searchPlaceholder: 'Поиск языка…',
noResults: 'Языки не найдены'
},
settings: {
closeSettings: 'Закрыть настройки',
exportConfig: 'Экспорт конфигурации',
importConfig: 'Импорт конфигурации',
resetToDefaults: 'Сбросить к значениям по умолчанию',
resetConfirm: 'Сбросить все настройки к значениям Hermes по умолчанию?',
exportFailed: 'Не удалось экспортировать',
resetFailed: 'Не удалось сбросить',
nav: {
providers: 'Провайдеры',
providerAccounts: 'Аккаунты',
providerApiKeys: 'API-ключи',
providerCustomEndpoints: 'Свои эндпоинты',
gateway: 'Шлюзы',
apiKeys: 'Инструменты и ключи',
keybinds: 'Горячие клавиши',
keysTools: 'Инструменты',
keysSettings: 'Настройки',
mcp: 'MCP',
archivedChats: 'Архив чатов',
about: 'О программе',
billing: 'Оплата',
notifications: 'Уведомления'
},
plugins: {
title: 'Плагины приложения',
blurb: 'Встроенные или добавленные в папку desktop-plugins. Отключите, чтобы выгрузить без перезапуска.',
count: n => `Установлено: ${n}`,
openFolder: 'Открыть папку плагинов',
rescan: 'Пересканировать',
reveal: 'Показать в файловом менеджере',
enable: 'Включить',
disable: 'Отключить',
failed: 'ошибка',
empty: 'Плагины приложения пока не установлены.',
kinds: { bundled: 'встроенный', disk: 'на диске', runtime: 'runtime' },
installModal: {
title: 'Установка плагина',
description: 'Перед установкой посмотрите, что содержит этот репозиторий.',
repoLabel: 'Репозиторий',
includesHeading: 'Состав пакета',
agentLabel: 'Плагин агента',
desktopLabel: 'UI приложения',
agentTargetLocal: profile => `Устанавливается в локальный бэкенд ${profile} (~/.hermes/plugins/)`,
agentTargetRemote: profile => `Устанавливается в подключённый бэкенд ${profile}`,
desktopTarget: 'Устанавливается в локальную папку desktop-plugins этого приложения',
desktopOnlyNote: 'Пакеты только для приложения не устанавливают плагин агента.',
insecureWarning:
'Этот URL использует небезопасную или локальную схему. Для боевой установки предпочитайте https:// или git@.',
securityHeading: 'Перед установкой',
securityIntro:
'Устанавливайте только из проверенных источников — при желании просмотрите репозиторий ниже, чтобы увидеть, что будет добавлено.',
sourceHeading: 'Исходный код',
viewRepository: 'Посмотреть репозиторий',
viewPluginFiles: 'Посмотреть файлы плагина',
gitCloneLabel: 'URL для git clone',
enableAgent: 'Включить плагин агента после установки',
forceReinstall: 'Принудительная переустановка (заменить, если уже установлен)',
pinToCommit: 'Закрепить на коммите (необязательно)',
pinToCommitPlaceholder: 'Полный SHA коммита (40 символов)',
pinToCommitHint:
'Все, кто установит этот SHA, получат одинаковый код; плагин перестанет обновляться до смены пина. Оставьте пустым для последнего коммита.',
pinToCommitInvalid: 'Нужен полный SHA коммита из 40 символов (ветки и теги не принимаются).',
install: 'Установить',
installing: 'Установка…',
probing: 'Осмотр репозитория…',
probeUnavailable: 'Осмотр плагинов недоступен в этом окружении.',
desktopUnavailable: 'Установка плагинов приложения недоступна в этом окружении.',
selectComponent: 'Выберите хотя бы один компонент для установки.',
agentSuccess: name => `Плагин агента ${name} установлен`,
desktopSuccess: name => `Плагин приложения ${name} установлен`,
agentFailed: 'Не удалось установить плагин агента',
desktopFailed: 'Не удалось установить плагин приложения',
missingEnv: vars => `Не хватает переменных окружения: ${vars}. Добавьте их в Настройки → Ключи.`
}
},
notifications: {
title: 'Уведомления',
intro: 'Системные уведомления (не всплывающие внутри приложения). Настраивается для каждого устройства.',
enableAll: 'Включить уведомления',
enableAllDesc: 'Если выключено — все уведомления ниже будут заглушены.',
focusedHint: 'Оповещения о завершении срабатывают только пока Hermes в фоне.',
kinds: {
approval: {
label: 'Нужно одобрение',
description: 'Команда ожидает вашего одобрения или отклонения.'
},
input: {
label: 'Нужен ввод',
description: 'Hermes задал вопрос или требует пароль/секрет.'
},
turnDone: {
label: 'Ответ готов',
description: 'Ход завершился, пока Hermes был в фоне.'
},
turnError: {
label: 'Ход не удался',
description: 'Ошибки фоновых ходов.'
},
backgroundDone: {
label: 'Фоновая задача завершена',
description: 'Фоновая команда терминала выполнена.'
},
credits: {
label: 'Оповещения о кредитах',
description: 'Доступ к кредитам приостановлен или восстановлен.'
},
plugin: {
label: 'Уведомления плагинов',
description: 'Плагин приложения отправил уведомление, пока Hermes был в фоне.'
}
},
test: 'Отправить тестовое уведомление',
testTitle: 'Hermes',
testBody: 'Уведомления работают.',
testSent:
'Тест отправлен. Если ничего не появилось, проверьте разрешения на уведомления в системе и режим «Не беспокоить».',
testUnsupported: 'Эта система не поддерживает системные уведомления.',
completionSoundTitle: 'Звук завершения',
completionSoundDesc: 'Воспроизводится, когда ход агента завершён. Выберите пресет и прослушайте здесь.',
completionSoundPreview: 'Прослушать'
},
sections: {
model: 'Модель',
chat: 'Чат',
appearance: 'Внешний вид',
workspace: 'Рабочее пространство',
safety: 'Безопасность',
memory: 'Память и контекст',
voice: 'Голос',
advanced: 'Дополнительно'
},
searchPlaceholder: {
about: 'О Hermes Desktop',
config: 'Поиск настроек…',
gateway: 'Подключение шлюза…',
keys: 'Поиск API-ключей…',
mcp: 'Поиск MCP-серверов…',
sessions: 'Поиск архивных сеансов…'
},
modeOptions: {
light: { label: 'Светлая', description: 'Яркие поверхности' },
dark: { label: 'Тёмная', description: 'Мягкое рабочее пространство' },
system: { label: 'Системная', description: 'Следовать настройкам ОС' }
},
appearance: {
title: 'Внешний вид',
intro: 'Только для приложения. Режим — это яркость, тема — палитра и оформление чата.',
colorMode: 'Цветовой режим',
colorModeDesc: 'Выберите фиксированный режим или позвольте Hermes следовать настройкам системы.',
toolViewTitle: 'Отображение вызовов инструментов',
toolViewDesc: 'Режим «Продукт» скрывает сырые данные инструментов, «Технический» показывает полный вход/выход.',
reasoningCollapsedTitle: 'Сворачивать «мышление» по умолчанию',
reasoningCollapsedDesc:
'Стриминговое рассуждение остаётся доступным, но не разворачивается, пока вы его не откроете.',
uiScaleTitle: 'Масштаб интерфейса',
uiScaleDesc: percent =>
`Масштабирует текст и элементы управления во всём приложении. Также работает Cmd/Ctrl с +, − и 0. Сейчас: ${percent}%.`,
sessionDensityTitle: 'Плотность списка сеансов',
sessionDensityDesc: 'Выберите, сколько контекста показывать под заголовками сеансов в боковой панели.',
sessionDensityCompact: 'Компактно',
sessionDensityComfortable: 'Комфортно',
sessionDensityDetailed: 'Подробно',
tabStripTitle: 'Панель вкладок',
tabStripDesc: 'Показывать вкладки над зоной. Автоматически скрываются, когда в зоне только одна панель.',
tabStripAuto: 'Авто',
tabStripAlways: 'Всегда',
tabStripNever: 'Никогда',
terminalFontTitle: 'Шрифт терминала',
terminalFontDesc:
'Выберите установленный шрифт для терминалов приложения. Nerd Fonts отображают Powerlevel10k и иконки оболочки; оставьте пустым, чтобы использовать встроенный JetBrains Mono.',
terminalFontPlaceholder: 'MesloLGS NF или CSS-стек шрифтов',
terminalFontPreview: 'Предпросмотр глифов',
terminalFontReset: 'Использовать по умолчанию',
translucencyTitle: 'Полупрозрачность окна',
translucencyDesc:
'Рабочий стол виден сквозь всё окно, включая текст. Отдельная настройка для светлой и тёмной тем.',
translucencyGlassDesc:
'Матовое стекло: рабочий стол виден как плавное размытие, а текст остаётся чётким. Отдельная настройка для светлой и тёмной тем.',
translucencyModeClear: 'Прозрачная',
translucencyModeGlass: 'Стекло',
translucencyTintTitle: 'Тон',
translucencyFadeTitle: 'Затухание',
translucencyFrostTitle: 'Матовость',
translucencyFrost: {
'under-window': 'Глубина',
popover: 'Мягкость',
titlebar: 'Яркость',
header: 'Отблеск'
},
translucencyScopeTitle: 'Область',
translucencyScope: {
window: 'Всё окно',
sidebar: 'Только боковая панель'
},
backdropTitle: 'Фон чата',
backdropDesc: 'Блёклый силуэт позади диалога.',
userBubbleTitle: 'Пузырь сообщения',
userBubbleDesc: 'Насколько прозрачны ваши сообщения. 0 — сплошная заливка, 100 — остаётся только контур.',
introSplashTitle: 'Экран приветствия',
introSplashDesc: 'Логотип и подсказка, показываемые на пустом чате.',
reactionsTitle: 'Реакции на сообщения',
reactionsDesc: 'Эмодзи-тапбеки в стиле iMessage — реагируйте на сообщения, и Hermes сможет реагировать на ваши.',
composerPopoutTitle: 'Плавающий композер',
composerPopoutDesc: 'Позволяет вытягивать композер из его док-зоны. Отключите, чтобы он был закреплён снизу.',
embedsTitle: 'Встроенные превью',
embedsDesc:
'Богатые превью загружаются со сторонних сайтов (YouTube, X, …). «Спрашивать» показывает заглушку, пока вы не разрешите каждый источник; «Всегда» загружает их автоматически; «Выкл» оставляет обычные ссылки.',
embedsAsk: 'Спрашивать',
embedsAlways: 'Всегда',
embedsOff: 'Выкл',
embedsReset: count =>
`Сбросить ${count} ${RU_NOUN(count, 'разрешённый сервис', 'разрешённых сервиса', 'разрешённых сервисов')}`,
resumeLastSessionTitle: 'Открывать последний чат при запуске',
resumeLastSessionDesc:
'Продолжайте с того места, где остановились. Выключите, чтобы всегда начинать с нового чата.',
product: 'Продукт',
productDesc: 'Дружелюбная активность инструментов с краткими сводками.',
technical: 'Технический',
technicalDesc: 'Показывать сырые аргументы/результаты инструментов и низкоуровневые детали.',
themeTitle: 'Тема',
themeDesc: 'Только палитры для приложения. Выбранный режим применяется поверх.',
themeProfileNote: profile => `Сохранено для профиля ${profile} — у каждого профиля своя тема.`,
installTitle: 'Установить из VS Code',
installDesc:
'Вставьте id расширения с Marketplace (напр. dracula-theme.theme-dracula), чтобы преобразовать его цветовую тему в палитру приложения.',
installPlaceholder: 'publisher.extension',
installButton: 'Установить',
installing: 'Установка…',
installError: 'Не удалось установить эту тему.',
installed: name => `Установлена «${name}».`,
removeTheme: 'Удалить тему',
importedBadge: 'Импортирована',
pet: {
title: 'Питомец',
intro:
'Заберите анимированного питомца из petdex, который парит над приложением и реагирует на действия Hermes — «бегает», пока выполняются инструменты, радуется успеху и хмурится при ошибках.',
restartHint:
'Питомцам нужен быстрый перезапуск — текущее приложение запустилось до появления этой функции. Выйдите из Hermes и откройте снова, затем вернитесь сюда.',
on: 'Вкл',
off: 'Выкл',
scaleTitle: 'Размер',
scaleDesc: 'Меняет размер парящего питомца. Применяется мгновенно везде.',
roamTitle: 'Блуждание',
roamDesc: 'Позволить питомцу самому бродить по окну, пока всё бездействие.',
chooseTitle: 'Выбрать питомца',
chooseDesc: 'Выбор установит его (при необходимости) и сделает активным.',
searchPlaceholder: 'Поиск питомцев…',
unreachable: 'Не удалось связаться с галереей petdex. Проверьте соединение и откройте страницу заново.',
noMatch: query => `Нет питомцев, подходящих под «${query}».`,
installedTag: 'установлен',
generatedTag: 'Сгенерирован',
countCapped: (cap, total) => `Показаны ${cap} из ${total} — начните вводить, чтобы сузить выбор.`,
count: n => `${n} ${RU_PLURAL(n, 'питомец', 'питомца', 'питомцев')}.`,
uninstall: name => `Удалить ${name}`,
delete: name => `Удалить навсегда ${name}`,
deleteTitle: name => `Удалить ${name}?`,
deleteBody: 'Питомец будет удалён безвозвратно — переустановить его будет нельзя.',
deleteConfirm: 'Удалить',
rename: name => `Переименовать ${name}`,
renameTitle: 'Переименовать питомца',
renamePlaceholder: 'Имя вашего питомца',
renameSave: 'Сохранить',
exportPet: name => `Экспортировать ${name}`,
adoptFailed: slug => `Не удалось забрать ${slug}`,
uninstallFailed: slug => `Не удалось удалить ${slug}`,
renameFailed: slug => `Не удалось переименовать ${slug}`,
exportFailed: slug => `Не удалось экспортировать ${slug}`,
noneAvailable: 'Сейчас нет доступных питомцев для включения.',
turnOnFailed: 'Не удалось включить питомца.',
turnOffFailed: 'Не удалось выключить питомца.'
}
},
fieldLabels: defineFieldCopy({
model: 'Модель по умолчанию',
modelContextLength: 'Окно контекста',
fallbackProviders: 'Резервные модели',
toolsets: 'Включённые наборы инструментов',
timezone: 'Часовой пояс',
display: {
personality: 'Личность',
showReasoning: 'Блоки рассуждений'
},
desktop: {
repoScanEnabled: 'Автоматическое обнаружение репозиториев',
repoScanRoots: 'Корни обнаружения репозиториев',
repoScanExcludePaths: 'Исключаемые пути репозиториев'
},
agent: {
maxTurns: 'Макс. шагов агента',
imageInputMode: 'Вложения изображений',
apiMaxRetries: 'Повторы API',
serviceTier: 'Уровень сервиса',
toolUseEnforcement: 'Принудительное использование инструментов'
},
terminal: {
cwd: 'Рабочий каталог',
backend: 'Бэкенд выполнения',
timeout: 'Тайм-аут команд',
persistentShell: 'Персистентная оболочка',
envPassthrough: 'Пропуск переменных окружения',
dockerImage: 'Образ Docker',
singularityImage: 'Образ Singularity',
modalImage: 'Образ Modal',
daytonaImage: 'Образ Daytona'
},
fileReadMaxChars: 'Лимит чтения файла',
toolOutput: {
maxBytes: 'Лимит вывода терминала',
maxLines: 'Лимит страниц файла',
maxLineLength: 'Лимит длины строки'
},
codeExecution: {
mode: 'Режим выполнения кода'
},
approvals: {
mode: 'Режим подтверждения',
timeout: 'Тайм-аут подтверждения',
mcpReloadConfirm: 'Подтверждать перезагрузку MCP'
},
commandAllowlist: 'Список разрешённых команд',
security: {
redactSecrets: 'Скрывать секреты',
allowPrivateUrls: 'Разрешать частные URL'
},
browser: {
allowPrivateUrls: 'Частные URL браузера',
autoLocalForPrivateUrls: 'Локальный браузер для частных URL'
},
checkpoints: {
enabled: 'Чекпоинты файлов',
maxSnapshots: 'Лимит чекпоинтов'
},
voice: {
recordKey: 'Горячая клавиша голосового ввода',
maxRecordingSeconds: 'Макс. длительность записи',
autoTts: 'Зачитывать ответы вслух'
},
stt: {
enabled: 'Распознавание речи',
echoTranscripts: 'Повторять расшифровки',
provider: 'Провайдер распознавания речи',
local: {
model: 'Локальная модель транскрипции',
language: 'Язык транскрипции'
},
openai: {
model: 'Модель STT OpenAI'
},
groq: {
model: 'Модель STT Groq'
},
mistral: {
model: 'Модель STT Mistral'
},
elevenlabs: {
modelId: 'Модель STT ElevenLabs',
languageCode: 'Язык ElevenLabs',
tagAudioEvents: 'Метки звуковых событий',
diarize: 'Разделение спикеров'
}
},
tts: {
provider: 'Провайдер синтеза речи',
edge: {
voice: 'Голос Edge'
},
openai: {
model: 'Модель TTS OpenAI',
voice: 'Голос OpenAI'
},
elevenlabs: {
voiceId: 'Голос ElevenLabs',
modelId: 'Модель ElevenLabs'
},
xai: {
voiceId: 'Голос xAI (Grok)',
language: 'Язык xAI',
speed: 'Скорость воспроизведения xAI',
autoSpeechTags: 'Автомаркеры речи xAI',
optimizeStreamingLatency: 'Оптимизация задержки стриминга xAI',
sampleRate: 'Частота дискретизации xAI',
bitRate: 'Битрейт xAI'
},
minimax: {
model: 'Модель TTS MiniMax',
voiceId: 'Голос MiniMax'
},
mistral: {
model: 'Модель TTS Mistral',
voiceId: 'Голос Mistral'
},
gemini: {
model: 'Модель TTS Gemini',
voice: 'Голос Gemini'
},
neutts: {
model: 'Модель NeuTTS',
device: 'Устройство NeuTTS'
},
kittentts: {
model: 'Модель KittenTTS',
voice: 'Голос KittenTTS'
},
piper: {
voice: 'Голос Piper'
},
deepinfra: {
model: 'Модель TTS DeepInfra',
voice: 'Голос DeepInfra'
}
},
memory: {
memoryEnabled: 'Персистентная память',
userProfileEnabled: 'Профиль пользователя',
memoryCharLimit: 'Бюджет памяти',
userCharLimit: 'Бюджет профиля',
provider: 'Провайдер памяти'
},
context: {
engine: 'Движок контекста'
},
compression: {
enabled: 'Авто-сжатие',
threshold: 'Порог сжатия',
targetRatio: 'Целевое сжатие',
protectLastN: 'Защищённые недавние сообщения'
},
delegation: {
model: 'Модель субагента',
provider: 'Провайдер субагента',
maxIterations: 'Лимит ходов субагента',
maxConcurrentChildren: 'Параллельные субагенты',
childTimeoutSeconds: 'Тайм-аут субагента',
reasoningEffort: 'Глубина рассуждений субагента'
},
updates: {
nonInteractiveLocalChanges: 'Локальные изменения при обновлении из приложения'
}
}),
fieldDescriptions: defineFieldCopy({
model: 'Используется для новых чатов, если вы не выберете другую модель в композере.',
modelContextLength: 'Оставьте 0, чтобы использовать обнаруженное окно контекста выбранной модели.',
fallbackProviders:
'Резервные записи provider:model, которые будут пробоваться, если модель по умолчанию не сработает.',
display: {
personality: 'Стиль ассистента по умолчанию для новых сеансов.',
showReasoning: 'Показывать блоки рассуждений, когда бэкенд их предоставляет.'
},
desktop: {
repoScanEnabled: 'Сканировать локальные папки на Git-репозитории, чтобы показывать их в Проектах.',
repoScanRoots: 'Папки для сканирования. Оставьте пустым, чтобы сканировать домашний каталог.',
repoScanExcludePaths: 'Папки и их вложенные, которые нужно пропускать при обнаружении репозиториев.'
},
timezone: 'Идентификатор часового пояса IANA. Пустое значение — системный часовой пояс.',
agent: {
imageInputMode: 'Управляет тем, как вложения изображений отправляются модели.',
maxTurns: 'Верхний предел ходов с вызовами инструментов, после которого Hermes останавливает запуск.'
},
terminal: {
cwd: 'Папка проекта по умолчанию для инструментов и терминала.',
persistentShell: 'Сохранять состояние оболочки между командами, если бэкенд это поддерживает.',
envPassthrough: 'Переменные окружения, которые передавать в выполнение инструментов.',
dockerImage: 'Образ контейнера, используемый, когда бэкенд выполнения — Docker.',
singularityImage: 'Образ, используемый, когда бэкенд выполнения — Singularity.',
modalImage: 'Образ, используемый, когда бэкенд выполнения — Modal.',
daytonaImage: 'Образ, используемый, когда бэкенд выполнения — Daytona.'
},
codeExecution: {
mode: 'Насколько строго выполнение кода ограничено текущим проектом.'
},
fileReadMaxChars: 'Максимальное число символов, которые Hermes может прочитать из одного запроса к файлу.',
approvals: {
mode: 'Как Hermes обрабатывает команды, требующие явного подтверждения.',
timeout: 'Как долго запросы подтверждения ждут перед тайм-аутом.'
},
security: {
redactSecrets: 'Скрывать обнаруженные секреты из видимого для модели содержимого, когда это возможно.'
},
checkpoints: {
enabled: 'Создавать снимки для отката перед правкой файлов.'
},
memory: {
memoryEnabled: 'Сохранять долговременные воспоминания, которые могут помочь будущим сеансам.',
userProfileEnabled: 'Поддерживать компактный профиль предпочтений пользователя.'
},
context: {
engine: 'Стратегия управления длинными диалогами у предела контекста.'
},
compression: {
enabled: 'Сжимать более старый контекст, когда диалоги становятся большими.'
},
voice: {
autoTts: 'Автоматически зачитывать ответы ассистента.'
},
tts: {
xai: {
voiceId: 'ID голоса xAI (например, eve) или ID пользовательского голоса.',
language: 'Код языка речи (например, en, pt-BR) или «auto» для автоопределения.',
speed: 'Скорость воспроизведения. 0.7 = медленнее, 1.0 = нормально, 1.5 = быстрее.',
autoSpeechTags:
'Позволить LLM вставлять выразительные звуковые теги ([laughing], [sighs]) в сценарий перед синтезом.',
optimizeStreamingLatency:
'Компромисс между задержкой и качеством. 0 = лучшее качество, 2 = минимальная задержка.',
sampleRate: 'Частота дискретизации аудио в Гц. Выше = лучшее качество, большие файлы.',
bitRate: 'Битрейт MP3 в бит/с. Действует только когда кодек — mp3.'
},
neutts: {
device: 'Локальное устройство инференса для NeuTTS.'
}
},
stt: {
enabled: 'Включать локальное или провайдерное распознавание речи.',
echoTranscripts: 'Возвращать в чат «сырую» 🎙️ расшифровку голосовых сообщений.',
elevenlabs: {
languageCode: 'Необязательный код языка ISO-639-3. Пустое значение — автоопределение ElevenLabs.'
}
},
updates: {
nonInteractiveLocalChanges:
'Когда Hermes обновляет себя из приложения (без запроса в терминале), сохранять локальные правки исходников (stash) или выбрасывать (discard). Обновления из терминала всегда спрашивают.'
}
}),
about: {
heading: 'Hermes Desktop',
version: value => `Версия ${value}`,
versionUnavailable: 'Версия недоступна',
bundleOutOfSync: 'Сборка приложения устарела',
bundleOutOfSyncDesc:
'Рантайм Hermes обновлён, но само приложение — ещё старая сборка: новые функции интерфейса (например, Bot Mode) не появятся до обновления. Запустите обновление ниже, чтобы пересобрать приложение. Если предупреждение не исчезнет, переустановите с последнего установщика.',
bundleOutOfSyncAction: 'Скачать установщик',
updates: 'Обновления',
checkNow: 'Проверить сейчас',
checking: 'Проверка…',
seeWhatsNew: 'Смотреть, что нового',
updateNow: 'Обновить сейчас',
releaseNotes: 'Заметки о выпуске',
onLatest: 'У вас последняя версия.',
installing: 'Сейчас устанавливается обновление.',
cantUpdate: 'Эта сборка не может обновляться изнутри приложения.',
cantReach: 'Не удалось связаться с сервером обновлений.',
tapCheck: 'Нажмите «Проверить сейчас», чтобы найти обновления.',
updateReady: count =>
`Готово новое обновление (включено ${count} ${RU_PLURAL(count, 'изменение', 'изменения', 'изменений')}).`,
updateReadyUnknown: 'Готово новое обновление.',
lastChecked: age => `Проверено ${age}`,
justNowSuffix: ' · только что',
automaticUpdates: 'Автоматические обновления',
automaticUpdatesDesc: 'Hermes автоматически проверяет обновления в фоне и сообщает, когда они готовы.',
branchCommit: (branch, commit) => `Ветка ${branch} · Коммит ${commit}`,
never: 'никогда',
justNow: 'только что',
minAgo: count => `${count} ${RU_NOUN(count, 'минуту', 'минуты', 'минут')} назад`,
hoursAgo: count => `${count} ${RU_NOUN(count, 'час', 'часа', 'часов')} назад`,
daysAgo: count => `${count} ${RU_NOUN(count, 'день', 'дня', 'дней')} назад`
},
config: {
none: 'Нет',
noneParen: '(нет)',
builtinOnly: 'Только встроенные',
notSet: 'Не задано',
commaSeparated: 'значения через запятую',
searchPlaceholder: 'Поиск…',
noResults: 'Ничего не найдено',
systemDefault: 'Системное по умолчанию',
loading: 'Загрузка конфигурации Hermes…',
emptyTitle: 'Настраивать нечего',
emptyDesc: 'В этом разделе нет настраиваемых параметров.',
failedLoad: 'Не удалось загрузить настройки',
autosaveFailed: 'Не удалось сохранить автоматически',
imported: 'Конфигурация импортирована',
invalidJson: 'Неверный JSON конфигурации',
toolsetsWipeConfirm:
'Удалить все включённые наборы инструментов? Это отключит память, терминал, веб-поиск, делегирование и большинство других инструментов, пока вы не включите их снова.',
keepAwakeTitle: 'Не давать компьютеру засыпать',
keepAwakeDesc:
'Не даёт этой машине засыпать, чтобы долгие или ночные прогоны продолжались. Экран при этом может гаснуть.',
disableF12Title: 'Отключить F12 DevTools',
disableF12Desc:
'Блокирует открытие Developer Tools по F12. Ctrl+Shift+I (на Mac — Cmd+Opt+I) продолжает работать.',
attachmentSizeTitle: 'Макс. размер превью / загрузки изображений',
attachmentSizeDesc:
'Насколько большой локальный файл приложение будет загружать для превью и вложений, в МБ. По умолчанию 16. Для удалённых неграфических вложений действует отдельный лимит 256 МБ. Слишком большое значение загружает весь файл в память и может подвесить или уронить приложение.',
attachmentSizeUnit: 'МБ',
attachmentSizeLabel: 'Макс. размер превью / загрузки изображений в мегабайтах'
},
quickEntry: {
enabledTitle: 'Быстрый ввод',
enabledDesc:
'Глобальным горячим ключом вызывайте маленький композер откуда угодно и отправляйте запрос, не открывая Hermes.',
shortcutTitle: 'Горячий ключ быстрого ввода',
shortcutDesc: 'Нужен хотя бы один модификатор, например CommandOrControl+Shift+Space.',
active: 'Горячий ключ активен.',
takenBy: 'Это сочетание уже занято другим приложением — выберите другое.',
invalidShortcut: 'Некорректное сочетание. Включите хотя бы одну модифицирующую клавишу.'
},
credentials: {
pasteKey: 'Вставить ключ',
pasteLabelKey: label => `Вставить ключ ${label}`,
optional: 'Необязательно',
enterValueFirst: 'Сначала введите значение.',
couldNotSave: 'Не удалось сохранить учётные данные.',
remove: 'Удалить',
getKey: 'Получить ключ',
saving: 'Сохранение'
},
envActions: {
actions: 'Действия',
manageInKeys: 'Управлять в API-ключах',
docs: 'Документация',
hideValue: 'Скрыть значение',
revealValue: 'Показать значение',
replace: 'Заменить',
set: 'Установить',
clear: 'Очистить'
},
// v2 multi-connection registry: Settings → Gateways.
connections: {
title: 'Зарегистрированные шлюзы',
intro:
'Управляйте этим устройством и всеми шлюзами Hermes, до которых можно дотянуться через удалённые, SSH или Cloud-соединения.',
stagedNote:
'Переключайтесь между шлюзами из раздела «Сеансы». Профили, чаты, мессенджеры и cron-задачи остаются за своим шлюзом; работа на других шлюзах продолжается.',
launchModeTitle: 'При запуске возвращаться к «Сеансам» на последнем шлюзе',
launchModeDesc: 'Если выключено, «Сеансы» открываются на основном шлюзе.',
searchPlaceholder: 'Поиск шлюзов…',
noSearchResults: 'Шлюзы, подходящие под поиск, не найдены.',
loadFailed: 'Не удалось загрузить соединения',
currentPill: 'Текущий',
primaryPill: 'Основной',
managedPill: 'Управляется приложением',
addConnection: 'Добавить соединение',
editConnection: 'Изменить',
removeConnection: 'Удалить',
removeConfirmTitle: 'Удалить это соединение?',
removeConfirmDesc: label =>
`«${label}» будет удалено из этого приложения. Сама инстанция не затрагивается — её можно добавить снова в любой момент.`,
makePrimary: 'Сделать основным',
testConnection: 'Проверить',
testOk: 'Доступен',
testFailed: 'Проверка соединения не удалась',
saveFailed: 'Не удалось сохранить соединение',
removeFailed: 'Не удалось удалить соединение',
updateAll: 'Обновить все инстансы',
updateAllRunning: 'Обновление всех инстансов…',
updateAllDone: 'Обновления разосланы',
updateAllFailed: 'Не удалось разослать обновления',
updateSkippedCloud: 'Управляется Hermes Cloud',
kindLocal: 'Локальный',
kindRemote: 'Удалённый шлюз',
kindCloud: 'Hermes Cloud',
kindSsh: 'SSH',
kindLocalDesc: 'Рантайм Hermes, которым управляет это приложение.',
kindRemoteDesc: 'Шлюз Hermes, доступный по HTTP(S) — LAN, Tailscale или интернет.',
kindCloudDesc: 'Хостинговая инстанция, обнаруженная через ваш аккаунт Hermes Cloud.',
kindSshDesc: 'Установка Hermes, доступная по SSH.',
labelTitle: 'Имя',
labelDesc:
'Обязательно. Показывается везде, где фигурирует эта инстанция; должно быть уникальным (напр. «Домашняя лаборатория», «Рабочий ноутбук»).',
labelPlaceholder: 'Домашняя лаборатория',
urlTitle: 'URL шлюза',
sshHostTitle: 'SSH-хост',
headersTitle: 'Дополнительные заголовки шлюза',
headersDesc:
'Отправляются с каждым HTTP- и WebSocket-запросом к этому шлюзу — например, для прокси доступа вроде Cloudflare Access (CF-Access-Client-Id / CF-Access-Client-Secret). Значения хранятся зашифрованными. Заголовки, которыми управляет Hermes (Authorization, Cookie, Host…), игнорируются.',
headerValuePlaceholder: 'Значение',
headerValueSaved: 'Сохранено — оставьте пустым, чтобы не менять',
headerAdd: 'Добавить заголовок',
headerRemove: 'Удалить',
duplicateLocal: 'Это приложение уже управляет локальным соединением — их может быть только одно.',
duplicateUrl: label => `Соединение с этим URL шлюза уже есть («${label}»).`,
duplicateSsh: label => `Соединение с этим SSH-хостом уже есть («${label}»).`,
sameBackendHint: label => `Тот же бэкенд, что и «${label}»`,
localAddHint: 'Локальное недоступно: управляемое локальное соединение уже существует (их всегда только одно).',
cloudAddHint:
'Подсказка: вход в Hermes Cloud выше автоматически обнаруживает ваших агентов — эту форму используйте только для ручной регистрации известного URL инстанса.',
save: 'Сохранить соединение',
saving: 'Сохранение…',
cancel: 'Отмена',
empty: 'Соединения пока не зарегистрированы.'
},
gateway: {
loading: 'Загрузка настроек шлюза…',
unavailableTitle: 'Настройки шлюза недоступны',
unavailableDesc: 'IPC-мост приложения не предоставляет настройки шлюза.',
title: 'Подключение шлюза',
envOverride: 'переопределение переменными окружения',
intro:
'По умолчанию — локальный. Используйте удалённый, когда приложение должно управлять бэкендом Hermes в другом месте. Соединения шлюзов — на уровне машины; профили обнаруживаются из подключённых шлюзов.',
envOverrideTitle: 'Переменные окружения управляют этой сессией приложения.',
envOverrideDesc:
'Сбросьте HERMES_DESKTOP_REMOTE_URL и HERMES_DESKTOP_REMOTE_TOKEN, чтобы использовать сохранённую настройку ниже.',
modeTitle: 'Режим подключения',
localTitle: 'Локальный шлюз',
localDesc: 'Запускает приватный бэкенд Hermes на localhost. Это значение по умолчанию, работает офлайн.',
remoteTitle: 'Удалённый шлюз',
remoteDesc: 'Подключает это приложение к удалённому бэкенду Hermes.',
remoteAuthHint:
'Хостинговые шлюзы используют OAuth или логин/пароль; самохостинговые могут использовать токен сессии.',
cloudTitle: 'Hermes Cloud',
cloudDesc: 'Войдите в Hermes Cloud один раз и выбирайте агентов из своего аккаунта — без вставки URL.',
cloudSignInTitle: 'Hermes Cloud',
cloudSignIn: 'Войти в Hermes Cloud',
cloudSignedIn: 'Вы вошли в Hermes Cloud',
cloudNeedsSignIn: 'Войдите в Hermes Cloud, чтобы обнаружить агентов в вашем аккаунте.',
cloudSignedInDesc: 'Вы вошли. Выберите агента ниже; сессия обновляется автоматически.',
cloudAgentsTitle: 'Ваши агенты',
cloudOrgPickerTitle: 'Выберите организацию',
cloudOrgSelect: 'Выбрать',
cloudOrgChange: 'Сменить организацию',
cloudOrgRole: role => `Роль: ${role}`,
cloudLoadingAgents: 'Загрузка ваших агентов…',
cloudNoAgents: {
before: 'Агенты на этом аккаунте не найдены. Создайте агента в',
linkText: 'портале Nous',
after: ', затем обновите страницу.'
},
cloudRefresh: 'Обновить',
cloudConnect: 'Подключиться',
cloudSavedTitle: 'Сохранённые облачные шлюзы',
cloudSavedDesc:
'Используйте сохранённый шлюз без изменения шлюза по умолчанию. Войдите ниже, чтобы добавить экземпляры. Имена и вход — в списке сохранённых подключений.',
cloudUseSaved: 'Использовать шлюз',
cloudActive: 'Активен в этом окне',
cloudConnecting: 'Подключение…',
cloudDiscoverFailed: 'Не удалось загрузить агентов Hermes Cloud',
cloudConnectFailed: 'Не удалось подключиться к этому агенту',
cloudSignInFailed: 'Не удалось войти в Hermes Cloud',
cloudSignedOutTitle: 'Выход из Hermes Cloud',
cloudSignedOutMessage: 'Сессия Hermes Cloud сброшена.',
cloudConnectedTitle: 'Подключено',
cloudConnectedPill: 'Подключено',
cloudConnectedTo: name => `Подключено к ${name}.`,
cloudAgentProvisioning: 'Развёртывание…',
cloudStatusLabel: status => `Статус: ${status}`,
remoteUrlTitle: 'Удалённый URL',
remoteUrlDesc: 'Базовый URL удалённого бэкенда дашборда. Поддерживаются префиксы пути, например /hermes.',
probing: 'Проверяем, как аутентифицируется этот шлюз…',
probeError:
'Пока не удалось связаться с этим шлюзом. Проверьте URL — способ аутентификации появится, когда он ответит.',
signedIn: 'Вы вошли',
signIn: 'Войти',
signOut: 'Выйти',
signInWith: provider => `Войти через ${provider}`,
authTitle: 'Аутентификация',
authSignedInPassword: 'Этот шлюз использует логин и пароль. Вы вошли; сессия обновляется автоматически.',
authSignedInOauth: 'Этот шлюз использует OAuth. Вы вошли; сессия обновляется автоматически.',
authNeedsPassword: 'Этот шлюз использует логин и пароль. Войдите, чтобы авторизовать это приложение.',
authNeedsOauth: provider =>
`Этот шлюз использует OAuth. Войдите через ${provider}, чтобы авторизовать это приложение.`,
tokenTitle: 'Токен сессии',
tokenDesc:
'Токен сессии дашборда для доступа через REST и WebSocket. Оставьте пустым, чтобы сохранить текущий токен.',
existingToken: value => `Текущий токен ${value}`,
savedToken: 'сохранён',
pasteSessionToken: 'Вставьте токен сессии',
plainTextConfirmTitle: 'Хранить токен шлюза в открытом виде?',
plainTextConfirmDesc:
'Служба системного хранилища ключей на этой машине не найдена, поэтому токен будет сохранён без шифрования в файле настроек соединения приложения, и его сможет прочитать любой процесс, работающий от вашего имени. Для шифрованного хранилища установите и включите GNOME Keyring или KWallet.',
plainTextConfirmAction: 'Сохранить в открытом виде',
plainTextStoredTitle: 'Токен сохранён в открытом виде',
plainTextStoredDesc:
'Безопасное хранилище недоступно, поэтому сохранённый токен хранится без шифрования в файле настроек соединения приложения на этой машине. Установите и включите GNOME Keyring или KWallet для шифрования.',
testRemote: 'Проверить удалённый',
saveForRestart: 'Сохранить до следующего перезапуска',
saveAndReconnect: 'Сохранить и переподключиться',
diagnostics: 'Диагностика',
diagnosticsDesc: 'Показать desktop.log в файловом менеджере — полезно, если шлюз не запускается.',
openLogs: 'Открыть логи',
incompleteTitle: 'Настройки удалённого шлюза неполные',
incompleteSignIn: 'Введите удалённый URL и войдите, прежде чем переключаться на удалённый режим.',
incompleteToken: 'Введите удалённый URL и токен сессии, прежде чем переключаться на удалённый режим.',
incompleteSignInTest: 'Введите удалённый URL и войдите, прежде чем проверять.',
incompleteTokenTest: 'Введите удалённый URL и токен сессии, прежде чем проверять.',
enterUrlFirst: 'Сначала введите удалённый URL.',
restartingTitle: 'Перезапуск соединения шлюза',
savedTitle: 'Настройки шлюза сохранены',
restartingMessage: 'Hermes Desktop переподключится с сохранёнными настройками — оболочка останется открытой.',
savedMessage: 'Сохранено для следующего перезапуска.',
connectedTo: (baseUrl, version) => `Подключено к ${baseUrl}${version ? ` · Hermes ${version}` : ''}`,
reachableTitle: 'Удалённый шлюз доступен',
signedOutTitle: 'Вы вышли',
signedOutMessage: 'Сессия удалённого шлюза сброшена.',
failedLoad: 'Не удалось загрузить настройки шлюза',
signInFailed: 'Не удалось войти',
signOutFailed: 'Не удалось выйти',
testFailed: 'Проверка удалённого шлюза не удалась',
applyFailed: 'Не удалось применить настройки шлюза',
saveFailed: 'Не удалось сохранить настройки шлюза',
sshTitle: 'Подключение по SSH',
sshDesc:
'Hermes запускается на удалённой машине по SSH и туннелируется в это приложение — ничего не нужно запускать или открывать самим. Требуется рабочая SSH-аутентификация по ключу на хост.',
sshTrustHint:
'Первый предъявленный ключ хоста доверяется и фиксируется; последующие изменения приведут к отказу.',
sshHostTitle: 'Хост',
sshHostDesc: 'user@host или псевдоним Host из ~/.ssh/config.',
sshHostPick: 'Выбрать хост…',
sshHostPickTitle: 'Хост',
sshHostPickDesc: 'Псевдоним Host из ~/.ssh/config, либо «Свой», чтобы ввести вручную.',
sshHostCustom: 'Свой (ввести вручную)…',
sshUserTitle: 'Пользователь',
sshUserDesc: 'Пусто = ~/.ssh/config или ваш текущий пользователь.',
sshUserPlaceholder: 'из ~/.ssh/config',
sshPortTitle: 'Порт',
sshPortDesc: 'Пусто = 22 или порт из ~/.ssh/config.',
sshKeyTitle: 'Файл ключа',
sshKeyDesc: 'Путь к закрытому ключу. Пусто = ssh-agent или ~/.ssh/config.',
sshHermesPathTitle: 'Путь к Hermes (необязательно)',
sshHermesPathDesc: 'Полный путь к бинарнику hermes на удалённой машине. Пусто = автоопределение.',
sshHermesPathPlaceholder: 'автоопределение',
sshTestConnection: 'Проверить SSH',
sshConnect: 'Подключиться',
sshButtonsHint: '«Сохранить» применится при следующем запуске. «Подключиться» переподключится сейчас.',
sshReachable: (host, platform) => `Доступен: ${host} (${platform}) — Hermes найден`,
sshIncompleteHost: 'Введите SSH-хост перед подключением.',
sshErrUnreachable: 'Не удалось достичь этого хоста по SSH. Проверьте хост, порт и сеть.',
sshErrAuth:
'SSH-аутентификация не удалась. Загрузите ключ в ssh-agent (ssh-add) или задайте IdentityFile в ~/.ssh/config — Hermes запускает ssh в неинтерактивном режиме.',
sshErrHostKey:
'Ключ хоста ИЗМЕНИЛСЯ с последнего подключения. Убедитесь, что это ожидаемо, затем выполните ssh-keygen -R <host> и переподключитесь.',
sshErrNotInstalled:
'Hermes не установлен на удалённой машине. Установите его там (curl -fsSL https://hermes-agent.nousresearch.com/install.sh | sh) или задайте путь к Hermes.',
sshErrPlatform:
'Неподдерживаемая удалённая платформа. SSH-режим Hermes Desktop поддерживает удалённые хосты Linux, macOS и Windows.',
sshErrTimeout: 'SSH-соединение истекло. Хост может быть недоступен или «спит».',
sshErrUpdateRequired: 'Перед подключением через SSH обновите Hermes на удалённой машине.',
sshErrUnknown: 'SSH-соединение не удалось.'
},
keys: {
loading: 'Загрузка API-ключей и учётных данных…',
failedLoad: 'Не удалось загрузить API-ключи',
empty: 'В этой категории пока ничего не настроено.'
},
search: {
placeholder: 'Поиск по всем настройкам…',
pill: 'Поиск'
},
profileScope: {
appliesTo: 'Применяется к',
editsProfile: profile => `Изменения на этой странице применяются к профилю «${profile}».`
},
mcp: {
loading: 'Загрузка MCP-серверов…',
failedLoad: 'Не удалось загрузить конфигурацию MCP',
nameRequiredTitle: 'Нужно имя',
nameRequiredMessage: 'Задайте этому MCP-серверу ключ конфигурации.',
objectRequired: 'Конфигурация сервера должна быть JSON-объектом',
invalidJson: 'Неверный JSON MCP',
saveFailed: 'Не удалось сохранить',
removeFailed: 'Не удалось удалить',
gatewayUnavailableTitle: 'Шлюз недоступен',
gatewayUnavailableMessage: 'Переподключите шлюз, прежде чем перезагружать MCP.',
reloadedTitle: 'Инструменты MCP перезагружены',
reloadedMessage: 'Новые схемы инструментов применяются к новым ходам.',
reloadFailed: 'Не удалось перезагрузить MCP',
savedTitle: 'MCP-сервер сохранён',
savedMessage: name => `${name} применится после перезагрузки MCP.`,
newServer: 'Новый сервер',
reload: 'Перезагрузить MCP',
reloading: 'Перезагрузка…',
emptyTitle: 'MCP-серверов нет',
emptyDesc: 'Добавьте stdio- или HTTP-сервер, чтобы получить инструменты MCP.',
disabled: 'отключён',
editServer: 'Изменить сервер',
name: 'Имя',
serverJson: 'JSON сервера',
remove: 'Удалить',
saveServer: 'Сохранить сервер',
test: 'Проверить соединение',
testing: 'Проверка…',
testOk: count =>
`Подключено — доступно ${count} ${RU_PLURAL(count, 'инструмент', 'инструмента', 'инструментов')}`,
testFailed: 'Не удалось подключиться',
enableServer: name => `Включить ${name}`,
disableServer: name => `Отключить ${name}`,
serverEnabled: name => `${name} включён — применится к новым сеансам.`,
serverDisabled: name => `${name} отключён — применится к новым сеансам.`,
toggleFailed: (name, enabled) => `Не удалось ${enabled ? 'включить' : 'отключить'} ${name}`,
tabServers: 'Серверы',
tabCatalog: 'Каталог',
catalogLoading: 'Загрузка каталога MCP…',
catalogLoadFailed: 'Не удалось загрузить каталог MCP',
catalogEmpty: 'Записей каталога нет.',
catalogInstalled: 'Установлен',
catalogEnabled: 'Включён',
catalogNeedsInstall: 'Нужна сборка',
catalogInstall: 'Установить',
catalogInstalling: 'Установка…',
catalogInstallStarted: name => `Установка ${name}… применится к новым сеансам после завершения.`,
catalogInstallFailed: name => `Не удалось установить ${name}`,
catalogEnvPrompt: name => `${name} требует учётные данные`,
catalogEnvRequired: 'Заполните обязательные значения перед установкой.',
capabilitySummary: (tools, prompts, resources) =>
`${[`${tools} ${RU_NOUN(tools, 'инструмент', 'инструмента', 'инструментов')}`, ...(prompts ? [`${prompts} ${RU_NOUN(prompts, 'промпт', 'промпта', 'промптов')}`] : []), ...(resources ? [`${resources} ${RU_NOUN(resources, 'ресурс', 'ресурса', 'ресурсов')}`] : [])].join(', ')} включено`,
costTokens: tokens => `~${tokens} ток/вызов`,
usage30d: uses => `${uses} ${RU_NOUN(uses, 'использование', 'использования', 'использований')}/30д`,
unusedPill: 'не используется',
statusConnecting: 'Подключение…',
statusNeedsAuth: 'Нужна аутентификация',
statusError: 'Ошибка',
statusOff: 'Выкл',
allServers: 'Все серверы',
authenticatedTitle: 'Аутентифицирован',
authenticatedMessage: (server, count) =>
`${server}: ${count} ${RU_PLURAL(count, 'инструмент', 'инструмента', 'инструментов')}`,
waitingForBrowser: 'Ожидание браузера…',
authenticate: 'Аутентифицироваться',
unsavedConnect: 'Не сохранено — сохраните mcp.json, чтобы подключиться.',
enableTool: tool => `Включить ${tool}`,
disableTool: tool => `Отключить ${tool}`,
noOutput: 'Вывода пока нет.',
deepLinkTitle: 'Добавить MCP-сервер?',
deepLinkDescription:
'Ссылка запросила добавить этот MCP-сервер в Hermes. Проверьте конфигурацию ниже — она пришла из ссылки, а не из Hermes.',
deepLinkStdioWarning:
'Этот сервер запускает локальный процесс на вашей машине командой, показанной ниже. Продолжайте только если доверяете источнику.',
deepLinkConfirm: 'Добавить сервер',
deepLinkNameInvalid: 'Имя: 1–64 символа — буквы, цифры, точки, дефисы или подчёркивания.',
deepLinkNameConflict: name => `Сервер с именем ${name} уже существует — выберите другое имя или отмените.`,
deepLinkErrorTitle: 'Ссылка установки MCP отклонена',
deepLinkErrorName: 'Имя сервера в ссылке отсутствует или некорректно.',
deepLinkErrorConfig: 'Конфигурация в ссылке не является корректным JSON в base64.',
deepLinkErrorShape: 'Конфигурация должна быть JSON-объектом со строковым полем `url` или `command`.',
deepLinkErrorUrl: 'Разрешены только URL серверов http:// и https://.',
deepLinkErrorTooLarge: 'Пакет конфигурации превышает лимит 32 КБ.',
importButton: 'Импортировать',
importPlaceholder:
'Вставьте фрагмент mcp.json, команду npx/docker, строку claude mcp add, URL или ссылку Cursor…',
importNoMatch: 'В вставленном тексте не распознана конфигурация сервера.',
importConfirm: 'Добавить в mcp.json',
importConfirmMany: count => `Добавить ${count} ${RU_PLURAL(count, 'сервер', 'сервера', 'серверов')} в mcp.json`
},
model: {
loading: 'Загрузка конфигурации модели…',
appliesDesc:
'Применяется к новым сеансам. Для горячей смены модели в активном чате используйте выборщик модели в композере.',
provider: 'Провайдер',
model: 'Модель',
applying: 'Применение…',
defaultsLabel: 'По умолчанию',
reasoning: 'Рассуждения',
reasoningOff: 'Выкл',
defaultsFailed: 'Не удалось сохранить модель по умолчанию',
auxiliaryTitle: 'Вспомогательные модели',
resetAllToMain: 'Сбросить всё на основную',
auxiliaryDesc:
'Вспомогательные задачи по умолчанию выполняются основной моделью. Назначьте отдельную модель любой задаче, чтобы переопределить.',
setToMain: 'На основную',
change: 'Изменить',
autoUseMain: 'авто · использовать основную модель',
providerDefault: '(по умолчанию провайдера)',
fallbackAdd: 'Добавить запасную',
fallbackEmpty: 'Запасных моделей нет — используется модель по умолчанию, если она не падает.',
notInCatalog: 'нет в списке моделей этого провайдера — вызовы могут уходить на запасную.',
tasks: {
vision: { label: 'Зрение', hint: 'Анализ изображений' },
web_extract: { label: 'Веб-извлечение', hint: 'Суммаризация страниц' },
compression: { label: 'Сжатие', hint: 'Компрессия контекста' },
skills_hub: { label: 'Хаб навыков', hint: 'Поиск навыков' },
approval: { label: 'Одобрение', hint: 'Умное авто-одобрение' },
mcp: { label: 'MCP', hint: 'Маршрутизация MCP-инструментов' },
title_generation: { label: 'Ген. заголовка', hint: 'Заголовки сеансов' },
curator: { label: 'Куратор', hint: 'Просмотр использования навыков' }
}
},
providers: {
connectAccount: 'Подключить аккаунт',
haveApiKey: 'Ввести API-ключ вместо этого?',
intro:
'Войдите по подписке — копировать API-ключ не нужно. Hermes проведёт вход в браузере прямо здесь, в приложении.',
connected: 'Подключено',
collapse: 'Свернуть',
connectAnother: 'Подключить другой провайдер',
otherProviders: 'Другие провайдеры',
disconnect: 'Отключить',
disconnectInTerminal: 'Отключить (выполнит команду удаления в терминале)',
removeConfirm: provider => `Удалить ${provider}?`,
removeExternalGeneric: provider => `${provider} управляется собственным CLI — удалите его там.`,
removeKeyManaged: provider => `${provider} настроен по API-ключу. Удалите его в разделе API-ключи.`,
removeTerminalConfirm: (provider, command) =>
`Отключить ${provider}? В терминале будет выполнена команда "${command}" для сброса учётных данных.`,
removeTerminalRunning: provider => `Выполняется отключение ${provider} в терминале…`,
removedTitle: 'Аккаунт удалён',
removedMessage: provider => `${provider} удалён.`,
failedRemove: provider => `Не удалось удалить ${provider}`,
noProviderKeys: 'API-ключи провайдеров недоступны.',
searchKeys: 'Поиск провайдеров…',
noKeysMatch: 'Провайдеры, подходящие под поиск, не найдены.',
localEndpoint: {
title: 'Локальный / свой эндпоинт',
description: 'Направьте Hermes на любой OpenAI-совместимый эндпоинт (Zyphra, vLLM, llama.cpp, Ollama и т. д.).'
},
loading: 'Загрузка провайдеров…'
},
sessions: {
loading: 'Загрузка архивных сеансов…',
archivedTitle: 'Архивные сеансы',
archivedIntro:
'Архивированные чаты скрыты из боковой панели, но сохраняют все сообщения. Чтобы архивировать чат из боковой панели — Ctrl/⌘-клик по нему.',
emptyArchivedTitle: 'Архив пуст',
emptyArchivedDesc: 'Архивируйте чат, чтобы скрыть его отсюда.',
unarchive: 'Восстановить',
deletePermanently: 'Удалить безвозвратно',
messages: count => `${count} ${RU_PLURAL(count, 'сообщение', 'сообщения', 'сообщений')}`,
restored: 'Восстановлено',
deleteConfirm: title => `Безвозвратно удалить «${title}»? Это действие необратимо.`,
autoArchiveTitle: 'Авто-архивация старых чатов',
autoArchiveDesc:
'Автоматически архивировать чаты, к которым вы давно не возвращались. Закреплённые чаты никогда не архивируются, ничего не удаляется — архивные чаты просто переезжают сюда.',
autoArchiveDaysLabel: 'Архивировать через',
autoArchiveDaysUnit: 'дн. бездействия',
autoArchiveFailed: 'Не удалось обновить авто-архивацию',
defaultDirTitle: 'Папка проекта по умолчанию',
defaultDirDesc:
'Новые сеансы начинаются в этой папке, если вы не выбрали другую. Оставьте пустым, чтобы использовать домашнюю директорию.',
defaultDirUpdated:
'Папка проекта по умолчанию обновлена — начните новый чат (Ctrl/⌘+N), чтобы она вступила в силу',
defaultsTo: label => `По умолчанию: ${label}.`,
change: 'Изменить',
choose: 'Выбрать',
clear: 'Очистить',
notSet: 'Не задано',
failedLoad: 'Не удалось загрузить архивные сеансы',
unarchiveFailed: 'Не удалось восстановить',
deleteFailed: 'Не удалось удалить',
updateDirFailed: 'Не удалось обновить папку по умолчанию',
clearDirFailed: 'Не удалось очистить папку по умолчанию'
},
toolsets: {
loadingConfig: 'Загрузка конфигурации',
savedTitle: 'Учётные данные сохранены',
savedMessage: key => `${key} обновлён.`,
removedTitle: 'Учётные данные удалены',
removedMessage: key => `${key} удалён.`,
failedSave: key => `Не удалось сохранить ${key}`,
failedRemove: key => `Не удалось удалить ${key}`,
failedReveal: key => `Не удалось показать ${key}`,
removeConfirm: key => `Удалить ${key} из .env?`,
set: 'Установить',
notSet: 'Не задано',
selectedTitle: 'Провайдер выбран',
selectedMessage: provider => `Сейчас активен ${provider}.`,
failedSelect: provider => `Не удалось выбрать ${provider}`,
failedLoad: 'Не удалось загрузить конфигурацию инструментов',
noProviderOptions:
'У этого набора инструментов нет вариантов провайдера — включите его, и он будет работать с вашей текущей конфигурацией.',
noProviders: 'Для этого набора инструментов сейчас нет доступных провайдеров.',
ready: 'Готово',
needsSignIn: 'Нужен вход',
needsSetup: 'Требуется настройка',
activeBackend: 'Активен',
activeBackendHint: 'Это ваш активный бэкенд',
useBackend: 'Использовать этот бэкенд',
nousIncluded: 'Входит в подписку Nous — войдите в Nous Portal, чтобы активировать.',
nousAuthNeededTitle: 'Войдите в Nous Portal',
nousAuthNeededMessage: provider => `${provider} сохранён, но не активируется, пока вы не войдёте в Nous Portal.`,
nousAuthSignIn: 'Войти',
nousAuthDoneTitle: 'Nous Portal подключён',
nousAuthDoneMessage: 'Ваши бэкенды по подписке теперь активны.',
nousAuthFailed: 'Вход в Nous Portal не завершён',
noApiKeyRequired: 'API-ключ не требуется.',
postSetupHint: step =>
`Этому бэкенду нужна однократная установка (${step}). Выполняется на этой машине — может занять несколько минут.`,
postSetupInstalledHint: 'Установлено. Повторите настройку, только если что-то сломано.',
postSetupRun: 'Запустить настройку',
postSetupRerun: 'Повторить настройку',
postSetupInstalled: 'Установлено',
postSetupRunning: 'Установка…',
postSetupStarting: 'Запуск…',
postSetupCompleteTitle: 'Настройка завершена',
postSetupCompleteMessage: step => `${step} установлен.`,
postSetupErrorTitle: 'Настройка завершилась с ошибками',
postSetupErrorMessage: step => `Посмотрите журнал ${step}.`,
postSetupFailed: step => `Не удалось выполнить настройку ${step}`,
webSearchActive: backend => `Поиск: ${backend}`,
webExtractActive: backend => `Извлечение: ${backend}`,
webCapabilityUnset: 'не задано',
webUseForSearch: 'Использовать для поиска',
webUseForExtract: 'Использовать для извлечения',
webUsedForSearch: 'Бэкенд поиска',
webUsedForExtract: 'Бэкенд извлечения',
webCapabilitySelectedMessage: (provider, capability) => `${provider} теперь отвечает за веб-${capability}.`,
failedSelectCapability: provider => `Не удалось настроить ${provider}`,
loadingModels: 'Загрузка каталога моделей…',
modelSectionTitle: 'Модель',
modelCount: count => `${count} ${RU_PLURAL(count, 'модель', 'модели', 'моделей')}`,
modelInUse: 'В использовании',
modelDefault: 'по умолчанию',
modelInactiveHint: 'Сначала выберите этот бэкенд, чтобы изменить его модель.',
modelSelectedTitle: 'Модель выбрана',
modelSelectedMessage: model => `${model} применится к новым сеансам.`,
failedSelectModel: model => `Не удалось выбрать ${model}`,
terminalBackend: {
sectionTitle: 'Бэкенд выполнения',
loading: 'Проверка бэкендов выполнения…',
failedLoad: 'Не удалось загрузить бэкенды терминала',
ready: 'Готово',
needsSetup: 'Нужна настройка',
unavailable: 'Недоступен',
inUse: 'В использовании',
selectedTitle: 'Бэкенд выбран',
selectedMessage: backend =>
`Команды терминала теперь выполняются через ${backend}. Применится к новым сеансам.`,
failedSelect: backend => `Не удалось выбрать ${backend}`,
needsSetupHint: 'Этот бэкенд можно выбрать сейчас — команды будут падать, пока настройка не завершена.'
}
}
},
skills: {
tabSkills: 'Навыки',
tabToolsets: 'Инструменты',
configuringProfile: 'Настраивается:',
tabMcp: 'MCP',
all: 'Все',
searchSkills: 'Поиск навыков...',
searchToolsets: 'Поиск инструментов...',
refresh: 'Обновить навыки',
refreshing: 'Обновление навыков',
loading: 'Загрузка возможностей...',
noSkillsTitle: 'Навыки не найдены',
noSkillsDesc: 'Попробуйте более широкий поиск или другую категорию.',
noToolsetsTitle: 'Toolsets не найдены',
noToolsetsDesc: 'Попробуйте более широкий запрос.',
noDescription: 'Без описания.',
configured: 'Настроено',
needsKeys: 'Нужны ключи',
visionModelHint:
'Зрение использует вашу конфигурацию вспомогательной модели — модель с поддержкой изображений выбирается там, а не здесь по провайдеру.',
visionModelLink: 'Выбрать модель зрения в Настройки → Модели',
toolsetsEnabled: (enabled, total) => `Включено наборов инструментов: ${enabled}/${total}`,
configureToolset: label => `Настроить ${label}`,
toggleToolset: (label, enabled) => `${enabled ? 'Включить' : 'Отключить'} набор инструментов ${label}`,
skillsLoadFailed: 'Не удалось загрузить навыки',
toolsetsRefreshFailed: 'Не удалось обновить наборы инструментов',
skillEnabled: 'Навык включён',
skillDisabled: 'Навык отключён',
toolsetEnabled: 'Набор инструментов включён',
toolsetDisabled: 'Набор инструментов отключён',
appliesToNewSessions: name => `${name} применится к новым сеансам.`,
failedToUpdate: name => `Не удалось обновить ${name}`,
sortMostUsed: 'Самые используемые',
sortAlpha: 'А–Я',
sortMostUsedDesc: '↓ Самые используемые',
sortLeastUsedAsc: '↑ Меньше всего используемые',
enableAll: 'Включить все',
disableAll: 'Отключить все',
disableUnused: 'Отключить неиспользуемые',
bulkUpdated: count => `Обновлено ${count} ${RU_NOUN(count, 'элемент', 'элемента', 'элементов')} для новых сеансов.`,
bulkNoChange: 'Менять нечего.',
usageCount: count => `использован ${count}×`,
provenance: {
agent: 'Научен',
bundled: 'Встроенный',
hub: 'Хаб'
},
emptyNoneFound: noun => `Не найдено: ${noun}`,
emptyNothingMatches: query => `Ничего не подходит под «${query}».`,
emptyNoneAvailable: noun => `${noun} пока недоступны.`,
changesApplyNewSessions: 'Изменения применяются к новым сеансам.',
skillUpdated: 'Навык обновлён',
edit: 'Изменить',
archive: 'В архив',
skillArchivedTitle: 'Навык в архиве',
skillArchivedMessage: 'Восстановить через hermes curator restore.',
hub: {
searchPlaceholder: 'Поиск в хабе навыков',
search: 'Поиск',
searching: 'Поиск...',
connectingHubs: 'Подключение к хабам навыков...',
connectedHubs: 'Подключённые хабы:',
featured: 'Избранные навыки',
landingHint:
'Ищите в хабе, чтобы просматривать устанавливаемые навыки из официального индекса, GitHub и источников сообщества.',
noResults: 'Совпадающие навыки в хабе не найдены.',
resultCount: (count, ms) =>
`${count} ${RU_PLURAL(count, 'результат', 'результата', 'результатов')}${ms !== null ? ` за ${ms}мс` : ''}`,
timedOut: sources => `Тайм-аут: ${sources}`,
installed: 'Установлен',
install: 'Установить',
installing: 'Установка...',
uninstall: 'Удалить',
uninstalling: 'Удаление...',
updateAll: 'Обновить установленные',
updating: 'Обновление...',
preview: 'Предпросмотр',
scan: 'Сканировать',
scanning: 'Сканирование...',
close: 'Закрыть',
files: 'Файлы',
noReadme: 'Для этого навыка нет предпросмотра SKILL.md.',
trust: {
builtin: 'встроенный',
trusted: 'доверенный',
community: 'сообщество'
},
verdictSafe: 'Безопасен',
verdictCaution: 'Осторожно',
verdictDangerous: 'Опасен',
policyAllow: 'Установка разрешена',
policyAsk: 'Проверьте перед установкой',
policyBlock: 'Установка заблокирована политикой',
findings: count => `${count} ${RU_NOUN(count, 'находка', 'находки', 'находок')}`,
noFindings: 'Находок безопасности нет.',
installStarted: name => `Установка ${name}...`,
uninstallStarted: name => `Удаление ${name}...`,
updateStarted: 'Обновление установленных навыков...',
actionFailed: 'Действие с навыком не удалось',
actionLog: 'Журнал действий',
alreadyInstalled: name => `«${name}» уже установлен`,
pickerTitle: 'Хаб навыков',
pickerBrowse: 'Открыть весь хаб',
pickerHide: 'Скрыть браузер хаба',
pickerHint: 'Нажмите «+ Добавить к этому агенту» на любом навыке — он установится и появится в списке выше.',
loadFailed: 'Не удалось загрузить хаб навыков',
previewFailed: 'Не удалось получить предпросмотр навыка',
scanFailed: 'Не удалось выполнить проверку безопасности',
searchFailed: 'Поиск в хабе не удался'
}
},
starmap: {
title: 'Граф памяти',
subtitle: (nodes, clusters) =>
`${nodes} ${RU_NOUN(nodes, 'навык', 'навыка', 'навыков')} в ${clusters} ${RU_NOUN(clusters, 'категория', 'категории', 'категорий')}`,
close: 'Закрыть граф памяти',
refresh: 'Обновить',
memory: 'Память',
filterAll: 'Все',
filterUsed: 'Использованные',
filterLearned: 'Наученные',
viewGraph: 'Граф',
loadFailed: 'Не удалось загрузить граф памяти',
loading: 'Загрузка…',
emptyTitle: 'Пока ничего не изучено',
emptyDesc: 'По мере того как Hermes создаёт навыки и память для вашей работы, они появятся здесь.',
share: 'Поделиться картой',
shareHint:
'Скопируйте код, чтобы поделиться этой картой, или вставьте код для загрузки. Включает только раскладку, а не вашу память или текст навыков.',
shareTitle: 'Импорт / экспорт карты',
sharePlaceholder: 'Вставьте код карты…',
copy: 'Скопировать код карты',
copied: 'Скопировано!',
importMap: 'Импортировать карту',
importBtn: 'Загрузить',
importEmpty: 'Вставьте код карты для загрузки.',
importSuccess: nodes => `Загружена карта с ${nodes} ${RU_NOUN(nodes, 'узлом', 'узла', 'узлов')}.`,
importedBadge: 'импортированная карта',
resetToMine: 'Вернуться к моей карте'
},
agents: {
extendedTranscript: 'Подробный журнал',
transcriptTruncated: 'Последние 16 КиБ',
transcriptUnavailable: 'Текущий журнал недоступен',
close: 'Закрыть агентов',
title: 'Дерево запусков',
subtitle: 'Активные субагенты текущего хода в реальном времени.',
emptyTitle: 'Нет активных субагентов',
emptyDesc: 'Когда ход делегирует работу, дочерние агенты стримят свой прогресс сюда.',
running: 'Выполняется',
failed: 'Ошибка',
done: 'Готово',
streaming: 'Стримится',
files: 'Файлы',
moreFiles: count => `+ещё ${count} ${RU_NOUN(count, 'файл', 'файла', 'файлов')}`,
moreAgents: count => `Ещё ${count} агентов`,
queued: 'В очереди',
waitingActivity: 'Ожидание активности',
steer: 'Направить',
steerPlaceholder: 'Инструкции этому субагенту',
steerQueued: 'В очереди до следующей контрольной точки',
stopRequested: 'Запрошена остановка',
requestRejected: 'Субагент не принял запрос',
delegation: index => `Делегирование ${index}`,
workers: count => `${count} ${RU_NOUN(count, 'воркер', 'воркера', 'воркеров')}`,
workersActive: count => `${count} ${RU_NOUN(count, 'активен', 'активно', 'активных')}`,
agentsCount: count => `${count} ${RU_PLURAL(count, 'агент', 'агента', 'агентов')}`,
activeCount: count => `${count} ${RU_NOUN(count, 'активен', 'активно', 'активных')}`,
failedCount: count => `${count} ${RU_NOUN(count, 'с ошибкой', 'с ошибками', 'с ошибками')}`,
toolsCount: count => `${count} ${RU_NOUN(count, 'инструмент', 'инструмента', 'инструментов')}`,
filesCount: count => `${count} ${RU_NOUN(count, 'файл', 'файла', 'файлов')}`,
updatedAgo: age => `обновлено ${age}`,
ageNow: 'только что',
ageSeconds: seconds => `${seconds}с назад`,
ageMinutes: minutes => `${minutes}м назад`,
ageHours: hours => `${hours}ч назад`,
ageDays: days => `${days}д назад`,
durationSeconds: seconds => `${seconds}с`,
durationMinutes: (minutes, seconds) => `${minutes}м ${seconds}с`,
tokens: value => `${value} ток`
},
commandCenter: {
close: 'Закрыть командный центр',
paletteTitle: 'Палитра команд',
back: 'Назад',
searchPlaceholder: 'Поиск сеансов, представлений и действий',
goTo: 'Перейти',
goToSession: 'Перейти к сеансу',
branches: 'Ветви',
projects: 'Проекты',
openFolder: 'Открыть папку как проект…',
openFolderAt: path => `Открыть папку как проект — ${path}`,
newSessionInProject: project => `Новый сеанс в ${project}`,
commands: 'Команды',
startInBranch: branch => `Новый диалог в ${branch}`,
commandCenter: 'Командный центр',
appearance: 'Внешний вид',
settings: 'Настройки',
changeTheme: 'Сменить тему',
changeColorMode: 'Сменить цветовой режим…',
pets: {
title: 'Питомцы',
placeholder: 'Поиск питомцев…',
loading: 'Загрузка галереи petdex…',
error: 'Не удалось подключиться к галерее petdex.',
staleBackend: 'Перезапустите Hermes, чтобы использовать питомцев — бэкенд старше этой функции.',
empty: 'Совпадающих питомцев нет.',
turnOff: 'Отключить',
turnOn: 'Включить',
installed: 'Установлен',
generatedTag: 'Сгенерирован',
adoptFailed: 'Не удалось усыновить этого питомца.',
toggleFailed: enabled => `Не удалось ${enabled ? 'включить' : 'отключить'} питомца.`,
noneAvailable: 'Питомцев пока нет — выберите ниже для установки.'
},
generatePet: {
title: 'Сгенерировать питомца',
placeholder: 'Опишите питомца для генерации…',
promptHint: 'Введите описание и нажмите Enter, чтобы получить четыре варианта облика.',
readyHint: 'Нажмите Enter, чтобы получить четыре варианта облика по вашему описанию.',
generate: 'Сгенерировать',
generating: 'Генерация…',
retry: 'Повторить',
hatch: 'Вылупить',
spawning: 'Создаём…',
hatching: 'Вылупливаем вашего питомца…',
hatchingSub: 'Оживляем его…',
hatched: 'Он вылупился!',
hatchRow: (_state, done, total) => `Рисуем кадр ${done} из ${total}…`,
hatchComposing: 'Собираем по частям…',
hatchSaving: 'Почти готово…',
namePlaceholder: 'Имя для вашего питомца',
staleBackend: 'Обновите Hermes, чтобы генерировать питомцев.',
backgroundHint: 'Можно закрыть — Hermes уведомит, когда будет готово.',
slowProviderHint: 'Это может занять несколько минут',
remix: 'Ремикс',
remixConfirmTitle: 'Сделать ремикс из этого облика?',
remixConfirmBody:
'Будет сгенерирован новый набор вариантов с этим как отправной точкой. Это может занять несколько минут.',
genericError: 'Генерация не удалась — попробуйте снова или выберите подсказку.',
referenceImageTooLarge: 'Изображение-референс слишком большое. Используйте меньше 16 МБ.',
referenceImageInvalid: 'Не удалось прочитать это изображение-референс. Попробуйте PNG, JPG, WebP или GIF.',
adopt: 'Усыновить',
startOver: 'Начать заново'
},
installTheme: {
title: 'Установить тему…',
pageTitle: 'Установка темы',
placeholder: 'Поиск в VS Code Marketplace...',
loading: 'Поиск в Marketplace...',
error: 'Не удалось подключиться к Marketplace.',
empty: 'Совпадающих тем нет.',
install: 'Установить',
installing: 'Установка...',
installed: 'Установлена',
installs: count => `${count} ${RU_NOUN(count, 'установка', 'установки', 'установок')}`
},
settingsFields: 'Поля настроек',
mcpServers: 'MCP-серверы',
archivedChats: 'Архивные чаты',
sections: { maintenance: 'Обслуживание', sessions: 'Сеансы', system: 'Система', usage: 'Использование' },
sectionDescriptions: {
maintenance: 'Диагностика, резервные копии, курир и данные памяти',
sessions: 'Поиск и управление сеансами',
system: 'Статус, журналы и системные действия',
usage: 'Токены, стоимость и активность навыков со временем'
},
nav: {
newChat: { title: 'Новый сеанс', detail: 'Начать новый сеанс' },
settings: { title: 'Настройки', detail: 'Настройка Hermes desktop' },
skills: { title: 'Возможности', detail: 'Навыки, инструменты и MCP-серверы' },
messaging: { title: 'Сообщения', detail: 'Настройка Telegram, Slack, Discord и других' },
artifacts: { title: 'Артефакты', detail: 'Просмотр сгенерированных результатов' }
},
sectionEntries: {
sessions: { title: 'Панель сеансов', detail: 'Поиск, закрепление и управление сеансами' },
system: { title: 'Системная панель', detail: 'Статус шлюза, журналы, перезапуск/обновление' },
usage: { title: 'Панель использования', detail: 'Токены, стоимость и активность навыков' }
},
providerNavigate: 'Перейти',
providerSessions: 'Сеансы',
refresh: 'Обновить',
refreshing: 'Обновление...',
noResults: 'Совпадающие результаты не найдены.',
pinSession: 'Закрепить сеанс',
unpinSession: 'Открепить сеанс',
exportSession: 'Экспортировать сеанс',
deleteSession: 'Удалить сеанс',
noSessions: 'Сеансов пока нет.',
gatewayRunning: 'Шлюз сообщений работает',
gatewayStopped: 'Шлюз сообщений остановлен',
hermesActiveSessions: (version, count) => `Hermes ${version} · Активные сеансы: ${count}`,
restartGateway: 'Перезапустить шлюз',
openBrowser: 'Открыть браузер',
gatewayRestartFailed: 'Не удалось перезапустить шлюз.',
updateHermes: 'Обновить Hermes',
reloadWindow: 'Перезагрузить окно',
actionRunning: 'выполняется',
actionDone: 'готово',
actionFailed: 'ошибка',
actionStartedWaiting: 'Действие запущено, ожидание статуса...',
loadingStatus: 'Загрузка статуса...',
recentLogs: 'Последние записи журнала',
noLogs: 'Журналы ещё не загружены.',
days: count => `${count}д`,
statSessions: 'Сеансы',
statApiCalls: 'Вызовы API',
statTokens: 'Токены вход/выход',
statCost: 'Оценка стоимости',
actualCost: cost => `фактически ${cost}`,
loadingUsage: 'Загрузка использования...',
noUsage: period => `Нет использования за последние ${period} ${RU_NOUN(period, 'день', 'дня', 'дней')}.`,
retry: 'Повторить',
dailyTokens: 'Токены за день',
input: 'вход',
output: 'выход',
noDailyActivity: 'Ежедневной активности нет.',
topModels: 'Топ моделей',
noModelUsage: 'Использование моделей пока отсутствует.',
topSkills: 'Топ навыков',
noSkillActivity: 'Активности навыков пока нет.',
actions: count => `${count} ${RU_NOUN(count, 'действие', 'действия', 'действий')}`,
logFile: 'Файл журнала',
logLevel: 'Уровень',
logSearchPlaceholder: 'Фильтр строк журнала...',
maintenance: {
runOps: 'Диагностика',
doctor: 'Запустить doctor',
doctorDesc: 'Проверка здоровья установки, конфигурации и провайдеров',
securityAudit: 'Аудит безопасности',
securityAuditDesc: 'Сканирование конфигурации и навыков на предмет рискованных настроек',
backup: 'Создать резервную копию',
backupDesc: 'Сжатие конфигурации, памяти, навыков и сеансов в zip',
debugShare: 'Поделиться отладкой',
debugShareDesc: 'Загрузка анонимизированного отчёта + журналов, получение ссылок (автоудаление через 6 ч)',
debugShareRunning: 'Загрузка отладочного отчёта...',
debugShareLinks: 'Ссылки для шаринга',
debugShareFailed: 'Ошибка шаринга отладки',
copyLink: 'Скопировать ссылку',
linkCopied: 'Ссылка скопирована',
curator: 'Курир навыков',
curatorDesc: 'Фоновый обзор, архивирующий устаревшие навыки, созданные агентом',
curatorPaused: 'Приостановлен',
curatorActive: 'Активен',
curatorDisabled: 'Отключён',
curatorLastRun: when => `Последний запуск ${when}`,
curatorNeverRan: 'Никогда не запускался',
pause: 'Приостановить',
resume: 'Продолжить',
runNow: 'Запустить сейчас',
memoryData: 'Данные памяти',
memoryDataDesc: 'Встроенные файлы памяти, внедряемые в каждый сеанс',
memoryProvider: name => `Активный провайдер: ${name}`,
builtinMemory: 'встроенный',
memoryFile: 'Память агента (MEMORY.md)',
userFile: 'Профиль пользователя (USER.md)',
bytes: size => size,
empty: 'пусто',
resetMemory: 'Сбросить память',
resetUser: 'Сбросить профиль',
resetAll: 'Сбросить оба',
resetConfirm: target => `Удалить ${target}? Это действие необратимо.`,
resetDone: files => `Удалено: ${files}.`,
resetFailed: 'Не удалось сбросить память',
actionStarted: name => `${name} запущен — следим за журналом...`,
actionFailed: name => `Не удалось запустить ${name}`,
running: 'Выполняется...',
viewLog: 'Журнал действия'
}
},
messaging: {
search: 'Поиск в сообщениях...',
loading: 'Загрузка платформ сообщений...',
loadFailed: 'Не удалось загрузить платформы сообщений',
states: {
connected: 'Подключено',
connecting: 'Подключение',
disabled: 'Отключено',
fatal: 'Ошибка',
gateway_stopped: 'Шлюз сообщений остановлен',
not_configured: 'Нужна настройка',
pending_restart: 'Нужен перезапуск',
retrying: 'Повторная попытка',
startup_failed: 'Не удалось запустить'
},
unknown: 'Неизвестно',
hintPendingRestart: 'Перезапустите шлюз из строки состояния, чтобы применить это изменение.',
hintGatewayStopped: 'Запустите шлюз из строки состояния для подключения.',
credentialsSet: 'Учётные данные заданы',
needsSetup: 'Нужна настройка',
gatewayStopped: 'Шлюз сообщений остановлен',
getCredentials: 'Получить учётные данные',
openSetupGuide: 'Открыть руководство по настройке',
required: 'Обязательно',
recommended: 'Рекомендуется',
advanced: count => `Расширенные (${count})`,
noTokenNeeded: 'Этой платформе здесь не нужен токен. Используйте руководство выше, затем включите её ниже.',
enabled: 'Включено',
disabled: 'Отключено',
unsavedChanges: 'Несохранённые изменения',
saving: 'Сохранение...',
saveChanges: 'Сохранить изменения',
saved: 'Сохранено',
replaceValue: 'Заменить текущее значение',
openDocs: 'Открыть документацию',
clearField: key => `Очистить ${key}`,
enableAria: name => `Включить ${name}`,
disableAria: name => `Отключить ${name}`,
platformEnabled: name => `${name} включено`,
platformDisabled: name => `${name} отключено`,
restartToApply: 'Это изменение вступит в силу после перезапуска шлюза.',
setupSaved: name => `Настройка ${name} сохранена`,
restartToReconnect: 'Новые учётные данные вступят в силу после перезапуска шлюза.',
keyCleared: key => `${key} очищено`,
setupUpdated: name => `Настройка ${name} обновлена.`,
failedUpdate: name => `Не удалось обновить ${name}`,
failedSave: name => `Не удалось сохранить ${name}`,
failedClear: key => `Не удалось очистить ${key}`,
pendingRequests: count => `Ожидающие запросы (${count})`,
pendingAria: count =>
`${count} ${RU_NOUN(count, 'ожидающий запрос на сопряжение', 'ожидающих запроса на сопряжение', 'ожидающих запросов на сопряжение')}`,
approvedUsers: count => `Одобрённые пользователи (${count})`,
approve: 'Одобрить',
approving: 'Одобрение...',
revoke: 'Отозвать',
revoking: 'Отзыв...',
revokeAria: name => `Отозвать ${name}`,
revokeTitle: 'Отозвать доступ',
revokeDesc: name => `${name} потеряет доступ и перестанет распознаваться начиная со следующего сообщения.`,
approvedUser: name => `${name} одобрен`,
approvedHint: 'Они распознаются автоматически начиная с их следующего сообщения.',
revokedUser: name => `${name} отозван`,
failedApprove: name => `Не удалось одобрить ${name}`,
failedRevoke: name => `Не удалось отозвать ${name}`,
pairingLockedOut: 'Слишком много неудачных одобрений — эта платформа заблокирована. Попробуйте позже.',
waitingSince: minutes => (minutes < 1 ? 'только что' : `${minutes}м назад`),
restartNeeded: 'Сохранено. Перезапустите шлюз сообщений, чтобы применить новые настройки.',
restartNow: 'Перезапустить',
restarting: 'Перезапуск…',
restartFailedManual: 'Не удалось перезапустить шлюз — перезапустите его вручную и проверьте журналы.',
telegramQr: {
title: 'Выберите способ подключения Telegram-бота',
subtitle:
'Оба способа подключают бота под вашим контролем и сохраняют его данные только в этой установке Hermes.',
quickSetup: 'Быстрая настройка',
recommended: 'Рекомендуется',
quickHelp:
'Отсканируйте QR-код и подтвердите в Telegram. Hermes создаст бота и определит ваш Telegram ID автоматически.',
createWithQr: 'Создать по QR',
starting: 'Запуск…',
replaceWarning:
'Данные Telegram уже настроены. Новая QR-настройка или токен заменят текущего бота при сохранении.',
scanHint: 'Отсканируйте в приложении Telegram на телефоне или откройте ссылку на этом компьютере.',
waiting: 'Ожидание Telegram…',
expiresIn: remaining => `Истекает через ${remaining}`,
expired: 'Истёк',
openTelegram: 'Открыть Telegram',
ready: 'Бот создан',
allowedUsers: 'Разрешённые пользователи',
ownerDetected: 'Владелец определён',
addAtLeastOne: 'Добавьте хотя бы один Telegram ID.',
userIdPlaceholder: 'Telegram ID пользователя',
add: 'Добавить',
numericOnly: 'Telegram ID должны быть числовыми.',
saveAndRestart: 'Сохранить и перезапустить',
applying: 'Сохранение…',
pairingExpired: 'Срок QR-настройки истёк. Начните новую.',
stillWaiting: detail => `Всё ещё ждём Telegram. Повтор после: ${detail}`,
savedRestarting: 'Telegram сохранён; шлюз перезапускается…',
savedRestartFailed: detail => `Telegram сохранён; перезапуск шлюза не удался${detail}`
},
fieldCopy: {
TELEGRAM_BOT_TOKEN: {
label: 'Токен бота',
help: 'Создайте бота через @BotFather и вставьте выданный им токен.',
placeholder: 'Вставьте токен бота Telegram'
},
TELEGRAM_ALLOWED_USERS: {
label: 'Разрешённые ID пользователей Telegram',
help: 'Рекомендуется. Числовые ID через @userinfobot через запятую. Без этого писать вашему боту может кто угодно.'
},
TELEGRAM_PROXY: { label: 'URL прокси', help: 'Нужен только в сетях, где Telegram заблокирован.' },
DISCORD_BOT_TOKEN: {
label: 'Токен бота',
help: 'Создайте приложение в Discord Developer Portal, добавьте бота и вставьте его токен.'
},
DISCORD_ALLOWED_USERS: {
label: 'Разрешённые ID пользователей Discord',
help: 'Рекомендуется. ID пользователей Discord через запятую.'
},
DISCORD_REPLY_TO_MODE: { label: 'Стиль ответов', help: 'first, all или off.' },
DISCORD_ALLOW_ALL_USERS: {
label: 'Разрешить всех пользователей Discord',
help: 'Только для разработки. Если true, писать боту в ЛС может кто угодно без allowlist.'
},
DISCORD_HOME_CHANNEL: {
label: 'ID домашнего канала',
help: 'Канал, куда бот шлёт проактивные сообщения (результаты cron, напоминания).'
},
DISCORD_HOME_CHANNEL_NAME: {
label: 'Название домашнего канала',
help: 'Отображаемое имя домашнего канала в журналах и выводе статуса.'
},
BLUEBUBBLES_ALLOW_ALL_USERS: {
label: 'Разрешить всех пользователей iMessage',
help: 'Если true, allowlist BlueBubbles пропускается.'
},
MATTERMOST_ALLOW_ALL_USERS: { label: 'Разрешить всех пользователей Mattermost' },
MATTERMOST_HOME_CHANNEL: { label: 'Домашний канал' },
QQ_ALLOW_ALL_USERS: { label: 'Разрешить всех пользователей QQ' },
QQBOT_HOME_CHANNEL: { label: 'Домашний канал QQ', help: 'Канал или группа по умолчанию для доставки cron.' },
QQBOT_HOME_CHANNEL_NAME: { label: 'Название домашнего канала QQ' },
SLACK_BOT_TOKEN: {
label: 'Токен бота Slack',
help: 'Используйте токен бота из OAuth & Permissions после установки вашего приложения Slack.',
placeholder: 'Вставьте токен бота Slack'
},
SLACK_APP_TOKEN: {
label: 'Токен приложения Slack',
help: 'Используйте токен уровня приложения, необходимый для Socket Mode.',
placeholder: 'Вставьте токен приложения Slack'
},
SLACK_ALLOWED_USERS: {
label: 'Разрешённые ID пользователей Slack',
help: 'Рекомендуется. ID пользователей Slack через запятую.'
},
MATTERMOST_URL: { label: 'URL сервера', placeholder: 'https://mattermost.example.com' },
MATTERMOST_TOKEN: { label: 'Токен бота' },
MATTERMOST_ALLOWED_USERS: {
label: 'Разрешённые ID пользователей',
help: 'Рекомендуется. ID пользователей Mattermost через запятую.'
},
MATRIX_HOMESERVER: { label: 'URL homeserver', placeholder: 'https://matrix.org' },
MATRIX_ACCESS_TOKEN: { label: 'Токен доступа' },
MATRIX_USER_ID: { label: 'ID пользователя бота', placeholder: '@hermes:example.org' },
MATRIX_ALLOWED_USERS: {
label: 'Разрешённые ID пользователей Matrix',
help: 'Рекомендуется. ID пользователей через запятую в формате @user:server.'
},
SIGNAL_HTTP_URL: {
label: 'URL моста Signal',
placeholder: 'http://127.0.0.1:8080',
help: 'URL работающего REST-моста signal-cli.'
},
SIGNAL_ACCOUNT: { label: 'Номер телефона', help: 'Номер, зарегистрированный в вашем мосте signal-cli.' },
SIGNAL_ALLOWED_USERS: {
label: 'Разрешённые пользователи Signal',
help: 'Рекомендуется. Идентификаторы Signal через запятую.'
},
WHATSAPP_ENABLED: {
label: 'Включить мост WhatsApp',
help: 'Устанавливается автоматически переключателем ниже. Не меняйте, если точно не нужно.'
},
WHATSAPP_MODE: { label: 'Режим моста' },
WHATSAPP_ALLOWED_USERS: {
label: 'Разрешённые пользователи WhatsApp',
help: 'Рекомендуется. Номера телефонов или ID WhatsApp через запятую.'
}
},
platformIntro: {}
},
webhooks: {
search: 'Поиск вебхуков...',
loading: 'Загрузка вебхуков...',
loadFailed: 'Не удалось загрузить вебхуки',
subscriptions: count => `Подписки (${count})`,
hint: 'Изменения подписок применяются на лету, когда приёмник запущен. Отключённые подписки отклоняют входящие события.',
empty: 'Подписок вебхуков пока нет.',
disabledTitle: 'Приёмник вебхуков отключён',
disabledBody:
'Вебхуки — это отдельная платформа шлюза. Включите их здесь, чтобы принимать входящие HTTP-события; чат-каналы нужны только когда подписка доставляет в Telegram, Discord, Slack или другой канал.',
enable: 'Включить вебхуки',
enabling: 'Включение...',
enabled: name => `Включено: «${name}»`,
disabled: name => `Отключено: «${name}»`,
enableRow: 'Включить',
disableRow: 'Отключить',
delete: 'Удалить',
deleting: 'Удаление...',
deleted: 'Вебхук удалён',
deleteTitle: 'Удалить вебхук',
deleteDescPrefix: 'Это навсегда удалит ',
deleteDescSuffix: '. Это действие необратимо.',
deleteFailed: name => `Не удалось удалить «${name}»`,
toggleFailed: (name, enabled) => `Не удалось ${enabled ? 'включить' : 'отключить'} «${name}»`,
newSubscription: 'Новая подписка',
restarting: 'Перезапуск шлюза...',
restartNeeded: 'Вебхуки включены, но шлюз всё ещё нужно перезапустить, чтобы приёмник заработал.',
restartGateway: 'Перезапустить шлюз',
restartingGateway: 'Перезапуск...',
restartFailed: detail => `Не удалось перезапустить шлюз${detail}`,
enabledRestarting: 'Вебхуки включены; шлюз перезапускается...',
all: '(все)',
deliverOnly: 'только доставка',
createdTitle: 'Подписка создана',
createdSecretHint: 'Скопируйте секрет сейчас — он показывается только один раз.',
webhookUrl: 'URL вебхука',
secretOnce: 'Секрет (показывается один раз)',
done: 'Готово',
fieldName: 'Имя',
fieldNamePlaceholder: 'например, github-push',
fieldDescription: 'Описание',
fieldDescriptionPlaceholder: 'Что делает этот вебхук (необязательно)',
fieldEvents: 'События',
fieldEventsPlaceholder: 'через запятую, пусто — все',
fieldSkills: 'Навыки',
fieldSkillsPlaceholder: 'имена навыков через запятую (необязательно)',
fieldDeliver: 'Доставить в',
fieldDeliverOnly: 'Доставлять только payload',
fieldPrompt: 'Промпт',
fieldPromptPlaceholder: 'Инструкции для агента при срабатывании этого вебхука (необязательно)',
nameRequired: 'Имя обязательно',
create: 'Создать',
creating: 'Создание...',
created: 'Создано',
createFailed: detail => `Не удалось создать: ${detail}`,
copy: 'Копировать',
deliverOptions: {
log: 'Журнал',
telegram: 'Telegram',
discord: 'Discord',
slack: 'Slack',
email: 'Email',
github_comment: 'Комментарий GitHub'
}
},
profiles: {
close: 'Закрыть профили',
nameHint: 'Строчные буквы, цифры, дефисы и подчёркивания. Должно начинаться с буквы или цифры.',
title: 'Профили',
count: count => `${count} ${RU_PLURAL(count, 'профиль', 'профиля', 'профилей')}`,
search: 'Поиск профилей...',
loading: 'Загрузка профилей...',
newProfile: 'Новый профиль',
importProfile: 'Импортировать профиль…',
exportProfile: 'Экспортировать профиль…',
imported: 'Профиль импортирован',
exported: 'Профиль экспортирован',
failedImport: 'Не удалось импортировать профиль',
failedExport: 'Не удалось экспортировать профиль',
allProfiles: 'Все профили',
showAllProfiles: 'Показать все профили',
switchToProfile: name => `Переключиться на ${name}`,
switchToConnection: name => `Переключиться на ${name}`,
switchConnectionFailed: name => `Не удалось подключиться к ${name}`,
manageProfiles: 'Управлять профилями…',
connectGateway: 'Управлять шлюзами…',
actions: 'Действия',
color: 'Цвет…',
colorFor: 'Цвет',
setColor: color => `Установить цвет ${color}`,
autoColor: 'Авто',
noProfiles: 'Профилей пока нет.',
selectPrompt: 'Выберите профиль, чтобы посмотреть его детали.',
refresh: 'Обновить профили',
refreshing: 'Обновление профилей',
default: 'по умолчанию',
skills: count => `${count} ${RU_PLURAL(count, 'навык', 'навыка', 'навыков')}`,
env: 'env',
defaultBadge: 'По умолчанию',
rename: 'Переименовать',
renameMenu: 'Переименовать…',
editSoul: 'Изменить SOUL.md…',
copySetup: 'Скопировать команду установки',
copying: 'Копирование...',
modelLabel: 'Модель',
skillsLabel: 'Навыки',
notSet: 'Не задано',
soulDesc: 'Системный промпт и инструкции по персоне, встроенные в этот профиль.',
soulOptional: 'необязательно',
soulPlaceholder: mode =>
`Системный промпт / персона этого профиля.\nОставьте пустым, чтобы сохранить ${mode} по умолчанию.`,
soulPlaceholderCloned: 'склонированный',
soulPlaceholderEmpty: 'пустой',
unsavedChanges: 'Несохранённые изменения',
loadingSoul: 'Загрузка SOUL.md...',
emptySoul: 'Пустой SOUL.md — начните писать персону...',
saving: 'Сохранение...',
saveSoul: 'Сохранить SOUL.md',
deleteTitle: 'Удалить профиль?',
deleteDescPrefix: 'Это удалит ',
deleteDescMid: ' и сотрёт его ',
deleteDescSuffix: ' каталог. Это действие необратимо.',
deleting: 'Удаление...',
createDesc: 'Профили — это независимые среды Hermes: отдельные настройки, навыки и SOUL.md.',
nameLabel: 'Имя',
cloneFrom: 'Клонировать из',
cloneFromNone: 'Нет (пустой)',
cloneFromDesc: 'Копирует настройки, навыки и SOUL.md из выбранного исходного профиля.',
cloneFromDefault: 'Клонировать из профиля по умолчанию',
cloneFromDefaultDesc: 'Скопируйте настройки, навыки и SOUL.md из вашего профиля по умолчанию.',
invalidName: hint => `Некорректное имя. ${hint}`,
nameRequired: 'Имя обязательно.',
creating: 'Создание...',
createAction: 'Создать профиль',
renameTitle: 'Переименовать профиль',
renameDescPrefix: 'Переименование обновляет каталог профиля и все wrapper-скрипты в ',
renameDescSuffix: '.',
displayNameTitle: 'Название этого агента',
displayNameDesc:
'Устанавливает отображаемое имя, показываемое во всём приложении. Внутренний ID профиля остаётся «default».',
displayNameLabel: 'Отображаемое имя',
newNameLabel: 'Новое имя',
renaming: 'Переименование...',
created: 'Профиль создан',
renamed: 'Профиль переименован',
deleted: 'Профиль удалён',
setupCopied: 'Команда установки скопирована',
soulSaved: 'SOUL.md сохранён',
failedLoad: 'Не удалось загрузить профили',
failedDelete: 'Не удалось удалить профиль',
failedCopy: 'Не удалось скопировать команду установки',
failedLoadSoul: 'Не удалось загрузить SOUL.md',
failedSaveSoul: 'Не удалось сохранить SOUL.md',
failedCreate: 'Не удалось создать профиль',
failedRename: 'Не удалось переименовать профиль'
},
cron: {
close: 'Закрыть cron',
title: 'Запланированные задачи',
count: count => `${count} ${RU_PLURAL(count, 'задача', 'задачи', 'задач')}`,
modelImpact: {
title: 'Запланированные задачи остаются на исходной модели',
message: count =>
`${count} незакреплённых запланированных задач продолжат работать на модели, с которой были созданы. Закрепите их или задайте cron.model, чтобы перевести.`,
detailMore: (names, remaining) => `${names} и ещё ${remaining}`,
review: 'Проверить запланированные задачи',
saveFailed: 'Hermes не сохранил это изменение модели.'
},
search: 'Поиск cron-задач...',
loading: 'Загрузка cron-задач...',
states: {
enabled: 'включено',
scheduled: 'запланировано',
running: 'выполняется',
paused: 'приостановлено',
disabled: 'отключено',
error: 'ошибка',
completed: 'завершено'
},
deliveryLabels: {
local: 'Это приложение',
telegram: 'Telegram',
discord: 'Discord',
slack: 'Slack',
email: 'Email'
},
scheduleLabels: {
daily: 'Ежедневно',
weekdays: 'Будни',
weekly: 'Еженедельно',
monthly: 'Ежемесячно',
hourly: 'Каждый час',
'every-15-minutes': 'Каждые 15 минут',
custom: 'Свой'
},
scheduleHints: {
daily: 'Каждый день в 9:00',
weekdays: 'С понедельника по пятницу в 9:00',
weekly: 'Каждый понедельник в 9:00',
monthly: 'В первый день каждого месяца в 9:00',
hourly: 'В начале каждого часа',
'every-15-minutes': 'Каждые 15 минут',
custom: 'Синтаксис cron или естественный язык'
},
days: {
'0': 'Воскресенье',
'1': 'Понедельник',
'2': 'Вторник',
'3': 'Среда',
'4': 'Четверг',
'5': 'Пятница',
'6': 'Суббота',
'7': 'Воскресенье'
},
dayFallback: value => `день ${value}`,
everyDayAt: time => `Каждый день в ${time}`,
weekdaysAt: time => `Будни в ${time}`,
everyDayOfWeekAt: (day, time) => `Каждый ${day} в ${time}`,
monthlyOnDayAt: (dayOfMonth, time) => `Ежемесячно ${dayOfMonth}-го числа в ${time}`,
topOfHour: 'В начале каждого часа',
everyHourAt: minute => `Каждый час на :${minute}`,
newCron: 'Новый cron',
emptyDescNew:
'Запланируйте промпт, который будет выполняться по cron-выражению. Hermes выполнит его и доставит результаты в выбранное вами место.',
emptyDescSearch: 'Попробуйте более широкий запрос.',
emptyTitleNew: 'Запланированных задач пока нет',
emptyTitleSearch: 'Нет совпадений',
last: 'Последний:',
next: 'Следующий:',
noRuns: 'Запусков пока не было',
manage: 'Управлять',
showRuns: 'Показать запуски',
hideRuns: 'Скрыть запуски',
runHistory: 'История запусков',
actionsTitle: 'Действия с cron-задачей',
resume: 'Продолжить cron',
pause: 'Приостановить cron',
resumeTitle: 'Продолжить',
pauseTitle: 'Приостановить',
triggerNow: 'Запустить сейчас',
edit: 'Изменить cron',
deleteTitle: 'Удалить cron-задачу?',
deleteDescPrefix: 'Это навсегда удалит ',
deleteDescSuffix: '. Она перестанет срабатывать сразу.',
deleting: 'Удаление...',
resumed: 'Cron продолжен',
paused: 'Cron приостановлен',
triggered: 'Cron запущен',
deleted: 'Cron удалён',
created: 'Cron создан',
updated: 'Cron обновлён',
failedLoad: 'Не удалось загрузить cron-задачи',
failedUpdate: 'Не удалось обновить cron-задачу',
failedTrigger: 'Не удалось запустить cron-задачу',
failedDelete: 'Не удалось удалить cron-задачу',
failedSave: 'Не удалось сохранить cron-задачу',
editTitle: 'Изменить cron-задачу',
createTitle: 'Новая cron-задача',
editDesc: 'Обновите расписание, промпт или место доставки. Изменения вступят в силу при следующем запуске.',
createDesc:
'Запланируйте промпт для автоматического выполнения. Используйте синтаксис cron или фразу вроде «каждые 15 минут».',
nameLabel: 'Имя',
namePlaceholder: 'Утренний брифинг',
promptLabel: 'Промпт',
promptPlaceholder: 'Суммируй мои непрочитанные треды Slack и пришли топ-5 на почту...',
frequencyLabel: 'Частота',
deliverLabel: 'Доставить в',
deliverNeedsHomeChannel: 'сначала задайте домашний канал',
modelLabel: 'Модель',
modelDefault: 'По умолчанию (глобальная модель)',
customScheduleLabel: 'Своё расписание',
customPlaceholder: '0 9 * * * или будни в 9:00',
customHint: 'Cron-выражение или фразы вроде «каждый час» или «будни в 9:00».',
optional: 'Необязательно',
promptRequired: 'Промпт обязателен.',
promptScheduleRequired: 'Промпт и расписание обязательны.',
scheduleRequired: 'Расписание обязательно.',
scriptOnlyEditHint: 'Задача только со скриптом (без AI-промпта). ID задачи:',
saveChanges: 'Сохранить изменения',
createAction: 'Создать cron',
tabs: {
jobs: 'Задачи',
blueprints: 'Шаблоны'
},
blueprints: {
tab: 'Шаблоны',
startFrom: 'Начать с',
custom: 'Свой',
subtitle: 'Готовые автоматизации',
dialogDesc: 'Заполните детали и запланируйте.',
scheduleIt: 'Запланировать',
scheduling: 'Планирование...',
scheduled: 'Шаблон запланирован',
loading: 'Загрузка шаблонов...',
failedLoad: 'Не удалось загрузить шаблоны',
emptyTitle: 'Шаблоны недоступны',
emptyDesc: 'На этом бэкенде нет шаблонов автоматизации.'
}
},
artifacts: {
search: 'Поиск артефактов...',
refresh: 'Обновить артефакты',
refreshing: 'Обновление артефактов',
indexing: 'Индексация недавних артефактов сеансов',
tabAll: 'Все',
tabImages: 'Изображения',
tabFiles: 'Файлы',
tabLinks: 'Ссылки',
noArtifactsTitle: 'Артефакты не найдены',
noArtifactsDesc: 'Сгенерированные изображения и файловые результаты появятся здесь по мере их создания в сеансах.',
failedLoad: 'Не удалось загрузить артефакты',
openFailed: 'Не удалось открыть',
itemsImage: 'изображения',
itemsLink: 'ссылки',
itemsFile: 'файлы',
itemsGeneric: 'элементы',
zero: '0',
rangeOf: (start, end, total) => `${start}–${end} из ${total}`,
goToPage: (itemLabel, page) => `Перейти на страницу ${page} ${itemLabel}`,
colTitleLink: 'Название ссылки',
colTitleFile: 'Имя',
colTitleDefault: 'Название / имя',
colLocationLink: 'URL',
colLocationFile: 'Путь',
colLocationDefault: 'Место',
colSession: 'Сеанс',
kindImage: 'изображение',
kindFile: 'файл',
kindLink: 'ссылка',
chat: 'Чат',
copyUrl: 'Копировать URL',
copyPath: 'Копировать путь'
},
artifactCard: {
kind: { code: 'Код', html: 'Интерактивная страница', svg: 'Графика' },
generating: lines => `Генерация… ${lines} ${RU_NOUN(lines, 'строка', 'строки', 'строк')}`,
versionBadge: count => `${count} ${RU_NOUN(count, 'версия', 'версии', 'версий')}`,
open: 'Открыть'
},
artifactPreview: {
versionOf: (current, total) => `v${current} из ${total}`,
olderVersion: 'Более старая версия',
newerVersion: 'Более новая версия',
latest: 'Последняя',
copyContent: 'Копировать содержимое',
download: 'Скачать',
openInBrowser: 'Открыть в браузере',
openInBrowserFailed: 'Не удалось открыть в браузере',
missingTitle: 'Артефакт недоступен',
missingBody: 'Этот артефакт больше нет в локальном реестре.'
},
sidebar: {
gatewayGroups: {
grouping: 'Шлюз и профиль',
rename: 'Переименовать группу',
aliasLabel: 'Отображаемое имя',
aliasHint: 'Меняется только отображаемое имя; имена шлюза и профиля остаются прежними.',
resetName: 'Сбросить имя',
moveUp: 'Переместить вверх',
moveDown: 'Переместить вниз',
reorder: 'Изменить порядок групп',
actions: 'Действия с группой'
},
nav: {
'new-session': 'Новый сеанс',
skills: 'Возможности',
messaging: 'Сообщения',
artifacts: 'Артефакты',
cron: 'Запланированные задачи'
},
searchAria: 'Поиск сеансов',
searchPlaceholder: 'Поиск сеансов…',
clearSearch: 'Очистить поиск',
noMatch: query => `Нет сеансов по запросу «${query}».`,
results: 'Результаты',
pinned: 'Закреплённые',
sessions: 'Сеансы',
cronJobs: 'Cron-задачи',
groupAriaGrouped: 'Показать сеансы одним списком',
groupAriaUngrouped: 'Сгруппировать сеансы по рабочим пространствам',
showProjects: 'Показать проекты',
showSessions: 'Показать сеансы',
groupTitleGrouped: 'Не группировать сеансы',
groupTitleUngrouped: 'Группировать по рабочему пространству',
allPinned: 'Здесь всё закреплено. Открепите чат, чтобы он появился в недавних.',
shiftClickHint: 'Shift-клик по чату, чтобы закрепить',
noWorkspace: 'Без рабочего пространства',
projectEmpty: 'Сеансов пока нет',
projectLoadFailed: 'Не удалось загрузить сеансы',
noSessions: 'Сеансов пока нет',
noFilterMatches: 'Нет сеансов по этим фильтрам',
projects: {
showAllSessions: 'Показать все сессии',
sectionLabel: 'Проекты',
home: 'Главная',
newButton: 'Новый проект',
createTitle: 'Новый проект',
createDesc: 'Назовите рабочее пространство и добавьте одну или несколько папок.',
renameTitle: 'Переименовать проект',
addFolderTitle: 'Добавить папку',
namePlaceholder: 'например, Skunkworks',
foldersLabel: 'Папки',
ideaLabel: 'Идея',
ideaPlaceholder: 'О чём этот проект? (сохраняется в IDEA.md)',
ideaGenerate: 'Сгенерировать идею',
ideaGenerating: 'Генерация…',
ideaShuffle: 'Перемешать шаблоны',
noFolders: 'Папки ещё не добавлены.',
addFolder: 'Добавить папку',
primaryBadge: 'основная',
removeFolder: 'Удалить',
create: 'Создать',
menu: 'Действия',
menuRename: 'Переименовать',
menuAppearance: 'Внешний вид',
noColor: 'Без цвета',
menuAddFolder: 'Добавить папку',
menuSetActive: 'Сделать активным',
menuDelete: 'Удалить',
moveToProject: 'Переместить в проект',
movedTo: name => `Перемещено в ${name}`,
moveFailed: 'Не удалось переместить сеанс',
moveNoFolder: 'У этого проекта нет папки для перемещения',
moveNoProjects: 'Нет других проектов',
reveal: 'Показать в папке',
copyPath: 'Копировать путь',
removeFromSidebar: 'Скрыть из боковой панели',
createFailed: 'Не удалось создать проект',
staleBackend:
'Обновите бэкенд Hermes, чтобы создавать проекты — ваш бэкенд старше этого desktop-приложения (Настройки → Обновления → Бэкенд).',
deleteConfirm: 'Это удалит сохранённый проект из Hermes. Файлы, git-репозитории и worktrees не пострадают.',
startWork: 'Новый worktree',
newWorktreeTitle: 'Новый worktree',
newWorktreeDesc: 'Назовите ветку для этого worktree.',
branchPlaceholder: 'например, my-feature',
branchOff: () => ({ after: '', before: 'от ветки ' }),
baseBranchPlaceholder: 'Поиск веток…',
baseBranchNone: 'Ветки не найдены',
startWorkFailed: 'Не удалось создать worktree',
worktreeStaleBackend:
'Обновите бэкенд Hermes, чтобы создавать worktrees по этому удалённому соединению — он старше git worktree API.',
worktreeProjectLabel: 'Проект',
worktreeProjectPlaceholder: 'Поиск проектов…',
worktreeProjectNone: 'Нет проектов с папкой',
convertBranch: 'Преобразовать ветку…',
convertBranchTitle: 'Преобразовать ветку',
convertBranchDesc: 'Откройте закоммиченные ветки или создайте worktree для свободной ветки.',
convertBranchPlaceholder: 'Поиск веток…',
convertBranchInstead: 'Преобразовать существующую ветку',
branchOpenExisting: 'открыть',
branchSwitchHome: 'сменить home',
branchCreateWorktree: 'новый worktree',
branchTrackRemote: 'отслеживать удалённую',
branchesLoading: 'Загрузка веток…',
noBranches: 'Ветки не найдены',
removeWorktree: 'Удалить worktree',
removeWorktreeFailed: 'Не удалось удалить worktree (есть незакоммиченные изменения?)',
removeWorktreeConfirm:
'Удалить из git (сотрёт каталог worktree; ветка останется) или просто скрыть лану из боковой панели, оставив worktree на диске.',
removeWorktreeDirty:
'В этом worktree есть незакоммиченные изменения. Удалить принудительно (сбросит эти изменения) или просто скрыть лану и оставить на диске.',
forceRemove: 'Удалить принудительно',
enter: label => `Открыть ${label}`,
reorder: label => `Изменить порядок ${label}`,
toggle: (label, open) => `${open ? 'Показать' : 'Скрыть'} сеансы ${label}`,
back: 'Все проекты'
},
newSessionIn: label => `Новый сеанс в ${label}`,
showMoreIn: (count, label) => `Показать ещё ${count} в ${label}`,
loading: 'Загрузка…',
loadMore: 'Загрузить ещё',
loadCount: step => `Загрузить ещё ${step}`,
messageCount: count => `${count} ${RU_PLURAL(count, 'сообщение', 'сообщения', 'сообщений')}`,
toolCallCount: count =>
`${count} ${RU_PLURAL(count, 'вызов инструмента', 'вызова инструмента', 'вызовов инструмента')}`,
row: {
pin: 'Закрепить',
unpin: 'Открепить',
markUnread: 'Отметить как непрочитанное',
markRead: 'Отметить как прочитанное',
unreadFailed: 'Не удалось обновить состояние непрочитанных',
copyId: 'Копировать ID',
export: 'Экспорт',
branchFrom: 'Ветка',
rename: 'Переименовать',
archive: 'В архив',
newWindow: 'Новое окно',
openInTerminal: 'Открыть в терминале',
hideTabBar: 'Скрыть панель вкладок',
openInNewTab: 'Открыть в новой вкладке',
openInSplit: 'Открыть в сплит-виде',
copyIdFailed: 'Не удалось скопировать ID сеанса',
sessionActions: 'Действия с сеансом',
sessionRunning: 'Сеанс выполняется',
needsInput: 'Нужен ваш ввод',
waitingForAnswer: 'Ждёт вашего ответа',
finishedUnread: 'Завершён — не прочитан',
backgroundRunning: 'Фоновая задача выполняется',
draftSession: 'Черновик — ещё ничего не отправлено',
handoffOrigin: platform => `Передано из ${platform}`,
ownedByProfile: profile => `Профиль: ${profile}`,
renamed: 'Переименовано',
renameFailed: 'Переименование не удалось',
renameTitle: 'Переименовать сеанс',
renameDesc: 'Оставьте пустым, чтобы очистить.',
untitledPlaceholder: 'Сеанс без названия',
deleteTitle: 'Удалить сеанс?',
deleteDesc: title => `Это навсегда удалит «${title}». Это действие необратимо.`,
deleting: 'Удаление…',
deleted: 'Сеанс удалён',
untitledChat: id => `Чат ${id}`,
messageCount: count => `${count} ${RU_PLURAL(count, 'сообщение', 'сообщения', 'сообщений')}`,
todoProgress: 'Задачи выполнены',
ageNow: 'сейчас',
ageDay: 'д',
ageHour: 'ч',
ageMin: 'м'
},
dateDivider: {
today: 'Ранее сегодня',
yesterday: 'Вчера',
thisWeek: 'Ранее на этой неделе',
lastWeek: 'На прошлой неделе',
thisMonth: 'Ранее в этом месяце'
},
statusDivider: {
working: 'Работает',
done: 'Готово'
},
markAllRead: 'Отметить все как прочитанные'
},
composer: {
message: 'Сообщение',
wakingProfile: profile => `Пробуждаем ${profile}…`,
placeholderStarting: 'Запуск Hermes...',
placeholderReconnecting: 'Переподключение к Hermes…',
placeholderFollowUp: 'Отправить продолжение',
newSessionPlaceholders: [
'Что будем делать?',
'Дайте Hermes задачу',
'О чём вы думаете?',
'Опишите, что нужно',
'Что обсудим?',
'Спросите о чём угодно',
'Начните с цели'
],
followUpPlaceholders: [
'Отправьте продолжение',
'Добавьте контекст',
'Уточните запрос',
'Что дальше?',
'Продолжаем',
'Запустите дальше',
'Скорректируйте или продолжите'
],
startVoice: 'Начать голосовой разговор',
openDirective: 'Открыть',
queueMessage: 'Вставить сообщение в очередь',
steer: 'Направить текущий запуск',
stop: 'Стоп',
send: 'Отправить',
speaking: 'Говорит',
transcribing: 'Расшифровка',
thinking: 'Думает',
muted: 'Приглушено',
listening: 'Слушает',
muteMic: 'Выключить микрофон',
unmuteMic: 'Включить микрофон',
stopListening: 'Перестать слушать и отправить',
stopShort: 'Стоп',
endConversation: 'Завершить голосовой разговор',
endShort: 'Завершить',
stopDictation: 'Остановить диктовку',
transcribingDictation: 'Расшифровка диктовки',
voiceControls: 'Голос',
voiceDictation: 'Голосовая диктовка',
speakReplies: 'Зачитывать ответы вслух',
stopSpeakingReplies: 'Перестать зачитывать ответы вслух',
wakeWordListening: phrase => `Слово-пробуждение: «${phrase}» — слушает`,
wakeWordOff: phrase => `Слово-пробуждение: «${phrase}» — выключено`,
wakeWordPausedVoice: phrase => `Слово-пробуждение: «${phrase}» — приостановлено во время голосового чата`,
lookupLoading: 'Ищем…',
lookupNoMatches: 'Нет совпадений.',
lookupTry: 'Попробуйте',
lookupOr: 'или',
commonCommands: 'Частые команды',
hotkeys: 'Горячие клавиши',
helpFooter: 'открывает полную панель · backspace закрывает',
commandDescs: {
'/help': 'полный список команд + горячих клавиш',
'/clear': 'начать новый сеанс',
'/resume': 'возобновить прошлый сеанс',
'/details': 'уровень детализации транскрипта',
'/copy': 'скопировать выделенное или последнее сообщение ассистента',
'/quit': 'выйти из hermes'
},
hotkeyDescs: {
'composer.mention': 'ссылки на файлы, папки, URL, git',
'composer.slash': 'палитра слэш-команд',
'composer.help': 'эта быстрая справка (удалите, чтобы закрыть)',
'composer.sendNewline': 'отправить · Shift+Enter — новая строка',
'composer.sendQueued': 'отправить следующий ход в очереди',
'keybinds.openPanel': 'все горячие клавиши',
'composer.cancel': 'закрыть поповер · отменить запуск',
'composer.history': 'переключать поповер / историю'
},
attachUrlTitle: 'Прикрепить URL',
attachUrlDesc: 'Hermes загрузит страницу и добавит её как контекст для этого хода.',
urlPlaceholder: 'https://example.com/post',
urlHintPre: 'Укажите полный URL, например ',
attach: 'Прикрепить',
queued: count => `${count} в очереди`,
queuedPaused: count => `${count} в очереди — пауза`,
attachmentOnly: 'Ход только с вложениями',
emptyTurn: 'Пустой ход',
attachments: count => `${count} ${RU_NOUN(count, 'вложение', 'вложения', 'вложений')}`,
editingInComposer: 'Редактирование в композере',
editingQueuedInComposer: 'Редактирование хода в очереди в композере',
queueEdit: 'Изменить',
queueSendNext: 'Дальше',
queueSteer: 'Направить — изменить текущий ход сейчас',
queueSend: 'Отправить',
queueDelete: 'Удалить',
queueResume: 'Продолжить',
queueResumeTip: 'Приостановлено стопом — продолжить отправку ходов из очереди',
queueStuckTitle: 'Сообщение из очереди не отправлено',
queueStuckBody:
'Ход из очереди несколько раз не удалось отправить. Он всё ещё в очереди — попробуйте отправить снова.',
previewUnavailable: 'Предпросмотр недоступен',
previewLabel: label => `Предпросмотр ${label}`,
couldNotPreview: label => `Не удалось предпросмотреть ${label}`,
removeAttachment: label => `Удалить ${label}`,
dictating: 'Диктовка',
preparingAudio: 'Подготовка аудио',
speakingResponse: 'Зачитывает ответ',
readingAloud: 'Читает вслух',
themeSuggestions: 'Предложения тем desktop',
noMatchingThemes: 'Нет совпадающих тем.',
themeTryPre: 'Попробуйте ',
themeTryPost: '.',
attachLabel: 'Прикрепить',
files: 'Файлы…',
folder: 'Папка…',
images: 'Изображения…',
pasteImage: 'Вставить изображение',
url: 'URL…',
promptSnippets: 'Фрагменты промптов…',
tipPre: 'Подсказка: введите ',
tipPost: ' чтобы ссылаться на файлы inline.',
snippetsTitle: 'Фрагменты промптов',
snippetsDesc: 'Выберите стартовый промпт, чтобы вставить в композер.',
dropFiles: 'Перетащите файлы, чтобы прикрепить',
dropSession: 'Перетащите, чтобы связать этот чат',
mcpSuggestions: {
label: server => `Добавить ${server}`,
tip: keyword => `Предложено, потому что вы упомянули «${keyword}» — нажмите, чтобы подключить`,
connecting: server => `Подключаем ${server}…`,
cancelTip: 'Нажмите, чтобы отменить',
added: server => `Добавлен ${server}`,
addedTip: 'Подключено — его инструменты готовы в этом чате',
connectFailed: server => `Не удалось подключить ${server}`
},
skillSuggestions: {
label: skill => `Использовать навык: ${skill}`,
tip: skill => `Вы упомянули «${skill}» — нажмите, чтобы начать с этого навыка`,
done: skill => `Добавлено /${skill}`,
doneTip: 'Навык загрузится при отправке'
},
githubSuggestions: {
label: 'Настроить GitHub',
tip: 'GitHub работает через навыки gh CLI здесь — нажмите, чтобы подключить аккаунт',
done: 'Добавлено /github-auth',
doneTip: 'Отправьте сообщение, и агент проведёт вас через вход в GitHub'
},
repairSuggestions: {
label: server => `Переподключить ${server}`,
tip: server => `Недавний вызов ${server} завершился ошибкой соединения`,
working: server => `Переподключаем ${server}…`,
workingTip: 'Нажмите, чтобы отменить',
done: server => `Переподключено ${server}`,
doneTip: 'Свежие учётные данные активны в этом чате',
failed: server => `Не удалось переподключить ${server}`
},
cronSuggestions: {
label: 'Запланировать это',
tip: phrase => `«${phrase}» звучит как повторяющееся — запланируйте выполнение по расписанию`,
prefix: 'Настроить это как запланированную задачу:',
done: 'Отмечено для планирования',
doneTip: 'Отправьте, и агент создаст задачу'
},
snippets: {
codeReview: {
label: 'Рецензия кода',
description: 'Аудит текущих изменений на предмет регрессий, упущенных граничных случаев и недостающих тестов.',
text: 'Пожалуйста, проверьте это на баги, регрессии и недостающие тесты.'
},
implementationPlan: {
label: 'План реализации',
description: 'Опишите подход перед правкой кода, чтобы diff остался сфокусированным.',
text: 'Пожалуйста, составьте краткий план реализации перед изменением кода.'
},
explainThis: {
label: 'Объяснить это',
description: 'Разберите, как работает выделенный код, и дайте ссылки на ключевые файлы.',
text: 'Пожалуйста, объясните, как это работает, и укажите ключевые файлы.'
}
}
},
statusStack: {
agents: 'Агенты',
background: count => `${count} ${RU_NOUN(count, 'фоновая задача', 'фоновые задачи', 'фоновых задач')}`,
goalActive: 'Цель активна',
goalBlocked: 'Цель заблокирована',
goalDone: 'Цель выполнена',
goalPaused: 'Цель на паузе',
goalWaiting: 'Цель ожидает',
subagents: count => `${count} ${RU_PLURAL(count, 'субагент', 'субагента', 'субагентов')}`,
todos: (done, total) => `Задачи ${done}/${total}`,
running: 'Выполняется',
stop: 'Стоп',
dismiss: 'Скрыть',
exit: code => `exit ${code}`,
control: {
goalActiveTurns: (turn, maxTurns) => `Ход ${turn}/${maxTurns}`,
goalDoneTurns: turns => `${turns} ходов`,
goalTurn: turn => `Ход ${turn}`,
goalActions: 'Действия с целью',
viewDetails: 'Подробнее',
addCriterion: 'Добавить критерий',
addCriterionDialogTitle: 'Добавить критерий',
addCriterionPlaceholder: 'Введите текст критерия...',
criterionLabel: 'Критерий',
pauseGoal: 'Приостановить цель',
resumeGoal: 'Возобновить цель',
resumeNow: 'Возобновить сейчас',
clearGoal: 'Очистить цель',
clearGoalConfirmTitle: 'Очистить цель?',
clearGoalConfirmBody: 'Вы уверены, что хотите очистить активную цель? Это действие необратимо.',
copyCriterion: index => `Скопировать критерий ${index}`,
removeCriterion: index => `Удалить критерий ${index}`,
removeCriterionConfirmTitle: index => `Удалить критерий ${index}?`,
removeCriterionConfirmBody: index => `Вы уверены, что хотите удалить критерий ${index}?`,
clearCriteria: 'Очистить все критерии',
clearCriteriaConfirmTitle: 'Очистить все критерии?',
clearCriteriaConfirmBody: 'Вы уверены, что хотите удалить все критерии этой цели?',
criteriaHeader: count => `Критерии · ${count}`,
noCriteria: 'Нет критериев',
goalDetailsTitle: 'Детали цели',
objectiveLabel: 'Задача',
contractOutcome: 'Результат',
contractVerification: 'Проверка',
contractConstraints: 'Ограничения',
contractBoundaries: 'Границы',
contractStopWhen: 'Условие остановки',
waitBarrierTitle: 'Условие ожидания',
waitUntil: target => `Ожидание до ${target}`,
waitSession: target => `Ожидание сессии ${target}`,
waitPid: pid => `Ожидание процесса ${pid}`,
qualityGatesTitle: 'Контроли качества',
gateCommand: 'Команда',
gateAttempts: (attempts, max) => `${attempts}/${max} попыток`,
gateTimeout: seconds => `таймаут ${seconds}с`,
gateLastExit: code => (code === null ? 'В ожидании' : `Код возврата: ${code}`),
loopActive: 'Цикл активен',
loopPaused: 'Цикл приостановлен',
loopDeferred: 'Цикл отложен',
loopFinished: 'Цикл завершен',
loopRuns: runs => `${runs} запусков`,
loopRunCount: (current, total) => `Запуск ${current}/${total}`,
loopNext: time => `следующий ${time}`,
loopEverySeconds: seconds => `каждые ${seconds}с`,
loopEveryMinutes: minutes => `каждые ${minutes}м`,
loopEveryHours: hours => `каждые ${hours}ч`,
loopSelfPaced: 'автономный',
loopActions: 'Действия цикла',
pauseLoop: 'Приостановить цикл',
resumeLoop: 'Возобновить цикл',
stopLoop: 'Остановить цикл',
stopLoopConfirmTitle: 'Остановить цикл?',
stopLoopConfirmBody: 'Вы уверены, что хотите остановить этот цикл?',
dismissLoop: 'Закрыть цикл',
loopPromptLabel: 'Промпт',
loopCadenceLabel: 'Интервал',
loopUntilLabel: 'Условие окончания',
loopDeferredNotice: 'Активная цель в данный момент управляет сессией.',
loopAwaitingResponse: 'Ожидание ответа',
heartbeatActive: 'Пульс активен',
heartbeatPaused: 'Пульс приостановлен',
heartbeatEveryMinutes: minutes => `каждые ${minutes}м`,
heartbeatEveryHours: hours => `каждые ${hours}ч`,
heartbeatEverySeconds: seconds => `каждые ${seconds}с`,
heartbeatNext: time => `следующий ${time}`,
heartbeatDueWaitingForIdle: 'пора — ожидание простоя',
heartbeatActions: 'Действия пульса',
pauseHeartbeat: 'Приостановить пульс',
resumeHeartbeat: 'Возобновить пульс',
clearHeartbeat: 'Очистить пульс',
clearHeartbeatConfirmTitle: 'Очистить пульс?',
clearHeartbeatConfirmBody: 'Вы уверены, что хотите очистить этот пульс?',
heartbeatFiredCount: count => `Сработал ${count} раз`,
actionFailed: msg => `Ошибка действия: ${msg}`,
actionSucceeded: 'Действие выполнено успешно',
copySuccess: 'Критерий скопирован в буфер обмена',
copyFailure: 'Не удалось скопировать критерий в буфер обмена',
continuationFailed: 'Не удалось отправить продолжение цели',
continuationQueued: 'Цель возобновлена — продолжение поставлено в очередь до конца текущего хода',
continuationBusy: 'Цель возобновлена — сессия занята, выполните /interrupt, чтобы продолжить',
controlUnavailable: msg => `Управление сессией недоступно: ${msg}`,
dismissError: 'Скрыть ошибку',
add: 'Добавить'
},
coding: {
title: 'Рабочее дерево',
noBranch: 'Без ветки',
detached: 'отсоединена',
clean: 'Чисто',
changed: count => `${count} изменено`,
ahead: count => `${count} впереди`,
behind: count => `${count} позади`,
review: 'Проверить',
close: 'Закрыть',
openChanges: 'Открыть изменения',
openFile: 'Открыть файл',
stage: 'Добавить в индекс',
unstage: 'Убрать из индекса',
stageAll: 'Добавить всё в индекс',
viewAsTree: 'Вид деревом',
viewAsList: 'Вид списком',
revert: 'Отменить',
revertAll: 'Отменить всё',
revertConfirm:
'Сбросить изменения в этом файле и вернуть его в закоммиченное состояние? Это действие необратимо.',
revertAllConfirm: 'Сбросить все изменения и вернуть файлы в закоммиченное состояние? Это действие необратимо.',
staged: 'В индексе',
noChanges: 'Изменений нет',
notRepo: 'Не git-репозиторий',
noDiff: 'Нет diff для показа',
scopeUncommitted: 'Незакоммиченные',
scopeBranch: 'Ветка',
scopeLastTurn: 'Последний ход',
commit: 'Коммит',
commitAndPush: 'Коммит и Push',
commitPlaceholder: shortcut => `Сообщение (${shortcut} — коммит)`,
generateCommitMessage: 'Сгенерировать сообщение коммита',
stopGenerating: 'Остановить генерацию',
createPr: 'Создать PR',
openPr: 'Открыть PR',
ghMissing: 'Установите GitHub CLI (gh) и войдите, чтобы открывать PR',
agentShip: 'Попросить Hermes открыть PR',
agentShipUnavailable: 'Чат, которому принадлежат эти изменения, не на экране.',
agentShipPrompt:
'Проверьте текущие изменения, закоммитьте их с ясным conventional-commit сообщением, запушьте ветку и создайте pull request.',
newBranch: 'Новая ветка',
branchOffFrom: base => `Новая ветка от ${base}`,
switchTo: branch => `Переключиться на ${branch}`,
switchFailed: branch => `Не удалось переключиться на ${branch}`,
worktrees: 'Worktrees'
}
},
updates: {
stages: {
idle: 'Готовимся…',
prepare: 'Готовимся…',
fetch: 'Скачиваем…',
pull: 'Почти готово…',
pydeps: 'Завершаем…',
update: 'Обновляем Hermes…',
rebuild: 'Пересобираем desktop-приложение…',
restart: 'Перезапускаем Hermes…',
done: 'Обновление завершено',
manual: 'Обновление из терминала',
guiSkew: 'Обновите desktop-приложение',
error: 'Обновление приостановлено'
},
checking: 'Ищем обновления…',
checkFailedTitle: 'Не удалось проверить обновления',
tryAgain: 'Попробовать снова',
notAvailableTitle: 'Обновление недоступно',
unsupportedMessage: 'Эта версия Hermes не может обновлять себя из приложения.',
connectionRetry: 'Проверьте соединение и попробуйте снова.',
latestBody: 'У вас последняя версия.',
latestBodyBackend: 'Бэкенд работает на последней версии.',
allSetTitle: 'Всё готово',
availableTitle: 'Доступно новое обновление',
availableBody: 'Новая версия Hermes готова к установке.',
availableTitleBackend: 'Доступно обновление бэкенда',
availableBodyBackend: 'Новая версия подключённого бэкенда Hermes готова к установке.',
availableBodyNoChangelog: 'Новая версия готова. Заметки о выпуске недоступны для этого типа установки.',
updateNow: 'Обновить сейчас',
maybeLater: 'Возможно позже',
moreChanges: count => `+ ещё ${count} ${RU_NOUN(count, 'изменение', 'изменения', 'изменений')} включено.`,
manualTitle: 'Обновление из терминала',
manualBody:
'Hermes установлен из командной строки, поэтому обновления тоже выполняются там. Вставьте это в терминал:',
manualPickedUp: 'Hermes подхватит новую версию при следующем запуске.',
guiSkewTitle: 'Обновите desktop-приложение',
guiSkewBody:
'Бэкенд обновлён, но пакет этого desktop-приложения не изменился. Обновите или переустановите desktop-приложение Hermes (ваш AppImage / .deb / .rpm), чтобы версии совпали.',
copy: 'Копировать',
copied: 'Скопировано',
done: 'Готово',
applyingBody:
'Обновлятор Hermes возьмёт процесс на себя в своём окне и автоматически перезапустит Hermes, когда закончит. Пожалуйста, не открывайте Hermes вручную во время обновления.',
applyingBodyBackend:
'Удалённый бэкенд применяет обновление и перезапустится. Hermes переподключится автоматически, когда он снова заработает.',
applyingClose: 'Это окно закроется во время обновления, затем Hermes откроется сам.',
errorTitle: 'Обновление не завершилось',
errorBody: 'Не переживайте — ничего не потеряно. Можно попробовать снова прямо сейчас.',
blockerTitle: 'Закрыть локальные предпросмотры, чтобы обновить Hermes?',
blockerBody:
'Hermes нужно остановить эти локальные предпросмотры перед обновлением. Ваши файлы не будут изменены или удалены.',
foreignBlockerTitle: 'Закрыть другие процессы, чтобы обновить Hermes',
foreignBlockerBody:
'Hermes не может безопасно закрыть эти процессы автоматически. Закройте приложение, терминал или службу, которой принадлежит каждый из них, и повторите обновление.',
mixedBlockerBody:
'Hermes может закрыть локальные предпросмотры, перечисленные ниже. Остальные процессы нужно закрыть вручную, прежде чем обновление сможет продолжиться.',
closePreviewsAndUpdate: 'Закрыть предпросмотры и обновить',
closePreviewsAndCheckAgain: 'Закрыть предпросмотры и проверить снова',
localPreview: 'Локальный предпросмотр',
portLabel: port => `Порт ${port}`,
pidLabel: pid => `PID ${pid}`,
technicalDetails: 'Технические детали',
notNow: 'Не сейчас',
clientAlsoBehindTitle: 'Desktop-приложение отстает',
clientAlsoBehindMessage:
'Бэкенд в актуальном состоянии, но это desktop-приложение ещё на старой версии. Обновите его, чтобы получить последние исправления.',
clientAlsoBehindAction: 'Обновить desktop-приложение',
everythingDispatched: 'Обновление запущено',
everythingSkipped: 'Пропущено',
everythingRowFailed: 'Обновление не удалось',
everythingFanoutFailedTitle: 'Не удалось обновить другие инстанции',
applyStatus: {
preparing: 'Обновляем бэкенд…',
pulling: 'Бэкенд обновляется…',
restarting: 'Бэкенд перезапускается для загрузки обновления…',
notAvailable: 'Обновление недоступно для этого бэкенда.',
failed: 'Не удалось обновить бэкенд.',
noReturn: 'Бэкенд не вернулся в сеть. Обновление могло не завершиться — проверьте хост бэкенда.'
}
},
install: {
stageStates: {
pending: 'Ожидает',
running: 'Установка',
succeeded: 'Готово',
skipped: 'Пропущено',
failed: 'Ошибка'
},
oneTimeTitle: 'Hermes требует одноразовой установки',
unsupportedDesc: platform =>
`Автоматическая установка при первом запуске пока недоступна на ${platform}. Откройте Терминал и выполните команду ниже, затем перезапустите это приложение. При следующих запусках этот шаг будет пропущен.`,
installCommand: 'Команда установки',
copyCommand: 'Копировать команду',
viewDocs: 'Открыть документацию по установке',
installTo: 'Будет установлено в',
retryAfterRun: 'Я выполнил — попробовать снова',
setupChoiceTitle: 'Настройка Hermes Desktop',
setupChoiceDesc:
'Подключите это приложение к уже работающему шлюзу Hermes или установите Hermes локально на этот компьютер.',
connectExistingTitle: 'Подключиться к существующему Hermes',
connectExistingShort: 'Подключить существующий',
connectExistingDesc:
'Используйте удалённый бэкенд с сессионным токеном или входом через браузер. Локальная установка не начнётся.',
installLocalTitle: 'Установить Hermes локально',
installLocalDesc: 'Скачайте Hermes, создайте его Python-окружение и запустите бэкенд на этом компьютере.',
localStartUnavailable: 'Не удалось начать локальную установку. Перезапустите Hermes Desktop и попробуйте снова.',
remoteSetupTitle: 'Подключиться к существующему Hermes',
remoteSetupDesc: 'Введите URL вашего шлюза. Hermes Desktop определит, нужен токен или вход через браузер.',
remoteUrlTitle: 'URL шлюза',
remoteUrlDesc: 'Используйте базовый URL шлюза Hermes, включая https:// для удалённых.',
remoteUrlPlaceholder: 'https://gateway.example.com/hermes',
probing: 'Определяем аутентификацию шлюза...',
probeError: 'Не удалось подключиться к этому шлюзу Hermes.',
identityProvider: 'ваш провайдер аутентификации',
authTitle: 'Аутентификация',
authNeedsOauth: provider => `Сначала войдите через ${provider}, чтобы проверить этот шлюз.`,
authSignedIn: 'Вход через браузер завершён.',
connected: 'Подключено',
signIn: 'Войти',
signInWith: provider => `Войти через ${provider}`,
enterUrlFirst: 'Сначала введите URL шлюза.',
signInIncomplete: 'Окно входа закрылось до завершения аутентификации.',
tokenTitle: 'Сессионный токен',
tokenDesc: 'Вставьте сессионный токен из файла .env удалённого шлюза.',
pasteSessionToken: 'Вставьте сессионный токен',
incompleteSignInTest: 'Войдите, чтобы проверить этот шлюз с OAuth-защитой.',
incompleteTokenTest: 'Введите сессионный токен, чтобы проверить этот шлюз.',
testConnection: 'Проверить соединение',
testSucceeded: (baseUrl, version) => `Подключено к ${baseUrl}${version ? ` (${version})` : ''}.`,
applyRemote: 'Применить и переподключиться',
backToSetup: 'Назад',
failedTitle: 'Установка не удалась',
settingUpTitle: 'Настройка Hermes Agent',
finishingTitle: 'Завершаем',
failedDesc:
'Один из шагов установки завершился ошибкой. На Windows это может произойти, если запущена другая инстанция Hermes CLI или desktop. Остановите все работающие инстанции Hermes и повторите. Подробности — ниже или в журнале desktop.',
activeDesc:
'Это одноразовая настройка. Установщик Hermes скачивает зависимости и настраивает вашу машину. При следующих запусках этот шаг будет пропущен.',
progress: (completed, total) => `Выполнено ${completed} из ${total} шагов`,
currentStage: stage => ` — сейчас: ${stage}`,
fetchingManifest: 'Загружаем манифест установщика...',
error: 'Ошибка',
hideOutput: 'Скрыть вывод установщика',
showOutput: 'Показать вывод установщика',
lines: count => `${count} ${RU_PLURAL(count, 'строка', 'строки', 'строк')}`,
noOutput: 'Вывода пока нет.',
cancelling: 'Отмена...',
cancelInstall: 'Отменить установку',
transcriptSaved: 'Полный транскрипт сохранён в',
copiedOutput: 'Скопировано!',
copyOutput: 'Копировать вывод',
reloadRetry: 'Перезагрузить и повторить'
},
onboarding: {
headerTitle: 'Настроим для вас Hermes Agent',
headerDesc: 'Подключите провайдера модели, чтобы начать общение. Большинство вариантов — в один клик.',
preparingInstall: 'Hermes завершает установку. Обычно это занимает меньше минуты при первом запуске.',
starting: 'Запускаем Hermes…',
lookingUpProviders: 'Ищем провайдеров...',
collapse: 'Свернуть',
otherProviders: 'Другие провайдеры',
haveApiKey: 'У меня есть API-ключ',
chooseLater: 'Выберу провайдера позже',
recommended: 'Рекомендуется',
connected: 'Подключено',
featuredPitch: 'Одна подписка, 300+ передовых моделей — рекомендуемый способ запускать Hermes',
fireworksPitch: 'Прямой API моделей — передовые модели на хостинге Fireworks',
openRouterPitch: 'Один ключ, сотни моделей — надёжный вариант по умолчанию',
apiKeyOptions: {
fireworks: {
short: 'прямой API моделей',
description: 'Прямой доступ к моделям на хостинге Fireworks AI.'
},
openrouter: {
short: 'один ключ, много моделей',
description: 'Сотни моделей за одним ключом. Хороший вариант по умолчанию для новых установок.'
},
openai: { short: 'модели класса GPT', description: 'Прямой доступ к моделям OpenAI.' },
gemini: { short: 'модели Gemini', description: 'Прямой доступ к моделям Google Gemini.' },
xai: { short: 'модели Grok', description: 'Прямой доступ к моделям xAI Grok.' },
local: {
short: 'self-hosted',
description:
'Укажите Hermes локальный или self-hosted OpenAI-совместимый endpoint (vLLM, llama.cpp, Ollama и т.д.).'
}
},
backToSignIn: 'Назад ко входу',
getKey: 'Получить ключ',
replaceCurrent: 'Заменить текущее значение',
pasteApiKey: 'Вставьте API-ключ',
localApiKeyPlaceholder: 'API-ключ (необязательно — только если ваш endpoint его требует)',
couldNotSave: 'Не удалось сохранить учётные данные.',
connecting: 'Подключение',
update: 'Обновить',
flowSubtitles: {
pkce: 'Откроет браузер для входа, затем продолжит здесь',
device_code: 'Откроет страницу подтверждения в браузере — Hermes подключится автоматически',
external: 'Войдите один раз в терминале, затем вернитесь в чат'
},
startingSignIn: provider => `Начинаем вход для ${provider}...`,
verifyingCode: provider => `Проверяем ваш код через ${provider}...`,
connectedProvider: provider => `${provider} подключён`,
connectedPicking: provider => `${provider} подключён. Выбираем модель по умолчанию...`,
signInFailed: 'Вход не удался. Попробуйте снова.',
pickDifferentProvider: 'Выбрать другого провайдера',
signInWith: provider => `Войти через ${provider}`,
openedBrowser: provider => `Мы открыли ${provider} в вашем браузере.`,
authorizeThere: 'Авторизуйте Hermes там.',
copyAuthCode: 'Скопируйте код авторизации и вставьте его ниже.',
pasteAuthCode: 'Вставьте код авторизации',
reopenAuthPage: 'Открыть страницу авторизации снова',
autoBrowser: provider =>
`Мы открыли ${provider} в вашем браузере. Авторизуйте Hermes там, и подключение произойдёт автоматически — ничего копировать и вставлять не нужно.`,
reopenSignInPage: 'Открыть страницу входа снова',
waitingAuthorize: 'Ждём вашей авторизации...',
externalPending: provider =>
`${provider} входит через собственный CLI. Выполните эту команду в терминале, затем вернитесь и выберите «Я вошёл»:`,
signedIn: 'Я вошёл',
deviceCodeOpened: provider => `Мы открыли ${provider} в вашем браузере. Введите там этот код:`,
reopenVerification: 'Открыть страницу подтверждения снова',
copy: 'Копировать',
defaultModel: 'Модель по умолчанию',
freeTier: 'Бесплатный тариф',
pro: 'Pro',
free: 'Free',
price: (input, output) => `${input} вход / ${output} выход за Mtok`,
change: 'Изменить',
startChatting: 'Начать',
docs: provider => `Документация ${provider}`
},
modelPicker: {
title: 'Сменить модель',
current: 'текущая:',
unknown: '(неизвестно)',
search: 'Фильтр провайдеров и моделей...',
noModels: 'Модели не найдены.',
addProvider: 'Добавить провайдера',
loadFailed: 'Не удалось загрузить модели',
noAuthenticatedProviders: 'Нет провайдеров с аутентификацией.',
pro: 'Pro',
proNeedsSubscription: 'Модели Pro требуют платной подписки Nous.',
free: 'Free',
freeTier: 'Бесплатный тариф',
priceTitle: 'Цена вход / выход за миллион токенов',
wasPrice: 'было'
},
modelVisibility: {
title: 'Модели',
search: 'Поиск моделей',
noAuthenticatedProviders: 'Нет провайдеров с аутентификацией.',
addProvider: 'Добавить провайдера…'
},
shell: {
windowControls: 'Управление окном',
paneControls: 'Управление панелями',
appControls: 'Управление приложением',
modelMenu: {
search: 'Поиск моделей',
noModels: 'Модели не найдены',
editModels: 'Изменить модели…',
refreshModels: 'Обновить модели',
fast: 'Быстрая'
},
modelOptions: {
noOptions: 'Для этой модели нет опций',
options: 'Опции',
thinking: 'Размышление',
fast: 'Быстрая',
effort: 'Усилия',
minimal: 'Минимально',
low: 'Низкое',
medium: 'Среднее',
high: 'Высокое',
xhigh: 'Очень высокое',
max: 'Максимум',
ultra: 'Ультра',
updateFailed: 'Не удалось обновить опцию модели',
fastFailed: 'Не удалось обновить быстрый режим'
},
gatewayMenu: {
gateway: 'Шлюз',
connected: 'Подключён',
connecting: 'Подключение',
offline: 'Недоступен',
inferenceReady: 'Инференс готов',
inferenceNotReady: 'Инференс не готов',
checkingInference: 'Проверка инференса',
disconnected: 'Отключён',
reconnectGateway: 'Переподключить шлюз',
openSystem: 'Открыть системную панель',
connection: label => `Соединение: ${label}`,
recentActivity: 'Недавняя активность',
viewAllLogs: 'Все журналы →',
messagingPlatforms: 'Платформы сообщений'
},
approvalMode: {
title: 'Режим подтверждения',
ariaLabel: mode => `Режим подтверждения: ${mode}`,
manual: 'Ручной',
manualDescription: 'Спрашивать перед действиями, требующими подтверждения',
smart: 'Умный',
smartDescription: 'Автоматически оценивать действия и спрашивать при необходимости',
off: 'Выкл',
offDescription: 'Выполнять без запросов подтверждения'
},
statusbar: {
unknown: 'неизвестно',
restart: 'перезапуск',
update: 'обновление',
updateInProgress: 'Обновление выполняется',
commitsBehind: (count, branch) => `${count} ${RU_NOUN(count, 'коммит', 'коммита', 'коммитов')} позади ${branch}`,
desktopVersion: version => `Hermes Desktop v${version}`,
backendVersion: version => `Бэкенд v${version}`,
clientLabel: version => `клиент v${version}`,
connectionSsh: host => `SSH: ${host}`,
connectionRemote: host => `Удалённый: ${host}`,
connectionCloud: host => `Облако: ${host}`,
connectionCloudTooltip: host => `Hermes Cloud · ${host}`,
connectionSshTooltip: host => `SSH · ${host}`,
connectionRemoteTooltip: host => `Удалённый · ${host}`,
backendLabel: version => `бэкенд v${version}`,
commit: sha => `коммит ${sha}`,
branch: branch => `ветка ${branch}`,
closeCommandCenter: 'Закрыть командный центр',
openCommandCenter: 'Открыть командный центр',
showTerminal: 'Показать терминал',
hideTerminal: 'Скрыть терминал',
gateway: 'Шлюз',
gatewayReady: 'готов',
gatewayNeedsSetup: 'нужна настройка',
gatewayChecking: 'проверка',
gatewayConnecting: 'подключение',
gatewayOffline: 'недоступен',
gatewayRestarting: 'перезапуск…',
gatewayTitle: 'Шлюз',
customizeTitle: 'Показывать в статус-баре',
hideStatusbar: 'Скрыть статус-бар',
resetStatusbar: 'Сбросить к значениям по умолчанию',
toggleApprovalMode: 'Подтверждения',
toggleBackendVersion: 'Версия бэкенда',
toggleCacheHitRate: 'Попадания в кэш',
toggleCommandCenter: 'Командный центр',
toggleContextUsage: 'Шкала контекста',
toggleRunningTimer: 'Таймер хода',
toggleSessionTimer: 'Таймер сеанса',
toggleTerminal: 'Терминал',
toggleTokensPerSecond: 'Токенов в секунду',
toggleVersion: 'Версия и обновления',
toggleWorkspace: 'Рабочее пространство',
cacheHitRateTitle: 'Доля попаданий в кэш промпта за сеанс — кэшированные токены дешевле, чем выше, тем дешевле',
tokensPerSecondTitle: 'Выходных токенов в секунду, среднее за последние 10 вызовов модели',
agents: 'Агенты',
closeAgents: 'Закрыть агентов',
openAgents: 'Открыть агентов',
subagents: count => `${count} ${RU_PLURAL(count, 'субагент', 'субагента', 'субагентов')}`,
failed: count => `${count} ${RU_NOUN(count, 'сбой', 'сбоя', 'сбоев')}`,
running: count => `${count} ${RU_NOUN(count, 'выполняется', 'выполняются', 'выполняется')}`,
cron: 'Cron',
openCron: 'Открыть cron-задачи',
webhooks: 'Вебхуки',
openWebhooks: 'Открыть вебхуки',
starmap: 'Граф памяти',
openStarmap: 'Открыть граф памяти',
turnRunning: 'Выполняется',
contextUsage: 'Использование контекста',
contextUsagePanel: {
categories: {
conversation: 'Диалог',
mcp: 'MCP',
memory: 'Память',
rules: 'Правила',
skills: 'Навыки',
subagent_definitions: 'Определения субагентов',
system_prompt: 'Системный промпт',
tool_definitions: 'Определения инструментов'
},
empty: 'Данных контекста пока нет',
loading: 'Загрузка разбивки…',
percentFull: percent => `${percent}% занято`,
title: 'Использование контекста',
tokenSummary: (used, max) => `${used} / ${max} токенов`
},
session: 'Сеанс',
yoloOn: 'YOLO включён — автоматическое подтверждение опасных команд. Shift-клик переключает глобально.',
yoloOff: 'YOLO выключен. Shift-клик переключает глобально.',
modelNone: 'нет',
noModel: 'модель не выбрана',
switchModel: 'Сменить модель',
openModelPicker: 'Открыть выбор модели',
modelPinned: 'закреплено вами; новые чаты используют её вместо модели по умолчанию из настроек',
modelTitle: (provider, model) => `Модель · ${provider}: ${model}`,
providerModelTitle: (provider, model) => `${provider} · ${model}`
}
},
rightSidebar: {
aria: 'Правая боковая панель',
panelsAria: 'Панели правой боковой панели',
files: 'Файловая система',
terminal: 'Терминал',
noFolderSelected: 'Папка не выбрана',
changeCwdTitle: 'Изменить рабочий каталог',
remotePickerTitle: 'Выбрать удалённую папку',
remotePickerDescription: 'Просмотрите папки на подключённом бэкенде.',
remotePickerSelect: 'Выбрать папку',
folderTip: cwd => cwd,
openFolder: 'Открыть папку',
refreshTree: 'Обновить дерево',
collapseAll: 'Свернуть все папки',
previewUnavailable: 'Предпросмотр недоступен',
couldNotPreview: path => `Не удалось предпросмотреть ${path}`,
noProjectTitle: 'Проект не открыт',
noProjectBody: 'Откройте проект, чтобы просматривать его файлы и проверять изменения.',
noProjectOpen: 'Проект не открыт',
noDiffs: 'Нет изменений',
unreadableTitle: 'Недоступно',
unreadableBody: error => `Не удалось прочитать эту папку (${error}).`,
emptyTitle: 'Пусто',
emptyBody: 'Эта папка пуста.',
treeErrorTitle: 'Ошибка дерева',
treeErrorBody: 'Дерево файлов столкнулось с ошибкой при отображении этой папки.',
tryAgain: 'Попробовать снова',
loadingTree: 'Загрузка дерева файлов',
loadingFiles: 'Загрузка файлов',
terminalHide: 'Скрыть терминал',
terminalsAria: 'Терминалы',
terminalNew: 'Новый терминал',
terminalCloseOthers: 'Закрыть другие',
terminalCloseAll: 'Закрыть все',
addToChat: 'Добавить в чат'
},
preview: {
tab: 'Предпросмотр',
closePane: 'Закрыть панель предпросмотра',
loading: 'Загрузка предпросмотра',
unavailable: 'Предпросмотр недоступен',
opening: 'Открываем...',
hide: 'Скрыть',
openPreview: 'Открыть предпросмотр',
openInBrowser: 'Открыть в браузере',
linkHint: '⌘/Ctrl-клик — панель предпросмотра',
sourceLineTitle: 'Клик — выбрать · Shift-клик — расширить · перетащите в композер',
source: 'ИСТОЧНИК',
renderedPreview: 'ПРЕДПРОСМОТР',
diff: 'DIFF',
unknownSize: 'размер неизвестен',
binaryTitle: 'Похоже на бинарный файл',
binaryBody: label => `Предпросмотр ${label} может показать нечитаемый текст.`,
largeTitle: 'Этот файл большой',
largeBody: (label, size) => `${label} — ${size}. Hermes покажет только первые 512 КБ.`,
previewAnyway: 'Предпросмотр всё равно',
truncated: 'Показаны первые 512 КБ.',
noInlineTitle: 'Нет inline-предпросмотра',
noInlineBody: mimeType => `${mimeType || 'Этот тип файла'} всё равно можно прикрепить как контекст.`,
edit: 'Изменить',
editing: 'Изменение',
unsavedChanges: 'Несохранённые изменения',
saveFailed: message => `Не удалось сохранить: ${message}`,
diskChangedTitle: 'Файл изменился на диске',
diskChangedBody:
'Этот файл изменился с момента открытия. Перезаписать его вашей версией или сбросить правки и перезагрузить?',
overwrite: 'Перезаписать',
discardReload: 'Сбросить и перезагрузить',
console: {
deselect: 'Снять выделение записи',
select: 'Выбрать запись',
copyFailed: 'Не удалось скопировать вывод консоли',
copyEntry: 'Скопировать эту запись',
sendEntry: 'Отправить эту запись в чат',
messages: count => `${count} ${RU_PLURAL(count, 'сообщение консоли', 'сообщения консоли', 'сообщений консоли')}`,
resize: 'Изменить размер консоли предпросмотра',
title: 'Консоль предпросмотра',
selected: count => `${count} ${count === 1 ? 'выделено' : 'выделено'}`,
sendToChat: 'Отправить в чат',
copySelected: 'Скопировать выбранное в буфер',
copyAll: 'Скопировать всё в буфер',
copy: 'Копировать',
clear: 'Очистить',
empty: 'Сообщений консоли пока нет.',
promptHeader: 'Консоль предпросмотра:',
sentTitle: 'Отправлено в чат',
sentMessage: count =>
`${count} ${RU_NOUN(count, 'запись лога', 'записи лога', 'записей лога')} добавлено в композер`
},
web: {
appFailedToBoot: 'Приложение предпросмотра не запустилось',
serverNotFound: 'Сервер не найден',
remoteLoopback:
'Этот адрес указывает на машину, на которой работает ваш агент, а не на эту. Панель браузера загружает страницы локально, поэтому для удалённого dev-сервера нужен порт-форвардинг или доступный hostname.',
failedToLoad: 'Не удалось загрузить предпросмотр',
tryAgain: 'Попробовать снова',
restarting: 'Hermes перезапускается...',
askRestart: 'Попросить Hermes перезапустить сервер',
lookingRestart: taskId => `Hermes ищет сервер предпросмотра для перезапуска (${taskId})`,
restartingTitle: 'Перезапуск сервера предпросмотра',
restartingMessage: 'Hermes работает в фоне. Следите за прогрессом в консоли предпросмотра.',
startRestartFailed: message => `Не удалось запустить перезапуск сервера: ${message}`,
restartFailed: 'Перезапуск сервера не удался',
hideConsole: 'Скрыть консоль предпросмотра',
showConsole: 'Показать консоль предпросмотра',
hideDevTools: 'Скрыть DevTools предпросмотра',
openDevTools: 'Открыть DevTools предпросмотра',
goBack: 'Назад',
goForward: 'Вперёд',
reload: 'Перезагрузить страницу',
address: 'Адрес',
addressPlaceholder: 'Введите адрес',
blankPageBody: 'Введите адрес выше, чтобы просматривать, или попросите Hermes открыть страницу.',
finishedRestarting: message => `Hermes завершил перезапуск сервера предпросмотра${message ? `: ${message}` : ''}`,
failedRestarting: message => `Перезапуск сервера не удался: ${message}`,
unknownError: 'неизвестная ошибка',
restartedTitle: 'Сервер предпросмотра перезапущен',
reloadingNow: 'Перезагружаем предпросмотр.',
restartFailedTitle: 'Перезапуск предпросмотра не удался',
restartFailedMessage: 'Hermes не смог перезапустить сервер.',
stillWorking:
'Hermes всё ещё работает, но результата перезапуска пока нет. Команда сервера может выполняться в foreground.',
workspaceReloading: 'Рабочее пространство изменилось, перезагружаем предпросмотр',
fileChanged: url => `Файл изменился, перезагружаем предпросмотр: ${url}`,
filesChanged: (count, url) =>
`${count} ${RU_NOUN(count, 'изменение файла', 'изменения файла', 'изменений файла')}, перезагружаем предпросмотр: ${url}`,
watchFailed: message => `Не удалось отслеживать файл предпросмотра: ${message}`,
moduleMimeDescription:
'Модульные скрипты раздаются с неверным MIME-типом. Обычно это значит, что статический файл-сервер раздаёт Vite/React-приложение вместо dev-сервера проекта.',
loadFailedConsole: (code, message) => `Не удалось загрузить${code ? ` (${code})` : ''}: ${message}`,
unreachableDescription: 'Страница предпросмотра недоступна.',
openTarget: url => `Открыть ${url}`,
fallbackTitle: 'Предпросмотр'
}
},
zones: {
showTabStrip: 'Показать вкладки',
hideTabStrip: 'Скрыть вкладки',
showStripTab: title => `Показать ${title}`,
hideStripTab: title => `Скрыть ${title}`,
lastTabKeptTitle: 'Последняя вкладка остаётся',
lastTabKeptBody:
'В этой зоне нужна хотя бы одна видимая вкладка. Сначала покажите другую вкладку или сверните всю боковую панель.',
toggleStripTab: title => `Переключить вкладку ${title}`,
minimize: 'Свернуть',
restore: 'Восстановить',
closeRunningTitle: 'Закрыть работающую вкладку?',
closeRunningBody:
'Этот чат ещё работает (или ждёт вашего ввода). Закрытие вкладки скроет его — сеанс сохранит прогресс и можно будет открыть снова из боковой панели.',
closeRunningConfirm: 'Закрыть вкладку',
reload: 'Перезагрузить',
closeOthers: 'Закрыть другие',
closeToRight: 'Закрыть справа',
closeAll: 'Закрыть все',
newSessionTab: 'Вкладка нового сеанса',
pluginDisabled: pluginId => `Плагин «${pluginId}» отключён`,
pluginDisabledBody: 'Включите его снова в Возможности → Плагины, чтобы вернуть панель.',
missingPane: paneId => `нет панели: ${paneId}`,
editTitle: 'Раскладки',
editHint: 'Выберите раскладку или перетащите панели между зонами.',
reset: 'Сбросить',
templates: 'Шаблоны',
custom: 'Свои',
newGridLayout: 'Новая сеточная раскладка',
saveCurrentAs: 'Сохранить текущую расстановку как шаблон',
nameLayoutPlaceholder: 'Название этой раскладки…',
deletePreset: name => `Удалить ${name}`,
zoneEditorTitle: 'Редактор зон',
editorHintPre: 'клик — разделить · ',
editorHintPost:
' переворачивает линию · перетаскивание между зонами — слить · перетаскивание общих граней — изменить размер',
templateColumns: 'Колонки',
templateRows: 'Строки',
templateGrid: 'Сетка',
templatePriority: 'Приоритет',
zoneTag: index => `зона ${index}`,
mergeZones: count => `Слить ${count} ${RU_NOUN(count, 'зона', 'зоны', 'зон')}`,
customZoneName: count => `Своя ${count}-зонная`,
layoutNamePlaceholder: fallback => `Название раскладки (${fallback})`,
saveApply: 'Сохранить и применить',
notExpressible: 'эта расстановка зацеплена (pinwheel) — пока не выразима как вложенные разделения',
zoneCount: count => `${count} ${RU_NOUN(count, 'зона', 'зоны', 'зон')}`,
tabCount: count => `${count} ${RU_NOUN(count, 'вкладка', 'вкладки', 'вкладок')}`
},
contextMenu: {
link: {
openInApp: 'Открыть во встроенном браузере',
openExternal: 'Открыть во внешнем браузере',
copyUrl: 'Копировать URL',
copyResolvedUrl: 'Копировать разрешённый URL'
},
image: {
copyImage: 'Копировать изображение',
copyImageAddress: 'Копировать адрес изображения',
saveImageAs: 'Сохранить изображение как…'
},
edit: {
cut: 'Вырезать',
paste: 'Вставить',
selectAll: 'Выделить всё',
addToDictionary: 'Добавить в словарь'
},
page: {
copyPageUrl: 'Копировать URL страницы',
inspectElement: 'Исследовать элемент'
}
},
assistant: {
thread: {
loadingSession: 'Загрузка сеанса',
showEarlier: 'Показать ранние сообщения',
loadingResponse: 'Hermes загружает ответ',
resumeWhenBackgroundDone: count =>
count === 1
? 'Продолжится, когда фоновая задача завершится'
: `Продолжится, когда ${count} ${RU_NOUN(count, 'фоновая задача', 'фоновые задачи', 'фоновых задач')} завершатся`,
thinking: 'Думает',
thought: 'Помыслил',
thoughtBriefly: 'Кратко подумал',
thoughtFor: duration => `Думал ${duration}`,
turnDuration: duration => `Этот ход занял ${duration}`,
today: time => `Сегодня, ${time}`,
yesterday: time => `Вчера, ${time}`,
copy: 'Копировать',
refresh: 'Обновить',
moreActions: 'Ещё действия',
branchNewChat: 'Ветка в новый чат',
react: 'Реакция',
dismissError: 'Скрыть ошибку',
filesChanged: count => `${count} ${RU_PLURAL(count, 'файл изменён', 'файла изменено', 'файлов изменено')}`,
reviewChanges: 'Проверить',
readAloudFailed: 'Не удалось зачитать вслух',
preparingAudio: 'Подготовка аудио...',
stopReading: 'Остановить чтение',
readAloud: 'Зачитать вслух',
editMessage: 'Изменить сообщение',
expandMessage: 'Развернуть сообщение',
scrollToBottom: 'Прокрутить вниз',
stop: 'Стоп',
restorePrevious: 'Восстановить предыдущий чекпоинт',
restoreCheckpoint: 'Восстановить чекпоинт',
restoreFromHere: 'Восстановить чекпоинт — запустить снова с этого промпта',
restoreTitle: 'Восстановить до этого чекпоинта?',
restoreBody: 'Всё, что было после этого промпта, будет удалено из разговора, и промпт запустится снова отсюда.',
restoreConfirm: 'Восстановить и запустить',
restoreNext: 'Восстановить следующий чекпоинт',
goForward: 'Двигаться вперёд',
sendEdited: 'Отправить изменённое сообщение',
attachingFile: 'Прикрепление…'
},
approval: {
gatewayDisconnected: 'Шлюз Hermes не подключён',
sendFailed: 'Не удалось отправить ответ на подтверждение',
run: 'Выполнить',
command: 'Команда',
moreOptions: 'Больше опций подтверждения',
allowSession: 'Разрешить в этом сеансе',
alwaysAllowMenu: 'Всегда разрешать…',
jumpToApproval: 'Нужно подтверждение',
reject: 'Отклонить',
alwaysTitle: 'Всегда разрешать эту команду?',
alwaysDescription: pattern =>
`Это добавит паттерн «${pattern}» в ваш постоянный список разрешений (~/.hermes/config.yaml). Hermes больше не будет спрашивать о подобных командах — ни в этом сеансе, ни в будущем.`,
alwaysAllow: 'Всегда разрешать'
},
clarify: {
notReady: 'Запрос уточнения ещё не готов',
gatewayDisconnected: 'Шлюз Hermes не подключён',
sendFailed: 'Не удалось отправить ответ на уточнение',
loadingQuestion: 'Загрузка вопроса…',
other: 'Другое (введите ответ)',
placeholder: 'Введите ваш ответ…',
skip: 'Пропустить',
skipped: 'Пропущено',
continueLabel: 'Продолжить',
confirmAndContinueLabel: 'Подтвердить и продолжить',
answeredBadge: 'Ответ дан',
questionProgress: (answered, total) => `Ответ дан на ${answered} из ${total}`,
lateAnswer: (question, choice) => `Re: «${question}» — мой ответ: ${choice}`,
lateAnswerTip: 'Составить этот ответ как продолжение',
lateAnswerHint: 'Этот промпт больше не ждёт. Выберите вариант, чтобы составить его как сообщение-продолжение.'
},
mcpSetup: {
installTitle: server => `Добавить MCP-сервер ${server}?`,
enableTitle: server => `Включить MCP-сервер ${server}?`,
authorizeTitle: server => `Авторизовать MCP-сервер ${server}?`,
installAction: 'Установить',
enableAction: 'Включить',
authorizeAction: 'Авторизовать',
decline: 'Не сейчас',
declined: 'Отклонено',
installed: server => `${server} установлен`,
enabled: server => `${server} включён`,
authorized: server => `${server} авторизован`,
failed: server => `Настройка не удалась для ${server}`,
unanswered: 'Нет ответа',
toolCount: count => `${count} ${RU_NOUN(count, 'инструмент', 'инструмента', 'инструментов')}`,
notInCatalog: server => `«${server}» нет в MCP-каталоге`,
catalogSource: 'Из каталога, одобренного Nous',
envRequired: 'Сначала заполните обязательные учётные данные',
sendFailed: 'Не удалось отправить ответ на настройку MCP',
reloadFailed: 'Сервер сохранён, но перезагрузка MCP-инструментов не удалась — они загрузятся в следующем сеансе',
gatewayDisconnected: 'Шлюз Hermes не подключён'
},
tool: {
copyCode: 'Копировать код',
renderingImage: 'Рендеринг изображения',
copyOutput: 'Копировать вывод',
copyCommand: 'Копировать команду',
copyContent: 'Копировать содержимое',
copyUrl: 'Копировать URL',
copyResults: 'Копировать результаты',
copyQuery: 'Копировать запрос',
copyFile: 'Копировать файл',
copyPath: 'Копировать путь',
outputAlt: 'Вывод инструмента',
rawResponse: 'Сырой ответ',
copyActivity: 'Копировать активность',
recoveredOne: 'Восстановлено после 1 неудачного шага',
recoveredMany: count =>
`Восстановлено после ${count} ${RU_NOUN(count, 'неудачного шага', 'неудачных шагов', 'неудачных шагов')}`,
failedOne: '1 шаг не удался',
failedMany: count => `${count} ${RU_NOUN(count, 'шаг не удался', 'шага не удалось', 'шагов не удалось')}`,
statusRunning: 'Выполняется',
statusError: 'Ошибка',
statusRecovered: 'Восстановлено',
statusDone: 'Готово',
memoryWriteNoted: 'Запись в память отмечена',
actions: {
read: 'Чтение',
reading: 'Читает',
opened: 'Открыто',
opening: 'Открывает',
failedToOpen: 'Не удалось открыть',
searched: 'Поиск выполнен',
searching: 'Ищет',
ran: 'Выполнено',
running: 'Выполняется',
ranCode: 'Код выполнен',
runningCode: 'Скриптинг'
},
prefixes: {
browser: 'Браузер',
web: 'Web'
},
titleTemplates: {
actionCommand: (action, command) => `${action} ${command}`,
actionQuoted: (action, value) => `${action} «${value}»`,
actionTarget: (action, target) => `${action} ${target}`,
prefixedDone: (prefix, action) => `${prefix} ${action}`,
runningPrefixedTool: (prefix, action) => `Выполняется: ${prefix.toLowerCase()} ${action.toLowerCase()}`,
runningTool: action => `Выполняется: ${action.toLowerCase()}`
},
titles: {
browser_click: {
done: 'Нажат элемент страницы',
pending: 'Нажимаю элемент страницы',
pendingAction: 'Нажимаю'
},
browser_fill: { done: 'Заполнено поле формы', pending: 'Заполняю поле формы', pendingAction: 'Заполняю' },
browser_navigate: { done: 'Открыта страница', pending: 'Открываю страницу', pendingAction: 'Открываю' },
browser_snapshot: {
done: 'Снят снимок страницы',
pending: 'Снимаю страницу',
pendingAction: 'Снимаю'
},
browser_take_screenshot: {
done: 'Снят скриншот',
pending: 'Снимаю скриншот',
pendingAction: 'Снимаю'
},
browser_type: { done: 'Введено на странице', pending: 'Ввожу на странице', pendingAction: 'Ввожу' },
clarify: { done: 'Задан вопрос', pending: 'Задаю вопрос', pendingAction: 'Задаю' },
cronjob: { done: 'Cron-задача', pending: 'Планирую cron-задачу', pendingAction: 'Планирую' },
edit_file: { done: 'Файл изменён', pending: 'Изменяю файл', pendingAction: 'Изменяю' },
execute_code: { done: 'Код выполнен', pending: 'Скриптинг', pendingAction: 'Скриптинг' },
image_generate: {
done: 'Изображение сгенерировано',
pending: 'Генерирую изображение',
pendingAction: 'Генерирую'
},
list_files: { done: 'Файлы перечислены', pending: 'Перечисляю файлы', pendingAction: 'Перечисляю' },
memory: { done: 'Сохранено в память', pending: 'Сохраняю в память', pendingAction: 'Сохраняю' },
patch: { done: 'Файл патчен', pending: 'Патчу файл', pendingAction: 'Патчу' },
read_file: { done: 'Файл прочитан', pending: 'Читаю файл', pendingAction: 'Читаю' },
search_files: { done: 'Файлы найдены', pending: 'Ищу файлы', pendingAction: 'Ищу' },
session_search_recall: {
done: 'История сеансов найдена',
pending: 'Ищу в истории сеансов',
pendingAction: 'Ищу'
},
terminal: { done: 'Команда выполнена', pending: 'Выполняю команду', pendingAction: 'Выполняю' },
todo: { done: 'Todo обновлены', pending: 'Обновляю todo', pendingAction: 'Обновляю' },
vision_analyze: {
done: 'Изображение проанализировано',
pending: 'Анализирую изображение',
pendingAction: 'Анализирую'
},
web_extract: { done: 'Веб-страница прочитана', pending: 'Читаю веб-страницу', pendingAction: 'Читаю' },
web_search: { done: 'Поиск в вебе выполнен', pending: 'Ищу в вебе', pendingAction: 'Ищу' },
write_file: { done: 'Файл изменён', pending: 'Изменяю файл', pendingAction: 'Изменяю' }
}
}
},
prompts: {
gatewayDisconnected: 'Шлюз Hermes не подключён',
sudoSendFailed: 'Не удалось отправить пароль sudo',
secretSendFailed: 'Не удалось отправить секрет',
sudoTitle: 'Пароль администратора',
sudoDesc:
'Hermes нужен ваш пароль sudo, чтобы выполнить команду с повышенными правами. Он отправляется только вашему локальному агенту.',
sudoPlaceholder: 'пароль sudo',
secretTitle: 'Требуется секрет',
secretDesc: 'Hermes нужны учётные данные, чтобы продолжить.',
secretPlaceholder: 'значение секрета'
},
desktop: {
audioReadFailed: 'Не удалось прочитать записанное аудио',
sessionUnavailable: 'Сеанс недоступен',
createSessionFailed: 'Не удалось создать новый сеанс',
promptFailed: 'Промпт не удался',
providerCredentialRequired: 'Добавьте учётные данные провайдера перед отправкой первого сообщения.',
emptySlashCommand: 'пустая слэш-команда',
desktopCommands: 'Команды desktop',
skillCommandsAvailable: count =>
`${count} ${RU_NOUN(count, 'команда навыка', 'команды навыка', 'команд навыка')} доступно.`,
warningLine: message => `предупреждение: ${message}`,
yoloArmed: 'YOLO включён для этого чата',
yoloOff: 'YOLO выключен',
yoloSystem: active => `YOLO ${active ? 'включён' : 'выключен'} для этого сеанса`,
yoloTitle: 'YOLO',
yoloToggleFailed: 'Не удалось переключить YOLO',
profileStatus: current =>
`Профиль: ${current}. Используйте /profile <name> или выборку «Новый сеанс», чтобы начать чат в другом профиле.`,
unknownProfile: 'Неизвестный профиль',
noProfileNamed: (target, available) => `Нет профиля «${target}». Доступны: ${available}`,
newChatsProfile: name => `Новые чаты будут использовать профиль ${name}.`,
setProfileFailed: 'Не удалось установить профиль',
sttDisabled: 'Распознавание речи отключено в настройках.',
stopFailed: 'Остановка не удалась',
regenerateFailed: 'Повторная генерация не удалась',
editFailed: 'Изменение не удалось',
editTurnUnavailable: 'Этого хода больше нет в истории сервера (возможно, он был сжат).',
resumeFailed: 'Возобновление не удалось',
resumeStrandedTitle: 'Не удалось загрузить этот сеанс',
resumeStrandedBody:
'Соединение с этим сеансом оборвалось, и автоматические повторные попытки исчерпаны. Проверьте, что шлюз работает, и попробуйте снова.',
resumeRetry: 'Повторить',
nothingToBranch: 'Нечего ветвить',
branchNeedsChat: 'Начните или возобновите чат перед ветвлением.',
sessionBusy: 'Сеанс занят',
branchStopCurrent: 'Остановите текущий ход перед ветвлением этого чата.',
branchNoText: 'В этом сообщении нет текста, от которого можно ответвить.',
branchTitle: n => `Черновик: Ветка #${n}`,
branchFailed: 'Ветвление не удалось',
deleteFailed: 'Удаление не удалось',
archived: 'В архиве',
archiveFailed: 'Архивирование не удалось',
cwdChangeFailed: 'Изменение рабочего каталога не удалось',
cwdStagedTitle: 'Рабочий каталог поставлен в очередь',
cwdStagedMessage: 'Перезапустите бэкенд desktop, чтобы применить изменения cwd к этому активному сеансу.',
modelSwitchFailed: 'Смена модели не удалась',
sessionExported: 'Сеанс экспортирован',
sessionExportFailed: 'Не удалось экспортировать сеанс',
imageSaved: 'Изображение сохранено',
downloadStarted: 'Загрузка началась',
restartToUseSaveImage: 'Перезапустите Hermes Desktop, чтобы использовать «Сохранить изображение».',
restartToSaveImages: 'Перезапустите Hermes Desktop, чтобы сохранять изображения',
imageDownloadFailed: 'Загрузка изображения не удалась',
openImage: 'Открыть изображение',
downloadImage: 'Скачать изображение',
savingImage: 'Сохранение изображения',
imagePreviewFailed: 'Предпросмотр изображения не удался',
imageAttach: 'Прикрепление изображения',
imageWriteFailed: 'Не удалось записать изображение на диск.',
imageAttachFailed: 'Прикрепление изображения не удалось',
attachImages: 'Прикрепить изображения',
clipboard: 'Буфер обмена',
noClipboardImage: 'Изображение в буфере обмена не найдено',
clipboardPasteFailed: 'Вставка из буфера обмена не удалась',
dropFiles: 'Перетащите файлы',
handoff: {
pickPlatform: 'Выберите назначение',
success: platform => `Передаём в ${platform}. Возобновите здесь в любой момент.`,
systemNote: platform => `↻ Передано в ${platform} — возобновите здесь в любой момент.`,
failed: error => `Передача не удалась: ${error}`,
timedOut: 'Превышено время ожидания шлюза. Выполняется ли `hermes gateway`?'
}
},
errors: {
genericFailure: 'Что-то пошло не так',
boundaryTitle: 'Что-то сломалось в интерфейсе',
boundaryDesc: 'Вид столкнулся с неожиданной ошибкой. Ваши чаты и настройки в безопасности.',
reloadWindow: 'Перезагрузить окно',
openLogs: 'Открыть журналы'
},
ui: {
search: {
clear: 'Очистить поиск'
},
pagination: {
label: 'пагинация',
previous: 'Назад',
previousAria: 'Перейти на предыдущую страницу',
next: 'Дальше',
nextAria: 'Перейти на следующую страницу'
},
sidebar: {
title: 'Боковая панель',
description: 'Показывает мобильную боковую панель.',
toggle: open => `${open ? 'Показать' : 'Скрыть'} боковую панель`
}
}
})