diff --git a/apps/desktop/src/i18n/catalog.ts b/apps/desktop/src/i18n/catalog.ts index 19556cb45c49..0e6a78dc2e49 100644 --- a/apps/desktop/src/i18n/catalog.ts +++ b/apps/desktop/src/i18n/catalog.ts @@ -3,10 +3,12 @@ import { ja } from './ja' import type { Locale, Translations } from './types' import { zh } from './zh' import { zhHant } from './zh-hant' +import { ru } from './ru' export const TRANSLATIONS: Record = { en, zh, 'zh-hant': zhHant, - ja + ja, + ru } diff --git a/apps/desktop/src/i18n/languages.ts b/apps/desktop/src/i18n/languages.ts index 5b4990f49706..04fd1f5e3ed0 100644 --- a/apps/desktop/src/i18n/languages.ts +++ b/apps/desktop/src/i18n/languages.ts @@ -26,6 +26,12 @@ export const LOCALE_OPTIONS = [ name: '日本語', englishName: 'Japanese', configValue: 'ja' + }, + { + id: 'ru', + name: 'Русский', + englishName: 'Russian', + configValue: 'ru' } ] as const satisfies readonly { configValue: string; englishName: string; id: Locale; name: string }[] @@ -62,7 +68,10 @@ const LOCALE_ALIASES: Record = { zh_hant_hk: 'zh-hant', ja: 'ja', 'ja-jp': 'ja', - ja_jp: 'ja' + ja_jp: 'ja', + ru: 'ru', + 'ru-ru': 'ru', + ru_ru: 'ru' } export function isLocale(value: unknown): value is Locale { diff --git a/apps/desktop/src/i18n/ru.ts b/apps/desktop/src/i18n/ru.ts new file mode 100644 index 000000000000..c4ffcb2ca57b --- /dev/null +++ b/apps/desktop/src/i18n/ru.ts @@ -0,0 +1,1757 @@ +import { FIELD_DESCRIPTIONS, FIELD_LABELS } from '@/app/settings/constants' + +import type { Translations } from './types' + +export const ru: Translations = { + common: { + apply: 'Применить', + back: 'Назад', + save: 'Сохранить', + saving: 'Сохранение…', + cancel: 'Отмена', + change: 'Изменить', + choose: 'Выбрать', + clear: 'Очистить', + close: 'Закрыть', + collapse: 'Свернуть', + confirm: 'Подтвердить', + connect: 'Подключить', + connecting: 'Подключение', + continue: 'Продолжить', + copied: 'Скопировано', + copy: 'Копировать', + copyFailed: 'Не удалось скопировать', + delete: 'Удалить', + docs: 'Документация', + done: 'Готово', + error: 'Ошибка', + failed: 'Не удалось', + free: 'Бесплатно', + loading: 'Загрузка…', + notSet: 'Не задано', + refresh: 'Обновить', + remove: 'Удалить', + replace: 'Заменить', + retry: 'Повторить', + run: 'Запустить', + send: 'Отправить', + set: 'Задать', + skip: 'Пропустить', + update: 'Обновить', + on: 'Вкл', + off: 'Выкл' + }, + + boot: { + ready: 'Hermes Desktop готов к работе', + desktopBootFailedWithMessage: message => `Не удалось запустить рабочий стол: ${message}`, + steps: { + connectingGateway: 'Подключение к шлюзу рабочего стола', + loadingSettings: 'Загрузка настроек Hermes', + loadingSessions: 'Загрузка недавних сессий', + startingDesktopConnection: 'Запуск подключения к рабочему столу', + startingHermesDesktop: 'Запуск Hermes Desktop…' + }, + errors: { + backgroundExited: 'Фоновый процесс Hermes завершился.', + backgroundExitedDuringStartup: 'Фоновый процесс Hermes завершился во время запуска.', + backendStopped: 'Бэкенд остановлен', + desktopBootFailed: 'Не удалось запустить рабочий стол', + gatewaySignInRequired: 'Требуется вход в шлюз', + ipcBridgeUnavailable: 'Мост IPC рабочего стола недоступен.' + }, + failure: { + title: 'Hermes не удалось запустить', + description: + 'Фоновый шлюз не запустился. Попробуйте один из способов восстановления ниже. Ничего из этого не удалит ваши чаты или настройки.', + remoteTitle: 'Требуется вход в удалённый шлюз', + remoteDescription: + 'Сессия удалённого шлюза истекла. Войдите снова, чтобы переподключиться. Ничего из этого не удалит ваши чаты или настройки.', + retry: 'Повторить', + repairInstall: 'Восстановить установку', + useLocalGateway: 'Использовать локальный шлюз', + openLogs: 'Открыть логи', + repairHint: 'Восстановление перезапускает установщик и может занять несколько минут на новой машине.', + remoteSignInHint: 'Открывает окно входа в шлюз. Используйте локальный шлюз, чтобы переключиться на встроенный бэкенд.', + hideRecentLogs: 'Скрыть последние логи', + showRecentLogs: 'Показать последние логи', + signedInTitle: 'Вход выполнен', + signedInMessage: 'Переподключение к удалённому шлюзу…', + signInIncompleteTitle: 'Вход не завершён', + signInIncompleteMessage: 'Окно входа закрылось до завершения аутентификации.', + signInFailed: 'Не удалось войти', + signInToRemoteGateway: 'Войти в удалённый шлюз', + signInWithProvider: provider => `Войти через ${provider}`, + identityProvider: 'ваш провайдер идентификации' + } + }, + + notifications: { + region: 'Уведомления', + hide: 'Скрыть', + show: 'Показать', + more: count => `ещё ${count} ${count === 1 ? 'уведомление' : count < 5 ? 'уведомления' : 'уведомлений'}`, + clearAll: 'Очистить все', + dismiss: 'Закрыть уведомление', + details: 'Подробности', + copyDetail: 'Копировать подробность', + copyDetailFailed: 'Не удалось скопировать подробность уведомления', + backendOutOfDateTitle: 'Бэкенд устарел', + backendOutOfDateMessage: + 'Ваш бэкенд Hermes старше этой сборки рабочего стола и может работать некорректно. Обновите для согласования версий.', + updateHermes: 'Обновить Hermes', + updateReadyTitle: 'Обновление готово', + updateReadyMessage: count => `Доступно ${count} изменен${count === 1 ? 'ие' : count < 5 ? 'ия' : 'ий'}.`, + seeWhatsNew: 'Что нового', + errors: { + elevenLabsNeedsKey: 'Для ElevenLabs STT нужен ELEVENLABS_API_KEY.', + elevenLabsRejectedKey: 'ElevenLabs отклонил API-ключ (401).', + methodNotAllowed: + 'Бэкенд рабочего стола отклонил этот запрос (405 Method Not Allowed). Попробуйте перезапустить Hermes Desktop.', + microphonePermission: 'Доступ к микрофону запрещён.', + openaiRejectedApiKey: 'OpenAI отклонил API-ключ.', + openaiRejectedApiKeyWithStatus: status => `OpenAI отклонил API-ключ (${status} invalid_api_key).`, + openaiTtsNeedsKey: 'Для OpenAI TTS нужен VOICE_TOOLS_OPENAI_KEY или OPENAI_API_KEY.' + }, + voice: { + configureSpeechToText: 'Настройте распознавание речи для использования голосового режима.', + couldNotStartSession: 'Не удалось начать голосовую сессию', + microphoneAccessDenied: 'Доступ к микрофону запрещён.', + microphoneConstraintsUnsupported: 'Ограничения микрофона не поддерживаются этим устройством.', + microphoneFailed: 'Ошибка микрофона', + microphoneInUse: 'Микрофон уже используется другим приложением.', + microphonePermissionDenied: 'Доступ к микрофону запрещён.', + microphoneStartFailed: 'Не удалось начать запись с микрофона.', + microphoneUnsupported: 'Эта среда выполнения не поддерживает запись с микрофона.', + noMicrophone: 'Микрофон не найден.', + noSpeechDetected: 'Речь не обнаружена', + playbackFailed: 'Не удалось воспроизвести голос', + recordingFailed: 'Не удалось записать голос', + transcriptionFailed: 'Не удалось распознать голос', + transcriptionUnavailable: 'Распознавание голоса пока недоступно.', + tryRecordingAgain: 'Попробуйте записать снова.', + unavailable: 'Голос недоступен' + } + }, + + titlebar: { + hideSidebar: 'Скрыть боковую панель', + showSidebar: 'Показать боковую панель', + search: 'Поиск', + searchTitle: 'Поиск сессий, представлений и действий', + swapSidebarSides: 'Поменять стороны боковой панели', + swapSidebarSidesTitle: 'Поменять местами панели сессий и файлового браузера', + hideRightSidebar: 'Скрыть правую боковую панель', + showRightSidebar: 'Показать правую боковую панель', + muteHaptics: 'Отключить тактильную обратную связь', + unmuteHaptics: 'Включить тактильную обратную связь', + openSettings: 'Открыть настройки', + openKeybinds: 'Горячие клавиши' + }, + + keybinds: { + title: 'Горячие клавиши', + subtitle: open => `Нажмите на сочетание, чтобы переназначить · ${open} открывает эту панель снова.`, + 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.next': 'Следующая сессия', + 'session.prev': 'Предыдущая сессия', + 'session.focusSearch': 'Поиск по сессиям', + 'session.togglePin': 'Закрепить / открепить текущую сессию', + 'composer.focus': 'Фокус на компоновщик', + 'composer.modelPicker': 'Открыть выбор модели', + 'view.toggleSidebar': 'Переключить боковую панель сессий', + 'view.toggleRightSidebar': 'Переключить файловый браузер', + 'view.showFiles': 'Показать файловый браузер', + 'view.showTerminal': 'Показать терминал', + 'view.terminalSelection': 'Отправить выделение терминала в компоновщик', + 'view.closePreviewTab': 'Закрыть вкладку предпросмотра', + 'view.flipPanes': 'Поменять местами стороны панели', + '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.sendQueued': 'Отправить следующий из очереди ход', + 'composer.mention': 'Ссылка на файлы, папки, URL', + 'composer.slash': 'Палитра слэш-команд', + 'composer.help': 'Быстрая справка', + 'composer.history': 'Переключить всплывающее окно / историю', + 'composer.cancel': 'Закрыть всплывающее окно · отменить выполнение' + } + }, + + language: { + label: 'Язык', + description: 'Выберите язык интерфейса рабочего стола.', + saving: 'Сохранение языка…', + saveError: 'Не удалось обновить язык', + switchTo: 'Переключить язык', + searchPlaceholder: 'Поиск языков…', + noResults: 'Языки не найдены' + }, + + settings: { + closeSettings: 'Закрыть настройки', + exportConfig: 'Экспорт конфигурации', + importConfig: 'Импорт конфигурации', + resetToDefaults: 'Сбросить к значениям по умолчанию', + resetConfirm: 'Сбросить все настройки к значениям Hermes по умолчанию?', + exportFailed: 'Не удалось экспортировать', + resetFailed: 'Не удалось сбросить', + nav: { + providers: 'Провайдеры', + providerAccounts: 'Аккаунты', + providerApiKeys: 'API-ключи', + gateway: 'Шлюз', + apiKeys: 'Инструменты и ключи', + keysTools: 'Инструменты', + keysSettings: 'Настройки', + mcp: 'MCP', + archivedChats: 'Архивные чаты', + about: 'О программе' + }, + 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: 'Продукт скрывает необработанные данные инструментов; Технический показывает полные входные/выходные данные.', + product: 'Продукт', + productDesc: 'Удобочитаемая активность инструментов с краткими сводками.', + technical: 'Технический', + technicalDesc: 'Включает необработанные аргументы/результаты инструментов и низкоуровневые детали.', + themeTitle: 'Тема', + themeDesc: 'Только палитры рабочего стола. Выбранный режим применяется поверх.' + }, + fieldLabels: FIELD_LABELS, + fieldDescriptions: FIELD_DESCRIPTIONS, + about: { + heading: 'Hermes Desktop', + version: value => `Версия ${value}`, + versionUnavailable: 'Версия недоступна', + updates: 'Обновления', + checkNow: 'Проверить сейчас', + checking: 'Проверка…', + seeWhatsNew: 'Что нового', + releaseNotes: 'Примечания к выпуску', + onLatest: 'У вас последняя версия.', + installing: 'Обновление устанавливается.', + cantUpdate: 'Эта сборка не может обновиться изнутри приложения.', + cantReach: 'Не удалось связаться с сервером обновлений.', + tapCheck: 'Нажмите «Проверить сейчас», чтобы проверить наличие обновлений.', + updateReady: count => `Готово новое обновление (${count} изменен${count === 1 ? 'ие' : count < 5 ? 'ия' : 'ий'}).`, + lastChecked: age => `Последняя проверка: ${age}`, + justNowSuffix: ' · только что', + automaticUpdates: 'Автоматические обновления', + automaticUpdatesDesc: + 'Hermes автоматически проверяет обновления в фоновом режиме и уведомляет, когда обновление готово.', + branchCommit: (branch, commit) => `Ветка ${branch} · Коммит ${commit}`, + never: 'никогда', + justNow: 'только что', + minAgo: count => `${count} мин. назад`, + hoursAgo: count => `${count} ч. назад`, + daysAgo: count => `${count} дн. назад` + }, + config: { + none: 'Нет', + noneParen: '(нет)', + notSet: 'Не задано', + commaSeparated: 'значения, разделённые запятыми', + loading: 'Загрузка конфигурации Hermes…', + emptyTitle: 'Нечего настраивать', + emptyDesc: 'В этом разделе нет настраиваемых параметров.', + failedLoad: 'Не удалось загрузить настройки', + autosaveFailed: 'Не удалось автосохранить', + imported: 'Конфигурация импортирована', + invalidJson: 'Недопустимый JSON конфигурации' + }, + credentials: { + pasteKey: 'Вставить ключ', + pasteLabelKey: label => `Вставить ключ ${label}`, + optional: 'Необязательно', + enterValueFirst: 'Сначала введите значение.', + couldNotSave: 'Не удалось сохранить учётные данные.', + remove: 'Удалить', + or: 'или', + escToCancel: 'Esc для отмены', + getKey: 'Получить ключ', + saving: 'Сохранение' + }, + envActions: { + actionsFor: label => `Действия для ${label}`, + credentialActions: 'Действия с учётными данными', + docs: 'Документация', + hideValue: 'Скрыть значение', + revealValue: 'Показать значение', + replace: 'Заменить', + set: 'Задать', + clear: 'Очистить' + }, + gateway: { + loading: 'Загрузка настроек шлюза…', + unavailableTitle: 'Настройки шлюза недоступны', + unavailableDesc: 'Мост IPC рабочего стола не предоставляет настройки шлюза.', + title: 'Подключение шлюза', + envOverride: 'переопределение окружения', + intro: + 'По умолчанию Hermes Desktop запускает собственный локальный шлюз. Используйте удалённый шлюз, когда хотите управлять уже работающим бэкендом Hermes на другой машине или через доверенный прокси. Выберите профиль ниже, чтобы задать свой удалённый хост.', + appliesTo: 'Применяется к', + allProfiles: 'Все профили', + defaultConnection: 'Подключение по умолчанию для каждого профиля без собственного переопределения.', + profileConnection: profile => `Подключение используется только когда «${profile}» — активный профиль. Установите «Локальный», чтобы наследовать настройки по умолчанию.`, + envOverrideTitle: 'Переменные окружения управляют этим сеансом рабочего стола.', + envOverrideDesc: + 'Отмените HERMES_DESKTOP_REMOTE_URL и HERMES_DESKTOP_REMOTE_TOKEN, чтобы использовать сохранённую настройку ниже.', + localTitle: 'Локальный шлюз', + localDesc: 'Запуск приватного бэкенда Hermes на localhost. Это вариант по умолчанию и работает офлайн.', + remoteTitle: 'Удалённый шлюз', + remoteDesc: + 'Подключите эту оболочку рабочего стола к удалённому бэкенду Hermes. Размещённые шлюзы используют OAuth или имя пользователя и пароль; локальные могут использовать токен сессии.', + 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: 'Вставить токен сессии', + 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: 'Не удалось сохранить настройки шлюза' + }, + keys: { + loading: 'Загрузка API-ключей и учётных данных…', + failedLoad: 'Не удалось загрузить API-ключи', + empty: 'В этой категории пока ничего не настроено.' + }, + 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: 'Сохранить сервер' + }, + model: { + loading: 'Загрузка конфигурации модели…', + appliesDesc: 'Применяется к новым сессиям. Используйте выбор модели в компоновщике, чтобы переключить модель в активном чате.', + provider: 'Провайдер', + model: 'Модель', + applying: 'Применение…', + auxiliaryTitle: 'Вспомогательные модели', + resetAllToMain: 'Сбросить все на основную', + auxiliaryDesc: 'Вспомогательные задачи по умолчанию выполняются на основной модели. Назначьте отдельную модель для любой задачи, чтобы переопределить.', + setToMain: 'Установить как основную', + change: 'Изменить', + autoUseMain: 'авто · использовать основную модель', + providerDefault: '(по умолчанию провайдера)', + 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: 'Другие провайдеры', + noProviderKeys: 'Нет доступных API-ключей провайдеров.', + loading: 'Загрузка провайдеров…' + }, + sessions: { + loading: 'Загрузка архивных сессий…', + archivedTitle: 'Архивные сессии', + archivedIntro: + 'Архивные чаты скрыты из боковой панели, но сохраняют все сообщения. Нажмите Ctrl/⌘+клик по чату в боковой панели, чтобы архивировать его.', + emptyArchivedTitle: 'Архив пуст', + emptyArchivedDesc: 'Заархивируйте чат, чтобы скрыть его здесь.', + unarchive: 'Разархивировать', + deletePermanently: 'Удалить навсегда', + messages: count => `${count} сообщен${count === 1 ? 'ие' : count < 5 ? 'ия' : 'ий'}`, + restored: 'Восстановлено', + deleteConfirm: title => `Навсегда удалить «${title}»? Это нельзя отменить.`, + defaultDirTitle: 'Папка проекта по умолчанию', + defaultDirDesc: + 'Новые сессии начинаются в этой папке, если вы не выберете другую. Оставьте пустым, чтобы использовать домашнюю директорию.', + defaultDirUpdated: 'Папка проекта по умолчанию обновлена', + 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: 'Готов', + nousIncluded: 'Включено в подписку Nous — войдите в портал Nous для активации.', + noApiKeyRequired: 'API-ключ не требуется.', + postSetupHint: step => + `Этому бэкенду требуется однократная установка (${step}). Выполняется на этой машине — может занять несколько минут.`, + postSetupRun: 'Запустить установку', + postSetupRunning: 'Установка…', + postSetupStarting: 'Запуск…', + postSetupCompleteTitle: 'Установка завершена', + postSetupCompleteMessage: step => `${step} установлена.`, + postSetupErrorTitle: 'Установка завершилась с ошибками', + postSetupErrorMessage: step => `Проверьте лог ${step}.`, + postSetupFailed: step => `Не удалось запустить установку ${step}` + } + }, + + skills: { + tabSkills: 'Навыки', + tabToolsets: 'Наборы инструментов', + all: 'Все', + searchSkills: 'Поиск навыков...', + searchToolsets: 'Поиск наборов инструментов...', + refresh: 'Обновить навыки', + refreshing: 'Обновление навыков', + loading: 'Загрузка возможностей...', + noSkillsTitle: 'Навыки не найдены', + noSkillsDesc: 'Попробуйте более широкий поиск или другую категорию.', + noToolsetsTitle: 'Наборы инструментов не найдены', + noToolsetsDesc: 'Попробуйте более широкий поисковый запрос.', + noDescription: 'Нет описания.', + configured: 'Настроен', + needsKeys: 'Нужны ключи', + toolsetsEnabled: (enabled, total) => `${enabled}/${total} наборов инструментов включено`, + configureToolset: label => `Настроить ${label}`, + toggleToolset: label => `Переключить набор инструментов ${label}`, + skillsLoadFailed: 'Не удалось загрузить навыки', + toolsetsRefreshFailed: 'Не удалось обновить наборы инструментов', + skillEnabled: 'Навык включён', + skillDisabled: 'Навык выключен', + toolsetEnabled: 'Набор инструментов включён', + toolsetDisabled: 'Набор инструментов выключен', + appliesToNewSessions: name => `${name} применяется к новым сессиям.`, + failedToUpdate: name => `Не удалось обновить ${name}` + }, + + agents: { + close: 'Закрыть агентов', + title: 'Дерево запуска', + subtitle: 'Активность дочерних агентов в текущем ходе.', + emptyTitle: 'Нет активных дочерних агентов', + emptyDesc: 'Когда ход делегирует работу, дочерние агенты транслируют свой прогресс здесь.', + running: 'Выполняется', + failed: 'Ошибка', + done: 'Готово', + streaming: 'Потоковая передача', + files: 'Файлы', + moreFiles: count => `+${count} ещё файлов`, + delegation: index => `Делегирование ${index}`, + workers: count => `${count} рабочих`, + workersActive: count => `${count} активных`, + agentsCount: count => `${count} агент${count === 1 ? '' : count < 5 ? 'а' : 'ов'}`, + activeCount: count => `${count} активных`, + failedCount: count => `${count} с ошибкой`, + toolsCount: count => `${count} инструмент${count === 1 ? '' : count < 5 ? 'а' : 'ов'}`, + filesCount: count => `${count} файл${count === 1 ? '' : count < 5 ? 'а' : 'ов'}`, + updatedAgo: age => `обновлено ${age}`, + ageNow: 'сейчас', + ageSeconds: seconds => `${seconds} сек. назад`, + ageMinutes: minutes => `${minutes} мин. назад`, + ageHours: hours => `${hours} ч. назад`, + durationSeconds: seconds => `${seconds} сек.`, + durationMinutes: (minutes, seconds) => `${minutes} мин. ${seconds} сек.`, + tokensK: k => `${k} тыс. ток.`, + tokens: value => `${value} ток.` + }, + + commandCenter: { + close: 'Закрыть центр команд', + paletteTitle: 'Палитра команд', + back: 'Назад', + searchPlaceholder: 'Поиск сессий, представлений и действий', + goTo: 'Перейти к', + commandCenter: 'Центр команд', + appearance: 'Внешний вид', + settings: 'Настройки', + changeTheme: 'Сменить тему...', + changeColorMode: 'Сменить цветовой режим...', + settingsFields: 'Поля настроек', + mcpServers: 'MCP-серверы', + archivedChats: 'Архивные чаты', + sections: { sessions: 'Сессии', system: 'Система', usage: 'Использование' }, + sectionDescriptions: { + sessions: 'Поиск и управление сессиями', + system: 'Статус, логи и системные действия', + usage: 'Токены, стоимость и активность навыков за период' + }, + nav: { + newChat: { title: 'Новая сессия', detail: 'Начать новую сессию' }, + settings: { title: 'Настройки', detail: 'Настроить Hermes Desktop' }, + skills: { title: 'Навыки и инструменты', detail: 'Включить навыки, наборы инструментов и провайдеров' }, + 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}`, + restartMessaging: 'Перезапустить мессенджеры', + updateHermes: 'Обновить Hermes', + actionRunning: 'выполняется', + actionDone: 'готово', + actionFailed: 'ошибка', + actionStartedWaiting: 'Действие запушено, ожидание статуса...', + loadingStatus: 'Загрузка статуса...', + recentLogs: 'Последние логи', + noLogs: 'Логи пока не загружены.', + days: count => `${count} дн.`, + statSessions: 'Сессии', + statApiCalls: 'API-вызовы', + statTokens: 'Токены ввод/вывод', + statCost: 'Примерная стоимость', + actualCost: cost => `фактическая ${cost}`, + loadingUsage: 'Загрузка использования...', + noUsage: period => `Нет использования за последние ${period} дней.`, + retry: 'Повторить', + dailyTokens: 'Токены за день', + input: 'ввод', + output: 'вывод', + noDailyActivity: 'Нет ежедневной активности.', + topModels: 'Популярные модели', + noModelUsage: 'Использования моделей пока нет.', + topSkills: 'Популярные навыки', + noSkillActivity: 'Активности навыков пока нет.', + actions: count => `действий: ${count}` + }, + + 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}`, + 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: 'Рекомендуется. Discord user IDs через запятую.' + }, + DISCORD_REPLY_TO_MODE: { label: 'Стиль ответа', help: 'first, all или off.' }, + DISCORD_ALLOW_ALL_USERS: { + label: 'Разрешить всем пользователям Discord', + help: 'Только для разработки. Когда включено, любой может написать боту без белого списка.' + }, + DISCORD_HOME_CHANNEL: { + label: 'ID домашнего канала', + help: 'Канал, куда бот отправляет проактивные сообщения (выход cron, напоминания).' + }, + DISCORD_HOME_CHANNEL_NAME: { + label: 'Название домашнего канала', + help: 'Отображаемое название домашнего канала в логах и статусе.' + }, + BLUEBUBBLES_ALLOW_ALL_USERS: { + label: 'Разрешить всем пользователям iMessage', + help: 'Когда включено, пропустить белый список 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: 'Рекомендуется. Slack user IDs через запятую.' }, + MATTERMOST_URL: { label: 'URL сервера', placeholder: 'https://mattermost.example.com' }, + MATTERMOST_TOKEN: { label: 'Токен бота' }, + MATTERMOST_ALLOWED_USERS: { + label: 'Разрешённые ID пользователей', + help: 'Рекомендуется. Mattermost user IDs через запятую.' + }, + MATRIX_HOMESERVER: { label: 'URL сервера', placeholder: 'https://matrix.org' }, + MATRIX_ACCESS_TOKEN: { label: 'Токен доступа' }, + MATRIX_USER_ID: { label: 'ID пользователя бота', placeholder: '@hermes:example.org' }, + MATRIX_ALLOWED_USERS: { + label: 'Разрешённые ID пользователей Matrix', + help: 'Рекомендуется. User IDs в формате @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: 'Рекомендуется. Номера телефонов или WhatsApp IDs через запятую.' + } + }, + platformIntro: {} + }, + + profiles: { + close: 'Закрыть профили', + nameHint: 'Строчные буквы, цифры, дефисы и подчёркивания. Должно начинаться с буквы или цифры.', + title: 'Профили', + count: count => `${count} профил${count === 1 ? 'ь' : count < 5 ? 'я' : 'ей'}`, + loading: 'Загрузка профилей…', + newProfile: 'Новый профиль', + allProfiles: 'Все профили', + showAllProfiles: 'Показать все профили', + switchToProfile: name => `Переключиться на ${name}`, + manageProfiles: 'Управление профилями...', + actionsFor: name => `Действия для ${name}`, + color: 'Цвет...', + colorFor: name => `Цвет для ${name}`, + setColor: color => `Установить цвет ${color}`, + autoColor: 'Авто', + noProfiles: 'Профилей пока нет.', + selectPrompt: 'Выберите профиль для просмотра его данных.', + refresh: 'Обновить профили', + refreshing: 'Обновление профилей', + default: 'по умолчанию', + skills: count => `${count} навык${count === 1 ? '' : count < 5 ? 'а' : 'ов'}`, + env: 'окружение', + defaultBadge: 'По умолчанию', + rename: 'Переименовать', + 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: 'Имя', + cloneFromDefault: 'Клонировать из профиля по умолчанию', + cloneFromDefaultDesc: 'Скопировать конфигурацию, навыки и SOUL.md из профиля по умолчанию.', + invalidName: hint => `Недопустимое имя. ${hint}`, + nameRequired: 'Имя обязательно.', + creating: 'Создание…', + createAction: 'Создать профиль', + renameTitle: 'Переименовать профиль', + renameDescPrefix: 'Переименование обновляет директорию профиля и любые скрипты-обёртки в ', + renameDescSuffix: '.', + newNameLabel: 'Новое имя', + renaming: 'Переименование…', + created: 'Профиль создан', + renamed: 'Профиль переименован', + deleted: 'Профиль удалён', + setupCopied: 'Команда настройки скопирована', + soulSaved: 'SOUL.md сохранён', + failedLoad: 'Не удалось загрузить профили', + failedDelete: 'Не удалось удалить профиль', + failedCopy: 'Не удалось скопировать команду настройки', + failedLoadSoul: 'Не удалось загрузить SOUL.md', + failedSaveSoul: 'Не удалось сохранить SOUL.md', + failedCreate: 'Не удалось создать профиль', + failedRename: 'Не удалось переименовать профиль' + }, + + cron: { + close: 'Закрыть cron', + 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: 'История запусков', + actionsFor: title => `Действия для ${title}`, + 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 по email...', + frequencyLabel: 'Частота', + deliverLabel: 'Доставить в', + customScheduleLabel: 'Своё расписание', + customPlaceholder: '0 9 * * * или будни в 9:00', + customHint: 'Выражение cron или фразы вроде «каждый час» или «будни в 9:00».', + optional: 'Необязательно', + promptScheduleRequired: 'Промпт и расписание обязательны.', + saveChanges: 'Сохранить изменения', + createAction: 'Создать cron' + }, + + 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) => `Перейти на страницу ${itemLabel} ${page}`, + colTitleLink: 'Заголовок ссылки', + colTitleFile: 'Имя', + colTitleDefault: 'Заголовок / имя', + colLocationLink: 'URL', + colLocationFile: 'Путь', + colLocationDefault: 'Расположение', + colSession: 'Сессия', + kindImage: 'изображение', + kindFile: 'файл', + kindLink: 'ссылка', + chat: 'Чат', + copyUrl: 'Копировать URL', + copyPath: 'Копировать путь' + }, + + sidebar: { + nav: { + 'new-session': 'Новая сессия', + skills: 'Навыки и инструменты', + messaging: 'Мессенджеры', + artifacts: 'Артефакты' + }, + searchAria: 'Поиск сессий', + searchPlaceholder: 'Поиск сессий…', + clearSearch: 'Очистить поиск', + noMatch: query => `Нет сессий, соответствующих «${query}».`, + results: 'Результаты', + pinned: 'Закреплённые', + sessions: 'Сессии', + cronJobs: 'Задания cron', + groupAriaGrouped: 'Показать сессии единым списком', + groupAriaUngrouped: 'Группировать сессии по рабочей области', + groupTitleGrouped: 'Разгруппировать сессии', + groupTitleUngrouped: 'Группировать по рабочей области', + allPinned: 'Всё здесь закреплено. Открепите чат, чтобы он появился в недавних.', + shiftClickHint: 'Shift+клик по чату для закрепления', + noWorkspace: 'Нет рабочей области', + newSessionIn: label => `Новая сессия в ${label}`, + reorderWorkspace: label => `Изменить порядок рабочей области ${label}`, + showMoreIn: (count, label) => `Показать ещё ${count} в ${label}`, + loading: 'Загрузка…', + loadMore: 'Загрузить ещё', + loadCount: step => `Загрузить ещё ${step}`, + row: { + pin: 'Закрепить', + unpin: 'Открепить', + copyId: 'Копировать ID', + export: 'Экспорт', + rename: 'Переименовать', + archive: 'Архивировать', + copyIdFailed: 'Не удалось скопировать ID сессии', + actionsFor: title => `Действия для ${title}`, + sessionActions: 'Действия с сессией', + sessionRunning: 'Сессия выполняется', + needsInput: 'Ожидает вашего ввода', + waitingForAnswer: 'Ожидает вашего ответа', + renamed: 'Переименовано', + renameFailed: 'Не удалось переименовать', + renameTitle: 'Переименовать сессию', + renameDesc: 'Дайте этому чату запоминающее название. Оставьте пустым, чтобы очистить.', + untitledPlaceholder: 'Безымянная сессия', + ageNow: 'сейчас', + ageDay: 'дн.', + ageHour: 'ч.', + ageMin: 'мин.' + } + }, + + composer: { + message: 'Сообщение', + wakingProfile: profile => `Пробуждение ${profile}…`, + placeholderStarting: 'Запуск Hermes...', + placeholderReconnecting: 'Переподключение к Hermes…', + placeholderFollowUp: 'Отправить продолжение', + newSessionPlaceholders: [ + 'Что будем строить?', + 'Дай Hermes задачу', + 'О чём вы думаете?', + 'Опишите, что вам нужно', + 'С чего начнём?', + 'Спросите что угодно', + 'Начните с цели' + ], + followUpPlaceholders: [ + 'Отправить продолжение', + 'Добавить контекст', + 'Уточнить запрос', + 'Что дальше?', + 'Продолжить', + 'Продвинуть дальше', + 'Скорректировать или продолжить' + ], + startVoice: 'Начать голосовой разговор', + queueMessage: 'Поместить сообщение в очередь', + steer: 'Направить текущий ход (⌘⏎)', + stop: 'Остановить', + send: 'Отправить', + speaking: 'Говорю', + transcribing: 'Распознаю', + thinking: 'Думаю', + muted: 'Приглушён', + listening: 'Слушаю', + muteMic: 'Выключить микрофон', + unmuteMic: 'Включить микрофон', + stopListening: 'Прекратить прослушивание и отправить', + stopShort: 'Стоп', + endConversation: 'Завершить голосовой разговор', + endShort: 'Завершить', + stopDictation: 'Остановить диктовку', + transcribingDictation: 'Распознаю диктовку', + voiceDictation: 'Голосовая диктовка', + lookupLoading: 'Ищу…', + lookupNoMatches: 'Нет совпадений.', + lookupTry: 'Попробуйте', + lookupOr: 'или', + commonCommands: 'Частые команды', + hotkeys: 'Горячие клавиши', + helpFooter: 'открыть полную панель · backspace закрывает', + commandDescs: { + '/help': 'полный список команд и горячих клавиш', + '/clear': 'начать новую сессию', + '/resume': 'возобновить предыдущую сессию', + '/details': 'управлять уровнем детализации транскрипта', + '/copy': 'копировать выделение или последнее сообщение ассистента', + '/quit': 'выйти из hermes' + }, + hotkeyDescs: { + '@': 'ссылка на файлы, папки, URL, git', + '/': 'палитра слэш-команд', + '?': 'эта быстрая справка (удалить, чтобы закрыть)', + Enter: 'отправить · Shift+Enter для новой строки', + 'Cmd/Ctrl+Shift+K': 'отправить следующий из очереди ход', + 'Cmd/Ctrl+/': 'все горячие клавиши', + Esc: 'закрыть всплывающее окно · отменить выполнение', + '↑ / ↓': 'переключить всплывающее окно / историю' + }, + attachUrlTitle: 'Прикрепить URL', + attachUrlDesc: 'Hermes загрузит страницу и включит её как контекст для этого хода.', + urlPlaceholder: 'https://example.com/post', + urlHintPre: 'Укажите полный URL, например ', + attach: 'Прикрепить', + queued: count => `${count} в очереди`, + attachmentOnly: 'Ход только с вложением', + emptyTurn: 'Пустой ход', + attachments: count => `${count} вложен${count === 1 ? 'ие' : count < 5 ? 'ия' : 'ий'}`, + editingInComposer: 'Редактирование в компоновщике', + editingQueuedInComposer: 'Редактирование хода из очереди в компоновщике', + editQueued: 'Редактировать ход из очереди', + sendQueuedNext: 'Отправить ход из очереди следующим', + sendQueuedNow: 'Отправить ход из очереди сейчас', + deleteQueued: 'Удалить ход из очереди', + previewUnavailable: 'Предпросмотр недоступен', + previewLabel: label => `Предпросмотр ${label}`, + couldNotPreview: label => `Не удалось предпросмотреть ${label}`, + removeAttachment: label => `Удалить ${label}`, + dictating: 'Диктую', + preparingAudio: 'Подготовка аудио', + speakingResponse: 'Произношу ответ', + readingAloud: 'Читаю вслух', + themeSuggestions: 'Предложения тем рабочего стола', + noMatchingThemes: 'Нет подходящих тем.', + themeTryPre: 'Попробуйте ', + themeTryPost: '.', + attachLabel: 'Прикрепить', + files: 'Файлы…', + folder: 'Папка…', + images: 'Изображения…', + pasteImage: 'Вставить изображение', + url: 'URL…', + promptSnippets: 'Сниппеты промптов…', + tipPre: 'Совет: введите ', + tipPost: ' для ссылки на файлы прямо в тексте.', + snippetsTitle: 'Сниппеты промптов', + snippetsDesc: 'Выберите начальный промпт, чтобы вставить в компоновщик.', + dropFiles: 'Перетащите файлы для прикрепления', + dropSession: 'Перетащите, чтобы связать этот чат', + snippets: { + codeReview: { + label: 'Обзор кода', + description: 'Проверить текущие изменения на наличие регрессий, пропущенных граничных случаев и отсутствующих тестов.', + text: 'Пожалуйста, проверьте это на наличие багов, регрессий и отсутствующих тестов.' + }, + implementationPlan: { + label: 'План реализации', + description: 'Набросать подход перед изменением кода, чтобы diff оставался сфокусированным.', + text: 'Пожалуйста, составьте краткий план реализации перед изменением кода.' + }, + explainThis: { + label: 'Объясни это', + description: 'Разобрать, как работает выбранный код, и указать на ключевые файлы.', + text: 'Пожалуйста, объясните, как это работает, и укажите на ключевые файлы.' + } + } + }, + + updates: { + stages: { + idle: 'Подготовка…', + prepare: 'Подготовка…', + fetch: 'Загрузка…', + pull: 'Почти готово…', + pydeps: 'Завершение…', + restart: 'Перезапуск Hermes…', + manual: 'Обновление из терминала', + error: 'Обновление приостановлено' + }, + checking: 'Проверка обновлений…', + checkFailedTitle: 'Не удалось проверить обновления', + tryAgain: 'Попробовать снова', + notAvailableTitle: 'Обновление недоступно', + unsupportedMessage: 'Эта версия Hermes не может обновиться изнутри приложения.', + connectionRetry: 'Проверьте подключение и попробуйте снова.', + latestBody: 'У вас последняя версия.', + allSetTitle: 'Всё готово', + availableTitle: 'Доступно новое обновление', + availableBody: 'Новая версия Hermes готова к установке.', + updateNow: 'Обновить сейчас', + maybeLater: 'Может быть позже', + moreChanges: count => `+ ещё ${count} изменен${count === 1 ? 'ие' : count < 5 ? 'ия' : 'ий'}.`, + manualTitle: 'Обновление из терминала', + manualBody: 'Вы установили Hermes из командной строки, поэтому обновления тоже запускаются оттуда. Вставьте это в терминал:', + manualPickedUp: 'Hermes подхватит новую версию при следующем запуске.', + copy: 'Копировать', + copied: 'Скопировано', + done: 'Готово', + applyingBody: 'Обновление Hermes возьмёт управление в собственное окно и перезапустит Hermes, когда завершится.', + applyingClose: 'Hermes закроется для применения обновления.', + errorTitle: 'Обновление не завершилось', + errorBody: 'Ничего страшного — ничего не потеряно. Вы можете попробовать снова сейчас.', + notNow: 'Не сейчас' + }, + + install: { + stageStates: { + pending: 'Ожидание', + running: 'Установка', + succeeded: 'Готово', + skipped: 'Пропущено', + failed: 'Ошибка' + }, + oneTimeTitle: 'Hermes требует однократную установку', + unsupportedDesc: platform => + `Автоматическая установка при первом запуске пока недоступна на ${platform}. Откройте терминал и выполните команду ниже, затем перезапустите приложение. Последующие запуски пропустят этот шаг.`, + installCommand: 'Команда установки', + copyCommand: 'Копировать команду', + viewDocs: 'Открыть документацию по установке', + installTo: 'Установка будет выполнена в', + retryAfterRun: 'Я выполнил команду — повторить', + failedTitle: 'Установка не удалась', + settingUpTitle: 'Настройка Hermes Agent', + finishingTitle: 'Завершение настройки', + failedDesc: + 'Один из шагов установки завершился с ошибкой. На Windows это может произойти, если запущен другой экземпляр Hermes CLI или рабочего стола. Остановите все запущенные экземпляры Hermes и повторите. Проверьте подробности ниже или лог рабочего стола для полной транскрипции.', + activeDesc: + 'Это однократная настройка. Установщик Hermes загружает зависимости и настраивает вашу машину. Последующие запуски пропустят этот шаг.', + progress: (completed, total) => `${completed} из ${total} шагов завершено`, + currentStage: stage => ` — сейчас: ${stage}`, + fetchingManifest: 'Загрузка манифеста установщика...', + error: 'Ошибка', + hideOutput: 'Скрыть вывод установщика', + showOutput: 'Показать вывод установщика', + lines: count => `${count} строк${count === 1 ? 'а' : count < 5 ? 'и' : ''}`, + 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', + openRouterPitch: 'Один ключ, сотни моделей — отличный вариант по умолчанию', + apiKeyOptions: { + openrouter: { + short: 'один ключ, много моделей', + description: 'Размещает сотни моделей за одним ключом. Хороший вариант по умолчанию для новых установок.' + }, + openai: { short: 'модели класса GPT', description: 'Прямой доступ к моделям OpenAI.' }, + gemini: { short: 'модели Gemini', description: 'Прямой доступ к моделям Google Gemini.' }, + xai: { short: 'модели Grok', description: 'Прямой доступ к моделям xAI Grok.' }, + local: { + short: 'свой сервер', + description: 'Направьте Hermes на локальный или самостоятельно размещённый сервер, совместимый с OpenAI (vLLM, llama.cpp, Ollama и др.).' + } + }, + backToSignIn: 'Назад к входу', + getKey: 'Получить ключ', + replaceCurrent: 'Заменить текущее значение', + pasteApiKey: 'Вставить API-ключ', + couldNotSave: 'Не удалось сохранить учётные данные.', + connecting: 'Подключение', + update: 'Обновить', + flowSubtitles: { + pkce: 'Откроет ваш браузер для входа, затем вернётся сюда', + device_code: 'Откроет страницу верификации в браузере — Hermes подключится автоматически', + loopback: 'Откроет ваш браузер для входа — 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: 'Бесплатно', + price: (input, output) => `${input} ввод / ${output} вывод за млн токенов`, + change: 'Изменить', + startChatting: 'Начать', + docs: provider => `Документация ${provider}` + }, + + modelPicker: { + title: 'Переключить модель', + current: 'текущая:', + unknown: '(неизвестно)', + search: 'Фильтр провайдеров и моделей...', + noModels: 'Модели не найдены.', + persistGlobalSession: 'Сохранить глобально (иначе только для этой сессии)', + persistGlobal: 'Сохранить глобально', + addProvider: 'Добавить провайдера', + loadFailed: 'Не удалось загрузить модели', + noAuthenticatedProviders: 'Нет аутентифицированных провайдеров.', + pro: 'Pro', + proNeedsSubscription: 'Модели Pro требуют платную подписку Nous.', + free: 'Бесплатно', + freeTier: 'Бесплатный тариф', + priceTitle: 'Цена ввода / вывода за миллион токенов' + }, + + modelVisibility: { + title: 'Модели', + search: 'Поиск моделей', + noAuthenticatedProviders: 'Нет аутентифицированных провайдеров.', + addProvider: 'Добавить провайдера…' + }, + + shell: { + windowControls: 'Управление окном', + paneControls: 'Управление панелями', + appControls: 'Управление приложением', + modelMenu: { + search: 'Поиск моделей', + noModels: 'Модели не найдены', + editModels: 'Редактировать модели…', + fast: 'Быстро', + medium: 'Средне' + }, + modelOptions: { + noOptions: 'Нет параметров для этой модели', + options: 'Параметры', + thinking: 'Размышление', + fast: 'Быстро', + effort: 'Усилие', + minimal: 'Минимальное', + low: 'Низкое', + medium: 'Среднее', + high: 'Высокое', + max: 'Максимальное', + updateFailed: 'Не удалось обновить параметр модели', + fastFailed: 'Не удалось обновить быстрый режим' + }, + gatewayMenu: { + gateway: 'Шлюз', + connected: 'Подключён', + connecting: 'Подключение', + offline: 'Офлайн', + inferenceReady: 'Инференс готов', + inferenceNotReady: 'Инференс не готов', + checkingInference: 'Проверка инференса', + disconnected: 'Отключён', + openSystem: 'Открыть системную панель', + connection: label => `Подключение: ${label}`, + recentActivity: 'Недавняя активность', + viewAllLogs: 'Все логи →', + messagingPlatforms: 'Платформы мессенджеров' + }, + statusbar: { + unknown: 'неизвестно', + restart: 'перезапуск', + update: 'обновление', + updateInProgress: 'Обновление выполняется', + commitsBehind: (count, branch) => `${count} коммит${count === 1 ? '' : count < 5 ? 'а' : 'ов'} отстаёт от ${branch}`, + desktopVersion: version => `Hermes Desktop v${version}`, + commit: sha => `коммит ${sha}`, + branch: branch => `ветка ${branch}`, + closeCommandCenter: 'Закрыть центр команд', + openCommandCenter: 'Открыть центр команд', + gateway: 'Шлюз', + gatewayReady: 'готов', + gatewayNeedsSetup: 'требует настройки', + gatewayChecking: 'проверка', + gatewayConnecting: 'подключение', + gatewayOffline: 'офлайн', + gatewayTitle: 'Статус шлюза Hermes', + agents: 'Агенты', + closeAgents: 'Закрыть агентов', + openAgents: 'Открыть агентов', + subagents: count => `${count} дочерн${count === 1 ? 'ий агент' : count < 5 ? 'их агента' : 'их агентов'}`, + failed: count => `${count} с ошибкой`, + running: count => `${count} выполняется`, + cron: 'Cron', + openCron: 'Открыть задания cron', + turnRunning: 'Выполняется', + currentTurnElapsed: 'Текущий ход длится', + contextUsage: 'Использование контекста', + session: 'Сессия', + runtimeSessionElapsed: 'Сессия длится', + yoloOn: 'YOLO вкл — автоматическое одобрение опасных команд. Нажмите, чтобы выключить.', + yoloOff: 'YOLO выкл — нажмите для автоматического одобрения опасных команд.', + modelNone: 'нет', + noModel: 'нет модели', + switchModel: 'Переключить модель', + openModelPicker: 'Открыть выбор модели', + modelTitle: (provider, model) => `Модель · ${provider}: ${model}`, + providerModelTitle: (provider, model) => `${provider} · ${model}` + } + }, + + rightSidebar: { + aria: 'Правая боковая панель', + panelsAria: 'Панели правой боковой панели', + files: 'Файловая система', + terminal: 'Терминал', + noFolderSelected: 'Папка не выбрана', + changeCwdTitle: 'Изменить рабочую директорию', + folderTip: cwd => `${cwd} — нажмите, чтобы сменить папку`, + openFolder: 'Открыть папку', + refreshTree: 'Обновить дерево', + collapseAll: 'Свернуть все папки', + previewUnavailable: 'Предпросмотр недоступен', + couldNotPreview: path => `Не удалось предпросмотреть ${path}`, + noProjectTitle: 'Нет проекта', + noProjectBody: 'Задайте рабочую директорию из строки состояния, чтобы просматривать файлы.', + unreadableTitle: 'Недоступно', + unreadableBody: error => `Не удалось прочитать эту папку (${error}).`, + emptyTitle: 'Пусто', + emptyBody: 'Эта папка пуста.', + treeErrorTitle: 'Ошибка дерева', + treeErrorBody: 'Дерево файлов столкнулось с ошибкой при отображении этой папки.', + tryAgain: 'Попробовать снова', + loadingTree: 'Загрузка дерева файлов', + loadingFiles: 'Загрузка файлов', + terminalFocus: 'Фокус на терминал', + terminalSplit: 'Вернуться к разделённому виду', + addToChat: 'Добавить в чат' + }, + + preview: { + tab: 'Предпросмотр', + closeTab: label => `Закрыть ${label}`, + closePane: 'Закрыть панель предпросмотра', + loading: 'Загрузка предпросмотра', + unavailable: 'Предпросмотр недоступен', + opening: 'Открытие...', + hide: 'Скрыть', + openPreview: 'Открыть предпросмотр', + sourceLineTitle: 'Нажмите для выбора · shift+клик для расширения · перетащите в компоновщик', + source: 'ИСТОЧНИК', + renderedPreview: 'ПРЕДПРОСМОТР', + unknownSize: 'неизвестный размер', + binaryTitle: 'Похоже, это бинарный файл', + binaryBody: label => `Предпросмотр ${label} может отображать нечитаемый текст.`, + largeTitle: 'Этот файл большой', + largeBody: (label, size) => `${label} размером ${size}. Hermes покажет только первые 512 КБ.`, + previewAnyway: 'Предпросмотреть всё равно', + truncated: 'Показаны первые 512 КБ.', + noInlineTitle: 'Нет встроенного предпросмотра', + noInlineBody: mimeType => `${mimeType || 'Этот тип файла'} всё ещё можно прикрепить как контекст.`, + console: { + deselect: 'Снять выделение', + select: 'Выделить', + copyFailed: 'Не удалось скопировать вывод консоли', + copyEntry: 'Копировать эту запись', + sendEntry: 'Отправить эту запись в чат', + messages: count => `${count} сообщен${count === 1 ? 'ие' : count < 5 ? 'ия' : 'ий'} консоли`, + resize: 'Изменить размер консоли предпросмотра', + title: 'Консоль предпросмотра', + selected: count => `Выбрано: ${count}`, + sendToChat: 'Отправить в чат', + copySelected: 'Копировать выделенное в буфер обмена', + copyAll: 'Копировать всё в буфер обмена', + copy: 'Копировать', + clear: 'Очистить', + empty: 'Сообщений консоли пока нет.', + promptHeader: 'Консоль предпросмотра:', + sentTitle: 'Отправлено в чат', + sentMessage: count => `${count} запис${count === 1 ? 'ь' : count < 5 ? 'и' : 'ей'} лога добавлено в компоновщик` + }, + web: { + appFailedToBoot: 'Приложение предпросмотра не удалось запустить', + serverNotFound: 'Сервер не найден', + failedToLoad: 'Не удалось загрузить предпросмотр', + tryAgain: 'Попробовать снова', + restarting: 'Hermes перезапускается...', + askRestart: 'Попросить Hermes перезапустить сервер', + lookingRestart: taskId => `Hermes ищет сервер предпросмотра для перезапуска (${taskId})`, + restartingTitle: 'Перезапуск сервера предпросмотра', + restartingMessage: 'Hermes работает в фоновом режиме. Следите за прогрессом в консоли предпросмотра.', + startRestartFailed: message => `Не удалось начать перезапуск сервера: ${message}`, + restartFailed: 'Не удалось перезапустить сервер', + hideConsole: 'Скрыть консоль предпросмотра', + showConsole: 'Показать консоль предпросмотра', + hideDevTools: 'Скрыть DevTools предпросмотра', + openDevTools: 'Открыть DevTools предпросмотра', + finishedRestarting: message => `Hermes завершил перезапуск сервера предпросмотра${message ? `: ${message}` : ''}`, + failedRestarting: message => `Не удалось перезапустить сервер: ${message}`, + unknownError: 'неизвестная ошибка', + restartedTitle: 'Сервер предпросмотра перезапущен', + reloadingNow: 'Предпросмотр перезагружается.', + restartFailedTitle: 'Не удалось перезапустить предпросмотр', + restartFailedMessage: 'Hermes не смог перезапустить сервер.', + stillWorking: + 'Hermes всё ещё работает, но результат перезапуска ещё не получен. Команда сервера может выполняться на переднем плане.', + workspaceReloading: 'Рабочая область изменена, предпросмотр перезагружается', + fileChanged: url => `Файл изменён, предпросмотр перезагружается: ${url}`, + filesChanged: (count, url) => `${count} изменений файлов, предпросмотр перезагружается: ${url}`, + watchFailed: message => `Не удалось отслеживать файл предпросмотра: ${message}`, + moduleMimeDescription: + 'Модульные скрипты обслуживаются с неправильным MIME-типом. Обычно это означает, что статический файловый сервер обслуживает Vite/React-приложение вместо dev-сервера проекта.', + loadFailedConsole: (code, message) => `Загрузка не удалась${code ? ` (${code})` : ''}: ${message}`, + unreachableDescription: 'Страница предпросмотра недоступна.', + openTarget: url => `Открыть ${url}`, + fallbackTitle: 'Предпросмотр' + } + }, + + assistant: { + thread: { + loadingSession: 'Загрузка сессии', + loadingResponse: 'Hermes загружает ответ', + thinking: 'Думает', + today: time => `Сегодня, ${time}`, + yesterday: time => `Вчера, ${time}`, + copy: 'Копировать', + refresh: 'Обновить', + moreActions: 'Ещё действия', + branchNewChat: 'Ветвление в новом чате', + readAloudFailed: 'Не удалось прочитать вслух', + preparingAudio: 'Подготовка аудио...', + stopReading: 'Остановить чтение', + readAloud: 'Прочитать вслух', + editMessage: 'Редактировать сообщение', + stop: 'Остановить', + editableCheckpoint: 'Редактируемая контрольная точка', + restorePrevious: 'Восстановить предыдущую контрольную точку', + restoreCheckpoint: 'Восстановить контрольную точку', + restoreNext: 'Восстановить следующую контрольную точку', + goForward: 'Перейти вперёд', + sendEdited: 'Отправить отредактированное сообщение' + }, + approval: { + gatewayDisconnected: 'Шлюз Hermes не подключён', + sendFailed: 'Не удалось отправить ответ одобрения', + run: 'Запустить', + moreOptions: 'Ещё варианты одобрения', + allowSession: 'Разрешить эту сессию', + alwaysAllowMenu: 'Всегда разрешать…', + reject: 'Отклонить', + alwaysTitle: 'Всегда разрешать эту команду?', + alwaysDescription: pattern => + `Это добавит шаблон «${pattern}» в ваш постоянный белый список (~/.hermes/config.yaml). Hermes больше не будет спрашивать для таких команд — ни в этой, ни в любой будущей сессии.`, + alwaysAllow: 'Всегда разрешать' + }, + clarify: { + notReady: 'Запрос уточнения ещё не готов', + gatewayDisconnected: 'Шлюз Hermes не подключён', + sendFailed: 'Не удалось отправить ответ уточнения', + loadingQuestion: 'Загрузка вопроса…', + other: 'Другое (введите свой ответ)', + placeholder: 'Введите ваш ответ…', + shortcut: '⌘/Ctrl + Enter для отправки', + back: 'Назад', + skip: 'Пропустить', + send: 'Отправить' + }, + tool: { + code: 'Код', + copyCode: 'Копировать код', + renderingImage: 'Рендеринг изображения', + copyOutput: 'Копировать вывод', + copyCommand: 'Копировать команду', + copyContent: 'Копировать содержимое', + copyUrl: 'Копировать URL', + copyResults: 'Копировать результаты', + copyQuery: 'Копировать запрос', + copyFile: 'Копировать файл', + copyPath: 'Копировать путь', + outputAlt: 'Вывод инструмента', + rawResponse: 'Необработанный ответ', + copyActivity: 'Копировать активность', + recoveredOne: 'Восстановлено после 1 неудачного шага', + recoveredMany: count => `Восстановлено после ${count} неудачных шагов`, + failedOne: '1 шаг завершился с ошибкой', + failedMany: count => `${count} шагов завершились с ошибкой`, + statusRunning: 'Выполняется', + statusError: 'Ошибка', + statusRecovered: 'Восстановлено', + statusDone: 'Готово' + } + }, + + prompts: { + gatewayDisconnected: 'Шлюз Hermes не подключён', + sudoSendFailed: 'Не удалось отправить пароль sudo', + secretSendFailed: 'Не удалось отправить секрет', + sudoTitle: 'Пароль администратора', + sudoDesc: 'Hermes нужен ваш sudo-пароль для выполнения привилегированной команды. Он отправляется только вашему локальному агенту.', + sudoPlaceholder: 'sudo-пароль', + secretTitle: 'Требуется секрет', + secretDesc: 'Hermes нужен учётные данные для продолжения.', + secretPlaceholder: 'секретное значение' + }, + + desktop: { + audioReadFailed: 'Не удалось прочитать записанное аудио', + sessionUnavailable: 'Сессия недоступна', + createSessionFailed: 'Не удалось создать новую сессию', + promptFailed: 'Не удалось выполнить промпт', + providerCredentialRequired: 'Добавьте учётные данные провайдера перед отправкой первого сообщения.', + emptySlashCommand: 'пустая слэш-команда', + desktopCommands: 'Команды рабочего стола', + skillCommandsAvailable: count => `Доступно ${count} команд навыков.`, + warningLine: message => `предупреждение: ${message}`, + yoloArmed: 'YOLO активирован для этого чата', + yoloOff: 'YOLO выключен', + yoloSystem: active => `YOLO ${active ? 'вкл' : 'выкл'} для этой сессии`, + yoloTitle: 'YOLO', + yoloToggleFailed: 'Не удалось переключить YOLO', + profileStatus: current => + `Профиль: ${current}. Используйте /profile <имя> или выбор «Новая сессия», чтобы начать чат в другом профиле.`, + unknownProfile: 'Неизвестный профиль', + noProfileNamed: (target, available) => `Нет профиля с именем «${target}». Доступны: ${available}`, + newChatsProfile: name => `Новые чаты будут использовать профиль ${name}.`, + setProfileFailed: 'Не удалось установить профиль', + sttDisabled: 'Распознавание речи отключено в настройках.', + stopFailed: 'Не удалось остановить', + regenerateFailed: 'Не удалось перегенерировать', + editFailed: 'Не удалось отредактировать', + resumeFailed: 'Не удалось возобновить', + nothingToBranch: 'Нечего ветвить', + branchNeedsChat: 'Начните или возобновите чат перед ветвлением.', + sessionBusy: 'Сессия занята', + branchStopCurrent: 'Остановите текущий ход перед ветвлением этого чата.', + branchNoText: 'У этого сообщения нет текста для ветвления.', + branchTitle: 'Ветвление', + branchFailed: 'Не удалось создать ветвление', + deleteFailed: 'Не удалось удалить', + archived: 'Архивировано', + archiveFailed: 'Не удалось архивировать', + cwdChangeFailed: 'Не удалось изменить рабочую директорию', + cwdStagedTitle: 'Рабочая директория задана', + cwdStagedMessage: 'Перезапустите бэкенд рабочего стола, чтобы применить изменения 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: 'Перетащите файлы' + }, + + errors: { + genericFailure: 'Что-то пошло не так', + boundaryTitle: 'Что-то сломалось в интерфейсе', + boundaryDesc: 'Представление столкнулось с непредвиденной ошибкой. Ваши чаты и настройки в безопасности.', + reloadWindow: 'Перезагрузить окно', + openLogs: 'Открыть логи' + }, + + ui: { + search: { + clear: 'Очистить поиск' + }, + pagination: { + label: 'пагинация', + previous: 'Назад', + previousAria: 'Перейти на предыдущую страницу', + next: 'Далее', + nextAria: 'Перейти на следующую страницу' + }, + sidebar: { + title: 'Боковая панель', + description: 'Отображает мобильную боковую панель.', + toggle: 'Переключить боковую панель' + } + } +} diff --git a/apps/desktop/src/i18n/types.ts b/apps/desktop/src/i18n/types.ts index b72d9d8fd71a..b88e6d9e5a03 100644 --- a/apps/desktop/src/i18n/types.ts +++ b/apps/desktop/src/i18n/types.ts @@ -5,7 +5,7 @@ // partial locales should use `defineLocale()` so missing desktop-only strings // fall back to English while new keys remain type-checked. -export type Locale = 'en' | 'zh' | 'zh-hant' | 'ja' +export type Locale = 'en' | 'zh' | 'zh-hant' | 'ja' | 'ru' interface ModeOptionCopy { label: string