Files
tiny-forge/web/src/lib/i18n/ru.json
T
alexei.dolgolyov 4707db1c3b feat(observability): event-triggers + log-scan-rules UI + i18n
Operator-facing surfaces for the two backend features:

- /event-triggers — list (filter summary, status pill),
  /event-triggers/new (form with regex validation), and
  /event-triggers/[id] (edit + Send-test + delete) with
  CONFIGURED secret badge + clear-to-rotate flow, ConfirmDialog
  for delete, aria-live regions on async result slots.
- /log-scan-rules — list with scope filter chips and stats panel
  (active tails, RATE-LIMITED, COOLED DOWN, COMPILE ERRORS),
  /log-scan-rules/new (with EntityPicker for workload scope and
  inline RegexTestBox), /log-scan-rules/[id] (edit + server-side
  /test + delete + live RegexTestBox panel).
- web/src/lib/components/RegexTestBox.svelte — reusable
  client-side regex test with sample input + captures display.
- web/src/lib/api.ts — typed wrappers for EventTrigger and
  LogScanRule CRUD + /test + getLogScanStats +
  getEffectiveLogScanRules.
- web/src/routes/+layout.svelte — nav entries for both surfaces.
- web/src/lib/i18n/{en,ru}.json — ~90 keys under observability.*,
  triggers.*, logscan.* namespaces; Russian translations cover
  every key.

Design + a11y polish per a frontend-design review pass: all
boolean inputs use ToggleSwitch, all destructive actions use
ConfirmDialog with confirmVariant="danger" / onconfirm /
oncancel, hand-rolled .btn-primary replaced with global
forge-btn classes, hex colors replaced with var(--*) tokens,
role="alert" on error banners, aria-invalid + aria-describedby
on invalid-regex inputs, aria-busy on async forms, mobile
breakpoints (hide-md columns, .row.three collapsing 3→2→1,
.table-wrap overflow-x).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 22:18:29 +03:00

