# Тексты интерфейса бота. Ключи читает app/texts.py; отсутствующий ключ — ошибка при старте. menu: commit: "📝 Коммит" status: "🩺 Статус" logs: "📜 Логи" alerts: "🚨 Алерты" members: "👥 Участники" back: "⬅️ Назад" cancel: "✖️ Отмена" placeholder: "Выберите раздел" start: welcome: |- Привет! Я tgit — git в телеграме. 📝 Коммит — изменить файлы и запушить 🩺 Статус — контейнеры и их health 📜 Логи — хвост, свой grep или вопрос агенту 🚨 Алерты — падения, перегрузка, список no_access: "Доступ к боту — только по приглашению. Попросите QR-код у администратора." invite_rejected: "Приглашение недействительно: истекло, уже использовано или ссылка неверная. Попросите новое у администратора." help: text: |- /menu — главное меню из любого места /help — эта справка Разделы открываются кнопками внизу. В любом шаге есть «⬅️ Назад» и «✖️ Отмена». section: cancelled: "Отменено. Главное меню — кнопками внизу." members: title: "👥 Участники: {count}" line: "{icon} {name}{username} · с {joined} · был(а) {seen}" never: "—" invite: "➕ Пригласить" revoke: "🚫 {name}" confirm: "Отозвать доступ у {name}? Его черновик удалится, кнопки перестанут работать." confirm_yes: "Да, отозвать" confirm_no: "Нет" revoked: "Доступ отозван: {name}." not_revoked: "Нельзя отозвать: администратор или уже без доступа." invite_caption: |- Приглашение в tgit — отсканируйте QR или откройте ссылку: {link} Одноразовое, действует до {expires} UTC. demo_invite: "🎤 Демо-QR" demo_invite_caption: |- 🎤 Демо-доступ к tgit — отсканируйте QR или откройте ссылку: {link} До {max_uses} человек, действует до {expires} UTC. revoke_guests: "🧹 Отключить гостей демо" confirm_guests: "Отключить всех, кто вошёл по демо-QR, и погасить сам QR? Их черновики удалятся." guests_revoked: "Гости демо отключены: {count}. Демо-QR больше не работает." commit: title: "📝 Коммит · /{path}" empty: "Папка пуста." page: "Стр. {page} из {pages}" up: "⬆️ Вверх" prev: "◀️" next: "▶️" dir_button: "📁 {name}" file_button: "📄 {name} · {size}" new_file: "➕ Новый файл" new_dir: "📁 Новая папка" file_card: |- 📄 /{path} Размер: {size} last_commit: |- Последний коммит: {sha} · {author} · {date} {subject} no_commits: "Коммитов по файлу нет." view: "👁 Показать" replace: "✏️ Заменить" delete: "🗑 Удалить" to_dir: "⬅️ К папке" soon: "Будет в следующей версии." unavailable: "Репозиторий загрузок недоступен, попробуйте через минуту." sent_as_file: "Файл большой или бинарный — отправил документом." ask_file_name: "Имя нового файла в /{dir} (латиница, цифры, «.», «_», «-»):" ask_dir_name: "Имя новой папки в /{dir}:" bad_name: "Недопустимое имя: латиница, цифры, «.», «_», «-», до 100 символов. Пришлите другое." exists: "Такое имя уже есть в этой папке. Пришлите другое." protected: "Это служебный путь репозитория — из бота его менять нельзя." ask_content: "Пришлите содержимое /{path} — текстом сообщения или файлом (до 100 КБ)." too_big: "Больше 100 КБ — не принимаю." binary: "Похоже на бинарный файл — принимаю только текст." encoding: "Файл не в кодировке UTF-8." syntax: "⚠️ Синтаксическая ошибка, строка {line}: {message}" fix: "✏️ Исправить" force: "Всё равно добавить" added: "🆕 /{path} — добавлен в черновик." modified: "✏️ /{path} — в черновике: +{added} −{removed}." deleted: "🗑 /{path} — в черновике на удаление." draft_button: "🧾 Черновик · {count}" draft_title: "🧾 Черновик · изменений: {count}" draft_empty: "Черновик пуст." unstage: "↩️ убрать {name}" draft_full: "В черновике уже 50 изменений — запушьте или уберите лишнее." in_draft_new: "В черновике: новый файл." in_draft_modified: "В черновике: изменён." in_draft_deleted: "В черновике: удалён." mark_new: "🆕 " mark_modified: "✏️ " mark_deleted: "🗑 " op_add: "A" op_modify: "M" op_delete: "D" message_button: "💬 Сообщение коммита" message_current: "💬 {message}" message_missing: "💬 Сообщение коммита не задано." ask_message: "Сообщение коммита: до 200 символов, первая строка — до 72." bad_message: "Сообщение: 1–200 символов, первая строка — до 72. Пришлите другое." push_button: "🚀 Push" commit_button: "🚀 Закоммитить · {count}" edit_message: "✏️ Изменить сообщение" push_confirm: |- Запушить в main? {summary} 💬 {message} push_go: "✅ Запушить" pushing: "⏳ Пушу…" pushed: "✅ Запушено: {sha} — {message}" conflict: |- ⚠️ Пока вы готовили черновик, в main изменились те же файлы: {paths} Перезаписать их вашей версией? overwrite: "Перезаписать" push_empty: "В main уже всё так же — коммитить нечего. Черновик очищен." push_limit: "Лимит — 20 пушей в час. Попробуйте позже." push_failed: "Не удалось запушить: репозиторий недоступен. Черновик сохранён — попробуйте позже." pipeline: ci_pending: "CI: ⏳ ждёт раннер" ci_running: "CI: ⏳ идёт проверка" ci_success: "CI: ✅ пройден" ci_failed: "CI: ❌ упал: {step}" ci_timeout: "CI: ⌛ не завершился за {minutes} мин" cd_waiting: "CD: ⏸ {services} — после CI" cd_running: "CD: 🚀 разворачиваю {services}…" cd_success: "CD: 🚀 {services} развёрнуто" cd_failed: "CD: ⛔ не развёрнуто: {services}" cd_not_run: "CD: ⏭ не запускался — CI не прошёл" cd_superseded: "CD: ⏭ эти сервисы развернёт более новый пуш" notify_ci_ok: |- ✅ CI пройден · {sha} {url} notify_ci_failed: |- ❌ CI упал на шаге {step} · {sha} Сервисы не развёрнуты. {url} notify_ci_timeout: "⌛ CI не завершился за {minutes} мин · {sha} — сервисы не развёрнуты." notify_cd_ok: "🚀 CD: сервисы {services} развёрнуты, ревизия {sha}" deploy_failed: |- ⛔ {service} не развёрнут: {reason} Работает прежняя версия, если она была. removed: "🗑 {service} удалён из репозитория — остановлен на хосте" log_file: "Полный лог CI" status: title: "🩺 Статус · {count} конт. · {time} UTC" line: "{light} {name} · {state}" empty: "Контейнеров tgit не найдено." unavailable: "Docker недоступен, попробуйте через минуту." card: |- {light} {name} Сервис: {service} · образ: {image} Состояние: {state} · health: {health} Аптайм: {uptime} · рестартов: {restarts} CPU: {cpu} · RAM: {mem} из {limit} ({mem_pct}) Ревизия: {sha} · деплой: {deployed} none: "—" no_limit: "без лимита" check_button: "🩺 Запустить проверки" refresh: "🔄 Обновить" to_list: "⬅️ К списку" to_card: "⬅️ К контейнеру" checking: "🩺 Проверяю контейнеры…" report_title: "🩺 Проверки · без ошибок {ok} из {total} · {time} UTC" report_container: "{light} {name}" report_line: " {verdict} {title}{detail}" check_running: "запущен" check_health: "Docker health" check_restarts: "рестарты за 5 мин не растут" check_cpu: "CPU ниже порога" check_mem: "RAM ниже порога" check_log_errors: "нет ошибок в логе за 5 мин" check_http: "HTTP-проба" days: "д" hours: "ч" minutes: "мин" seconds: "с" restart: "♻️ Перезапустить" restarted: "♻️ Перезапущен" restart_failed: "Не удалось перезапустить: {reason}" logs: title: "📜 Логи · выберите контейнер" empty: "Контейнеров tgit не найдено." unavailable: "Docker недоступен, попробуйте через минуту." container: |- 📜 Логи · {name} Хвост, свой grep по последним 24 ч или вопрос агенту. tail: "{n} строк" since: "за {period}" minutes: "{n} мин" hours: "{n} ч" grep_button: "🔎 Свой grep" agent_button: "🤖 Спросить агента" agent_ask: |- 🤖 Вопрос агенту — по логу {name} или другого контейнера, если назовёте его. Например: «когда были ошибки подключения за сутки?», «сколько таймаутов за час?» Агент сам построит grep и покажет его. Осталось вопросов на сегодня: {left} из {quota}. agent_off: "Агент не настроен: боту нужен LLM (LLM_PROVIDER)." agent_thinking: "🤖 Строю запрос…" agent_refused: "С таким запросом помочь не могу. Спросите о содержимом логов: ошибки, таймауты, падения, подключения." agent_quota: "Лимит — {quota} вопросов агенту в сутки, он исчерпан. Свой grep работает без лимита." agent_unavailable: "Агент сейчас недоступен. Попробуйте позже или воспользуйтесь своим grep." agent_not_understood: "Не получилось построить запрос. Переформулируйте вопрос или воспользуйтесь своим grep." head_agent: "🤖 {command}\n{name} · за {period} · совпадений: {matched}" to_list: "⬅️ К контейнерам" to_container: "⬅️ К контейнеру" ask_grep: |- 🔎 Выражение grep — ищу по логу {name} за 24 ч. Например: -i -C 2 error Флаги: -i без регистра, -v кроме, -E расширенный regex, -c только число, -C N контекст. head_tail: "📜 {name} · {scope} · строк: {lines}" head_grep: "🔎 {command}\n{name} · за 24 ч · совпадений: {matched}" nothing: "Пусто." truncated: "Показаны первые {n} строк." partial: "Поиск остановлен по времени — просмотрена часть лога." skipped: "Пропущено строк из-за тяжёлого шаблона: {n}." sent_as_file: "Результат длинный — отправил файлом." err_empty: "Нужен шаблон, например: -i error" err_quotes: "Не закрыта кавычка." err_flag: "Флаг {arg} не поддерживается. Можно: -i -v -E -c -C N" err_context: "После -C нужно число от 0 до 10." err_extra: "Нужен один шаблон, а «{arg}» лишнее. Шаблон с пробелами — в кавычках." err_too_long: "Шаблон длиннее 200 символов." err_regex: "Шаблон не разбирается: {arg}" alerts: kinds: crash: "💥 Краш" oom: "🧠 OOM" restart_loop: "🔁 Цикл рестартов" unhealthy: "🩺 Unhealthy" overload: "🔥 Перегрузка" deploy_refused: "⛔ Не запустился после деплоя" cd_failed: "📦 CD упал" disk_low: "💾 Мало места на хосте" dependency_down: "🔌 Зависимость недоступна" agent_attack: "🛡 Атака через агента" details: crash: "Контейнер вышел с кодом {exit_code}." oom: "Убит по памяти (OOM), код {exit_code}." restart_loop: "{restarts} рестартов за {window_min} мин." unhealthy: "Health-check не проходит." overload: "CPU {cpu_pct}% · RAM {mem_pct}% от лимита — дольше минуты." deploy_refused: "{detail}" cd_failed: "{detail}" disk_low: "Свободно {free_pct}% диска." dependency_down: "Не отвечает с {since}." agent_attack: "{who} · отсечено: {stage} · {reason}" message: |- {title} · {service} {detail} 🕒 {opened} UTC{repeats} repeats: " · повторов: {count}" resolved: "✅ Восстановлено · {title} · {service} · длилось {duration}" logs_button: "📜 Логи" status_button: "🩺 Статус" list_button: "📋 Список алертов" list_title: "🚨 Алерты · активных: {active} · всего за 7 дней: {total}" list_empty: "За 7 дней алертов не было." list_line: "{mark} {title} · {service} · {time}{repeats}" active_mark: "🔴" resolved_mark: "✅" page: "Стр. {page} из {pages}" prev: "◀️" next: "▶️" card: |- {title} · {service} {detail} Начало: {opened} UTC Конец: {resolved} Повторов: {count} ongoing: "продолжается" to_list: "⬅️ К списку" host: "хост" gone: "Алерт не найден."