{ "app": { "name": "Tinyforge", "version": "v0.1" }, "layout": { "serviceStatus": "Состояние служб" }, "health": { "connected": "подключён", "disconnected": "отключён", "rawError": "Технические детали", "retryNow": "Проверить сейчас" }, "nav": { "dashboard": "Панель", "apps": "Приложения", "eventTriggers": "Триггеры", "logScanRules": "Лог-правила", "triggers": "Триггеры", "proxies": "Прокси", "events": "События", "settings": "Настройки", "logout": "Выйти", "dns": "DNS-записи", "containers": "Контейнеры" }, "dashboard": { "title": "Панель управления", "newApp": "Новое приложение", "totalWorkloads": "Всего нагрузок", "runningContainers": "Запущенных контейнеров", "failedContainers": "Сбойных контейнеров", "recentWorkloads": "Недавние нагрузки", "retry": "Повторить", "noWorkloads": "Нагрузок пока нет.", "noWorkloadsDesc": "Создайте приложение и выкуйте первую нагрузку, чтобы начать.", "loadFailed": "Не удалось загрузить панель", "staleContainers": "Устаревшие контейнеры", "unusedImagesWarning": "Неиспользуемые Docker-образы занимают дисковое пространство", "unusedImages": "неиспользуемых образов", "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ч." }, "tagPicker": { "registry": "Реестр", "local": "Локальный" }, "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": "Прокси-маршруты", "description": "Активные прокси-маршруты от контейнеров и статических сайтов.", "domain": "Домен", "project": "Проект / Сайт", "stage": "Этап / Режим", "tag": "Тег", "port": "Порт", "status": "Статус", "source": "Источник", "sourceContainer": "Контейнер", "sourceStatic": "Статический сайт", "sourceDeno": "Deno-сайт", "filterAll": "Все", "filterContainers": "Контейнеры", "filterSites": "Сайты", "noRoutes": "Нет прокси-маршрутов", "noRoutesDesc": "Прокси-маршруты создаются автоматически при развёртывании контейнера с прокси или публикации статического сайта.", "searchPlaceholder": "Поиск по домену, проекту или тегу...", "noMatch": "Нет маршрутов, соответствующих поиску.", "loadFailed": "Не удалось загрузить прокси-маршруты", "route": "маршрут", "routes": "маршрутов", "actions": "Действия", "viewTriggers": "Триггеры", "viewTriggersTitle": "Посмотреть привязки триггеров для этой нагрузки" }, "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": "Замечен" } }, "empty": { "noRegistries": "Нет реестров", "noRegistriesDesc": "Добавьте реестр контейнеров для обнаружения образов.", "noUsers": "Нет пользователей", "noUsersDesc": "Добавьте локальных пользователей для управления доступом." }, "validation": { "required": "Поле {field} обязательно", "invalidUrl": "Неверный формат URL", "invalidDomain": "Неверный формат домена", "invalidIp": "Неверный формат IP", "invalidEmail": "Неверный формат email", "invalidPort": "Порт должен быть от 1 до 65535", "invalidPollingInterval": "Интервал опроса должен быть от 60 до 86400 секунд", "invalidProjectName": "Допускаются только строчные буквы, цифры и дефисы", "requiredWhenUpdating": "Поле {field} обязательно при обновлении учётных данных", "requiredForNew": "Поле {field} обязательно для новых реестров" }, "theme": { "light": "Светлая", "dark": "Тёмная", "system": "Системная" }, "entityPicker": { "search": "Поиск...", "noResults": "Ничего не найдено" }, "stale": { "title": "Устаревшие контейнеры", "noStale": "Нет устаревших контейнеров", "noStaleDesc": "Все контейнеры исправны и работают.", "cleanup": "Очистить", "cleanupAll": "Очистить все", "confirmCleanup": "Это остановит и удалит контейнер. Продолжить?", "confirmBulkCleanup": "Это остановит и удалит все устаревшие контейнеры. Продолжить?", "daysStale": "дней устарел", "lastAlive": "Последний раз жив", "count": "Устаревшие", "cleanedUp": "Контейнер очищен", "bulkCleanedUp": "{count} контейнеров очищено", "cleanupFailed": "Не удалось очистить", "loadFailed": "Не удалось загрузить устаревшие контейнеры" }, "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": "Русский" }, "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": "Создать переопределение глобального правила для этой нагрузки" } }, "redeployTriggers": { "section": "Кузница", "title": "Триггеры передеплоя", "titleNew": "Новый триггер", "titleSingular": "Триггер", "lede": "Источники сигналов передеплоя — push в registry, события git, ручной запуск, расписания, webhook'и, совпадения в логах. Триггер создаётся один раз и веером раздаёт сигнал всем привязанным к нему нагрузкам.", "ledeNew": "Выберите вид, дайте имя и решите, могут ли внешние системы дёргать его через webhook. Привязку к нагрузкам делайте со страницы нагрузки после создания.", "ledeDetail": "Редактируйте конфигурацию триггера, управляйте webhook-приёмом и просматривайте все нагрузки, слушающие этот сигнал.", "stat": { "total": "ВСЕГО", "byKind": "{kind}", "withWebhook": "С WEBHOOK", "boundWorkloads": "НАГРУЗОК" }, "kind": { "registry": "Registry", "git": "Git", "manual": "Ручной", "schedule": "Расписание", "webhook": "Webhook", "logscan": "Лог-скан", "unknown": "Неизвестный" }, "kindShort": { "registry": "REG", "git": "GIT", "manual": "MAN", "schedule": "CRN", "webhook": "HK", "logscan": "LOG", "unknown": "?" }, "kindHint": { "registry": "Следит за образом контейнера; срабатывает при push нового тега, подходящего под шаблон.", "git": "Срабатывает при продвижении указанной ветки или создании тега, подходящего под шаблон.", "manual": "Срабатывает только через кнопку Deploy на странице нагрузки или POST /workloads/{id}/deploy.", "schedule": "Срабатывает по фиксированному cron-расписанию.", "webhook": "Чистый webhook — срабатывает при обращении к URL приёма.", "logscan": "Срабатывает, когда правило сканирования логов совпадает со строкой.", "unknown": "Неизвестный вид триггера — используйте сырой JSON-редактор." }, "toolbar": { "newButton": "Новый триггер", "backToList": "К списку триггеров" }, "filter": { "all": "ВСЕ", "ariaLabel": "Фильтр по виду" }, "empty": { "heading": "Триггеров пока нет", "body": "Триггер — источник сигнала передеплоя: registry-watcher, git-hook, ручная кнопка, расписание или webhook. Создайте один и привяжите к скольким угодно нагрузкам.", "cta": "Создать первый триггер" }, "list": { "name": "Имя", "kind": "Вид", "bindings": "Нагрузки", "webhook": "Webhook", "created": "Создан", "open": "Открыть", "webhookOn": "ВКЛ", "webhookOff": "—", "noBindings": "—", "bindingsCount": "{count}" }, "detail": { "config": "Конфигурация триггера", "configSub": "вид {kind} · id {id} · обновлено {updatedAt}", "webhook": "Webhook-приём", "webhookSub": "Когда включено, внешние системы могут дёргать триггер по URL ниже. Каждая привязанная нагрузка будет передеплоена по очереди.", "webhookEnable": "Включить webhook-приём", "webhookEnableHint": "Когда выключено, триггер срабатывает только из внутренних источников (по конфигу его вида) и кнопки ручного деплоя.", "webhookRequireSig": "Требовать HMAC-подпись", "webhookRequireSigHint": "Отклонять запросы без корректного X-Hub-Signature-256. Рекомендуется, если URL доступен из публичной сети.", "webhookUrlLabel": "URL приёма", "webhookUrlNote": "Вставьте это в настройки CI / registry / webhook GitHub. Сегмент-секрет — это пароль, обращайтесь как с паролем.", "webhookCopy": "Копировать", "webhookCopied": "Скопировано", "webhookRotate": "Сменить секрет", "webhookRotating": "Смена…", "webhookDisabledNote": "Webhook-приём выключен. Включите тумблер, сохраните — и URL появится здесь.", "bindings": "Привязанные нагрузки", "bindingsSub": "Все нагрузки, слушающие этот триггер. Чтобы привязать новую нагрузку, откройте её страницу и добавьте этот триггер оттуда.", "bindingsEmpty": "К этому триггеру пока не привязана ни одна нагрузка. Откройте нагрузку и привяжите этот триггер из её панели «Триггеры».", "bindingsListItem": { "openWorkload": "Открыть нагрузку", "unbind": "Отвязать" }, "bindingEnabledHint": "Выключите, чтобы оставить привязку, но запретить триггеру передеплоить эту нагрузку.", "dangerZone": "Опасная зона", "dangerZoneSub": "Удаление триггера происходит сразу. Все привязки к нему удаляются каскадом.", "deleteButton": "Удалить триггер", "deleteTitle": "Удалить триггер?", "deleteMessage": "Триггер «{name}» будет удалён немедленно вместе с {count} привязкой(-ами). Действие необратимо.", "rotateTitle": "Сменить секрет webhook?", "rotateMessage": "Текущий URL приёма перестанет работать сразу. После смены обновите URL во всех внешних интеграциях.", "rotateConfirm": "Сменить", "unbindTitle": "Отвязать нагрузку?", "unbindMessage": "Нагрузка «{name}» перестанет передеплоиваться при срабатывании этого триггера. Сама нагрузка не удаляется.", "unbindConfirm": "Отвязать", "lastFired": "Последний запуск", "lastFiredNever": "Ни разу не срабатывал", "scheduleStatus": "Состояние расписания", "scheduleStatusSub": "Рабочее состояние внутреннего планировщика для этого триггера. «Запустить сейчас» сдвигает следующий запуск и начинает отсчёт нового интервала с этого момента.", "fireNow": "Запустить сейчас", "fireNowTitle": "Запустить триггер немедленно и сбросить окно следующего срабатывания.", "fireNowDisabledTitle": "Привяжите хотя бы одну нагрузку перед запуском.", "firing": "Запуск…", "fireConfirmTitle": "Запустить триггер расписания?", "fireConfirmMessage": "Триггер «{name}» сработает немедленно и развернёт {count} связанных нагрузок. Следующий естественный запуск будет через полный интервал от текущего момента.", "fireConfirm": "Запустить", "fireResult": "Сработал · задеплоено {deployed}/{bindings} · ошибок {errored}" }, "form": { "kindLabel": "Вид", "kindHint": "Выберите источник сигнала передеплоя. Форма ниже подстраивается под вид.", "name": "Имя", "namePlaceholder": "например, ghcr.io/me/api · main", "required": "ОБЯЗАТЕЛЬНО", "configLabel": "Конфигурация", "image": "Ссылка на образ", "imagePlaceholder": "registry.example.com/owner/app", "imageHint": "Полная ссылка на образ без тега — Tinyforge ловит новые теги, выкладываемые под этой ссылкой.", "tagPattern": "Шаблон тега", "tagPatternPlaceholder": "*", "tagPatternHint": "Glob path.Match (например, v*, release-*). * совпадает с любым тегом.", "repo": "Репозиторий", "repoPlaceholder": "owner/name", "repoHint": "owner/name в формате git-хостинга, не зависит от провайдера.", "mode": "Режим", "modePush": "Push в ветку", "modeTag": "Создание тега", "branch": "Ветка", "branchPlaceholder": "main", "branchHint": "Только push'и, продвигающие эту ветку, дёргают триггер.", "manualNote": "У ручных триггеров нет конфига. Они срабатывают только через кнопку Deploy на странице нагрузки или POST /workloads/{id}/deploy.", "scheduleNote": "Срабатывает по фиксированному интервалу, который ведёт внутренний планировщик Tinyforge. Внешний webhook не нужен — включите его ниже только если CI тоже должен запускать триггер вручную.", "intervalPresets": "Быстрые пресеты", "intervalPreset": { "hourly": "Каждый час", "daily": "Каждый день", "weekly": "Каждую неделю" }, "interval": "Интервал", "intervalHint": "Длительность в формате Go (например «30m», «6h», «24h», «168h»). Минимум 1 минута.", "scheduleReference": "Фиксированная ссылка (опционально)", "scheduleReferencePlaceholder": "stable", "scheduleReferenceHint": "Опциональный тег, ветка или ревизия, которые источник будет подтягивать на каждом срабатывании. Оставьте пустым, чтобы использовать значение по умолчанию.", "unknownNote": "У этого вида ещё нет встроенной формы. Используйте JSON-редактор ниже; сервер валидирует форму.", "advancedToggle": "Расширенный JSON", "advancedHint": "Запасной вариант для опытных пользователей — заменяет структурированную форму сырым payload'ом.", "configJson": "JSON конфигурации", "configJsonHint": "Должен распарситься как корректный JSON-объект. Структура проверяется сервером по виду.", "invalidJson": "Некорректный JSON — сервер отклонит.", "webhookEnabled": "Включить webhook-приём сразу", "webhookEnabledHint": "Генерирует секретный URL, по которому внешние системы могут дёргать триггер.", "webhookRequireSig": "Требовать HMAC-подпись", "webhookRequireSigHint": "Отклонять неподписанные запросы. Секрет — тот же, что вшит в URL — подпишите тело HMAC-SHA256 и пришлите в X-Hub-Signature-256.", "submit": "Создать триггер", "submitting": "Создание…", "cancel": "Отмена" }, "binding": { "enabled": "Включена", "disabled": "Выключена" } }, "apps": { "list": { "eyebrowSuffix": "ПРИЛОЖЕНИЯ", "title": "Приложения", "ledePrefix": "Plugin-native деплои —", "ledeKindImage": "image", "ledeKindCompose": "compose", "ledeKindStatic": "static", "ledeMiddle": ", или", "ledeSuffix": ", с подключаемыми триггерами передеплоя. Старые проекты, стеки и сайты на время переезда остаются в собственных разделах.", "statTotal": "ВСЕГО", "statImage": "IMAGE", "statCompose": "COMPOSE", "statStatic": "STATIC", "refresh": "Обновить", "newApp": "Новое приложение", "filterAriaLabel": "Фильтр по source-плагину", "filterAll": "ВСЕ", "loadError": "Не удалось загрузить приложения", "alertTag": "ОШ", "emptyTitle": "Приложений пока нет", "emptyBody": "Приложения объединяют image, compose и static-деплои за единым plugin-driven интерфейсом. Создайте первое, чтобы увидеть его здесь.", "emptyCta": "Создать первое приложение", "colName": "Имя", "colSource": "Источник", "colTrigger": "Триггер", "colCreated": "Создано", "colActions": "Действия", "rowOpen": "Открыть" }, "new": { "pageTitle": "Новое приложение · Tinyforge", "backLabel": "К приложениям", "eyebrowSuffix": "НОВОЕ ПРИЛОЖЕНИЕ", "title": "Создать приложение", "ledePrefix": "Создайте plugin-native нагрузку.", "ledeSourceLabel": "Источник", "ledeSourceMid": "= как она деплоится (image, compose, static). Выберите или создайте", "ledeTriggerLabel": "триггер", "ledeSuffix": "ниже — при срабатывании source-плагин передеплоит приложение.", "loadingKinds": "Загрузка доступных видов плагинов…", "alertTag": "ОШ", "fieldName": "Имя", "fieldNameRequired": "ОБЯЗАТЕЛЬНО", "fieldNamePlaceholder": "my-app", "fieldNameHint": "В нижнем регистре, без пробелов. Используется в именах контейнеров и поддоменах.", "fieldSourcePlugin": "Source-плагин", "fieldSourceLabel": "Источник", "fieldSourceHint": "Список берётся из запущенного демона — видны только вкомпилированные плагины. Триггеры (registry / git / manual) настраиваются ниже как отдельные записи.", "fieldSourceConfig": "Конфигурация источника", "fieldConfigYaml": "YAML", "fieldConfigForm": "ФОРМА", "fieldConfigJson": "JSON", "advancedJson": "Расширенный JSON", "backToForm": "К форме", "resetSample": "Сбросить к примеру", "switchToJsonTitle": "Переключиться на сырой JSON-редактор", "switchToFormTitle": "Вернуться к форме", "jsonOk": "JSON ОК", "jsonInvalid": "JSON НЕВЕРНЫЙ", "linesUnit": "строк", "composeHeader": "compose.yaml · compose", "composeAriaLabel": "Compose YAML", "composeProjectLabel": "Имя compose-проекта (опционально)", "composeProjectPlaceholder": "(по умолчанию — нормализованное имя нагрузки)", "composePlaceholder": "services:\n web:\n image: nginx:alpine\n ports:\n - \"80\"", "imageHeader": "image-источник · параметры рантайма", "imageRefLabel": "Образ (путь в реестре)", "imageRefPlaceholder": "registry.example.com/owner/app", "imageRefHint": "Полная ссылка без тега; тег задаётся при деплое — триггером или полем «Тег по умолчанию» ниже.", "imagePort": "Порт", "imageHealthcheck": "Путь healthcheck", "imageDefaultTag": "Тег по умолчанию", "imageRegistryLabel": "Реестр (для приватных pull-ов)", "imageRegistryPublic": "(публичный — без авторизации)", "imageRegistryHint": "Имя должно совпадать с записью на странице «Реестры». Оставьте пустым для публичных образов.", "imageCpu": "Лимит CPU (ядра, 0 = ∞)", "imageMemory": "Лимит памяти (МБ, 0 = ∞)", "imageMax": "Макс. инстансов", "imageMaxHint": "1 = строгий blue-green.", "imageFoot": "Переменные окружения и тома задаются в отдельных панелях на странице нагрузки после создания.", "staticHeader": "static-источник · страницы из репозитория", "staticProvider": "Провайдер", "staticBaseUrl": "Base URL", "staticBaseUrlPlaceholder": "https://git.example.com", "staticRepoOwner": "Владелец репозитория", "staticRepoOwnerPlaceholder": "owner", "staticRepoName": "Имя репозитория", "staticRepoNamePlaceholder": "pages", "staticBranch": "Ветка", "staticBranchPlaceholder": "main", "staticFolder": "Папка (опционально)", "staticFolderPlaceholder": "(корень репозитория)", "staticToken": "Токен доступа (приватные репозитории)", "staticTokenPlaceholder": "(оставьте пустым для публичных репозиториев)", "staticTokenHint": "Шифруется при хранении. Нужен только для приватных репозиториев.", "staticMode": "Режим", "staticModeStaticDesc": "— раздача файлов через nginx; ноль рантайм-оверхеда.", "staticModeDenoDesc": "— Deno-рантайм с опциональной динамической маршрутизацией.", "staticRenderMarkdown": "Рендерить markdown", "staticRenderMarkdownDesc": "— автоматически отдавать .md файлы как HTML-страницы.", "staticFoot": "Секрет вебхука для git push-триггеров появляется в панели вебхука нагрузки после создания.", "staticDetectProvider": "Определить", "staticDetectedOk": "Определено: {provider}", "staticDetectedFailed": "Не удалось определить: {error}", "staticTestConnection": "Проверить соединение", "staticConnectionOk": "Соединение установлено", "staticConnectionFailed": "Ошибка соединения: {error}", "staticBrowseRepos": "Выбрать репозиторий", "staticBrowseBranches": "Выбрать ветку", "staticBrowseFolders": "Выбрать папку", "staticPickerRepoTitle": "Выбор репозитория", "staticPickerRepoPlaceholder": "Фильтр репозиториев…", "staticPickerBranchTitle": "Выбор ветки", "staticPickerBranchPlaceholder": "Фильтр веток…", "staticFolderRoot": "/ (корень)", "staticFolderSelectedPrefix": "Выбранная папка:", "staticTreeLoading": "Загрузка дерева папок…", "staticTreeEmpty": "В этой ветке нет папок.", "staticDenoAutoDetected": "Обнаружена папка api/ — режим автоматически переключён на Deno.", "imageConflictTag": "ОБРАЗ УЖЕ ИСПОЛЬЗУЕТСЯ", "imageConflictHeading": "Этот образ уже используется в {count} нагрузке(ах):", "imageConflictOpenBtn": "Открыть", "imageConflictAcknowledgeNote": "Если это намеренно (например, отдельный этап), нажмите «Создать» ещё раз для продолжения.", "imageConflictBlockedSubmit": "Обнаружены конфликты по этому образу — изучите список выше и нажмите «Создать» повторно для продолжения.", "sourceConfigJsonTitle": "source_config.json · {kind}", "sourceConfigJsonAria": "Конфигурация source-плагина (JSON)", "triggerNumLabel": "Триггер", "triggerNumOptional": "ОПЦИОНАЛЬНО", "triggerNewTag": "НОВЫЙ", "triggerPickTag": "ВЫБРАТЬ", "triggerSkipTag": "ПРОПУСК", "noteSkipTag": "ПРОПУСК", "noteEmptyTag": "∅", "faceLabel": "Публичный фронт", "faceOptional": "ОПЦИОНАЛЬНО", "faceSubdomain": "Поддомен", "faceSubdomainPlaceholder": "myapp", "faceDomain": "Домен", "faceDomainPlaceholder": "(наследуется из настроек)", "facePort": "Целевой порт", "faceHint": "Оставьте пустым, чтобы не создавать прокси-маршрут. Заполнение любого поля создаст одну запись фронта, привязанную к этой нагрузке.", "cancel": "Отмена", "submit": "Создать приложение", "submitting": "Создание…", "submitAnyway": "Всё равно создать", "errors": { "detectionFailed": "Не удалось определить провайдера.", "connectionFailed": "Ошибка соединения.", "reposFailed": "Не удалось загрузить репозитории.", "branchesFailed": "Не удалось загрузить ветки.", "treeFailed": "Не удалось загрузить дерево папок.", "sourceConfigInvalid": "source_config не является корректным JSON.", "triggerBindUnknown": "неизвестная ошибка", "createFailed": "Не удалось создать нагрузку.", "inspectFailed": "Не удалось проинспектировать образ." }, "imageInspect": "Инспектировать", "imageInspectHint": "Подставляет порт и healthcheck из образа, чтобы не вводить вручную.", "imageInspectOk": "Готово — порт и healthcheck подставлены.", "imageInspectError": "Ошибка инспекции: {error}", "triggers": { "section": "Триггер", "sectionSub": "Необязательно. Выберите, откуда придёт сигнал передеплоя — слежение за реестром, git-событие или ручная кнопка.", "modeInline": "Создать триггер", "modeInlineHint": "Создаёт новую запись триггера, привязанную к этому приложению — подходит для частого случая 1:1.", "modePick": "Выбрать существующий", "modePickHint": "Привязать существующий триггер, чтобы несколько приложений делили один сигнал.", "modeSkip": "Пропустить — добавить позже", "modeSkipHint": "Приложение создаётся без привязки триггера. Ручной деплой по-прежнему работает.", "switchToPick": "Выбрать существующий →", "switchToInline": "← Создать новый триггер", "switchToSkip": "Пропустить", "pickPlaceholder": "Выберите триггер…", "pickEmpty": "Триггеров ещё нет — создайте один выше или перейдите в /triggers.", "pickLabel": "Существующий триггер", "pickHint": "Один триггер можно привязать к нескольким приложениям. Управление автономными триггерами — в /triggers.", "pickWebhookOn": "ВЕБХУК ВКЛ", "skippedNote": "Триггер не будет привязан. Добавьте его из панели «Триггеры» в карточке приложения после создания.", "bindError": "Приложение создано, но привязка триггера не удалась: {error}. Откройте панель «Триггеры» в карточке, чтобы повторить." } }, "detail": { "pageTitleFallback": "Приложение", "backLabel": "К приложениям", "eyebrowSuffix": "ПРИЛОЖЕНИЕ", "kickerId": "id: {id}", "loading": "Загрузка нагрузки…", "loadError": "Не удалось загрузить приложение", "deployError": "Деплой не удался", "saveError": "Сохранение не удалось", "deleteError": "Удаление не удалось", "runtimeState": { "title": "Статус синхронизации", "sub": "Последняя успешная синхронизация репозитория и текущее состояние контейнера.", "status": "Статус", "lastCommit": "Последний коммит", "lastSync": "Последняя синхронизация", "container": "Контейнер", "neverDeployed": "Ещё не разворачивалось. Нажмите «Деплой», чтобы опубликовать впервые.", "loading": "Загрузка состояния…" }, "storage": { "title": "Постоянное хранилище", "sub": "Смонтировано в /app/data внутри контейнера.", "used": "Использовано", "limit": "Лимит", "unlimited": "без лимита", "unavailable": "Не удалось получить размер (контейнер мог быть остановлен).", "loading": "Вычисление размера…" }, "toolbar": { "stop": "Стоп", "start": "Старт", "openSite": "Открыть", "more": "Ещё" }, "liveBadge": { "running": "РАБОТАЕТ", "transitioning": "ПЕРЕХОД", "stopped": "ОСТАНОВЛЕНО", "notDeployed": "НЕ РАЗВЁРНУТО", "mixed": "СМЕШАННО · {running}/{total} РАБОТАЕТ" }, "stats": { "title": "Ресурсы", "sub": "CPU и память запущенного контейнера.", "subMany": "CPU и память по каждому из {count} контейнеров." }, "webhooks": { "title": "Привязки вебхуков", "sub": "Триггеры, привязанные к приложению — управление URL и подписями на странице триггера.", "openTrigger": "Открыть триггер", "disabled": "отключён", "empty": "К приложению не привязан ни один триггер." }, "errors": { "stopFailed": "Не удалось остановить.", "stopNothing": "Останавливать нечего — нет запущенного контейнера.", "stopAllFailed": "Остановка не удалась — все контейнеры отклонили запрос.", "startFailed": "Не удалось запустить.", "startNothing": "Запускать нечего — сначала выполните Деплой, чтобы создать контейнер.", "startAllFailed": "Запуск не удался — все контейнеры отклонили запрос.", "runtimeStateFailed": "Не удалось загрузить состояние.", "storageFailed": "Не удалось загрузить размер хранилища." }, "alertTag": "ОШ", "createdAt": "создано", "refreshLabel": "Обновить", "editButton": "Изменить", "deleteButton": "Удалить", "editTitle": "Редактирование конфигурации", "editSubPrefix": "Источник", "editSubSuffix": "· триггеры настраиваются в панели «Триггеры» ниже", "editFieldName": "Имя", "editFieldParent": "Родительская нагрузка", "editFieldOptional": "ОПЦИОНАЛЬНО", "editFieldParentPlaceholder": "UUID нагрузки (пусто для корневой)", "editSourceConfig": "Конфигурация источника", "editConfigYaml": "YAML", "editConfigForm": "ФОРМА", "editConfigJson": "JSON", "advancedJson": "Расширенный JSON", "backToForm": "К форме", "switchToJsonTitle": "Переключиться на сырой JSON-редактор", "switchToFormTitle": "Вернуться к форме", "jsonOk": "JSON ОК", "jsonInvalid": "JSON НЕВЕРНЫЙ", "editComposeProject": "Имя compose-проекта (опционально)", "editComposeProjectPlaceholder": "(по умолчанию — нормализованное имя нагрузки)", "editComposePlaceholder": "services:\n web:\n image: nginx:alpine", "editComposeAria": "Compose YAML", "editComposeHeader": "compose.yaml", "editImageHeader": "image-источник · параметры рантайма", "editImageRef": "Образ (путь в реестре)", "editImageRefPlaceholder": "registry.example.com/owner/app", "editImagePort": "Порт", "editImageHealthcheck": "Путь healthcheck", "editImageDefaultTag": "Тег по умолчанию", "editImageRegistry": "Реестр (для приватных pull-ов)", "editImageRegistryPublic": "(публичный — без авторизации)", "editImageCpu": "Лимит CPU (ядра, 0 = ∞)", "editImageMemory": "Лимит памяти (МБ, 0 = ∞)", "editImageMax": "Макс. инстансов", "editImageFoot": "Переменные окружения и тома живут в своих панелях ниже — сохранение здесь их не затронет.", "editStaticHeader": "static-источник · страницы из репозитория", "editStaticProvider": "Провайдер", "editStaticBaseUrl": "Base URL", "editStaticBaseUrlPlaceholder": "https://git.example.com", "editStaticRepoOwner": "Владелец репозитория", "editStaticRepoName": "Имя репозитория", "editStaticBranch": "Ветка", "editStaticFolder": "Папка (опционально)", "editStaticFolderPlaceholder": "(корень репозитория)", "editStaticToken": "Токен доступа (приватные репозитории)", "editStaticTokenPlaceholder": "(оставьте пустым для публичных репозиториев)", "editStaticMode": "Режим", "editStaticModeStaticDesc": "— раздача файлов через nginx.", "editStaticModeDenoDesc": "— Deno-рантайм с динамической маршрутизацией.", "editStaticRenderMarkdown": "Рендерить markdown", "editStaticRenderMarkdownDesc": "— автоматически отдавать .md как HTML.", "editSourceJsonHeader": "source_config.json", "editSourceJsonAria": "Конфигурация source-плагина (JSON)", "editPublicFaces": "Публичные фронты", "editPublicFacesTag": "JSON МАССИВ", "editPublicFacesHeader": "public_faces.json", "editPublicFacesAria": "Конфигурация публичных фронтов (JSON-массив)", "editCancel": "Отмена", "editSave": "Сохранить", "editSaving": "Сохранение…", "manualDeployTitle": "Ручной деплой", "manualDeployOk": "ОК", "manualDeployDispatched": "Отправлено {reference} как {by}", "manualDeployRefAria": "Референс для деплоя", "manualDeployRefPlaceholder": "референс (тег образа, git sha; пусто — по умолчанию)", "manualDeployButton": "Деплой", "manualDeployDispatching": "Отправка…", "manualDeployHint": "Задайте конкретный тег образа, git sha или ветку, чтобы принудительно деплоить. Оставьте пустым, чтобы source-плагин выбрал референс по умолчанию.", "containersTitle": "Контейнеры", "containersEmpty": "Контейнеров пока нет", "containersCount": "{count} согласовано", "containersEmptyInline": "Контейнеров пока нет — задеплойте, чтобы поднять первый.", "containersColRole": "Роль", "containersColState": "Состояние", "containersColImage": "Образ", "containersColSubdomain": "Поддомен", "containersColLastSeen": "Последний раз виден", "containersColActions": "Действия", "containersLogsAction": "Логи", "chainTitle": "Цепочка", "chainSubFromParent": "продвигается от родителя", "chainSubParentOf": "родитель для", "chainChildSingular": "дочерней нагрузки", "chainChildPlural": "дочерних нагрузок", "chainParentLabel": "Родитель", "chainSelfLabel": "Эта", "chainChildrenLabel": "Дочерние", "chainPromoteButton": "Продвинуть от родителя", "chainPromoting": "Продвижение…", "chainHint": "Задайте parent_workload_id у нагрузки, чтобы построить цепочку. Дочерние image-источники могут одним кликом продвинуть текущий запущенный тег родителя.", "volumesTitle": "Тома", "volumesEmpty": "Нет монтирований", "volumesCountSingular": "{count} монтирование", "volumesCountPlural": "{count} монтирований", "volumesColTarget": "Цель", "volumesColSource": "Источник", "volumesColScope": "Скоуп", "volumesColUpdated": "Обновлено", "volumesColActions": "Действия", "volumeSource": "Источник (хост)", "volumeSourcePlaceholder": "/srv/data/myapp", "volumeTarget": "Цель (контейнер)", "volumeTargetPlaceholder": "/data", "volumeScope": "Скоуп", "volumeAddButton": "Добавить / Заменить", "volumeSaving": "Сохранение…", "volumeHint": "Абсолютные монтирования прокидывают путь хоста в контейнер. Другие скоупы приняты для будущих сценариев; сегодня при деплое применяется только absolute.", "volumeTargetError": "Цель должна быть абсолютным путём в контейнере (например, /data)", "volumeSetFailed": "Не удалось задать том", "volumeDeleteFailed": "Не удалось удалить том", "envTitle": "Окружение", "envEmpty": "Нет переопределений", "envCountSingular": "{count} переопределение", "envCountPlural": "{count} переопределений", "envColKey": "Ключ", "envColValue": "Значение", "envColUpdated": "Обновлено", "envColActions": "Действия", "envEncrypted": "ЗАШИФРОВАНО", "envKey": "Ключ", "envKeyPlaceholder": "DATABASE_URL", "envValue": "Значение", "envValuePlaceholder": "(пусто — снять)", "envEncryptLabel": "Шифровать при хранении", "envAddButton": "Добавить / Заменить", "envSaving": "Сохранение…", "envHint": "Зашифрованные значения после записи доступны только на запись — API скрывает их при чтении. Ротация — установка нового значения.", "envKeyRequired": "Ключ обязателен", "envSetFailed": "Не удалось задать переменную", "envDeleteFailed": "Не удалось удалить переменную", "sourceConfigTitle": "Конфигурация источника", "sourceConfigCopy": "Копировать", "sourceConfigCopied": "Скопировано", "sourceConfigCopyAria": "Копировать конфиг источника", "publicFacesTitle": "Публичные фронты", "publicFacesCopyAria": "Копировать публичные фронты", "deleteConfirmTitle": "Удалить это приложение?", "deleteConfirmMessage": "Сносит все контейнеры и прокси-маршруты, принадлежащие «{name}», затем удаляет запись. Это нельзя отменить.", "deleteConfirmFallbackName": "эта нагрузка", "deleteConfirmYes": "Да, удалить", "deleteConfirmDeleting": "Удаление…", "manualDeploySub": "Обходит настроенные триггеры и отправляет деплой напрямую через source-плагин.", "chainTriggersZero": "без триггеров", "chainTriggersOne": "1 триггер", "chainTriggersMany": "{count} триггер(ов)", "bindings": { "title": "Триггеры", "subEmpty": "Триггеры не привязаны. Ручной деплой работает — добавьте триггер, чтобы подключить передеплой по реестру / git / вебхуку.", "subCount": "{count} привязанный триггер", "subCountMany": "{count} привязанных триггеров", "addButton": "Добавить триггер", "openTrigger": "Открыть триггер", "unbindAction": "Отвязать", "rowEnabled": "Включён", "rowDisabled": "Выключен", "rowEnableHint": "Отключите, чтобы сохранить привязку, но остановить передеплой этого приложения.", "loading": "Загрузка триггеров…", "loadError": "Не удалось загрузить привязки триггеров", "unbindTitle": "Отвязать триггер?", "unbindMessage": "Триггер «{name}» перестанет передеплоить это приложение. Сам триггер не удаляется — он остаётся в /triggers и сохраняет привязки к другим приложениям.", "unbindConfirm": "Отвязать", "modal": { "title": "Добавить триггер", "subtitle": "Привяжите триггер к этому приложению — создайте новый или выберите существующий, чтобы делить его.", "tabInline": "Создать новый", "tabPick": "Выбрать существующий", "submitInline": "Создать и привязать", "submitPick": "Привязать", "submitting": "Привязка…", "cancel": "Отмена", "error": "Не удалось привязать", "pickPlaceholder": "Выберите триггер…", "pickEmpty": "Триггеров ещё нет — переключитесь на «Создать новый», чтобы добавить.", "pickLabel": "Существующий триггер", "pickKind": "Фильтр по виду", "pickKindAll": "Все виды" }, "override": { "toggle": "Переопределить", "title": "Переопределения привязки", "subtitle": "Переопределите поля конфига триггера только для этого приложения. Верхнеуровневые ключи отсюда побеждают; остальное наследуется из триггера.", "badgeOne": "ПЕРЕОПРЕДЕЛЕНО: 1 ПОЛЕ", "badgeMany": "ПЕРЕОПРЕДЕЛЕНО ПОЛЕЙ: {count}", "badgeTitle": "Эта привязка переопределяет одно или несколько полей конфига триггера.", "baseLabel": "Конфиг триггера", "baseLoading": "Загрузка конфига триггера…", "baseHint": "Конфиг родительского триггера в режиме чтения. Редактируйте его на странице триггера, если изменения нужны для всех привязок.", "editLabel": "Переопределение (JSON-объект)", "editHint": "Слияние по верхнему уровню: переопределяются только указанные здесь ключи. Оставьте {} — будет наследоваться без изменений.", "previewLabel": "Итоговый конфиг", "previewHint": "Предпросмотр того, что увидит эта привязка при срабатывании триггера (конфиг триггера + наложенное переопределение).", "invalidJson": "Переопределение должно быть JSON-объектом.", "tooLarge": "Размер переопределения — {size} Б, превышает серверный лимит {limit} Б.", "errInvalidJson": "Нельзя сохранить: переопределение не является валидным JSON-объектом.", "errTooLarge": "Нельзя сохранить: переопределение превышает серверный лимит 8 КиБ.", "saveButton": "Сохранить переопределение", "saving": "Сохранение…", "resetButton": "Сбросить к наследованию", "closeButton": "Закрыть" } } } } }