1532 lines
88 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"app": {
"name": "Tinyforge",
"version": "v0.1"
},
"layout": {
"serviceStatus": "Состояние служб"
},
"health": {
"connected": "подключён",
"disconnected": "отключён",
"rawError": "Технические детали",
"retryNow": "Проверить сейчас"
},
"nav": {
"dashboard": "Панель",
"apps": "Приложения",
"eventTriggers": "Триггеры",
"logScanRules": "Лог-правила",
"projects": "Проекты",
"deploy": "Деплой",
"proxies": "Прокси",
"events": "События",
"settings": "Настройки",
"logout": "Выйти",
"dns": "DNS-записи",
"sites": "Сайты",
"stacks": "Стеки",
"containers": "Контейнеры"
},
"dashboard": {
"title": "Панель управления",
"quickDeploy": "Быстрый деплой",
"totalProjects": "Всего проектов",
"runningInstances": "Запущенных экземпляров",
"failedInstances": "Сбойных экземпляров",
"projects": "Проекты",
"retry": "Повторить",
"noProjects": "Проектов пока нет.",
"addFirst": "Добавьте первый проект",
"loadFailed": "Не удалось загрузить панель",
"staleContainers": "Устаревшие контейнеры",
"unusedImagesWarning": "Неиспользуемые Docker-образы занимают дисковое пространство",
"unusedImages": "неиспользуемых образов",
"staticSites": "Статические сайты",
"totalSites": "Всего сайтов",
"deployedSites": "развёрнуто",
"failedSites": "с ошибкой",
"noSites": "Статических сайтов пока нет.",
"addFirstSite": "Разверните первый сайт",
"viewAllSites": "Все сайты",
"systemHealth": "Состояние системы",
"daemons": "Демоны",
"systemResources": "Системные ресурсы",
"systemResourcesSubtitle": "CPU, память, диск и топ потребителей"
},
"resources": {
"cpuCores": "Ядра CPU",
"memory": "Память",
"running": "Запущено",
"dockerDisk": "Диск Docker",
"workloadUtilization": "Использование нагрузкой",
"windowMinutes": "{n} минут",
"windowHours": "{n} часов",
"noSamples": "Пока нет данных — сбор идёт каждые {interval}с.",
"collectionDisabled": "Сбор статистики отключён. Включите его в Настройках, чтобы заполнить график.",
"diskImages": "Образы",
"diskContainers": "Контейнеры",
"diskVolumes": "Тома",
"diskBuildCache": "Кэш сборки",
"reclaimable": "{size} можно освободить",
"topConsumers": "Топ потребителей",
"byCpu": "по CPU",
"byMemory": "по памяти",
"noRunning": "Нет запущенных контейнеров.",
"instance": "экземпляр",
"site": "сайт",
"showHistory": "Показать историю",
"hideHistory": "Скрыть историю",
"cpuSeries": "CPU %",
"memorySeries": "Память %",
"loading": "Загрузка…",
"sectionTitle": "Ресурсы",
"showLogs": "Показать логи",
"hideLogs": "Скрыть логи",
"dockerUnavailable": "Docker недоступен. Проверьте, что демон запущен."
},
"statsSettings": {
"intervalLabel": "Интервал сбора статистики (с)",
"intervalHelp": "Как часто собираются замеры ресурсов. 0 отключает сбор. Диапазон: 5–300с.",
"retentionLabel": "Хранение статистики (часы)",
"retentionHelp": "Как долго хранятся замеры ресурсов. 0 отключает сбор. Диапазон: 0–24ч."
},
"projects": {
"title": "Проекты",
"addProject": "Добавить проект",
"cancel": "Отмена",
"newProject": "Новый проект",
"name": "Название",
"image": "Образ",
"port": "Порт",
"registry": "Реестр",
"created": "Создан",
"view": "Открыть",
"noProjects": "Проекты ещё не настроены.",
"getStarted": "Нажмите «Добавить проект» для начала.",
"createProject": "Создать проект",
"creating": "Создание...",
"healthcheck": "Путь проверки здоровья",
"nameRequired": "Название и образ обязательны.",
"loadFailed": "Не удалось загрузить проекты",
"createFailed": "Не удалось создать проект",
"browseImages": "Обзор образов",
"selectImage": "Выберите образ",
"noImages": "Образы не найдены",
"loadingImages": "Загрузка образов...",
"imageLoadFailed": "Не удалось загрузить образы",
"alreadyAdded": "Уже добавлен",
"portHelpText": "Автоопределение из EXPOSE, если пусто",
"healthcheckHelpText": "Автоопределение из образа, если пусто",
"searchPlaceholder": "Поиск по имени, образу или реестру...",
"noMatchingProjects": "Проекты не найдены."
},
"projectDetail": {
"webhookTitle": "Webhook проекта",
"webhookDesc": "Отправьте POST с image-ссылкой на этот URL из CI — и Tinyforge запустит деплой. Стейдж выбирается по tag_pattern.",
"outgoingWebhookTitle": "Исходящий webhook (проект)",
"outgoingWebhookDesc": "Куда Tinyforge отправляет события деплоя для этого проекта. Стейджи могут переопределить; если нигде не задано — используется глобальная настройка.",
"outgoingFallbackGlobal": "глобальной настройки интеграций",
"notificationUrlLabel": "URL исходящего webhook",
"notificationUrlHelp": "Оставьте пустым для наследования из глобальных настроек. Стейджи могут переопределить.",
"stageNotificationUrlLabel": "URL исходящего webhook (этот стейдж)",
"stageNotificationUrlHelp": "Оставьте пустым для наследования от проекта, затем — из глобальных настроек.",
"stageOutgoingTitle": "Исходящий webhook (стейдж)",
"stageOutgoingDesc": "Куда Tinyforge отправляет события деплоя этого стейджа. Побеждает самый конкретный уровень.",
"stageFallbackLabel": "проектной или глобальной настройки",
"deleteProject": "Удалить проект",
"envVars": "Переменные окружения",
"volumes": "Тома",
"stages": "Стадии",
"noStages": "Для этого проекта не настроены стадии.",
"pattern": "Шаблон",
"autoDeploy": "авто-деплой",
"requiresConfirm": "нужно подтверждение",
"instances": "экземпляров",
"deployNewVersion": "Развернуть новую версию",
"selectTag": "Выберите тег для деплоя",
"loadingTags": "Загрузка тегов...",
"chooseTag": "Выберите тег...",
"enterTag": "Введите тег образа (напр., dev-abc123)",
"registryTag": "Реестр",
"localTag": "Локальный",
"alsoLocal": "Также доступен локально",
"searchTags": "Поиск тегов...",
"deployTag": "Тег",
"deploy": "Развернуть",
"deploying": "Развёртывание...",
"recentDeploys": "Последние деплои",
"noDeployHistory": "Нет истории деплоев для этого проекта.",
"tag": "Тег",
"status": "Статус",
"started": "Начат",
"finished": "Завершён",
"error": "Ошибка",
"noInstancesRunning": "Нет запущенных экземпляров",
"deleteConfirmTitle": "Удалить проект",
"deleteConfirmMessage": "Это безвозвратно удалит проект '{name}' и все его стадии, экземпляры и историю деплоев.",
"loadFailed": "Не удалось загрузить проект",
"deleteFailed": "Не удалось удалить проект",
"deployFailed": "Деплой не удался",
"nameLabel": "Название *",
"imageLabel": "Образ *",
"portLabel": "Порт",
"healthcheckLabel": "Путь проверки",
"saving": "Сохранение...",
"addStage": "Добавить стадию",
"tagPattern": "Шаблон тега",
"tagPatternHelp": "Glob-шаблон (напр., dev-*, v*)",
"maxInstances": "Макс. экземпляров",
"autoDeployLabel": "Авто-деплой",
"enableProxy": "Включить прокси",
"accessListId": "ID списка доступа NPM",
"accessListIdHelp": "Переопределить глобальный список доступа для этого проекта. Очистите, чтобы наследовать из настроек NPM.",
"localImages": "Локальные Docker-образы",
"imageTag": "Тег",
"imageId": "ID образа",
"imageSize": "Размер",
"imageCreated": "Создан",
"cpuLimit": "Лимит CPU (ядра)",
"cpuLimitHelp": "напр., 0.5, 1, 2. Оставьте 0 для без ограничений",
"memoryLimit": "Лимит памяти (МБ)",
"memoryLimitHelp": "напр., 256, 512, 1024. Оставьте 0 для без ограничений",
"npmProxy": "NPM прокси",
"creating": "Создание...",
"createStage": "Создать стадию",
"noProxy": "Без прокси",
"deleteStage": "Удалить стадию",
"deleteStageConfirm": "Удалить стадию \"{name}\"?",
"stageCreated": "Стадия \"{name}\" создана",
"stageUpdated": "Стадия обновлена",
"stageUpdateFailed": "Не удалось обновить стадию",
"stageDeleted": "Стадия \"{name}\" удалена",
"projectUpdated": "Проект обновлён",
"updateFailed": "Не удалось обновить проект",
"stageCreateFailed": "Не удалось создать стадию",
"stageDeleteFailed": "Не удалось удалить стадию"
},
"envEditor": {
"title": "Переменные окружения",
"description": "Управление переопределениями переменных окружения на уровне стадий. Значения стадий переопределяют значения проекта.",
"stage": "Стадия",
"projectDefaults": "Значения проекта по умолчанию",
"noProjectEnv": "Переменные окружения на уровне проекта ещё не определены.",
"stageOverrides": "Переопределения стадии",
"key": "Ключ",
"value": "Значение",
"secret": "Секрет",
"source": "Источник",
"actions": "Действия",
"overridden": "переопределено",
"inherited": "наследуется",
"overridesProject": "переопределяет проект",
"stageOnly": "только стадия",
"edit": "Изменить",
"change": "Изменить",
"delete": "Удалить",
"save": "Сохранить",
"add": "Добавить",
"adding": "Добавление...",
"noStages": "Стадии не настроены. Сначала добавьте стадии к проекту.",
"loadFailed": "Не удалось загрузить проект",
"envAdded": "Переменная окружения добавлена",
"envUpdated": "Переменная окружения обновлена",
"envDeleted": "Переменная окружения удалена",
"addFailed": "Не удалось добавить переменную",
"updateFailed": "Не удалось обновить переменную",
"deleteFailed": "Не удалось удалить переменную",
"loadEnvFailed": "Не удалось загрузить переменные",
"leaveEmptyToKeep": "Оставьте пустым, чтобы сохранить текущее",
"deleteTitle": "Удалить переменную окружения",
"deleteMessage": "Вы уверены, что хотите удалить эту переменную окружения? Это действие нельзя отменить."
},
"volumeEditor": {
"title": "Тома",
"description": "Настройка монтирования томов для контейнеров. Выберите область видимости для управления общим доступом между развёртываниями.",
"sourceHost": "Источник (хост)",
"targetContainer": "Цель (контейнер)",
"scope": "Область",
"nameColumn": "Имя",
"namePlaceholder": "напр. shared-db",
"requiresName": "требуется имя",
"noHostPath": "нет пути на хосте",
"tmpfs": "tmpfs (в памяти)",
"actions": "Действия",
"edit": "Изменить",
"delete": "Удалить",
"save": "Сохранить",
"add": "Добавить",
"adding": "Добавление...",
"scopeGuide": "Области видимости томов",
"noVolumes": "Тома ещё не настроены. Добавьте один выше.",
"volumeAdded": "Том добавлен",
"volumeUpdated": "Том обновлён",
"volumeDeleted": "Том удалён",
"loadFailed": "Не удалось загрузить тома",
"addFailed": "Не удалось добавить том",
"updateFailed": "Не удалось обновить том",
"deleteFailed": "Не удалось удалить том"
},
"volumeBrowser": {
"title": "Обзор тома",
"loadFailed": "Не удалось загрузить каталог",
"empty": "Этот каталог пуст.",
"name": "Имя",
"size": "Размер",
"modified": "Изменён",
"downloadAll": "Скачать том как ZIP",
"downloadFolder": "Скачать папку как ZIP",
"upload": "Загрузить файлы",
"uploaded": "Загружено",
"files": "файл(ов)",
"uploadFailed": "Не удалось загрузить файлы",
"browse": "Обзор",
"download": "Скачать"
},
"quickDeploy": {
"title": "Быстрый деплой",
"description": "Разверните образ контейнера без настройки. Вставьте URL образа, проверьте параметры и разверните.",
"step1": "1. Введите URL образа",
"imageUrl": "URL образа",
"imageUrlHelp": "Полный URL образа с тегом (напр., git.example.com/user/app:dev-abc123)",
"inspect": "Проверить",
"inspecting": "Проверка...",
"step2": "2. Проверка конфигурации",
"reviewDesc": "Эти параметры были обнаружены из образа. Измените при необходимости перед деплоем.",
"projectName": "Имя проекта",
"port": "Порт",
"portHelp": "Порт контейнера (1-65535)",
"healthCheckPath": "Путь проверки здоровья",
"healthCheckHelp": "Необязательный HTTP-путь для проверки работоспособности",
"stage": "Стадия",
"development": "Разработка",
"release": "Релиз",
"production": "Продакшн",
"stageHelp": "Стадия развёртывания для этого образа",
"subdomainOverride": "Переопределение поддомена",
"subdomainHelp": "Оставьте пустым для использования шаблона по умолчанию",
"envVars": "Переменные окружения",
"envVarsHelp": "По одной на строку, формат KEY=VALUE",
"step3": "3. Развёртывание",
"deployDesc": "Будет создан новый проект и контейнер будет развёрнут немедленно.",
"deployBtn": "Развернуть",
"inspectedSuccess": "Образ успешно проверен",
"deployedSuccess": "{name} успешно развёрнут!",
"inspectFailed": "Не удалось проверить образ",
"deployFailed": "Развёртывание не удалось",
"browseImages": "Обзор",
"selectImage": "Выберите образ из реестра",
"noImages": "Образы не найдены",
"loadingImages": "Загрузка...",
"imageLoadFailed": "Не удалось загрузить образы",
"autoDeployLabel": "Развернуть сразу",
"lowercaseHint": "Строчные буквы и дефисы",
"imageAlreadyExists": "Образ уже развёрнут",
"conflictDescription": "Проект с этим образом уже существует. Откройте существующий проект для развёртывания новой версии или создайте отдельный проект.",
"openProject": "Открыть проект \u2192",
"createNewAnyway": "Создать новый проект"
},
"settings": {
"title": "Настройки",
"general": "Общие",
"integrations": "Интеграции",
"dns": "DNS",
"maintenance": "Обслуживание",
"registries": "Реестры",
"credentials": "Учётные данные",
"authentication": "Аутентификация",
"backup": "Резервные копии",
"appearance": "Внешний вид",
"groupMain": "Обзор",
"groupProxy": "Маршрутизация",
"groupSystem": "Система",
"groupSecurity": "Безопасность",
"staleThreshold": "Порог устаревания (дни)",
"staleThresholdHelp": "Контейнеры, неактивные дольше этого срока, будут помечены как устаревшие.",
"dockerCleanup": "Очистка Docker-образов",
"dockerCleanupHelp": "Удаление неиспользуемых Docker-образов ваших проектов. Удаляются только образы, не используемые активными экземплярами.",
"pruneThreshold": "Порог предупреждения (МБ)",
"pruneThresholdHelp": "Показывать предупреждение на дашборде, когда неиспользуемые образы превышают этот размер. 0 = отключено.",
"pruneImages": "Очистить неиспользуемые образы",
"pruning": "Очистка...",
"pruneResult": "Удалено {count} образов, освобождено {mb} МБ",
"pruneConfirmMessage": "Будут удалены неиспользуемые Docker-образы ваших проектов. Образы активных экземпляров не затрагиваются.",
"pruneFailed": "Не удалось очистить образы",
"proxyProvider": "Провайдер прокси",
"proxyProviderHelp": "Выберите способ управления обратным прокси для развёрнутых контейнеров.",
"proxyNone": "Нет",
"proxyNoneDesc": "Без прокси — контейнеры доступны напрямую по порту",
"proxyNpm": "Nginx Proxy Manager",
"proxyNpmDesc": "Маршруты через NPM API (настройте учётные данные ниже)",
"npm": "Nginx Proxy Manager",
"traefik": "Traefik",
"traefikLabelsTitle": "Справка по Docker-меткам",
"traefikLabelsDesc": "Эти метки автоматически добавляются к развёрнутым контейнерам. Показаны для справки.",
"proxyTraefik": "Traefik",
"proxyTraefikDesc": "Автообнаружение через Docker-метки — без API-вызовов",
"proxyNoneWarning": "Переключение на «Нет» не удаляет существующие прокси-маршруты. Возможно, потребуется очистить их вручную.",
"traefikEntrypoint": "Точка входа",
"traefikEntrypointHelp": "Имя точки входа Traefik для HTTPS-маршрутов",
"traefikCertResolver": "Резолвер сертификатов",
"traefikCertResolverHelp": "Имя резолвера TLS-сертификатов (напр., letsencrypt)",
"traefikNetwork": "Docker-сеть",
"traefikNetworkHelp": "Сеть, которую слушает Traefik (оставьте пустым для глобальной сети)",
"traefikApiUrl": "URL API Traefik",
"traefikApiUrlHelp": "Необязательно — для проверки состояния (напр., http://traefik:8080)"
},
"settingsGeneral": {
"title": "Общие настройки",
"globalConfig": "Глобальная конфигурация",
"globalConfigDesc": "Базовая инфраструктура: домен, сеть и интервал опроса, используемые Tinyforge для оркестрации контейнеров.",
"configureNpm": "Выбран Nginx Proxy Manager.",
"configureTraefik": "Выбран Traefik.",
"configureLink": "Настроить провайдера",
"domain": "Домен",
"domainHelp": "Базовый домен для маршрутизации (напр., example.com → stage-dev-app.example.com)",
"serverIp": "IP сервера (Docker Host)",
"serverIpHelp": "IP машины с Docker. Используется для удалённого NPM.",
"publicIp": "Публичный IP (для DNS)",
"publicIpHelp": "IP для DNS A-записей — обычно адрес прокси/балансировщика. Если пусто, используется IP сервера.",
"dockerNetwork": "Docker-сеть",
"dockerNetworkHelp": "Docker-сеть, общая для контейнеров и прокси. Должна совпадать с сетью NPM/Traefik.",
"subdomainPattern": "Шаблон поддомена",
"subdomainPatternHelp": "Шаблон для автоматически генерируемых поддоменов",
"subdomainVarsTitle": "Доступные переменные",
"varProject": "Имя проекта",
"varStage": "Имя стадии",
"varTag": "Тег образа",
"varPort": "Порт контейнера",
"pollingInterval": "Интервал опроса (секунды)",
"pollingIntervalHelp": "Как часто проверять реестры на новые теги (60-86400)",
"notificationUrl": "URL уведомлений",
"notificationUrlHelp": "URL вебхука для уведомлений о деплоях",
"saveSettings": "Сохранить настройки",
"saving": "Сохранение...",
"saved": "Настройки успешно сохранены",
"saveFailed": "Не удалось сохранить настройки",
"loadFailed": "Не удалось загрузить настройки",
"webhookUrl": "URL вебхука",
"webhookDesc": "Этот секретный URL получает уведомления о push-событиях из вашего CI-пайплайна.",
"noWebhookUrl": "URL вебхука не настроен",
"copy": "Копировать",
"copied": "URL вебхука скопирован в буфер обмена",
"regenerateUrl": "Перегенерировать URL",
"regenerating": "Перегенерация...",
"regenerated": "URL вебхука перегенерирован",
"regenerateFailed": "Не удалось перегенерировать URL вебхука",
"regenerateWarning": "Внимание: перегенерация сделает текущий URL недействительным. Обновите ваши CI-пайплайны.",
"sslCertificate": "SSL-сертификат",
"sslCertificateHelp": "Wildcard-сертификат из NPM для автоматического SSL на прокси-хостах",
"selectCertificate": "Выбрать сертификат",
"noCertificate": "Нет (без SSL)",
"clearCertificate": "Очистить",
"loadingCertificates": "Загрузка сертификатов...",
"noCertificatesFound": "Wildcard-сертификаты в NPM не найдены",
"dnsConfig": "Настройки DNS",
"wildcardDns": "Wildcard DNS настроен",
"wildcardDnsHelp": "Когда включено, все поддомены разрешаются на ваш сервер через wildcard DNS правило. Отключите для управления DNS-записями для каждого сервиса.",
"dnsProvider": "DNS-провайдер",
"dnsProviderHelp": "Выберите DNS-провайдера для автоматического управления записями",
"cloudflareApiToken": "API-токен Cloudflare",
"cloudflareApiTokenHelp": "API-токен с правами редактирования DNS для вашей зоны",
"cloudflareApiTokenPlaceholder": "Введите API-токен Cloudflare",
"cloudflareApiTokenConfigured": "API-токен настроен",
"cloudflareZone": "Зона Cloudflare",
"cloudflareZoneHelp": "Выберите DNS-зону для управления записями",
"selectZone": "Выбрать зону",
"noZone": "Зона не выбрана",
"loadingZones": "Загрузка зон...",
"noZonesFound": "Зоны для этого токена не найдены",
"testConnection": "Проверить соединение",
"testingConnection": "Проверка...",
"connectionSuccess": "Соединение успешно",
"connectionFailed": "Ошибка соединения",
"baseVolumePath": "Базовый путь томов",
"baseVolumePathHelp": "Добавляется к относительным путям источников (напр., /data + my-app/uploads = /data/my-app/uploads)"
},
"settingsRegistries": {
"title": "Реестры контейнеров",
"description": "Управление реестрами контейнеров для обнаружения образов.",
"addRegistry": "Добавить реестр",
"editRegistry": "Редактировать реестр",
"addNewRegistry": "Добавить новый реестр",
"name": "Название",
"nameHelp": "Понятное название для этого реестра",
"url": "URL",
"urlHelp": "Базовый URL реестра",
"type": "Тип",
"typeHelp": "Тип реестра для совместимости API",
"token": "Токен",
"tokenHelpNew": "API-токен для аутентификации",
"tokenHelpEdit": "Оставьте пустым, чтобы сохранить текущий токен",
"owner": "Владелец",
"ownerHelp": "Владельцы пакетов через запятую (напр., alexei,my-org)",
"save": "Сохранить",
"saving": "Сохранение...",
"update": "Обновить",
"test": "Тест",
"testing": "Тестирование...",
"edit": "Изменить",
"delete": "Удалить",
"noRegistries": "Реестры ещё не настроены.",
"addFirst": "Добавьте первый реестр",
"registryUpdated": "Реестр обновлён",
"registryAdded": "Реестр добавлен",
"registryDeleted": "Реестр «{name}» удалён",
"testSuccess": "Подключение к «{name}» успешно",
"saveFailed": "Не удалось сохранить реестр",
"deleteFailed": "Не удалось удалить реестр",
"testFailed": "Тест подключения не удался",
"loadFailed": "Не удалось загрузить реестры",
"deleteConfirm": "Удалить реестр «{name}»? Это действие необратимо.",
"healthChecking": "Проверка...",
"healthConnected": "Подключено",
"healthUnreachable": "Недоступно"
},
"settingsNpm": {
"testConnection": "Проверить соединение",
"testing": "Проверка...",
"testSuccess": "Подключение к NPM успешно",
"testFailed": "Не удалось подключиться к NPM",
"saveFailedConnection": "Невозможно сохранить — проверка соединения не пройдена",
"remoteMode": "Удалённый NPM",
"remoteModeHelp": "Включите, если NPM работает на другой машине. Перенаправление на IP сервера с опубликованными портами.",
"remoteModeWarning": "Требуется IP сервера в общих настройках. Порты автоматически привязываются к случайным портам хоста.",
"accessList": "Список доступа по умолчанию",
"accessListHelp": "Список доступа NPM для HTTP-аутентификации на прокси-хостах. Можно переопределить для каждого проекта.",
"noAccessList": "Глобальные настройки",
"selectAccessList": "Выберите список доступа",
"noAccessLists": "Списки доступа в NPM не найдены",
"accessListLoadFailed": "Не удалось загрузить списки доступа"
},
"settingsCredentials": {
"title": "Учётные данные",
"description": "Управление учётными данными для Nginx Proxy Manager и токенами реестров. Все значения зашифрованы.",
"npm": "Nginx Proxy Manager",
"npmDesc": "Учётные данные для управления прокси-хостами через NPM API",
"configured": "Настроено",
"npmUrl": "URL NPM",
"npmUrlHelp": "URL API Nginx Proxy Manager",
"email": "Email",
"emailHelp": "Email администратора NPM",
"password": "Пароль",
"passwordHelpNew": "Пароль администратора NPM (будет зашифрован)",
"passwordHelpEdit": "Введите новый пароль для замены текущего",
"changeCredentials": "Изменить учётные данные",
"save": "Сохранить",
"saving": "Сохранение...",
"saved": "Учётные данные NPM сохранены",
"saveFailed": "Не удалось сохранить учётные данные NPM",
"loadFailed": "Не удалось загрузить учётные данные",
"registryTokens": "Токены реестров",
"registryTokensDesc": "Токены аутентификации реестров управляются для каждого реестра в разделе",
"registriesLink": "Реестры",
"registryTokensSuffix": ". Каждый реестр хранит свой токен в зашифрованном виде.",
"notSet": "Не задано"
},
"settingsBackup": {
"title": "Управление резервными копиями",
"description": "Управление резервными копиями базы данных и настройка автоматического резервного копирования.",
"autoBackup": "Автоматическое резервное копирование",
"autoBackupHelp": "Автоматически создавать резервные копии с заданным интервалом.",
"interval": "Интервал копирования",
"intervalHelp": "Как часто создавать автоматические резервные копии.",
"intervalHours": "{hours} часов",
"retention": "Количество хранимых копий",
"retentionHelp": "Максимальное количество хранимых резервных копий. Старые удаляются первыми.",
"backupNow": "Создать копию",
"creatingBackup": "Создание...",
"backupCreated": "Резервная копия создана",
"backupFailed": "Не удалось создать резервную копию",
"backupList": "Резервные копии",
"noBackups": "Резервных копий пока нет. Создайте вручную или включите автоматическое копирование.",
"columnFilename": "Файл",
"columnSize": "Размер",
"columnType": "Тип",
"columnDate": "Создано",
"columnActions": "Действия",
"download": "Скачать",
"delete": "Удалить",
"restore": "Восстановить",
"deleteConfirm": "Вы уверены, что хотите удалить эту резервную копию?",
"deleted": "Резервная копия удалена",
"deleteFailed": "Не удалось удалить резервную копию",
"restoreConfirm": "Вы уверены, что хотите восстановить из этой копии? Текущая база данных будет заменена и сервер будет перезапущен. Все текущие данные будут потеряны.",
"restoreWarning": "Это действие необратимо!",
"restored": "База данных восстановлена. Сервер перезапускается...",
"restoreFailed": "Не удалось восстановить резервную копию",
"typeManual": "Ручная",
"typeAuto": "Авто",
"typePreDeploy": "Перед деплоем",
"preDeploy": "Снимок перед каждым деплоем",
"preDeployHelp": "Создавать снимок БД Tinyforge в начале каждого деплоя проекта. Независимо от периодического расписания выше; восстанавливается из списка ниже по типу «Перед деплоем».",
"save": "Сохранить",
"saving": "Сохранение...",
"saved": "Настройки копирования сохранены",
"saveFailed": "Не удалось сохранить настройки копирования"
},
"settingsAuth": {
"title": "Настройки аутентификации",
"description": "Настройка режима аутентификации и управление пользователями.",
"authMode": "Режим аутентификации",
"local": "Локальный (логин/пароль)",
"oidc": "OIDC (SSO)",
"oidcConfig": "Конфигурация OIDC-провайдера",
"issuerUrl": "URL издателя",
"clientId": "ID клиента",
"clientSecret": "Секрет клиента",
"redirectUrl": "URL перенаправления",
"saveSettings": "Сохранить настройки",
"saving": "Сохранение...",
"saved": "Настройки сохранены",
"saveFailed": "Не удалось сохранить",
"loadFailed": "Не удалось загрузить настройки",
"localUsers": "Локальные пользователи",
"username": "Имя пользователя",
"email": "Email",
"role": "Роль",
"created": "Создан",
"noUsers": "Пользователи не найдены.",
"addUser": "Добавить пользователя",
"viewer": "Наблюдатель",
"admin": "Администратор",
"userCreated": "Пользователь создан",
"userDeleted": "Пользователь удалён",
"createFailed": "Не удалось создать пользователя",
"deleteFailed": "Не удалось удалить пользователя",
"deleteConfirm": "Вы уверены, что хотите удалить этого пользователя?",
"usernameRequired": "Имя пользователя и пароль обязательны",
"networkError": "Ошибка сети",
"password": "Пароль"
},
"login": {
"title": "Tinyforge",
"subtitle": "Войдите в свой аккаунт",
"username": "Имя пользователя",
"password": "Пароль",
"signIn": "Войти",
"signingIn": "Вход...",
"or": "или",
"ssoButton": "Войти через SSO (OIDC)",
"loginFailed": "Ошибка входа",
"networkError": "Ошибка сети"
},
"proxies": {
"title": "Менеджер прокси",
"create": "Создать прокси",
"standalone": "Автономные прокси",
"managed": "Управляемые прокси",
"noProxies": "Прокси не найдены",
"noProxiesDesc": "Создайте автономный прокси или разверните проект с включённым прокси.",
"filter": {
"search": "Поиск по домену или назначению...",
"health": "Здоровье",
"type": "Тип",
"all": "Все",
"clear": "Сбросить фильтры"
},
"health": {
"healthy": "Здоров",
"unhealthy": "Нездоров",
"unknown": "Неизвестно"
},
"lastChecked": "Последняя проверка"
},
"sites": {
"webhookTitle": "Webhook сайта",
"webhookDesc": "Укажите этот URL в push-вебхуке Git-провайдера. Tinyforge пересинхронизирует сайт при подходящей ref-ссылке (ветка для push, шаблон тега для tag). Пустое тело запускает синхронизацию безусловно.",
"outgoingUrlTitle": "URL исходящего webhook (этот сайт)",
"outgoingUrlDesc": "Куда Tinyforge отправляет события site_sync_success / site_sync_failure. Пусто — наследовать из глобальных настроек.",
"outgoingWebhookTitle": "Исходящий webhook (сайт)",
"outgoingWebhookDesc": "HMAC-секрет и тестовая отправка для разрешённого исходящего URL.",
"outgoingFallbackGlobal": "глобальной настройки интеграций",
"title": "Статические сайты",
"addSite": "Новый сайт",
"newSite": "Новый статический сайт",
"createSite": "Создать сайт",
"noSites": "Нет статических сайтов",
"noSitesDesc": "Разверните статический контент из папки Git-репозитория.",
"searchPlaceholder": "Поиск по имени, домену или репозиторию...",
"noMatching": "Нет сайтов, соответствующих поиску.",
"name": "Имя",
"domain": "Домен",
"mode": "Режим",
"status": "Статус",
"lastSync": "Последняя синхр.",
"deploy": "Развернуть",
"stop": "Остановить",
"start": "Запустить",
"openSite": "Открыть сайт",
"confirmDelete": "Удалить сайт",
"confirmDeleteMsg": "Это удалит сайт и его контейнер",
"confirmDeleteSecret": "Удалить секрет",
"confirmDeleteSecretMsg": "Вы уверены, что хотите удалить секрет",
"siteInfo": "Информация о сайте",
"folder": "Папка",
"syncTrigger": "Триггер синхр.",
"commitSha": "Коммит SHA",
"secrets": "Секреты",
"addSecret": "Добавить секрет",
"noSecrets": "Секреты не настроены. Добавьте их, если сайту нужны серверные API-ключи.",
"secretKey": "Ключ",
"secretValue": "Значение",
"encryptSecret": "Шифровать значение",
"saveSecret": "Добавить секрет",
"step1Title": "1. Репозиторий",
"step2Title": "2. Выбор ветки",
"step3Title": "3. Выбор папки",
"step4Title": "4. Настройки",
"step5Title": "5. Проверка и создание",
"fullRepoUrl": "URL репозитория",
"fullRepoUrlHelp": "Вставьте полный URL для автозаполнения полей ниже (напр., https://git.example.com/owner/repo)",
"serverUrl": "URL сервера",
"repoUrl": "URL Git-сервера",
"repoUrlHelp": "Вставьте полный URL репозитория или базовый URL сервера (Gitea, Forgejo, Gogs)",
"repoOwner": "Владелец",
"repoName": "Репозиторий",
"accessToken": "Токен доступа",
"accessTokenPlaceholder": "Необязательно — для приватных репозиториев",
"accessTokenHelp": "Персональный токен с правами на чтение репозитория. Оставьте пустым для публичных.",
"noToken": "Нет (публичный репо)",
"testConnection": "Проверить соединение",
"connectionSuccess": "Репозиторий доступен",
"loadingBranches": "Загрузка веток...",
"selectBranch": "Выберите ветку",
"chooseBranch": "Выберите ветку...",
"branch": "Ветка",
"loadingTree": "Загрузка дерева репозитория...",
"selectFolder": "Выберите папку с файлами сайта",
"selectedFolder": "Выбранная папка",
"siteName": "Имя сайта",
"domainHelp": "Публичный домен сайта. Прокси будет настроен автоматически.",
"modeStaticDesc": "HTML, CSS, JS, изображения через Nginx",
"modeDenoDesc": "Статические файлы + серверный API из папки api/",
"triggerManual": "Вручную",
"triggerPush": "При пуше",
"triggerTag": "По тегу",
"tagPattern": "Паттерн тега",
"tagPatternHelp": "Glob-паттерн для тегов (напр., v*, pages-*)",
"renderMarkdown": "Рендерить Markdown-файлы в HTML",
"provider": "Git-провайдер",
"detectedProvider": "Автоопределён",
"browseRepos": "Обзор репозиториев",
"selectRepo": "Выберите репозиторий",
"storage": "Хранилище данных",
"enableStorage": "Включить хранилище данных",
"storageHelp": "Подключает Docker-том в /app/data, чтобы Deno-бэкенд мог читать и записывать файлы, сохраняющиеся между деплоями.",
"storageLimitMB": "Лимит хранилища (МБ)",
"storageLimitHelp": "Максимальный размер хранилища в мегабайтах. 0 = без ограничений.",
"storageVolume": "Том",
"dataPath": "Путь к данным",
"storageMountPath": "Путь монтирования",
"storageLimit": "Лимит",
"storageUsed": "Использовано",
"storageOfLimit": "от лимита использовано",
"unlimited": "Без ограничений"
},
"common": {
"cancel": "Отмена",
"confirm": "Подтвердить",
"delete": "Удалить",
"edit": "Изменить",
"change": "Изменить",
"save": "Сохранить",
"retry": "Повторить",
"loading": "Загрузка...",
"noData": "Нет данных",
"project": "Проект",
"stack": "Стек",
"site": "Сайт",
"back": "Назад",
"actions": "Действия",
"stop": "Остановить",
"start": "Запустить",
"restart": "Перезапустить",
"remove": "Удалить",
"instance": "экземпляр",
"instances": "экземпляров",
"next": "Далее",
"yes": "Да",
"no": "Нет",
"saving": "Сохранение...",
"refresh": "Обновить",
"all": "Все",
"running": "Работает",
"stopped": "Остановлен",
"missing": "Отсутствует"
},
"containers": {
"errLoad": "Не удалось загрузить контейнеры",
"searchPlaceholder": "Поиск по нагрузке, роли, образу, поддомену…",
"kindFilterLabel": "Тип нагрузки",
"stateFilterLabel": "Состояние контейнера",
"emptyTitle": "Нет контейнеров",
"emptyDesc": "Разверните проект, стек или сайт — контейнеры появятся здесь.",
"noMatch": "Нет контейнеров, подходящих под фильтры.",
"showingN": "Показано {visible} из {total} контейнеров",
"col": {
"workload": "Нагрузка",
"kind": "Тип",
"role": "Роль",
"image": "Образ",
"state": "Состояние",
"subdomain": "Поддомен",
"lastSeen": "Замечен"
}
},
"instance": {
"stopConfirm": "Контейнер будет остановлен. Экземпляр можно будет запустить снова позже.",
"restartConfirm": "Контейнер будет перезапущен с кратковременным простоем.",
"removeConfirm": "Контейнер и его прокси-конфигурация будут безвозвратно удалены.",
"actionFailed": "Действие не удалось"
},
"empty": {
"noProjects": "Проектов пока нет",
"noProjectsDesc": "Начните с создания первого проекта или используйте быстрый деплой.",
"createProject": "Создать проект",
"noInstances": "Нет экземпляров",
"noInstancesDesc": "Разверните новую версию, чтобы увидеть экземпляры здесь.",
"noDeploys": "Нет истории деплоев",
"noDeploysDesc": "История деплоев появится здесь после первого развёртывания.",
"noRegistries": "Нет реестров",
"noRegistriesDesc": "Добавьте реестр контейнеров для обнаружения образов.",
"noVolumes": "Нет томов",
"noVolumesDesc": "Настройте монтирование томов для постоянных данных.",
"noUsers": "Нет пользователей",
"noUsersDesc": "Добавьте локальных пользователей для управления доступом."
},
"validation": {
"required": "Поле {field} обязательно",
"invalidUrl": "Неверный формат URL",
"invalidDomain": "Неверный формат домена",
"invalidIp": "Неверный формат IP",
"invalidEmail": "Неверный формат email",
"invalidPort": "Порт должен быть от 1 до 65535",
"invalidPollingInterval": "Интервал опроса должен быть от 60 до 86400 секунд",
"invalidProjectName": "Допускаются только строчные буквы, цифры и дефисы",
"requiredWhenUpdating": "Поле {field} обязательно при обновлении учётных данных",
"requiredForNew": "Поле {field} обязательно для новых реестров"
},
"confirm": {
"stopInstance": "Остановить экземпляр",
"startInstance": "Запустить экземпляр",
"restartInstance": "Перезапустить экземпляр",
"removeInstance": "Удалить экземпляр",
"stopAction": "Остановить",
"restartAction": "Перезапустить",
"removeAction": "Удалить"
},
"theme": {
"light": "Светлая",
"dark": "Тёмная",
"system": "Системная"
},
"entityPicker": {
"search": "Поиск...",
"noResults": "Ничего не найдено"
},
"stale": {
"title": "Устаревшие контейнеры",
"noStale": "Нет устаревших контейнеров",
"noStaleDesc": "Все контейнеры исправны и работают.",
"cleanup": "Очистить",
"cleanupAll": "Очистить все",
"confirmCleanup": "Это остановит и удалит контейнер. Продолжить?",
"confirmBulkCleanup": "Это остановит и удалит все устаревшие контейнеры. Продолжить?",
"daysStale": "дней устарел",
"lastAlive": "Последний раз жив",
"count": "Устаревшие",
"cleanedUp": "Контейнер очищен",
"bulkCleanedUp": "{count} контейнеров очищено",
"cleanupFailed": "Не удалось очистить",
"loadFailed": "Не удалось загрузить устаревшие контейнеры"
},
"proxies": {
"title": "Прокси",
"create": "Создать прокси",
"noProxies": "Прокси ещё не настроены.",
"noProxiesDesc": "Создайте автономный прокси или разверните проект, чтобы увидеть прокси здесь.",
"standalone": "Автономные прокси",
"managed": "Управляемые",
"lastChecked": "Последняя проверка",
"health": {
"healthy": "Работает",
"unhealthy": "Недоступен",
"unknown": "Неизвестно"
},
"filter": {
"search": "Поиск прокси...",
"health": "Здоровье",
"type": "Тип",
"all": "Все",
"clear": "Сбросить фильтры"
},
"form": {
"title": "Создать прокси",
"editTitle": "Редактировать прокси",
"destination": "URL / IP назначения",
"port": "Порт",
"domain": "Домен",
"domainHelp": "Публичный домен для этого прокси.",
"validate": "Проверить",
"validating": "Проверка...",
"create": "Создать прокси",
"save": "Сохранить изменения",
"cancel": "Отмена",
"delete": "Удалить",
"deleteConfirm": "Удалить этот прокси? Это действие необратимо."
},
"validation": {
"title": "Проверка назначения",
"syntax": "Синтаксис URL",
"dns": "DNS разрешение",
"tcp": "TCP подключение",
"http": "HTTP ответ",
"checking": "Проверка...",
"skipped": "Пропущено"
}
},
"proxies": {
"title": "Прокси-маршруты",
"description": "Активные прокси-маршруты от контейнеров и статических сайтов.",
"domain": "Домен",
"project": "Проект / Сайт",
"stage": "Этап / Режим",
"tag": "Тег",
"port": "Порт",
"status": "Статус",
"source": "Источник",
"sourceContainer": "Контейнер",
"sourceStatic": "Статический сайт",
"sourceDeno": "Deno-сайт",
"filterAll": "Все",
"filterContainers": "Контейнеры",
"filterSites": "Сайты",
"noRoutes": "Нет прокси-маршрутов",
"noRoutesDesc": "Прокси-маршруты создаются автоматически при развёртывании контейнера с прокси или публикации статического сайта.",
"searchPlaceholder": "Поиск по домену, проекту или тегу...",
"noMatch": "Нет маршрутов, соответствующих поиску.",
"loadFailed": "Не удалось загрузить прокси-маршруты",
"route": "маршрут",
"routes": "маршрутов"
},
"logs": {
"title": "Логи контейнера",
"lines": "строк",
"follow": "Следить",
"following": "Слежение...",
"loading": "Загрузка логов...",
"noLogs": "Нет вывода логов"
},
"events": {
"title": "Журнал событий",
"noEvents": "Событий не найдено",
"noEventsDesc": "События будут отображаться здесь по мере их возникновения.",
"loadMore": "Загрузить ещё",
"newEvents": "новых событий",
"totalCount": "всего {count}",
"clearAll": "Очистить всё",
"clearAllTitle": "Очистить журнал событий",
"clearAllMessage": "Все записи журнала событий будут удалены безвозвратно.",
"cleared": "Удалено {count} событий",
"clearFailed": "Не удалось очистить события",
"filter": {
"severity": "Уровень",
"source": "Источник",
"dateRange": "Период",
"search": "Поиск событий...",
"lastHour": "Последний час",
"last24h": "Последние 24 часа",
"last7d": "Последние 7 дней",
"allTime": "За всё время",
"clear": "Сбросить фильтры"
},
"severity": {
"info": "Инфо",
"warn": "Предупреждение",
"error": "Ошибка"
},
"source": {
"deploy": "Развёртывание",
"static_site": "Статический сайт",
"stale_scanner": "Сканер устаревших",
"stale_cleanup": "Очистка устаревших",
"admin": "Администратор"
},
"metadata": "Подробности"
},
"stats": {
"cpu": "ЦП",
"mem": "ОЗУ",
"unavailable": "Статистика недоступна"
},
"systemHealth": {
"title": "Состояние системы",
"containers": "Контейнеры",
"proxies": "Прокси",
"recentErrors": "Недавние ошибки"
},
"daemons": {
"title": "Демоны",
"refresh": "Обновить",
"refreshing": "Обновление",
"docker": "Docker Engine",
"npm": "Nginx Proxy Manager",
"traefik": "Traefik",
"proxy": "Прокси",
"online": "Онлайн",
"offline": "Оффлайн",
"notConfigured": "Не настроено",
"containers": "Контейнеры",
"running": "Запущено",
"paused": "Пауза",
"stopped": "Остановлено",
"version": "Версия",
"apiVersion": "Версия API",
"platform": "Платформа",
"kernel": "Ядро",
"cpu": "CPU",
"memory": "Память",
"storage": "Хранилище",
"images": "Образы",
"latency": "Задержка",
"rootDir": "Корневой каталог",
"provider": "Провайдер",
"endpoint": "Адрес",
"proxyHosts": "Прокси-хосты",
"managed": "наши",
"external": "внешние",
"accessLists": "Списки доступа",
"certificates": "Сертификаты",
"dockerHint": "Проверьте, что Docker-демон запущен и сокет доступен.",
"proxyHint": "Проверьте URL прокси, учётные данные и доступность сервиса.",
"noProxyDesc": "Провайдер прокси не настроен. Tinyforge поддерживает Nginx Proxy Manager или Traefik.",
"configureProxy": "Настроить в параметрах",
"dockerNotReachable": "Docker-демон недоступен.",
"dockerUnreachable": "Docker недоступен",
"proxyUnreachable": "Прокси недоступен",
"reachable": "доступен"
},
"dns": {
"title": "DNS-записи",
"description": "Просмотр и управление DNS-записями, созданными Tinyforge.",
"wildcardActive": "Режим Wildcard DNS активен",
"wildcardActiveDesc": "DNS-записи управляются внешне через wildcard DNS. Отключите wildcard DNS в настройках для индивидуального управления записями.",
"refresh": "Обновить",
"syncNow": "Синхронизировать",
"syncing": "Синхронизация...",
"syncComplete": "Синхронизация завершена: {created} создано, {deleted} удалено, {synced} уже синхронизировано",
"syncFailed": "Ошибка синхронизации DNS",
"searchPlaceholder": "Поиск по FQDN...",
"allConsumers": "Все потребители",
"managed": "Управляемые (инстансы)",
"standalone": "Автономные прокси",
"orphaned": "Осиротевшие",
"allStatuses": "Все статусы",
"statusSynced": "Синхронизировано",
"statusMissing": "Отсутствует",
"statusOrphaned": "Осиротевшее",
"columnFqdn": "FQDN",
"columnType": "Тип",
"columnValue": "Значение",
"columnConsumer": "Потребитель",
"columnStatus": "Статус",
"columnActions": "Действия",
"noConsumer": "Нет потребителя",
"noRecords": "DNS-записи не найдены. Записи появятся здесь после развёртывания сервисов.",
"noMatchingRecords": "Нет записей, соответствующих текущим фильтрам.",
"deleteRecord": "Удалить запись",
"recordDeleted": "DNS-запись {fqdn} удалена",
"deleteFailed": "Не удалось удалить DNS-запись",
"loadFailed": "Не удалось загрузить DNS-записи",
"totalRecords": "Всего: {count}",
"syncedCount": "Синхронизировано: {count}",
"missingCount": "Отсутствует: {count}",
"orphanedCount": "Осиротевших: {count}"
},
"language": {
"en": "Английский",
"ru": "Русский"
},
"stacks": {
"eyebrow": "КУЗНИЦА",
"title": "Стеки",
"lede": "Compose-чертежи, выкованные как <em>атомарные единицы</em>. Запускайте сервисы, меняйте ревизии и откатывайтесь без нервов.",
"newStack": "Новый стек",
"refresh": "Обновить",
"total": "Всего",
"running": "Работают",
"deploying": "Куются",
"failed": "Сбой",
"stopped": "Холодные",
"empty": {
"title": "Наковальня остыла.",
"desc": "Загрузите docker-compose.yml, чтобы выковать первый стек."
},
"card": {
"noDescription": "Без описания",
"updated": "Обновлён",
"start": "Запустить",
"stop": "Остановить",
"delete": "Удалить",
"open": "Открыть"
},
"new": {
"eyebrow": "НОВЫЙ ЧЕРТЁЖ",
"title": "Выковать новый стек.",
"lede": "Загрузите или вставьте <code>docker-compose.yml</code>. Все сервисы чертежа разворачиваются как одна атомарная единица.",
"back": "Стеки",
"name": "Имя",
"namePlaceholder": "мой-стек",
"nameHint": "Строчные буквы, через дефис. Используется как имя compose-проекта.",
"description": "Описание",
"descriptionPlaceholder": "Что делает этот стек?",
"composeYaml": "Compose YAML",
"required": "обязательно",
"optional": "необязательно",
"loadSample": "Загрузить пример",
"uploadFile": "Загрузить файл",
"dropHere": "Перетащите сюда docker-compose.yml",
"dropSub": "или нажмите для выбора · или используйте <strong>Загрузить пример</strong> выше",
"lines": "{n} строк",
"bytes": "{n} байт",
"clear": "Очистить",
"deployImmediate": "Развернуть сразу",
"deployHint": "Куй железо, пока горячо. Без галочки стек сохраняется холодным.",
"cancel": "Отмена",
"forging": "Куём…",
"forgeAndDeploy": "Выковать и развернуть",
"saveBlueprint": "Сохранить чертёж",
"errorRequired": "Имя и compose YAML обязательны.",
"errorCreate": "Не удалось создать стек"
},
"detail": {
"manifest": "МАНИФЕСТ",
"loading": "Загрузка чертежа…",
"composeProject": "COMPOSE-ПРОЕКТ",
"noDescription": "Без описания",
"refresh": "Обновить",
"start": "Запустить",
"stop": "Остановить",
"delete": "Удалить",
"fault": "СБОЙ",
"err": "ОШБ",
"stats": {
"services": "Сервисы",
"servicesSub": "в чертеже",
"running": "Работают",
"runningSub": "активных контейнеров",
"revisions": "Ревизии",
"revisionsSub": "в истории",
"current": "Текущая",
"currentSub": "развёрнута"
},
"services": {
"title": "Сервисы",
"count": "{n} в работе",
"empty": "— нет запущенных контейнеров —"
},
"tabs": {
"blueprint": "Чертёж",
"revisions": "Ревизии",
"logs": "Логи"
},
"yaml": {
"currentRevision": "Текущая ревизия",
"edit": "Править и развернуть",
"cancel": "Отмена",
"forging": "Куём…",
"deployNew": "Развернуть новую ревизию"
},
"revisions": {
"current": "ТЕКУЩАЯ",
"by": "автор",
"rollback": "← Откатиться к этой ревизии",
"rollbackTitle": "Откатить ревизию?",
"rollbackMessage": "Создать новую ревизию из rev {n} и развернуть стек заново.",
"rollbackConfirm": "Откатить"
},
"logs": {
"service": "Сервис:",
"allServices": "Все сервисы",
"fetching": "Загрузка…",
"fetch": "Получить логи",
"empty": "— логи не загружены. нажмите получить. —"
},
"delete": {
"title": "Удалить стек?",
"messageBase": "Будет выполнен 'docker compose down' и удалён \"{name}\".",
"messageVolumes": " Именованные тома также будут удалены.",
"confirm": "Удалить"
},
"errors": {
"load": "Не удалось загрузить стек",
"stop": "Остановка не удалась",
"start": "Запуск не удался",
"update": "Обновление не удалось",
"rollback": "Откат не удался",
"delete": "Удаление не удалось",
"fetchLogs": "Не удалось загрузить логи"
}
}
},
"timezone": {
"eyebrow": "The Forge // Хронограф",
"title": "Часовой пояс отображения",
"subtitle": "Все даты в Tinyforge — лог событий, деплои, бэкапы, сайты — показываются в этом поясе.",
"modeLabel": "Режим определения",
"modeAuto": "Автоопределение",
"modeManual": "Вручную",
"autoDetect": "Автоопределение из браузера",
"autoBadge": "Авто",
"activeZone": "Активный пояс",
"changeZone": "Сменить часовой пояс",
"clickToChange": "Нажмите, чтобы выбрать пояс →",
"pickerTitle": "Выбор часового пояса",
"pickerPlaceholder": "Поиск — город, регион, смещение UTC…",
"groupAuto": "Определение",
"groupPopular": "Популярные",
"groupAll": "Все пояса",
"previewFull": "Полная метка времени",
"previewDate": "Только дата",
"previewHint": "Метки времени в логе событий будут выглядеть именно так."
},
"settingsDns": {
"title": "Настройка DNS",
"description": "Выберите, использовать ли wildcard-запись или отдельные поддомены, управляемые DNS-провайдером."
},
"settingsIntegrations": {
"title": "Интеграции",
"outgoing": "Исходящие уведомления",
"outgoingDesc": "Куда Tinyforge отправляет события деплоев и алертов. Укажите webhook-URL (Apprise, Discord, Slack, свой обработчик).",
"incoming": "Входящие вебхуки",
"incomingMovedDesc": "Входящие вебхуки теперь привязаны к конкретному проекту или сайту. Откройте страницу проекта или статического сайта, чтобы увидеть и перегенерировать URL."
},
"webhookLog": {
"title": "Последние доставки вебхуков",
"description": "Последние 14 дней входящих вебхуков — результат, состояние подписи и причина. Обновляется каждые 30 секунд.",
"refresh": "Обновить",
"loadFailed": "Не удалось загрузить журнал доставок",
"empty": "Пока нет доставок.",
"colTime": "Когда",
"colStatus": "Статус",
"colOutcome": "Результат",
"colSignature": "Подпись",
"colDetail": "Подробности",
"colSource": "Источник",
"outcome": {
"deploy": "Развёрнуто",
"skip": "Пропущено",
"rejected": "Отклонено",
"not_found": "Не найдено",
"bad_request": "Неверный запрос",
"error": "Ошибка"
},
"sig": {
"valid": "верна",
"invalid": "неверна",
"missing": "отсутствует",
"unconfigured": "выкл"
}
},
"webhookPanel": {
"copy": "Копировать",
"copied": "Webhook-URL скопирован в буфер обмена",
"copyFailed": "Не удалось скопировать",
"noUrl": "Webhook-URL не настроен",
"loadFailed": "Не удалось загрузить webhook-URL",
"regenerate": "Перегенерировать URL",
"regenerated": "Webhook-URL перегенерирован",
"regenerateFailed": "Не удалось перегенерировать webhook-URL",
"regenerateWarning": "Перегенерация инвалидирует текущий URL. Обновите CI-пайплайны и Git-вебхуки, использующие его.",
"confirmRegenerate": "Заменить текущий URL?",
"confirmYes": "Перегенерировать",
"confirmNo": "Отмена",
"signingTitle": "Подпись входящих вебхуков (HMAC)",
"signingDesc": "Проверка подписи HMAC-SHA256 — утечка только URL не позволит подделать запрос. Совместимо с секретами вебхуков Gitea/GitHub.",
"signingActive": "Секрет подписи настроен.",
"signingInactive": "Секрет подписи не задан — входящие запросы не проверяются помимо URL.",
"signingIssue": "Сгенерировать секрет",
"signingRotate": "Перевыпустить секрет",
"signingDisable": "Отключить подпись",
"signingDisableConfirm": "Отключить",
"signingIssued": "Новый секрет подписи выпущен — скопируйте его сейчас",
"signingIssueFailed": "Не удалось сгенерировать секрет подписи",
"signingDisabled": "Подпись отключена",
"signingDisableFailed": "Не удалось отключить подпись",
"signingShownOnce": "Скопируйте секрет сейчас — он больше не будет показан.",
"signingDismiss": "Скрыть",
"signingHint": "Используйте это значение как webhook-секрет в Gitea/GitHub/GitLab. Tinyforge ожидает заголовок {header}.",
"signingCopied": "Секрет подписи скопирован в буфер обмена",
"requireSignature": "Требовать подпись",
"requireSignatureHelp": "Отклонять запросы без действительной подписи. Сначала сгенерируйте секрет.",
"signingRequireFailed": "Не удалось обновить требование подписи"
},
"outgoingWebhook": {
"signingOn": "Подпись включена",
"signingOff": "Без подписи",
"signingSecret": "HMAC-секрет",
"noSecret": "Секрет не задан — исходящие события отправляются без подписи.",
"reveal": "Показать",
"generate": "Сгенерировать",
"copy": "Копировать",
"copied": "Секрет скопирован в буфер обмена",
"copyFailed": "Не удалось скопировать",
"loadFailed": "Не удалось загрузить секрет",
"regenerate": "Перегенерировать",
"regenerated": "Секрет перегенерирован",
"regenerateFailed": "Не удалось перегенерировать секрет",
"confirmRegenerateTitle": "Перегенерировать секрет?",
"confirmRegenerate": "Текущий секрет инвалидируется немедленно. Все получатели должны быть обновлены синхронно — иначе начнут отклонять события.",
"confirmDisableTitle": "Отключить HMAC-подпись?",
"confirmDisable": "Будущие события пойдут без заголовка X-Hub-Signature-256. Получатели, требующие подпись, начнут их отклонять.",
"confirmYes": "Подтвердить",
"confirmNo": "Отмена",
"disable": "Отключить подпись",
"disabled": "Подпись отключена",
"disableFailed": "Не удалось отключить подпись",
"sendTestTitle": "Отправить тестовое событие",
"sendTestHelp": "Отправляет синтетическое событие \"test\" на разрешённый URL с текущим секретом.",
"sendTest": "Отправить тест",
"sending": "Отправка…",
"testFailed": "Не удалось отправить тестовое событие",
"tier": "Уровень",
"signed": "Подписано",
"unsigned": "Без подписи",
"deliveryId": "Доставка",
"responseBody": "Тело ответа",
"networkError": "Сетевая ошибка",
"fallbackTo": "URL не задан на этом уровне — события унаследуются от {label}.",
"noUrlConfigured": "URL не задан. Настройте его выше перед тестом."
},
"settingsMaintenance": {
"title": "Обслуживание",
"thresholds": "Пороги",
"thresholdsDesc": "Настройте, когда Tinyforge помечает контейнеры как устаревшие и предупреждает о неиспользуемых образах.",
"dangerZone": "Опасная зона"
},
"observability": {
"section": "Наблюдаемость",
"manage": "управление",
"loading": "Загрузка…",
"anyEvent": "любое событие",
"noUrlSet": "URL не настроен",
"configured": "НАСТРОЕН",
"clear": "Очистить",
"advanced": "Расширенно",
"cancel": "Отмена",
"save": "Сохранить",
"saving": "Сохранение…",
"delete": "Удалить",
"deleting": "Удаление…",
"refresh": "Обновить",
"open": "Открыть",
"edit": "Изменить",
"back": "Назад",
"regex": {
"sampleLabel": "Пример строки",
"placeholder": "вставьте сюда характерную строку лога",
"promptType": "введите образец для проверки шаблона",
"noMatch": "НЕТ СОВПАДЕНИЯ",
"noMatchHint": "шаблон не совпал с этой строкой",
"match": "СОВПАЛО",
"invalid": "REGEX",
"captures": "Группы"
}
},
"triggers": {
"title": "Триггеры событий",
"titleNew": "Новый триггер",
"titleSingular": "Триггер",
"lede": "Фильтруйте записи журнала событий (события деплоев, вывод сканера логов, будущие источники) и отправляйте webhook при совпадении. Фильтры объединяются по И; пустой фильтр означает «совпадает всё».",
"ledeNew": "Создайте правило «фильтр + действие». Диспетчер объединяет фильтры по И. Оставьте поле пустым, чтобы пропустить это измерение.",
"stat": {
"total": "ВСЕГО",
"enabled": "ВКЛЮЧЕНО",
"disabled": "ВЫКЛЮЧЕНО"
},
"toolbar": {
"newButton": "Новый триггер",
"backToList": "К списку триггеров"
},
"empty": {
"heading": "Триггеров пока нет",
"body": "Настройте триггер, чтобы пересылать записи журнала событий в Slack, мост уведомлений или любой HTTP-приёмник. Tinyforge подписывает запросы заголовком X-Hub-Signature-256, если задан секрет.",
"cta": "Создать первый триггер"
},
"list": {
"name": "Имя",
"filters": "Фильтры",
"action": "Действие",
"status": "Статус",
"open": "Открыть"
},
"detail": {
"config": "Конфигурация",
"configSub": "id #{id} · обновлено {updatedAt}",
"dangerZone": "Опасная зона",
"dangerZoneSub": "Удаление триггера происходит сразу. Восстановления нет.",
"sendTest": "Отправить тест",
"sending": "Отправка…",
"testHttp": "HTTP {code}",
"testSigned": "подписано",
"testOk": "OK",
"testFail": "ОШИБКА",
"deleteButton": "Удалить триггер",
"deleteTitle": "Удалить триггер?",
"deleteMessage": "Триггер «{name}» будет удалён немедленно. Действие необратимо."
},
"form": {
"name": "Имя",
"namePlaceholder": "например, Slack #alerts при сбое деплоя",
"required": "ОБЯЗАТЕЛЬНО",
"andComposed": "ОБЪЕДИНЕНИЕ ПО И",
"filtersLabel": "Фильтры",
"actionLabel": "Действие",
"actionWebhookBadge": "WEBHOOK",
"severityCsv": "Уровень (CSV)",
"severityPlaceholder": "warn,error",
"sourceCsv": "Источник (CSV)",
"sourcePlaceholder": "deploy,logscan",
"messageRegex": "Регулярное выражение сообщения (необязательно)",
"messageRegexPlaceholder": "(?i)\\bpanic\\b",
"invalidRegex": "Некорректный regex — сервер отклонит.",
"urlLabel": "URL",
"urlPlaceholder": "https://hooks.slack.com/services/...",
"secretLabel": "HMAC-секрет (необязательно)",
"secretPlaceholder": "оставьте пустым для неподписанной доставки",
"secretHint": "Приёмники проверяют X-Hub-Signature-256 по сырому телу запроса.",
"secretRotateHint": "Хранится в зашифрованном виде. После создания API не возвращает значение — оставьте плейсхолдер без изменений, чтобы сохранить существующий секрет, введите новое значение для смены или очистите и сохраните, чтобы отключить подпись.",
"enabled": "Включён",
"enabledHint": "Выключенные триггеры остаются в таблице, но не срабатывают.",
"submit": "Создать триггер",
"submitting": "Создание…",
"webhookUrl": "URL webhook"
},
"status": {
"enabled": "включён",
"disabled": "выключен"
}
},
"logscan": {
"title": "Правила сканирования логов",
"titleNew": "Новое правило",
"titleSingular": "Правило",
"lede": "Регулярные выражения, которые сканер применяет к потоку логов каждого работающего контейнера. Совпавшие строки попадают в event_log с уровнем правила, откуда триггеры событий передают их на настроенные webhook-приёмники. Включено {enabled} из {total}.",
"ledeNew": "Сканируйте логи контейнеров по регулярному выражению. Оставьте поле «нагрузка» пустым, чтобы создать глобальное правило. Чтобы переопределить глобальное для одной нагрузки, используйте действие «Переопределить» на странице нагрузки.",
"stat": {
"total": "ВСЕГО",
"global": "ГЛОБАЛЬНЫЕ",
"workload": "НАГРУЗКА",
"overrides": "ПЕРЕОПРЕДЕЛЕНИЯ",
"activeTails": "АКТИВНЫХ TAIL",
"droppedBucket": "ЛИМИТ",
"droppedCooldown": "COOLDOWN",
"compileErrors": "ОШИБКИ КОМПИЛЯЦИИ"
},
"stats": {
"heading": "Статистика сканера",
"headingSub": "Счётчики отбрасываний движка и ошибки компиляции из последнего снимка. Счётчики сбрасываются при перезапуске сервера.",
"noCompileErrors": "Все правила компилируются без ошибок.",
"compileErrorsHeading": "Ошибки компиляции (правило отброшено из снимка)",
"tailsExplain": "Сейчас открыто goroutine-tail'ов по контейнерам у менеджера сканера."
},
"toolbar": {
"newButton": "Новое правило",
"backToList": "К списку правил"
},
"filter": {
"all": "ВСЕ",
"global": "ГЛОБАЛЬНЫЕ",
"workload": "НАГРУЗКА",
"overrides": "ПЕРЕОПРЕДЕЛЕНИЯ"
},
"empty": {
"heading": "Правил пока нет",
"body": "Начните с глобального правила вроде (?i)\\bpanic\\b с уровнем error, затем сужайте по нагрузкам через переопределения на странице нагрузки.",
"cta": "Создать первое правило"
},
"list": {
"name": "Имя",
"pattern": "Шаблон",
"scope": "Область",
"severity": "Уровень",
"streams": "Потоки",
"status": "Статус",
"open": "Открыть"
},
"detail": {
"config": "Конфигурация",
"configSub": "id #{id} · область {scope}",
"regexTest": "Проверка regex",
"regexTestSub": "Предпросмотр использует JavaScript-движок regex в браузере. Нажмите «Проверить на сервере», чтобы получить авторитетную проверку Go RE2 — это единственный надёжный сигнал для конструкций, специфичных для RE2.",
"runServerTest": "Проверить на сервере",
"testing": "Проверка…",
"serverTestHint": "Сначала введите пример строки выше",
"serverTestSendHint": "Отправить пример на backend /test",
"serverMatch": "СОВПАЛО (СЕРВЕР)",
"serverNoMatch": "НЕТ СОВПАДЕНИЯ",
"serverNoMatchHint": "серверный regex не совпал с примером",
"serverError": "ОШИБКА",
"dangerZone": "Опасная зона",
"dangerZoneSub": "Удаление глобального правила каскадно удаляет его переопределения для нагрузок.",
"deleteButton": "Удалить правило",
"deleteTitle": "Удалить правило?",
"deleteMessage": "Правило «{name}» будет удалено немедленно. Переопределения по нагрузкам, ссылающиеся на него, также удалятся."
},
"form": {
"name": "Имя",
"namePlaceholder": "например, Panic в воркере",
"pattern": "Шаблон",
"regex": "REGEX",
"patternPlaceholder": "(?i)\\bpanic\\b",
"invalidRegex": "Некорректный regex — сервер отклонит.",
"matchShape": "Параметры совпадения",
"matchShapeOpts": "УРОВЕНЬ · ПОТОКИ · COOLDOWN",
"severity": "Уровень",
"streams": "Потоки",
"cooldown": "Cooldown (с)",
"cooldownHint": "Cooldown — на правило × на контейнер: одно правило, срабатывающее в двух контейнерах, считается независимо. Token bucket ограничивает выдачу на контейнер до 10 событий / 60с, чтобы не переполнить event_log.",
"scope": "Область",
"scopePlaceholder": "пусто для глобального правила или вставьте id нагрузки",
"scopeHint": "Правила области нагрузки применяются только к её контейнерам. Переопределения для отдельных нагрузок проще создавать со страницы нагрузки.",
"scopeGlobal": "Глобально (применяется ко всем нагрузкам)",
"scopePick": "Выбрать нагрузку…",
"scopePickTitle": "Выберите нагрузку",
"scopeClear": "Сделать глобальным",
"scopeSelected": "Нагрузка",
"scopeUnknown": "Неизвестная нагрузка",
"enabled": "Включено",
"enabledHint": "Выключенные правила остаются в таблице, но не срабатывают.",
"required": "ОБЯЗАТЕЛЬНО",
"optional": "НЕОБЯЗАТЕЛЬНО",
"submit": "Создать правило",
"submitting": "Создание…"
},
"scope": {
"global": "глобальное",
"workload": "нагрузка {id}",
"override": "переопределение #{id}",
"overrideShort": "переопр. #{id}"
},
"status": {
"enabled": "включено",
"disabled": "выключено",
"on": "вкл",
"off": "выкл"
},
"panel": {
"heading": "Лог-правила",
"subEmpty": "Для этой нагрузки правил нет",
"subCount": "Действует правил: {count}",
"subCountOne": "Действует 1 правило",
"emptyHint": "Для этой нагрузки нет правил сканирования логов. Создайте через «Новое правило» — глобальные правила применяются автоматически; для этой нагрузки также можно завести свои или переопределения.",
"newRule": "Новое правило",
"footerHint": "Глобальные правила применяются ко всем нагрузкам. Правила нагрузки — только здесь. Переопределения замещают глобальное для этой нагрузки — изменяйте уровень или отключайте их, не трогая исходное глобальное.",
"override": "Переопределить",
"overriding": "Переопределение…",
"overrideTitle": "Создать переопределение глобального правила для этой нагрузки"
}
}
}