feat(i18n): add multi-language support (ja/ko/vi/ru/zh-TW) with system auto-detection

- Extend AppLanguage to 7 locales: zh-CN, zh-TW, en, ja, ko, vi, ru
- Auto-detect system language via navigator.languages; fallback to English
- Add BCP47 locale mapping for date formatting and sorting
- Create 50 translation JSON files (5 languages × 10 namespaces, 1137 keys each)
- Update language selector with native language names
- Extend locale key parity test to cover all 7 languages (70 assertions)
- Add VNC/RDP desktop, serial terminal, AI agent, port forwarding, and SFTP features
- Add credential encryption, host metrics, session backup crypto, and zmodem bridge
- Update Rust backend: desktop services, host metrics, SSH/SFTP enhancements
This commit is contained in:
Logan.Li committed 2026-08-12 00:07:48 +08:00
1 parent fea6d8db04
commit b0d0e3a5f3
301 files changed
+28002 -2705

No files matched your search

+237
View File
@@ -0,0 +1,237 @@
{
"sidebar": {
"quickConnect": "Быстрое подключение",
"quickConnectShort": "Подключить",
"newSession": "Новый сеанс",
"newSessionShort": "Новый",
"searchPlaceholder": "Поиск сеансов...",
"batchMode": "Пакетные действия",
"exitBatchMode": "Выйти из пакетного режима",
"batchSelected": "Выбрано SSH: {{count}}",
"batchExcludedDesktop": " · RDP/VNC исключены",
"selectAllVisible": "Выбрать все видимые",
"deselectAllVisible": "Снять выделение видимых",
"batchExec": "Пакетное выполнение",
"settings": "Настройки",
"resizeHint": "Перетаскивайте для изменения размера; двойной клик — свернуть"
},
"tree": {
"selectSession": "Выберите сеанс",
"noMatchTitle": "Совпадающие сеансы не найдены",
"noMatchDesc": "Попробуйте другое ключевое слово",
"emptyTitle": "Сеансов пока нет",
"emptyDesc": "Быстрое подключение или создайте сеанс; двойной клик открывает терминал",
"emptyAction": "Новый сеанс"
},
"contextMenu": {
"blank": {
"newGroup": "Новая группа",
"newSession": "Новый сеанс"
},
"group": {
"newSessionHere": "Новый сеанс здесь",
"newGroup": "Новая группа",
"renameGroup": "Переименовать группу",
"deleteEmptyGroup": "Удалить пустую группу"
},
"session": {
"open": "Открыть",
"edit": "Изменить",
"duplicate": "Дублировать",
"delete": "Удалить"
}
},
"groups": {
"default": "По умолчанию",
"new": {
"title": "Новая группа",
"message": "Введите имя группы",
"placeholder": "напр. Продакшен",
"confirm": "Создать"
},
"rename": {
"title": "Переименовать группу",
"message": "Переименовать «{{name}}» в",
"placeholder": "Имя группы",
"confirm": "Сохранить"
},
"deleteBlocked": {
"title": "Невозможно удалить",
"message": "В группе «{{name}}» ещё есть сеансы. Сначала переместите или удалите их.",
"confirm": "Понятно"
},
"newOption": "Новая группа…",
"hint": "Используется группа по умолчанию, если не выбрать или не создать другую"
},
"deleteSession": {
"title": "Удалить сеанс",
"message": "Удалить сеанс «{{name}}»?",
"confirm": "Удалить"
},
"welcome": {
"tagline": "Удалённый терминал · SFTP · Проброс портов",
"sectionStart": "Начало работы",
"sectionDesktop": "Удалённый рабочий стол",
"sectionRecent": "Недавние сеансы",
"quickConnect": {
"title": "Быстрое подключение",
"desc": "Разовое SSH; не сохраняется в библиотеке"
},
"newSession": {
"title": "Новый сеанс",
"desc": "Сохраняйте хосты; двойной клик в боковой панели для подключения"
},
"localShell": {
"title": "Локальная оболочка",
"selectShell": "Выберите оболочку",
"default": "По умолчанию"
},
"rdp": {
"title": "Удалённый рабочий стол RDP",
"desc": "Windows · NLA / CredSSP"
},
"vnc": {
"title": "Удалённый рабочий стол VNC",
"desc": "RFB · кроссплатформенный графический сеанс"
},
"recentEmpty": {
"title": "Сохранённых сеансов нет",
"desc": "Попробуйте быстрое подключение или создайте сеанс для повторного использования"
}
},
"launcher": {
"searchPlaceholder": "Поиск хостов, групп или вкладок"
},
"quickConnect": {
"title": "Быстрое подключение",
"target": "Цель",
"targetPlaceholder": "root@192.168.1.10:22",
"targetHint": "Поддерживаются host, user@host, user@host:port",
"password": "Пароль",
"passwordOptional": "Необязательно · оставьте пустым при использовании ключа",
"connect": "Подключить",
"errorInvalidTarget": "Введите хост, напр. root@192.168.1.10 или opsbase.cn:5555",
"errorConnectFailed": "Сбой подключения"
},
"dialog": {
"create": "Новый сеанс",
"edit": "Изменить сеанс",
"subtitle": "Цель одной строкой · расширенные параметры свернуты",
"saving": "Сохранение…",
"save": "Сохранить",
"createAndSave": "Создать и сохранить",
"basic": "Основные",
"login": "Вход",
"advanced": "Дополнительно",
"advancedCollapsed": "Прокси · jump-хост · теги",
"collapse": "Свернуть",
"protocol": "Протокол",
"displayName": "Отображаемое имя (необязательно)",
"displayNamePlaceholder": "По умолчанию user@host или vnc:host",
"host": "Хост",
"hostPlaceholder": "192.168.1.10",
"hostHint": "IP или имя хоста; также принимается host:port",
"port": "Порт",
"username": "Имя пользователя",
"usernamePlaceholderRdp": "Administrator",
"usernamePlaceholderSsh": "root",
"width": "Ширина",
"height": "Высота",
"group": "Группа",
"authPassword": "Пароль",
"authKey": "Ключ",
"password": "Пароль",
"domain": "Домен (необязательно)",
"domainPlaceholder": "Редко требуется, кроме доменных учётных записей",
"keySource": "Источник ключа",
"keyFile": "Файл",
"keyText": "Текст",
"privateKeyPath": "Путь к закрытому ключу",
"privateKeyText": "Текст закрытого ключа",
"privateKeyTextHint": "Вставленные ключи шифруются на диске как пароли",
"pickKeyFile": "Обзор…",
"pickKeyDialogTitle": "Выберите файл закрытого ключа",
"allFilesFilter": "Все файлы",
"passphrase": "Парольная фраза ключа (необязательно)",
"passphrasePlaceholder": "Необязательно",
"tags": "Теги",
"tagsPlaceholder": "prod, web",
"startupCommand": "Команда при запуске (необязательно)",
"startupCommandPlaceholder": "Выполнить после подключения, напр. cd /var/log",
"proxy": "Прокси",
"proxyNone": "Нет",
"proxyHostPlaceholder": "Хост прокси",
"proxyPortPlaceholder": "Порт",
"proxyUserPlaceholder": "Имя пользователя (необязательно)",
"proxyPasswordPlaceholder": "Пароль (необязательно)",
"jumpHost": "Jump-хост (необязательно)",
"jumpNone": "Без jump-хоста",
"jumpNoneAlt": "Без jump-хоста",
"jumpHint": "Сохранённый сеанс можно использовать как jump-хост"
},
"batch": {
"title": "Пакетное выполнение",
"subtitle": "Выбрано {{selected}} · параллелизм {{concurrency}} · результаты отбрасываются при закрытии вкладки",
"dialogTitle": "Пакетная команда",
"dialogDesc": "Выбрано хостов {{selected}}/{{total}} · макс. параллелизм {{concurrency}}",
"targetCount": "Целей: {{count}}",
"runningProgress": "Выполняется {{completed}}/{{total}}",
"execute": "Запустить на {{count}} хостах",
"executeAgain": "Запустить снова",
"success": "Успешно: {{count}}",
"failed": "Сбой: {{count}}",
"command": "Удалённая команда",
"commandPlaceholder": "напр. uptime",
"targets": "Целевые хосты",
"filterPlaceholder": "Имя, IP, группа или тег",
"filterAria": "Фильтр хостов",
"noMatchHosts": "Совпадающие хосты не найдены",
"resultMatrix": "Матрица результатов",
"resultMatrixHint": "Хост × статус × вывод",
"emptySelection": "Выберите цели слева или откройте из пакетного режима боковой панели с выделением",
"noOutput": "(нет вывода)",
"errorReadConfig": "Не удалось прочитать конфигурацию сеанса",
"errorConnectFailed": "Сбой подключения",
"errorExecFailed": "Сбой выполнения команды",
"results": "Результаты",
"resultsHint": "Вывод хранится только в этом диалоге",
"ctrlEnterHint": "Ctrl/⌘ + Enter для запуска",
"executeTo": "Запустить на {{count}} хостах",
"selectFromLeft": "Выберите цели слева"
},
"importExport": {
"exportTitle": "Экспорт сеансов",
"exportDefaultPath": "opsxshell-sessions.bak",
"exportFilterBak": "Резервная копия OpsXShell",
"exportFilterJson": "JSON",
"exportSuccess": "Экспортировано сеансов: {{count}} в:\n{{path}}",
"exportWriteFailed": "Не удалось записать файл экспорта",
"exportFailed": "Сбой экспорта сеансов",
"exportFailedDetail": "Сбой экспорта сеансов: {{message}}",
"exportPasswordTitle": "Задайте пароль резервной копии",
"exportPasswordMessage": "Файл экспорта зашифрует группы хостов, закрытые ключи, пароли и другие секреты. Выберите пароль резервной копии.",
"exportPasswordPlaceholder": "Введите пароль резервной копии",
"exportPasswordConfirmTitle": "Подтвердите пароль резервной копии",
"exportPasswordConfirmMessage": "Введите пароль резервной копии ещё раз для подтверждения.",
"exportPasswordConfirmPlaceholder": "Повторите пароль резервной копии",
"exportPasswordMismatch": "Пароли не совпадают",
"exportPasswordRequired": "Для экспорта требуется пароль резервной копии",
"importTitle": "Импорт сеансов",
"importFilterBak": "Резервная копия OpsXShell",
"importFilterJson": "JSON",
"importFilterSshConfig": "SSH Config",
"importFilterAll": "Все файлы",
"readFileFailed": "Не удалось прочитать файл",
"readFileFailedDetail": "Не удалось прочитать файл: {{message}}",
"importPasswordTitle": "Введите пароль резервной копии",
"importPasswordMessage": "Этот файл зашифрован. Введите пароль, заданный при экспорте, чтобы импортировать группы хостов и учётные данные.",
"importPasswordPlaceholder": "Введите пароль резервной копии",
"importPasswordRequired": "Для импорта зашифрованной резервной копии требуется пароль",
"importPasswordInvalid": "Неверный пароль резервной копии или файл повреждён",
"noSessionsFound": "Импортируемые сеансы не найдены",
"noValidSessions": "В файле нет корректных записей сеансов",
"importSuccess": "Импортировано сеансов: {{created}}/{{total}}",
"importFailed": "Сбой импорта сеансов",
"importFailedDetail": "Сбой импорта сеансов: {{message}}"
}
}