feat: harden local shell, SFTP, and desktop session reliability

Finder-launched terminals now inherit a login PATH so tools like ollama resolve, destructive SFTP/editor actions require confirmation, and terminal search plus window dragging work again. SSH forwards and RDP sessions keep listeners and survive reconnect without FastPath crashes.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Logan.LiandCursor committed 2026-08-16 16:08:11 +08:00
1 parent 188f3556ed
commit 08696896f9
223 files changed
+13493 -3423

No files matched your search

+122 -38
View File
@@ -90,6 +90,8 @@
"terminal": "Терминал",
"batch": "Пакетное выполнение",
"newTerminal": "Новый терминал",
"closeAria": "Закрыть {{title}}",
"listAria": "Открытые вкладки",
"kind": {
"terminal": "Терминал",
"sftp": "SFTP",
@@ -105,7 +107,8 @@
"closeRight": "Закрыть вкладки справа",
"closeLeft": "Закрыть вкладки слева",
"closeAll": "Закрыть все",
"clone": "Дублировать вкладку"
"clone": "Дублировать вкладку",
"openSftp": "Открыть SFTP"
}
},
"sidebar": {
@@ -149,9 +152,9 @@
"disconnect": "Отключиться"
},
"pro": {
"upgrade": "Обновить Pro",
"upgrade": "Обновить до Pro",
"feature": "Возможности Pro",
"unlock": "Разблокировать {{feature}}",
"unlock": "Разблокировать расширенные функции Pro",
"comingSoon": "Скоро",
"activateSuccess": "Активировано",
"activateFailed": "Сбой активации",
@@ -160,28 +163,33 @@
"invalidLicense": "Сбой активации: недействительная лицензия",
"goUpgrade": "Обновить",
"haveLicense": "Есть лицензия? Активировать",
"title": "OpsXShell Pro · Оперативные возможности Pro",
"subtitleComingSoon": "{{feature}} скоро · обновите Pro для раннего доступа",
"subtitleUnlock": "Разблокировка: {{feature}}",
"subtitleDefault": "Проброс портов, прокси, serial, AI, пакетное выполнение и рассылка",
"title": "OpsXShell · Разблокировать Pro",
"subtitleComingSoon": "Расширенные функции скоро · обновите до Pro для раннего доступа",
"subtitleUnlock": "Разблокируются расширенные функции",
"subtitleDefault": "Проброс портов, AI в терминале, пакетные операции, списки хостов и удалённый рабочий стол в Pro",
"activated": "Pro активирован",
"expiresAt": " · действует до {{date}}",
"lifetime": " · бессрочная лицензия",
"oneTimeFee": "Разовая покупка · бессрочно на этом устройстве · без подписки",
"oneTimeFee": "Бесплатно / Pro год / Pro бессрочно",
"pasteLicense": "Вставить лицензию",
"activateButton": "Активировать",
"clearLicense": "Очистить локальную лицензию",
"paymentNote": "Оплата обрабатывается Waffo; это приложение не собирает данные карт.",
"testDeviceId": "Тестовая среда · ID устройства: {{deviceId}}",
"later": "Может быть позже",
"viewPro": "Посмотреть Pro",
"wallComingSoonSuffix": " · скоро",
"wallUnlock": "Разблокировать {{feature}}",
"wallMessageComingSoon": "Эта возможность Pro ещё в разработке. Обновите для раннего доступа.",
"wallMessageDefault": "Эта функция требует OpsXShell Pro. Обновите, чтобы использовать её на этом устройстве.",
"later": "Позже",
"viewPro": "Смотреть тарифы Pro",
"wallComingSoonSuffix": "скоро",
"wallUnlock": "Разблокировать расширенные функции Pro",
"wallMessageComingSoon": "Эта возможность входит в OpsXShell Pro и ещё дорабатывается. Обновите до Pro на год или бессрочно для раннего доступа.",
"wallMessageDefault": "Проброс портов, AI в терминале, пакетный запуск, списки хостов и удалённый рабочий стол входят в Pro. Обновите до Pro на год или бессрочно, чтобы использовать на этом устройстве.",
"checkoutUnavailable": "Оформление недоступно ({{status}})",
"emailLabel": "Email",
"emailRequired": "Введите email перед оформлением заказа",
"claimPending": "Получение лицензии…",
"invalidKeyFormat": "Неверный формат ключа — вставьте лицензию LIC-",
"accountEmail": "Аккаунт: {{email}}",
"freeTier": "Бесплатно",
"proTier": "Pro",
"proTier": "Профессиональная",
"freeItems": {
"ssh": "SSH-терминал",
"sftp": "Менеджер файлов SFTP",
@@ -191,22 +199,60 @@
},
"proItems": {
"forward": "Проброс портов",
"proxy": "Прокси сеанса / SOCKS",
"proxy": "Прокси сессии / SOCKS",
"serial": "Последовательный терминал",
"ai": "Терминальный AI-ассистент",
"batch": "Пакетное выполнение / синхронизация рассылки",
"broadcast": "Синхронизация ввода рассылки",
"importExport": "Массовый импорт/экспорт хостов",
"desktop": "Удалённый рабочий стол RDP / VNC"
}
"ai": "AI-помощник в терминале",
"batch": "Пакетный запуск / синхронный broadcast",
"broadcast": "Синхронный ввод broadcast",
"importExport": "Пакетный импорт/экспорт списка хостов",
"desktop": "Удалённый рабочий стол (RDP / VNC)"
},
"planYearly": "Pro на год",
"planYearlyDesc": "¥99 / год · 2 устройства",
"planPerpetual": "Pro бессрочно",
"planPerpetualDesc": "¥199 · 5 устройств · навсегда",
"planTrial": "Pro пробный",
"devices": "До {{count}} устройств",
"goUpgradeYearly": "Купить Pro на год",
"goUpgradePerpetual": "Купить Pro бессрочно",
"activatedYearly": "Активирован Pro на год",
"activatedPerpetual": "Активирован Pro бессрочно",
"activatedTrial": "Активирован Pro пробный",
"choosePlan": "Выбор: бесплатно / Pro год / Pro бессрочно",
"planFreePrice": "¥0",
"planFreeDesc": "SSH · SFTP · локальный терминал",
"planFreeCta": "Текущий план",
"planIncludesPro": "Все функции Pro",
"planFree": "Бесплатно",
"subtitleUnlockDetail": "Связанная возможность: {{feature}}",
"wallUnlockComingSoon": "Расширенные функции Pro скоро",
"wallMessageComingSoonFeature": "«{{feature}}» — расширенная функция Pro, ещё дорабатывается. Обновите до Pro на год или бессрочно для раннего доступа.",
"wallMessageFeature": "«{{feature}}» — расширенная функция Pro. Обновите до Pro на год или бессрочно, чтобы использовать на этом устройстве."
},
"launcher": {
"title": "Быстрый запуск",
"description": "Сеансы, локальные оболочки и консоли устройств",
"description": "Сессии, локальный Shell, рабочий стол, serial",
"cards": {
"quickConnect": { "title": "Быстрое подключение", "desc": "user@host:port" },
"saveSession": { "title": "Сохранить сеанс", "desc": "Учётные данные, группы, прокси" },
"serial": { "title": "Последовательный терминал", "desc": "Скорость (baud) и настройки порта" }
"quickConnect": {
"title": "Быстрое подключение",
"desc": "user@host:port"
},
"saveSession": {
"title": "Сохранить сеанс",
"desc": "Учётные данные, группы, прокси"
},
"serial": {
"title": "Последовательный терминал",
"desc": "Скорость (baud) и настройки порта"
},
"rdp": {
"title": "RDP рабочий стол",
"desc": "Удалённый Windows"
},
"vnc": {
"title": "VNC рабочий стол",
"desc": "Удалённый графический"
}
},
"sectionLocalShell": "Локальная оболочка",
"sectionSavedSessions": "Сохранённые сеансы",
@@ -220,6 +266,7 @@
"forwardOpenRunning": "SOCKS5 работает (:{{port}}) · клик для управления · правый клик для копирования",
"forwardStarted": "Проброс запущен",
"forwardStartedDynamic": "SOCKS5 запущен: socks5://127.0.0.1:{{port}}",
"forwardResumed": "Восстановлено перенаправлений: {{count}}",
"socksStartFailed": "Сбой запуска: {{message}}",
"socksStopFailed": "Сбой остановки: {{message}}",
"socksCopied": "Скопировано socks5://127.0.0.1:{{port}}"
@@ -240,7 +287,7 @@
"typeDynamic": "Dynamic",
"running": "Выполняется",
"socksProxy": "SOCKS5: 127.0.0.1:{{port}}",
"remoteToLocal": "Удалённый {{remote}} → локальный {{local}}",
"remoteToLocal": "удалённо {{bind}}:{{remote}} → {{host}}:{{local}}",
"stats": "{{connections}} подкл. · ↑{{up}} · ↓{{down}}",
"copyProxy": "Копировать прокси",
"copyRule": "Дублировать правило",
@@ -250,15 +297,15 @@
"delete": "Удалить",
"editTitle": "Изменить проброс",
"addTitle": "Добавить проброс",
"typeLocalDesc": "Локальный → удалённый",
"typeRemoteDesc": "Удалённый → локальный",
"typeLocalDesc": "Доступ к удалённому сервису",
"typeRemoteDesc": "Открыть локальный сервис",
"typeDynamicDesc": "SOCKS5",
"selectType": "Тип",
"drawerSubtitle": "Туннель через текущий SSH-сеанс",
"ruleFields": "Подробности",
"dynamicHint": "Локальный SOCKS5, по умолчанию 127.0.0.1:1080 (можно менять). Трафик выходит через публичный IP этого SSH-сервера.",
"dynamicSingleHint": "Одно правило Dynamic на SSH-хост; измените порт прослушивания при остановке.",
"dynamicPortHint": "Если порт занят, выберите другой локальный порт (напр. 1081).",
"dynamicHint": "Локальный SOCKS5 (по умолчанию :1080). Трафик выходит через этот SSH.",
"dynamicSingleHint": "Одно динамическое правило на SSH-хост; порт меняйте, когда остановлено.",
"dynamicPortHint": "Если занят — другой порт (напр. 1081).",
"ruleList": "Правила",
"ruleListEmpty": "Правил Local / Remote пока нет",
"addSection": "Добавить новое",
@@ -292,17 +339,30 @@
"internet": "Интернет",
"localCaption": "Local: обращение к удалённому порту как к локальному.",
"remoteCaption": "Remote: удалённый доступ пробрасывается к локальному сервису.",
"dynamicCaption": "Dynamic: эта машина становится прокси SOCKS5, выходящим через публичный IP текущего SSH-сервера."
"dynamicCaption": "Динамический: локальный SOCKS5 через выход этого SSH."
},
"ruleName": "Название",
"ruleNamePlaceholder": "напр. MySQL",
"localListenPort": "Локальный порт прослушивания",
"localListenPortHint": "По умолчанию localhost — как ssh -L порт:…. Обычно достаточно порта.",
"localBindAdvanced": "Дополнительно: сменить локальный bind",
"localTargetPort": "Локальный целевой порт",
"remoteTargetPort": "Удалённый целевой порт",
"remoteTargetPort": "Целевой порт",
"remoteListenPort": "Удалённый порт прослушивания",
"remoteListenPortHint": "По умолчанию localhost на SSH-сервере — как ssh -R порт:…. Обычно достаточно порта.",
"remoteListenAddress": "Удалённый listen",
"remoteListenAddressPlaceholder": "127.0.0.1:18080",
"remoteListenAddressHint": "127.0.0.1 — только удалённый хост; 0.0.0.0 — удалённая LAN (нужен GatewayPorts).",
"errorRemoteListenAddress": "Укажите корректный удалённый listen, напр. 127.0.0.1:18080",
"localTargetAddress": "Локальная / LAN цель",
"localTargetAddressPlaceholder": "127.0.0.1:8080",
"localTargetAddressHint": "На локальный/LAN сервис, напр. 127.0.0.1:8080. Не используйте 0.0.0.0.",
"viaSessionRemote": "Слушать на {{target}} (по умолчанию localhost)",
"errorRemoteListenPort": "Укажите корректный удалённый порт прослушивания (1–65535)",
"errorLocalTargetAddress": "Укажите корректную локальную/LAN цель, напр. 127.0.0.1:8080",
"portPlaceholder": "напр. 3306",
"remoteHost": "Удалённый хост",
"remoteHostPlaceholder": "localhost или IP",
"remoteHost": "Целевой хост",
"remoteHostPlaceholder": "127.0.0.1",
"socksRuleName": "Прокси SOCKS5",
"defaultLocalName": "Локальный проброс",
"defaultRemoteName": "Удалённый проброс",
@@ -316,11 +376,35 @@
"deleteConfirm": "Удалить это правило?",
"errorNamePort": "Введите имя и корректный порт (1–65535)",
"errorTargetPort": "Введите корректный целевой порт (1–65535)",
"errorRemoteHost": "Введите удалённый хост",
"errorRemoteHost": "Укажите целевой хост (относительно SSH-сервера)",
"errorNotConnected": "Нет подключения",
"errorStartFailed": "Сбой запуска",
"errorStopFailed": "Сбой остановки",
"errorSocksRunning": "SOCKS5 уже работает",
"copySuffix": "Копировать"
"copySuffix": "Копировать",
"remoteBindHint": "0.0.0.0 нужен GatewayPorts; иначе часто только 127.0.0.1. Цель не должна быть 0.0.0.0.",
"localToRemote": "локально {{bind}}:{{local}} → удалённо {{host}}:{{remote}}",
"localToRemotePort": "локально :{{local}} → удалённо {{host}}:{{remote}}",
"remoteHostHint": "Относительно SSH-сервера. Сам хост — 127.0.0.1, LAN — достижимый IP. Не 0.0.0.0.",
"localBindHost": "Локальный bind",
"localBindHostPlaceholder": "127.0.0.1",
"localBindHostHint": "По умолчанию только этот ПК. 0.0.0.0 или LAN IP — доступ из сети.",
"errorTargetHostInvalid": "Цель не может быть 0.0.0.0 — укажите 127.0.0.1 или достижимый IP",
"listenAddress": "Локальный listen",
"listenAddressPlaceholder": "127.0.0.1:8080",
"listenAddressHint": "По умолчанию только этот ПК; 0.0.0.0 разрешает LAN.",
"targetAddress": "Удалённая цель",
"targetAddressPlaceholder": "127.0.0.1:80",
"targetAddressHint": "С точки зрения SSH-сервера. Сервис на нём — 127.0.0.1:порт.",
"errorListenAddress": "Укажите корректный listen, напр. 127.0.0.1:8080",
"errorTargetAddress": "Укажите корректную цель, напр. 127.0.0.1:80",
"targetAddressHintVia": "Через {{target}}. Сервис на том хосте — 127.0.0.1:порт (новый порт не открывается).",
"viaSession": "Через сессию {{target}}",
"remoteToLocalPort": "удал. :{{remote}} → {{host}}:{{local}}",
"remoteBindAdvanced": "Дополнительно: сменить remote bind",
"remoteBindHost": "Remote bind",
"remoteBindHostPlaceholder": "127.0.0.1",
"remoteBindHostHint": "По умолчанию только на SSH-сервере. 0.0.0.0 требует GatewayPorts; без него sshd часто тихо слушает только 127.0.0.1.",
"remoteGatewayPortsWarning": "Запрошен remote-listen 0.0.0.0. Без GatewayPorts sshd часто молча слушает только 127.0.0.1."
}
}
+11 -4
View File
@@ -15,10 +15,12 @@
"passwordOptional": "Пароль (необязательно)",
"width": "Ширина",
"height": "Высота",
"rdpFootnote": "Нативный IronRDP: TCP → X.224 → TLS → CredSSP/NLA → MCS; тайм-аут рукопожатия 45 с. Проверка сертификата во время разработки ослаблена.",
"vncFootnote": "Нативный vnc-rs RFB с декодированием Tight JPEG; буфер обмена передаётся в UTF-8 (очень старые серверы могут искажать текст).",
"rdpFootnote": "В полноэкранном или развёрнутом режиме удалённое разрешение запрашивается под текущую область окна. Начальный размер можно выбрать и до подключения. Проверка сертификата сейчас ослаблена для разработки.",
"vncFootnote": "У большинства VNC-серверов удалённое разрешение фиксировано; полноэкранный режим только масштабирует изображение локально. Предпочтительный размер при подключении носит справочный характер. Буфер обмена отправляется в UTF-8.",
"errorHostRequired": "Введите адрес хоста",
"errorUsernameRequired": "Введите имя пользователя"
"errorUsernameRequired": "Введите имя пользователя",
"resolution": "Разрешение",
"resolutionCustom": "Свой размер"
},
"toolbar": {
"pasteClipboard": "Вставить локальный буфер обмена на удалённый сервер",
@@ -28,7 +30,12 @@
"openShareDir": "Открыть общую папку локально (Downloads)",
"openShareDirAria": "Открыть общую папку",
"disconnect": "Отключиться",
"disconnectAria": "Отключиться"
"disconnectAria": "Отключиться",
"fullscreen": "Полный экран",
"fullscreenAria": "Развернуть на весь экран",
"exitFullscreen": "Выйти из полного экрана",
"exitFullscreenAria": "Выйти из полного экрана",
"exitFullscreenHint": "Esc / F11 — выход"
},
"status": {
"connecting": "Подключение…",
+16 -3
View File
@@ -39,7 +39,9 @@
"closed": "Подключение закрыто",
"dnsFailed": "Не удалось разрешить имя хоста",
"refused": "В подключении отказано",
"disconnected": "Отключено"
"disconnected": "Отключено",
"keepaliveFailed": "SSH keep-alive несколько раз не удался; соединение закрыто",
"idleKicked": "Удалённая оболочка завершилась по таймауту простоя (TMOUT)"
},
"auth": {
"failed": "Сбой аутентификации. Проверьте имя пользователя и пароль.",
@@ -68,7 +70,10 @@
"forward": {
"failed": "Сбой настройки проброса портов",
"bindFailed": "Не удалось привязать порт",
"alreadyInUse": "Порт уже занят"
"alreadyInUse": "Локальный порт уже занят. Остановите этот проброс или другой процесс на порту, затем запустите снова.",
"remoteDenied": "Сервер отклонил удалённый проброс (AllowTcpForwarding / PermitListen, порт занят или ограничение bind). Слушаем только 127.0.0.1 на удалённой стороне.",
"remoteDeniedPrivileged": "Отклонён привилегированный порт (<1024). Используйте ≥1024 или root и проверьте AllowTcpForwarding / PermitListen.",
"invalidTargetHost": "Цель не может быть 0.0.0.0 — укажите 127.0.0.1 или доступный внутренний IP"
},
"sftp": {
"connectFailed": "Сбой SFTP-подключения",
@@ -80,7 +85,15 @@
"writeFailed": "Сбой сохранения",
"renameFailed": "Сбой переименования",
"chmodFailed": "Не удалось обновить права доступа",
"apiUnavailable": "SFTP API недоступен"
"apiUnavailable": "SFTP API недоступен",
"channelFailed": "Не удалось открыть канал SFTP (лимит сервера или занятая сессия). Переподключитесь",
"sessionGone": "Сессия SFTP завершена; переподключитесь",
"busy": "Идёт передача файла. Повторите позже.",
"editTooLarge": "Файл слишком большой для текстового редактора (макс. ~2 МБ). Скачайте и откройте локально.",
"editBinary": "Файл похож на двоичный и не открывается в текстовом редакторе",
"editNotUtf8": "Удалённый файл не является допустимым текстом UTF-8",
"transferCancelled": "Передача отменена",
"diskFull": "Недостаточно места на диске (локально или удалённо)"
},
"session": {
"notFound": "Сеанс не найден",
+6 -4
View File
@@ -69,17 +69,17 @@
"confirm": "Удалить"
},
"welcome": {
"tagline": "Удалённый терминал · SFTP · Проброс портов",
"tagline": "Просто и по делу · терминал · файлы · рабочий стол",
"sectionStart": "Начало работы",
"sectionDesktop": "Удалённый рабочий стол",
"sectionRecent": "Недавние сеансы",
"quickConnect": {
"title": "Быстрое подключение",
"desc": "Разовое SSH; не сохраняется в библиотеке"
"desc": "Разовый SSH — не сохраняется в библиотеке"
},
"newSession": {
"title": "Новый сеанс",
"desc": "Сохраняйте хосты; двойной клик в боковой панели для подключения"
"desc": "Сохраните хост — двойной клик в сайдбаре для подключения"
},
"localShell": {
"title": "Локальная оболочка",
@@ -167,7 +167,9 @@
"jumpHost": "Jump-хост (необязательно)",
"jumpNone": "Без jump-хоста",
"jumpNoneAlt": "Без jump-хоста",
"jumpHint": "Сохранённый сеанс можно использовать как jump-хост"
"jumpHint": "Сохранённый сеанс можно использовать как jump-хост",
"resolution": "Разрешение",
"resolutionCustom": "Свой размер"
},
"batch": {
"title": "Пакетное выполнение",
+50 -18
View File
@@ -1,9 +1,7 @@
{
"nav": {
"general": "Рабочее пространство",
"generalDesc": "Макет и ресурсы",
"appearance": "Внешний вид",
"appearanceDesc": "Тема и язык",
"general": "Основные",
"generalDesc": "Тема, язык и макет",
"terminal": "Терминал",
"terminalDesc": "Шрифт и цвета",
"shortcuts": "Горячие клавиши",
@@ -15,11 +13,13 @@
"ai": "AI-модели",
"aiDesc": "AI-провайдеры",
"about": "О приложении",
"aboutDesc": "Версия и данные"
"aboutDesc": "Версия и данные",
"account": "Аккаунт",
"accountDesc": "Email, лицензия и устройство"
},
"title": "Настройки",
"search": {
"placeholder": "Поиск по настройкам..."
"placeholder": "Поиск настроек…"
},
"language": {
"label": "Язык",
@@ -89,7 +89,7 @@
"ssh": {
"connection": "Подключение",
"keepAlive": "Keep-alive",
"keepAliveDesc": "Отправлять keep-alive при простое для уменьшения разрывов",
"keepAliveDesc": "Отправлять SSH keep-alive в простое; отключать только после нескольких сбоев",
"autoReconnect": "Авто-переподключение",
"autoReconnectDesc": "Автоматически повторять после неожиданного разрыва",
"timeout": "Тайм-аут подключения",
@@ -102,7 +102,9 @@
"maxBackground": "Лимит фоновых подключений",
"maxBackgroundDesc": "Вытеснять давно не использованные подключения при превышении лимита",
"suspend": "Фоновая приостановка",
"suspendDesc": "Минуты до освобождения активных каналов, когда вкладка в фоне"
"suspendDesc": "Через сколько минут приостанавливать простаивающие фоновые вкладки (активные Shell/SFTP остаются)",
"keepAliveInterval": "Интервал keep-alive (сек)",
"keepAliveIntervalDesc": "Как часто опрашивать сервер (как ClientAliveInterval). При нестабильной сети — ≤15 с"
},
"security": {
"credentials": "Защита учётных данных",
@@ -119,7 +121,7 @@
"clearConfirm": "Очистить все известные ключи хостов? При следующем подключении запрос появится снова."
},
"about": {
"tagline": "Лёгкий нативный терминал эксплуатации SSH / SFTP",
"tagline": "Простой и удобный нативный терминал для эксплуатации",
"appVersion": "Приложение v{{version}}",
"copyVersion": "Копировать сведения о версии",
"runtime": "Среда выполнения",
@@ -127,8 +129,9 @@
"localData": "Локальные данные",
"sessionsSettings": "Сеансы и настройки",
"masterKeyPath": "Мастер-ключ",
"privacyNote": "Пароли и парольные фразы ключей шифруются AES-256-GCM в Rust. OpsXShell не сохраняет историю команд терминала.",
"license": "Лицензия MIT · Rust · XTerm.js"
"privacyNote": "Пароли и passphrase шифруются в Rust (AES-256-GCM). Без истории команд и без аудита-бастиона.",
"license": "Лицензия MIT · Rust · XTerm.js",
"logPath": "Журнал приложения"
},
"ai": {
"presets": "Пресеты провайдеров",
@@ -162,13 +165,42 @@
"apiKeySaveFailed": "Не удалось сохранить API-ключ",
"providerNotFound": "Провайдер не найден",
"modelCatalogReadFailed": "Не удалось прочитать каталог моделей",
"catalogUnavailable": "models.dev недоступен"
"catalogUnavailable": "models.dev недоступен",
"family": {
"openai_compatible": "Совместимые с OpenAI",
"standalone": "Нативные API",
"plan": "Подписки",
"local": "Локальные",
"custom": "Свой"
}
},
"family": {
"openai_compatible": "Совместимый с OpenAI",
"standalone": "────────",
"plan": "План",
"local": "Self-hosted",
"custom": "Своё"
"account": {
"title": "Account",
"email": "Email",
"emailDesc": "Used for checkout and license claims",
"plan": "Current plan",
"planFree": "Free",
"planYearly": "Pro на год",
"planPerpetual": "Pro бессрочно",
"planTrial": "Pro пробный",
"expires": "Expires {{date}}",
"lifetime": "Lifetime",
"devices": "Seats: up to {{count}} devices",
"deviceId": "This device ID",
"saveEmail": "Save email",
"activate": "Activate license",
"pasteLicense": "Paste LIC- license key",
"clear": "Clear local license",
"deactivate": "Deactivate this device",
"upgrade": "Upgrade",
"saved": "Saved",
"activateOk": "Activated",
"deviceList": "Привязанные устройства",
"loadingDevices": "Загрузка устройств…",
"noDevices": "Нет привязанных устройств",
"thisDevice": "Это устройство",
"removeDevice": "Отвязать",
"deviceRemoved": "Устройство отвязано",
"seatFull": "Все места заняты. Отвяжите другое устройство перед активацией на новой машине."
}
}
+54 -24
View File
@@ -3,8 +3,9 @@
"local": "Локально",
"remote": "Удалённо",
"notConnectedTitle": "Нет подключения к удалённому серверу",
"notConnectedDesc": "Сначала подключите SSH-сеанс, чтобы использовать SFTP",
"ariaLabel": "Панель файлов: {{title}}"
"notConnectedDesc": "Сначала подключите SSH — потом файлы и правки",
"ariaLabel": "Панель файлов: {{title}}",
"reconnectResumeDesc": "Соединение потеряно. Переподключение вернёт в {{path}}"
},
"connection": {
"selectServer": "Выберите сервер",
@@ -12,7 +13,9 @@
"createSessionHint": "Сначала создайте сеанс на боковой панели",
"reconnect": "Переподключиться",
"notConnected": "Нет подключения",
"connecting": "Подключение..."
"connecting": "Подключение...",
"resumeHint": "После переподключения вернёмся в {{path}}",
"reconnectingResume": "Переподключение и восстановление {{path}}…"
},
"errors": {
"notConnected": "SFTP не подключён",
@@ -23,19 +26,14 @@
"selectDownloadFiles": "Сначала выберите файлы для скачивания",
"uploadFailed": "Сбой загрузки",
"downloadFailed": "Сбой скачивания",
"fileError": "{{fileName}}: {{message}}"
"fileError": "{{fileName}}: {{message}}",
"mkdirFailed": "Не удалось создать папку"
},
"toolbar": {
"upload": "Загрузить",
"uploadAria": "Загрузить",
"download": "Скачать",
"downloadAria": "Скачать",
"downloadNeedSelection": "Сначала выберите удалённые файлы",
"downloadHint": "Скачать выбранные удалённые файлы",
"refresh": "Обновить",
"refreshAria": "Обновить",
"refreshAria": "Обновить эту панель",
"newFolder": "Новая папка",
"newFolderAria": "Новая папка"
"newFolderAria": "Создать папку на этой панели"
},
"sort": {
"name": "Имя",
@@ -55,7 +53,8 @@
"hideHidden": "Скрыть dot-файлы (.xxx)",
"showHiddenAria": "Показать скрытые файлы",
"hideHiddenAria": "Скрыть dot-файлы",
"searchAria": "Поиск в {{title}}"
"searchAria": "Поиск в {{title}}",
"upAtRoot": "Уже в корне"
},
"status": {
"loading": "Загрузка...",
@@ -63,7 +62,8 @@
"filtered": "Показано {{shown}} / {{total}}",
"hiddenCount": "{{total}} объектов ({{hidden}} скрыто)",
"total": "Объектов: {{count}}",
"filterHint": "Фильтр: {{query}}"
"filterHint": "Фильтр: {{query}}",
"refreshing": "Обновление..."
},
"empty": {
"dirTitle": "Пустой каталог",
@@ -99,7 +99,6 @@
"download": "Скачать",
"edit": "Изменить",
"rename": "Переименовать",
"copyPath": "Копировать путь",
"properties": "Свойства",
"permissions": "Права доступа",
"delete": "Удалить"
@@ -116,7 +115,18 @@
"chmodTitle": "Изменить права доступа",
"chmodApply": "Применить",
"pickUploadTitle": "Выберите файлы или папки для загрузки",
"pickUploadRzTitle": "Выберите файл для загрузки (rz)"
"pickUploadRzTitle": "Выберите файл для загрузки (rz)",
"overwriteTitle": "Перезаписать существующие файлы?",
"overwriteConfirm": "Перезаписать",
"overwriteSkip": "Пропустить",
"overwriteOneRemote": "«{{name}}» уже есть на удалённой стороне. Перезаписать?",
"overwriteOneLocal": "«{{name}}» уже есть локально. Перезаписать?",
"overwriteManyRemote": "На удалённой стороне уже есть {{count}} одноимённых. Перезаписать все?",
"overwriteManyLocal": "Локально уже есть {{count}} одноимённых. Перезаписать все?",
"overwriteMore": "…и ещё {{count}}",
"deleteTitle": "Подтвердите удаление",
"deleteMessage": "Удалить {{count}} элемент(ов): {{names}}",
"deleteRecursiveWarn": "Есть каталоги. Удаление рекурсивное и необратимо."
},
"properties": {
"name": "Имя",
@@ -152,27 +162,41 @@
"statusDone": "Готово",
"statusFailed": "Сбой",
"statusCancelled": "Отменено",
"dropUpload": "Загрузить в {{path}}",
"dropDownload": "Скачать в {{path}}"
"dropUpload": "Отпустите здесь, чтобы загрузить в {{path}}",
"dropDownload": "Отпустите здесь, чтобы скачать в {{path}}",
"railAria": "Передача между локальным и удалённым",
"railUpload": "Загрузить выбранные локальные файлы (или выбрать файлы, если ничего не выбрано)",
"railDownload": "Скачать выбранные удалённые файлы локально",
"railUploadAria": "Загрузить на удалённый",
"railDownloadAria": "Скачать локально",
"railDownloadNeedSelection": "Сначала выберите удалённые файлы",
"retry": "Повторить"
},
"editor": {
"readFailed": "Сбой чтения",
"saveFailed": "Сбой сохранения",
"unsavedClose": "Есть несохранённые изменения. Всё равно закрыть?",
"unsavedTitle": "Несохранённые изменения",
"discardAndClose": "Отменить и закрыть",
"reloadTitle": "Перезагрузить",
"discardAndReload": "Отменить и перезагрузить",
"saving": "Сохранение...",
"save": "Сохранить",
"saved": "Файл сохранён",
"loadingEditor": "Загрузка редактора...",
"footer": "UTF-8 · Monaco · ⌘/Ctrl+S сохранить · максимум ~2 МБ",
"loadBlocked": "Не удалось загрузить содержимое файла",
"footer": "UTF-8 · ⌘/Ctrl+S сохранить · Esc закрыть · макс. ~2 МБ",
"charCount": "Символов: {{count}}",
"unsavedMark": " · не сохранено",
"savedMark": " · сохранено"
"savedMark": " · сохранено",
"title": "Редактирование {{name}}",
"dirtyBadge": "Не сохранено",
"reload": "Перезагрузить",
"unsavedReload": "Отменить несохранённые изменения и перезагрузить?"
},
"toast": {
"renamed": "Переименовано в {{name}}",
"renameFailed": "Сбой переименования",
"pathCopied": "Путь скопирован в буфер обмена",
"pathCopyFailed": "Не удалось скопировать путь",
"chmodUpdated": "Права доступа изменены на {{mode}}",
"chmodFailed": "Не удалось обновить права доступа",
"uploadPartial": "{{action}} завершено для {{completed}}, сбой {{failed}}: {{detail}}",
@@ -180,7 +204,13 @@
"uploadAction": "Загрузка",
"downloadAction": "Скачивание",
"noFolderUpload": "Сбой загрузки папки",
"noDragPath": "Пути перетаскивания недоступны; используйте «Загрузить»",
"noFolderRecursive": "Сбой рекурсивной передачи папки; повторите или используйте «Загрузить»"
"noDragPath": "Система не предоставила путь при перетаскивании. Используйте стрелку → или выбор файлов.",
"noFolderRecursive": "Передача папки не удалась. Повторите или используйте → / выбор файлов.",
"dropFailed": "Перетаскивание не удалось: не удалось прочитать файл. Повторите или используйте стрелки.",
"folderCreated": "Создана папка {{name}}",
"allSkippedExisting": "Все уже существующие файлы пропущены",
"skippedExisting": "Пропущено существующих: {{count}}",
"deleteFailed": "Не удалось удалить",
"deleted": "Удалено"
}
}
+3 -2
View File
@@ -34,7 +34,7 @@
"reopenShell": "Открыть оболочку снова",
"reopening": "Открытие…",
"disconnected": "Отключено",
"disconnectedHint": "SSH-сеанс завершился или сеть упала. Нажмите ниже, чтобы переподключиться.",
"disconnectedHint": "SSH-сеанс завершился или сеть упала. Один клик ниже — и можно продолжать работу.",
"reconnect": "Переподключиться",
"suspended": "Подключение приостановлено",
"suspendedHint": "Долгое отсутствие активности; подключение приостановлено для экономии ресурсов",
@@ -70,7 +70,8 @@
"splitRight": "Разделить справа",
"splitDown": "Разделить вниз",
"maximizePane": "Развернуть текущую панель",
"restoreLayout": "Восстановить разделённый макет"
"restoreLayout": "Восстановить разделённый макет",
"openSftp": "Открыть SFTP"
},
"search": {
"placeholder": "Поиск...",