export default { route: { dashboard: "Дашборд", peer: "Пиры", peerList: "Управление пирами", hysteria: "Hysteria", hysteriaList: "Управление Hysteria", config: "Система", configList: "Настройки системы", monitor: "Мониторинг", monitorSystem: "Системный мониторинг", log: "Логи", logSystem: "Системные логи", logHysteria: "Логи Hysteria", }, login: { title: "HY2XS admin", username: "Логин", password: "Пароль", login: "Войти", capsLockOn: "Caps Lock включён", }, // Требования к учётным данным администратора. Фразы общие для формы входа и // формы смены пароля: требование одно, и второй его формулировки быть не // должно — расхождение здесь означало бы, что оператору обещают разное про // одно и то же поле. // // Обе фразы НАЗЫВАЮТ требование, а не сообщают о его нарушении. Прежние // «Неверный формат логина» и «Некорректное значение» не давали оператору ни // одного способа узнать, что именно от него хотят: набор символов логина // приходит из hy2xs.env, и посмотреть его в панели негде. credentials: { usernameFormat: "Логин: от {min} до {max} символов из набора {charset}", // Фраза называет ОБЕ границы, потому что их две и они в разных единицах. // Пароль из 40 эмодзи укладывается в 64 символа и не укладывается в 72 // байта; сообщение «не длиннее {max} символов» отправило бы оператора // сокращать пароль, отвергнутый не за это. passwordFormat: "Пароль: от {min} до {max} символов и не более {maxBytes} байт в UTF-8, без управляющих символов и U+FEFF", }, dashboard: { stale: "Данные дашборда устарели. Выполняется автоматическая повторная попытка...", topPeers24h: "Топ пиров (24ч)", topPeersRange: "Топ пиров ({range})", refreshFailed: "Не удалось обновить данные дашборда", cpu: "CPU", ram: "RAM", disk: "Диск", peers: "Пиры", onlinePeers: "Пиры онлайн", onlineDevices: "Устройства онлайн", todayDownload: "Скачано за сегодня", todayUpload: "Отдано за сегодня", trafficChart: "Трафик и системные метрики", trafficSeriesChart: "Трафик: download/upload", noTrafficData: "Нет данных трафика за выбранный период", systemSeriesChart: "Система: CPU/RAM", download: "Скачано", upload: "Отдано", total: "Всего", serviceStateLabel: "Служба Hysteria", // Три состояния, а не два: «спросить systemd не удалось» — это не // «служба остановлена», и действия оператора у них разные. serviceState: { active: "Работает", inactive: "Остановлена", unknown: "Состояние неизвестно", }, trafficApiLabel: "Traffic Stats API", trafficApiReachable: "Доступен", trafficApiUnreachable: "Недоступен", security: { hysteriaStopped: "Hysteria2 остановлена", hysteriaStateUnknown: "Состояние службы Hysteria2 неизвестно: systemd не ответил", trafficApiUnavailable: "Traffic API недоступен", }, error: { trafficApiUnavailable: "Traffic API недоступен", collectorStale: "Данные коллектора устарели", }, }, admin: { changePasswordTitle: "Смена пароля", oldPassword: "Старый пароль", newPassword: "Новый пароль", forcePasswordChangeNotice: "Перед продолжением необходимо сменить пароль.", changedSuccess: "Пароль обновлён", }, navbar: { logout: "Выйти", }, common: { id: "ID", createTime: "Создано", operate: "Действия", edit: "Изменить", delete: "Удалить", deleted: "Статус", all: "Все", enable: "Включено", disable: "Отключено", search: "Поиск", reset: "Сброс", add: "Добавить", confirm: "Подтвердить", cancel: "Отмена", close: "Закрыть", copySuccess: "Скопировано", nodeUrl: "URL узла", nodeQrCode: "QR узла", resetTraffic: "Сбросить трафик", import: "Импорт", export: "Экспорт", save: "Сохранить", update: "Обновить", downloadSuccess: "Загрузка завершена", // Фразы `wait`, `enableSuccess` и `disableSuccess` удалены вместе с // операциями, которых у панели нет: запуском, остановкой и сменой версии // Hysteria. Жизненный цикл принадлежит systemd и оркестратору. success: "Готово", refresh: "Обновить", yes: "Да", no: "Нет", securityRisk: "Риски безопасности", required: "Обязательное поле", warning: "Внимание", fileFormatUnsupported: "Формат файла не поддерживается", fileTooLarge: "Файл слишком большой, не более 2 МБ", weekLater: "Через неделю", monthLater: "Через месяц", yearLater: "Через год", hourLater: "Через час", dayLater: "Через день", deleteConfirm: "Удалить пир «{username}»?", resetTrafficConfirm: "Сбросить трафик для пира?", invalid: "Некорректное значение", switchLanguageSuccess: "Язык переключён", logoutConfirm: "Выйти из системы?", sessionExpired: "Сессия истекла. Войдите снова, чтобы продолжить.", signInRequired: "Требуется вход в панель.", signIn: "Войти", systemError: "Системная ошибка", networkError: "Сервер не отвечает. Проверьте соединение с панелью.", }, // Причины отказа API. // // Ключи строятся из КОДА ответа, а не из его текста: панель не разбирает // человеческие сообщения сервера. Числа правил приходят в параметрах, поэтому // второй копии границ длины здесь нет — она неизбежно разошлась бы с // серверной. error: { field: { name: "Имя пира", secret: "Секрет", remark: "Комментарий", quotaBytes: "Квота", expiresAt: "Срок действия", maxDevices: "Лимит устройств", disabled: "Состояние", bannedUntil: "Блокировка до", file: "Файл", id: "Идентификатор", username: "Логин", pass: "Пароль", oldPassword: "Старый пароль", newPassword: "Новый пароль", key: "Ключ настройки", value: "Значение настройки", numLine: "Число строк", pageNum: "Номер страницы", pageSize: "Размер страницы", }, code: { required: "«{field}»: поле обязательно", min: "«{field}»: значение не может быть меньше {min}", max: "«{field}»: значение не может быть больше {max}", min_length: "«{field}»: не короче {min} символов", max_length: "«{field}»: не длиннее {max} символов", len: "«{field}»: длина должна быть ровно {len}", oneof: "«{field}»: допустимые значения — {values}", gt: "«{field}»: значение должно быть больше {gt}", peer_name: "«{field}»: от {min} до {max} символов из набора {charset}. Пробелы, кириллица и знаки / : ; . недопустимы", // Сервер присылает границы и набор в params — фраза называет требование // целиком. Прежнее «недопустимые символы» вдобавок описывало этими же // словами отказ по ДЛИНЕ: правило одно, и оно проверяет и то, и другое. credential_format: "«{field}»: от {min} до {max} символов из набора {charset}", admin_password_format: "«{field}»: от {min} до {max} символов и не более {maxBytes} байт в UTF-8, без управляющих символов и U+FEFF", rule_violated: "«{field}»: значение не подходит", validation_failed: "Проверка данных не пройдена", body_invalid: "Запрос не разобран: проверьте формат и типы полей", peer_name_taken: "Пир с таким именем уже существует", peer_name_reserved: "Это имя зарезервировано за пиром установщика", peer_bootstrap_identity_locked: "Имя и секрет пира установщика продублированы в файле на сервере и не меняются через панель. Ненужный bootstrap-пир следует удалить целиком.", invalid_credentials: "Неверный логин или пароль", // Фраза обязана начинаться с того, что УЖЕ СДЕЛАНО: иначе оператор // прочитает её как «операция не выполнена» и повторит действие, которое // на самом деле применилось. // // И она НЕ называет конкретную операцию. Этим кодом отчитываются // отключение пира, временная блокировка, смена секрета, урезание квоты и // срока, снижение лимита устройств, импорт партии и удаление пира; // прежнее «новые подключения пира запрещены» было верно ровно для // первого случая, а для удалённого пира — просто бессмысленно. peer_disconnect_failed: "Изменения сохранены, но завершить связанные активные сессии не удалось: Traffic Stats API Hysteria недоступен. Установленные соединения могут работать до переподключения клиента. Проверьте состояние службы hysteria-server и повторите действие.", import_file_extension: "Импорт принимает только файлы .json", unauthorized: "Требуется вход в панель", session_expired: "Сессия истекла", token_invalid: "Сессия недействительна", account_disabled: "Учётная запись отключена", }, }, sidebar: { // {brand} подставляется ссылкой, поэтому фраза обязана остаться одной // строкой с одним подстановочным местом. developedBy: "Разработано во {brand}", }, info: { expireTime: "г-М-д Ч:м:с", greeting1: "Доброе утро,", greeting2: "Доброе утро,", greeting3: "Добрый день,", greeting4: "Добрый вечер,", greeting5: "Доброй ночи,", }, peer: { name: "Пир", remark: "Комментарий", secret: "Секрет", form: { namePlaceholder: "client-01", // Набор символов назван так, как его понимает service.IsValidPeerName. // Прежняя формулировка «латиница, цифры и дефис» осталась от правила, // действовавшего до EX-03: подсказка обещала более узкий набор, чем // сервер принимает, и оператор не имел причин пробовать остальные. nameHint: "Идентификатор пира: от 6 до 32 символов — латиница, цифры и знаки !@#$%^&*()_+-=. Имя попадает в автогенерируемый секрет и показывается клиенту как название профиля.", remarkPlaceholder: "ноутбук", remarkHint: "Необязательная пометка для оператора. Клиент её не видит.", secretPlaceholder: "оставьте пустым — сгенерируем", secretHint: "Пароль подключения клиента. Если оставить поле пустым, секрет будет сгенерирован автоматически. При ручном вводе: от 6 до 128 символов.", quotaHint: "Лимит трафика в байтах. Укажите -1 для безлимита.", }, maxDevices: "Лимит устройств", disabled: "Отключён", status: "Статус", traffic: "Трафик", devices: "Устройства", lastConnectionAt: "Последнее подключение", overview: "Обзор", authId: "Auth ID", clientUri: "URI клиента", clientQr: "QR клиента", unlimited: "Безлимит", secretRotateConfirm: "Сменить секрет пира? Текущие клиентские конфиги перестанут работать до обновления.", exportSettings: "Экспорт настроек", exportBackup: "Резервная копия", exportSettingsHint: "Список пиров без секретов. Безопасно хранить и передавать, но после импорта на другой сервер пирам будут выданы новые секреты, и прежние клиентские ссылки работать не будут.", exportBackupConfirm: "Резервная копия содержит ДЕЙСТВУЮЩИЕ секреты подключения всех пиров в открытом виде: любой, кто получит этот файл, получит доступ к VPN. Храните его как пароль и удалите, когда перенос завершён. Только такая копия сохраняет работоспособность существующих клиентских ссылок после переноса на другой сервер.", exportBackupConfirmButton: "Понимаю, скачать копию с секретами", quota: "Квота", download: "Скачано", upload: "Отдано", expireTime: "Срок действия", kickUtilTimeLast: "Осталось офлайн", kickUtilTime: "Отключить до", deviceNo: "Лимит устройств", onlineStatus: "Онлайн", online: "Онлайн", offline: "Офлайн", onlineUnknown: "Онлайн неизвестен", onlineUnavailable: "Картина подключений сейчас недоступна", onlineUnavailableHint: "Traffic Stats API Hysteria не ответил, поэтому онлайн и число устройств неизвестны. Сохранённое состояние пиров в таблице верно.", device: "Устройства", createdAt: "Создан", bannedUntil: "Блокировка до", totalTraffic: "Суммарный трафик", copyUri: "Копировать URI", more: "Ещё", loginAt: "Последний вход", conAt: "Последнее подключение", createTime: "Создано", releaseSuccess: "Ограничение снято", kick: "Временно заблокировать", kickTip: "Прервать соединение и не пускать до истечения срока", kickConfirm: "Заблокировать пир «{name}» до {until}? Текущее соединение будет разорвано, новые подключения до этого времени приниматься не будут.", kickSuccess: "Пир временно заблокирован", releaseKick: "Снять блокировку", releaseKickTip: "Снять временную блокировку досрочно", // Отключение и временная блокировка — разные механизмы: у блокировки есть // срок, который истекает сам, отключение снимается только руками. enablePeer: "Включить пир", disablePeer: "Отключить пир", disableConfirm: "Отключить пир «{name}»? Текущее соединение будет разорвано, а новые подключения запрещены, пока пир не будет включён обратно.", enableSuccess: "Пир включён", disableSuccess: "Пир отключён", }, config: { orchestratorManaged: "Управляется hy2xs-orchestrator reconfigure", resetTrafficCron: "Расписание сброса трафика", resetTrafficCronTip: "Cron-выражение для планового сброса трафика. Пустое значение отключает автоматический сброс", resetTrafficMonth: "Раз в месяц, в полночь первого дня", resetTrafficWeek: "Раз в неделю, в полночь между субботой и воскресеньем", }, // Раздел `monitor` удалён вместе со своим единственным потребителем — // плашкой «Статус Hysteria2» на странице конфигурации. Состояние службы // имеет три значения, а не два, и его фразы живут в `dashboard.serviceState`: // «остановлена» и «неизвестно» — разные ответы оператору. log: { numLine: "Количество строк", level: "Уровень", message: "Сообщение", time: "Время", exportFailed: "Не удалось выгрузить журнал", }, errorPage: { back: "Назад", oops: "Упс!", forbiddenTitle: "У вас нет прав для доступа к этой странице", forbiddenGo: "Или вы можете перейти:", backHome: "На главную", forbiddenImageAlt: "Девочка уронила своё мороженое.", notFoundHeadline: "Вебмастер сообщил, что вам нельзя на эту страницу...", notFoundInfo: "Проверьте правильность URL или нажмите кнопку ниже, чтобы вернуться на главную страницу.", notFoundBackHome: "На главную", }, hysteria: { hysteria2Version: "Версия Hysteria2", ownedByOrchestrator: "Конфигурацией Hysteria владеет установщик", ownedByOrchestratorHint: "Панель показывает файл /etc/hysteria/config.yaml только на чтение. Изменения вносит `hy2xs-orchestrator reconfigure`.", // Разделы production-профиля. Экран показывает то, что записано в файле, // и отдельно называет секции сверх профиля: универсального редактора всех // возможностей Hysteria здесь нет намеренно — конфиг создаёт оркестратор. listen: "Адрес прослушивания", auth: "Авторизация пиров", tls: "TLS", obfs: "Маскировка", quic: "Параметры QUIC", bandwidth: "Полоса", congestion: "Congestion control", trafficStats: "Traffic Stats API", notSet: "не задано", sectionMissing: "Секция в конфигурации отсутствует", tlsMissing: "Ни tls, ни acme в конфигурации не заданы", valuesHidden: "значения не показываются", secretSet: "задан", secretMissing: "не задан", obfsPasswordHint: "выдаётся в клиентской ссылке пира", driftTitle: "Расхождение конфигурации", driftHint: "В файле есть секции вне production-профиля HY2XS. Оркестратор их не создаёт и не поддерживает: скорее всего, конфигурацию правили вручную.", trafficStatsMissing: "Traffic Stats API не настроен", trafficStatsMissingHint: "Без секции trafficStats не работают лимит устройств, учёт трафика и принудительное отключение: панель обращается к Hysteria только через этот API.", // Две разные новости об одном поле, и объединять их нельзя: первая // означает, что доступ пиров уже не работает, вторая — что API работает, но // опубликован шире необходимого. Прочие адреса loopback (127.0.0.5 и // подобные) относятся к ПЕРВОЙ: слушатель на конкретном адресе соединения // на 127.0.0.1 не принимает. trafficStatsUnreachable: "панель не может обратиться по этому адресу: Traffic Stats API запрашивается строго через 127.0.0.1. Пока адреса расходятся, лимит устройств, учёт трафика и принудительное отключение не работают", trafficStatsWildcard: "API доступен, но слушает все интерфейсы. Production-профиль HY2XS использует 127.0.0.1: выполните hy2xs-orchestrator reconfigure", }, };