From 0cfa67ea0a01b998ad995ae9d3dae7d2628897df Mon Sep 17 00:00:00 2001 From: Evgeny Polupanov Date: Sun, 19 Jul 2026 15:50:07 +0300 Subject: [PATCH] fix(i18n): align Russian locale with dev --- frontend/scripts/check-i18n-parity.mjs | 13 + frontend/src/i18n/locales/ru.ts | 11429 ++++++++++++----------- 2 files changed, 5749 insertions(+), 5693 deletions(-) diff --git a/frontend/scripts/check-i18n-parity.mjs b/frontend/scripts/check-i18n-parity.mjs index dd7acf207..3b5566217 100644 --- a/frontend/scripts/check-i18n-parity.mjs +++ b/frontend/scripts/check-i18n-parity.mjs @@ -382,6 +382,18 @@ const TR_COGNATES = [ '{{filament}} @ {{temp}}°C', // drying badge: filament code + universal °C ]; +const RU_COGNATES = [ + 'MakerWorld: {{designer}}', + 'email', + '{{printer}}: {{error}}', + 'Bambu Cloud', + 'Orca Cloud', + '{{name}} — {{stage}} ({{percent}}%) — {{elapsed}}', + 'EC984C,#6CD4BC,A66EB9,D87694', + '({{count}}/8)', + '(25%, 50%, 75%)', +]; + const IDENTICAL_TO_EN_ALLOWED = { de: new Set(DE_COGNATES), fr: new Set(FR_COGNATES), @@ -393,6 +405,7 @@ const IDENTICAL_TO_EN_ALLOWED = { 'zh-CN': new Set(ZH_CN_COGNATES), 'zh-TW': new Set(ZH_TW_COGNATES), tr: new Set(TR_COGNATES), + ru: new Set(RU_COGNATES), }; // Pure comparison logic, exported so tests can verify each failure mode diff --git a/frontend/src/i18n/locales/ru.ts b/frontend/src/i18n/locales/ru.ts index e9d17867b..75f4b66e1 100644 --- a/frontend/src/i18n/locales/ru.ts +++ b/frontend/src/i18n/locales/ru.ts @@ -1,6422 +1,6465 @@ -/** - * Russian locale — complete translation. - */ export default { nav: { - printers: 'Принтеры', - archives: 'Архив', - queue: 'Очередь печати', - stats: 'Статистика', - profiles: 'Профили', - maintenance: 'Обслуживание', - projects: 'Проекты', - inventory: 'Филамент', - files: 'Файловый менеджер', - makerworld: 'MakerWorld', - notifications: 'Уведомления', - settings: 'Настройки', - system: 'Система', - collapseSidebar: 'Свернуть боковую панель', - expandSidebar: 'Развернуть боковую панель', - update: 'Обновить', - updateAvailable: 'Доступно обновление: v{{version}}', - updateAvailableBanner: 'Доступна версия {{version}}!', - viewUpdate: 'Посмотреть обновление', - viewOnGithub: 'Открыть на GitHub', - keyboardShortcuts: 'Горячие клавиши (?)', - switchToLight: 'Включить светлую тему', - switchToDark: 'Включить тёмную тему', - switchToSystem: 'Использовать системную тему', - smartSwitches: 'Умные выключатели', - logout: 'Выйти', - installApp: 'Установить приложение', - installAppSuccess: 'Bambuddy установлен', + printers: "Принтеры", + archives: "Архив", + queue: "Очередь печати", + stats: "Статистика", + profiles: "Профили", + maintenance: "Обслуживание", + projects: "Проекты", + inventory: "Филамент", + files: "Файловый менеджер", + makerworld: "MakerWorld", + notifications: "Уведомления", + settings: "Настройки", + system: "Система", + collapseSidebar: "Свернуть боковую панель", + expandSidebar: "Развернуть боковую панель", + update: "Обновить", + updateAvailable: "Доступно обновление: v{{version}}", + updateAvailableBanner: "Доступна версия {{version}}!", + viewUpdate: "Посмотреть обновление", + viewOnGithub: "Открыть на GitHub", + keyboardShortcuts: "Горячие клавиши (?)", + switchToLight: "Включить светлую тему", + switchToDark: "Включить тёмную тему", + switchToSystem: "Использовать системную тему", + smartSwitches: "Умные выключатели", + logout: "Выйти", + installApp: "Установить приложение", + installAppSuccess: "Bambuddy установлен", }, common: { - save: 'Сохранить', - saving: 'Сохранение...', - cancel: 'Отмена', - delete: 'Удалить', - edit: 'Изменить', - add: 'Добавить', - close: 'Закрыть', - confirm: 'Подтвердить', - loading: 'Загрузка...', - error: 'Ошибка', - errorLoading: 'Не удалось загрузить данные', - retry: 'Повторить', - success: 'Готово', - warning: 'Предупреждение', - enabled: 'Включено', - disabled: 'Отключено', - yes: 'Да', - no: 'Нет', - on: 'Вкл.', - off: 'Выкл.', - all: 'Все', - none: 'Нет', - search: 'Поиск', - filter: 'Фильтр', - sort: 'Сортировка', - refresh: 'Обновить', - download: 'Скачать', - upload: 'Загрузить', - uploading: 'Загрузка...', - uploadFailed: 'Ошибка загрузки', - actions: 'Действия', - status: 'Состояние', - name: 'Название', - description: 'Описание', - date: 'Дата', - time: 'Время', - hours: 'часов', - minutes: 'минут', - seconds: 'секунд', - days: 'дней', - enable: 'Включить', - disable: 'Отключить', - permissions: 'Разрешения', - noPrinters: 'Принтеры не настроены', - noData: 'Нет данных', - linkNotFound: 'Ссылка не найдена', - required: 'Обязательно', - optional: 'Необязательно', - dismiss: 'Закрыть', - apply: 'Применить', - reset: 'Сбросить', - export: 'Экспорт', - import: 'Импорт', - clear: 'Очистить', - selectAll: 'Выбрать все', - deselectAll: 'Снять выделение', - noChange: '— Без изменений —', - unchanged: 'Без изменений', - unassigned: 'Не назначено', - unknown: 'Неизвестно', - unknownError: 'Неизвестная ошибка', - today: 'Сегодня', - tomorrow: 'Завтра', - asap: 'Как можно скорее', - overdue: 'Просрочено', - now: 'Сейчас', - collapse: 'Свернуть', - expand: 'Развернуть', - previous: 'Назад', - next: 'Далее', - viewArchive: 'Открыть архив', - viewInFileManager: 'Открыть в файловом менеджере', - addedBy: 'Добавил: {{username}}', - prints: 'печатей', - more: 'ещё +{{count}}', - ascending: 'По возрастанию', - descending: 'По убыванию', - back: 'Назад', - copy: 'Копировать', - copied: 'Скопировано!', - printer: 'Принтер', - remove: 'Удалить', - type: 'Тип', - print: 'Печать', - rename: 'Переименовать', - move: 'Переместить', - create: 'Создать', - duplicate: 'Дублировать', - left: 'Левый', - right: 'Правый', + save: "Сохранить", + saving: "Сохранение...", + cancel: "Отмена", + delete: "Удалить", + edit: "Изменить", + add: "Добавить", + close: "Закрыть", + confirm: "Подтвердить", + loading: "Загрузка...", + error: "Ошибка", + errorLoading: "Не удалось загрузить данные", + retry: "Повторить", + success: "Готово", + warning: "Предупреждение", + enabled: "Включено", + disabled: "Отключено", + yes: "Да", + no: "Нет", + on: "Вкл.", + off: "Выкл.", + all: "Все", + none: "Нет", + search: "Поиск", + filter: "Фильтр", + sort: "Сортировка", + refresh: "Обновить", + download: "Скачать", + upload: "Загрузить", + uploading: "Загрузка...", + uploadFailed: "Ошибка загрузки", + actions: "Действия", + status: "Состояние", + name: "Название", + description: "Описание", + date: "Дата", + time: "Время", + hours: "часов", + minutes: "минут", + seconds: "секунд", + days: "дней", + enable: "Включить", + disable: "Отключить", + permissions: "Разрешения", + noPrinters: "Принтеры не настроены", + noData: "Нет данных", + linkNotFound: "Ссылка не найдена", + required: "Обязательно", + optional: "Необязательно", + dismiss: "Закрыть", + apply: "Применить", + reset: "Сбросить", + export: "Экспорт", + import: "Импорт", + clear: "Очистить", + selectAll: "Выбрать все", + deselectAll: "Снять выделение", + noChange: "— Без изменений —", + unchanged: "Без изменений", + unassigned: "Не назначено", + unknown: "Неизвестно", + unknownError: "Неизвестная ошибка", + today: "Сегодня", + tomorrow: "Завтра", + asap: "Как можно скорее", + overdue: "Просрочено", + now: "Сейчас", + collapse: "Свернуть", + expand: "Развернуть", + previous: "Назад", + next: "Далее", + viewArchive: "Открыть архив", + viewInFileManager: "Открыть в файловом менеджере", + addedBy: "Добавил: {{username}}", + prints: "печатей", + more: "ещё +{{count}}", + ascending: "По возрастанию", + descending: "По убыванию", + back: "Назад", + copy: "Копировать", + copied: "Скопировано!", + printer: "Принтер", + remove: "Удалить", + type: "Тип", + print: "Печать", + rename: "Переименовать", + move: "Переместить", + create: "Создать", + duplicate: "Дублировать", + left: "Левый", + right: "Правый", }, printers: { - title: 'Принтеры', - addPrinter: 'Добавить принтер', + title: "Принтеры", + addPrinter: "Добавить принтер", addPreflight: { - checking: 'Проверка подключения...', - warning: 'Некоторые проверки подключения не пройдены. Принтер может отображаться как отключённый. Проверьте результаты ниже, исправьте возможные проблемы или сохраните настройки как есть.', - back: 'Назад', - saveAnyway: 'Всё равно сохранить', + checking: "Проверка подключения...", + warning: "Некоторые проверки подключения не пройдены. Принтер может отображаться как отключённый. Проверьте результаты ниже, исправьте возможные проблемы или сохраните настройки как есть.", + back: "Назад", + saveAnyway: "Всё равно сохранить", }, - editPrinter: 'Изменить принтер', - deletePrinter: 'Удалить принтер', - printerName: 'Название принтера', - serialNumber: 'Серийный номер', - ipAddress: 'IP-адрес / имя хоста', - accessCode: 'Код доступа', - model: 'Модель', - nozzleCount: 'Количество сопел', - autoArchive: 'Автоматически архивировать', + editPrinter: "Изменить принтер", + deletePrinter: "Удалить принтер", + printerName: "Название принтера", + serialNumber: "Серийный номер", + ipAddress: "IP-адрес / имя хоста", + accessCode: "Код доступа", + model: "Модель", + nozzleCount: "Количество сопел", + autoArchive: "Автоматически архивировать", status: { - title: 'Статус', - available: 'Доступен', - idle: 'Ожидает', - printing: 'Печать', - paused: 'Приостановлен', - offline: 'Не в сети', - problem: 'Проблема', - error: 'Ошибка', - finished: 'Завершено', - unknown: 'Неизвестно', + available: "Доступен", + idle: "Ожидает", + printing: "Печать", + paused: "Приостановлен", + offline: "Не в сети", + problem: "Проблема", + error: "Ошибка", + finished: "Завершено", + unknown: "Неизвестно", }, temperatures: { - nozzle: 'Сопло', - bed: 'Стол', - chamber: 'Камера', + nozzle: "Сопло", + bed: "Стол", + chamber: "Камера", }, heaterHistory: { - title: 'История нагрева', - openLabel: 'Открыть историю нагрева', - nozzle: 'Сопло', - nozzle2: 'Сопло 2', - bed: 'Стол', - chamber: 'Камера', - error: 'Не удалось загрузить историю', - empty: 'Данные пока не записывались', + title: "История нагрева", + openLabel: "Открыть историю нагрева", + nozzle: "Сопло", + nozzle2: "Сопло 2", + bed: "Стол", + chamber: "Камера", + error: "Не удалось загрузить историю", + empty: "Данные пока не записывались", }, - progress: 'Выполнено {{percent}}%', - timeRemaining: 'Осталось {{time}}', - deleteConfirm: 'Удалить принтер «{{name}}»?', - maintenanceOk: 'Обслуживание в норме', - maintenanceWarning: '{{count}} предупреждение', - maintenanceWarning_plural: '{{count}} предупреждений', - maintenanceDue: 'Требуется: {{count}}', - maintenanceDue_plural: 'Требуется: {{count}}', + progress: "Выполнено {{percent}}%", + timeRemaining: "Осталось {{time}}", + deleteConfirm: "Удалить принтер «{{name}}»?", + maintenanceOk: "Обслуживание в норме", + maintenanceWarning: "{{count}} предупреждение", + maintenanceWarning_plural: "{{count}} предупреждений", + maintenanceDue: "Требуется: {{count}}", + maintenanceDue_plural: "Требуется: {{count}}", sort: { - name: 'Название', - status: 'Состояние', - model: 'Модель', - location: 'Расположение', - eta: 'Время завершения', - ascending: 'По возрастанию', - descending: 'По убыванию', + name: "Название", + status: "Состояние", + model: "Модель", + location: "Расположение", + eta: "Время завершения", + ascending: "По возрастанию", + descending: "По убыванию", }, cardSize: { - small: 'Маленькие карточки', - medium: 'Средние карточки', - large: 'Большие карточки', - extraLarge: 'Очень большие карточки', + small: "Маленькие карточки", + medium: "Средние карточки", + large: "Большие карточки", + extraLarge: "Очень большие карточки", }, pageView: { - cards: 'Карточки', - camWall: 'Стена камер', + openCamWallPage: "Открыть стену камер как отдельную страницу", + cards: "Карточки", + camWall: "Стена камер", }, camWall: { - noPrinters: 'Нет принтеров для отображения', - noSignal: 'Нет сигнала', - live: 'Онлайн', - snap: 'Снимок', - off: 'Выкл.', - summary: 'Онлайн: {{live}}, снимки: {{snap}}, всего: {{total}}', - layer: 'Слой {{cur}}/{{total}}', - timeLeft: 'Осталось {{time}}', + page: { + tokenRejected: "Эта ссылка на стену камер больше недействительна. Возможно, срок действия токена истёк или он был отозван.", + loadFailed: "Не удалось загрузить принтеры.", + }, + noPrinters: "Нет принтеров для отображения", + noSignal: "Нет сигнала", + live: "Онлайн", + snap: "Снимок", + off: "Выкл.", + summary: "Онлайн: {{live}}, снимки: {{snap}}, всего: {{total}}", + layer: "Слой {{cur}}/{{total}}", + timeLeft: "Осталось {{time}}", statusMode: { - off: 'Выкл.', - compact: 'Компактный', - full: 'Полный', + off: "Выкл.", + compact: "Компактный", + full: "Полный", }, settings: { - title: 'Настройки стены камер', - maxLive: 'Максимум онлайн-трансляций', - maxLiveHint: 'Сколько камер одновременно показывать в реальном времени. Остальные будут обновляться снимками.', - snapshotInterval: 'Интервал снимков, с', - snapshotIntervalHint: 'Как часто обновлять снимки камер без онлайн-трансляции.', - statusOverlay: 'Наложение состояния', - statusOverlayHint: 'Компактный режим: только состояние. Полный: прогресс, слой и оставшееся время.', + title: "Настройки стены камер", + maxLive: "Максимум онлайн-трансляций", + maxLiveHint: "Сколько камер одновременно показывать в реальном времени. Остальные будут обновляться снимками.", + snapshotInterval: "Интервал снимков, с", + snapshotIntervalHint: "Как часто обновлять снимки камер без онлайн-трансляции.", + statusOverlay: "Наложение состояния", + statusOverlayHint: "Компактный режим: только состояние. Полный: прогресс, слой и оставшееся время.", }, }, - hideOffline: 'Скрыть принтеры не в сети', - nextAvailable: 'Ближайший доступный', - power: 'Питание', - powerOn: 'Включить питание', - offlinePrintersWithPlugs: 'Принтеры не в сети с умными розетками', - noPrintersConfigured: 'Принтеры пока не настроены', - search: 'Поиск принтеров...', - noSearchResults: 'Нет принтеров, соответствующих поиску или фильтрам', + hideOffline: "Скрыть принтеры не в сети", + nextAvailable: "Ближайший доступный", + powerOn: "Включить питание", + offlinePrintersWithPlugs: "Принтеры не в сети с умными розетками", + noPrintersConfigured: "Принтеры пока не настроены", + search: "Поиск принтеров...", + noSearchResults: "Нет принтеров, соответствующих поиску или фильтрам", filter: { - allStatuses: 'Все состояния', - allLocations: 'Все расположения', + allStatuses: "Все состояния", + allLocations: "Все расположения", }, toolbar: { - filters: 'Фильтры', - view: 'Вид', - actions: 'Действия', + filters: "Фильтры", + view: "Вид", + actions: "Действия", }, - readyToPrint: 'Готов к печати', - external: 'Внешняя подача', - extL: 'Внешняя, левое', - extR: 'Внешняя, правое', - deleteArchives: 'Удалить архивы печати', - noLabel: 'Без метки', - printPreview: 'Предпросмотр печати', - width: 'Ширина', - height: 'Высота', - noObjectsFound: 'Объекты не найдены', - objectsLoadedOnPrintStart: 'Объекты загрузятся после начала печати', - willBeSkipped: 'Будет пропущен', - name: 'Название', - serialCannotBeChanged: 'Серийный номер нельзя изменить', - locationHelp: 'Используется для группировки принтеров и фильтрации заданий в очереди', + readyToPrint: "Готов к печати", + external: "Внешняя подача", + extL: "Внешняя, левое", + extR: "Внешняя, правое", + deleteArchives: "Удалить архивы печати", + noLabel: "Без метки", + printPreview: "Предпросмотр печати", + width: "Ширина", + height: "Высота", + noObjectsFound: "Объекты не найдены", + objectsLoadedOnPrintStart: "Объекты загрузятся после начала печати", + willBeSkipped: "Будет пропущен", + name: "Название", + serialCannotBeChanged: "Серийный номер нельзя изменить", + locationHelp: "Используется для группировки принтеров и фильтрации заданий в очереди", wifiSignal: { - veryWeak: 'Очень слабый', - weak: 'Слабый', - fair: 'Средний', - good: 'Хороший', - excellent: 'Отличный', + veryWeak: "Очень слабый", + weak: "Слабый", + fair: "Средний", + good: "Хороший", + excellent: "Отличный", }, - maintenanceUpToDate: 'Обслуживание выполнено — нажмите для просмотра', + maintenanceUpToDate: "Обслуживание выполнено — нажмите для просмотра", maintenance: { - title: 'На обслуживании', - subtitle: 'Принтер приостановлен: подключение отключено, задания из очереди не назначаются, уведомления не отправляются.', - pillLabel: 'Обслуживание', - exitButton: 'Завершить обслуживание', - menuEnter: 'Перевести в режим обслуживания', - menuExit: 'Выйти из режима обслуживания', - toastEntered: 'Принтер {{name}} переведён в режим обслуживания', - toastExited: 'Принтер {{name}} снова в работе', - confirmMidPrintTitle: 'Перевести в режим обслуживания во время печати?', - confirmMidPrintMessage: 'Принтер {{name}} сейчас печатает. Режим обслуживания отключит MQTT, отслеживание прогресса и уведомление о завершении этого задания. Продолжить?', - editFieldLabel: 'Режим обслуживания', - editFieldHelp: 'Отключает для принтера MQTT, назначение заданий из очереди и уведомления. Используйте при ремонте, параллельных установках Bambuddy или временной приостановке.', + title: "На обслуживании", + subtitle: "Принтер приостановлен: подключение отключено, задания из очереди не назначаются, уведомления не отправляются.", + pillLabel: "Обслуживание", + exitButton: "Завершить обслуживание", + menuEnter: "Перевести в режим обслуживания", + menuExit: "Выйти из режима обслуживания", + toastEntered: "Принтер {{name}} переведён в режим обслуживания", + toastExited: "Принтер {{name}} снова в работе", + confirmMidPrintTitle: "Перевести в режим обслуживания во время печати?", + confirmMidPrintMessage: "Принтер {{name}} сейчас печатает. Режим обслуживания отключит MQTT, отслеживание прогресса и уведомление о завершении этого задания. Продолжить?", + editFieldLabel: "Режим обслуживания", + editFieldHelp: "Отключает для принтера MQTT, назначение заданий из очереди и уведомления. Используйте при ремонте, параллельных установках Bambuddy или временной приостановке.", }, - chamberLightOn: 'Включить подсветку камеры', - chamberLightOff: 'Выключить подсветку камеры', - files: 'Файлы', - browseFiles: 'Открыть файлы принтера', - autoOffAfterPrint: 'Автовыключение после печати', - autoOffExecuted: 'Автовыключение выполнено — включите принтер для сброса состояния', - hmsErrors: 'Ошибки HMS', - viewHmsErrors: 'Показать ошибки HMS: {{count}}', - resume: 'Продолжить', - pause: 'Пауза', - stop: 'Стоп', - camera: 'Камера', - skipObject: 'Пропустить объект', - reconnect: 'Переподключить', - forceRefresh: 'Принудительно обновить', - forceRefreshSuccess: 'Запрошено обновление данных', - mqttDebug: 'Отладка MQTT', - printerInformation: 'Информация о принтере', - copyToClipboard: 'Копировать', - copied: 'Скопировано!', - state: 'Состояние', - wifiSignalLabel: 'Сигнал Wi‑Fi', - developerMode: 'Режим разработчика', - enabled: 'Включён', - disabled: 'Отключён', - addedOn: 'Добавлен', - sdCard: 'SD-карта', - inserted: 'Установлена', - notInserted: 'Не установлена', - totalPrintHours: 'Часов печати', - activeNozzle: 'Активное сопло: {{nozzle}}', - nozzleRack: 'Держатель сопел', - nozzleDocked: 'В держателе', - nozzleMounted: 'Установлено', - nozzleActive: 'Активно', - nozzleIdle: 'Ожидание', - nozzleDiameter: 'Диаметр', - nozzleType: 'Тип', - nozzleStatus: 'Состояние', - nozzleFilament: 'Филамент', - nozzleWear: 'Износ', - nozzleMaxTemp: 'Макс. температура', - nozzleSerial: 'Серийный номер', - nozzleHardenedSteel: 'Закалённая сталь', - nozzleStainlessSteel: 'Нержавеющая сталь', - nozzleTungstenCarbide: 'Карбид вольфрама', - nozzleFlow: 'Производительность', - nozzleHighFlow: 'Высокопоточное', - nozzleStandardFlow: 'Стандартное', - firmwareUpdate: 'Обновление прошивки', - firmwareInstructions: 'На сенсорном экране принтера откройте', - firmwareNav: 'Перейдите в раздел', - settings: 'Настройки', - firmware: 'Прошивка', - discoverPrinters: 'Найти принтеры', - searching: 'Поиск...', - manualEntry: 'Добавить вручную', - addFromCloud: 'Добавить из облака', + chamberLightOn: "Включить подсветку камеры", + chamberLightOff: "Выключить подсветку камеры", + files: "Файлы", + browseFiles: "Открыть файлы принтера", + autoOffAfterPrint: "Автовыключение после печати", + autoOffExecuted: "Автовыключение выполнено — включите принтер для сброса состояния", + hmsErrors: "Ошибки HMS", + viewHmsErrors: "Показать ошибки HMS: {{count}}", + resume: "Продолжить", + pause: "Пауза", + stop: "Стоп", + camera: "Камера", + skipObject: "Пропустить объект", + reconnect: "Переподключить", + forceRefresh: "Принудительно обновить", + forceRefreshSuccess: "Запрошено обновление данных", + mqttDebug: "Отладка MQTT", + printerInformation: "Информация о принтере", + copyToClipboard: "Копировать", + copied: "Скопировано!", + state: "Состояние", + wifiSignalLabel: "Сигнал Wi‑Fi", + developerMode: "Режим разработчика", + enabled: "Включён", + disabled: "Отключён", + addedOn: "Добавлен", + sdCard: "SD-карта", + inserted: "Установлена", + notInserted: "Не установлена", + totalPrintHours: "Часов печати", + activeNozzle: "Активное сопло: {{nozzle}}", + nozzleRack: "Держатель сопел", + nozzleDocked: "В держателе", + nozzleMounted: "Установлено", + nozzleActive: "Активно", + nozzleIdle: "Ожидание", + nozzleDiameter: "Диаметр", + nozzleType: "Тип", + nozzleStatus: "Состояние", + nozzleFilament: "Филамент", + nozzleWear: "Износ", + nozzleMaxTemp: "Макс. температура", + nozzleSerial: "Серийный номер", + nozzleHardenedSteel: "Закалённая сталь", + nozzleStainlessSteel: "Нержавеющая сталь", + nozzleTungstenCarbide: "Карбид вольфрама", + nozzleFlow: "Производительность", + nozzleHighFlow: "Высокопоточное", + nozzleStandardFlow: "Стандартное", + firmwareUpdate: "Обновление прошивки", + firmwareInstructions: "На сенсорном экране принтера откройте", + firmwareNav: "Перейдите в раздел", + settings: "Настройки", + firmware: "Прошивка", + discoverPrinters: "Найти принтеры", + searching: "Поиск...", + manualEntry: "Добавить вручную", + addFromCloud: "Добавить из облака", toast: { - printerDeleted: 'Принтер удалён', - missingSpoolAssignment: 'На принтере {{printer}} началась печать. Не назначены катушки для слотов: {{slots}}', - printerAdded: 'Принтер добавлен', - printerUpdated: 'Настройки принтера обновлены', - failedToDelete: 'Не удалось удалить принтер', - failedToAdd: 'Не удалось добавить принтер', - connectionFailedNotAdded: 'Не удалось подключиться к принтеру. Проверьте IP-адрес, серийный номер и код доступа, а также убедитесь, что включён режим «Только локальная сеть». Принтер не добавлен.', - failedToUpdate: 'Не удалось обновить настройки принтера', - commandSent: 'Команда отправлена', - failedToSendCommand: 'Не удалось отправить команду', - turnedOn: 'Питание {{name}} включено', - failedToPowerOn: 'Не удалось включить {{name}}', - scriptTriggered: 'Сценарий запущен', - printStopped: 'Печать остановлена', - printPaused: 'Печать приостановлена', - printResumed: 'Печать продолжена', - referenceDeleted: 'Эталон удалён', - detectionAreaSaved: 'Область обнаружения сохранена', - failedToRunScript: 'Не удалось запустить сценарий', - failedToStopPrint: 'Не удалось остановить печать', - failedToPausePrint: 'Не удалось приостановить печать', - failedToResumePrint: 'Не удалось продолжить печать', - failedToControlChamberLight: 'Не удалось изменить состояние подсветки камеры', - failedToSetSpeed: 'Не удалось изменить скорость печати', - failedToUpdateSetting: 'Не удалось изменить настройку', - failedToSkipObjects: 'Не удалось пропустить объекты', - failedToRereadRfid: 'Не удалось повторно считать RFID', - failedToCheckPlate: 'Не удалось проверить пластину', - failedToUpdateLabel: 'Не удалось обновить метку', - failedToDeleteReference: 'Не удалось удалить эталон', - failedToSaveDetectionArea: 'Не удалось сохранить область обнаружения', - plateCheckEnabled: 'Проверка пластины включена', - plateCheckDisabled: 'Проверка пластины отключена', - calibrationSaved: 'Калибровка сохранена', - calibrationFailed: 'Не удалось выполнить калибровку', - rfidRereadInitiated: 'Запущено повторное считывание RFID', - loadInitiated: 'Загрузка филамента…', - unloadInitiated: 'Выгрузка филамента…', - failedToLoad: 'Не удалось загрузить филамент', - failedToUnload: 'Не удалось выгрузить филамент', + printerDeleted: "Принтер удалён", + missingSpoolAssignment: "На принтере {{printer}} началась печать. Не назначены катушки для слотов: {{slots}}", + printerAdded: "Принтер добавлен", + printerUpdated: "Настройки принтера обновлены", + failedToDelete: "Не удалось удалить принтер", + failedToAdd: "Не удалось добавить принтер", + connectionFailedNotAdded: "Не удалось подключиться к принтеру. Проверьте IP-адрес, серийный номер и код доступа, а также убедитесь, что включён режим «Только локальная сеть». Принтер не добавлен.", + failedToUpdate: "Не удалось обновить настройки принтера", + commandSent: "Команда отправлена", + failedToSendCommand: "Не удалось отправить команду", + turnedOn: "Питание {{name}} включено", + failedToPowerOn: "Не удалось включить {{name}}", + scriptTriggered: "Сценарий запущен", + printStopped: "Печать остановлена", + printPaused: "Печать приостановлена", + printResumed: "Печать продолжена", + referenceDeleted: "Эталон удалён", + detectionAreaSaved: "Область обнаружения сохранена", + failedToRunScript: "Не удалось запустить сценарий", + failedToStopPrint: "Не удалось остановить печать", + failedToPausePrint: "Не удалось приостановить печать", + failedToResumePrint: "Не удалось продолжить печать", + failedToControlChamberLight: "Не удалось изменить состояние подсветки камеры", + failedToSetSpeed: "Не удалось изменить скорость печати", + failedToUpdateSetting: "Не удалось изменить настройку", + failedToSkipObjects: "Не удалось пропустить объекты", + failedToRereadRfid: "Не удалось повторно считать RFID", + failedToCheckPlate: "Не удалось проверить пластину", + failedToUpdateLabel: "Не удалось обновить метку", + failedToDeleteReference: "Не удалось удалить эталон", + failedToSaveDetectionArea: "Не удалось сохранить область обнаружения", + plateCheckEnabled: "Проверка пластины включена", + plateCheckDisabled: "Проверка пластины отключена", + calibrationSaved: "Калибровка сохранена", + calibrationFailed: "Не удалось выполнить калибровку", + rfidRereadInitiated: "Запущено повторное считывание RFID", + loadInitiated: "Загрузка филамента…", + unloadInitiated: "Выгрузка филамента…", + failedToLoad: "Не удалось загрузить филамент", + failedToUnload: "Не удалось выгрузить филамент", }, connection: { - connected: 'Подключён', - offline: 'Не в сети', + connected: "Подключён", + offline: "Не в сети", }, plateStatus: { - markCleared: 'Отметить пластину как очищенную', - cleared: 'Пластина очищена', - notCleared: 'Пластина не очищена', - inUse: 'Пластина используется', + markCleared: "Отметить пластину как очищенную", + cleared: "Пластина очищена", + notCleared: "Пластина не очищена", + inUse: "Пластина используется", }, queue: { - inQueue: 'В очереди: {{count}} задание', - inQueue_plural: 'В очереди: {{count}} заданий', + inQueue: "В очереди: {{count}} задание", + inQueue_plural: "В очереди: {{count}} заданий", }, - controls: 'Управление', + controls: "Управление", rfid: { - reread: 'Повторно считать RFID', + reread: "Повторно считать RFID", }, ams: { - load: 'Загрузить', - unload: 'Выгрузить', + load: "Загрузить", + unload: "Выгрузить", }, bedJog: { - title: 'Ручное перемещение', - bed: 'Стол', - step: 'Шаг (мм)', - up: 'Поднять стол', - down: 'Опустить стол', - disabledWhilePrinting: 'Недоступно во время печати', - notHomedTitle: 'Принтер не выполнил парковку по осям', - notHomedMessage: 'После последней печати принтер не выполнял Home. Сначала запустите автоматическую парковку для безопасного позиционирования (парковка печатающей головы и поиск нуля по X, Y и Z) либо продолжите перемещение — программные концевики будут отключены.', - homeZ: 'Автопарковка', - moveAnyway: 'Всё равно переместить', - homingStarted: 'Выполняется автопарковка…', + limitWarning: "При ручном перемещении ограничения хода не контролируются: из-за ошибки прошивки Bambu программные концевики игнорируются для удалённых команд. Перемещайте осторожно, чтобы избежать столкновения.", + title: "Ручное перемещение", + bed: "Стол", + step: "Шаг (мм)", + up: "Поднять стол", + down: "Опустить стол", + disabledWhilePrinting: "Недоступно во время печати", + notHomedTitle: "Принтер не выполнил парковку по осям", + notHomedMessage: "После последней печати принтер не выполнял Home. Сначала запустите автоматическую парковку для безопасного позиционирования (парковка печатающей головы и поиск нуля по X, Y и Z) либо продолжите перемещение — программные концевики будут отключены.", + homeZ: "Автопарковка", + moveAnyway: "Всё равно переместить", + homingStarted: "Выполняется автопарковка…", }, permission: { - noAdd: 'У вас нет разрешения на добавление принтеров', - noEdit: 'У вас нет разрешения на изменение принтеров', - noDelete: 'У вас нет разрешения на удаление принтеров', - noControl: 'У вас нет разрешения на управление принтерами', - noFiles: 'У вас нет разрешения на доступ к файлам принтера', - noAmsRfid: 'У вас нет разрешения на повторное считывание RFID AMS', - noSmartPlugControl: 'У вас нет разрешения на управление умными розетками', - noCamera: 'У вас нет разрешения на просмотр камер', + noAdd: "У вас нет разрешения на добавление принтеров", + noEdit: "У вас нет разрешения на изменение принтеров", + noDelete: "У вас нет разрешения на удаление принтеров", + noControl: "У вас нет разрешения на управление принтерами", + noFiles: "У вас нет разрешения на доступ к файлам принтера", + noAmsRfid: "У вас нет разрешения на повторное считывание RFID AMS", + noSmartPlugControl: "У вас нет разрешения на управление умными розетками", + noCamera: "У вас нет разрешения на просмотр камер", }, modal: { - addTitle: 'Добавить принтер', - editTitle: 'Изменить принтер', - myPrinter: 'Мой принтер', - selectModel: 'Выберите модель…', - locationGroup: 'Расположение / группа (необязательно)', - locationPlaceholder: 'например: Мастерская, Офис, Подвал', - autoArchiveLabel: 'Автоматически архивировать завершённые печати', - fromPrinterSettings: 'Из настроек принтера', - modelOptional: 'Модель (необязательно)', - saveChanges: 'Сохранить изменения', + addTitle: "Добавить принтер", + editTitle: "Изменить принтер", + myPrinter: "Мой принтер", + selectModel: "Выберите модель…", + locationGroup: "Расположение / группа (необязательно)", + locationPlaceholder: "например: Мастерская, Офис, Подвал", + autoArchiveLabel: "Автоматически архивировать завершённые печати", + fromPrinterSettings: "Из настроек принтера", + modelOptional: "Модель (необязательно)", + saveChanges: "Сохранить изменения", }, skipObjects: { - tooltip: 'Пропустить объекты', - onlyWhilePrinting: 'Пропуск объектов доступен только во время печати', - requiresMultiple: 'Для пропуска требуется не менее двух объектов', - title: 'Пропустить объекты', - matchIdsInfo: 'Сопоставьте идентификаторы с экраном принтера', - printerShowsIds: 'На экране принтера отображаются идентификаторы объектов на пластине', - skipSelected: 'Пропустить выбранные', - skipping: 'Пропуск…', - noObjectsSelected: 'Объекты не выбраны', - selectObjectsToSkip: 'Выберите объекты, которые нужно исключить из текущей печати', - skipped: 'пропущен', - objectsSkipped: 'Объекты пропущены', - activeCount: 'Активно: {{count}}', - waitForLayer: 'Пропуск объектов доступен со второго слоя (сейчас слой {{layer}})', - skip: 'Пропустить', - confirmTitle: 'Пропустить объект?', - confirmMessage: 'Пропустить объект «{{name}}»? Это действие нельзя отменить.', + tooltip: "Пропустить объекты", + onlyWhilePrinting: "Пропуск объектов доступен только во время печати", + requiresMultiple: "Для пропуска требуется не менее двух объектов", + title: "Пропустить объекты", + matchIdsInfo: "Сопоставьте идентификаторы с экраном принтера", + printerShowsIds: "На экране принтера отображаются идентификаторы объектов на пластине", + skipSelected: "Пропустить выбранные", + skipping: "Пропуск…", + noObjectsSelected: "Объекты не выбраны", + selectObjectsToSkip: "Выберите объекты, которые нужно исключить из текущей печати", + skipped: "пропущен", + objectsSkipped: "Объекты пропущены", + activeCount: "Активно: {{count}}", + waitForLayer: "Пропуск объектов доступен со второго слоя (сейчас слой {{layer}})", + skip: "Пропустить", + confirmTitle: "Пропустить объект?", + confirmMessage: "Пропустить объект «{{name}}»? Это действие нельзя отменить.", }, confirm: { - deleteTitle: 'Удалить принтер', - deleteMessage: 'Удалить принтер «{{name}}»? Все параметры подключения будут удалены.', - deleteArchivesNote: 'Вся история печати этого принтера будет удалена безвозвратно.', - keepArchivesNote: 'История печати сохранится, но больше не будет связана с этим принтером.', - stopTitle: 'Остановить печать', - stopMessage: 'Остановить текущую печать на принтере «{{name}}»? Задание будет отменено.', - stopButton: 'Остановить печать', - pauseTitle: 'Приостановить печать', - pauseMessage: 'Приостановить текущую печать на принтере «{{name}}»?', - pauseButton: 'Приостановить печать', - resumeTitle: 'Продолжить печать', - resumeMessage: 'Продолжить печать на принтере «{{name}}»?', - resumeButton: 'Продолжить печать', - powerOnTitle: 'Включить принтер', - powerOnMessage: 'Включить питание принтера «{{name}}»?', - powerOnButton: 'Включить питание', - powerOffTitle: 'Выключить принтер', - powerOffMessage: 'Выключить питание принтера «{{name}}»?', - powerOffWarning: 'ВНИМАНИЕ: принтер «{{name}}» сейчас печатает. Выключение питания прервёт печать и может повредить принтер. Продолжить?', - powerOffButton: 'Выключить питание', - haToggleTitle: 'Переключить «{{name}}»', - haToggleMessage: 'Переключить сущность Home Assistant {{entity}}? Если она сейчас включена, питание может отключиться.', - haToggleWarning: 'ВНИМАНИЕ: принтер «{{name}}» сейчас печатает. Переключение {{entity}} может отключить питание и прервать печать. Продолжить?', - haToggleButton: 'Переключить', + deleteTitle: "Удалить принтер", + deleteMessage: "Удалить принтер «{{name}}»? Все параметры подключения будут удалены.", + deleteArchivesNote: "Вся история печати этого принтера будет удалена безвозвратно.", + keepArchivesNote: "История печати сохранится, но больше не будет связана с этим принтером.", + stopTitle: "Остановить печать", + stopMessage: "Остановить текущую печать на принтере «{{name}}»? Задание будет отменено.", + stopButton: "Остановить печать", + pauseTitle: "Приостановить печать", + pauseMessage: "Приостановить текущую печать на принтере «{{name}}»?", + pauseButton: "Приостановить печать", + resumeTitle: "Продолжить печать", + resumeMessage: "Продолжить печать на принтере «{{name}}»?", + resumeButton: "Продолжить печать", + powerOnTitle: "Включить принтер", + powerOnMessage: "Включить питание принтера «{{name}}»?", + powerOnButton: "Включить питание", + powerOffTitle: "Выключить принтер", + powerOffMessage: "Выключить питание принтера «{{name}}»?", + powerOffWarning: "ВНИМАНИЕ: принтер «{{name}}» сейчас печатает. Выключение питания прервёт печать и может повредить принтер. Продолжить?", + powerOffButton: "Выключить питание", + haToggleTitle: "Переключить «{{name}}»", + haToggleMessage: "Переключить сущность Home Assistant {{entity}}? Если она сейчас включена, питание может отключиться.", + haToggleWarning: "ВНИМАНИЕ: принтер «{{name}}» сейчас печатает. Переключение {{entity}} может отключить питание и прервать печать. Продолжить?", + haToggleButton: "Переключить", }, bulk: { - select: 'Выбрать', - selectAll: 'Выбрать все', - selectByLocation: 'Выбрать по расположению', - selected: 'Выбрано: {{count}}', + select: "Выбрать", + selectAll: "Выбрать все", + selectByLocation: "Выбрать по расположению", + selected: "Выбрано: {{count}}", actions: { - stop: 'Стоп', - pause: 'Пауза', - resume: 'Продолжить', - clearPlate: 'Освободить стол', - clearHMS: 'Очистить уведомления', + stop: "Стоп", + pause: "Пауза", + resume: "Продолжить", + clearPlate: "Освободить стол", + clearHMS: "Очистить уведомления", }, confirm: { - stopTitle: 'Остановить печати: {{count}}', - stopMessage: 'Активные печати на {{count}} принтерах будут отменены. Это действие нельзя отменить.', - stopButton: 'Остановить все', - pauseTitle: 'Приостановить печати: {{count}}', - pauseMessage: 'Активные печати на {{count}} принтерах будут приостановлены.', - pauseButton: 'Приостановить все', - clearPlateTitle: 'Освободить столы: {{count}}', - clearPlateMessage: 'Столы на {{count}} принтерах будут отмечены как свободные; после этого могут запуститься задания из очереди.', - clearPlateButton: 'Освободить все', + stopTitle: "Остановить печати: {{count}}", + stopMessage: "Активные печати на {{count}} принтерах будут отменены. Это действие нельзя отменить.", + stopButton: "Остановить все", + pauseTitle: "Приостановить печати: {{count}}", + pauseMessage: "Активные печати на {{count}} принтерах будут приостановлены.", + pauseButton: "Приостановить все", + clearPlateTitle: "Освободить столы: {{count}}", + clearPlateMessage: "Столы на {{count}} принтерах будут отмечены как свободные; после этого могут запуститься задания из очереди.", + clearPlateButton: "Освободить все", }, - success: 'Действие «{{action}}» выполнено на {{count}} принтерах', - partial: 'Успешно: {{succeeded}}, с ошибкой: {{failed}}', - noneApplicable: 'Ни один из выбранных принтеров не находится в подходящем состоянии', - selectByState: 'Выбрать по состоянию', + success: "Действие «{{action}}» выполнено на {{count}} принтерах", + partial: "Успешно: {{succeeded}}, с ошибкой: {{failed}}", + noneApplicable: "Ни один из выбранных принтеров не находится в подходящем состоянии", + selectByState: "Выбрать по состоянию", }, discovery: { - title: 'Поиск принтеров', - searching: 'Поиск…', - scanning: 'Сканирование…', - scanProgress: 'Сканирование: {{scanned}}/{{total}}', - foundPrinters: 'Найдено принтеров: {{count}}', - noPrintersFound: 'Принтеры не найдены', - noPrintersFoundSubnet: 'В указанной подсети принтеры не найдены.', - noPrintersFoundNetwork: 'Принтеры в сети не найдены.', - allConfigured: 'Все найденные принтеры уже настроены.', - alreadyAdded: 'Уже добавлен', - select: 'Выбрать', - manualEntry: 'Добавить вручную', - addFromCloud: 'Добавить из облака', - subnetToScan: 'Подсеть для сканирования', - dockerNote: 'Обнаружен Docker. Укажите подсеть принтера в формате CIDR. В docker-compose.yml требуется network_mode: host.', - scanSubnet: 'Сканировать подсеть', - discoverNetwork: 'Найти принтеры в сети', - scanningSubnet: 'Поиск принтеров Bambu в подсети…', - scanningNetwork: 'Сканирование сети…', - serialRequired: 'Требуется серийный номер', - unknown: 'Неизвестно', - failedToStart: 'Не удалось запустить поиск', - customSubnetOption: 'Другая подсеть…', - customSubnetLabel: 'Другая подсеть (CIDR)', - customSubnetNote: 'Используйте другую подсеть, если принтер находится в иной сети. Порты FTP (990) и MQTT (8883) должны быть доступны через маршрутизатор.', + title: "Поиск принтеров", + searching: "Поиск…", + scanning: "Сканирование…", + scanProgress: "Сканирование: {{scanned}}/{{total}}", + foundPrinters: "Найдено принтеров: {{count}}", + noPrintersFound: "Принтеры не найдены", + noPrintersFoundSubnet: "В указанной подсети принтеры не найдены.", + noPrintersFoundNetwork: "Принтеры в сети не найдены.", + allConfigured: "Все найденные принтеры уже настроены.", + alreadyAdded: "Уже добавлен", + select: "Выбрать", + manualEntry: "Добавить вручную", + addFromCloud: "Добавить из облака", + subnetToScan: "Подсеть для сканирования", + dockerNote: "Обнаружен Docker. Укажите подсеть принтера в формате CIDR. В docker-compose.yml требуется network_mode: host.", + scanSubnet: "Сканировать подсеть", + discoverNetwork: "Найти принтеры в сети", + scanningSubnet: "Поиск принтеров Bambu в подсети…", + scanningNetwork: "Сканирование сети…", + serialRequired: "Требуется серийный номер", + unknown: "Неизвестно", + failedToStart: "Не удалось запустить поиск", + customSubnetOption: "Другая подсеть…", + customSubnetLabel: "Другая подсеть (CIDR)", + customSubnetNote: "Используйте другую подсеть, если принтер находится в иной сети. Порты FTP (990) и MQTT (8883) должны быть доступны через маршрутизатор.", }, - delete: 'Удалить этот принтер?', - unsavedChanges: 'Есть несохранённые изменения. Покинуть страницу?', - clearQueue: 'Очистить очередь печати?', drying: { - start: 'Начать сушку', - stop: 'Остановить сушку', - temperature: 'Температура', - duration: 'Продолжительность', - hours: 'ч', - timeRemaining: 'осталось {{time}}', - active: 'Сушка', - targetSummary: '{{filament}} при {{temp}} °C', - notSupported: 'Сушка не поддерживается', - powerRequired: 'Подключите адаптер питания AMS, чтобы включить сушку', - startingDrying: 'Запуск сушки...', - stoppingDrying: 'Остановка сушки...', - rotateTray: 'Вращать катушку во время сушки', - rotateUnavailableReason: 'Недоступно: слот этого AMS сейчас подаёт филамент в печатающую головку. Катушка зафиксирована подающей трубкой и не может вращаться. Сначала выгрузите филамент.', + start: "Начать сушку", + stop: "Остановить сушку", + temperature: "Температура", + duration: "Продолжительность", + hours: "ч", + timeRemaining: "осталось {{time}}", + active: "Сушка", + targetSummary: "{{filament}} при {{temp}} °C", + notSupported: "Сушка не поддерживается", + powerRequired: "Подключите адаптер питания AMS, чтобы включить сушку", + startingDrying: "Запуск сушки...", + toastCommandSent: "Команда сушки отправлена", + toastStopped: "Сушка остановлена", + toastNotStarted: "Принтер принял команду, но AMS не начал сушку. Убедитесь, что адаптер питания AMS подключён, а принтер не занят.", + screenOnly: "На этом принтере управлять сушкой AMS можно только с экрана самого принтера (ограничение Bambu)", + stoppingDrying: "Остановка сушки...", + rotateTray: "Вращать катушку во время сушки", + rotateUnavailableReason: "Недоступно: слот этого AMS сейчас подаёт филамент в печатающую головку. Катушка зафиксирована подающей трубкой и не может вращаться. Сначала выгрузите филамент.", }, amsBackup: { - titleOn: 'Резервный филамент AMS включён. Нажмите, чтобы отключить.', - titleOff: 'Резервный филамент AMS выключен. Нажмите, чтобы включить.', - titleUnknown: 'Состояние резервного филамента AMS недоступно для этого принтера.', - toastEnabled: 'Резервный филамент AMS включён', - toastDisabled: 'Резервный филамент AMS отключён', - modalTitle: 'Резервный филамент AMS', - modalHelp: 'Когда активный слот опустеет, принтер по очереди использует подходящие слоты с тем же профилем и цветом в указанном порядке.', - modalNoSlots: 'Филамент не загружен.', - modalNoPairs: 'Резервных пар нет: ни у двух слотов не совпадают профиль филамента и цвет.', - extruderRightShort: 'П', - extruderLeftShort: 'Л', - stateOn: 'Включено', - stateOff: 'Отключено', - stateUnknown: 'Не поддерживается этим принтером', + titleOn: "Резервный филамент AMS включён. Нажмите, чтобы отключить.", + titleOff: "Резервный филамент AMS выключен. Нажмите, чтобы включить.", + titleUnknown: "Состояние резервного филамента AMS недоступно для этого принтера.", + toastEnabled: "Резервный филамент AMS включён", + toastDisabled: "Резервный филамент AMS отключён", + modalTitle: "Резервный филамент AMS", + modalHelp: "Когда активный слот опустеет, принтер по очереди использует подходящие слоты с тем же профилем и цветом в указанном порядке.", + modalNoSlots: "Филамент не загружен.", + modalNoPairs: "Резервных пар нет: ни у двух слотов не совпадают профиль филамента и цвет.", + extruderRightShort: "П", + extruderLeftShort: "Л", + stateOn: "Включено", + stateOff: "Отключено", + stateUnknown: "Не поддерживается этим принтером", }, activeJobSlot: { - title: 'Этот слот используется как филамент {{n}} в текущей печати', - ariaLabel: 'Слот {{n}} текущей печати', + title: "Этот слот используется как филамент {{n}} в текущей печати", + ariaLabel: "Слот {{n}} текущей печати", }, - filaments: 'Филаменты', - openCameraOverlay: 'Открыть камеру поверх интерфейса', - openCameraWindow: 'Открыть камеру в новом окне', - firmwareUpdateAvailable: 'Доступно обновление прошивки: {{current}} → {{latest}}', - firmwareUpToDate: 'Прошивка {{version}} — актуальная версия', - firmwareUpdateButton: 'Обновить', + expectedSlot: { + title: "Принтер ожидает филамент в этом слоте", + ariaLabel: "Ожидаемый слот филамента {{n}}", + label: "{{ams}} · Слот {{slot}}", + external: "Внешняя катушка", + }, + filaments: "Филаменты", + openCameraOverlay: "Открыть камеру поверх интерфейса", + openCameraWindow: "Открыть камеру в новом окне", + firmwareUpdateAvailable: "Доступно обновление прошивки: {{current}} → {{latest}}", + firmwareUpToDate: "Прошивка {{version}} — актуальная версия", + firmwareUpdateButton: "Обновить", plateDetection: { - noPermission: 'У вас нет прав на изменение настроек принтера', - enabledClick: 'Проверка пластины включена — нажмите, чтобы отключить', - disabledClick: 'Проверка пластины отключена — нажмите, чтобы включить', - manageCalibration: 'Управление калибровкой проверки пластины', - calibrationRequired: 'Требуется калибровка', - calibrationInstructions: 'Убедитесь, что печатная пластина полностью пустая, затем нажмите «Калибровать».', - calibrationDescription: 'При калибровке сохраняется эталонное изображение пустой пластины. При последующих проверках система сравнивает снимок с эталоном и обнаруживает посторонние объекты.', - calibrationTip: 'Совет: можно сохранить до 5 калибровок для разных пластин. При проверке система автоматически выберет наиболее подходящий эталон.', - plateEmpty: 'Пластина выглядит пустой', - objectsDetected: 'На пластине обнаружены объекты', - confidence: 'Уверенность', - difference: 'Различие', - analysisPreview: 'Предпросмотр анализа:', - analysisLegend: 'Зелёная рамка — область обнаружения, красная заливка — отличия от калибровочного снимка', - savedReferences: 'Сохранённые эталоны ({{count}}/{{max}})', - deleteReference: 'Удалить эталон', - labelPlaceholder: 'Метка...', - clickToEdit: '{{label}} — нажмите для изменения', - clickToAddLabel: 'Нажмите, чтобы добавить метку', + noPermission: "У вас нет прав на изменение настроек принтера", + enabledClick: "Проверка пластины включена — нажмите, чтобы отключить", + disabledClick: "Проверка пластины отключена — нажмите, чтобы включить", + manageCalibration: "Управление калибровкой проверки пластины", + calibrationRequired: "Требуется калибровка", + calibrationInstructions: "Убедитесь, что печатная пластина полностью пустая, затем нажмите «Калибровать».", + calibrationDescription: "При калибровке сохраняется эталонное изображение пустой пластины. При последующих проверках система сравнивает снимок с эталоном и обнаруживает посторонние объекты.", + calibrationTip: "Совет: можно сохранить до 5 калибровок для разных пластин. При проверке система автоматически выберет наиболее подходящий эталон.", + plateEmpty: "Пластина выглядит пустой", + objectsDetected: "На пластине обнаружены объекты", + confidence: "Уверенность", + difference: "Различие", + analysisPreview: "Предпросмотр анализа:", + analysisLegend: "Зелёная рамка — область обнаружения, красная заливка — отличия от калибровочного снимка", + savedReferences: "Сохранённые эталоны ({{count}}/{{max}})", + deleteReference: "Удалить эталон", + labelPlaceholder: "Метка...", + clickToEdit: "{{label}} — нажмите для изменения", + clickToAddLabel: "Нажмите, чтобы добавить метку", }, speed: { - title: 'Скорость печати', - silent: 'Тихий (50%)', - standard: 'Стандартный (100%)', - sport: 'Спортивный (124%)', - ludicrous: 'Безумный (166%)', + title: "Скорость печати", + silent: "Тихий (50%)", + standard: "Стандартный (100%)", + sport: "Спортивный (124%)", + ludicrous: "Безумный (166%)", }, airduct: { - title: 'Режим воздуховода', - cooling: 'Охлаждение', - heating: 'Нагрев', + title: "Режим воздуховода", + cooling: "Охлаждение", + heating: "Нагрев", }, - noSdCard: 'Нет SD-карты', + noSdCard: "Нет SD-карты", door: { - open: 'Открыта', - closed: 'Закрыта', + open: "Открыта", + closed: "Закрыта", }, fans: { - partCooling: 'Вентилятор обдува модели', - auxiliary: 'Дополнительный вентилятор', - chamber: 'Вентилятор камеры', + partCooling: "Вентилятор обдува модели", + auxiliary: "Дополнительный вентилятор", + chamber: "Вентилятор камеры", }, - clickToViewHmsErrors: 'Нажмите, чтобы посмотреть ошибки HMS', - estimatedCompletion: 'Расчётное время завершения', - plateNumber: 'Пластина {{number}}', - slotOptions: 'Параметры слота', + clickToViewHmsErrors: "Нажмите, чтобы посмотреть ошибки HMS", + estimatedCompletion: "Расчётное время завершения", + plateNumber: "Пластина {{number}}", + slotOptions: "Параметры слота", amsPopup: { - friendlyName: 'Имя AMS', - friendlyNamePlaceholder: 'Например, левый AMS', - serialNumber: 'Серийный номер', - firmwareVersion: 'Прошивка', - save: 'Сохранить', - clear: 'Очистить', - noEditPermission: 'У вас нет прав на переименование модулей AMS', + friendlyName: "Имя AMS", + friendlyNamePlaceholder: "Например, левый AMS", + serialNumber: "Серийный номер", + firmwareVersion: "Прошивка", + save: "Сохранить", + clear: "Очистить", + noEditPermission: "У вас нет прав на переименование модулей AMS", }, firmwareModal: { - title: 'Обновление прошивки', - titleUpToDate: 'Сведения о прошивке', - currentVersion: 'Текущая:', - latestVersion: 'Последняя:', - releaseNotes: 'Примечания к выпуску', - checkingPrereqs: 'Проверка условий...', - sdCardReady: 'SD-карта готова. Нажмите ниже, чтобы загрузить прошивку.', - uploadedSuccess: 'Прошивка загружена на SD-карту!', - applyInstructions: 'Чтобы установить обновление на принтер:', - step1: 'На экране принтера откройте Настройки', - step2: 'Перейдите в раздел Прошивка', - step3: 'Выберите Обновить с SD-карты', - step4: 'Обновление займёт 10–20 минут', - done: 'Готово', - starting: 'Запуск...', - uploadFirmware: 'Загрузить прошивку', - uploadFailed: 'Не удалось начать загрузку: {{error}}', - uploadedToast: 'Прошивка загружена. Запустите обновление с экрана принтера.', - availableVersions: 'Доступные версии', - usable: 'Доступна', - unavailable: 'Недоступна', - installed: 'Установлена', - newerBadge: 'новее', - olderBadge: 'старее', - currentBadge: 'текущая', + title: "Обновление прошивки", + titleUpToDate: "Сведения о прошивке", + currentVersion: "Текущая:", + latestVersion: "Последняя:", + releaseNotes: "Примечания к выпуску", + checkingPrereqs: "Проверка условий...", + sdCardReady: "SD-карта готова. Нажмите ниже, чтобы загрузить прошивку.", + uploadedSuccess: "Прошивка загружена на SD-карту!", + applyInstructions: "Чтобы установить обновление на принтер:", + step1: "На экране принтера откройте Настройки", + step2: "Перейдите в раздел Прошивка", + step3: "Выберите Обновить с SD-карты", + step4: "Обновление займёт 10–20 минут", + done: "Готово", + starting: "Запуск...", + uploadFirmware: "Загрузить прошивку", + uploadFailed: "Не удалось начать загрузку: {{error}}", + uploadedToast: "Прошивка загружена. Запустите обновление с экрана принтера.", + availableVersions: "Доступные версии", + usable: "Доступна", + unavailable: "Недоступна", + installed: "Установлена", + newerBadge: "новее", + olderBadge: "старее", + currentBadge: "текущая", }, - accessCodePlaceholder: 'Оставьте пустым, чтобы сохранить текущий', + accessCodePlaceholder: "Оставьте пустым, чтобы сохранить текущий", roi: { - title: 'Область обнаружения (ROI)', - xStart: 'Начало X', - yStart: 'Начало Y', - width: 'Ширина', - height: 'Высота', - instruction: 'Настройте область обнаружения так, чтобы она охватывала печатную пластину. Зелёная рамка в предпросмотре показывает текущую область.', + title: "Область обнаружения (ROI)", + xStart: "Начало X", + yStart: "Начало Y", + width: "Ширина", + height: "Высота", + instruction: "Настройте область обнаружения так, чтобы она охватывала печатную пластину. Зелёная рамка в предпросмотре показывает текущую область.", }, - developerModeWarning: 'Режим LAN для разработчиков не включён на: {{names}}. Некоторые функции могут не работать.', - howToEnable: 'Как включить', - incompatibleFile: 'Файл подготовлен для {{slicedFor}}, а выбранный принтер — {{printerModel}}', - dropNotPrintable: 'Для печати подходят только файлы .gcode и .gcode.3mf', - dropToPrint: 'Перетащите файл для печати', - cannotPrint: 'Принтер занят', + developerModeWarning: "Режим LAN для разработчиков не включён на: {{names}}. Некоторые функции могут не работать.", + howToEnable: "Как включить", + incompatibleFile: "Файл подготовлен для {{slicedFor}}, а выбранный принтер — {{printerModel}}", + dropNotPrintable: "Для печати подходят только файлы .gcode и .gcode.3mf", + dropToPrint: "Перетащите файл для печати", + cannotPrint: "Принтер занят", }, archives: { - title: 'Архив печати', + title: "Архив печати", no3mfBanner: { - title: 'Некоторые последние задания сохранены без миниатюр', - body: 'Слайсер не оставил файл .gcode.3mf на SD-карте принтера, поэтому Bambuddy не смог получить миниатюру и метаданные слайсера. Обычно это происходит, если в слайсере отключена настройка «Сохранять отправленные файлы во внешнем хранилище» (вкладка «Устройство» в Bambu Studio / OrcaSlicer).', - docsLink: 'См. шаг 4 установки', - dismissLabel: 'Закрыть это уведомление', + title: "Некоторые последние задания сохранены без миниатюр", + body: "Слайсер не оставил файл .gcode.3mf на SD-карте принтера, поэтому Bambuddy не смог получить миниатюру и метаданные слайсера. Обычно это происходит, если в слайсере отключена настройка «Сохранять отправленные файлы во внешнем хранилище» (вкладка «Устройство» в Bambu Studio / OrcaSlicer).", + docsLink: "См. шаг 4 установки", + dismissLabel: "Закрыть это уведомление", }, - searchPlaceholder: 'Поиск в архиве...', - filterByPrinter: 'Фильтр по принтеру', - filterByStatus: 'Фильтр по состоянию', - sortBy: 'Сортировать по', - sortNewest: 'Сначала новые', - sortOldest: 'Сначала старые', - sortName: 'Названию', - sortDuration: 'Длительности', - sortLargest: 'Сначала большие', - sortSmallest: 'Сначала маленькие', - sortSize: 'Размеру', - noArchives: 'Архивы не найдены', - noArchivesSearch: 'По вашему запросу ничего не найдено', - originalPrintNotVisible: 'Исходная печать не отображается — попробуйте сбросить фильтры', - noArchivesYet: 'Архив пока пуст', - prints: 'печатей', + searchPlaceholder: "Поиск в архиве...", + filterByPrinter: "Фильтр по принтеру", + filterByStatus: "Фильтр по состоянию", + sortBy: "Сортировать по", + sortNewest: "Сначала новые", + sortOldest: "Сначала старые", + sortName: "Названию", + sortDuration: "Длительности", + sortLargest: "Сначала большие", + sortSmallest: "Сначала маленькие", + sortSize: "Размеру", + noArchives: "Архивы не найдены", + noArchivesSearch: "По вашему запросу ничего не найдено", + originalPrintNotVisible: "Исходная печать не отображается — попробуйте сбросить фильтры", + noArchivesYet: "Архив пока пуст", + prints: "печатей", pagination: { - showing: 'Показано', - to: '–', - of: 'из', - show: 'Показывать', - page: 'Страница', - all: 'Все', + showing: "Показано", + to: "–", + of: "из", + show: "Показывать", + page: "Страница", + all: "Все", }, - loadingArchives: 'Загрузка архива...', - releaseToUpload: 'Отпустите файлы для загрузки', - showAll: 'Показать все', - showFavoritesOnly: 'Только избранное', - gridView: 'Плитка', - listView: 'Список', - calendarView: 'Календарь', - logView: 'Журнал печати', - manageTags: 'Управление тегами', - showFailedPrints: 'Показывать неудачные печати', - hideFailedPrints: 'Скрывать неудачные печати', - hideDuplicates: 'Скрывать дубликаты', - viewOriginalPrint: 'Открыть исходную печать №{{id}}', - printTime: 'Время печати', - filamentUsed: 'Израсходовано филамента', - cost: 'Стоимость', - preview: 'Предпросмотр', - deleteArchive: 'Удалить запись из архива', - deleteConfirm: 'Удалить эту запись из архива?', - favorite: 'В избранное', - unfavorite: 'Удалить из избранного', - viewDetails: 'Подробнее', + loadingArchives: "Загрузка архива...", + releaseToUpload: "Отпустите файлы для загрузки", + showAll: "Показать все", + showFavoritesOnly: "Только избранное", + gridView: "Плитка", + listView: "Список", + calendarView: "Календарь", + logView: "Журнал печати", + manageTags: "Управление тегами", + showFailedPrints: "Показывать неудачные печати", + hideFailedPrints: "Скрывать неудачные печати", + hideDuplicates: "Скрывать дубликаты", + viewOriginalPrint: "Открыть исходную печать №{{id}}", + printTime: "Время печати", + filamentUsed: "Израсходовано филамента", + cost: "Стоимость", + preview: "Предпросмотр", + deleteArchive: "Удалить запись из архива", + deleteConfirm: "Удалить эту запись из архива?", + favorite: "В избранное", + unfavorite: "Удалить из избранного", + viewDetails: "Подробнее", status: { - completed: 'Завершено', - failed: 'Ошибка', - stopped: 'Остановлено', + completed: "Завершено", + failed: "Ошибка", + stopped: "Остановлено", }, toast: { - source3mfAttached: 'Исходный 3MF прикреплён: {{filename}}', - failedUploadSource3mf: 'Не удалось загрузить исходный 3MF', - source3mfRemoved: 'Исходный 3MF удалён', - failedRemoveSource3mf: 'Не удалось удалить исходный 3MF', - f3dAttached: 'F3D прикреплён: {{filename}}', - failedUploadF3d: 'Не удалось загрузить F3D', - f3dRemoved: 'F3D удалён', - failedRemoveF3d: 'Не удалось удалить F3D', - timelapseAttached: 'Таймлапс прикреплён: {{filename}}', - timelapseAlreadyAttached: 'Таймлапс уже прикреплён', - noMatchingTimelapse: 'Подходящий таймлапс не найден', - failedScanTimelapse: 'Не удалось найти таймлапс', - failedAttachTimelapse: 'Не удалось прикрепить таймлапс', - timelapseRemoved: 'Таймлапс удалён', - failedRemoveTimelapse: 'Не удалось удалить таймлапс', - timelapseUploaded: 'Таймлапс загружен: {{filename}}', - failedUploadTimelapse: 'Не удалось загрузить таймлапс', - archiveDeleted: 'Запись удалена из архива', - failedDeleteArchive: 'Не удалось удалить запись из архива', - addedToFavorites: 'Добавлено в избранное', - removedFromFavorites: 'Удалено из избранного', - projectUpdated: 'Проект обновлён', - failedUpdateProject: 'Не удалось обновить проект', - linkCopied: 'Ссылка скопирована', - failedCopyLink: 'Не удалось скопировать ссылку', - photoDeleted: 'Фотография удалена', - failedDeletePhoto: 'Не удалось удалить фотографию', - failedDeleteArchives: 'Не удалось удалить записи из архива', - failedUpdateFavorites: 'Не удалось обновить избранное', - exportDownloaded: 'Экспортированный файл скачан', - exportFailed: 'Ошибка экспорта', + source3mfAttached: "Исходный 3MF прикреплён: {{filename}}", + failedUploadSource3mf: "Не удалось загрузить исходный 3MF", + source3mfRemoved: "Исходный 3MF удалён", + failedRemoveSource3mf: "Не удалось удалить исходный 3MF", + f3dAttached: "F3D прикреплён: {{filename}}", + failedUploadF3d: "Не удалось загрузить F3D", + f3dRemoved: "F3D удалён", + failedRemoveF3d: "Не удалось удалить F3D", + timelapseAttached: "Таймлапс прикреплён: {{filename}}", + timelapseAlreadyAttached: "Таймлапс уже прикреплён", + noMatchingTimelapse: "Подходящий таймлапс не найден", + failedScanTimelapse: "Не удалось найти таймлапс", + failedAttachTimelapse: "Не удалось прикрепить таймлапс", + timelapseRemoved: "Таймлапс удалён", + failedRemoveTimelapse: "Не удалось удалить таймлапс", + timelapseUploaded: "Таймлапс загружен: {{filename}}", + failedUploadTimelapse: "Не удалось загрузить таймлапс", + archiveDeleted: "Запись удалена из архива", + failedDeleteArchive: "Не удалось удалить запись из архива", + addedToFavorites: "Добавлено в избранное", + removedFromFavorites: "Удалено из избранного", + projectUpdated: "Проект обновлён", + failedUpdateProject: "Не удалось обновить проект", + linkCopied: "Ссылка скопирована", + failedCopyLink: "Не удалось скопировать ссылку", + photoDeleted: "Фотография удалена", + failedDeletePhoto: "Не удалось удалить фотографию", + failedDeleteArchives: "Не удалось удалить записи из архива", + failedUpdateFavorites: "Не удалось обновить избранное", + exportDownloaded: "Экспортированный файл скачан", + exportFailed: "Ошибка экспорта", }, menu: { - print: 'Печать', - openInBambuStudio: 'Открыть в слайсере', - slice: 'Нарезать', - externalLink: 'Внешняя ссылка', - viewOnMakerWorld: 'Открыть на MakerWorld', - preview3d: '3D-просмотр', - viewTimelapse: 'Посмотреть таймлапс', - scanForTimelapse: 'Найти таймлапс', - uploadTimelapse: 'Загрузить таймлапс', - removeTimelapse: 'Удалить таймлапс', - downloadSource3mf: 'Скачать исходный 3MF', - uploadSource3mf: 'Загрузить исходный 3MF', - replaceSource3mf: 'Заменить исходный 3MF', - removeSource3mf: 'Удалить исходный 3MF', - uploadF3d: 'Загрузить F3D', - replaceF3d: 'Заменить F3D', - downloadF3d: 'Скачать F3D', - removeF3d: 'Удалить F3D', - download: 'Скачать', - copyDownloadLink: 'Скопировать ссылку для скачивания', - qrCode: 'QR-код', - viewPhotos: 'Посмотреть фотографии', - viewPhotosCount: 'Фотографии ({{count}})', - projectPage: 'Страница проекта', - addToFavorites: 'Добавить в избранное', - removeFromFavorites: 'Удалить из избранного', - edit: 'Изменить', - printLog: 'Журнал печати', - goToProject: 'Открыть проект: {{name}}', - addToProject: 'Добавить в проект', - removeFromProject: 'Удалить из проекта', - loading: 'Загрузка...', - noProjectsAvailable: 'Нет доступных проектов', - searchProjects: 'Поиск проектов…', - select: 'Выбрать', - deselect: 'Снять выделение', - delete: 'Удалить', + print: "Печать", + openInBambuStudio: "Открыть в слайсере", + slice: "Нарезать", + externalLink: "Внешняя ссылка", + viewOnMakerWorld: "Открыть на MakerWorld", + preview3d: "3D-просмотр", + viewTimelapse: "Посмотреть таймлапс", + scanForTimelapse: "Найти таймлапс", + uploadTimelapse: "Загрузить таймлапс", + removeTimelapse: "Удалить таймлапс", + downloadSource3mf: "Скачать исходный 3MF", + uploadSource3mf: "Загрузить исходный 3MF", + replaceSource3mf: "Заменить исходный 3MF", + removeSource3mf: "Удалить исходный 3MF", + uploadF3d: "Загрузить F3D", + replaceF3d: "Заменить F3D", + downloadF3d: "Скачать F3D", + removeF3d: "Удалить F3D", + download: "Скачать", + copyDownloadLink: "Скопировать ссылку для скачивания", + qrCode: "QR-код", + viewPhotos: "Посмотреть фотографии", + viewPhotosCount: "Фотографии ({{count}})", + projectPage: "Страница проекта", + addToFavorites: "Добавить в избранное", + removeFromFavorites: "Удалить из избранного", + edit: "Изменить", + printLog: "Журнал печати", + goToProject: "Открыть проект: {{name}}", + addToProject: "Добавить в проект", + removeFromProject: "Удалить из проекта", + loading: "Загрузка...", + noProjectsAvailable: "Нет доступных проектов", + searchProjects: "Поиск проектов…", + select: "Выбрать", + deselect: "Снять выделение", + delete: "Удалить", }, permission: { - noReprint: 'У вас нет разрешения на повторную печать этой записи', - noAddToQueue: 'У вас нет разрешения на добавление в очередь', - noUpdateArchives: 'У вас нет разрешения на изменение архива', - noUploadFiles: 'У вас нет разрешения на загрузку файлов', - noDownload: 'У вас нет разрешения на скачивание файлов из архива', - noCopyLink: 'У вас нет разрешения на копирование ссылок для скачивания', - noDelete: 'У вас нет разрешения на удаление этой записи', - noEdit: 'У вас нет разрешения на изменение этой записи', - noCreate: 'У вас нет разрешения на создание записей в архиве', + noReprint: "У вас нет разрешения на повторную печать этой записи", + noAddToQueue: "У вас нет разрешения на добавление в очередь", + noUpdateArchives: "У вас нет разрешения на изменение архива", + noUploadFiles: "У вас нет разрешения на загрузку файлов", + noDownload: "У вас нет разрешения на скачивание файлов из архива", + noCopyLink: "У вас нет разрешения на копирование ссылок для скачивания", + noDelete: "У вас нет разрешения на удаление этой записи", + noEdit: "У вас нет разрешения на изменение этой записи", + noCreate: "У вас нет разрешения на создание записей в архиве", }, platePicker: { - title: 'Выберите пластину для предпросмотра', - hint: 'В этой записи несколько пластин. Выберите пластину, которую нужно открыть в просмотрщике G-code.', - plateLabel: 'Пластина {{index}}', - objectCount: '{{count}} объект', - objectCount_plural: '{{count}} объектов', - noGcode: 'В этой записи нет нарезанного G-code для предпросмотра. Сначала откройте файл в Bambu Studio и выполните нарезку.', + title: "Выберите пластину для предпросмотра", + hint: "В этой записи несколько пластин. Выберите пластину, которую нужно открыть в просмотрщике G-code.", + plateLabel: "Пластина {{index}}", + objectCount: "{{count}} объект", + objectCount_plural: "{{count}} объектов", + noGcode: "В этой записи нет нарезанного G-code для предпросмотра. Сначала откройте файл в Bambu Studio и выполните нарезку.", }, card: { - previousPlate: 'Предыдущая пластина', - nextPlate: 'Следующая пластина', - plateNumber: 'Пластина {{index}}', - moreOptions: 'Щёлкните правой кнопкой для дополнительных действий', - addToFavorites: 'Добавить в избранное', - removeFromFavorites: 'Удалить из избранного', - cancelled: 'отменено', - failed: 'ошибка', - duplicate: 'дубликат', - duplicateTitle: 'Эта модель уже печаталась', - openSource3mf: 'Открыть исходный 3MF в Bambu Studio (щёлкните правой кнопкой для дополнительных действий)', - downloadF3d: 'Скачать файл проекта Fusion 360', - viewTimelapse: 'Посмотреть таймлапс', - viewPhoto: 'Посмотреть фотографию', - viewPhotos: 'Посмотреть фотографии: {{count}}', - openFolder: 'Открыть папку: {{name}}', - slicedFile: 'Нарезанный файл — готов к печати', - sourceFile: 'Только исходный файл — сопоставление AMS недоступно', - gcode: 'G-CODE', - source: 'ИСХОДНИК', - project: 'Проект: {{name}}', - runsBadge: 'Печатей: {{count}}', - runsBadgeTitle: 'Всего печатей: {{count}}, успешных: {{successful}}, неудачных: {{failed}}. Нажмите, чтобы открыть полный журнал.', - estimated: 'Расчётное время: {{time}}', - actual: 'Фактическое время: {{time}}', - accuracy: 'Точность оценки: {{percent}}%', - filament: '{{weight}} г', - layer: '{{count}} слой', - layers: '{{count}} слоёв', - object: '{{count}} объект', - objects: '{{count}} объектов', - slicedFor: 'Нарезано для {{model}}', - uploadedBy: 'Загрузил', - noPermissionReprint: 'У вас нет разрешения на повторную печать', - noFileForReprint: 'Файл 3MF недоступен: при сохранении задания не удалось скачать его с принтера', - noPermissionEdit: 'У вас нет разрешения на изменение архива', - noPermissionDelete: 'У вас нет разрешения на удаление записей из архива', - openInBambuStudio: 'Открыть в слайсере', - openInBambuStudioToSlice: 'Открыть в слайсере для нарезки', - slice: 'Нарезать', - externalLink: 'Внешняя ссылка', - makerWorld: 'MakerWorld: {{designer}}', - viewProject: 'Открыть проект', - noExternalLink: 'Внешняя ссылка не указана', - preview3d: '3D-просмотр', - download: 'Скачать', - edit: 'Изменить', - delete: 'Удалить', + previousPlate: "Предыдущая пластина", + nextPlate: "Следующая пластина", + plateNumber: "Пластина {{index}}", + moreOptions: "Щёлкните правой кнопкой для дополнительных действий", + addToFavorites: "Добавить в избранное", + removeFromFavorites: "Удалить из избранного", + cancelled: "отменено", + failed: "ошибка", + duplicate: "дубликат", + duplicateTitle: "Эта модель уже печаталась", + openSource3mf: "Открыть исходный 3MF в Bambu Studio (щёлкните правой кнопкой для дополнительных действий)", + downloadF3d: "Скачать файл проекта Fusion 360", + viewTimelapse: "Посмотреть таймлапс", + viewPhoto: "Посмотреть фотографию", + viewPhotos: "Посмотреть фотографии: {{count}}", + openFolder: "Открыть папку: {{name}}", + slicedFile: "Нарезанный файл — готов к печати", + sourceFile: "Только исходный файл — сопоставление AMS недоступно", + gcode: "G-CODE", + source: "ИСХОДНИК", + project: "Проект: {{name}}", + runsBadge: "Печатей: {{count}}", + runsBadgeTitle: "Всего печатей: {{count}}, успешных: {{successful}}, неудачных: {{failed}}. Нажмите, чтобы открыть полный журнал.", + estimated: "Расчётное время: {{time}}", + actual: "Фактическое время: {{time}}", + accuracy: "Точность оценки: {{percent}}%", + filament: "{{weight}} г", + layer: "{{count}} слой", + layers: "{{count}} слоёв", + object: "{{count}} объект", + objects: "{{count}} объектов", + slicedFor: "Нарезано для {{model}}", + uploadedBy: "Загрузил", + noPermissionReprint: "У вас нет разрешения на повторную печать", + noFileForReprint: "Файл 3MF недоступен: при сохранении задания не удалось скачать его с принтера", + noPermissionEdit: "У вас нет разрешения на изменение архива", + noPermissionDelete: "У вас нет разрешения на удаление записей из архива", + openInBambuStudio: "Открыть в слайсере", + openInBambuStudioToSlice: "Открыть в слайсере для нарезки", + slice: "Нарезать", + externalLink: "Внешняя ссылка", + makerWorld: "MakerWorld: {{designer}}", + viewProject: "Открыть проект", + noExternalLink: "Внешняя ссылка не указана", + preview3d: "3D-просмотр", + download: "Скачать", + edit: "Изменить", + delete: "Удалить", }, runLog: { - title: 'Журнал печати', - modalTitle: 'Журнал печати — {{name}}', - modalTitleFallback: 'эта запись', - empty: 'Для этой записи пока нет событий печати.', + title: "Журнал печати", + modalTitle: "Журнал печати — {{name}}", + modalTitleFallback: "эта запись", + empty: "Для этой записи пока нет событий печати.", col: { - date: 'Дата', - status: 'Состояние', - duration: 'Длительность', - filament: 'Филамент', - cost: 'Стоимость', + date: "Дата", + status: "Состояние", + duration: "Длительность", + filament: "Филамент", + cost: "Стоимость", }, status: { - completed: 'Завершено', - failed: 'Ошибка', - cancelled: 'Отменено', - stopped: 'Остановлено', - skipped: 'Пропущено', - printing: 'Печать', + completed: "Завершено", + failed: "Ошибка", + cancelled: "Отменено", + stopped: "Остановлено", + skipped: "Пропущено", + printing: "Печать", }, }, modal: { - deleteArchive: 'Удаление записи из архива', - deleteConfirm: 'Удалить «{{name}}»? Это действие нельзя отменить.', - deleteButton: 'Удалить', - deletePurgeStats: 'Также исключить эту печать из краткой статистики: филамент, время, стоимость и энергия', - deleteQueueItemsWarning: 'Также будут удалены связанные элементы очереди: {{count}}.', - deleteBlockedByPrinting: 'Удаление невозможно: сейчас печатаются связанные элементы очереди ({{count}}). Сначала остановите печать.', - removeSource3mf: 'Удаление исходного 3MF', - removeSource3mfConfirm: 'Удалить исходный файл 3MF из «{{name}}»? Исходный проект слайсера будет удалён.', - removeButton: 'Удалить', - removeF3d: 'Удаление F3D', - removeF3dConfirm: 'Удалить файл проекта Fusion 360 из «{{name}}»?', - removeTimelapse: 'Удаление таймлапса', - removeTimelapseConfirm: 'Удалить видео таймлапса из «{{name}}»?', - timelapse: '{{name}} — таймлапс', - selectTimelapse: 'Выбор таймлапса', - selectTimelapseDesc: 'Автоматически найти совпадение не удалось. Выберите таймлапс для этой печати:', - deleteArchives: 'Удаление записей из архива', - deleteArchivesConfirm: 'Удалить записи из архива ({{count}})? Это действие нельзя отменить.', - deleteCount: 'Удалить: {{count}}', + deleteArchive: "Удаление записи из архива", + deleteConfirm: "Удалить «{{name}}»? Это действие нельзя отменить.", + deleteButton: "Удалить", + deletePurgeStats: "Также исключить эту печать из краткой статистики: филамент, время, стоимость и энергия", + deleteQueueItemsWarning: "Также будут удалены связанные элементы очереди: {{count}}.", + deleteBlockedByPrinting: "Удаление невозможно: сейчас печатаются связанные элементы очереди ({{count}}). Сначала остановите печать.", + removeSource3mf: "Удаление исходного 3MF", + removeSource3mfConfirm: "Удалить исходный файл 3MF из «{{name}}»? Исходный проект слайсера будет удалён.", + removeButton: "Удалить", + removeF3d: "Удаление F3D", + removeF3dConfirm: "Удалить файл проекта Fusion 360 из «{{name}}»?", + removeTimelapse: "Удаление таймлапса", + removeTimelapseConfirm: "Удалить видео таймлапса из «{{name}}»?", + timelapse: "{{name}} — таймлапс", + selectTimelapse: "Выбор таймлапса", + selectTimelapseDesc: "Автоматически найти совпадение не удалось. Выберите таймлапс для этой печати:", + deleteArchives: "Удаление записей из архива", + deleteArchivesConfirm: "Удалить записи из архива ({{count}})? Это действие нельзя отменить.", + deleteCount: "Удалить: {{count}}", }, page: { - title: 'Архив', - printsCount: 'Показано {{filtered}} из {{total}} печатей', - dropFilesHere: 'Перетащите сюда файлы .3mf', - releaseToUpload: 'Отпустите файлы для загрузки', - only3mfSupported: 'Поддерживаются только файлы .3mf', - close: 'Закрыть', - selected: 'Выбрано: {{count}}', - selectAll: 'Выбрать все', - tags: 'Теги', - project: 'Проект', - favorite: 'Избранное', - delete: 'Удалить', - toggledFavorites: 'Избранное изменено для записей: {{count}}', - failedUpdateFavorites: 'Не удалось обновить избранное', - archivesDeleted: 'Удалено записей: {{count}}', - failedDeleteArchives: 'Не удалось удалить записи из архива', - photoDeleted: 'Фотография удалена', - failedDeletePhoto: 'Не удалось удалить фотографию', + title: "Архив", + printsCount: "Показано {{filtered}} из {{total}} печатей", + dropFilesHere: "Перетащите сюда файлы .3mf", + releaseToUpload: "Отпустите файлы для загрузки", + only3mfSupported: "Поддерживаются только файлы .3mf", + close: "Закрыть", + selected: "Выбрано: {{count}}", + selectAll: "Выбрать все", + tags: "Теги", + project: "Проект", + favorite: "Избранное", + delete: "Удалить", + toggledFavorites: "Избранное изменено для записей: {{count}}", + failedUpdateFavorites: "Не удалось обновить избранное", + archivesDeleted: "Удалено записей: {{count}}", + failedDeleteArchives: "Не удалось удалить записи из архива", + photoDeleted: "Фотография удалена", + failedDeletePhoto: "Не удалось удалить фотографию", }, list: { - name: 'Название', - printer: 'Принтер', - date: 'Дата', - size: 'Размер', - actions: 'Действия', - hasTimelapse: 'Есть таймлапс', + name: "Название", + printer: "Принтер", + date: "Дата", + size: "Размер", + actions: "Действия", + hasTimelapse: "Есть таймлапс", }, log: { - date: 'Дата', - printName: 'Название печати', - printer: 'Принтер', - user: 'Пользователь', - status: 'Состояние', - duration: 'Длительность', - filament: 'Филамент', - allPrinters: 'Все принтеры', - allUsers: 'Все пользователи', - allStatuses: 'Все состояния', - cancelled: 'Отменено', - skipped: 'Пропущено', - dateFrom: 'С', - dateTo: 'По', - noEntries: 'Записи журнала не найдены', - showing: 'Показано {{count}} из {{total}} записей', - rowsPerPage: 'Строк', - page: 'Страница', - prev: 'Назад', - next: 'Далее', - clearLog: 'Очистить журнал', - clearLogTitle: 'Очистка журнала печати', - clearLogConfirm: 'Все записи журнала печати будут удалены без возможности восстановления. Архив и очередь не изменятся. Продолжить?', - clearLogButton: 'Очистить всё', - cleared: 'Удалено записей журнала: {{count}}', - clearFailed: 'Не удалось очистить журнал печати', - deleteEntryTitle: 'Удаление записи журнала', - deleteEntryConfirm: 'Запись будет удалена из журнала, а её вклад в расход филамента, время и стоимость исчезнет из краткой статистики. Связанная запись архива, если она существует, не изменится. Это действие нельзя отменить.', - entryDeleted: 'Запись журнала удалена', - entryDeleteFailed: 'Не удалось удалить запись журнала', - editEntryTitle: 'Изменение записи журнала', - editEntryDescription: 'Укажите результат этой печати. Виджет анализа отказов группирует данные по этим значениям, поэтому изменения сразу отразятся в статистике.', - entryUpdated: 'Запись журнала обновлена', - entryUpdateFailed: 'Не удалось обновить запись журнала', + date: "Дата", + printName: "Название печати", + printer: "Принтер", + user: "Пользователь", + status: "Состояние", + duration: "Длительность", + filament: "Филамент", + allPrinters: "Все принтеры", + allUsers: "Все пользователи", + allStatuses: "Все состояния", + cancelled: "Отменено", + skipped: "Пропущено", + dateFrom: "С", + dateTo: "По", + noEntries: "Записи журнала не найдены", + showing: "Показано {{count}} из {{total}} записей", + rowsPerPage: "Строк", + page: "Страница", + prev: "Назад", + next: "Далее", + clearLog: "Очистить журнал", + clearLogTitle: "Очистка журнала печати", + clearLogConfirm: "Все записи журнала печати будут удалены без возможности восстановления. Архив и очередь не изменятся. Продолжить?", + clearLogButton: "Очистить всё", + cleared: "Удалено записей журнала: {{count}}", + clearFailed: "Не удалось очистить журнал печати", + deleteEntryTitle: "Удаление записи журнала", + deleteEntryConfirm: "Запись будет удалена из журнала, а её вклад в расход филамента, время и стоимость исчезнет из краткой статистики. Связанная запись архива, если она существует, не изменится. Это действие нельзя отменить.", + entryDeleted: "Запись журнала удалена", + entryDeleteFailed: "Не удалось удалить запись журнала", + editEntryTitle: "Изменение записи журнала", + editEntryDescription: "Укажите результат этой печати. Виджет анализа отказов группирует данные по этим значениям, поэтому изменения сразу отразятся в статистике.", + entryUpdated: "Запись журнала обновлена", + entryUpdateFailed: "Не удалось обновить запись журнала", statuses: { - completed: 'Завершено', - failed: 'Ошибка', - stopped: 'Остановлено', - cancelled: 'Отменено', - skipped: 'Пропущено', + completed: "Завершено", + failed: "Ошибка", + stopped: "Остановлено", + cancelled: "Отменено", + skipped: "Пропущено", }, }, }, dispatchToast: { - untitled: 'Задание печати', - startingPrints: 'Запуск печати', - progressSummary: 'Завершено: {{complete}}/{{total}} • Обрабатывается: {{processing}}', - expandDetails: 'Показать подробности отправки', - collapseDetails: 'Скрыть подробности отправки', - awaitingPrinter: 'Ожидание принтера…', + untitled: "Задание печати", + startingPrints: "Запуск печати", + progressSummary: "Завершено: {{complete}}/{{total}} • Обрабатывается: {{processing}}", + expandDetails: "Показать подробности отправки", + collapseDetails: "Скрыть подробности отправки", + awaitingPrinter: "Ожидание принтера…", status: { - processing: 'Обработка', - completed: 'Завершено', - failed: 'Ошибка', + processing: "Обработка", + completed: "Завершено", + failed: "Ошибка", }, failed: { - generic: 'Не удалось отправить задание', - upload_failed: 'Не удалось загрузить файл на принтер', - start_command_failed: 'Принтер отклонил команду запуска', + generic: "Не удалось отправить задание", + upload_failed: "Не удалось загрузить файл на принтер", + start_command_failed: "Принтер отклонил команду запуска", }, - dismiss: 'Закрыть', + dismiss: "Закрыть", }, pipelineRuns: { - title: 'Запуски конвейеров', - loading: 'Загрузка…', - empty: 'Конвейеры ещё не запускались.', + title: "Запуски конвейеров", + loading: "Загрузка…", + empty: "Конвейеры ещё не запускались.", filter: { - pipeline: 'Конвейер', - status: 'Состояние', - target: 'Назначение', - all: 'Все', - allPipelines: 'Все конвейеры', - allStatus: 'Все состояния', - allTargets: 'Все назначения', - clear: 'Сбросить фильтры', - noMatches: 'Нет запусков, соответствующих выбранным фильтрам.', + pipeline: "Конвейер", + status: "Состояние", + target: "Назначение", + all: "Все", + allPipelines: "Все конвейеры", + allStatus: "Все состояния", + allTargets: "Все назначения", + clear: "Сбросить фильтры", + noMatches: "Нет запусков, соответствующих выбранным фильтрам.", }, - totalCount_one: '{{n}} запуск', - totalCount_other: '{{n}} запусков', - copies: 'Копий: {{n}}', - failedCount: 'Ошибок: {{n}}', - copyN: 'Копия {{n}}', - retryFailed: 'Повторить неудачные', - retryOf: 'повтор запуска №{{n}}', - pagination: '{{start}}–{{end}} из {{total}}', - cancelledByUser: 'Отменено пользователем', + totalCount_one: "{{n}} запуск", + totalCount_other: "{{n}} запусков", + copies: "Копий: {{n}}", + failedCount: "Ошибок: {{n}}", + copyN: "Копия {{n}}", + retryFailed: "Повторить неудачные", + retryOf: "повтор запуска №{{n}}", + pagination: "{{start}}–{{end}} из {{total}}", + cancelledByUser: "Отменено пользователем", toast: { - cancelled: 'Запуск отменён', - cancelFailed: 'Не удалось отменить запуск', - retryStarted: 'Повторный запуск начат', - retryFailed: 'Повторный запуск не удался', - cleared: 'Удалено запусков: {{n}}', - clearFailed: 'Не удалось очистить журнал', + cancelled: "Запуск отменён", + cancelFailed: "Не удалось отменить запуск", + retryStarted: "Повторный запуск начат", + retryFailed: "Повторный запуск не удался", + cleared: "Удалено запусков: {{n}}", + clearFailed: "Не удалось очистить журнал", }, - clearLog: 'Очистить журнал', - clearConfirmTitle: 'Очистить журнал?', - clearConfirmBody: 'Удалить все завершённые, неудачные, отменённые и частично неудачные запуски конвейеров? Выполняющиеся запуски сохранятся. Это действие нельзя отменить.', - clearConfirmAction: 'Очистить', + clearLog: "Очистить журнал", + clearConfirmTitle: "Очистить журнал?", + clearConfirmBody: "Удалить все завершённые, неудачные, отменённые и частично неудачные запуски конвейеров? Выполняющиеся запуски сохранятся. Это действие нельзя отменить.", + clearConfirmAction: "Очистить", jobStatus: { - pending: 'ожидание', - awaiting_printer: 'ожидание принтера', - queued: 'в очереди', - printing: 'печать', - completed: 'завершено', - failed: 'ошибка', - cancelled: 'отменено', + pending: "ожидание", + awaiting_printer: "ожидание принтера", + queued: "в очереди", + printing: "печать", + completed: "завершено", + failed: "ошибка", + cancelled: "отменено", }, }, queue: { - title: 'Очередь печати', - subtitle: 'Планирование и управление заданиями печати', + title: "Очередь печати", + subtitle: "Планирование и управление заданиями печати", filamentShort: { - rowBadge: 'Для назначенной катушки недостаточно филамента', - rowTooltip: 'Планировщик отметил это задание. Нажмите «Запустить», чтобы увидеть нехватку по каждому слоту и решить, печатать ли всё равно.', - confirmTitle: 'Недостаточно филамента', - confirmIntro: 'Назначенной катушки недостаточно как минимум для одного слота. Всё равно запустить печать?', - lineItem: 'Слот {{slot}}: требуется {{required}} г, осталось {{remaining}} г', - unknown: 'неизвестно', - printAnyway: 'Всё равно печатать', + rowBadge: "Для назначенной катушки недостаточно филамента", + rowTooltip: "Планировщик отметил это задание. Нажмите «Запустить», чтобы увидеть нехватку по каждому слоту и решить, печатать ли всё равно.", + confirmTitle: "Недостаточно филамента", + confirmIntro: "Назначенной катушки недостаточно как минимум для одного слота. Всё равно запустить печать?", + lineItem: "Слот {{slot}}: требуется {{required}} г, осталось {{remaining}} г", + unknown: "неизвестно", + printAnyway: "Всё равно печатать", }, - editQueueItem: 'Изменить задание в очереди', - selectAllPlates: 'Выбрать все пластины ({{count}})', - deselectAll: 'Снять выделение', - printQueued: 'Задание добавлено в очередь', - printQueuedWillStartWhenIdle: 'Запустится, когда принтер освободится', - itemsQueued: 'Добавлено в очередь: {{count}}', - sending: 'Отправка...', - sendingProgress: 'Отправка {{current}} из {{total}}...', - adding: 'Добавление...', - addingProgress: 'Добавление {{current}} из {{total}}...', - savingProgress: 'Сохранение {{current}} из {{total}}...', - clearQueue: 'Очистить очередь', - clearHistory: 'Очистить историю', - emptyQueue: 'Очередь пуста', - position: 'Позиция', - scheduledTime: 'Запланированное время', - moveUp: 'Переместить вверх', - moveDown: 'Переместить вниз', - startNow: 'Запустить сейчас', - printingInProgress: 'Идёт печать...', - viewArchive: 'Открыть архив', - viewInFileManager: 'Открыть в файловом менеджере', - itemCount: '{{count}} задание', - itemCount_plural: '{{count}} заданий', - dragToReorder: 'Перетащите для изменения порядка (только задания «как можно скорее»)', - reorderHint: 'Позиция влияет только на задания «как можно скорее». Запланированные задания запускаются в указанное время.', + editQueueItem: "Изменить задание в очереди", + selectAllPlates: "Выбрать все пластины ({{count}})", + deselectAll: "Снять выделение", + printQueued: "Задание добавлено в очередь", + printQueuedWillStartWhenIdle: "Запустится, когда принтер освободится", + itemsQueued: "Добавлено в очередь: {{count}}", + sending: "Отправка...", + sendingProgress: "Отправка {{current}} из {{total}}...", + adding: "Добавление...", + addingProgress: "Добавление {{current}} из {{total}}...", + savingProgress: "Сохранение {{current}} из {{total}}...", + clearQueue: "Очистить очередь", + clearHistory: "Очистить историю", + emptyQueue: "Очередь пуста", + position: "Позиция", + scheduledTime: "Запланированное время", + moveUp: "Переместить вверх", + moveDown: "Переместить вниз", + startNow: "Запустить сейчас", + printingInProgress: "Идёт печать...", + viewArchive: "Открыть архив", + viewInFileManager: "Открыть в файловом менеджере", + itemCount: "{{count}} задание", + itemCount_plural: "{{count}} заданий", + dragToReorder: "Перетащите для изменения порядка (только задания «как можно скорее»)", + reorderHint: "Позиция влияет только на задания «как можно скорее». Запланированные задания запускаются в указанное время.", sjf: { - label: 'SJF', - tooltip: 'Shortest Job First — планировщик отдаёт приоритет более коротким заданиям', + label: "SJF", + tooltip: "Shortest Job First — планировщик отдаёт приоритет более коротким заданиям", }, - addedBy: 'Добавил: {{name}}', - nextInQueue: 'Следующее в очереди', - clearPlateSuccess: 'Пластина очищена — можно запускать следующую печать', - plateNumber: 'Пластина {{index}}', - quantity: 'Количество', - quantityHint: 'Будет создано заданий: {{count}}', - activeBatches: 'Активные партии', - batchProgress: 'Выполнено {{completed}} из {{total}}', - cancelBatch: 'Отменить оставшиеся', - batchCancelled: 'Оставшиеся задания партии отменены', - cancelBatchConfirmTitle: 'Отменить партию', - cancelBatchConfirmMessage: 'Отменить все оставшиеся задания этой партии?', + addedBy: "Добавил: {{name}}", + nextInQueue: "Следующее в очереди", + clearPlateSuccess: "Пластина очищена — можно запускать следующую печать", + plateNumber: "Пластина {{index}}", + quantity: "Количество", + quantityHint: "Будет создано заданий: {{count}}", + activeBatches: "Активные партии", + batchProgress: "Выполнено {{completed}} из {{total}}", + cancelBatch: "Отменить оставшиеся", + batchCancelled: "Оставшиеся задания партии отменены", + cancelBatchConfirmTitle: "Отменить партию", + cancelBatchConfirmMessage: "Отменить все оставшиеся задания этой партии?", batch: { - defaultName: 'Партия', - label: '{{count}} задание', - label_plural: '{{count}} заданий', - pendingCount: 'Ожидает: {{count}}', - pendingCount_plural: 'Ожидают: {{count}}', - expand: 'Развернуть партию', - collapse: 'Свернуть партию', - groupAsBatch: 'Объединить в партию…', - groupAsBatchDescription: 'Объединить выбранные задания ({{count}}) в одну сворачиваемую партию.', - nameLabel: 'Название партии', - namePlaceholder: 'Например: подарки к пятнице', - create: 'Создать партию', - ungroup: 'Разгруппировать', - ungroupConfirmTitle: 'Разгруппировать партию?', - ungroupConfirmMessage: 'Задания останутся в очереди, но больше не будут объединены в одну группу.', - dragGroup: 'Перетащить группу', + defaultName: "Партия", + label: "{{count}} задание", + label_plural: "{{count}} заданий", + pendingCount: "Ожидает: {{count}}", + pendingCount_plural: "Ожидают: {{count}}", + expand: "Развернуть партию", + collapse: "Свернуть партию", + groupAsBatch: "Объединить в партию…", + groupAsBatchDescription: "Объединить выбранные задания ({{count}}) в одну сворачиваемую партию.", + nameLabel: "Название партии", + namePlaceholder: "Например: подарки к пятнице", + create: "Создать партию", + ungroup: "Разгруппировать", + ungroupConfirmTitle: "Разгруппировать партию?", + ungroupConfirmMessage: "Задания останутся в очереди, но больше не будут объединены в одну группу.", + dragGroup: "Перетащить группу", }, tabs: { - queue: 'Очередь', - history: 'История', - timeline: 'Временная шкала', - pipelines: 'Конвейеры', + queue: "Очередь", + history: "История", + timeline: "Временная шкала", + pipelines: "Конвейеры", }, layout: { - flatList: 'Список', - byPrinter: 'По принтерам', - groupByPrinter: 'Группировать по принтерам', + flatList: "Список", + byPrinter: "По принтерам", + groupByPrinter: "Группировать по принтерам", }, history: { - emptyTitle: 'История пока пуста', - emptyDescription: 'Здесь появятся завершённые, отменённые и неудачные задания.', + emptyTitle: "История пока пуста", + emptyDescription: "Здесь появятся завершённые, отменённые и неудачные задания.", }, dragGhost: { - multiCount: '{{count}} заданий', - batch: '{{name}} ({{count}} копия)', - batch_plural: '{{name}} ({{count}} копий)', + multiCount: "{{count}} заданий", + batch: "{{name}} ({{count}} копия)", + batch_plural: "{{name}} ({{count}} копий)", }, sections: { - currentlyPrinting: 'Сейчас печатается', - queued: 'В очереди', - history: 'История', + currentlyPrinting: "Сейчас печатается", + queued: "В очереди", + history: "История", }, status: { - pending: 'Ожидает', - waiting: 'Ожидание', - printing: 'Печать', - paused: 'Приостановлено', - completed: 'Завершено', - failed: 'Ошибка', - skipped: 'Пропущено', - cancelled: 'Отменено', + pending: "Ожидает", + waiting: "Ожидание", + printing: "Печать", + paused: "Приостановлено", + completed: "Завершено", + failed: "Ошибка", + skipped: "Пропущено", + cancelled: "Отменено", }, summary: { - printing: 'Печатается', - queued: 'В очереди', - totalTime: 'Общее время очереди', - totalWeight: 'Общий вес очереди', - history: 'История', + printing: "Печатается", + queued: "В очереди", + totalTime: "Общее время очереди", + totalWeight: "Общий вес очереди", + history: "История", }, filter: { - allPrinters: 'Все принтеры', - unassigned: 'Не назначено', - allStatus: 'Все состояния', - allLocations: 'Все расположения', - any: 'Любое', + allPrinters: "Все принтеры", + unassigned: "Не назначено", + allStatus: "Все состояния", + allLocations: "Все расположения", + any: "Любое", }, sort: { - byPosition: 'По позиции', - byName: 'По названию', - byPrinter: 'По принтеру', - bySchedule: 'По времени запуска', - byDate: 'По дате', - ascendingOldest: 'По возрастанию (сначала старые)', - descendingNewest: 'По убыванию (сначала новые)', + byPosition: "По позиции", + byName: "По названию", + byPrinter: "По принтеру", + bySchedule: "По времени запуска", + byDate: "По дате", + ascendingOldest: "По возрастанию (сначала старые)", + descendingNewest: "По убыванию (сначала новые)", }, badges: { - staged: 'Подготовлено', - requiresPrevious: 'Требуется успешное предыдущее задание', - autoPowerOff: 'Автоотключение питания', - gcodeInjection: 'G-code', + staged: "Подготовлено", + requiresPrevious: "Требуется успешное предыдущее задание", + autoPowerOff: "Автоотключение питания", + gcodeInjection: "G-code", }, empty: { - title: 'Нет запланированных заданий', - description: 'Запланируйте печать на странице архива через пункт «Запланировать» в контекстном меню либо перетащите сюда файлы.', + title: "Нет запланированных заданий", + description: "Запланируйте печать на странице архива через пункт «Запланировать» в контекстном меню либо перетащите сюда файлы.", }, time: { - asap: 'Как можно скорее', - overdue: 'Просрочено', - now: 'Сейчас', - lessThanMinute: 'Меньше чем через минуту', - inMinutes: 'Через {{count}} мин', - inHours: 'Через {{count}} ч', + asap: "Как можно скорее", + overdue: "Просрочено", + now: "Сейчас", + lessThanMinute: "Меньше чем через минуту", + inMinutes: "Через {{count}} мин", + inHours: "Через {{count}} ч", }, actions: { - startPrint: 'Запустить печать', - stopPrint: 'Остановить печать', - requeue: 'Вернуть в очередь', + startPrint: "Запустить печать", + stopPrint: "Остановить печать", + requeue: "Вернуть в очередь", }, bulkEdit: { - title: 'Изменить {{count}} задание', - title_plural: 'Изменить задания: {{count}}', - description: 'К выбранным заданиям будут применены только изменённые параметры.', - printer: 'Принтер', - noChange: '— Без изменений —', - queueOptions: 'Параметры очереди', - staged: 'Подготовлено (ручной запуск)', - autoPowerOff: 'Отключить питание после печати', - requirePrevious: 'Требовать успешного завершения предыдущего задания', - printOptions: 'Параметры печати', - bedLevelling: 'Калибровка стола', - flowCalibration: 'Калибровка потока', - vibrationCalibration: 'Калибровка вибраций', - layerInspection: 'Проверка первого слоя', - timelapse: 'Таймлапс', - useAms: 'Использовать AMS', - nozzleOffsetCali: 'Калибровка смещения сопел', - applyChanges: 'Применить изменения', - selectAll: 'Выбрать все', - deselectAll: 'Снять выделение', - selected: 'Выбрано: {{count}}', - editSelected: 'Изменить выбранные', - cancelSelected: 'Отменить выбранные', + title: "Изменить {{count}} задание", + title_plural: "Изменить задания: {{count}}", + description: "К выбранным заданиям будут применены только изменённые параметры.", + printer: "Принтер", + noChange: "— Без изменений —", + queueOptions: "Параметры очереди", + staged: "Подготовлено (ручной запуск)", + autoPowerOff: "Отключить питание после печати", + requirePrevious: "Требовать успешного завершения предыдущего задания", + printOptions: "Параметры печати", + bedLevelling: "Калибровка стола", + flowCalibration: "Калибровка потока", + vibrationCalibration: "Калибровка вибраций", + layerInspection: "Проверка первого слоя", + timelapse: "Таймлапс", + useAms: "Использовать AMS", + nozzleOffsetCali: "Калибровка смещения сопел", + applyChanges: "Применить изменения", + selectAll: "Выбрать все", + deselectAll: "Снять выделение", + selected: "Выбрано: {{count}}", + editSelected: "Изменить выбранные", + cancelSelected: "Отменить выбранные", }, confirm: { - cancelTitle: 'Отменить запланированную печать', - cancelMessage: 'Отменить задание «{{name}}»?', - stopTitle: 'Остановить печать', - stopMessage: 'Остановить текущую печать «{{name}}»? Задание на принтере будет отменено.', - removeTitle: 'Удалить из истории', - removeMessage: 'Удалить «{{name}}» из истории очереди?', - clearHistoryTitle: 'Очистить историю', - clearHistoryMessage: 'Удалить все записи из истории ({{count}})?', - cancelButton: 'Отменить печать', - stopButton: 'Остановить печать', - thisPrint: 'эту печать', - thisItem: 'это задание', + cancelTitle: "Отменить запланированную печать", + cancelMessage: "Отменить задание «{{name}}»?", + stopTitle: "Остановить печать", + stopMessage: "Остановить текущую печать «{{name}}»? Задание на принтере будет отменено.", + removeTitle: "Удалить из истории", + removeMessage: "Удалить «{{name}}» из истории очереди?", + clearHistoryTitle: "Очистить историю", + clearHistoryMessage: "Удалить все записи из истории ({{count}})?", + cancelButton: "Отменить печать", + stopButton: "Остановить печать", + thisPrint: "эту печать", + thisItem: "это задание", }, toast: { - cancelled: 'Задание отменено', - cancelFailed: 'Не удалось отменить задание', - removed: 'Задание удалено', - removeFailed: 'Не удалось удалить задание', - stopped: 'Печать остановлена', - stopFailed: 'Не удалось остановить печать', - released: 'Задание передано в очередь', - startFailed: 'Не удалось запустить печать', - reorderFailed: 'Не удалось изменить порядок очереди', - historyCleared: 'Удалено записей истории: {{count}}', - clearHistoryFailed: 'Не удалось очистить историю', - updateFailed: 'Не удалось обновить задания', - bulkCancelled: 'Отменено заданий: {{count}}', - bulkCancelFailed: 'Не удалось отменить задания', - batchCreated: 'Партия «{{name}}» создана', - batchCreateFailed: 'Не удалось создать партию', - batchUngrouped: 'Разгруппировано заданий: {{count}}', - batchUngroupFailed: 'Не удалось разгруппировать партию', - resumedAfterFailure: 'Очередь возобновлена — восстановлено заданий: {{restored}}', - resumeAfterFailureFailed: 'Не удалось возобновить очередь', + cancelled: "Задание отменено", + cancelFailed: "Не удалось отменить задание", + removed: "Задание удалено", + removeFailed: "Не удалось удалить задание", + stopped: "Печать остановлена", + stopFailed: "Не удалось остановить печать", + released: "Задание передано в очередь", + startFailed: "Не удалось запустить печать", + reorderFailed: "Не удалось изменить порядок очереди", + historyCleared: "Удалено записей истории: {{count}}", + clearHistoryFailed: "Не удалось очистить историю", + updateFailed: "Не удалось обновить задания", + bulkCancelled: "Отменено заданий: {{count}}", + bulkCancelFailed: "Не удалось отменить задания", + batchCreated: "Партия «{{name}}» создана", + batchCreateFailed: "Не удалось создать партию", + batchUngrouped: "Разгруппировано заданий: {{count}}", + batchUngroupFailed: "Не удалось разгруппировать партию", + resumedAfterFailure: "Очередь возобновлена — восстановлено заданий: {{restored}}", + resumeAfterFailureFailed: "Не удалось возобновить очередь", }, resumeAfterFailure: { - banner: 'Очередь принтера {{printer}} заблокирована из-за ошибки предыдущей печати — пропущено заданий: {{count}}', - bannerHint: 'Устраните проблему с принтером, затем возобновите очередь, чтобы вернуть пропущенные задания и снять блокировку.', - button: 'Возобновить после ошибки', - confirmTitle: 'Возобновить очередь после ошибки?', - confirmMessage: 'Вернуть пропущенные задания ({{count}}) для принтера {{printer}} в состояние ожидания и снять блокировку. Перед продолжением убедитесь, что принтер готов.', + banner: "Очередь принтера {{printer}} заблокирована из-за ошибки предыдущей печати — пропущено заданий: {{count}}", + bannerHint: "Устраните проблему с принтером, затем возобновите очередь, чтобы вернуть пропущенные задания и снять блокировку.", + button: "Возобновить после ошибки", + confirmTitle: "Возобновить очередь после ошибки?", + confirmMessage: "Вернуть пропущенные задания ({{count}}) для принтера {{printer}} в состояние ожидания и снять блокировку. Перед продолжением убедитесь, что принтер готов.", }, timeline: { - listView: 'Список', - timelineView: 'Временная шкала', - unassigned: 'Не назначено', - noData: 'На этот день печать не запланирована', - nothingCommitted: 'В этом интервале нет заданий с фиксированным временем. Подготовленные и ожидающие задания, а также задания «как можно скорее» на свободных принтерах не показываются — задайте время запуска или выпустите подготовленное задание в очередь.', - allDoneBy: 'Расчётное завершение всех заданий: {{time}}', - staged: 'Подготовлено', - filterAll: 'Показать все', - filterPrinting: 'Печатается', - filterQueued: 'В очереди', + listView: "Список", + timelineView: "Временная шкала", + unassigned: "Не назначено", + noData: "На этот день печать не запланирована", + nothingCommitted: "В этом интервале нет заданий с фиксированным временем. Подготовленные и ожидающие задания, а также задания «как можно скорее» на свободных принтерах не показываются — задайте время запуска или выпустите подготовленное задание в очередь.", + allDoneBy: "Расчётное завершение всех заданий: {{time}}", + staged: "Подготовлено", + filterAll: "Показать все", + filterPrinting: "Печатается", + filterQueued: "В очереди", time: { - anyMoment: 'в любой момент', - minutesLeft: 'осталось {{minutes}} мин', - hoursLeft: 'осталось {{hours}} ч', - hoursMinutesLeft: 'осталось {{hours}} ч {{minutes}} мин', + anyMoment: "в любой момент", + minutesLeft: "осталось {{minutes}} мин", + hoursLeft: "осталось {{hours}} ч", + hoursMinutesLeft: "осталось {{hours}} ч {{minutes}} мин", }, day: { - previous: 'Предыдущий день', - next: 'Следующий день', - today: 'Сегодня', + previous: "Предыдущий день", + next: "Следующий день", + today: "Сегодня", }, window: { - back12h: 'Назад на 12 часов', - forward12h: 'Вперёд на 12 часов', - now: 'Сейчас', + back12h: "Назад на 12 часов", + forward12h: "Вперёд на 12 часов", + now: "Сейчас", }, - printerColumnHeader: 'Принтер', + printerColumnHeader: "Принтер", }, permissions: { - noStopPrint: 'У вас нет разрешения останавливать печать', - noStartPrint: 'У вас нет разрешения запускать печать', - noEdit: 'У вас нет разрешения изменять это задание', - noCancel: 'У вас нет разрешения отменять это задание', - noRequeue: 'У вас нет разрешения возвращать задания в очередь', - noRemove: 'У вас нет разрешения удалять это задание', - noClearHistory: 'У вас нет разрешения очищать историю', - noEditItems: 'У вас нет разрешения изменять задания очереди', - noCancelItems: 'У вас нет разрешения отменять задания очереди', + noStopPrint: "У вас нет разрешения останавливать печать", + noStartPrint: "У вас нет разрешения запускать печать", + noEdit: "У вас нет разрешения изменять это задание", + noCancel: "У вас нет разрешения отменять это задание", + noRequeue: "У вас нет разрешения возвращать задания в очередь", + noRemove: "У вас нет разрешения удалять это задание", + noClearHistory: "У вас нет разрешения очищать историю", + noEditItems: "У вас нет разрешения изменять задания очереди", + noCancelItems: "У вас нет разрешения отменять задания очереди", }, }, stats: { - title: 'Статистика', - subtitle: 'Перетаскивайте виджеты для изменения порядка. Нажмите значок глаза, чтобы скрыть виджет.', - overview: 'Обзор', - totalPrints: 'Всего печатей', - successRate: 'Успешность', - totalPrintTime: 'Общее время печати', - printTime: 'Время печати', - totalFilament: 'Всего использовано филамента', - filamentUsed: 'Использовано филамента', - filamentCost: 'Стоимость филамента', - totalCost: 'Общая стоимость', - energyUsed: 'Потреблено энергии', - energyCost: 'Стоимость энергии', - energyWarmingUpTooltip: 'Учёт энергии ещё накапливает почасовые снимки. Итоги за выбранный период станут точными, когда появится хотя бы один снимок до начала этого периода. Первые значения могут быть занижены.', - averagePrintTime: 'Среднее время печати', - printsPerDay: 'Печатей в день', - byPrinter: 'По принтерам', - printsByPrinter: 'Печати по принтерам', - byMaterial: 'По материалам', - byMonth: 'По месяцам', - last7Days: 'Последние 7 дней', - last30Days: 'Последние 30 дней', - last90Days: 'Последние 90 дней', - allTime: 'За всё время', - quickStats: 'Краткая статистика', - printActivity: 'Активность печати', - filamentTypes: 'Типы филамента', - filamentTrends: 'Расход филамента', - failureAnalysis: 'Анализ неудач', - timeAccuracy: 'Точность оценки времени', - successful: 'Успешно:', - failed: 'Неудачно:', - cancelled: 'Отменено:', - perfectEstimate: '100% — точное совпадение с оценкой', - noTimeAccuracyData: 'Данных о точности оценки времени пока нет', - noFilamentData: 'Нет данных о филаменте', - noPrinterData: 'Нет данных о принтерах', - noPrintData: 'Нет данных о печати', - noPrintDataLast30Days: 'За последние 30 дней данных о печати нет', - failureReasons: 'Причины неудач', - topFailureReasons: 'Основные причины неудач', - failedPrintsCount: 'Неудачных печатей: {{failed}} из {{total}}', - lastWeekRate: 'За прошлую неделю: {{rate}}%', - resetLayout: 'Сбросить расположение', - recalculateCosts: 'Пересчитать стоимость', - recalculateCostsHint: 'Пересчитать стоимость всех архивных заданий по текущим ценам филамента', - exportStats: 'Экспорт статистики', - exportAsCsv: 'Экспорт в CSV', - exportAsExcel: 'Экспорт в Excel', - hiddenCount: 'Скрыто: {{count}}', - exportDownloaded: 'Файл экспорта скачан', - exportFailed: 'Не удалось экспортировать данные', - layoutReset: 'Расположение сброшено', - recalculatedCosts: 'Пересчитана стоимость архивов: {{count}}', - recalculateFailed: 'Не удалось пересчитать стоимость', - loadingStats: 'Загрузка статистики...', - noPermissionResetLayout: 'У вас нет разрешения сбрасывать расположение', - noPermissionRecalculate: 'У вас нет разрешения пересчитывать стоимость', - noPrintDataInRange: 'В выбранном периоде нет данных о печати', - periodFilament: 'Филамент за период', - periodCost: 'Стоимость за период', - avgPerPrint: 'В среднем на печать', - usageOverTime: 'Расход по времени', - filamentByWeight: 'По весу', - printDuration: 'Продолжительность печати', - printerUtilization: 'Загрузка принтеров', - filamentSuccess: 'Успешность по материалам', - printHabits: 'Характер печати', - printTimeOfDay: 'Время суток', - colorDistribution: 'Распределение по цветам', - noColorData: 'Нет данных о цветах', - records: 'Рекорды', - longestPrint: 'Самая долгая печать', - heaviestPrint: 'Самая тяжёлая печать', - mostExpensivePrint: 'Самая дорогая печать', - busiestDay: 'Самый загруженный день', - successStreak: 'Серия успешных печатей', - streakPrint: 'успешная печать подряд', - streakPrints: '{{count}} успешных печатей подряд', - printerStats: 'Статистика принтеров', - hours: 'часов', - avgPrints: 'В среднем печатей', - noArchiveData: 'Нет данных о печати', - filamentByTime: 'По времени', - avgWeight: 'Средний вес', - avgTime: 'Среднее время', - filamentByPrints: 'По количеству печатей', + title: "Статистика", + subtitle: "Перетаскивайте виджеты для изменения порядка. Нажмите значок глаза, чтобы скрыть виджет.", + overview: "Обзор", + totalPrints: "Всего печатей", + successRate: "Успешность", + totalPrintTime: "Общее время печати", + printTime: "Время печати", + totalFilament: "Всего использовано филамента", + filamentUsed: "Использовано филамента", + filamentCost: "Стоимость филамента", + totalCost: "Общая стоимость", + energyUsed: "Потреблено энергии", + energyCost: "Стоимость энергии", + energyWarmingUpTooltip: "Учёт энергии ещё накапливает почасовые снимки. Итоги за выбранный период станут точными, когда появится хотя бы один снимок до начала этого периода. Первые значения могут быть занижены.", + averagePrintTime: "Среднее время печати", + printsPerDay: "Печатей в день", + byPrinter: "По принтерам", + printsByPrinter: "Печати по принтерам", + byMaterial: "По материалам", + byMonth: "По месяцам", + last7Days: "Последние 7 дней", + last30Days: "Последние 30 дней", + last90Days: "Последние 90 дней", + allTime: "За всё время", + quickStats: "Краткая статистика", + printActivity: "Активность печати", + filamentTypes: "Типы филамента", + filamentTrends: "Расход филамента", + failureAnalysis: "Анализ неудач", + timeAccuracy: "Точность оценки времени", + successful: "Успешно:", + failed: "Неудачно:", + cancelled: "Отменено:", + perfectEstimate: "100% — точное совпадение с оценкой", + noTimeAccuracyData: "Данных о точности оценки времени пока нет", + noFilamentData: "Нет данных о филаменте", + noPrinterData: "Нет данных о принтерах", + noPrintData: "Нет данных о печати", + noPrintDataLast30Days: "За последние 30 дней данных о печати нет", + failureReasons: "Причины неудач", + topFailureReasons: "Основные причины неудач", + failedPrintsCount: "Неудачных печатей: {{failed}} из {{total}}", + lastWeekRate: "За прошлую неделю: {{rate}}%", + resetLayout: "Сбросить расположение", + recalculateCosts: "Пересчитать стоимость", + recalculateCostsHint: "Пересчитать стоимость всех архивных заданий по текущим ценам филамента", + exportStats: "Экспорт статистики", + exportAsCsv: "Экспорт в CSV", + exportAsExcel: "Экспорт в Excel", + hiddenCount: "Скрыто: {{count}}", + exportDownloaded: "Файл экспорта скачан", + exportFailed: "Не удалось экспортировать данные", + layoutReset: "Расположение сброшено", + recalculatedCosts: "Пересчитана стоимость архивов: {{count}}", + recalculateFailed: "Не удалось пересчитать стоимость", + loadingStats: "Загрузка статистики...", + noPermissionResetLayout: "У вас нет разрешения сбрасывать расположение", + noPermissionRecalculate: "У вас нет разрешения пересчитывать стоимость", + noPrintDataInRange: "В выбранном периоде нет данных о печати", + periodFilament: "Филамент за период", + periodCost: "Стоимость за период", + avgPerPrint: "В среднем на печать", + usageOverTime: "Расход по времени", + filamentByWeight: "По весу", + printDuration: "Продолжительность печати", + printerUtilization: "Загрузка принтеров", + filamentSuccess: "Успешность по материалам", + printHabits: "Характер печати", + printTimeOfDay: "Время суток", + colorDistribution: "Распределение по цветам", + noColorData: "Нет данных о цветах", + records: "Рекорды", + longestPrint: "Самая долгая печать", + heaviestPrint: "Самая тяжёлая печать", + mostExpensivePrint: "Самая дорогая печать", + busiestDay: "Самый загруженный день", + successStreak: "Серия успешных печатей", + streakPrint: "успешная печать подряд", + streakPrints: "{{count}} успешных печатей подряд", + printerStats: "Статистика принтеров", + hours: "часов", + avgPrints: "В среднем печатей", + noArchiveData: "Нет данных о печати", + filamentByTime: "По времени", + avgWeight: "Средний вес", + avgTime: "Среднее время", + filamentByPrints: "По количеству печатей", timeframe: { - today: 'Сегодня', - 'this-week': 'Эта неделя', - 'this-month': 'Этот месяц', - 'last-7': 'Последние 7 дней', - 'last-30': 'Последние 30 дней', - 'last-90': 'Последние 90 дней', - 'this-year': 'Этот год', - 'all-time': 'За всё время', - custom: 'Произвольный период', - from: 'С', - to: 'По', + today: "Сегодня", + "this-week": "Эта неделя", + "this-month": "Этот месяц", + "last-7": "Последние 7 дней", + "last-30": "Последние 30 дней", + "last-90": "Последние 90 дней", + "this-year": "Этот год", + "all-time": "За всё время", + custom: "Произвольный период", + from: "С", + to: "По", }, - allUsers: 'Все пользователи', - noUser: 'Без пользователя (система)', - filterByUser: 'Фильтр по пользователю', + allUsers: "Все пользователи", + noUser: "Без пользователя (система)", + filterByUser: "Фильтр по пользователю", }, maintenance: { - title: 'Обслуживание', - overview: 'Обзор', - allOk: 'Всё обслуживание выполнено вовремя', - dueCount: 'Требуется выполнить: {{count}}', - dueCount_plural: 'Требуется выполнить: {{count}}', - warningCount: 'Предупреждений: {{count}}', - warningCount_plural: 'Предупреждений: {{count}}', - totalPrintTime: 'Общее время печати', - nextMaintenance: 'Следующее обслуживание', - nothingDue: 'Пока ничего не требуется', - tasks: 'Задачи', - lastPerformed: 'Последнее выполнение', - interval: 'Интервал', - hoursRemaining: 'Осталось {{hours}} ч', - hoursOverdue: 'Просрочено на {{hours}} ч', - markDone: 'Отметить выполненным', - performMaintenance: 'Выполнить обслуживание', - history: 'История', - noHistory: 'История обслуживания пуста', - editPrintHours: 'Изменить часы печати', - currentHours: 'Текущие часы', - statusTab: 'Состояние', - settingsTab: 'Настройки', - overdueCount: 'Просрочено: {{count}}', - dueSoonCount: 'Скоро потребуется: {{count}}', - dueSoon: 'Скоро потребуется', - allGood: 'Всё в порядке', - overdueBy: 'Просрочено на {{duration}}', - dueIn: 'Через {{duration}}', - timeLeft: 'Осталось {{duration}}', - day: '1 день', - days: '{{count}} дней', - week: '1 неделя', - weeks: '{{count}} недель', - month: '1 месяц', - months: '{{count}} месяцев', - year: '1 год', - maintenanceTypes: 'Виды обслуживания', - maintenanceTypesDescription: 'Системные и пользовательские задачи обслуживания', - addCustomType: 'Добавить пользовательскую задачу', - restoreDefaults: 'Восстановить стандартные задачи', - intervalType: 'Тип интервала', - intervalValue: 'Интервал ({{type}})', - icon: 'Значок', - documentationLink: 'Ссылка на документацию (необязательно)', - assignToPrinters: 'Назначить принтерам', - selectAtLeastOnePrinter: 'Выберите хотя бы один принтер', - addType: 'Добавить вид', - custom: 'Пользовательский', - printHours: 'Часы печати', - calendarDays: 'Календарные дни', - exampleName: 'Например: заменить HEPA-фильтр', - viewDocumentation: 'Открыть документацию', - timeBasedInterval: 'Интервал по времени', - intervalOverrides: 'Переопределение интервалов', - intervalOverridesDescription: 'Настройка отдельных интервалов для конкретных принтеров', - assignedToPrinters: 'Назначено принтерам:', - noPrintersAssigned: 'Не назначено ни одному принтеру', - addPrinterShort: 'Добавить:', - printersAssignedClick: 'Назначено принтерам: {{count}} — нажмите для управления', - removeFromPrinter: 'Убрать с этого принтера', + title: "Обслуживание", + overview: "Обзор", + allOk: "Всё обслуживание выполнено вовремя", + dueCount: "Требуется выполнить: {{count}}", + dueCount_plural: "Требуется выполнить: {{count}}", + warningCount: "Предупреждений: {{count}}", + warningCount_plural: "Предупреждений: {{count}}", + totalPrintTime: "Общее время печати", + nextMaintenance: "Следующее обслуживание", + nothingDue: "Пока ничего не требуется", + tasks: "Задачи", + lastPerformed: "Последнее выполнение", + interval: "Интервал", + hoursRemaining: "Осталось {{hours}} ч", + hoursOverdue: "Просрочено на {{hours}} ч", + markDone: "Отметить выполненным", + performMaintenance: "Выполнить обслуживание", + history: "История", + noHistory: "История обслуживания пуста", + editPrintHours: "Изменить часы печати", + currentHours: "Текущие часы", + statusTab: "Состояние", + settingsTab: "Настройки", + overdueCount: "Просрочено: {{count}}", + dueSoonCount: "Скоро потребуется: {{count}}", + dueSoon: "Скоро потребуется", + allGood: "Всё в порядке", + overdueBy: "Просрочено на {{duration}}", + dueIn: "Через {{duration}}", + timeLeft: "Осталось {{duration}}", + day: "1 день", + days: "{{count}} дней", + week: "1 неделя", + weeks: "{{count}} недель", + month: "1 месяц", + months: "{{count}} месяцев", + year: "1 год", + maintenanceTypes: "Виды обслуживания", + maintenanceTypesDescription: "Системные и пользовательские задачи обслуживания", + addCustomType: "Добавить пользовательскую задачу", + restoreDefaults: "Восстановить стандартные задачи", + intervalType: "Тип интервала", + intervalValue: "Интервал ({{type}})", + icon: "Значок", + documentationLink: "Ссылка на документацию (необязательно)", + assignToPrinters: "Назначить принтерам", + selectAtLeastOnePrinter: "Выберите хотя бы один принтер", + addType: "Добавить вид", + custom: "Пользовательский", + printHours: "Часы печати", + calendarDays: "Календарные дни", + exampleName: "Например: заменить HEPA-фильтр", + viewDocumentation: "Открыть документацию", + timeBasedInterval: "Интервал по времени", + intervalOverrides: "Переопределение интервалов", + intervalOverridesDescription: "Настройка отдельных интервалов для конкретных принтеров", + assignedToPrinters: "Назначено принтерам:", + noPrintersAssigned: "Не назначено ни одному принтеру", + addPrinterShort: "Добавить:", + printersAssignedClick: "Назначено принтерам: {{count}} — нажмите для управления", + removeFromPrinter: "Убрать с этого принтера", types: { - lubricateCarbonRods: 'Смазать карбоновые валы', - lubricateRails: 'Смазать линейные направляющие', - cleanNozzle: 'Очистить сопло/хотэнд', - checkBelts: 'Проверить натяжение ремней', - cleanBuildPlate: 'Очистить печатную пластину', - checkExtruder: 'Проверить шестерни экструдера', - checkCooling: 'Проверить вентиляторы охлаждения', - generalInspection: 'Общий осмотр', - cleanCarbonRods: 'Очистить карбоновые валы', - lubricateSteelRods: 'Смазать стальные валы', - cleanSteelRods: 'Очистить стальные валы', - cleanLinearRails: 'Очистить линейные направляющие', - checkPtfeTube: 'Проверить PTFE-трубку', - replaceHepaFilter: 'Заменить HEPA-фильтр', - replaceCarbonFilter: 'Заменить угольный фильтр', - lubricateLeftNozzleRail: 'Смазать направляющую левого сопла', + lubricateCarbonRods: "Смазать карбоновые валы", + lubricateRails: "Смазать линейные направляющие", + cleanNozzle: "Очистить сопло/хотэнд", + checkBelts: "Проверить натяжение ремней", + cleanBuildPlate: "Очистить печатную пластину", + checkExtruder: "Проверить шестерни экструдера", + checkCooling: "Проверить вентиляторы охлаждения", + generalInspection: "Общий осмотр", + cleanCarbonRods: "Очистить карбоновые валы", + lubricateSteelRods: "Смазать стальные валы", + cleanSteelRods: "Очистить стальные валы", + cleanLinearRails: "Очистить линейные направляющие", + checkPtfeTube: "Проверить PTFE-трубку", + replaceHepaFilter: "Заменить HEPA-фильтр", + replaceCarbonFilter: "Заменить угольный фильтр", + lubricateLeftNozzleRail: "Смазать направляющую левого сопла", }, - maintenanceComplete: 'Обслуживание отмечено выполненным', - typeUpdated: 'Вид обслуживания обновлён', - typeDeleted: 'Вид обслуживания удалён', - defaultsRestored: 'Восстановлено стандартных задач: {{count}}', - printHoursUpdated: 'Часы печати обновлены', - printerAssigned: 'Принтер назначен', - printerRemoved: 'Принтер удалён из назначения', - deleteTypeConfirm: 'Удалить «{{name}}»?', - deleteSystemTypeTitle: 'Удалить стандартную задачу обслуживания?', - deleteSystemTypeMessage: 'Удалить стандартную задачу обслуживания «{{name}}»?', - noPermissionUpdate: 'У вас нет разрешения обновлять задачи обслуживания', - noPermissionPerform: 'У вас нет разрешения отмечать обслуживание выполненным', - noPermissionEditTypes: 'У вас нет разрешения изменять виды обслуживания', - noPermissionDeleteTypes: 'У вас нет разрешения удалять виды обслуживания', - noPermissionEditHours: 'У вас нет разрешения изменять часы печати', - noPermissionRemovePrinter: 'У вас нет разрешения удалять назначения принтеров', - noPermissionAssignPrinter: 'У вас нет разрешения назначать принтеры', - noPermissionEditIntervals: 'У вас нет разрешения изменять интервалы', - configureSettings: 'Настроить виды обслуживания и интервалы', + maintenanceComplete: "Обслуживание отмечено выполненным", + typeUpdated: "Вид обслуживания обновлён", + typeDeleted: "Вид обслуживания удалён", + defaultsRestored: "Восстановлено стандартных задач: {{count}}", + printHoursUpdated: "Часы печати обновлены", + printerAssigned: "Принтер назначен", + printerRemoved: "Принтер удалён из назначения", + deleteTypeConfirm: "Удалить «{{name}}»?", + deleteSystemTypeTitle: "Удалить стандартную задачу обслуживания?", + deleteSystemTypeMessage: "Удалить стандартную задачу обслуживания «{{name}}»?", + noPermissionUpdate: "У вас нет разрешения обновлять задачи обслуживания", + noPermissionPerform: "У вас нет разрешения отмечать обслуживание выполненным", + noPermissionEditTypes: "У вас нет разрешения изменять виды обслуживания", + noPermissionDeleteTypes: "У вас нет разрешения удалять виды обслуживания", + noPermissionEditHours: "У вас нет разрешения изменять часы печати", + noPermissionRemovePrinter: "У вас нет разрешения удалять назначения принтеров", + noPermissionAssignPrinter: "У вас нет разрешения назначать принтеры", + noPermissionEditIntervals: "У вас нет разрешения изменять интервалы", + configureSettings: "Настроить виды обслуживания и интервалы", }, settings: { - title: 'Настройки', - general: 'Общие', + title: "Настройки", + general: "Общие", tabs: { - general: 'Общие', - smartPlugs: 'Умные розетки', - notifications: 'Уведомления', - queue: 'Рабочий процесс', - queueDispatch: 'Очередь и отправка', - queuePipelines: 'Конвейеры', - filament: 'Филамент', - network: 'Сеть', - apiKeys: 'Ключи API', - virtualPrinter: 'Виртуальный принтер', - spoolbuddy: 'SpoolBuddy', - failureDetection: 'Обнаружение сбоев', - users: 'Аутентификация', - backup: 'Резервное копирование', - emailAuth: 'Аутентификация по email', - ldap: 'LDAP', - twoFa: 'Двухфакторная аутентификация', - oidc: 'SSO / OIDC', - security: 'Безопасность', + general: "Общие", + smartPlugs: "Умные розетки", + notifications: "Уведомления", + queue: "Рабочий процесс", + queueDispatch: "Очередь и отправка", + queuePipelines: "Конвейеры", + filament: "Филамент", + network: "Сеть", + apiKeys: "Ключи API", + virtualPrinter: "Виртуальный принтер", + spoolbuddy: "SpoolBuddy", + failureDetection: "Обнаружение сбоев", + users: "Аутентификация", + backup: "Резервное копирование", + emailAuth: "Аутентификация по email", + ldap: "LDAP", + twoFa: "Двухфакторная аутентификация", + oidc: "SSO / OIDC", + security: "Безопасность", }, spoolbuddy: { - infoTitle: 'Устройства SpoolBuddy', - infoBody: 'Киоски SpoolBuddy регистрируются автоматически по сигналам активности. Удалите регистрацию устройства, если оно больше не используется или после сбоя демона остался устаревший дубликат.', - duplicatesTitle: 'Зарегистрировано устройств: {{count}}', - duplicatesBody: 'Интерфейс киоска использует только первое зарегистрированное устройство. Если один из экземпляров — устаревший дубликат после сбоя, удалите его регистрацию: работающее устройство зарегистрируется снова при следующем сигнале активности.', - empty: 'Устройства SpoolBuddy пока не зарегистрированы.', - online: 'В сети', - offline: 'Не в сети', - unregister: 'Удалить регистрацию', - unregisterSuccess: 'Регистрация устройства удалена', - unregisterError: 'Не удалось удалить регистрацию устройства', - confirmTitle: 'Удалить регистрацию устройства SpoolBuddy?', - confirmBody: 'Устройство «{{hostname}}» ({{deviceId}}) будет удалено из базы данных. Если оно работает, то зарегистрируется снова при следующем сигнале активности.', - ipAddress: 'IP-адрес', - firmware: 'Прошивка', - lastSeen: 'Последняя активность', - daemonUptime: 'Время работы демона', - systemUptime: 'Время работы системы', - never: 'никогда', - nfc: 'NFC', - scale: 'Весы', - cpuTemp: 'Температура ЦП', - cpuLoad: 'Загрузка ЦП', - memory: 'Память', - disk: 'Диск', - update: 'Обновить', - updateConfirmTitle: 'Обновить демон SpoolBuddy?', - updateConfirmBody: 'Запустить обновление ПО на «{{hostname}}»? После установки обновления демон будет перезапущен.', - restartBrowser: 'Перезапустить браузер', - restartBrowserConfirmTitle: 'Перезапустить браузер киоска?', - restartBrowserConfirmBody: 'Перезапустить браузер киоска на «{{hostname}}»? Экран ненадолго погаснет.', - restartDaemon: 'Перезапустить демон', - restartDaemonConfirmTitle: 'Перезапустить демон SpoolBuddy?', - restartDaemonConfirmBody: 'Перезапустить демон SpoolBuddy на «{{hostname}}»? Устройство на несколько секунд пропадёт из сети.', - reboot: 'Перезагрузить', - rebootConfirmTitle: 'Перезагрузить устройство?', - rebootConfirmBody: 'Перезагрузить «{{hostname}}»? Устройство будет недоступно около минуты.', - shutdown: 'Выключить', - shutdownConfirmTitle: 'Выключить устройство?', - shutdownConfirmBody: 'Выключить «{{hostname}}»? Для повторного включения потребуется физический доступ.', - commandConfirm: 'Подтвердить', - commandQueued: 'Команда поставлена в очередь', - commandError: 'Не удалось отправить команду', + infoTitle: "Устройства SpoolBuddy", + infoBody: "Киоски SpoolBuddy регистрируются автоматически по сигналам активности. Удалите регистрацию устройства, если оно больше не используется или после сбоя демона остался устаревший дубликат.", + duplicatesTitle: "Зарегистрировано устройств: {{count}}", + duplicatesBody: "Интерфейс киоска использует только первое зарегистрированное устройство. Если один из экземпляров — устаревший дубликат после сбоя, удалите его регистрацию: работающее устройство зарегистрируется снова при следующем сигнале активности.", + empty: "Устройства SpoolBuddy пока не зарегистрированы.", + online: "В сети", + offline: "Не в сети", + unregister: "Удалить регистрацию", + unregisterSuccess: "Регистрация устройства удалена", + unregisterError: "Не удалось удалить регистрацию устройства", + confirmTitle: "Удалить регистрацию устройства SpoolBuddy?", + confirmBody: "Устройство «{{hostname}}» ({{deviceId}}) будет удалено из базы данных. Если оно работает, то зарегистрируется снова при следующем сигнале активности.", + ipAddress: "IP-адрес", + firmware: "Прошивка", + lastSeen: "Последняя активность", + daemonUptime: "Время работы демона", + systemUptime: "Время работы системы", + never: "никогда", + nfc: "NFC", + scale: "Весы", + cpuTemp: "Температура ЦП", + cpuLoad: "Загрузка ЦП", + memory: "Память", + disk: "Диск", + update: "Обновить", + updateConfirmTitle: "Обновить демон SpoolBuddy?", + updateConfirmBody: "Запустить обновление ПО на «{{hostname}}»? После установки обновления демон будет перезапущен.", + restartBrowser: "Перезапустить браузер", + restartBrowserConfirmTitle: "Перезапустить браузер киоска?", + restartBrowserConfirmBody: "Перезапустить браузер киоска на «{{hostname}}»? Экран ненадолго погаснет.", + restartDaemon: "Перезапустить демон", + restartDaemonConfirmTitle: "Перезапустить демон SpoolBuddy?", + restartDaemonConfirmBody: "Перезапустить демон SpoolBuddy на «{{hostname}}»? Устройство на несколько секунд пропадёт из сети.", + reboot: "Перезагрузить", + rebootConfirmTitle: "Перезагрузить устройство?", + rebootConfirmBody: "Перезагрузить «{{hostname}}»? Устройство будет недоступно около минуты.", + shutdown: "Выключить", + shutdownConfirmTitle: "Выключить устройство?", + shutdownConfirmBody: "Выключить «{{hostname}}»? Для повторного включения потребуется физический доступ.", + commandConfirm: "Подтвердить", + commandQueued: "Команда поставлена в очередь", + commandError: "Не удалось отправить команду", }, ldap: { - title: 'Аутентификация LDAP', - enabledDesc: 'Аутентификация LDAP включена', - disabledDesc: 'Аутентификация LDAP отключена', - disabledHint: 'Настройте и сохраните параметры LDAP ниже, затем включите аутентификацию.', - enabled: 'Аутентификация LDAP включена', - disabled: 'Аутентификация LDAP отключена', - feature1: 'Пользователи могут входить с учётными данными LDAP', - feature2: 'Локальная учётная запись администратора остаётся резервным способом входа', - feature3: 'При входе группы LDAP сопоставляются с группами Bambuddy', - serverConfig: 'Настройка сервера LDAP', - serverUrl: 'Адрес сервера', - serverUrlHint: 'Используйте ldaps:// для SSL либо ldap:// с StartTLS', - security: 'Безопасность', - securityHint: 'StartTLS переводит обычное соединение на TLS. LDAPS использует TLS с самого начала.', - bindDn: 'Bind DN (служебная учётная запись)', - bindPassword: 'Пароль Bind', - searchBase: 'Базовый DN поиска', - userFilter: 'Фильтр поиска пользователей', - userFilterHint: '{username} заменяется именем пользователя при входе. Для OpenLDAP используйте (uid={username}).', - advanced: 'Дополнительно', - autoProvision: 'Автоматически создавать пользователей', - autoProvisionHint: 'Автоматически создавать учётную запись Bambuddy при первом входе через LDAP', - defaultGroup: 'Группа по умолчанию', - defaultGroupNone: '— Нет (без резервной группы) —', - defaultGroupHint: 'Резервная группа для пользователя LDAP, который успешно вошёл, но не состоит ни в одной сопоставленной группе LDAP. Оставьте пустым, чтобы такие пользователи не получали разрешений.', - groupMapping: 'Сопоставление групп (JSON)', - groupMappingHint: 'Сопоставьте DN групп LDAP с группами Bambuddy. Доступные группы: ', - testConnection: 'Проверить подключение', - settingsSaved: 'Настройки LDAP сохранены', + title: "Аутентификация LDAP", + enabledDesc: "Аутентификация LDAP включена", + disabledDesc: "Аутентификация LDAP отключена", + disabledHint: "Настройте и сохраните параметры LDAP ниже, затем включите аутентификацию.", + enabled: "Аутентификация LDAP включена", + disabled: "Аутентификация LDAP отключена", + feature1: "Пользователи могут входить с учётными данными LDAP", + feature2: "Локальная учётная запись администратора остаётся резервным способом входа", + feature3: "При входе группы LDAP сопоставляются с группами Bambuddy", + serverConfig: "Настройка сервера LDAP", + serverUrl: "Адрес сервера", + serverUrlHint: "Используйте ldaps:// для SSL либо ldap:// с StartTLS", + security: "Безопасность", + securityHint: "StartTLS переводит обычное соединение на TLS. LDAPS использует TLS с самого начала.", + bindDn: "Bind DN (служебная учётная запись)", + bindPassword: "Пароль Bind", + searchBase: "Базовый DN поиска", + userFilter: "Фильтр поиска пользователей", + userFilterHint: "{username} заменяется именем пользователя при входе. Для OpenLDAP используйте (uid={username}).", + advanced: "Дополнительно", + autoProvision: "Автоматически создавать пользователей", + autoProvisionHint: "Автоматически создавать учётную запись Bambuddy при первом входе через LDAP", + defaultGroup: "Группа по умолчанию", + defaultGroupNone: "— Нет (без резервной группы) —", + defaultGroupHint: "Резервная группа для пользователя LDAP, который успешно вошёл, но не состоит ни в одной сопоставленной группе LDAP. Оставьте пустым, чтобы такие пользователи не получали разрешений.", + groupMapping: "Сопоставление групп (JSON)", + groupMappingHint: "Сопоставьте DN групп LDAP с группами Bambuddy. Доступные группы: ", + testConnection: "Проверить подключение", + settingsSaved: "Настройки LDAP сохранены", errors: { - serverRequired: 'Укажите адрес сервера LDAP', - searchBaseRequired: 'Укажите базовый DN поиска', - enableAuthFirst: 'Сначала включите аутентификацию', - configureLdapFirst: 'Сначала сохраните настройки LDAP', + serverRequired: "Укажите адрес сервера LDAP", + searchBaseRequired: "Укажите базовый DN поиска", + enableAuthFirst: "Сначала включите аутентификацию", + configureLdapFirst: "Сначала сохраните настройки LDAP", }, }, email: { - smtpSettings: 'Настройка SMTP', - smtpHost: 'SMTP-сервер', - smtpPort: 'Порт SMTP', - security: 'Безопасность', - authentication: 'Аутентификация', - username: 'Имя пользователя', - password: 'Пароль', - fromEmail: 'Адрес отправителя', - fromName: 'Имя отправителя', - testConnection: 'Проверить подключение SMTP', - testRecipient: 'Адрес получателя тестового письма', - sendTest: 'Отправить тестовое письмо', - sending: 'Отправка...', - save: 'Сохранить настройки', - saving: 'Сохранение...', - advancedAuth: 'Расширенная аутентификация', - advancedAuthEnabled: 'Расширенная аутентификация включена', - advancedAuthEnabledDesc: 'Функции управления пользователями через email активны. Новые пользователи получают автоматически созданные пароли по почте и могут сбрасывать пароль через функцию восстановления.', - advancedAuthDisabled: 'Расширенная аутентификация отключена', - advancedAuthDisabledDesc: 'Включите расширенную аутентификацию, чтобы использовать почтовые функции управления пользователями.', - enable: 'Включить', - disable: 'Отключить', - feature1: 'Пароли для новых пользователей создаются автоматически и отправляются по email', - feature2: 'Пользователи могут входить по имени или адресу электронной почты', - feature3: 'Доступно восстановление забытого пароля', - feature4: 'Администраторы могут сбрасывать пароли пользователей по email', + smtpSettings: "Настройка SMTP", + smtpHost: "SMTP-сервер", + smtpPort: "Порт SMTP", + security: "Безопасность", + authentication: "Аутентификация", + username: "Имя пользователя", + password: "Пароль", + fromEmail: "Адрес отправителя", + fromName: "Имя отправителя", + testConnection: "Проверить подключение SMTP", + testRecipient: "Адрес получателя тестового письма", + sendTest: "Отправить тестовое письмо", + sending: "Отправка...", + save: "Сохранить настройки", + saving: "Сохранение...", + advancedAuth: "Расширенная аутентификация", + advancedAuthEnabled: "Расширенная аутентификация включена", + advancedAuthEnabledDesc: "Функции управления пользователями через email активны. Новые пользователи получают автоматически созданные пароли по почте и могут сбрасывать пароль через функцию восстановления.", + advancedAuthDisabled: "Расширенная аутентификация отключена", + advancedAuthDisabledDesc: "Включите расширенную аутентификацию, чтобы использовать почтовые функции управления пользователями.", + enable: "Включить", + disable: "Отключить", + feature1: "Пароли для новых пользователей создаются автоматически и отправляются по email", + feature2: "Пользователи могут входить по имени или адресу электронной почты", + feature3: "Доступно восстановление забытого пароля", + feature4: "Администраторы могут сбрасывать пароли пользователей по email", errors: { - requiredFields: 'Заполните все обязательные поля', - usernameRequired: 'При включённой аутентификации требуется имя пользователя', - enterTestEmail: 'Введите адрес для тестового письма', - smtpServerAndEmail: 'Перед проверкой укажите SMTP-сервер и адрес отправителя', - usernamePasswordRequired: 'При включённой аутентификации требуются имя пользователя и пароль', - configureSmtpFirst: 'Сначала настройте и проверьте SMTP', - enableAuthFirst: 'Чтобы использовать почтовые функции, сначала включите аутентификацию.', + requiredFields: "Заполните все обязательные поля", + usernameRequired: "При включённой аутентификации требуется имя пользователя", + enterTestEmail: "Введите адрес для тестового письма", + smtpServerAndEmail: "Перед проверкой укажите SMTP-сервер и адрес отправителя", + usernamePasswordRequired: "При включённой аутентификации требуются имя пользователя и пароль", + configureSmtpFirst: "Сначала настройте и проверьте SMTP", + enableAuthFirst: "Чтобы использовать почтовые функции, сначала включите аутентификацию.", }, success: { - settingsSaved: 'Настройки SMTP сохранены', + settingsSaved: "Настройки SMTP сохранены", }, securityOptions: { - starttls: 'STARTTLS (порт 587)', - ssl: 'SSL/TLS (порт 465)', - none: 'Без шифрования (порт 25)', + starttls: "STARTTLS (порт 587)", + ssl: "SSL/TLS (порт 465)", + none: "Без шифрования (порт 25)", }, authOptions: { - enabled: 'Включена', - disabled: 'Отключена', + enabled: "Включена", + disabled: "Отключена", }, }, - appearance: 'Внешний вид', - notifications: 'Уведомления', - smartPlugs: 'Умные розетки', - spoolman: 'Spoolman', - updates: 'Обновления', - language: 'Язык', - languageDescription: 'Выберите предпочитаемый язык', - theme: 'Тема', - themeLight: 'Светлая', - themeDark: 'Тёмная', - themeSystem: 'Системная', - defaultView: 'Страница по умолчанию', - defaultViewDescription: 'Страница, которая открывается при запуске приложения', - checkForUpdates: 'Проверить обновления', - autoUpdate: 'Автоматическое обновление', - currentVersion: 'Текущая версия', - latestVersion: 'Последняя версия', - upToDate: 'Установлена последняя версия', - updateAvailable: 'Доступно обновление', - notificationLanguage: 'Язык уведомлений', - notificationLanguageDescription: 'Язык push-уведомлений', - bedCooledThreshold: 'Порог остывания стола', - bedCooledThresholdDescription: 'Температура, ниже которой стол после печати считается остывшим', - userNotificationsEnabled: 'Пользовательские уведомления', - userNotificationsEnabledDescription: 'Включить меню пользовательских и почтовых уведомлений о событиях печати. Требуется расширенная аутентификация.', - userNotificationsDisabledHint: 'Для пользовательских уведомлений включите расширенную аутентификацию.', - notificationProviders: 'Провайдеры уведомлений', - addProvider: 'Добавить провайдера', - editProvider: 'Изменить провайдера', - providerType: 'Тип провайдера', - testNotification: 'Проверить уведомление', - testSuccess: 'Тестовое уведомление успешно отправлено', - testFailed: 'Не удалось отправить тестовое уведомление', - quietHours: 'Тихие часы', - quietHoursDescription: 'Не беспокоить в указанный период', - quietHoursStart: 'Начало', - quietHoursEnd: 'Окончание', + appearance: "Внешний вид", + notifications: "Уведомления", + smartPlugs: "Умные розетки", + spoolman: "Spoolman", + updates: "Обновления", + language: "Язык", + languageDescription: "Выберите предпочитаемый язык", + theme: "Тема", + themeLight: "Светлая", + themeDark: "Тёмная", + themeSystem: "Системная", + defaultView: "Страница по умолчанию", + defaultViewDescription: "Страница, которая открывается при запуске приложения", + checkForUpdates: "Проверить обновления", + autoUpdate: "Автоматическое обновление", + currentVersion: "Текущая версия", + latestVersion: "Последняя версия", + upToDate: "Установлена последняя версия", + updateAvailable: "Доступно обновление", + notificationLanguage: "Язык уведомлений", + notificationLanguageDescription: "Язык push-уведомлений", + bedCooledThreshold: "Порог остывания стола", + bedCooledThresholdDescription: "Температура, ниже которой стол после печати считается остывшим", + userNotificationsEnabled: "Пользовательские уведомления", + userNotificationsEnabledDescription: "Включить меню пользовательских и почтовых уведомлений о событиях печати. Требуется расширенная аутентификация.", + userNotificationsDisabledHint: "Для пользовательских уведомлений включите расширенную аутентификацию.", + notificationProviders: "Провайдеры уведомлений", + addProvider: "Добавить провайдера", + editProvider: "Изменить провайдера", + providerType: "Тип провайдера", + testNotification: "Проверить уведомление", + testSuccess: "Тестовое уведомление успешно отправлено", + testFailed: "Не удалось отправить тестовое уведомление", + quietHours: "Тихие часы", + quietHoursDescription: "Не беспокоить в указанный период", + quietHoursStart: "Начало", + quietHoursEnd: "Окончание", events: { - title: 'События уведомлений', - printStart: 'Печать началась', - printComplete: 'Печать завершена', - printFailed: 'Ошибка печати', - printStopped: 'Печать остановлена', - printProgress: 'Этапы выполнения', - printProgressDescription: 'Уведомлять при 25%, 50% и 75%', - printerOffline: 'Принтер не в сети', - printerError: 'Ошибка принтера', - filamentLow: 'Филамент заканчивается', - maintenanceDue: 'Требуется обслуживание', - maintenanceDueDescription: 'Уведомлять, когда наступил срок обслуживания', + title: "События уведомлений", + printStart: "Печать началась", + printComplete: "Печать завершена", + printFailed: "Ошибка печати", + printStopped: "Печать остановлена", + printProgress: "Этапы выполнения", + printProgressDescription: "Уведомлять при 25%, 50% и 75%", + printerOffline: "Принтер не в сети", + printerError: "Ошибка принтера", + filamentLow: "Филамент заканчивается", + maintenanceDue: "Требуется обслуживание", + maintenanceDueDescription: "Уведомлять, когда наступил срок обслуживания", }, smartPlug: { - title: 'Умные розетки', - add: 'Добавить умную розетку', - edit: 'Изменить умную розетку', - name: 'Название', - ipAddress: 'IP-адрес', - linkedPrinter: 'Связанный принтер', - autoOn: 'Автоматическое включение', - autoOnDescription: 'Включать при начале печати', - autoOff: 'Автоматическое выключение', - autoOffDescription: 'Выключать после завершения печати', - offDelay: 'Задержка выключения', - offDelayMinutes: 'Минут после печати', - offDelayTemp: 'Когда сопло остынет ниже заданной температуры', - currentState: 'Текущее состояние', - turnOn: 'Включить', - turnOff: 'Выключить', + title: "Умные розетки", + add: "Добавить умную розетку", + edit: "Изменить умную розетку", + name: "Название", + ipAddress: "IP-адрес", + linkedPrinter: "Связанный принтер", + autoOn: "Автоматическое включение", + autoOnDescription: "Включать при начале печати", + autoOff: "Автоматическое выключение", + autoOffDescription: "Выключать после завершения печати", + offDelay: "Задержка выключения", + offDelayMinutes: "Минут после печати", + offDelayTemp: "Когда сопло остынет ниже заданной температуры", + currentState: "Текущее состояние", + turnOn: "Включить", + turnOff: "Выключить", }, - filamentTracking: 'Учёт филамента', - filamentTrackingDesc: 'Выберите способ учёта катушек: встроенный инвентарь или внешний сервер Spoolman.', - autoAddUnknownRfid: 'Автоматически добавлять неизвестные RFID-катушки', - autoAddUnknownRfidDesc: 'Автоматически создавать запись в инвентаре при обнаружении катушки с неизвестной RFID-меткой. Отключите, если регистрируете новые катушки заранее, чтобы избежать дубликатов.', - filamentChecks: 'Проверки филамента', - disableFilamentWarnings: 'Отключить предупреждения о филаменте', - disableFilamentWarningsDesc: 'Не показывать предупреждения о нехватке филамента при запуске печати или добавлении в очередь', - preferLowestFilament: 'Предпочитать катушку с наименьшим остатком', - preferLowestFilamentDesc: 'Если подходят несколько катушек, использовать катушку с наименьшим остатком филамента', - preferLowestFilamentBackupNote: 'Работает только при включённом AMS Filament Backup: иначе принтер не сможет переключиться на вторую катушку после окончания выбранной.', - trackingModeBuiltIn: 'Встроенный инвентарь', - trackingModeBuiltInDesc: 'Автоматическое сопоставление RFID и учёт расхода', - trackingModeSpoolmanDesc: 'Внешний сервер управления филаментом', - builtInFeatureRfid: 'Автоматически определяет официальные RFID-катушки Bambu Lab в AMS', - builtInFeatureUsage: 'Учитывает расход филамента для каждой печати', - builtInFeatureCatalog: 'Управление катушками, цветами и профилями K-фактора', - builtInFeatureThirdParty: 'Сторонние катушки можно связать с катушками инвентаря', - amsSyncButton: 'Синхронизировать вес из AMS', - amsSyncTitle: 'Синхронизация веса катушек из AMS', - amsSyncMessage: 'Вес всех катушек инвентаря будет заменён текущими значениями остатка из AMS подключённых принтеров. Используйте это для восстановления повреждённых данных о весе. Принтеры должны быть в сети.', - amsSyncing: 'Синхронизация...', - amsSyncSuccess: 'Синхронизировано катушек: {{synced}}, пропущено: {{skipped}}', - amsSyncError: 'Не удалось синхронизировать вес из AMS', - spoolmanAmsSyncButton: 'Синхронизировать вес Spoolman из AMS', - spoolmanAmsSyncTitle: 'Синхронизация веса катушек Spoolman из AMS', - spoolmanAmsSyncMessage: 'Вес всех катушек Spoolman будет обновлён по текущим значениям остатка из AMS подключённых принтеров. Принтеры должны быть в сети.', - spoolmanAmsSyncing: 'Синхронизация...', - spoolmanAmsSyncSuccess: 'Синхронизировано катушек: {{synced}}, пропущено: {{skipped}}', - spoolmanAmsSyncError: 'Не удалось синхронизировать вес Spoolman из AMS', - spoolmanAmsSyncErrorUnreachable: 'Не удалось синхронизировать вес Spoolman: сервер недоступен', - spoolmanAmsSyncErrorNotConfigured: 'Не удалось синхронизировать вес Spoolman: сервер не настроен', - spoolmanNotConfigured: 'Spoolman не настроен', - spoolmanFilamentCatalogTitle: 'Каталог филамента Spoolman', - spoolmanFilamentCatalogDesc: 'Названия филаментов и масса пустых катушек из Spoolman. Здесь можно изменить название и массу катушки; остальные свойства редактируются непосредственно в Spoolman.', - spoolmanUrl: 'Адрес Spoolman', - spoolmanUrlHint: 'Адрес сервера Spoolman, например http://localhost:7912', - spoolmanConnected: 'Подключён', - spoolmanDisconnected: 'Отключён', - status: 'Состояние', - connect: 'Подключить', - disconnect: 'Отключить', - howSyncWorks: 'Как работает синхронизация', - syncInfoRfidOnly: 'Синхронизируются только официальные RFID-катушки Bambu Lab', - syncInfoAutoCreate: 'Новые катушки автоматически создаются в Spoolman при первой синхронизации', - syncInfoThirdPartySkipped: 'Неофициальные катушки Bambu Lab, сторонние и перезаправленные катушки пропускаются', - oidc: { - title: 'Провайдеры SSO / OIDC', - desc: 'Настройка провайдеров OpenID Connect для единого входа через внешние службы идентификации.', - addProvider: 'Добавить провайдера', - newProvider: 'Новый провайдер', - empty: 'Провайдеры OIDC пока не настроены.', - created: 'Провайдер создан.', - updated: 'Провайдер обновлён.', - deleted: 'Провайдер удалён.', - refreshIcon: 'Обновить значок', - removeIcon: 'Удалить значок', - iconRefreshed: 'Значок обновлён.', - iconRemoved: 'Значок удалён.', - iconFetchFailed: 'Не удалось получить значок по URL провайдера.', - deleteTitle: 'Удалить провайдера', - deleteMessage: 'Удалить «{{name}}»? Все связанные учётные записи пользователей будут отсоединены.', - form: { - name: 'Отображаемое имя', - issuerUrl: 'URL издателя', - clientId: 'Идентификатор клиента', - clientSecret: 'Секрет клиента', - scopes: 'Области доступа', - iconUrl: 'URL значка (необязательно)', - enabled: 'Включён', - autoCreate: 'Автоматически создавать пользователей', - autoCreateDesc: 'Автоматически создавать локальную учётную запись при первом входе.', - autoLink: 'Автоматически связывать существующие учётные записи', - autoLinkDesc: 'При первом входе связывать локальные учётные записи по совпадающему адресу электронной почты.', - secretHint: 'оставьте пустым, чтобы сохранить текущий', - secretPlaceholder: 'новый секрет', - emailClaim: 'Поле электронной почты', - emailClaimDesc: 'Поле JWT, используемое как адрес электронной почты. Для Azure Entra ID, который не передаёт email_verified, используйте preferred_username или upn. Указывайте только доверенные поля.', - emailClaimPlaceholder: 'email', - emailClaimCustomClaimAutoLinkWarning: 'Пользовательское поле безопасно для автоматического связывания только тогда, когда его значение назначается администратором арендатора, например upn или preferred_username в Azure Entra ID. Не включайте автоматическое связывание, если ваш IdP позволяет пользователям самостоятельно задавать это поле.', - requireEmailVerified: 'Требовать подтверждённый адрес электронной почты', - requireEmailVerifiedDesc: 'Принимать адрес электронной почты только в том случае, если провайдер пометил его как подтверждённый.', - requireEmailVerifiedWarning: 'Предупреждение: адрес будет принят даже без подтверждения. Используйте только с доверенными провайдерами.', - requireEmailVerifiedAutoLink: 'Чтобы изменить этот параметр, сначала отключите автоматическое связывание.', - defaultGroup: 'Группа по умолчанию', - defaultGroupDesc: 'Группа для автоматически созданных пользователей. Если не задана, используется группа «Наблюдатели».', - defaultGroupViewersFallback: 'Наблюдатели (по умолчанию)', - autologin: 'Автоматический вход', - autologinDesc: 'Сразу перенаправлять неавторизованных посетителей к этому провайдеру. Этот флаг может быть установлен только у одного провайдера.', - }, + filamentTracking: "Учёт филамента", + filamentTrackingDesc: "Выберите способ учёта катушек: встроенный инвентарь или внешний сервер Spoolman.", + autoAddUnknownRfid: "Автоматически добавлять неизвестные RFID-катушки", + autoAddUnknownRfidDesc: "Автоматически создавать запись в инвентаре при обнаружении катушки с неизвестной RFID-меткой. Отключите, если регистрируете новые катушки заранее, чтобы избежать дубликатов.", + filamentChecks: "Проверки филамента", + disableFilamentWarnings: "Отключить предупреждения о филаменте", + disableFilamentWarningsDesc: "Не показывать предупреждения о нехватке филамента при запуске печати или добавлении в очередь", + preferLowestFilament: "Предпочитать катушку с наименьшим остатком", + preferLowestFilamentDesc: "Если подходят несколько катушек, использовать катушку с наименьшим остатком филамента", + preferLowestFilamentBackupNote: "Работает только при включённом AMS Filament Backup: иначе принтер не сможет переключиться на вторую катушку после окончания выбранной.", + trackingModeBuiltIn: "Встроенный инвентарь", + trackingModeBuiltInDesc: "Автоматическое сопоставление RFID и учёт расхода", + trackingModeSpoolmanDesc: "Внешний сервер управления филаментом", + builtInFeatureRfid: "Автоматически определяет официальные RFID-катушки Bambu Lab в AMS", + builtInFeatureUsage: "Учитывает расход филамента для каждой печати", + builtInFeatureCatalog: "Управление катушками, цветами и профилями K-фактора", + builtInFeatureThirdParty: "Сторонние катушки можно связать с катушками инвентаря", + amsSyncButton: "Синхронизировать вес из AMS", + amsSyncTitle: "Синхронизация веса катушек из AMS", + amsSyncMessage: "Вес всех катушек инвентаря будет заменён текущими значениями остатка из AMS подключённых принтеров. Используйте это для восстановления повреждённых данных о весе. Принтеры должны быть в сети.", + amsSyncing: "Синхронизация...", + amsSyncSuccess: "Синхронизировано катушек: {{synced}}, пропущено: {{skipped}}", + amsSyncError: "Не удалось синхронизировать вес из AMS", + spoolmanAmsSyncButton: "Синхронизировать вес Spoolman из AMS", + spoolmanAmsSyncTitle: "Синхронизация веса катушек Spoolman из AMS", + spoolmanAmsSyncMessage: "Вес всех катушек Spoolman будет обновлён по текущим значениям остатка из AMS подключённых принтеров. Принтеры должны быть в сети.", + spoolmanAmsSyncing: "Синхронизация...", + spoolmanAmsSyncSuccess: "Синхронизировано катушек: {{synced}}, пропущено: {{skipped}}", + spoolmanAmsSyncError: "Не удалось синхронизировать вес Spoolman из AMS", + spoolmanAmsSyncErrorUnreachable: "Не удалось синхронизировать вес Spoolman: сервер недоступен", + spoolmanAmsSyncErrorNotConfigured: "Не удалось синхронизировать вес Spoolman: сервер не настроен", + spoolmanNotConfigured: "Spoolman не настроен", + spoolmanFilamentCatalogTitle: "Каталог филамента Spoolman", + spoolmanFilamentCatalogDesc: "Названия филаментов и масса пустых катушек из Spoolman. Здесь можно изменить название и массу катушки; остальные свойства редактируются непосредственно в Spoolman.", + spoolmanUrl: "Адрес Spoolman", + spoolmanUrlHint: "Адрес сервера Spoolman, например http://localhost:7912", + spoolmanConnected: "Подключён", + spoolmanDisconnected: "Отключён", + status: "Состояние", + connect: "Подключить", + disconnect: "Отключить", + howSyncWorks: "Как работает синхронизация", + syncInfoRfidOnly: "Синхронизируются только официальные RFID-катушки Bambu Lab", + syncInfoAutoCreate: "Новые катушки автоматически создаются в Spoolman при первой синхронизации", + syncInfoThirdPartySkipped: "Неофициальные катушки Bambu Lab, сторонние и перезаправленные катушки пропускаются", + linkingExistingSpools: "Привязка существующих катушек", + linkingExistingSpoolsDesc: "Чтобы привязать существующую катушку Spoolman к AMS, наведите указатель на слот AMS и нажмите «Привязать к Spoolman».", + syncMode: "Режим синхронизации", + syncModeAuto: "Автоматически", + syncModeManual: "Только вручную", + syncModeAutoDesc: "Данные AMS синхронизируются автоматически при обнаружении изменений", + syncModeManualDesc: "Синхронизация выполняется только вручную", + syncAmsData: "Синхронизировать данные AMS", + syncAmsDataDesc: "Вручную синхронизировать данные AMS принтера со Spoolman", + allPrinters: "Все принтеры", + noDefaultPrinter: "Без принтера по умолчанию (спрашивать каждый раз)", + sidebarOrder: "Порядок пунктов боковой панели", + saveThumbnails: "Сохранять миниатюры", + captureFinishPhoto: "Снимок после завершения", + noPrintersConfigured: "Принтеры не настроены", + archiveMode: { + always: "Всегда создавать запись в архиве", + never: "Не создавать запись в архиве", + ask: "Спрашивать каждый раз", }, + checkForUpdatesLabel: "Проверять обновления", + checkPrinterFirmware: "Проверять прошивку принтера", + includeBetaUpdates: "Включать бета-версии", + includeBetaUpdatesDesc: "Уведомлять о бета- и предварительных версиях при проверке обновлений", + localLogin: { + disable: "Отключить локальный вход по имени пользователя и паролю", + disableHint: "Если включено, вход возможен только через провайдеров SSO. На LDAP это не влияет. Чтобы сохранить аварийный способ входа, задайте на сервере BAMBUDDY_LOCAL_LOGIN=true.", + }, + enableRetry: "Включить повторные попытки", + homeAssistantDescription: "Управление умными розетками через Home Assistant", + environmentManagedLabel: "(управляется окружением)", + autoEnabledViaEnv: "Автоматически включено переменными окружения", + urlFromEnvReadOnly: "Значение задано переменной окружения HA_URL (только чтение)", + tokenFromEnvReadOnly: "Значение задано переменной окружения HA_TOKEN (только чтение)", + mqttConnectedTo: "Подключено к", + prometheusDescription: "Экспортировать данные принтеров в формате Prometheus", + noSmartPlugsTitle: "Умные розетки не настроены", + noSmartPlugsDescription: "Добавьте умную розетку на базе Tasmota для учёта энергии и автоматизации питания.", + noProvidersTitle: "Провайдеры не настроены", + noProvidersDescription: "Добавьте провайдера для получения уведомлений.", + noTemplatesAvailable: "Шаблоны отсутствуют. Перезапустите бэкенд, чтобы создать стандартные шаблоны.", + apiPermissionView: "Просмотр состояния принтеров и очереди", + apiPermissionEdit: "Добавление и удаление заданий из очереди печати", + apiKeysEmptyTitle: "Ключей API нет", + apiKeysEmptyDescription: "Создайте ключ API для интеграции с внешними сервисами.", + noUsersFound: "Пользователи не найдены", + noGroupsFound: "Группы не найдены", + noGroupsAvailable: "Доступных групп нет", + passwordsDoNotMatch: "Пароли не совпадают", + systemGroupWarning: "Названия системных групп изменить нельзя", + authDisabledTitle: "Аутентификация отключена", + authDisabledFeature1: "Требовать вход для доступа к системе", + authDisabledFeature2: "Создавать нескольких пользователей с групповыми разрешениями", + authDisabledFeature3: "Управлять доступом с помощью более чем 50 отдельных разрешений", + userHasCreated: "Этот пользователь создал:", + userItemsQuestion: "Что сделать с этими объектами?", + deleteUserConfirm: "Удалить этого пользователя?", + actionCannotBeUndone: "Это действие нельзя отменить.", + addFirstSmartPlug: "Добавить первую умную розетку", + providers: "Провайдеры", + log: "Журнал", + testAll: "Проверить все", + testResults: "Результаты проверки", + testPassedCount: "Успешно: {{count}}", + testFailedCount: "Ошибок: {{count}}", + messageTemplates: "Шаблоны сообщений", + messageTemplatesDescription: "Настройте тексты уведомлений для каждого события.", + apiKeys: "Ключи API", + apiKeysDescription: "Создавайте ключи API для внешних интеграций и вебхуков.", + createKey: "Создать ключ", + apiKeyCreated: "Ключ API успешно создан", + apiKeyCopyWarning: "Скопируйте ключ сейчас — повторно он показан не будет!", + useInApiBrowser: "Использовать в обозревателе API", + apiKeyQrButton: "QR-код", + apiKeyQrTitle: "Сканирование для настройки", + apiKeyQrCaption: "Отсканируйте мобильным приложением, чтобы добавить этот сервер и ключ API.", + apiKeyQrWarning: "QR-код содержит секретный ключ API — не передавайте его и не публикуйте снимок экрана.", + createNewApiKey: "Создать ключ API", + keyName: "Название ключа", + keyNamePlaceholder: "например, Home Assistant или OctoPrint", + readStatus: "Чтение состояния", + readStatusDescription: "Просмотр состояния принтеров и очереди", + manageQueue: "Управление очередью", + manageQueueDescription: "Добавление и удаление заданий из очереди печати", + controlPrinter: "Управление принтером", + controlPrinterDescription: "Пауза, продолжение и остановка печати", + manageLibrary: "Управление библиотекой", + manageLibraryDescription: "Загрузка, переименование и удаление файлов библиотеки; импорт моделей из MakerWorld", + manageInventory: "Управление запасами", + manageInventoryDescription: "Создание, изменение и удаление катушек и записей учёта. Требуется для киосков SpoolBuddy: сканирование NFC, показания весов и системные команды киоска.", + manageMaintenance: "Управление обслуживанием", + manageMaintenanceDescription: "Регистрация выполненного обслуживания, сброс счётчиков, изменение интервалов и управление каталогом видов обслуживания. Подходит для автоматизаций Home Assistant, которые отмечают, например, «сопло очищено», без предоставления более широких прав управления принтером.", + manageArchives: "Управление архивом", + manageArchivesDescription: "Изменение и удаление записей архива печати, включая старые задания. Не включает удаление их вклада в статистику. Подходит для автоматизаций очистки истории печати.", + manageProjects: "Управление проектами", + manageProjectsDescription: "Создание, изменение и удаление проектов, а также добавление в них архивных заданий. Подходит для автоматизаций, распределяющих печати по проектам.", + libraryBadge: "Библиотека", + inventoryBadge: "Запасы", + maintenanceBadge: "Обслуживание", + archivesBadge: "Архив", + projectsBadge: "Проекты", + cloudAccess: "Разрешить доступ к облаку", + cloudAccessDescription: "Чтение пресетов и филаментов Bambu Cloud от вашего имени. Требуется вход в Bambu Cloud.", + cloudBadge: "Облако", + updateEnergyCost: "Обновление тарифа на электроэнергию", + updateEnergyCostDescription: "Разрешить этому ключу отправлять новую цену за кВт⋅ч методом POST в /settings/electricity-price. Полезно для автоматизаций динамического тарифа Home Assistant (Tibber, Octopus и т. п.). Это единственный параметр настроек, доступный для записи через ключ API.", + energyCostBadge: "Энергия", + legacyKey: "Устаревший", + legacyKeyTooltip: "Создан до появления привязки к пользователям; пересоздайте для доступа к облаку", + unnamedKey: "Ключ без названия", + lastUsed: "Последнее использование", + read: "Чтение", + control: "Управление", + createFirstKey: "Создать первый ключ", + webhookEndpoints: "Конечные точки вебхуков", + webhookApiKeyHint: "Передавайте ключ API в заголовке X-API-Key.", + webhook: { + getAllStatus: "Получить состояние всех принтеров", + getSpecificStatus: "Получить состояние выбранного принтера", + addToQueue: "Добавить в очередь печати", + pausePrint: "Приостановить печать", + resumePrint: "Продолжить печать", + stopPrint: "Остановить печать", + }, + apiBrowser: "Обозреватель API", + apiBrowserDescription: "Просмотр и проверка всех доступных конечных точек API.", + apiKeyForTesting: "Ключ API для проверки", + apiKeyPlaceholder: "Вставьте ключ API для проверки защищённых конечных точек…", + apiKeyHint: "Ключ будет передан в запросе в заголовке X-API-Key.", + deleteApiKeyTitle: "Удаление ключа API", + deleteApiKeyMessage: "Удалить этот ключ API? Все использующие его интеграции перестанут работать.", + deleteKey: "Удалить ключ", + amsDisplayThresholds: "Пороговые значения AMS", + amsThresholdsDescription: "Настройте цветовые пороги индикаторов влажности и температуры AMS.", + humidity: "Влажность", + goodGreen: "Норма (зелёный)", + fairOrange: "Допустимо (оранжевый)", + aboveFairBad: "Выше допустимого порога отображается красным", + fairAlsoDryingThreshold: "Этот порог также запускает автоматическую сушку, если она включена", + temperature: "Температура", + goodBlue: "Норма (синий)", + aboveFairHot: "Выше допустимого порога отображается красным как перегрев", + historyRetention: "Хранение истории", + keepSensorHistory: "Хранить историю датчиков", + historyRetentionDescription: "Более старые данные влажности и температуры будут удаляться автоматически", + defaultPrintOptions: "Параметры печати по умолчанию", + defaultPrintOptionsDescription: "Задайте значения по умолчанию для новых заданий. Их можно изменить отдельно в диалоге запуска печати.", + defaultBedLevelling: "Калибровка стола", + defaultBedLevellingDesc: "Автоматически выровнять стол перед печатью", + defaultFlowCali: "Калибровка потока", + defaultFlowCaliDesc: "Калибровать поток экструзии", + defaultVibrationCali: "Калибровка вибраций", + defaultVibrationCaliDesc: "Уменьшить артефакты звона", + defaultLayerInspect: "Проверка первого слоя", + defaultLayerInspectDesc: "Проверка первого слоя системой компьютерного зрения", + defaultTimelapse: "Таймлапс", + defaultTimelapseDesc: "Записывать таймлапс", + defaultNozzleOffsetCali: "Калибровка смещения сопел", + defaultNozzleOffsetCaliDesc: "Калибровать взаимное смещение сопел экструдеров", + tempFanPresetsTitle: "Пресеты температуры и вентиляторов", + tempFanPresetsDescription: "Настройте значения быстрого выбора в меню температуры и скорости вентиляторов карточки принтера. Кнопка «Выкл.» отображается всегда.", + tempFanPresetsNozzle: "Температура сопла", + tempFanPresetsBed: "Температура стола", + tempFanPresetsChamber: "Температура камеры", + tempFanPresetsFan: "Скорость вентилятора", + tempFanPresetsReset: "Восстановить значения по умолчанию", + concurrentUploadsTitle: "Одновременные загрузки", + concurrentUploadsDescription: "Количество принтеров, на которые очередь может одновременно отправлять файлы. Передача файлов на принтеры выполняется медленно: крупная модель может загружаться несколько минут, а каждый принтер ждёт своей очереди. В большом парке увеличение этого значения сокращает ожидание последних принтеров в группе. Уменьшите его, если сеть или сервер Bambuddy плохо справляются с параллельной передачей.", + concurrentUploadsLabel: "Одновременно загружать на принтеры", + concurrentUploadsHelp: "Значение 1 отправляет файл только на один принтер за раз, как в прежнем режиме. По умолчанию: 4.", + staggeredStart: "Поэтапный запуск", + staggeredStartDescription: "Размер группы и интервал по умолчанию при поэтапном запуске партии на нескольких принтерах. Для каждой партии значения можно изменить в окне запуска печати.", + preheatTitle: "Преднагрев и выдержка", + preheatDescription: "Нагреть стол и, если поддерживается, камеру, затем выдержать температуру перед запуском каждого задания очереди. Полезно для инженерных пластиков (PA, ABS) на принтерах без активного нагревателя камеры: во время выдержки стол прогревает камеру излучением. Целевая температура стола берётся из файла печати; поведение камеры зависит от модели принтера.", + preheatEnabled: "Включить преднагрев и выдержку", + preheatEnabledDesc: "Если выключено, задания очереди отправляются сразу. Для каждого задания можно задать отдельное значение.", + preheatFilamentTargetsLabel: "Целевая температура камеры по типу филамента (°C)", + preheatFilamentTargetsHint: "Bambuddy выбирает наибольшее значение среди задействованных слотов AMS; для печати только PLA автоматически используется 0 и этап прогрева камеры пропускается.", + preheatFilamentTargetsReset: "Восстановить значения по умолчанию", + preheatFilamentTargetsDefaultRow: "Прочее / без сопоставления", + preheatMaxWait: "Максимальное ожидание (с)", + preheatMaxWaitHelp: "Предельное время прогрева камеры, после которого процесс продолжится.", + preheatSoak: "Выдержка (с)", + preheatSoakHelp: "Время выдержки после достижения цели или истечения максимального ожидания.", + preheatHardwareTitle: "Поведение по моделям принтеров:", + preheatHardwareDetail: "H2C/H2D/H2D Pro/H2S/X2D/X1E активно нагревают камеру командой M141. X1C/P2S измеряют температуру камеры, но прогревают её только теплом стола. У P1S/P1P/A1/A1 Mini нет датчика камеры — применяется только таймер выдержки.", + preheatPerItemDesc: "Нагревать стол и камеру перед запуском этого задания. По умолчанию используется общий переключатель «Настройки → Рабочий процесс».", + preheatOverride_inherit: "Наследовать", + preheatOverride_on: "Включено", + preheatOverride_off: "Выключено", + preheatTargetOverride: "Переопределение температуры камеры (°C; пусто — значение для филамента)", + plateClear: "Подтверждение очистки пластины", + requirePlateClear: "Требовать подтверждение очистки пластины", + requirePlateClearDescription: "Если включено, планировщик не запускает следующее задание на принтере с завершённой печатью, пока не подтверждена очистка пластины. Отключение также скрывает индикатор состояния пластины и кнопку «Пластина очищена» на карточках принтеров.", + gcodeInjection: "Вставка G-кода", + gcodeInjectionDescription: "Настройте G-код, добавляемый в начало и/или конец печати для систем автоматического запуска, таких как Farmloop, SwapMod, AutoClear и Printflow 3D. Фрагменты задаются отдельно для каждой модели принтера и применяются, когда для задания очереди включена вставка G-кода.", + gcodeInjectionNoPrinters: "Принтеры не найдены. Добавьте принтеры, чтобы настроить фрагменты G-кода.", + gcodeStartLabel: "Начальный G-код", + gcodeEndLabel: "Завершающий G-код", + gcodeStartPlaceholder: "G-код, добавляемый перед началом печати…", + gcodeEndPlaceholder: "G-код, добавляемый после завершения печати…", + staggerGroupSize: "Размер группы", + staggerGroupSizeHelp: "Количество принтеров, запускаемых одновременно", + staggerInterval: "Интервал (мин)", + staggerIntervalHelp: "Задержка между запуском групп", + queueDrying: "Автоматическая сушка в очереди", + queueDryingDescription: "Автоматически сушить филамент в AMS, когда принтер простаивает между заданиями очереди. Запуск определяется указанным выше порогом влажности.", + queueDryingEnabled: "Включить автоматическую сушку", + queueDryingEnabledDescription: "Автоматически запускать сушку AMS, когда принтер простаивает и влажность выше порога", + queueDryingBlock: "Ожидать завершения сушки", + queueDryingBlockDescription: "Блокировать очередь печати до завершения сушки. Если выключено, печать имеет приоритет.", + ambientDryingEnabled: "Фоновая сушка", + ambientDryingEnabledDescription: "Автоматически сушить филамент на простаивающих принтерах при превышении порога влажности, даже если очередь пуста.", + printDryingEnabled: "Продолжать сушку во время печати", + printDryingEnabledDescription: "Разрешить автоматической сушке продолжаться во время печати на поддерживаемом оборудовании (H2D, H2C, H2S, P2S, H2D Pro, X2D, X1C, A2L с актуальной прошивкой). Для защиты катушек температура автоматически ограничивается на 5 °C ниже пресета сушки в простое.", + dryingPresets: "Пресеты сушки", + dryingPresetsDescription: "Температура и длительность для каждого типа филамента. AMS 2 Pro использует более низкие температуры, AMS-HT поддерживает более высокие.", + dryingFilament: "Филамент", + humidityThresholds: "Пороги влажности", + humidityThresholdsDescription: "Порог запуска автоматической сушки и предупреждений для каждого типа филамента. При смешанной загрузке AMS используется наименьший порог.", + humidityThresholdCol: "Порог", + humidityThresholdDefault: "По умолчанию (неизвестные типы)", + printModal: "Окно запуска печати", + expandCustomMapping: "Разворачивать пользовательское сопоставление", + expandCustomMappingDescription: "При печати на нескольких принтерах сразу показывать развёрнутое сопоставление AMS для каждого принтера", + authentication: "Аутентификация", + authEnabledDescription: "Доступ к экземпляру защищён аутентификацией пользователей", + authDisabledDescription: "Включите, чтобы требовать вход и управлять доступом пользователей", + authDisabledMessage: "Включите аутентификацию, чтобы создавать учётные записи, управлять правами и защитить экземпляр Bambuddy.", + enableAuthentication: "Включить аутентификацию", + currentUser: "Текущий пользователь", + changePassword: "Изменить пароль", + admin: "Администратор", + users: "Пользователи", + addUser: "Добавить пользователя", + groups: "Группы", + addGroup: "Добавить группу", + system: "Система", + noDescription: "Нет описания", + userCount: "Пользователей: {{count}}", + permissionCount: "Разрешений: {{count}}", + createUser: "Создать пользователя", + username: "Имя пользователя", + enterUsername: "Введите имя пользователя", + password: "Пароль", + enterPassword: "Введите пароль", + passwordRequirements: "Не менее 8 символов: одна заглавная и одна строчная буква, одна цифра и один специальный символ.", + confirmPassword: "Подтвердите пароль", + confirmPasswordPlaceholder: "Повторите пароль", + viewReleaseOnGitHub: "Открыть выпуск на GitHub", + turnAllPlugsOn: "Включить все розетки", + turnAllPlugsOff: "Выключить все розетки", + clearNotificationLogs: "Очистить журнал уведомлений", + clearLogsMessage: "Все записи журнала уведомлений старше 30 дней будут безвозвратно удалены. Это действие нельзя отменить.", + clearLogs: "Очистить журнал", + resetUiPreferences: "Сбросить настройки интерфейса", + resetUiPreferencesMessage: "Будут сброшены порядок боковой панели, тема, компоновка панели, режимы отображения и сортировка. Принтеры, архивы и серверные настройки НЕ изменятся. После очистки страница перезагрузится.", + resetPreferences: "Сбросить настройки", + deleteGroupTitle: "Удаление группы", + deleteGroupMessage: "Удалить эту группу? Пользователи группы потеряют назначенные ей разрешения.", + deleteGroup: "Удалить группу", + disableAuthenticationTitle: "Отключение аутентификации", + disableAuthenticationMessage: "Отключить аутентификацию? Bambuddy станет доступен без входа. Все пользователи останутся в базе данных, но проверка учётных данных выполняться не будет.", + disableAuthentication: "Отключить аутентификацию", + configureBambuddy: "Настроить Bambuddy", + systemDefault: "Системное значение", + archiveSettings: "Настройки архива", + newWindow: "Новое окно", + embeddedOverlay: "Встроенное наложение", + preferredSlicer: "Предпочитаемый слайсер", + preferredSlicerDescription: "Слайсер для нарезки внутри приложения через API-службу", + openInSlicerLabel: "Открывать в слайсере", + openInSlicerInherit: "Как для API-слайсера", + openInSlicerDescription: "Настольный слайсер, который открывается кнопкой «Открыть в слайсере». Оставьте «Как для API-слайсера», чтобы наследовать настройку, либо выберите другой локальный слайсер.", + orcaslicerKnownIssuesWarning: "В OrcaSlicer 2.3.2 и 2.4.0-dev известны ошибки CLI, мешающие нарезке многих 3MF от Bambu: #12426 (сбой на раскрашенных файлах с несколькими экструдерами) и #13386 (слишком строгая проверка диапазонов параметров). До исправления рекомендуется Bambu Studio.", + useSlicerApi: "Использовать API слайсера", + useSlicerApiDescription: "Если включено, действия «Нарезать» открывают встроенное окно и обращаются к API-службе слайсера. Если выключено, файл передаётся настольному слайсеру через URI-схему.", + slicerCard: "Слайсер", + orcaslicerApiUrl: "URL API-службы OrcaSlicer", + bambuStudioApiUrl: "URL API-службы Bambu Studio", + slicerApiUrlDescription: "URL контейнера API-службы слайсера. Оставьте пустым, чтобы использовать значения переменных окружения SLICER_API_URL или BAMBU_STUDIO_API_URL.", + slicerBundlesRemoved: { + title: "Пакеты профилей слайсера (удалено)", + description: "Импорт пакетов профилей принтера .bbscfg удалён. Экспорт пакета из BambuStudio содержит только пользовательские профили, поэтому стандартные процессы и филаменты не импортировались, а нарезка использовала встроенные настройки.", + alternatives: "Для отдельных пользовательских профилей используйте импорт одного профиля либо синхронизацию через Bambu Cloud или Orca Cloud. Стандартные профили автоматически предоставляет служба слайсера.", + lookupOrder: "Порядок поиска профиля при нарезке: 1) импортированный локально, 2) Orca Cloud, 3) Bambu Cloud, 4) стандартный профиль службы слайсера.", + }, + externalCameras: "Внешние камеры", + costTracking: "Учёт затрат", + printsOnly: "Только печать", + totalConsumption: "Общее потребление", + dataManagement: "Управление данными", + storageUsage: "Использование хранилища", + storageUsageDescription: "Распределение занятого места по категориям", + storageUsageTotal: "Всего", + storageUsageErrors: "Ошибки", + storageUsageOtherBreakdown: "Прочее (статические ресурсы, скрипты и конфигурационные файлы)", + storageUsageSystem: "Система", + storageUsageData: "Данные", + storageUsageUnavailable: "Сведения об использовании хранилища недоступны", + clearNotificationLogsDescription: "Удалить записи журнала уведомлений старше 30 дней", + resetUiPreferencesDescription: "Сбросить порядок боковой панели, тему, режимы отображения и компоновку. Принтеры, архивы и настройки не изменятся.", + enableHomeAssistant: "Включить Home Assistant", + enableMqtt: "Включить MQTT", + useTls: "Использовать TLS", + enableMetricsEndpoint: "Включить endpoint метрик", + availableMetrics: "Доступные метрики", + editUser: "Изменить пользователя", + deleteUserTitle: "Удаление пользователя", + groupName: "Название группы", + leaveEmptyForAnonymous: "Оставьте пустым для анонимного доступа", + leaveEmptyForNoAuth: "Оставьте пустым, чтобы не использовать аутентификацию", + enterNewPassword: "Введите новый пароль", + confirmNewPassword: "Повторите новый пароль", + enterGroupName: "Введите название группы", + enterDescriptionOptional: "Введите описание (необязательно)", + enterCurrentPassword: "Введите текущий пароль", + enterNewPasswordMin6: "Введите новый пароль (не менее 6 символов)", + toast: { + keyCopied: "Ключ скопирован в буфер обмена", + copyFailed: "Не удалось скопировать ключ", + keyAddedToBrowser: "Ключ добавлен в обозреватель API", + clearLogsFailed: "Не удалось очистить журнал", + uiPreferencesReset: "Настройки интерфейса сброшены. Обновление страницы…", + authDisabled: "Аутентификация отключена", + authDisableFailed: "Не удалось отключить аутентификацию", + apiKeyCreated: "Ключ API создан", + apiKeyDeleted: "Ключ API удалён", + userCreated: "Пользователь создан", + userUpdated: "Пользователь обновлён", + userDeleted: "Пользователь удалён", + groupCreated: "Группа создана", + groupUpdated: "Группа обновлена", + groupDeleted: "Группа удалена", + fillRequiredFields: "Заполните все обязательные поля", + passwordsDoNotMatch: "Пароли не совпадают", + passwordTooShort: "Пароль должен содержать не менее 8 символов", + passwordNeedsUppercase: "Пароль должен содержать хотя бы одну заглавную букву", + passwordNeedsLowercase: "Пароль должен содержать хотя бы одну строчную букву", + passwordNeedsDigit: "Пароль должен содержать хотя бы одну цифру", + passwordNeedsSpecial: "Пароль должен содержать хотя бы один специальный символ", + enterGroupName: "Введите название группы", + settingsSaved: "Настройки сохранены", + noPermissionUpdate: "У вас нет прав на изменение настроек", + cameraSettingsSaved: "Настройки камеры сохранены", + enterCameraUrl: "Введите URL камеры", + passwordChanged: "Пароль изменён", + connectionFailed: "Не удалось подключиться", + testFailed: "Проверка завершилась ошибкой", + cameraConnected: "Камера подключена{{resolution}}", + }, + testConnection: "Проверить подключение", + catalog: { + spoolCatalog: "Каталог катушек", + spoolCatalogDescription: "Масса пустых катушек по производителю и типу. Используется для автоматического определения массы при добавлении катушек.", + searchCatalog: "Поиск по каталогу…", + addNewEntry: "Добавить запись", + namePlaceholder: "Название (например, Bambu Lab — пластик)", + weight: "Масса", + type: "Тип", + default: "Стандартная", + custom: "Пользовательская", + noMatch: "Ничего не найдено", + empty: "Каталог пуст", + deleteEntry: "Удалить запись", + deleteConfirm: "Удалить «{{name}}»?", + resetCatalog: "Сбросить каталог", + resetConfirm: "Сбросить каталог к исходному состоянию? Все пользовательские записи будут удалены.", + loadFailed: "Не удалось загрузить каталог катушек", + nameWeightRequired: "Укажите название и массу", + entryAdded: "Запись добавлена", + addFailed: "Не удалось добавить запись", + entryUpdated: "Запись обновлена", + updateFailed: "Не удалось обновить запись", + entryDeleted: "Запись удалена", + deleteFailed: "Не удалось удалить запись", + resetSuccess: "Каталог сброшен к исходному состоянию", + resetFailed: "Не удалось сбросить каталог", + exported: "Экспортировано записей: {{count}}", + imported: "Импортировано записей: {{added}}, пропущено: {{skipped}}", + importFailed: "Не удалось импортировать: неверный формат JSON", + exportTooltip: "Экспортировать каталог в JSON", + importTooltip: "Импортировать каталог из JSON", + resetTooltip: "Сбросить к исходному состоянию", + selectedCount: "Выбрано: {{count}}", + deleteSelected: "Удалить выбранные", + bulkDeleteConfirm: "Удалить выбранные записи ({{count}})?", + bulkDeleted: "Удалено записей: {{count}}", + bulkDeleteFailed: "Не удалось удалить записи", + material: "Материал", + spoolWeight: "Масса катушки", + color: "Цвет", + updateSpoolWeight: "Обновить массу катушки", + filamentUpdated: "Филамент обновлён", + filamentUpdateFailed: "Не удалось обновить филамент", + filamentUpdateInvalid: "Некорректные данные филамента", + keepExistingSpoolWeight: "Сохранить прежнюю массу у существующих катушек", + keepExistingSpoolWeightDesc: "Катушки, уже созданные с этим типом филамента, сохранят прежнюю массу тары. Для новых катушек будет использоваться обновлённое значение.", + applyToAllSpools: "Применить ко всем катушкам", + applyToAllSpoolsDesc: "Все расчёты массы для катушек этого типа немедленно начнут использовать новую массу тары.", + }, + colorCatalog: { + title: "Каталог цветов", + description: "Цвета филамента по производителю и материалу. Используются для автоматического определения цвета при добавлении катушек.", + searchColors: "Поиск цветов…", + allManufacturers: "Все производители", + addNewColor: "Добавить цвет", + manufacturer: "Производитель", + colorName: "Название цвета", + hex: "HEX", + materialOptional: "Материал (необязательно)", + showing: "Показано {{filtered}} из {{total}} цветов", + noMatch: "Подходящие цвета не найдены", + empty: "Каталог цветов пуст", + deleteColor: "Удалить цвет", + deleteConfirm: "Удалить «{{name}}»?", + resetCatalog: "Сбросить каталог цветов", + resetConfirm: "Сбросить каталог к исходному состоянию? Все пользовательские цвета будут удалены.", + sync: "Синхронизировать", + starting: "Запуск…", + syncTooltip: "Синхронизировать с FilamentColors.xyz (более 2000 цветов; может занять около минуты)", + loadFailed: "Не удалось загрузить каталог цветов", + fieldsRequired: "Укажите производителя, название цвета и HEX-код", + colorAdded: "Цвет добавлен", + addFailed: "Не удалось добавить цвет", + colorUpdated: "Цвет обновлён", + updateFailed: "Не удалось обновить цвет", + colorDeleted: "Цвет удалён", + deleteFailed: "Не удалось удалить цвет", + resetSuccess: "Каталог цветов сброшен к исходному состоянию", + resetFailed: "Не удалось сбросить каталог цветов", + syncUpToDate: "Каталог уже актуален (проверено цветов: {{count}})", + syncComplete: "Добавлено новых цветов: {{added}}, уже существовало: {{skipped}}", + syncError: "Ошибка синхронизации", + syncFailed: "Не удалось синхронизировать каталог с FilamentColors.xyz", + exported: "Экспортировано цветов: {{count}}", + imported: "Импортировано цветов: {{added}}, пропущено: {{skipped}}", + importFailed: "Не удалось импортировать: неверный формат JSON", + selectedCount: "Выбрано: {{count}}", + deleteSelected: "Удалить выбранные", + bulkDeleteConfirm: "Удалить выбранные цвета ({{count}})?", + bulkDeleted: "Удалено цветов: {{count}}", + bulkDeleteFailed: "Не удалось удалить цвета", + }, + dateFormat: "Формат даты", + dateFormatUs: "США (ММ/ДД/ГГГГ)", + dateFormatEu: "Европа (ДД/ММ/ГГГГ)", + dateFormatIso: "ISO (ГГГГ-ММ-ДД)", + timeFormat: "Формат времени", + timeFormat12: "12-часовой (3:30 PM)", + timeFormat24: "24-часовой (15:30)", + defaultPrinter: "Принтер по умолчанию", + defaultPrinterDescription: "Предварительно выбирать этот принтер при загрузке файлов, повторной печати и других операциях.", + slicerBambuStudio: "Bambu Studio", + slicerOrcaSlicer: "OrcaSlicer", + sidebarOrderDescription: "Изменяйте порядок, видимость и пользовательские ссылки в настройках боковой панели.", + setDefault: "Сделать стандартным", + sidebarOrderSetDefaultHint: "Текущий порядок меню будет применяться к пользователям, которые ещё не настроили его самостоятельно.", + sidebarDefaultSet: "Стандартный порядок меню сохранён.", + sidebarDefaultCleared: "Стандартный порядок меню сброшен.", + sidebarDefaultFailed: "Не удалось сохранить стандартный порядок меню.", + reset: "Сбросить", + darkMode: "Тёмная тема", + lightMode: "Светлая тема", + active: "(активна)", + background: "Фон", + accent: "Акцент", + style: "Стиль", + bgNeutral: "Нейтральный", + bgWarm: "Тёплый", + bgCool: "Холодный", + bgOled: "Чёрный OLED", + bgSlate: "Сланцево-синий", + bgForest: "Лесной зелёный", + accentGreen: "Зелёный", + accentTeal: "Бирюзовый", + accentBlue: "Синий", + accentOrange: "Оранжевый", + accentPurple: "Фиолетовый", + accentRed: "Красный", + styleClassic: "Классический", + styleGlow: "Свечение", + styleVibrant: "Насыщенный", + themeToggleHint: "Переключайте тёмную, светлую и системную тему значком в боковой панели.", + autoArchivePrints: "Автоматически архивировать печать", + autoArchiveDescription: "Автоматически сохранять 3MF после завершения печати", + saveThumbnailsDescription: "Извлекать и сохранять изображения предпросмотра из 3MF", + captureFinishPhotoDescription: "Сделать снимок камерой принтера после завершения печати. Во время печати Bambuddy записывает короткий таймлапс, чтобы получить кадр до опускания стола. Если таймлапс был включён для задания, файл сохранится; иначе после получения снимка он будет автоматически удалён.", + ffmpegNotInstalled: "ffmpeg не установлен", + ffmpegRequired: "Для захвата изображения требуется ffmpeg. Установите его командой brew install ffmpeg в macOS или apt install ffmpeg в Linux.", + camera: "Камера", + cameraViewMode: "Режим просмотра камеры", + cameraOverlayDescription: "Камера открывается поверх основного экрана в окне с изменяемым размером", + cameraWindowDescription: "Камера открывается в отдельном окне браузера", + externalCamerasDescription: "Настройка внешних камер вместо встроенной камеры принтера. Поддерживаются потоки MJPEG, RTSP, HTTP-снимки и USB-камеры V4L2. Внешняя камера используется для просмотра в реальном времени и снимков после завершения печати.", + cameraPlaceholderUsb: "Путь к устройству (/dev/video0)", + cameraPlaceholderUrl: "URL камеры (rtsp://… или http://…)", + cameraTypeMjpeg: "Поток MJPEG", + cameraTypeRtsp: "Поток RTSP", + cameraTypeSnapshot: "HTTP-снимок", + cameraTypeUsb: "USB-камера (V4L2)", + cameraSnapshotUrl: "URL снимка (необязательно)", + cameraSnapshotUrlPlaceholder: "http://192.168.1.61:1984/api/frame.jpeg?src=printer", + cameraSnapshotUrlHelp: "URL одиночного кадра для миниатюр уведомлений, снимков после печати, кадров послойного таймлапса и распознавания пластины. Для таймлапса и распознавания пластины должны быть отдельно включены соответствующие настройки принтера; этот URL задаёт лишь источник изображения. Оставьте пустым, чтобы получать кадры из указанного выше видеопотока. Полезно для go2rtc (/api/frame.jpeg) и IP-камер с отдельным endpoint снимка.", + cameraRotation: "Поворот", + test: "Проверить", + connected: "Подключено", + disconnected: "Отключено", + currency: "Валюта", + defaultFilamentCost: "Стоимость филамента по умолчанию (за кг)", + electricityCost: "Стоимость электроэнергии за кВт·ч", + energyDisplayMode: "Режим отображения энергии", + energyModePrintDescription: "На панели показывается сумма энергии, израсходованной во время печати", + energyModeTotalDescription: "На панели показывается полный расход энергии по данным умных розеток", + fileManager: "Файловый менеджер", + createArchiveEntry: "Создавать запись в архиве при печати", + createArchiveEntryDescription: "При запуске печати из файлового менеджера при необходимости создавать запись в архиве", + lowDiskSpaceWarning: "Предупреждение о нехватке места", + lowDiskSpaceDescription: "Показывать предупреждение, когда свободного места остаётся меньше указанного порога", + printerFirmware: "Прошивка принтера", + checkFirmwareDescription: "Проверять наличие обновлений прошивки принтера у Bambu Lab", + bambuddySoftware: "Программное обеспечение Bambuddy", + autoCheckDescription: "Автоматически проверять наличие новых версий при запуске", + checkNow: "Проверить сейчас", + updateAvailableVersion: "Доступно обновление: v{{version}}", + releaseNotes: "Примечания к выпуску", + updateViaDocker: "Обновление через Docker Compose:", + updateViaHomeAssistant: "Обновления устанавливаются через Home Assistant Supervisor. Чтобы установить новую версию, откройте в Home Assistant: Настройки → Дополнения → Bambuddy.", + updateViaWindowsInstaller: "В Windows обновление выполняется повторным запуском установщика. Скачайте новую версию ниже — данные, настройки и принтеры сохранятся.", + downloadWindowsInstaller: "Скачать установщик v{{version}}", + installUpdate: "Установить обновление", + latestVersionRunning: "Установлена последняя версия", + failedToCheckUpdates: "Не удалось проверить обновления: {{error}}", + backupRestore: "Резервное копирование и восстановление", + backupRestoreDescription: "Экспорт и импорт настроек, а также резервное копирование в GitHub", + goToBackup: "Перейти к резервным копиям", + externalUrl: "Внешний URL", + externalUrlDescription: "Внешний адрес, по которому доступен Bambuddy. Используется для изображений в уведомлениях и внешних интеграций.", + bambuddyUrl: "URL Bambuddy", + externalUrlHint: "Укажите протокол и порт, например http://192.168.1.100:8000", + ftpRetry: "Повтор FTP-операций", + ftpRetryDescription: "Повторять FTP-операции при нестабильном Wi‑Fi принтера. Применяется к скачиванию 3MF и таймлапсов, загрузке заданий и обновлению прошивки.", + autoRetryDescription: "Автоматически повторять неудачные FTP-операции", + retryAttempts: "Количество попыток", + retryDelay: "Задержка между попытками", + connectionTimeout: "Тайм-аут подключения", + time_one: "{{count}} раз", + time_other: "{{count}} раз", + second_one: "{{count}} секунда", + second_other: "{{count}} секунд", + nSeconds: "{{count}} секунд", + increaseForWeakWifi: "Увеличьте для принтеров со слабым Wi‑Fi", + homeAssistant: "Home Assistant", + homeAssistantFullDescription: "Подключение к Home Assistant для управления умными розетками через REST API. Поддерживаются сущности switch, light, input_boolean и script.", + homeAssistantUrl: "URL Home Assistant", + longLivedAccessToken: "Долгосрочный токен доступа", + haTokenHint: "Создайте токен в Home Assistant: Профиль → Долгосрочные токены доступа → Создать токен", + connectionSuccessful: "Подключение установлено", + connectionFailed: "Подключиться не удалось", + haConnectionSuccess: "Подключение к Home Assistant установлено.", + haConnectionFailed: "Не удалось подключиться к Home Assistant.", + mqttPublishing: "Публикация MQTT", + mqttDescription: "Публикация событий Bambuddy во внешнем MQTT-брокере для интеграции с Node-RED, Home Assistant и другими системами автоматизации.", + mqttEnableDescription: "Публиковать события во внешнем MQTT-брокере", + brokerHostname: "Имя хоста брокера", + port: "Порт", + usernameOptional: "Имя пользователя (необязательно)", + passwordOptional: "Пароль (необязательно)", + topicPrefix: "Префикс топиков", + topicPrefixHint: "Топики будут иметь вид {{prefix}}/printers//status и т. п.", + prometheusMetrics: "Метрики Prometheus", + prometheusEndpointDescription: "Публиковать метрики принтеров по адресу /api/v1/metrics для мониторинга в Prometheus/Grafana.", + bearerTokenOptional: "Bearer-токен (необязательно)", + bearerTokenHint: "Если задан, запросы должны содержать Authorization: Bearer ", + metricsConnectionStatus: "Состояние подключения", + metricsPrinterState: "Состояние принтера (ожидание, печать и т. п.)", + metricsPrintProgress: "Прогресс печати, 0–100 %", + metricsBedTemp: "Температура стола", + metricsNozzleTemp: "Температура сопла", + metricsPrintsTotal: "Общее количество заданий по результату", + metricsMore: "…и другие данные: слои, вентиляторы, очередь, расход филамента", + smartPlugsDescription: "Подключение умных розеток Tasmota или Home Assistant для автоматического управления питанием принтеров и учёта энергопотребления.", + allOn: "Включить все", + allOff: "Выключить все", + addSmartPlug: "Добавить умную розетку", + energySummary: "Сводка энергопотребления", + currentPower: "Текущая мощность", + plugsOnline: "Розеток в сети: {{reachable}}/{{total}}", + today: "Сегодня", + yesterday: "Вчера", + total: "Всего", + enablePlugsForSummary: "Включите розетки, чтобы увидеть сводку энергопотребления", + addNotificationProvider: "Добавить", + systemBadge: "(системная)", + creating: "Создание…", + changing: "Изменение…", + deleteUserAndItems: "Удалить пользователя И его объекты", + deleteUserKeepItems: "Удалить пользователя, объекты оставить без владельца", + ok: "ОК", twoFa: { - totpTitle: 'Приложение-аутентификатор (TOTP)', - totpDesc: 'Использовать приложение-аутентификатор, например Google Authenticator, Aegis или Authy.', - emailOtpTitle: 'Одноразовый код по электронной почте', - emailOtpDesc: 'Отправлять одноразовый код на {{email}} при входе.', - emailOtpNoEmail: 'Добавьте адрес электронной почты в учётную запись, чтобы включить этот способ.', - addEmailFirst: 'В вашей учётной записи нет адреса электронной почты. Перед включением одноразовых кодов попросите администратора добавить его.', - setupTotp: 'Настроить приложение-аутентификатор', - setupAuthApp: 'Настроить приложение-аутентификатор', - setupInstructions: 'Отсканируйте QR-код приложением-аутентификатором, затем подтвердите настройку кодом.', - manualEntry: 'Не получается отсканировать? Введите этот секрет вручную:', - scannedContinue: 'Код отсканирован — продолжить', - enterCodeToConfirm: 'Для подтверждения настройки введите шестизначный код из приложения-аутентификатора.', - activate: 'Активировать', - disableTotp: 'Отключить аутентификатор', - disableConfirmHint: 'Чтобы отключить аутентификатор, введите действующий код TOTP или резервный код.', - totpDisabled: 'Приложение-аутентификатор отключено.', - emailOtpEnabled: 'Одноразовые коды по электронной почте включены.', - emailOtpDisabled: 'Одноразовые коды по электронной почте отключены.', - smtpRequired: 'Сначала настройте и проверьте SMTP.', - invalidCode: 'Неверный код. Повторите попытку.', - enableEmailOtp: 'Включить одноразовые коды по электронной почте', - disableEmailOtp: 'Отключить одноразовые коды по электронной почте', - emailSetupEnterCode: 'Код подтверждения отправлен на вашу электронную почту. Введите его ниже, чтобы подтвердить доступ к этому почтовому ящику.', - verifyAndEnable: 'Подтвердить и включить', - emailDisablePasswordHint: 'Чтобы подтвердить отключение одноразовых кодов по электронной почте, введите пароль учётной записи.', - passwordPlaceholder: 'Введите пароль', - backupCodesTitle: 'Сохраните резервные коды', - backupCodesWarning: 'Сохраните эти коды в надёжном месте. Каждый код можно использовать только один раз; повторно они показаны не будут.', - backupCodesRemaining: 'Осталось резервных кодов: {{count}}', - savedCodes: 'Я сохранил коды', - regenBackup: 'Создать новые резервные коды', - regenBackupHint: 'Введите текущий код TOTP, чтобы создать 10 новых резервных кодов. Все существующие резервные коды станут недействительными.', - newBackupCodes: 'Новые резервные коды', - linkedAccounts: 'Связанные учётные записи SSO', - linkedAccountsDesc: 'Эти внешние провайдеры идентификации связаны с вашей учётной записью.', - oidcUnlinked: 'Учётная запись отсоединена.', - }, - encryption: { - title: 'Состояние шифрования MFA', - enabledFromEnv: 'Шифрование данных включено; ключ получен из переменной окружения MFA_ENCRYPTION_KEY', - enabledFromFile: 'Шифрование данных включено; ключ загружен из каталога данных', - enabledGenerated: 'Шифрование данных включено с автоматически созданным ключом', - notConfigured: 'Шифрование хранимых данных не настроено', - notConfiguredDesc: 'Секреты TOTP и client_secret провайдеров OIDC хранятся открытым текстом. Задайте MFA_ENCRYPTION_KEY или перезапустите Bambuddy с доступным для записи каталогом данных, чтобы ключ был создан автоматически.', - allEncrypted: 'Все секреты MFA зашифрованы при хранении.', - legacyRowsLabel: 'Старые записи открытым текстом', - encryptedRowsLabel: 'Зашифрованные записи', - legacyRowsWarning: 'Обнаружено старых записей открытым текстом: {{count}}. Чтобы перенести их в зашифрованное хранилище, повторно сохраните провайдера OIDC или заново подключите приложение-аутентификатор пользователя.', - backupHint: 'Автоматически созданный ключ хранится в DATA_DIR/.mfa_encryption_key и включается в локальные ZIP-копии. Храните резервные копии в безопасном месте либо задайте MFA_ENCRYPTION_KEY явно.', - decryptionBrokenTitle: 'Отсутствует ключ шифрования', - decryptionBrokenError: 'Невозможно расшифровать записей: {{count}}, поскольку ключ шифрования больше недоступен. Для восстановления верните прежний MFA_ENCRYPTION_KEY или файл DATA_DIR/.mfa_encryption_key.', - migrationErrorWarning: 'Не удалось повторно зашифровать старых записей при запуске: {{count}}. Проверьте журнал сервера и перезапустите Bambuddy для повторной попытки.', + totpTitle: "Приложение-аутентификатор (TOTP)", + totpDesc: "Использовать приложение-аутентификатор, например Google Authenticator, Aegis или Authy.", + emailOtpTitle: "Одноразовый код по электронной почте", + emailOtpDesc: "Отправлять одноразовый код на {{email}} при входе.", + emailOtpNoEmail: "Добавьте адрес электронной почты в учётную запись, чтобы включить этот способ.", + addEmailFirst: "В вашей учётной записи нет адреса электронной почты. Перед включением одноразовых кодов попросите администратора добавить его.", + setupTotp: "Настроить приложение-аутентификатор", + setupAuthApp: "Настроить приложение-аутентификатор", + setupInstructions: "Отсканируйте QR-код приложением-аутентификатором, затем подтвердите настройку кодом.", + manualEntry: "Не получается отсканировать? Введите этот секрет вручную:", + scannedContinue: "Код отсканирован — продолжить", + enterCodeToConfirm: "Для подтверждения настройки введите шестизначный код из приложения-аутентификатора.", + activate: "Активировать", + disableTotp: "Отключить аутентификатор", + disableConfirmHint: "Чтобы отключить аутентификатор, введите действующий код TOTP или резервный код.", + totpDisabled: "Приложение-аутентификатор отключено.", + emailOtpEnabled: "Одноразовые коды по электронной почте включены.", + emailOtpDisabled: "Одноразовые коды по электронной почте отключены.", + smtpRequired: "Сначала настройте и проверьте SMTP.", + invalidCode: "Неверный код. Повторите попытку.", + enableEmailOtp: "Включить одноразовые коды по электронной почте", + disableEmailOtp: "Отключить одноразовые коды по электронной почте", + emailSetupEnterCode: "Код подтверждения отправлен на вашу электронную почту. Введите его ниже, чтобы подтвердить доступ к этому почтовому ящику.", + verifyAndEnable: "Подтвердить и включить", + emailDisablePasswordHint: "Чтобы подтвердить отключение одноразовых кодов по электронной почте, введите пароль учётной записи.", + passwordPlaceholder: "Введите пароль", + backupCodesTitle: "Сохраните резервные коды", + backupCodesWarning: "Сохраните эти коды в надёжном месте. Каждый код можно использовать только один раз; повторно они показаны не будут.", + backupCodesRemaining: "Осталось резервных кодов: {{count}}", + savedCodes: "Я сохранил коды", + regenBackup: "Создать новые резервные коды", + regenBackupHint: "Введите текущий код TOTP, чтобы создать 10 новых резервных кодов. Все существующие резервные коды станут недействительными.", + newBackupCodes: "Новые резервные коды", + linkedAccounts: "Связанные учётные записи SSO", + linkedAccountsDesc: "Эти внешние провайдеры идентификации связаны с вашей учётной записью.", + oidcUnlinked: "Учётная запись отсоединена.", }, sessionPolicy: { - title: 'Политика сеансов', - description: 'Максимальный срок действия сеанса для новых входов пользователей. Уже выданные токены сохраняют исходный срок действия.', - preset24h: '24 часа', - preset7d: '7 дней', - preset30d: '30 дней', - customHoursLabel: 'Срок действия сеанса в часах', - hoursSuffix: 'часов', - warning: 'Длительные сеансы снижают защиту автоматическим выходом. Рекомендуется только для доверенных установок с одним пользователем.', + title: "Политика сеансов", + description: "Максимальный срок действия сеанса для новых входов пользователей. Уже выданные токены сохраняют исходный срок действия.", + preset24h: "24 часа", + preset7d: "7 дней", + preset30d: "30 дней", + customHoursLabel: "Срок действия сеанса в часах", + hoursSuffix: "часов", + warning: "Длительные сеансы снижают защиту автоматическим выходом. Рекомендуется только для доверенных установок с одним пользователем.", }, - webhook: { - getAllStatus: 'Получить состояние всех принтеров', - getSpecificStatus: 'Получить состояние выбранного принтера', - addToQueue: 'Добавить в очередь печати', - pausePrint: 'Приостановить печать', - resumePrint: 'Продолжить печать', - stopPrint: 'Остановить печать', + oidc: { + title: "Провайдеры SSO / OIDC", + desc: "Настройка провайдеров OpenID Connect для единого входа через внешние службы идентификации.", + addProvider: "Добавить провайдера", + newProvider: "Новый провайдер", + empty: "Провайдеры OIDC пока не настроены.", + created: "Провайдер создан.", + updated: "Провайдер обновлён.", + deleted: "Провайдер удалён.", + refreshIcon: "Обновить значок", + removeIcon: "Удалить значок", + iconRefreshed: "Значок обновлён.", + iconRemoved: "Значок удалён.", + iconFetchFailed: "Не удалось получить значок по URL провайдера.", + deleteTitle: "Удалить провайдера", + deleteMessage: "Удалить «{{name}}»? Все связанные учётные записи пользователей будут отсоединены.", + form: { + name: "Отображаемое имя", + issuerUrl: "URL издателя", + clientId: "Идентификатор клиента", + clientSecret: "Секрет клиента", + scopes: "Области доступа", + iconUrl: "URL значка (необязательно)", + enabled: "Включён", + autoCreate: "Автоматически создавать пользователей", + autoCreateDesc: "Автоматически создавать локальную учётную запись при первом входе.", + autoLink: "Автоматически связывать существующие учётные записи", + autoLinkDesc: "При первом входе связывать локальные учётные записи по совпадающему адресу электронной почты.", + secretHint: "оставьте пустым, чтобы сохранить текущий", + secretPlaceholder: "новый секрет", + emailClaim: "Поле электронной почты", + emailClaimDesc: "Поле JWT, используемое как адрес электронной почты. Для Azure Entra ID, который не передаёт email_verified, используйте preferred_username или upn. Указывайте только доверенные поля.", + emailClaimPlaceholder: "email", + emailClaimCustomClaimAutoLinkWarning: "Пользовательское поле безопасно для автоматического связывания только тогда, когда его значение назначается администратором арендатора, например upn или preferred_username в Azure Entra ID. Не включайте автоматическое связывание, если ваш IdP позволяет пользователям самостоятельно задавать это поле.", + requireEmailVerified: "Требовать подтверждённый адрес электронной почты", + requireEmailVerifiedDesc: "Принимать адрес электронной почты только в том случае, если провайдер пометил его как подтверждённый.", + requireEmailVerifiedWarning: "Предупреждение: адрес будет принят даже без подтверждения. Используйте только с доверенными провайдерами.", + requireEmailVerifiedAutoLink: "Чтобы изменить этот параметр, сначала отключите автоматическое связывание.", + defaultGroup: "Группа по умолчанию", + defaultGroupDesc: "Группа для автоматически созданных пользователей. Если не задана, используется группа «Наблюдатели».", + defaultGroupViewersFallback: "Наблюдатели (по умолчанию)", + autologin: "Автоматический вход", + autologinDesc: "Сразу перенаправлять неавторизованных посетителей к этому провайдеру. Этот флаг может быть установлен только у одного провайдера.", + }, + }, + encryption: { + title: "Состояние шифрования MFA", + enabledFromEnv: "Шифрование данных включено; ключ получен из переменной окружения MFA_ENCRYPTION_KEY", + enabledFromFile: "Шифрование данных включено; ключ загружен из каталога данных", + enabledGenerated: "Шифрование данных включено с автоматически созданным ключом", + notConfigured: "Шифрование хранимых данных не настроено", + notConfiguredDesc: "Секреты TOTP и client_secret провайдеров OIDC хранятся открытым текстом. Задайте MFA_ENCRYPTION_KEY или перезапустите Bambuddy с доступным для записи каталогом данных, чтобы ключ был создан автоматически.", + allEncrypted: "Все секреты MFA зашифрованы при хранении.", + legacyRowsLabel: "Старые записи открытым текстом", + encryptedRowsLabel: "Зашифрованные записи", + legacyRowsWarning: "Обнаружено старых записей открытым текстом: {{count}}. Чтобы перенести их в зашифрованное хранилище, повторно сохраните провайдера OIDC или заново подключите приложение-аутентификатор пользователя.", + backupHint: "Автоматически созданный ключ хранится в DATA_DIR/.mfa_encryption_key и включается в локальные ZIP-копии. Храните резервные копии в безопасном месте либо задайте MFA_ENCRYPTION_KEY явно.", + decryptionBrokenTitle: "Отсутствует ключ шифрования", + decryptionBrokenError: "Невозможно расшифровать записей: {{count}}, поскольку ключ шифрования больше недоступен. Для восстановления верните прежний MFA_ENCRYPTION_KEY или файл DATA_DIR/.mfa_encryption_key.", + migrationErrorWarning: "Не удалось повторно зашифровать старых записей при запуске: {{count}}. Проверьте журнал сервера и перезапустите Bambuddy для повторной попытки.", + }, + pipelineLimits: { + title: "Ограничения конвейеров слайсера", + maxCopiesLabel: "Максимум копий за запуск", + maxCopiesDesc: "Верхний предел количества копий, которое оператор может запросить при запуске конвейера. Жёсткое серверное ограничение — 1000.", }, pipelines: { - title: 'Конвейеры слайсера', - subtitle: 'Повторно используемые наборы профилей: принтер, процесс, филаменты и тип стола. Сохраните набор в окне нарезки и применяйте его к следующему файлу одним нажатием.', - loading: 'Загрузка конвейеров...', - loadError: 'Не удалось загрузить конвейеры.', - confirmDelete: 'Удалить этот конвейер? Отменить действие нельзя.', - staleWarning: 'Один или несколько связанных профилей больше не существуют. Чтобы исправить конвейер, повторно сохраните его из окна нарезки.', + title: "Конвейеры слайсера", + subtitle: "Повторно используемые наборы профилей: принтер, процесс, филаменты и тип стола. Сохраните набор в окне нарезки и применяйте его к следующему файлу одним нажатием.", + loading: "Загрузка конвейеров...", + loadError: "Не удалось загрузить конвейеры.", + confirmDelete: "Удалить этот конвейер? Отменить действие нельзя.", + staleWarning: "Один или несколько связанных профилей больше не существуют. Чтобы исправить конвейер, повторно сохраните его из окна нарезки.", empty: { - title: 'Конвейеров пока нет.', - howto: 'Откройте окно нарезки любого файла, выберите принтер, процесс, филаменты и тип стола, затем нажмите «Сохранить как конвейер». Сохранённые конвейеры появятся здесь.', + title: "Конвейеров пока нет.", + howto: "Откройте окно нарезки любого файла, выберите принтер, процесс, филаменты и тип стола, затем нажмите «Сохранить как конвейер». Сохранённые конвейеры появятся здесь.", }, field: { - name: 'Название конвейера', - description: 'Описание', - targetPrinter: 'Целевой принтер', - noTarget: '— Цель не задана —', - targetKind: 'Тип цели', - targetKindSpecific: 'Конкретный принтер', - targetKindClass: 'Класс принтеров', - targetModelClass: 'Модель принтера', - fanoutStrategy: 'Стратегия распределения', + name: "Название конвейера", + description: "Описание", + targetPrinter: "Целевой принтер", + noTarget: "— Цель не задана —", + targetKind: "Тип цели", + targetKindSpecific: "Конкретный принтер", + targetKindClass: "Класс принтеров", + targetModelClass: "Модель принтера", + fanoutStrategy: "Стратегия распределения", fanout: { - max_parallel: 'Максимум параллельно — распределять между любыми свободными подходящими принтерами', - round_robin: 'По кругу — последовательно чередовать подходящие принтеры', - fill_one_first: 'Сначала один — назначить все копии одному принтеру', + max_parallel: "Максимум параллельно — распределять между любыми свободными подходящими принтерами", + round_robin: "По кругу — последовательно чередовать подходящие принтеры", + fill_one_first: "Сначала один — назначить все копии одному принтеру", }, fanoutShort: { - max_parallel: 'параллельно', - round_robin: 'по кругу', - fill_one_first: 'сначала один', + max_parallel: "параллельно", + round_robin: "по кругу", + fill_one_first: "сначала один", }, }, action: { - save: 'Сохранить', - cancel: 'Отмена', - rename: 'Переименовать', - delete: 'Удалить', + save: "Сохранить", + cancel: "Отмена", + rename: "Переименовать", + delete: "Удалить", }, slot: { - printer: 'Принтер', - process: 'Процесс', - filament: 'Филамент', - filamentN: 'Филамент {{n}}', - filamentAll: 'Все слоты: {{n}}', - bed: 'Стол', + printer: "Принтер", + process: "Процесс", + filament: "Филамент", + filamentN: "Филамент {{n}}", + filamentAll: "Все слоты: {{n}}", + bed: "Стол", }, group: { - profiles: 'Профили', - filaments: 'Филаменты', + profiles: "Профили", + filaments: "Филаменты", }, - searchPlaceholder: 'Поиск конвейеров...', - filterTargetType: 'Фильтр по типу цели', - filterTarget: 'Фильтр по цели', + searchPlaceholder: "Поиск конвейеров...", + filterTargetType: "Фильтр по типу цели", + filterTarget: "Фильтр по цели", filter: { - all: 'Все цели', - noTarget: 'Цель не задана', - count: '{{shown}} из {{total}}', - noMatches: 'Нет конвейеров, соответствующих текущим фильтрам.', + all: "Все цели", + noTarget: "Цель не задана", + count: "{{shown}} из {{total}}", + noMatches: "Нет конвейеров, соответствующих текущим фильтрам.", }, toast: { - saved: 'Конвейер сохранён', - saveFailed: 'Не удалось сохранить', - deleted: 'Конвейер удалён', - deleteFailed: 'Не удалось удалить', + saved: "Конвейер сохранён", + saveFailed: "Не удалось сохранить", + deleted: "Конвейер удалён", + deleteFailed: "Не удалось удалить", }, - noTargetHint: 'Чтобы запустить, задайте целевой принтер', - noTargetWarning: 'Перед запуском конвейера задайте целевой принтер.', + noTargetHint: "Чтобы запустить, задайте целевой принтер", + noTargetWarning: "Перед запуском конвейера задайте целевой принтер.", runs: { - lastRun: 'Последний запуск', + lastRun: "Последний запуск", status: { - queued: 'в очереди', - slicing: 'нарезка', - dispatching: 'назначение', - in_progress: 'печать', - completed: 'завершено', - failed: 'ошибка', - partial_failure: 'частичная ошибка', - cancelled: 'отменено', + queued: "в очереди", + slicing: "нарезка", + dispatching: "назначение", + in_progress: "печать", + completed: "завершено", + failed: "ошибка", + partial_failure: "частичная ошибка", + cancelled: "отменено", }, }, }, - pipelineLimits: { - title: 'Ограничения конвейеров слайсера', - maxCopiesLabel: 'Максимум копий за запуск', - maxCopiesDesc: 'Верхний предел количества копий, которое оператор может запросить при запуске конвейера. Жёсткое серверное ограничение — 1000.', - }, - slicerBundlesRemoved: { - title: 'Пакеты профилей слайсера (удалено)', - description: 'Импорт пакетов профилей принтера .bbscfg удалён. Экспорт пакета из BambuStudio содержит только пользовательские профили, поэтому стандартные процессы и филаменты не импортировались, а нарезка использовала встроенные настройки.', - alternatives: 'Для отдельных пользовательских профилей используйте импорт одного профиля либо синхронизацию через Bambu Cloud или Orca Cloud. Стандартные профили автоматически предоставляет служба слайсера.', - lookupOrder: 'Порядок поиска профиля при нарезке: 1) импортированный локально, 2) Orca Cloud, 3) Bambu Cloud, 4) стандартный профиль службы слайсера.', - }, - linkingExistingSpools: 'Привязка существующих катушек', - linkingExistingSpoolsDesc: 'Чтобы привязать существующую катушку Spoolman к AMS, наведите указатель на слот AMS и нажмите «Привязать к Spoolman».', - syncMode: 'Режим синхронизации', - syncModeAuto: 'Автоматически', - syncModeManual: 'Только вручную', - syncModeAutoDesc: 'Данные AMS синхронизируются автоматически при обнаружении изменений', - syncModeManualDesc: 'Синхронизация выполняется только вручную', - syncAmsData: 'Синхронизировать данные AMS', - syncAmsDataDesc: 'Вручную синхронизировать данные AMS принтера со Spoolman', - allPrinters: 'Все принтеры', - noDefaultPrinter: 'Без принтера по умолчанию (спрашивать каждый раз)', - sidebarOrder: 'Порядок пунктов боковой панели', - saveThumbnails: 'Сохранять миниатюры', - captureFinishPhoto: 'Снимок после завершения', - noPrintersConfigured: 'Принтеры не настроены', - archiveMode: { - always: 'Всегда создавать запись в архиве', - never: 'Не создавать запись в архиве', - ask: 'Спрашивать каждый раз', - }, - checkForUpdatesLabel: 'Проверять обновления', - checkPrinterFirmware: 'Проверять прошивку принтера', - includeBetaUpdates: 'Включать бета-версии', - includeBetaUpdatesDesc: 'Уведомлять о бета- и предварительных версиях при проверке обновлений', - localLogin: { - disable: 'Отключить локальный вход по имени пользователя и паролю', - disableHint: 'Если включено, вход возможен только через провайдеров SSO. На LDAP это не влияет. Чтобы сохранить аварийный способ входа, задайте на сервере BAMBUDDY_LOCAL_LOGIN=true.', - }, - enableRetry: 'Включить повторные попытки', - homeAssistantDescription: 'Управление умными розетками через Home Assistant', - environmentManagedLabel: '(управляется окружением)', - autoEnabledViaEnv: 'Автоматически включено переменными окружения', - urlFromEnvReadOnly: 'Значение задано переменной окружения HA_URL (только чтение)', - tokenFromEnvReadOnly: 'Значение задано переменной окружения HA_TOKEN (только чтение)', - mqttConnectedTo: 'Подключено к', - prometheusDescription: 'Экспортировать данные принтеров в формате Prometheus', - noSmartPlugsTitle: 'Умные розетки не настроены', - noSmartPlugsDescription: 'Добавьте умную розетку на базе Tasmota для учёта энергии и автоматизации питания.', - noProvidersTitle: 'Провайдеры не настроены', - noProvidersDescription: 'Добавьте провайдера для получения уведомлений.', - noTemplatesAvailable: 'Шаблоны отсутствуют. Перезапустите бэкенд, чтобы создать стандартные шаблоны.', - apiPermissionView: 'Просмотр состояния принтеров и очереди', - apiPermissionEdit: 'Добавление и удаление заданий из очереди печати', - apiKeysEmptyTitle: 'Ключей API нет', - apiKeysEmptyDescription: 'Создайте ключ API для интеграции с внешними сервисами.', - noUsersFound: 'Пользователи не найдены', - noGroupsFound: 'Группы не найдены', - noGroupsAvailable: 'Доступных групп нет', - passwordsDoNotMatch: 'Пароли не совпадают', - systemGroupWarning: 'Названия системных групп изменить нельзя', - authDisabledTitle: 'Аутентификация отключена', - authDisabledFeature1: 'Требовать вход для доступа к системе', - authDisabledFeature2: 'Создавать нескольких пользователей с групповыми разрешениями', - authDisabledFeature3: 'Управлять доступом с помощью более чем 50 отдельных разрешений', - userHasCreated: 'Этот пользователь создал:', - userItemsQuestion: 'Что сделать с этими объектами?', - deleteUserConfirm: 'Удалить этого пользователя?', - actionCannotBeUndone: 'Это действие нельзя отменить.', - addFirstSmartPlug: 'Добавить первую умную розетку', - providers: 'Провайдеры', - log: 'Журнал', - testAll: 'Проверить все', - testResults: 'Результаты проверки', - testPassedCount: 'Успешно: {{count}}', - testFailedCount: 'Ошибок: {{count}}', - messageTemplates: 'Шаблоны сообщений', - messageTemplatesDescription: 'Настройте тексты уведомлений для каждого события.', - apiKeys: 'Ключи API', - apiKeysDescription: 'Создавайте ключи API для внешних интеграций и вебхуков.', - createKey: 'Создать ключ', - apiKeyCreated: 'Ключ API успешно создан', - apiKeyCopyWarning: 'Скопируйте ключ сейчас — повторно он показан не будет!', - useInApiBrowser: 'Использовать в обозревателе API', - apiKeyQrButton: 'QR-код', - apiKeyQrTitle: 'Сканирование для настройки', - apiKeyQrCaption: 'Отсканируйте мобильным приложением, чтобы добавить этот сервер и ключ API.', - apiKeyQrWarning: 'QR-код содержит секретный ключ API — не передавайте его и не публикуйте снимок экрана.', - createNewApiKey: 'Создать ключ API', - keyName: 'Название ключа', - keyNamePlaceholder: 'например, Home Assistant или OctoPrint', - readStatus: 'Чтение состояния', - readStatusDescription: 'Просмотр состояния принтеров и очереди', - manageQueue: 'Управление очередью', - manageQueueDescription: 'Добавление и удаление заданий из очереди печати', - controlPrinter: 'Управление принтером', - controlPrinterDescription: 'Пауза, продолжение и остановка печати', - manageLibrary: 'Управление библиотекой', - manageLibraryDescription: 'Загрузка, переименование и удаление файлов библиотеки; импорт моделей из MakerWorld', - manageInventory: 'Управление запасами', - manageInventoryDescription: 'Создание, изменение и удаление катушек и записей учёта. Требуется для киосков SpoolBuddy: сканирование NFC, показания весов и системные команды киоска.', - manageMaintenance: 'Управление обслуживанием', - manageMaintenanceDescription: 'Регистрация выполненного обслуживания, сброс счётчиков, изменение интервалов и управление каталогом видов обслуживания. Подходит для автоматизаций Home Assistant, которые отмечают, например, «сопло очищено», без предоставления более широких прав управления принтером.', - manageArchives: 'Управление архивом', - manageArchivesDescription: 'Изменение и удаление записей архива печати, включая старые задания. Не включает удаление их вклада в статистику. Подходит для автоматизаций очистки истории печати.', - manageProjects: 'Управление проектами', - manageProjectsDescription: 'Создание, изменение и удаление проектов, а также добавление в них архивных заданий. Подходит для автоматизаций, распределяющих печати по проектам.', - libraryBadge: 'Библиотека', - inventoryBadge: 'Запасы', - maintenanceBadge: 'Обслуживание', - archivesBadge: 'Архив', - projectsBadge: 'Проекты', - cloudAccess: 'Разрешить доступ к облаку', - cloudAccessDescription: 'Чтение пресетов и филаментов Bambu Cloud от вашего имени. Требуется вход в Bambu Cloud.', - cloudBadge: 'Облако', - updateEnergyCost: 'Обновление тарифа на электроэнергию', - updateEnergyCostDescription: 'Разрешить этому ключу отправлять новую цену за кВт⋅ч методом POST в /settings/electricity-price. Полезно для автоматизаций динамического тарифа Home Assistant (Tibber, Octopus и т. п.). Это единственный параметр настроек, доступный для записи через ключ API.', - energyCostBadge: 'Энергия', - legacyKey: 'Устаревший', - legacyKeyTooltip: 'Создан до появления привязки к пользователям; пересоздайте для доступа к облаку', - unnamedKey: 'Ключ без названия', - lastUsed: 'Последнее использование', - read: 'Чтение', - control: 'Управление', - createFirstKey: 'Создать первый ключ', - webhookEndpoints: 'Конечные точки вебхуков', - webhookApiKeyHint: 'Передавайте ключ API в заголовке X-API-Key.', - apiBrowser: 'Обозреватель API', - apiBrowserDescription: 'Просмотр и проверка всех доступных конечных точек API.', - apiKeyForTesting: 'Ключ API для проверки', - apiKeyPlaceholder: 'Вставьте ключ API для проверки защищённых конечных точек…', - apiKeyHint: 'Ключ будет передан в запросе в заголовке X-API-Key.', - deleteApiKeyTitle: 'Удаление ключа API', - deleteApiKeyMessage: 'Удалить этот ключ API? Все использующие его интеграции перестанут работать.', - deleteKey: 'Удалить ключ', - amsDisplayThresholds: 'Пороговые значения AMS', - amsThresholdsDescription: 'Настройте цветовые пороги индикаторов влажности и температуры AMS.', - humidity: 'Влажность', - goodGreen: 'Норма (зелёный)', - fairOrange: 'Допустимо (оранжевый)', - aboveFairBad: 'Выше допустимого порога отображается красным', - fairAlsoDryingThreshold: 'Этот порог также запускает автоматическую сушку, если она включена', - temperature: 'Температура', - goodBlue: 'Норма (синий)', - aboveFairHot: 'Выше допустимого порога отображается красным как перегрев', - historyRetention: 'Хранение истории', - keepSensorHistory: 'Хранить историю датчиков', - historyRetentionDescription: 'Более старые данные влажности и температуры будут удаляться автоматически', - defaultPrintOptions: 'Параметры печати по умолчанию', - defaultPrintOptionsDescription: 'Задайте значения по умолчанию для новых заданий. Их можно изменить отдельно в диалоге запуска печати.', - defaultBedLevelling: 'Калибровка стола', - defaultBedLevellingDesc: 'Автоматически выровнять стол перед печатью', - defaultFlowCali: 'Калибровка потока', - defaultFlowCaliDesc: 'Калибровать поток экструзии', - defaultVibrationCali: 'Калибровка вибраций', - defaultVibrationCaliDesc: 'Уменьшить артефакты звона', - defaultLayerInspect: 'Проверка первого слоя', - defaultLayerInspectDesc: 'Проверка первого слоя системой компьютерного зрения', - defaultTimelapse: 'Таймлапс', - defaultTimelapseDesc: 'Записывать таймлапс', - defaultNozzleOffsetCali: 'Калибровка смещения сопел', - defaultNozzleOffsetCaliDesc: 'Калибровать взаимное смещение сопел экструдеров', - tempFanPresetsTitle: 'Пресеты температуры и вентиляторов', - tempFanPresetsDescription: 'Настройте значения быстрого выбора в меню температуры и скорости вентиляторов карточки принтера. Кнопка «Выкл.» отображается всегда.', - tempFanPresetsNozzle: 'Температура сопла', - tempFanPresetsBed: 'Температура стола', - tempFanPresetsChamber: 'Температура камеры', - tempFanPresetsFan: 'Скорость вентилятора', - tempFanPresetsReset: 'Восстановить значения по умолчанию', - staggeredStart: 'Поэтапный запуск', - staggeredStartDescription: 'Размер группы и интервал по умолчанию при поэтапном запуске партии на нескольких принтерах. Для каждой партии значения можно изменить в окне запуска печати.', - preheatTitle: 'Преднагрев и выдержка', - preheatDescription: 'Нагреть стол и, если поддерживается, камеру, затем выдержать температуру перед запуском каждого задания очереди. Полезно для инженерных пластиков (PA, ABS) на принтерах без активного нагревателя камеры: во время выдержки стол прогревает камеру излучением. Целевая температура стола берётся из файла печати; поведение камеры зависит от модели принтера.', - preheatEnabled: 'Включить преднагрев и выдержку', - preheatEnabledDesc: 'Если выключено, задания очереди отправляются сразу. Для каждого задания можно задать отдельное значение.', - preheatFilamentTargetsLabel: 'Целевая температура камеры по типу филамента (°C)', - preheatFilamentTargetsHint: 'Bambuddy выбирает наибольшее значение среди задействованных слотов AMS; для печати только PLA автоматически используется 0 и этап прогрева камеры пропускается.', - preheatFilamentTargetsReset: 'Восстановить значения по умолчанию', - preheatFilamentTargetsDefaultRow: 'Прочее / без сопоставления', - preheatMaxWait: 'Максимальное ожидание (с)', - preheatMaxWaitHelp: 'Предельное время прогрева камеры, после которого процесс продолжится.', - preheatSoak: 'Выдержка (с)', - preheatSoakHelp: 'Время выдержки после достижения цели или истечения максимального ожидания.', - preheatHardwareTitle: 'Поведение по моделям принтеров:', - preheatHardwareDetail: 'H2C/H2D/H2D Pro/H2S/X2D/X1E активно нагревают камеру командой M141. X1C/P2S измеряют температуру камеры, но прогревают её только теплом стола. У P1S/P1P/A1/A1 Mini нет датчика камеры — применяется только таймер выдержки.', - preheatPerItemDesc: 'Нагревать стол и камеру перед запуском этого задания. По умолчанию используется общий переключатель «Настройки → Рабочий процесс».', - preheatOverride_inherit: 'Наследовать', - preheatOverride_on: 'Включено', - preheatOverride_off: 'Выключено', - preheatTargetOverride: 'Переопределение температуры камеры (°C; пусто — значение для филамента)', - plateClear: 'Подтверждение очистки пластины', - requirePlateClear: 'Требовать подтверждение очистки пластины', - requirePlateClearDescription: 'Если включено, планировщик не запускает следующее задание на принтере с завершённой печатью, пока не подтверждена очистка пластины. Отключение также скрывает индикатор состояния пластины и кнопку «Пластина очищена» на карточках принтеров.', - gcodeInjection: 'Вставка G-кода', - gcodeInjectionDescription: 'Настройте G-код, добавляемый в начало и/или конец печати для систем автоматического запуска, таких как Farmloop, SwapMod, AutoClear и Printflow 3D. Фрагменты задаются отдельно для каждой модели принтера и применяются, когда для задания очереди включена вставка G-кода.', - gcodeInjectionNoPrinters: 'Принтеры не найдены. Добавьте принтеры, чтобы настроить фрагменты G-кода.', - gcodeStartLabel: 'Начальный G-код', - gcodeEndLabel: 'Завершающий G-код', - gcodeStartPlaceholder: 'G-код, добавляемый перед началом печати…', - gcodeEndPlaceholder: 'G-код, добавляемый после завершения печати…', - staggerGroupSize: 'Размер группы', - staggerGroupSizeHelp: 'Количество принтеров, запускаемых одновременно', - staggerInterval: 'Интервал (мин)', - staggerIntervalHelp: 'Задержка между запуском групп', - queueDrying: 'Автоматическая сушка в очереди', - queueDryingDescription: 'Автоматически сушить филамент в AMS, когда принтер простаивает между заданиями очереди. Запуск определяется указанным выше порогом влажности.', - queueDryingEnabled: 'Включить автоматическую сушку', - queueDryingEnabledDescription: 'Автоматически запускать сушку AMS, когда принтер простаивает и влажность выше порога', - queueDryingBlock: 'Ожидать завершения сушки', - queueDryingBlockDescription: 'Блокировать очередь печати до завершения сушки. Если выключено, печать имеет приоритет.', - ambientDryingEnabled: 'Фоновая сушка', - ambientDryingEnabledDescription: 'Автоматически сушить филамент на простаивающих принтерах при превышении порога влажности, даже если очередь пуста.', - printDryingEnabled: 'Продолжать сушку во время печати', - printDryingEnabledDescription: 'Разрешить автоматической сушке продолжаться во время печати на поддерживаемом оборудовании (H2D, H2C, H2S, P2S, H2D Pro, X2D, X1C, A2L с актуальной прошивкой). Для защиты катушек температура автоматически ограничивается на 5 °C ниже пресета сушки в простое.', - dryingPresets: 'Пресеты сушки', - dryingPresetsDescription: 'Температура и длительность для каждого типа филамента. AMS 2 Pro использует более низкие температуры, AMS-HT поддерживает более высокие.', - dryingFilament: 'Филамент', - humidityThresholds: 'Пороги влажности', - humidityThresholdsDescription: 'Порог запуска автоматической сушки и предупреждений для каждого типа филамента. При смешанной загрузке AMS используется наименьший порог.', - humidityThresholdCol: 'Порог', - humidityThresholdDefault: 'По умолчанию (неизвестные типы)', - printModal: 'Окно запуска печати', - expandCustomMapping: 'Разворачивать пользовательское сопоставление', - expandCustomMappingDescription: 'При печати на нескольких принтерах сразу показывать развёрнутое сопоставление AMS для каждого принтера', - authentication: 'Аутентификация', - authEnabledDescription: 'Доступ к экземпляру защищён аутентификацией пользователей', - authDisabledDescription: 'Включите, чтобы требовать вход и управлять доступом пользователей', - authDisabledMessage: 'Включите аутентификацию, чтобы создавать учётные записи, управлять правами и защитить экземпляр Bambuddy.', - enableAuthentication: 'Включить аутентификацию', - currentUser: 'Текущий пользователь', - changePassword: 'Изменить пароль', - admin: 'Администратор', - users: 'Пользователи', - addUser: 'Добавить пользователя', - groups: 'Группы', - addGroup: 'Добавить группу', - system: 'Система', - noDescription: 'Нет описания', - userCount: 'Пользователей: {{count}}', - permissionCount: 'Разрешений: {{count}}', - createUser: 'Создать пользователя', - username: 'Имя пользователя', - enterUsername: 'Введите имя пользователя', - password: 'Пароль', - enterPassword: 'Введите пароль', - passwordRequirements: 'Не менее 8 символов: одна заглавная и одна строчная буква, одна цифра и один специальный символ.', - confirmPassword: 'Подтвердите пароль', - confirmPasswordPlaceholder: 'Повторите пароль', - viewReleaseOnGitHub: 'Открыть выпуск на GitHub', - turnAllPlugsOn: 'Включить все розетки', - turnAllPlugsOff: 'Выключить все розетки', - clearNotificationLogs: 'Очистить журнал уведомлений', - clearLogsMessage: 'Все записи журнала уведомлений старше 30 дней будут безвозвратно удалены. Это действие нельзя отменить.', - clearLogs: 'Очистить журнал', - resetUiPreferences: 'Сбросить настройки интерфейса', - resetUiPreferencesMessage: 'Будут сброшены порядок боковой панели, тема, компоновка панели, режимы отображения и сортировка. Принтеры, архивы и серверные настройки НЕ изменятся. После очистки страница перезагрузится.', - resetPreferences: 'Сбросить настройки', - deleteGroupTitle: 'Удаление группы', - deleteGroupMessage: 'Удалить эту группу? Пользователи группы потеряют назначенные ей разрешения.', - deleteGroup: 'Удалить группу', - disableAuthenticationTitle: 'Отключение аутентификации', - disableAuthenticationMessage: 'Отключить аутентификацию? Bambuddy станет доступен без входа. Все пользователи останутся в базе данных, но проверка учётных данных выполняться не будет.', - disableAuthentication: 'Отключить аутентификацию', - configureBambuddy: 'Настроить Bambuddy', - systemDefault: 'Системное значение', - archiveSettings: 'Настройки архива', - newWindow: 'Новое окно', - embeddedOverlay: 'Встроенное наложение', - preferredSlicer: 'Предпочитаемый слайсер', - preferredSlicerDescription: 'Слайсер для нарезки внутри приложения через API-службу', - openInSlicerLabel: 'Открывать в слайсере', - openInSlicerInherit: 'Как для API-слайсера', - openInSlicerDescription: 'Настольный слайсер, который открывается кнопкой «Открыть в слайсере». Оставьте «Как для API-слайсера», чтобы наследовать настройку, либо выберите другой локальный слайсер.', - orcaslicerKnownIssuesWarning: 'В OrcaSlicer 2.3.2 и 2.4.0-dev известны ошибки CLI, мешающие нарезке многих 3MF от Bambu: #12426 (сбой на раскрашенных файлах с несколькими экструдерами) и #13386 (слишком строгая проверка диапазонов параметров). До исправления рекомендуется Bambu Studio.', - useSlicerApi: 'Использовать API слайсера', - useSlicerApiDescription: 'Если включено, действия «Нарезать» открывают встроенное окно и обращаются к API-службе слайсера. Если выключено, файл передаётся настольному слайсеру через URI-схему.', - slicerCard: 'Слайсер', - orcaslicerApiUrl: 'URL API-службы OrcaSlicer', - bambuStudioApiUrl: 'URL API-службы Bambu Studio', - slicerApiUrlDescription: 'URL контейнера API-службы слайсера. Оставьте пустым, чтобы использовать значения переменных окружения SLICER_API_URL или BAMBU_STUDIO_API_URL.', - externalCameras: 'Внешние камеры', - costTracking: 'Учёт затрат', - printsOnly: 'Только печать', - totalConsumption: 'Общее потребление', - dataManagement: 'Управление данными', - storageUsage: 'Использование хранилища', - storageUsageDescription: 'Распределение занятого места по категориям', - storageUsageTotal: 'Всего', - storageUsageErrors: 'Ошибки', - storageUsageOtherBreakdown: 'Прочее (статические ресурсы, скрипты и конфигурационные файлы)', - storageUsageSystem: 'Система', - storageUsageData: 'Данные', - storageUsageUnavailable: 'Сведения об использовании хранилища недоступны', - clearNotificationLogsDescription: 'Удалить записи журнала уведомлений старше 30 дней', - resetUiPreferencesDescription: 'Сбросить порядок боковой панели, тему, режимы отображения и компоновку. Принтеры, архивы и настройки не изменятся.', - enableHomeAssistant: 'Включить Home Assistant', - enableMqtt: 'Включить MQTT', - useTls: 'Использовать TLS', - enableMetricsEndpoint: 'Включить endpoint метрик', - availableMetrics: 'Доступные метрики', - editUser: 'Изменить пользователя', - deleteUserTitle: 'Удаление пользователя', - groupName: 'Название группы', - leaveEmptyForAnonymous: 'Оставьте пустым для анонимного доступа', - leaveEmptyForNoAuth: 'Оставьте пустым, чтобы не использовать аутентификацию', - enterNewPassword: 'Введите новый пароль', - confirmNewPassword: 'Повторите новый пароль', - enterGroupName: 'Введите название группы', - enterDescriptionOptional: 'Введите описание (необязательно)', - enterCurrentPassword: 'Введите текущий пароль', - enterNewPasswordMin6: 'Введите новый пароль (не менее 6 символов)', - toast: { - keyCopied: 'Ключ скопирован в буфер обмена', - copyFailed: 'Не удалось скопировать ключ', - keyAddedToBrowser: 'Ключ добавлен в обозреватель API', - clearLogsFailed: 'Не удалось очистить журнал', - uiPreferencesReset: 'Настройки интерфейса сброшены. Обновление страницы…', - authDisabled: 'Аутентификация отключена', - authDisableFailed: 'Не удалось отключить аутентификацию', - apiKeyCreated: 'Ключ API создан', - apiKeyDeleted: 'Ключ API удалён', - userCreated: 'Пользователь создан', - userUpdated: 'Пользователь обновлён', - userDeleted: 'Пользователь удалён', - groupCreated: 'Группа создана', - groupUpdated: 'Группа обновлена', - groupDeleted: 'Группа удалена', - fillRequiredFields: 'Заполните все обязательные поля', - passwordsDoNotMatch: 'Пароли не совпадают', - passwordTooShort: 'Пароль должен содержать не менее 8 символов', - passwordNeedsUppercase: 'Пароль должен содержать хотя бы одну заглавную букву', - passwordNeedsLowercase: 'Пароль должен содержать хотя бы одну строчную букву', - passwordNeedsDigit: 'Пароль должен содержать хотя бы одну цифру', - passwordNeedsSpecial: 'Пароль должен содержать хотя бы один специальный символ', - enterGroupName: 'Введите название группы', - settingsSaved: 'Настройки сохранены', - noPermissionUpdate: 'У вас нет прав на изменение настроек', - cameraSettingsSaved: 'Настройки камеры сохранены', - enterCameraUrl: 'Введите URL камеры', - passwordChanged: 'Пароль изменён', - connectionFailed: 'Не удалось подключиться', - testFailed: 'Проверка завершилась ошибкой', - cameraConnected: 'Камера подключена{{resolution}}', - }, - testConnection: 'Проверить подключение', - catalog: { - spoolCatalog: 'Каталог катушек', - spoolCatalogDescription: 'Масса пустых катушек по производителю и типу. Используется для автоматического определения массы при добавлении катушек.', - searchCatalog: 'Поиск по каталогу…', - addNewEntry: 'Добавить запись', - namePlaceholder: 'Название (например, Bambu Lab — пластик)', - weight: 'Масса', - type: 'Тип', - default: 'Стандартная', - custom: 'Пользовательская', - noMatch: 'Ничего не найдено', - empty: 'Каталог пуст', - deleteEntry: 'Удалить запись', - deleteConfirm: 'Удалить «{{name}}»?', - resetCatalog: 'Сбросить каталог', - resetConfirm: 'Сбросить каталог к исходному состоянию? Все пользовательские записи будут удалены.', - loadFailed: 'Не удалось загрузить каталог катушек', - nameWeightRequired: 'Укажите название и массу', - entryAdded: 'Запись добавлена', - addFailed: 'Не удалось добавить запись', - entryUpdated: 'Запись обновлена', - updateFailed: 'Не удалось обновить запись', - entryDeleted: 'Запись удалена', - deleteFailed: 'Не удалось удалить запись', - resetSuccess: 'Каталог сброшен к исходному состоянию', - resetFailed: 'Не удалось сбросить каталог', - exported: 'Экспортировано записей: {{count}}', - imported: 'Импортировано записей: {{added}}, пропущено: {{skipped}}', - importFailed: 'Не удалось импортировать: неверный формат JSON', - exportTooltip: 'Экспортировать каталог в JSON', - importTooltip: 'Импортировать каталог из JSON', - resetTooltip: 'Сбросить к исходному состоянию', - selectedCount: 'Выбрано: {{count}}', - deleteSelected: 'Удалить выбранные', - bulkDeleteConfirm: 'Удалить выбранные записи ({{count}})?', - bulkDeleted: 'Удалено записей: {{count}}', - bulkDeleteFailed: 'Не удалось удалить записи', - material: 'Материал', - spoolWeight: 'Масса катушки', - color: 'Цвет', - updateSpoolWeight: 'Обновить массу катушки', - filamentUpdated: 'Филамент обновлён', - filamentUpdateFailed: 'Не удалось обновить филамент', - filamentUpdateInvalid: 'Некорректные данные филамента', - keepExistingSpoolWeight: 'Сохранить прежнюю массу у существующих катушек', - keepExistingSpoolWeightDesc: 'Катушки, уже созданные с этим типом филамента, сохранят прежнюю массу тары. Для новых катушек будет использоваться обновлённое значение.', - applyToAllSpools: 'Применить ко всем катушкам', - applyToAllSpoolsDesc: 'Все расчёты массы для катушек этого типа немедленно начнут использовать новую массу тары.', - }, - colorCatalog: { - title: 'Каталог цветов', - description: 'Цвета филамента по производителю и материалу. Используются для автоматического определения цвета при добавлении катушек.', - searchColors: 'Поиск цветов…', - allManufacturers: 'Все производители', - addNewColor: 'Добавить цвет', - manufacturer: 'Производитель', - colorName: 'Название цвета', - hex: 'HEX', - materialOptional: 'Материал (необязательно)', - showing: 'Показано {{filtered}} из {{total}} цветов', - noMatch: 'Подходящие цвета не найдены', - empty: 'Каталог цветов пуст', - deleteColor: 'Удалить цвет', - deleteConfirm: 'Удалить «{{name}}»?', - resetCatalog: 'Сбросить каталог цветов', - resetConfirm: 'Сбросить каталог к исходному состоянию? Все пользовательские цвета будут удалены.', - sync: 'Синхронизировать', - starting: 'Запуск…', - syncTooltip: 'Синхронизировать с FilamentColors.xyz (более 2000 цветов; может занять около минуты)', - loadFailed: 'Не удалось загрузить каталог цветов', - fieldsRequired: 'Укажите производителя, название цвета и HEX-код', - colorAdded: 'Цвет добавлен', - addFailed: 'Не удалось добавить цвет', - colorUpdated: 'Цвет обновлён', - updateFailed: 'Не удалось обновить цвет', - colorDeleted: 'Цвет удалён', - deleteFailed: 'Не удалось удалить цвет', - resetSuccess: 'Каталог цветов сброшен к исходному состоянию', - resetFailed: 'Не удалось сбросить каталог цветов', - syncUpToDate: 'Каталог уже актуален (проверено цветов: {{count}})', - syncComplete: 'Добавлено новых цветов: {{added}}, уже существовало: {{skipped}}', - syncError: 'Ошибка синхронизации', - syncFailed: 'Не удалось синхронизировать каталог с FilamentColors.xyz', - exported: 'Экспортировано цветов: {{count}}', - imported: 'Импортировано цветов: {{added}}, пропущено: {{skipped}}', - importFailed: 'Не удалось импортировать: неверный формат JSON', - selectedCount: 'Выбрано: {{count}}', - deleteSelected: 'Удалить выбранные', - bulkDeleteConfirm: 'Удалить выбранные цвета ({{count}})?', - bulkDeleted: 'Удалено цветов: {{count}}', - bulkDeleteFailed: 'Не удалось удалить цвета', - }, - dateFormat: 'Формат даты', - dateFormatUs: 'США (ММ/ДД/ГГГГ)', - dateFormatEu: 'Европа (ДД/ММ/ГГГГ)', - dateFormatIso: 'ISO (ГГГГ-ММ-ДД)', - timeFormat: 'Формат времени', - timeFormat12: '12-часовой (3:30 PM)', - timeFormat24: '24-часовой (15:30)', - defaultPrinter: 'Принтер по умолчанию', - defaultPrinterDescription: 'Предварительно выбирать этот принтер при загрузке файлов, повторной печати и других операциях.', - slicerBambuStudio: 'Bambu Studio', - slicerOrcaSlicer: 'OrcaSlicer', - sidebarOrderDescription: 'Изменяйте порядок, видимость и пользовательские ссылки в настройках боковой панели.', - setDefault: 'Сделать стандартным', - sidebarOrderSetDefaultHint: 'Текущий порядок меню будет применяться к пользователям, которые ещё не настроили его самостоятельно.', - sidebarDefaultSet: 'Стандартный порядок меню сохранён.', - sidebarDefaultCleared: 'Стандартный порядок меню сброшен.', - sidebarDefaultFailed: 'Не удалось сохранить стандартный порядок меню.', - reset: 'Сбросить', - darkMode: 'Тёмная тема', - lightMode: 'Светлая тема', - active: '(активна)', - background: 'Фон', - accent: 'Акцент', - style: 'Стиль', - bgNeutral: 'Нейтральный', - bgWarm: 'Тёплый', - bgCool: 'Холодный', - bgOled: 'Чёрный OLED', - bgSlate: 'Сланцево-синий', - bgForest: 'Лесной зелёный', - accentGreen: 'Зелёный', - accentTeal: 'Бирюзовый', - accentBlue: 'Синий', - accentOrange: 'Оранжевый', - accentPurple: 'Фиолетовый', - accentRed: 'Красный', - styleClassic: 'Классический', - styleGlow: 'Свечение', - styleVibrant: 'Насыщенный', - themeToggleHint: 'Переключайте тёмную, светлую и системную тему значком в боковой панели.', - autoArchivePrints: 'Автоматически архивировать печать', - autoArchiveDescription: 'Автоматически сохранять 3MF после завершения печати', - saveThumbnailsDescription: 'Извлекать и сохранять изображения предпросмотра из 3MF', - captureFinishPhotoDescription: 'Сделать снимок камерой принтера после завершения печати. Во время печати Bambuddy записывает короткий таймлапс, чтобы получить кадр до опускания стола. Если таймлапс был включён для задания, файл сохранится; иначе после получения снимка он будет автоматически удалён.', - ffmpegNotInstalled: 'ffmpeg не установлен', - ffmpegRequired: 'Для захвата изображения требуется ffmpeg. Установите его командой brew install ffmpeg в macOS или apt install ffmpeg в Linux.', - camera: 'Камера', - cameraViewMode: 'Режим просмотра камеры', - cameraOverlayDescription: 'Камера открывается поверх основного экрана в окне с изменяемым размером', - cameraWindowDescription: 'Камера открывается в отдельном окне браузера', - externalCamerasDescription: 'Настройка внешних камер вместо встроенной камеры принтера. Поддерживаются потоки MJPEG, RTSP, HTTP-снимки и USB-камеры V4L2. Внешняя камера используется для просмотра в реальном времени и снимков после завершения печати.', - cameraPlaceholderUsb: 'Путь к устройству (/dev/video0)', - cameraPlaceholderUrl: 'URL камеры (rtsp://… или http://…)', - cameraTypeMjpeg: 'Поток MJPEG', - cameraTypeRtsp: 'Поток RTSP', - cameraTypeSnapshot: 'HTTP-снимок', - cameraTypeUsb: 'USB-камера (V4L2)', - cameraSnapshotUrl: 'URL снимка (необязательно)', - cameraSnapshotUrlPlaceholder: 'http://192.168.1.61:1984/api/frame.jpeg?src=printer', - cameraSnapshotUrlHelp: 'URL одиночного кадра для миниатюр уведомлений, снимков после печати, кадров послойного таймлапса и распознавания пластины. Для таймлапса и распознавания пластины должны быть отдельно включены соответствующие настройки принтера; этот URL задаёт лишь источник изображения. Оставьте пустым, чтобы получать кадры из указанного выше видеопотока. Полезно для go2rtc (/api/frame.jpeg) и IP-камер с отдельным endpoint снимка.', - cameraRotation: 'Поворот', - test: 'Проверить', - connected: 'Подключено', - disconnected: 'Отключено', - currency: 'Валюта', - defaultFilamentCost: 'Стоимость филамента по умолчанию (за кг)', - electricityCost: 'Стоимость электроэнергии за кВт·ч', - energyDisplayMode: 'Режим отображения энергии', - energyModePrintDescription: 'На панели показывается сумма энергии, израсходованной во время печати', - energyModeTotalDescription: 'На панели показывается полный расход энергии по данным умных розеток', - fileManager: 'Файловый менеджер', - createArchiveEntry: 'Создавать запись в архиве при печати', - createArchiveEntryDescription: 'При запуске печати из файлового менеджера при необходимости создавать запись в архиве', - lowDiskSpaceWarning: 'Предупреждение о нехватке места', - lowDiskSpaceDescription: 'Показывать предупреждение, когда свободного места остаётся меньше указанного порога', - printerFirmware: 'Прошивка принтера', - checkFirmwareDescription: 'Проверять наличие обновлений прошивки принтера у Bambu Lab', - bambuddySoftware: 'Программное обеспечение Bambuddy', - autoCheckDescription: 'Автоматически проверять наличие новых версий при запуске', - checkNow: 'Проверить сейчас', - updateAvailableVersion: 'Доступно обновление: v{{version}}', - releaseNotes: 'Примечания к выпуску', - updateViaDocker: 'Обновление через Docker Compose:', - updateViaHomeAssistant: 'Обновления устанавливаются через Home Assistant Supervisor. Чтобы установить новую версию, откройте в Home Assistant: Настройки → Дополнения → Bambuddy.', - updateViaWindowsInstaller: 'В Windows обновление выполняется повторным запуском установщика. Скачайте новую версию ниже — данные, настройки и принтеры сохранятся.', - downloadWindowsInstaller: 'Скачать установщик v{{version}}', - installUpdate: 'Установить обновление', - latestVersionRunning: 'Установлена последняя версия', - failedToCheckUpdates: 'Не удалось проверить обновления: {{error}}', - backupRestore: 'Резервное копирование и восстановление', - backupRestoreDescription: 'Экспорт и импорт настроек, а также резервное копирование в GitHub', - goToBackup: 'Перейти к резервным копиям', - externalUrl: 'Внешний URL', - externalUrlDescription: 'Внешний адрес, по которому доступен Bambuddy. Используется для изображений в уведомлениях и внешних интеграций.', - bambuddyUrl: 'URL Bambuddy', - externalUrlHint: 'Укажите протокол и порт, например http://192.168.1.100:8000', - ftpRetry: 'Повтор FTP-операций', - ftpRetryDescription: 'Повторять FTP-операции при нестабильном Wi‑Fi принтера. Применяется к скачиванию 3MF и таймлапсов, загрузке заданий и обновлению прошивки.', - autoRetryDescription: 'Автоматически повторять неудачные FTP-операции', - retryAttempts: 'Количество попыток', - retryDelay: 'Задержка между попытками', - connectionTimeout: 'Тайм-аут подключения', - time_one: '{{count}} раз', - time_other: '{{count}} раз', - second_one: '{{count}} секунда', - second_other: '{{count}} секунд', - nSeconds: '{{count}} секунд', - increaseForWeakWifi: 'Увеличьте для принтеров со слабым Wi‑Fi', - homeAssistant: 'Home Assistant', - homeAssistantFullDescription: 'Подключение к Home Assistant для управления умными розетками через REST API. Поддерживаются сущности switch, light, input_boolean и script.', - homeAssistantUrl: 'URL Home Assistant', - longLivedAccessToken: 'Долгосрочный токен доступа', - haTokenHint: 'Создайте токен в Home Assistant: Профиль → Долгосрочные токены доступа → Создать токен', - connectionSuccessful: 'Подключение установлено', - connectionFailed: 'Подключиться не удалось', - haConnectionSuccess: 'Подключение к Home Assistant установлено.', - haConnectionFailed: 'Не удалось подключиться к Home Assistant.', - mqttPublishing: 'Публикация MQTT', - mqttDescription: 'Публикация событий Bambuddy во внешнем MQTT-брокере для интеграции с Node-RED, Home Assistant и другими системами автоматизации.', - mqttEnableDescription: 'Публиковать события во внешнем MQTT-брокере', - brokerHostname: 'Имя хоста брокера', - port: 'Порт', - usernameOptional: 'Имя пользователя (необязательно)', - passwordOptional: 'Пароль (необязательно)', - topicPrefix: 'Префикс топиков', - topicPrefixHint: 'Топики будут иметь вид {{prefix}}/printers//status и т. п.', - prometheusMetrics: 'Метрики Prometheus', - prometheusEndpointDescription: 'Публиковать метрики принтеров по адресу /api/v1/metrics для мониторинга в Prometheus/Grafana.', - bearerTokenOptional: 'Bearer-токен (необязательно)', - bearerTokenHint: 'Если задан, запросы должны содержать Authorization: Bearer ', - metricsConnectionStatus: 'Состояние подключения', - metricsPrinterState: 'Состояние принтера (ожидание, печать и т. п.)', - metricsPrintProgress: 'Прогресс печати, 0–100 %', - metricsBedTemp: 'Температура стола', - metricsNozzleTemp: 'Температура сопла', - metricsPrintsTotal: 'Общее количество заданий по результату', - metricsMore: '…и другие данные: слои, вентиляторы, очередь, расход филамента', - smartPlugsDescription: 'Подключение умных розеток Tasmota или Home Assistant для автоматического управления питанием принтеров и учёта энергопотребления.', - allOn: 'Включить все', - allOff: 'Выключить все', - addSmartPlug: 'Добавить умную розетку', - energySummary: 'Сводка энергопотребления', - currentPower: 'Текущая мощность', - plugsOnline: 'Розеток в сети: {{reachable}}/{{total}}', - today: 'Сегодня', - yesterday: 'Вчера', - total: 'Всего', - enablePlugsForSummary: 'Включите розетки, чтобы увидеть сводку энергопотребления', - addNotificationProvider: 'Добавить', - systemBadge: '(системная)', - creating: 'Создание…', - changing: 'Изменение…', - deleteUserAndItems: 'Удалить пользователя И его объекты', - deleteUserKeepItems: 'Удалить пользователя, объекты оставить без владельца', - ok: 'ОК', }, notification: { printStarted: { - title: 'Печать началась', - body: '{{printer}}: началась печать файла {{filename}}', + title: "Печать началась", + body: "{{printer}}: началась печать файла {{filename}}", }, printCompleted: { - title: 'Печать завершена', - body: '{{printer}}: файл {{filename}} успешно напечатан', + title: "Печать завершена", + body: "{{printer}}: файл {{filename}} успешно напечатан", }, printFailed: { - title: 'Ошибка печати', - body: '{{printer}}: печать файла {{filename}} завершилась ошибкой', + title: "Ошибка печати", + body: "{{printer}}: печать файла {{filename}} завершилась ошибкой", }, printStopped: { - title: 'Печать остановлена', - body: '{{printer}}: печать файла {{filename}} остановлена', + title: "Печать остановлена", + body: "{{printer}}: печать файла {{filename}} остановлена", }, printProgress: { - title: 'Ход печати', - body: '{{printer}}: файл {{filename}} напечатан на {{percent}}%', + title: "Ход печати", + body: "{{printer}}: файл {{filename}} напечатан на {{percent}}%", }, printerOffline: { - title: 'Принтер не в сети', - body: '{{printer}} не в сети', + title: "Принтер не в сети", + body: "{{printer}} не в сети", }, printerError: { - title: 'Ошибка принтера', - body: '{{printer}}: {{error}}', + title: "Ошибка принтера", + body: "{{printer}}: {{error}}", }, filamentLow: { - title: 'Заканчивается филамент', - body: '{{printer}}: осталось мало филамента', + title: "Заканчивается филамент", + body: "{{printer}}: осталось мало филамента", }, maintenanceDue: { - title: 'Требуется обслуживание', - body: '{{printer}}: требуют внимания: {{items}}', + title: "Требуется обслуживание", + body: "{{printer}}: требуют внимания: {{items}}", }, }, errors: { - generic: 'Что-то пошло не так', - networkError: 'Ошибка сети. Проверьте подключение.', - notFound: 'Не найдено', - unauthorized: 'Доступ не разрешён', - serverError: 'Ошибка сервера', - validationError: 'Проверьте введённые данные', - printerConnectionFailed: 'Не удалось подключиться к принтеру', - saveFailed: 'Не удалось сохранить изменения', - deleteFailed: 'Не удалось удалить', - loadFailed: 'Не удалось загрузить данные', + generic: "Что-то пошло не так", + networkError: "Ошибка сети. Проверьте подключение.", + notFound: "Не найдено", + unauthorized: "Доступ не разрешён", + serverError: "Ошибка сервера", + validationError: "Проверьте введённые данные", + printerConnectionFailed: "Не удалось подключиться к принтеру", + saveFailed: "Не удалось сохранить изменения", + deleteFailed: "Не удалось удалить", + loadFailed: "Не удалось загрузить данные", }, hmsErrors: { - title: 'Ошибки — {{name}}', - noErrors: 'Ошибок нет', - viewOnWiki: 'Открыть в Bambu Lab Wiki', - unknownCode: 'Неизвестный код HMS — подробности см. в Bambu Lab Wiki.', - clearInstructions: 'Устраните ошибки на принтере, чтобы они исчезли из этого списка.', - clearErrors: 'Очистить ошибки', - clearSuccess: 'Ошибки HMS очищены', - clearFailed: 'Не удалось очистить ошибки HMS', - actionSuccess: 'Команда отправлена принтеру', - actionFailed: 'Не удалось отправить команду', + title: "Ошибки — {{name}}", + noErrors: "Ошибок нет", + viewOnWiki: "Открыть в Bambu Lab Wiki", + unknownCode: "Неизвестный код HMS — подробности см. в Bambu Lab Wiki.", + clearInstructions: "Устраните ошибки на принтере, чтобы они исчезли из этого списка.", + clearErrors: "Очистить ошибки", + clearSuccess: "Ошибки HMS очищены", + clearFailed: "Не удалось очистить ошибки HMS", + actionSuccess: "Команда отправлена принтеру", + actionFailed: "Не удалось отправить команду", + runoutExpectedSlot: "Филамент закончился в {{ranOut}}. Теперь принтер ожидает совместимый филамент в {{expected}}. Установите катушку в {{expected}}, затем выберите «Повторить».", + runoutExpectedSlotOnly: "Принтер ожидает совместимый филамент в {{expected}}. Установите туда катушку, затем выберите «Повторить».", + runoutSlotUnknown: "Филамент закончился, и печать приостановлена. Bambuddy не смог определить, какой слот теперь ожидает принтер. Проверьте запрошенный слот на экране принтера.", actions: { - RESUME_PRINTING: 'Продолжить печать', - RESUME_PRINTING_DEFECTS: 'Продолжить (дефекты допустимы)', - RESUME_PRINTING_PROBELM_SOLVED: 'Продолжить (проблема устранена)', - STOP_PRINTING: 'Остановить печать', - CHECK_ASSISTANT: 'Проверить помощник', - FILAMENT_EXTRUDED: 'Филамент выдавливается — продолжить', - RETRY_FILAMENT_EXTRUDED: 'Филамент ещё не выходит — повторить', - CONTINUE: 'Готово — продолжить', - LOAD_VIRTUAL_TRAY: 'Загрузить филамент', - OK_BUTTON: 'ОК', - FILAMENT_LOAD_RESUME: 'Филамент загружен — продолжить', - JUMP_TO_LIVEVIEW: 'Открыть камеру', - NO_REMINDER_NEXT_TIME: 'Больше не напоминать', - REFRESH_NOZZLE: 'Проверить ещё раз', - IGNORE_NO_REMINDER_NEXT_TIME: 'Игнорировать и больше не напоминать', - IGNORE_RESUME: 'Игнорировать и продолжить', - PROBLEM_SOLVED_RESUME: 'Проблема устранена — продолжить', - TURN_OFF_FIRE_ALARM: 'Понятно, отключить пожарную тревогу', - RETRY_PROBLEM_SOLVED: 'Повторить (проблема устранена)', - CANCLE: 'Отмена', - STOP_DRYING: 'Остановить сушку', - PROCEED: 'Продолжить', - OK_JUMP_RACK: 'ОК', - ABORT: 'Прервать', - DISABLE_PURIFICATION: 'Отключить очистку воздуха для этой печати', - DONT_REMIND_NEXT_TIME: 'Больше не напоминать', - DBL_CHECK_CANCEL: 'Отмена', - DBL_CHECK_DONE: 'Готово', - DBL_CHECK_RETRY: 'Повторить', - DBL_CHECK_RESUME: 'Продолжить', - DBL_CHECK_OK: 'Подтвердить', - REMOVE_CLOSE_BTN: 'Закрыть', + RESUME_PRINTING: "Продолжить печать", + RESUME_PRINTING_DEFECTS: "Продолжить (дефекты допустимы)", + RESUME_PRINTING_PROBELM_SOLVED: "Продолжить (проблема устранена)", + STOP_PRINTING: "Остановить печать", + CHECK_ASSISTANT: "Проверить помощник", + FILAMENT_EXTRUDED: "Филамент выдавливается — продолжить", + RETRY_FILAMENT_EXTRUDED: "Филамент ещё не выходит — повторить", + CONTINUE: "Готово — продолжить", + LOAD_VIRTUAL_TRAY: "Загрузить филамент", + OK_BUTTON: "ОК", + FILAMENT_LOAD_RESUME: "Филамент загружен — продолжить", + JUMP_TO_LIVEVIEW: "Открыть камеру", + NO_REMINDER_NEXT_TIME: "Больше не напоминать", + REFRESH_NOZZLE: "Проверить ещё раз", + IGNORE_NO_REMINDER_NEXT_TIME: "Игнорировать и больше не напоминать", + IGNORE_RESUME: "Игнорировать и продолжить", + PROBLEM_SOLVED_RESUME: "Проблема устранена — продолжить", + TURN_OFF_FIRE_ALARM: "Понятно, отключить пожарную тревогу", + RETRY_PROBLEM_SOLVED: "Повторить (проблема устранена)", + CANCLE: "Отмена", + STOP_DRYING: "Остановить сушку", + PROCEED: "Продолжить", + OK_JUMP_RACK: "ОК", + ABORT: "Прервать", + DISABLE_PURIFICATION: "Отключить очистку воздуха для этой печати", + DONT_REMIND_NEXT_TIME: "Больше не напоминать", + DBL_CHECK_CANCEL: "Отмена", + DBL_CHECK_DONE: "Готово", + DBL_CHECK_RETRY: "Повторить", + DBL_CHECK_RESUME: "Продолжить", + DBL_CHECK_OK: "Подтвердить", + REMOVE_CLOSE_BTN: "Закрыть", }, }, mqttDebug: { - title: 'Журнал отладки MQTT', - searchPlaceholder: 'Поиск по теме или содержимому...', - noMessages: 'Сообщений пока нет', - startLoggingHint: 'Нажмите «Начать запись», чтобы перехватывать сообщения MQTT', - noMessagesMatch: 'Нет сообщений, соответствующих фильтру', - adjustFilterHint: 'Измените запрос или параметры фильтра', - incoming: 'Входящие', - outgoing: 'Исходящие', - loggingStopped: 'Запись остановлена', - loggingActive: 'Запись активна — сообщения обновляются автоматически', - startLogging: 'Начать запись', - stopLogging: 'Остановить запись', - clearLog: 'Очистить журнал', - topic: 'Тема', - timestamp: 'Время', - direction: 'Направление', - all: 'Все', + title: "Журнал отладки MQTT", + searchPlaceholder: "Поиск по теме или содержимому...", + noMessages: "Сообщений пока нет", + startLoggingHint: "Нажмите «Начать запись», чтобы перехватывать сообщения MQTT", + noMessagesMatch: "Нет сообщений, соответствующих фильтру", + adjustFilterHint: "Измените запрос или параметры фильтра", + incoming: "Входящие", + outgoing: "Исходящие", + loggingStopped: "Запись остановлена", + loggingActive: "Запись активна — сообщения обновляются автоматически", + startLogging: "Начать запись", + stopLogging: "Остановить запись", + clearLog: "Очистить журнал", + topic: "Тема", + timestamp: "Время", + direction: "Направление", + all: "Все", }, printerFiles: { - title: 'Файлы принтера', - storageUsed: 'Занято:', - storageFree: 'Свободно:', - filterPlaceholder: 'Фильтр файлов...', - deleteButton: 'Удалить', - deleteFiles: 'Удалить файлы: {{count}}', - deleteFileConfirm: 'Удалить «{{name}}»? Это действие нельзя отменить.', - deleteFilesConfirm: 'Удалить выбранные файлы ({{count}})? Это действие нельзя отменить.', - noFiles: 'На принтере нет файлов', - loadingFiles: 'Загрузка файлов...', - failedToLoad: 'Не удалось загрузить файлы', + title: "Файлы принтера", + storageUsed: "Занято:", + storageFree: "Свободно:", + filterPlaceholder: "Фильтр файлов...", + deleteButton: "Удалить", + deleteFiles: "Удалить файлы: {{count}}", + deleteFileConfirm: "Удалить «{{name}}»? Это действие нельзя отменить.", + deleteFilesConfirm: "Удалить выбранные файлы ({{count}})? Это действие нельзя отменить.", + noFiles: "На принтере нет файлов", + loadingFiles: "Загрузка файлов...", + failedToLoad: "Не удалось загрузить файлы", toast: { - filesDeleted: 'Удалено файлов: {{count}}', - deleteFailed: 'Не удалось удалить: {{error}}', + filesDeleted: "Удалено файлов: {{count}}", + deleteFailed: "Не удалось удалить: {{error}}", }, }, confirm: { - delete: 'Удалить этот объект?', - unsavedChanges: 'Есть несохранённые изменения. Всё равно уйти со страницы?', - clearQueue: 'Очистить очередь печати?', + delete: "Удалить этот объект?", + unsavedChanges: "Есть несохранённые изменения. Всё равно уйти со страницы?", + clearQueue: "Очистить очередь печати?", + }, + login: { + title: "Вход в Bambuddy", + subtitle: "Войдите в свою учётную запись", + username: "Имя пользователя", + usernamePlaceholder: "Введите имя пользователя", + usernameOrEmail: "Имя пользователя или email", + usernameOrEmailPlaceholder: "Имя пользователя или email", + password: "Пароль", + passwordPlaceholder: "Введите пароль", + signIn: "Войти", + signingIn: "Вход...", + rememberMe: "Запомнить меня", + forgotPassword: "Забыли пароль?", + autologinFailed: "Автоматический вход через SSO не выполнен. Выберите провайдера ниже.", + localDisabledNotice: "Локальный вход отключён. Используйте одного из SSO-провайдеров ниже.", + loginSuccess: "Вход выполнен", + loginFailed: "Не удалось войти", + enterCredentials: "Введите имя пользователя и пароль", + enterEmail: "Введите адрес электронной почты", + oidcLoginFailed: "Ошибка входа через OIDC", + oidcErrors: { + providerError: "Провайдер удостоверений вернул ошибку", + missingParameters: "В ответе OIDC отсутствуют обязательные параметры", + invalidState: "Состояние OIDC недействительно или уже использовано", + stateExpired: "Сеанс входа OIDC истёк — повторите попытку", + providerNotFound: "Провайдер OIDC не найден", + discoveryFailed: "Не удалось получить документ обнаружения OIDC", + invalidDiscovery: "Документ обнаружения OIDC недействителен", + networkError: "Сетевая ошибка при обмене токена OIDC", + badResponse: "Неожиданный ответ при обмене токена OIDC", + noIdToken: "Провайдер OIDC не вернул ID-токен", + validationFailed: "Не удалось проверить токен OIDC", + nonceMismatch: "Несовпадение nonce OIDC — возможна атака повторного воспроизведения", + missingSubClaim: "В токене OIDC отсутствует поле sub", + noLinkedAccount: "С этой учётной записью OIDC не связана локальная учётная запись", + accountInactive: "Учётная запись неактивна", + userResolutionFailed: "Не удалось определить учётную запись", + internalError: "Внутренняя ошибка при входе через OIDC", + tokenExchangeFailed: "Не удалось обменять токен OIDC", + }, + forgotPasswordTitle: "Восстановление пароля", + forgotPasswordMessage: "Обратитесь к системному администратору для сброса пароля.", + forgotPasswordEmailMessage: "Введите адрес электронной почты, и мы отправим новый пароль.", + emailAddress: "Адрес электронной почты", + emailPlaceholder: "your.email@example.com", + cancel: "Отмена", + sending: "Отправка...", + sendResetEmail: "Отправить письмо для сброса", + howToReset: "Как сбросить пароль:", + resetStep1: "Свяжитесь с администратором Bambuddy", + resetStep2: "Попросите сбросить пароль в разделе управления пользователями", + resetStep3: "Администратор задаст новый временный пароль", + resetStep4: "Войдите с новым паролем и измените его в настройках", + gotIt: "Понятно", + resetPassword: { + title: "Новый пароль", + subtitle: "Введите и подтвердите новый пароль.", + newPassword: "Новый пароль", + newPasswordPlaceholder: "Не менее 8 символов", + confirmPassword: "Подтверждение пароля", + confirmPasswordPlaceholder: "Повторите новый пароль", + saving: "Сохранение…", + submit: "Установить новый пароль", + backToLogin: "Вернуться ко входу", + passwordsDoNotMatch: "Пароли не совпадают", + passwordTooShort: "Пароль должен содержать не менее 8 символов", + resetFailed: "Не удалось сбросить пароль. Возможно, ссылка устарела.", + }, + twoFA: { + title: "Двухфакторная аутентификация", + subtitle: "Учётная запись защищена 2FA. Введите код подтверждения.", + methodAuthenticator: "Приложение-аутентификатор", + methodEmail: "Код по email", + methodBackup: "Резервный код", + instructionsTotp: "Откройте приложение-аутентификатор и введите шестизначный код Bambuddy.", + instructionsEmail: "Шестизначный код отправлен на вашу электронную почту. Он действует 10 минут.", + instructionsEmailNotSent: "Нажмите кнопку ниже, чтобы получить код подтверждения по email.", + instructionsBackup: "Введите один из восьмизначных резервных кодов восстановления. Каждый код используется только один раз.", + sendCodeButton: "Отправить код по email", + sendingCode: "Отправка...", + resendCode: "Отправить код повторно", + codeLabel: "Код подтверждения", + backupCodeLabel: "Резервный код", + codePlaceholder: "000000", + backupCodePlaceholder: "XXXXXXXX", + verifyButton: "Подтвердить", + verifyingButton: "Проверка...", + backToLogin: "← Вернуться ко входу", + orContinueWith: "или продолжить через", + signInWith: "Войти через {{provider}}", + enterCode: "Введите код подтверждения", + sendCodeFailed: "Не удалось отправить код подтверждения", + invalidCode: "Неверный код. Повторите попытку.", + }, + }, + setup: { + title: "Настройка Bambuddy", + subtitle: "Настройте аутентификацию для этого экземпляра Bambuddy", + enableAuth: "Включить аутентификацию", + adminAccount: "Учётная запись администратора", + adminAccountDesc: "Если администраторы уже существуют, аутентификация будет включена с использованием существующих учётных записей. Оставьте поля пустыми или введите данные для создания нового администратора.", + adminUsername: "Имя администратора", + adminPassword: "Пароль администратора", + optionalIfAdminExists: "(необязательно, если администратор уже существует)", + adminUsernamePlaceholder: "Введите имя администратора (необязательно)", + adminPasswordPlaceholder: "Введите пароль администратора (необязательно)", + confirmPassword: "Подтвердите пароль", + confirmPasswordPlaceholder: "Повторите пароль администратора", + settingUp: "Настройка...", + completeSetup: "Завершить настройку", + toast: { + authEnabledAdminCreated: "Аутентификация включена, администратор создан", + authEnabledExistingAdmins: "Аутентификация включена для существующих администраторов", + setupCompleted: "Настройка завершена", + enterBothCredentials: "Введите имя и пароль администратора либо оставьте оба поля пустыми", + passwordsDoNotMatch: "Пароли не совпадают", + passwordTooShort: "Пароль должен содержать не менее 6 символов", + }, }, changePassword: { - title: 'Изменение пароля', - currentPassword: 'Текущий пароль', - currentPasswordPlaceholder: 'Введите текущий пароль', - newPassword: 'Новый пароль', - newPasswordPlaceholder: 'Введите новый пароль (не менее 6 символов)', - confirmPassword: 'Подтвердите новый пароль', - confirmPasswordPlaceholder: 'Повторите новый пароль', - passwordsDoNotMatch: 'Пароли не совпадают', - passwordTooShort: 'Пароль должен содержать не менее 6 символов', - changing: 'Изменение...', - success: 'Пароль изменён', - failed: 'Не удалось изменить пароль', + title: "Изменение пароля", + currentPassword: "Текущий пароль", + currentPasswordPlaceholder: "Введите текущий пароль", + newPassword: "Новый пароль", + newPasswordPlaceholder: "Введите новый пароль (не менее 6 символов)", + confirmPassword: "Подтвердите новый пароль", + confirmPasswordPlaceholder: "Повторите новый пароль", + passwordsDoNotMatch: "Пароли не совпадают", + passwordTooShort: "Пароль должен содержать не менее 6 символов", + changing: "Изменение...", + success: "Пароль изменён", + failed: "Не удалось изменить пароль", }, plateAlert: { - title: 'Печать приостановлена!', - message: 'На печатной пластине обнаружены объекты. Печать автоматически приостановлена. Очистите пластину и продолжите печать.', - understand: 'Понятно', + title: "Печать приостановлена!", + message: "На печатной пластине обнаружены объекты. Печать автоматически приостановлена. Очистите пластину и продолжите печать.", + understand: "Понятно", }, camera: { - title: 'Камера', - invalidPrinterId: 'Неверный идентификатор принтера', - live: 'Прямой эфир', - snapshot: 'Снимок', - restartStream: 'Перезапустить поток', - refreshSnapshot: 'Обновить снимок', - fullscreen: 'На весь экран', - exitFullscreen: 'Выйти из полноэкранного режима', - connectingToCamera: 'Подключение к камере...', - capturingSnapshot: 'Создание снимка...', - connectionLost: 'Соединение потеряно', - connectionFailed: 'Не удалось подключиться к камере', - reconnecting: 'Повторное подключение через {{countdown}} с... (попытка {{attempt}} из {{max}})', - reconnectNow: 'Подключиться сейчас', - cameraUnavailable: 'Камера недоступна', - cameraUnavailableDesc: 'Убедитесь, что принтер включён и подключён к сети.', - noCamera: 'Камера отсутствует', - retry: 'Повторить', - cameraStream: 'Видеопоток камеры', - zoomOut: 'Уменьшить', - zoomIn: 'Увеличить', - resetZoom: 'Сбросить масштаб', - recording: 'Запись', - startRecording: 'Начать запись', - stopRecording: 'Остановить запись', - chamberLight: 'Переключить освещение камеры принтера', - unavailable: 'Камера недоступна', + title: "Камера", + invalidPrinterId: "Неверный идентификатор принтера", + live: "Прямой эфир", + snapshot: "Снимок", + restartStream: "Перезапустить поток", + refreshSnapshot: "Обновить снимок", + fullscreen: "На весь экран", + exitFullscreen: "Выйти из полноэкранного режима", + connectingToCamera: "Подключение к камере...", + capturingSnapshot: "Создание снимка...", + connectionLost: "Соединение потеряно", + connectionFailed: "Не удалось подключиться к камере", + reconnecting: "Повторное подключение через {{countdown}} с... (попытка {{attempt}} из {{max}})", + reconnectNow: "Подключиться сейчас", + cameraUnavailable: "Камера недоступна", + cameraUnavailableDesc: "Убедитесь, что принтер включён и подключён к сети.", + noCamera: "Камера отсутствует", + retry: "Повторить", + cameraStream: "Видеопоток камеры", + zoomOut: "Уменьшить", + zoomIn: "Увеличить", + resetZoom: "Сбросить масштаб", + recording: "Запись", + startRecording: "Начать запись", + stopRecording: "Остановить запись", + chamberLight: "Переключить освещение камеры принтера", + unavailable: "Камера недоступна", diagnose: { - button: 'Диагностика', - modalTitle: 'Диагностика камеры', - running: 'Выполняется диагностика...', - runFailed: 'Не удалось выполнить диагностику: {{error}}', - retry: 'Запустить снова', + button: "Диагностика", + modalTitle: "Диагностика камеры", + running: "Выполняется диагностика...", + runFailed: "Не удалось выполнить диагностику: {{error}}", + retry: "Запустить снова", stage: { - tcp_reachable: 'Доступность по сети', - first_frame: 'Получение кадра', - live_stream_active: 'Активный видеопоток', + tcp_reachable: "Доступность по сети", + first_frame: "Получение кадра", + live_stream_active: "Активный видеопоток", }, summary: { - all_ok: 'Камера работает. Все этапы диагностики завершены успешно.', - live_stream_active_healthy: 'Камера уже передаёт свежие кадры — дополнительная проверка не требуется.', - printer_unreachable: 'Принтер недоступен. Проверьте IP-адрес, сетевое подключение и питание принтера.', - camera_port_closed: 'Принтер доступен, но порт камеры закрыт. Убедитесь, что в настройках принтера включены режим «Только LAN» и режим разработчика.', - no_frame: 'Подключение к камере установлено, но кадры не поступают. Повторите попытку или проверьте, включена ли камера в настройках принтера.', - unknown_failure: 'Диагностика камеры завершилась неизвестной ошибкой. Подробности см. в журнале поддержки.', + all_ok: "Камера работает. Все этапы диагностики завершены успешно.", + live_stream_active_healthy: "Камера уже передаёт свежие кадры — дополнительная проверка не требуется.", + printer_unreachable: "Принтер недоступен. Проверьте IP-адрес, сетевое подключение и питание принтера.", + camera_port_closed: "Принтер доступен, но порт камеры закрыт. Убедитесь, что в настройках принтера включены режим «Только LAN» и режим разработчика.", + no_frame: "Подключение к камере установлено, но кадры не поступают. Повторите попытку или проверьте, включена ли камера в настройках принтера.", + unknown_failure: "Диагностика камеры завершилась неизвестной ошибкой. Подробности см. в журнале поддержки.", }, meta: { - protocol: 'Протокол', - port: 'Порт', - profile: 'Профиль', + protocol: "Протокол", + port: "Порт", + profile: "Профиль", }, }, }, groups: { - title: 'Управление группами', - subtitle: 'Настройка групп разрешений для контроля доступа', - backToSettings: 'Вернуться к настройкам', - createGroup: 'Создать группу', - noPermission: 'У вас нет доступа к этой странице.', - system: 'Системная', - noDescription: 'Без описания', - usersCount: 'Пользователей: {{count}}', - permissionsCount: 'Разрешений: {{count}}', - edit: 'Изменить', - delete: 'Удалить', + title: "Управление группами", + subtitle: "Настройка групп разрешений для контроля доступа", + backToSettings: "Вернуться к настройкам", + createGroup: "Создать группу", + noPermission: "У вас нет доступа к этой странице.", + system: "Системная", + noDescription: "Без описания", + usersCount: "Пользователей: {{count}}", + permissionsCount: "Разрешений: {{count}}", + edit: "Изменить", + delete: "Удалить", toast: { - created: 'Группа создана', - updated: 'Группа обновлена', - deleted: 'Группа удалена', - enterGroupName: 'Введите название группы', + created: "Группа создана", + updated: "Группа обновлена", + deleted: "Группа удалена", + enterGroupName: "Введите название группы", }, modal: { - editGroup: 'Изменение группы', - createGroup: 'Создание группы', - cancel: 'Отмена', - saving: 'Сохранение...', - creating: 'Создание...', - saveChanges: 'Сохранить изменения', + editGroup: "Изменение группы", + createGroup: "Создание группы", + cancel: "Отмена", + saving: "Сохранение...", + creating: "Создание...", + saveChanges: "Сохранить изменения", }, form: { - groupName: 'Название группы', - groupNamePlaceholder: 'Введите название группы', - systemGroupWarning: 'Названия системных групп изменить нельзя', - description: 'Описание', - descriptionPlaceholder: 'Введите описание (необязательно)', - permissions: 'Разрешения (выбрано: {{count}})', + groupName: "Название группы", + groupNamePlaceholder: "Введите название группы", + systemGroupWarning: "Названия системных групп изменить нельзя", + description: "Описание", + descriptionPlaceholder: "Введите описание (необязательно)", + permissions: "Разрешения (выбрано: {{count}})", }, deleteModal: { - title: 'Удаление группы', - message: 'Удалить эту группу? Входящие в неё пользователи потеряют соответствующие разрешения.', - confirm: 'Удалить группу', + title: "Удаление группы", + message: "Удалить эту группу? Входящие в неё пользователи потеряют соответствующие разрешения.", + confirm: "Удалить группу", }, editor: { - title: 'Изменение группы', - createTitle: 'Создание группы', - search: 'Поиск разрешений...', - selectAll: 'Выбрать все', - clearAll: 'Очистить выбор', - permissionsSelected: 'Выбрано: {{count}}', - noResults: 'Нет разрешений, соответствующих запросу', - websocketHint: 'Необходимо для обновлений в реальном времени. Без этого интерфейс будет периодически опрашивать сервер.', + title: "Изменение группы", + createTitle: "Создание группы", + search: "Поиск разрешений...", + selectAll: "Выбрать все", + clearAll: "Очистить выбор", + permissionsSelected: "Выбрано: {{count}}", + noResults: "Нет разрешений, соответствующих запросу", + websocketHint: "Необходимо для обновлений в реальном времени. Без этого интерфейс будет периодически опрашивать сервер.", }, }, users: { - title: 'Управление пользователями', - subtitle: 'Управление пользователями и их доступом к Bambuddy', - backToSettings: 'Вернуться к настройкам', - createUser: 'Создать пользователя', - noPermission: 'У вас нет доступа к этой странице.', - admin: 'Администратор', - noGroups: 'Без групп', - active: 'Активен', - inactive: 'Отключён', - edit: 'Изменить', - delete: 'Удалить', - system: 'Системный', - noGroupsAvailable: 'Нет доступных групп', + title: "Управление пользователями", + subtitle: "Управление пользователями и их доступом к Bambuddy", + backToSettings: "Вернуться к настройкам", + createUser: "Создать пользователя", + noPermission: "У вас нет доступа к этой странице.", + admin: "Администратор", + noGroups: "Без групп", + active: "Активен", + inactive: "Отключён", + edit: "Изменить", + delete: "Удалить", + system: "Системный", + noGroupsAvailable: "Нет доступных групп", table: { - username: 'Имя пользователя', - groups: 'Группы', - status: 'Состояние', - actions: 'Действия', + username: "Имя пользователя", + groups: "Группы", + status: "Состояние", + actions: "Действия", }, toast: { - created: 'Пользователь создан', - updated: 'Пользователь обновлён', - deleted: 'Пользователь удалён', - fillRequired: 'Заполните все обязательные поля', - passwordsDoNotMatch: 'Пароли не совпадают', - passwordTooShort: 'Пароль должен содержать не менее 6 символов', - ldapProvisioned: 'Пользователь LDAP «{{username}}» добавлен', + created: "Пользователь создан", + updated: "Пользователь обновлён", + deleted: "Пользователь удалён", + fillRequired: "Заполните все обязательные поля", + passwordsDoNotMatch: "Пароли не совпадают", + passwordTooShort: "Пароль должен содержать не менее 6 символов", + ldapProvisioned: "Пользователь LDAP «{{username}}» добавлен", }, modal: { - createUser: 'Создание пользователя', - editUser: 'Изменение пользователя', - cancel: 'Отмена', - creating: 'Создание...', - saving: 'Сохранение...', - saveChanges: 'Сохранить изменения', - advancedAuthSubtitle: 'с расширенной аутентификацией', - tabsAriaLabel: 'Источник пользователя', - localTab: 'Локальный', - ldapTab: 'LDAP', - ldapSearchLabel: 'Поиск в каталоге', - ldapSearchPlaceholder: 'Введите имя пользователя, имя или email...', - ldapMinChars: 'Для поиска введите не менее 2 символов', - ldapTypeToSearch: 'Начните вводить запрос для поиска в каталоге LDAP', - ldapSearching: 'Поиск в каталоге...', - ldapNoResults: 'В каталоге нет подходящих пользователей', - ldapSearchError: 'Ошибка поиска в каталоге. Проверьте состояние сервера LDAP.', - ldapAlreadyProvisioned: 'Уже добавлен', - ldapSelectedLabel: 'Выбран', - ldapProvision: 'Добавить пользователя', - ldapProvisioning: 'Добавление...', - ldapErrorProvision: 'Не удалось добавить пользователя. Проверьте сервер LDAP и повторите попытку.', + createUser: "Создание пользователя", + editUser: "Изменение пользователя", + cancel: "Отмена", + creating: "Создание...", + saving: "Сохранение...", + saveChanges: "Сохранить изменения", + advancedAuthSubtitle: "с расширенной аутентификацией", + tabsAriaLabel: "Источник пользователя", + localTab: "Локальный", + ldapTab: "LDAP", + ldapSearchLabel: "Поиск в каталоге", + ldapSearchPlaceholder: "Введите имя пользователя, имя или email...", + ldapMinChars: "Для поиска введите не менее 2 символов", + ldapTypeToSearch: "Начните вводить запрос для поиска в каталоге LDAP", + ldapSearching: "Поиск в каталоге...", + ldapNoResults: "В каталоге нет подходящих пользователей", + ldapSearchError: "Ошибка поиска в каталоге. Проверьте состояние сервера LDAP.", + ldapAlreadyProvisioned: "Уже добавлен", + ldapSelectedLabel: "Выбран", + ldapProvision: "Добавить пользователя", + ldapProvisioning: "Добавление...", + ldapErrorProvision: "Не удалось добавить пользователя. Проверьте сервер LDAP и повторите попытку.", }, form: { - username: 'Имя пользователя', - usernamePlaceholder: 'Введите имя пользователя', - email: 'Email', - emailPlaceholder: 'user@example.com', - password: 'Пароль', - passwordPlaceholder: 'Введите пароль', - confirmPassword: 'Подтверждение пароля', - confirmPasswordPlaceholder: 'Повторите пароль', - newPasswordPlaceholder: 'Введите новый пароль', - confirmNewPasswordPlaceholder: 'Повторите новый пароль', - leaveBlankToKeep: 'оставьте пустым, чтобы сохранить текущий', - groups: 'Группы', - optional: 'необязательно', - autoGeneratedPassword: 'Надёжный пароль будет создан автоматически и отправлен пользователю по email.', - passwordManagedByAdvancedAuth: 'Пароль управляется расширенной аутентификацией. Используйте «Сбросить пароль», чтобы отправить пользователю новый пароль по email.', - resetPassword: 'Сбросить пароль', - resettingPassword: 'Сброс пароля...', + username: "Имя пользователя", + usernamePlaceholder: "Введите имя пользователя", + email: "Электронная почта", + emailPlaceholder: "user@example.com", + password: "Пароль", + passwordPlaceholder: "Введите пароль", + confirmPassword: "Подтверждение пароля", + confirmPasswordPlaceholder: "Повторите пароль", + newPasswordPlaceholder: "Введите новый пароль", + confirmNewPasswordPlaceholder: "Повторите новый пароль", + leaveBlankToKeep: "оставьте пустым, чтобы сохранить текущий", + groups: "Группы", + optional: "необязательно", + autoGeneratedPassword: "Надёжный пароль будет создан автоматически и отправлен пользователю по email.", + passwordManagedByAdvancedAuth: "Пароль управляется расширенной аутентификацией. Используйте «Сбросить пароль», чтобы отправить пользователю новый пароль по email.", + resetPassword: "Сбросить пароль", + resettingPassword: "Сброс пароля...", }, deleteModal: { - title: 'Удаление пользователя', - message: 'Удалить этого пользователя? Это действие нельзя отменить.', - confirm: 'Удалить пользователя', + title: "Удаление пользователя", + message: "Удалить этого пользователя? Это действие нельзя отменить.", + confirm: "Удалить пользователя", }, }, streamOverlay: { - title: 'Оформление трансляции', - invalidPrinterId: 'Неверный идентификатор принтера', - cameraStream: 'Видеопоток камеры', - progress: 'Прогресс', - eta: 'Осталось', - printerIdle: 'Принтер простаивает', - printerOffline: 'Принтер не в сети', + title: "Оформление трансляции", + invalidPrinterId: "Неверный идентификатор принтера", + cameraStream: "Видеопоток камеры", + progress: "Прогресс", + eta: "Осталось", + printerIdle: "Принтер простаивает", + printerOffline: "Принтер не в сети", status: { - printing: 'Печать', - paused: 'Приостановлено', - finished: 'Завершено', - failed: 'Ошибка', - idle: 'Ожидание', - unknown: 'Неизвестно', + printing: "Печать", + paused: "Приостановлено", + finished: "Завершено", + failed: "Ошибка", + idle: "Ожидание", + unknown: "Неизвестно", }, }, profiles: { - title: 'Профили', - subtitle: 'Управление профилями слайсера и калибровками Pressure Advance', + title: "Профили", + subtitle: "Управление профилями слайсера и калибровками Pressure Advance", tabs: { - bambuCloud: 'Bambu Cloud', - orcaCloud: 'Orca Cloud', - local: 'Локальные профили', - kprofiles: 'K-профили', + bambuCloud: "Bambu Cloud", + orcaCloud: "Orca Cloud", + local: "Локальные профили", + kprofiles: "K-профили", }, orcaCloud: { - connectedAs: 'Подключено как', - logout: 'Отключиться', - noLogoutPermission: 'У вас нет разрешения отключаться от Orca Cloud', - noConnectPermission: 'У вас нет разрешения подключаться к Orca Cloud', - retry: 'Повторить', - back: 'Использовать другой способ входа', + connectedAs: "Подключено как", + connectedShort: "Подключено к Orca Cloud", + logout: "Отключиться", + noLogoutPermission: "У вас нет разрешения отключаться от Orca Cloud", + noConnectPermission: "У вас нет разрешения подключаться к Orca Cloud", + retry: "Повторить", + connectButton: "Подключить Orca Cloud", connect: { - title: 'Подключение к Orca Cloud', - description: 'Войдите в Orca Cloud, чтобы синхронизировать профили слайсера с Bambuddy.', + title: "Подключение к Orca Cloud", + description: "Войдите в Orca Cloud, чтобы синхронизировать профили слайсера с Bambuddy.", }, - providers: { - google: 'Войти через Google', - apple: 'Войти через Apple', - github: 'Войти через GitHub', - email: 'Войти по email и паролю', - }, - password: { - title: 'Вход по email и паролю', - email: 'Email', - emailPlaceholder: 'you@example.com', - password: 'Пароль', - submit: 'Войти', - }, - paste: { - title: 'Завершение входа', - step1: 'В новой вкладке открылась страница входа Orca Cloud. Войдите в свою учётную запись Orca.', - step2: 'Браузер перенаправит вас на адрес localhost, который не откроется. Это нормально — нам нужен сам адрес.', - step3: 'Скопируйте весь адрес из адресной строки браузера и вставьте его ниже.', - signInUrl: 'Если вкладка входа не открылась, нажмите эту ссылку:', - label: 'Вставьте сюда URL обратного вызова', - placeholder: 'http://localhost:41172/callback?code=...&state=...', - submit: 'Завершить подключение', + device: { + title: "Разрешите доступ Bambuddy в Orca Cloud", + instruction: "Откройте Orca Cloud и подтвердите этот код. После подтверждения Bambuddy подключится автоматически.", + codeLabel: "Код сопряжения", + openButton: "Открыть страницу подтверждения Orca Cloud", + manualHint: "Либо откройте {{url}} и введите указанный выше код.", + waiting: "Ожидание подтверждения…", + cancel: "Отмена", }, profiles: { - title: 'Ваши профили Orca Cloud ({{count}})', - refresh: 'Обновить', - empty: 'В вашей учётной записи Orca Cloud пока нет профилей.', + title: "Ваши профили Orca Cloud ({{count}})", + refresh: "Обновить", + empty: "В вашей учётной записи Orca Cloud пока нет профилей.", }, toast: { - connected: 'Подключено к Orca Cloud как {{email}}', - disconnected: 'Orca Cloud отключён', + disconnected: "Orca Cloud отключён", }, errors: { - startFailed: 'Не удалось начать вход в Orca Cloud.', - finishFailed: 'Не удалось завершить вход в Orca Cloud.', - passwordFailed: 'Не удалось войти с указанными email и паролем.', - passwordEmpty: 'Введите email и пароль.', - emptyPaste: 'Вставьте URL обратного вызова из браузера.', - noCode: 'Этот адрес не похож на URL обратного вызова Orca Cloud: отсутствует параметр code. Скопируйте полный адрес из адресной строки.', + startFailed: "Не удалось начать вход в Orca Cloud.", + denied: "Сопряжение отклонено в Orca Cloud.", + expired: "Срок действия кода сопряжения истёк. Нажмите «Подключить», чтобы повторить попытку.", + pollFailed: "Соединение было потеряно во время ожидания подтверждения. Повторите попытку.", }, }, localProfiles: { - title: 'Локальные профили', - subtitle: 'Импорт профилей OrcaSlicer и управление ими', - import: 'Импортировать профили', - importDesc: 'Перетащите сюда файлы .bbscfg, .bbsflmt, .orca_filament, .zip или .json', - importing: 'Импорт...', - search: 'Поиск локальных профилей...', - noPresets: 'Локальных профилей пока нет', - noSearchResults: 'Нет профилей, соответствующих запросу', - badge: 'Локальный', - edit: 'Изменить', - delete: 'Удалить', - cancel: 'Отмена', - deleteConfirmTitle: 'Удаление профиля', - deleteConfirm: 'Удалить этот профиль? Это действие нельзя отменить.', - source: 'Источник', - inheritsFrom: 'Наследует', - filamentType: 'Тип', - vendor: 'Производитель', - compatiblePrinters: 'Принтеры', - nozzleTemp: 'Температура сопла', - cost: 'Стоимость', - density: 'Плотность', - pressureAdvance: 'Pressure Advance', - filament: 'Филамент', - process: 'Процесс', - printer: 'Принтер', + title: "Локальные профили", + subtitle: "Импорт профилей OrcaSlicer и управление ими", + import: "Импортировать профили", + importDesc: "Перетащите сюда файлы .bbscfg, .bbsflmt, .orca_filament, .zip или .json", + importing: "Импорт...", + search: "Поиск локальных профилей...", + noPresets: "Локальных профилей пока нет", + noSearchResults: "Нет профилей, соответствующих запросу", + badge: "Локальный", + edit: "Изменить", + delete: "Удалить", + cancel: "Отмена", + deleteConfirmTitle: "Удаление профиля", + deleteConfirm: "Удалить этот профиль? Это действие нельзя отменить.", + source: "Источник", + inheritsFrom: "Наследует", + filamentType: "Тип", + vendor: "Производитель", + compatiblePrinters: "Принтеры", + nozzleTemp: "Температура сопла", + cost: "Стоимость", + density: "Плотность", + pressureAdvance: "Опережение давления", + filament: "Филамент", + process: "Процесс", + printer: "Принтер", toast: { - importSuccess: 'Импортировано профилей: {{count}}', - importSkipped: 'Пропущено дубликатов: {{count}}', - importError: 'Ошибок при импорте: {{count}}', - deleted: 'Профиль удалён', - updated: 'Профиль обновлён', + importSuccess: "Импортировано профилей: {{count}}", + importSkipped: "Пропущено дубликатов: {{count}}", + importError: "Ошибок при импорте: {{count}}", + deleted: "Профиль удалён", + updated: "Профиль обновлён", }, }, - connectedAs: 'Подключено как', - logout: 'Выйти', - noLogoutPermission: 'У вас нет разрешения выходить из учётной записи', - failedToLoad: 'Не удалось загрузить профили', - retry: 'Повторить', + connectedAs: "Подключено как", + signInExpiredTitle: "Срок авторизации в Bambu Cloud истёк", + signInExpiredBody: "Bambu Lab больше не принимает сохранённый токен. Выполните вход снова, чтобы восстановить облачные профили, импорт из MakerWorld и проверку прошивок.", + logout: "Выйти", + noLogoutPermission: "У вас нет разрешения выходить из учётной записи", + failedToLoad: "Не удалось загрузить профили", + retry: "Повторить", time: { - justNow: 'Только что', - minsAgo: '{{count}} мин назад', - hoursAgo: '{{count}} ч назад', - daysAgo: '{{count}} дн назад', + justNow: "Только что", + minsAgo: "{{count}} мин назад", + hoursAgo: "{{count}} ч назад", + daysAgo: "{{count}} дн назад", }, toast: { - loggedOut: 'Вы вышли из учётной записи', + loggedOut: "Вы вышли из учётной записи", }, login: { - title: 'Подключение к Bambu Cloud', - subtitle: 'Синхронизация профилей слайсера между устройствами', - email: 'Email', - password: 'Пароль', - region: 'Регион', - regionGlobal: 'Глобальный', - regionChina: 'Китай', - verificationCode: 'Код подтверждения', - totpCode: 'Код приложения-аутентификатора', - checkEmail: 'Проверьте почту {{email}}: туда отправлен шестизначный код', - enterTotpHint: 'Введите шестизначный код из приложения-аутентификатора', - accessToken: 'Токен доступа', - accessTokenHint: 'Вставьте токен доступа Bambu Cloud. Для учётных записей китайского региона, привязанных к телефону, доступен только этот способ: вход по email не поддерживается. Инструкцию по получению токена из cookie MakerWorld см. в wiki.', - back: 'Назад', - loginButton: 'Войти', - verifyButton: 'Подтвердить', - setTokenButton: 'Сохранить токен', - useToken: 'Использовать токен доступа', - useEmail: 'Войти по email', + title: "Подключение к Bambu Cloud", + subtitle: "Синхронизация профилей слайсера между устройствами", + email: "Электронная почта", + password: "Пароль", + region: "Регион", + regionGlobal: "Глобальный", + regionChina: "Китай", + verificationCode: "Код подтверждения", + totpCode: "Код приложения-аутентификатора", + checkEmail: "Проверьте почту {{email}}: туда отправлен шестизначный код", + enterTotpHint: "Введите шестизначный код из приложения-аутентификатора", + accessToken: "Токен доступа", + accessTokenHint: "Вставьте токен доступа Bambu Cloud. Для учётных записей китайского региона, привязанных к телефону, доступен только этот способ: вход по email не поддерживается. Инструкцию по получению токена из cookie MakerWorld см. в wiki.", + back: "Назад", + loginButton: "Войти", + verifyButton: "Подтвердить", + setTokenButton: "Сохранить токен", + useToken: "Использовать токен доступа", + useEmail: "Войти по email", toast: { - loggedIn: 'Вход выполнен', - codeSent: 'Код подтверждения отправлен по email', - enterTotp: 'Введите код из приложения-аутентификатора', - tokenSet: 'Токен сохранён', + loggedIn: "Вход выполнен", + codeSent: "Код подтверждения отправлен по email", + enterTotp: "Введите код из приложения-аутентификатора", + tokenSet: "Токен сохранён", }, }, presets: { - myPreset: 'Мой профиль (редактируемый)', - duplicate: 'Создать копию', - editable: 'Редактируемый', - failedToLoadDetails: 'Не удалось загрузить сведения о профиле', - deleteConfirm: 'Удалить этот профиль?', - deleteWarning: 'Профиль «{{name}}» будет безвозвратно удалён из Bambu Cloud. Это действие нельзя отменить.', - noDuplicatePermission: 'У вас нет разрешения копировать профили', - noEditPermission: 'У вас нет разрешения редактировать профили', - noDeletePermission: 'У вас нет разрешения удалять профили', + myPreset: "Мой профиль (редактируемый)", + duplicate: "Создать копию", + editable: "Редактируемый", + failedToLoadDetails: "Не удалось загрузить сведения о профиле", + deleteConfirm: "Удалить этот профиль?", + deleteWarning: "Профиль «{{name}}» будет безвозвратно удалён из Bambu Cloud. Это действие нельзя отменить.", + noDuplicatePermission: "У вас нет разрешения копировать профили", + noEditPermission: "У вас нет разрешения редактировать профили", + noDeletePermission: "У вас нет разрешения удалять профили", types: { - filament: 'Профиль филамента', - printer: 'Профиль принтера', - process: 'Профиль процесса', + filament: "Профиль филамента", + printer: "Профиль принтера", + process: "Профиль процесса", }, toast: { - deleted: 'Профиль удалён', - created: 'Профиль создан', - updated: 'Профиль обновлён', - duplicated: 'Копия профиля создана', - fieldAdded: 'Добавлено поле «{{key}}»', - exported: 'Профиль экспортирован', + deleted: "Профиль удалён", + created: "Профиль создан", + updated: "Профиль обновлён", + duplicated: "Копия профиля создана", + fieldAdded: "Добавлено поле «{{key}}»", + exported: "Профиль экспортирован", }, - baseLabel: 'Базовый: {{name}}', - currentLabel: 'Текущий: {{name}}', - newPreset: 'Новый профиль', - editPreset: 'Изменение профиля', - duplicatePreset: 'Копирование профиля', - createNewPreset: 'Создание профиля', - customizeSettings: 'Настройте параметры нового профиля', - compareWithBase: 'Сравнить с базовым профилем', - compare: 'Сравнить', - basePreset: 'Базовый профиль', - selectBasePreset: 'Выберите базовый профиль...', - presetName: 'Название профиля', - myCustomPreset: 'Мой профиль', - inheritsFrom: 'Наследует от', - dropJsonToImport: 'Перетащите JSON для импорта', + baseLabel: "Базовый: {{name}}", + currentLabel: "Текущий: {{name}}", + newPreset: "Новый профиль", + editPreset: "Изменение профиля", + duplicatePreset: "Копирование профиля", + createNewPreset: "Создание профиля", + customizeSettings: "Настройте параметры нового профиля", + compareWithBase: "Сравнить с базовым профилем", + compare: "Сравнить", + basePreset: "Базовый профиль", + selectBasePreset: "Выберите базовый профиль...", + presetName: "Название профиля", + myCustomPreset: "Мой профиль", + inheritsFrom: "Наследует от", + dropJsonToImport: "Перетащите JSON для импорта", tabs: { - common: 'Основные', - allFields: 'Все поля', + common: "Основные", + allFields: "Все поля", }, - availableFields: 'Доступные поля', - searchFieldsPlaceholder: 'Поиск полей...', - noMatchingFields: 'Нет подходящих полей', - allFieldsAdded: 'Все поля добавлены', - addCustomField: 'Добавить произвольное поле', - yourOverrides: 'Ваши переопределения', - noOverridesYet: 'Переопределений пока нет', - clickFieldsToAdd: 'Нажимайте поля слева, чтобы добавить их', - saveAsTemplate: 'Сохранить как шаблон', - jsonTip: 'Совет: перетащите файл .json в это окно, чтобы импортировать настройки', + availableFields: "Доступные поля", + searchFieldsPlaceholder: "Поиск полей...", + noMatchingFields: "Нет подходящих полей", + allFieldsAdded: "Все поля добавлены", + addCustomField: "Добавить произвольное поле", + yourOverrides: "Ваши переопределения", + noOverridesYet: "Переопределений пока нет", + clickFieldsToAdd: "Нажимайте поля слева, чтобы добавить их", + saveAsTemplate: "Сохранить как шаблон", + jsonTip: "Совет: перетащите файл .json в это окно, чтобы импортировать настройки", }, cloudView: { - searchPlaceholder: 'Поиск профилей...', - templates: 'Шаблоны', - refresh: 'Обновить', - newPreset: 'Новый профиль', - clearFilters: 'Сбросить фильтры', - compareMode: 'Режим сравнения', - selectAnotherPreset: 'Выберите другой профиль типа «{{type}}»', - clickTwoPresets: 'Выберите два профиля одного типа для сравнения', - selectFirst: '1. Выберите первый', - selectSecond: '2. Выберите второй', - compareNow: 'Сравнить', - lastSynced: 'Последняя синхронизация:', - showingCount: 'Показано {{showing}} из {{total}} профилей', - noPresetsFound: 'Профили не найдены', + searchPlaceholder: "Поиск профилей...", + templates: "Шаблоны", + refresh: "Обновить", + newPreset: "Новый профиль", + clearFilters: "Сбросить фильтры", + compareMode: "Режим сравнения", + selectAnotherPreset: "Выберите другой профиль типа «{{type}}»", + clickTwoPresets: "Выберите два профиля одного типа для сравнения", + selectFirst: "1. Выберите первый", + selectSecond: "2. Выберите второй", + compareNow: "Сравнить", + lastSynced: "Последняя синхронизация:", + showingCount: "Показано {{showing}} из {{total}} профилей", + noPresetsFound: "Профили не найдены", columns: { - filament: 'Филамент', - process: 'Процесс', - printer: 'Принтер', + filament: "Филамент", + process: "Процесс", + printer: "Принтер", }, - noFilamentPresets: 'Нет профилей филамента', - noProcessPresets: 'Нет профилей процесса', - noPrinterPresets: 'Нет профилей принтера', + noFilamentPresets: "Нет профилей филамента", + noProcessPresets: "Нет профилей процесса", + noPrinterPresets: "Нет профилей принтера", filters: { - type: 'Тип', - owner: 'Владелец', - printer: 'Принтер', - nozzle: 'Сопло', - filament: 'Филамент', - layer: 'Слой', - all: 'Все', - myPresets: 'Мои профили', - builtIn: 'Встроенные', - process: 'Процесс', + type: "Тип", + owner: "Владелец", + printer: "Принтер", + nozzle: "Сопло", + filament: "Филамент", + layer: "Слой", + all: "Все", + myPresets: "Мои профили", + builtIn: "Встроенные", + process: "Процесс", }, - noTemplatesPermission: 'У вас нет разрешения управлять шаблонами', - noRefreshPermission: 'У вас нет разрешения обновлять профили', - noCreatePermission: 'У вас нет разрешения создавать профили', + noTemplatesPermission: "У вас нет разрешения управлять шаблонами", + noRefreshPermission: "У вас нет разрешения обновлять профили", + noCreatePermission: "У вас нет разрешения создавать профили", }, templates: { - title: 'Быстрые шаблоны', - noTemplates: 'Шаблонов пока нет', - createFirst: 'Создавайте шаблоны в редакторе профилей', - typeFilter: 'Тип:', - deleteTitle: 'Удаление шаблона', - deleteWarning: 'Это действие нельзя отменить', - deleteConfirm: 'Удалить шаблон «{{name}}»?', - namePlaceholder: 'Название шаблона', - descriptionPlaceholder: 'Описание', - settingsJson: 'Настройки (JSON)', - fieldsCount: 'Полей: {{count}}', - shownInModals: 'Показывается в окнах', - hiddenInModals: 'Скрыт в окнах', - apply: 'Применить', + title: "Быстрые шаблоны", + noTemplates: "Шаблонов пока нет", + createFirst: "Создавайте шаблоны в редакторе профилей", + typeFilter: "Тип:", + deleteTitle: "Удаление шаблона", + deleteWarning: "Это действие нельзя отменить", + deleteConfirm: "Удалить шаблон «{{name}}»?", + namePlaceholder: "Название шаблона", + descriptionPlaceholder: "Описание", + settingsJson: "Настройки (JSON)", + fieldsCount: "Полей: {{count}}", + shownInModals: "Показывается в окнах", + hiddenInModals: "Скрыт в окнах", + apply: "Применить", toast: { - deleted: 'Шаблон удалён', - updated: 'Шаблон обновлён', - created: 'Шаблон создан', - applied: 'Шаблон применён', + deleted: "Шаблон удалён", + updated: "Шаблон обновлён", + created: "Шаблон создан", + applied: "Шаблон применён", }, }, }, support: { - debugLoggingActive: 'Отладочное журналирование включено', - manageLogs: 'Управление', - collectItem7: 'Подключение принтеров и версии прошивок', - collectItem8: 'Состояние интеграций (Spoolman, MQTT, HA)', - collectItem9: 'Сетевые интерфейсы (только подсети)', - collectItem10: 'Версии пакетов Python', - collectItem11: 'Проверка состояния базы данных', - collectItem12: 'Сведения о среде Docker', - bundleGenerating: 'Формирование пакета...', - bundleStepConnection: 'Проверка подключения принтеров', - bundleStepVirtualPrinters: 'Проверка настройки виртуальных принтеров', - bundleStepLogScan: 'Поиск известных проблем в последних журналах', - bundleStepBuild: 'Создание ZIP-пакета для поддержки', - }, - login: { - title: 'Вход в Bambuddy', - subtitle: 'Войдите в свою учётную запись', - username: 'Имя пользователя', - usernamePlaceholder: 'Введите имя пользователя', - usernameOrEmail: 'Имя пользователя или email', - usernameOrEmailPlaceholder: 'Имя пользователя или email', - password: 'Пароль', - passwordPlaceholder: 'Введите пароль', - signIn: 'Войти', - signingIn: 'Вход...', - rememberMe: 'Запомнить меня', - forgotPassword: 'Забыли пароль?', - autologinFailed: 'Автоматический вход через SSO не выполнен. Выберите провайдера ниже.', - localDisabledNotice: 'Локальный вход отключён. Используйте одного из SSO-провайдеров ниже.', - loginSuccess: 'Вход выполнен', - loginFailed: 'Не удалось войти', - enterCredentials: 'Введите имя пользователя и пароль', - enterEmail: 'Введите адрес электронной почты', - oidcLoginFailed: 'Ошибка входа через OIDC', - oidcErrors: { - providerError: 'Провайдер удостоверений вернул ошибку', - missingParameters: 'В ответе OIDC отсутствуют обязательные параметры', - invalidState: 'Состояние OIDC недействительно или уже использовано', - stateExpired: 'Сеанс входа OIDC истёк — повторите попытку', - providerNotFound: 'Провайдер OIDC не найден', - discoveryFailed: 'Не удалось получить документ обнаружения OIDC', - invalidDiscovery: 'Документ обнаружения OIDC недействителен', - networkError: 'Сетевая ошибка при обмене токена OIDC', - badResponse: 'Неожиданный ответ при обмене токена OIDC', - noIdToken: 'Провайдер OIDC не вернул ID-токен', - validationFailed: 'Не удалось проверить токен OIDC', - nonceMismatch: 'Несовпадение nonce OIDC — возможна атака повторного воспроизведения', - missingSubClaim: 'В токене OIDC отсутствует поле sub', - noLinkedAccount: 'С этой учётной записью OIDC не связана локальная учётная запись', - accountInactive: 'Учётная запись неактивна', - userResolutionFailed: 'Не удалось определить учётную запись', - internalError: 'Внутренняя ошибка при входе через OIDC', - tokenExchangeFailed: 'Не удалось обменять токен OIDC', - }, - forgotPasswordTitle: 'Восстановление пароля', - forgotPasswordMessage: 'Обратитесь к системному администратору для сброса пароля.', - forgotPasswordEmailMessage: 'Введите адрес электронной почты, и мы отправим новый пароль.', - emailAddress: 'Адрес электронной почты', - emailPlaceholder: 'your.email@example.com', - cancel: 'Отмена', - sending: 'Отправка...', - sendResetEmail: 'Отправить письмо для сброса', - howToReset: 'Как сбросить пароль:', - resetStep1: 'Свяжитесь с администратором Bambuddy', - resetStep2: 'Попросите сбросить пароль в разделе управления пользователями', - resetStep3: 'Администратор задаст новый временный пароль', - resetStep4: 'Войдите с новым паролем и измените его в настройках', - gotIt: 'Понятно', - resetPassword: { - title: 'Новый пароль', - subtitle: 'Введите и подтвердите новый пароль.', - newPassword: 'Новый пароль', - newPasswordPlaceholder: 'Не менее 8 символов', - confirmPassword: 'Подтверждение пароля', - confirmPasswordPlaceholder: 'Повторите новый пароль', - saving: 'Сохранение…', - submit: 'Установить новый пароль', - backToLogin: 'Вернуться ко входу', - passwordsDoNotMatch: 'Пароли не совпадают', - passwordTooShort: 'Пароль должен содержать не менее 8 символов', - resetFailed: 'Не удалось сбросить пароль. Возможно, ссылка устарела.', - }, - twoFA: { - title: 'Двухфакторная аутентификация', - subtitle: 'Учётная запись защищена 2FA. Введите код подтверждения.', - methodAuthenticator: 'Приложение-аутентификатор', - methodEmail: 'Код по email', - methodBackup: 'Резервный код', - instructionsTotp: 'Откройте приложение-аутентификатор и введите шестизначный код Bambuddy.', - instructionsEmail: 'Шестизначный код отправлен на вашу электронную почту. Он действует 10 минут.', - instructionsEmailNotSent: 'Нажмите кнопку ниже, чтобы получить код подтверждения по email.', - instructionsBackup: 'Введите один из восьмизначных резервных кодов восстановления. Каждый код используется только один раз.', - sendCodeButton: 'Отправить код по email', - sendingCode: 'Отправка...', - resendCode: 'Отправить код повторно', - codeLabel: 'Код подтверждения', - backupCodeLabel: 'Резервный код', - codePlaceholder: '000000', - backupCodePlaceholder: 'XXXXXXXX', - verifyButton: 'Подтвердить', - verifyingButton: 'Проверка...', - backToLogin: '← Вернуться ко входу', - orContinueWith: 'или продолжить через', - signInWith: 'Войти через {{provider}}', - enterCode: 'Введите код подтверждения', - sendCodeFailed: 'Не удалось отправить код подтверждения', - invalidCode: 'Неверный код. Повторите попытку.', - }, + debugLoggingActive: "Отладочное журналирование включено", + manageLogs: "Управление", + collectItem7: "Подключение принтеров и версии прошивок", + collectItem8: "Состояние интеграций (Spoolman, MQTT, HA)", + collectItem9: "Сетевые интерфейсы (только подсети)", + collectItem10: "Версии пакетов Python", + collectItem11: "Проверка состояния базы данных", + collectItem12: "Сведения о среде Docker", + bundleGenerating: "Формирование пакета...", + bundleStepConnection: "Проверка подключения принтеров", + bundleStepVirtualPrinters: "Проверка настройки виртуальных принтеров", + bundleStepLogScan: "Поиск известных проблем в последних журналах", + bundleStepBuild: "Создание ZIP-пакета для поддержки", }, fileManager: { - title: 'Файловый менеджер', - subtitle: 'Организация и управление файлами для печати', - uploadFiles: 'Загрузить файлы', - newFolder: 'Новая папка', - folderName: 'Имя папки', - folderNamePlaceholder: 'например, Функциональные детали', - renameFile: 'Переименовать файл', - renameFolder: 'Переименовать папку', - invalidFilenameChar: 'Символ «{{char}}» нельзя использовать в именах файлов печати. SD-карта принтера не принимает: < > : " / \\ | ? *', - moveFiles: 'Переместить файлы ({{count}})', - rootNoFolder: 'Корень (без папки)', - current: 'текущая', - linkFolder: 'Связать папку', - linkFolderDescription: 'Связать «{{name}}» с проектом или архивом для быстрого доступа.', - project: 'Проект', - archive: 'Архив', - noProjectsFound: 'Проекты не найдены', - noArchivesFound: 'Архивы не найдены', - unlink: 'Отвязать', - link: 'Связать', - dragDropFiles: 'Перетащите файлы сюда', - dropFilesHere: 'Перетащите файлы сюда', - releaseToUpload: 'Отпустите для загрузки', - orClickToBrowse: 'или нажмите для выбора', - allFileTypesSupported: 'Поддерживаются все типы файлов. ZIP-архивы будут распакованы.', - zipFilesDetected: 'Обнаружены ZIP-архивы', - zipExtractOptions: 'ZIP-архивы будут распакованы. Выберите способ обработки структуры папок:', - preserveZipStructure: 'Сохранить структуру папок из ZIP', - createFolderFromZip: 'Создать папку с именем ZIP-файла', - stlThumbnailGeneration: 'Создание миниатюр STL', - zipMayContainStl: 'ZIP-архивы могут содержать STL-файлы. Миниатюры можно создать при распаковке.', - thumbnailsCanBeGenerated: 'Для STL-файлов можно создать миниатюры. Обработка крупных моделей может занять больше времени.', - generateThumbnailsForStl: 'Создать миниатюры для STL-файлов', - threemfDetected: 'Обнаружены файлы 3MF', - threemfExtractionInfo: 'Модель принтера, материал, цвет и параметры печати будут автоматически извлечены из файлов 3MF.', - willBeExtracted: 'Будут извлечены', - filesExtracted: 'Извлечено файлов: {{count}}', - uploadComplete: 'Загрузка завершена: успешно — {{succeeded}}', - uploadFailed: 'Ошибка загрузки', - zipFilesFailed: 'Не удалось обработать файлов: {{count}}', - uploading: 'Загрузка...', - changeLink: 'Изменить связь...', - linkTo: 'Связать с...', - linkToProjectOrArchive: 'Связать с проектом или архивом', - generateThumbnail: 'Создать миниатюру', - generateThumbnails: 'Создать миниатюры', - generateThumbnailsForMissing: 'Создать отсутствующие миниатюры для STL-файлов', - gridView: 'Плитка', - listView: 'Список', - lowDiskSpaceWarning: 'Мало свободного места', - lowDiskSpaceDetails: 'Свободно только {{free}} из {{total}}. Порог предупреждения в настройках: {{threshold}} ГБ.', - files: 'Файлы', - folders: 'Папки', - size: 'Размер', - free: 'Свободно', - allFiles: 'Все файлы', - allExternal: 'Внешние', - externalIsEmpty: 'Внешних файлов нет', - externalEmptyDescription: 'Здесь появятся файлы из подключённых внешних папок.', - wrap: 'Перенос строк', - enableTextWrapping: 'Включить перенос строк', - disableTextWrapping: 'Отключить перенос строк', - collapse: 'Свернуть', - collapseFoldersByDefault: 'Сворачивать папки по умолчанию', - expandFoldersByDefault: 'Разворачивать папки по умолчанию', - folderSort: 'Сортировка папок', - folderSortByName: 'По имени', - folderSortByActivity: 'По последней активности', - dragToResizeTooltip: 'Перетащите для изменения размера, двойной щелчок — сброс', - searchFiles: 'Поиск файлов...', - searchSubfoldersHint: 'С учётом вложенных папок', + title: "Файловый менеджер", + subtitle: "Организация и управление файлами для печати", + uploadFiles: "Загрузить файлы", + newFolder: "Новая папка", + folderName: "Имя папки", + folderNamePlaceholder: "например, Функциональные детали", + renameFile: "Переименовать файл", + renameFolder: "Переименовать папку", + invalidFilenameChar: "Символ «{{char}}» нельзя использовать в именах файлов печати. SD-карта принтера не принимает: < > : \" / \\ | ? *", + moveFiles: "Переместить файлы ({{count}})", + rootNoFolder: "Корень (без папки)", + current: "текущая", + linkFolder: "Связать папку", + linkFolderDescription: "Связать «{{name}}» с проектом или архивом для быстрого доступа.", + project: "Проект", + archive: "Архив", + noProjectsFound: "Проекты не найдены", + noArchivesFound: "Архивы не найдены", + unlink: "Отвязать", + link: "Связать", + dragDropFiles: "Перетащите файлы сюда", + dropFilesHere: "Перетащите файлы сюда", + releaseToUpload: "Отпустите для загрузки", + orClickToBrowse: "или нажмите для выбора", + allFileTypesSupported: "Поддерживаются все типы файлов. ZIP-архивы будут распакованы.", + zipFilesDetected: "Обнаружены ZIP-архивы", + zipExtractOptions: "ZIP-архивы будут распакованы. Выберите способ обработки структуры папок:", + preserveZipStructure: "Сохранить структуру папок из ZIP", + createFolderFromZip: "Создать папку с именем ZIP-файла", + stlThumbnailGeneration: "Создание миниатюр STL", + zipMayContainStl: "ZIP-архивы могут содержать STL-файлы. Миниатюры можно создать при распаковке.", + thumbnailsCanBeGenerated: "Для STL-файлов можно создать миниатюры. Обработка крупных моделей может занять больше времени.", + generateThumbnailsForStl: "Создать миниатюры для STL-файлов", + threemfDetected: "Обнаружены файлы 3MF", + threemfExtractionInfo: "Модель принтера, материал, цвет и параметры печати будут автоматически извлечены из файлов 3MF.", + willBeExtracted: "Будут извлечены", + filesExtracted: "Извлечено файлов: {{count}}", + uploadComplete: "Загрузка завершена: успешно — {{succeeded}}", + uploadFailed: "Ошибка загрузки", + zipFilesFailed: "Не удалось обработать файлов: {{count}}", + uploading: "Загрузка...", + changeLink: "Изменить связь...", + linkTo: "Связать с...", + linkToProjectOrArchive: "Связать с проектом или архивом", + generateThumbnail: "Создать миниатюру", + generateThumbnails: "Создать миниатюры", + generateThumbnailsForMissing: "Создать отсутствующие миниатюры для STL-файлов", + gridView: "Плитка", + listView: "Список", + lowDiskSpaceWarning: "Мало свободного места", + lowDiskSpaceDetails: "Свободно только {{free}} из {{total}}. Порог предупреждения в настройках: {{threshold}} ГБ.", + files: "Файлы", + folders: "Папки", + size: "Размер", + free: "Свободно", + allFiles: "Все файлы", + allExternal: "Внешние", + externalIsEmpty: "Внешних файлов нет", + externalEmptyDescription: "Здесь появятся файлы из подключённых внешних папок.", + wrap: "Перенос строк", + enableTextWrapping: "Включить перенос строк", + disableTextWrapping: "Отключить перенос строк", + collapse: "Свернуть", + collapseFoldersByDefault: "Сворачивать папки по умолчанию", + expandFoldersByDefault: "Разворачивать папки по умолчанию", + folderSort: "Сортировка папок", + folderSortByName: "По имени", + folderSortByActivity: "По последней активности", + dragToResizeTooltip: "Перетащите для изменения размера, двойной щелчок — сброс", + searchFiles: "Поиск файлов...", + searchSubfoldersHint: "С учётом вложенных папок", readme: { - truncated: 'Показано не полностью', + truncated: "Показано не полностью", + show: "Показать README", + hide: "Скрыть README", + label: "README", }, tags: { - title: 'Метки', - subtitle: 'Помечайте файлы для сквозной фильтрации: игрушки, безопасно для детей, только PETG и что угодно ещё.', - manage: 'Метки', - manageTitle: 'Управление каталогом меток', - add: 'Новая метка', - edit: 'Переименовать метку', - name: 'Название', - fileCount: 'Файлы', - empty: 'Меток пока нет. Создайте первую, чтобы начать помечать файлы.', - noMatches: 'Подходящие метки не найдены.', - createPlaceholder: 'например, игрушки, детям, petg', - createButton: 'Создать', - nameRequired: 'Укажите название.', - searchPlaceholder: 'Фильтр меток...', - created: 'Метка создана.', - updated: 'Метка переименована.', - deleted: 'Метка удалена.', - saveFailed: 'Не удалось сохранить метку.', - deleteFailed: 'Не удалось удалить метку.', - applyFailed: 'Не удалось применить метки.', - applyAdd: 'Добавить метки', - applyRemove: 'Удалить метки', - applyAddSuccess: 'Добавлено меток: {{count}}; обработано файлов: {{files}}.', - applyRemoveSuccess: 'Удалено меток: {{count}}; обработано файлов: {{files}}.', - actionAdd: 'Добавить к выбранным файлам', - actionRemove: 'Удалить с выбранных файлов', - tagAction: 'Метки', - bulkTitle: 'Метки для выбранных файлов ({{count}})', - bulkTooltip: 'Добавить или удалить метки у всех выбранных файлов.', - noPermission: 'У вас нет прав на изменение меток файлов.', - filterLabel: 'Фильтр по меткам:', - clearAll: 'Очистить всё', - confirmDelete: 'Удалить метку «{{name}}»?', - confirmDeleteMessage: 'Метка будет удалена из каталога. Остальные метки файлов сохранятся.', - confirmDeleteInUseMessage: 'Эта метка назначена файлам ({{count}}). При удалении метка исчезнет у всех файлов; сами файлы не изменятся.', - editAria: 'Изменить {{name}}', - deleteAria: 'Удалить {{name}}', + title: "Метки", + subtitle: "Помечайте файлы для сквозной фильтрации: игрушки, безопасно для детей, только PETG и что угодно ещё.", + manage: "Метки", + manageTitle: "Управление каталогом меток", + add: "Новая метка", + edit: "Переименовать метку", + name: "Название", + fileCount: "Файлы", + empty: "Меток пока нет. Создайте первую, чтобы начать помечать файлы.", + noMatches: "Подходящие метки не найдены.", + createPlaceholder: "например, игрушки, детям, petg", + createButton: "Создать", + nameRequired: "Укажите название.", + searchPlaceholder: "Фильтр меток...", + created: "Метка создана.", + updated: "Метка переименована.", + deleted: "Метка удалена.", + saveFailed: "Не удалось сохранить метку.", + deleteFailed: "Не удалось удалить метку.", + applyFailed: "Не удалось применить метки.", + applyAdd: "Добавить метки", + applyRemove: "Удалить метки", + applyAddSuccess: "Добавлено меток: {{count}}; обработано файлов: {{files}}.", + applyRemoveSuccess: "Удалено меток: {{count}}; обработано файлов: {{files}}.", + actionAdd: "Добавить к выбранным файлам", + actionRemove: "Удалить с выбранных файлов", + tagAction: "Метки", + bulkTitle: "Метки для выбранных файлов ({{count}})", + bulkTooltip: "Добавить или удалить метки у всех выбранных файлов.", + noPermission: "У вас нет прав на изменение меток файлов.", + filterLabel: "Фильтр по меткам:", + clearAll: "Очистить всё", + confirmDelete: "Удалить метку «{{name}}»?", + confirmDeleteMessage: "Метка будет удалена из каталога. Остальные метки файлов сохранятся.", + confirmDeleteInUseMessage: "Эта метка назначена файлам ({{count}}). При удалении метка исчезнет у всех файлов; сами файлы не изменятся.", + editAria: "Изменить {{name}}", + deleteAria: "Удалить {{name}}", }, - allTypes: 'Все типы', - prints: 'Файлы печати', - ascending: 'По возрастанию', - descending: 'По убыванию', - resultsCount: 'Показано {{showing}} из {{total}} файлов', - selectAll: 'Выбрать всё', - deselectAll: 'Снять выделение', - selected: 'Выбрано: {{count}}', - adding: 'Добавление...', - loadingFiles: 'Загрузка файлов...', - folderIsEmpty: 'Папка пуста', - noFilesYet: 'Файлов пока нет', - folderEmptyDescription: 'Загрузите или переместите файлы в эту папку.', - noFilesDescription: 'Загрузите файлы, чтобы начать упорядочивать материалы для печати.', - noMatchingFiles: 'Подходящие файлы не найдены', - noMatchingFilesDescription: 'Нет файлов, соответствующих текущему поиску или фильтрам.', - clearFilters: 'Сбросить фильтры', - printedCount: 'Напечатано: {{count}}×', - uploadedBy: 'Загрузил', - deleteFolder: 'Удалить папку', - deleteFile: 'Удалить файл', - deleteFilesCount: 'Удалить файлы ({{count}})', - deleteFolderConfirm: 'Удалить эту папку? Все находящиеся в ней файлы также будут удалены.', - deleteFileConfirm: 'Удалить этот файл?', - deleteFilesConfirm: 'Удалить выбранные файлы ({{count}})? Это действие нельзя отменить.', - deleting: 'Удаление...', - noPermissionRenameFolder: 'У вас нет прав на переименование папок', - noPermissionLinkFolder: 'У вас нет прав на связывание папок', - noPermissionDeleteFolder: 'У вас нет прав на удаление папок', - noPermissionPrint: 'У вас нет прав на запуск печати', - noPermissionAddToQueue: 'У вас нет прав на добавление в очередь', - noPermissionSlice: 'У вас нет прав на нарезку файлов', - noPermissionDownload: 'У вас нет прав на скачивание файлов', - noPermissionRenameFile: 'У вас нет прав на переименование этого файла', - noPermissionGenerateThumbnail: 'У вас нет прав на создание миниатюр', - noPermissionDeleteFile: 'У вас нет прав на удаление этого файла', - noPermissionCreateFolder: 'У вас нет прав на создание папок', - noPermissionUpload: 'У вас нет прав на загрузку файлов', - noPermissionMoveFiles: 'У вас нет прав на перемещение файлов', - noPermissionDeleteFiles: 'У вас нет прав на удаление файлов', - linkExternal: 'Подключить внешнюю папку', - linkExternalFolder: 'Подключить внешнюю папку', - linkExternalFolderDescription: 'Подключить каталог хоста (NAS, USB-накопитель или сетевой ресурс) к файловому менеджеру. Файлы не копируются — Bambuddy обращается к ним по исходному пути.', - externalFolderNamePlaceholder: 'например, Модели на NAS', - externalPath: 'Путь на хосте', - externalPathHelp: 'Абсолютный путь к каталогу на хосте Docker. Каталог должен быть подключён к контейнеру через bind mount.', - readOnly: 'Только чтение', - readOnlyHelp: 'запрещает загрузку и удаление файлов', - showHiddenFiles: 'Показывать скрытые файлы (dotfiles)', - externalFolder: 'Внешняя папка', - scanFolder: 'Сканировать', + allTypes: "Все типы", + prints: "Файлы печати", + ascending: "По возрастанию", + descending: "По убыванию", + resultsCount: "Показано {{showing}} из {{total}} файлов", + selectAll: "Выбрать всё", + deselectAll: "Снять выделение", + selected: "Выбрано: {{count}}", + adding: "Добавление...", + loadingFiles: "Загрузка файлов...", + folderIsEmpty: "Папка пуста", + noFilesYet: "Файлов пока нет", + folderEmptyDescription: "Загрузите или переместите файлы в эту папку.", + noFilesDescription: "Загрузите файлы, чтобы начать упорядочивать материалы для печати.", + noMatchingFiles: "Подходящие файлы не найдены", + noMatchingFilesDescription: "Нет файлов, соответствующих текущему поиску или фильтрам.", + clearFilters: "Сбросить фильтры", + printedCount: "Напечатано: {{count}}×", + uploadedBy: "Загрузил", + deleteFolder: "Удалить папку", + deleteFile: "Удалить файл", + deleteFilesCount: "Удалить файлы ({{count}})", + deleteFolderConfirm: "Удалить эту папку? Все находящиеся в ней файлы также будут удалены.", + deleteFileConfirm: "Удалить этот файл?", + deleteFilesConfirm: "Удалить выбранные файлы ({{count}})? Это действие нельзя отменить.", + deleting: "Удаление...", + noPermissionRenameFolder: "У вас нет прав на переименование папок", + noPermissionLinkFolder: "У вас нет прав на связывание папок", + noPermissionDeleteFolder: "У вас нет прав на удаление папок", + noPermissionPrint: "У вас нет прав на запуск печати", + noPermissionAddToQueue: "У вас нет прав на добавление в очередь", + noPermissionSlice: "У вас нет прав на нарезку файлов", + noPermissionDownload: "У вас нет прав на скачивание файлов", + noPermissionRenameFile: "У вас нет прав на переименование этого файла", + noPermissionGenerateThumbnail: "У вас нет прав на создание миниатюр", + noPermissionDeleteFile: "У вас нет прав на удаление этого файла", + noPermissionCreateFolder: "У вас нет прав на создание папок", + noPermissionUpload: "У вас нет прав на загрузку файлов", + noPermissionMoveFiles: "У вас нет прав на перемещение файлов", + noPermissionDeleteFiles: "У вас нет прав на удаление файлов", + linkExternal: "Подключить внешнюю папку", + linkExternalFolder: "Подключить внешнюю папку", + linkExternalFolderDescription: "Подключить каталог хоста (NAS, USB-накопитель или сетевой ресурс) к файловому менеджеру. Файлы не копируются — Bambuddy обращается к ним по исходному пути.", + externalFolderNamePlaceholder: "например, Модели на NAS", + externalPath: "Путь на хосте", + externalPathHelp: "Абсолютный путь к каталогу на хосте Docker. Каталог должен быть подключён к контейнеру через bind mount.", + readOnly: "Только чтение", + readOnlyHelp: "запрещает загрузку и удаление файлов", + showHiddenFiles: "Показывать скрытые файлы (dotfiles)", + externalFolder: "Внешняя папка", + scanFolder: "Сканировать", toast: { - folderCreated: 'Папка создана', - folderDeleted: 'Папка удалена', - fileDeleted: 'Файл удалён', - filesDeleted: 'Удалено файлов: {{count}}', - filesMoved: 'Файлы перемещены', - folderLinked: 'Папка связана', - folderUnlinked: 'Связь с папкой удалена', - externalFolderLinked: 'Внешняя папка подключена и просканирована', - folderScanned: 'Сканирование завершено: добавлено — {{added}}, удалено — {{removed}}', - addedToQueue: 'Добавлено в очередь файлов: {{count}}', - addedToQueuePartial: 'Добавлено файлов: {{added}}, ошибок: {{failed}}', - failedToAddToQueue: 'Не удалось добавить файлы: {{error}}', - fileRenamed: 'Файл переименован', - folderRenamed: 'Папка переименована', - thumbnailsGenerated: 'Создано миниатюр: {{count}}', - thumbnailsGeneratedPartial: 'Создано миниатюр: {{succeeded}}, ошибок: {{failed}}', - noStlMissingThumbnails: 'Нет STL-файлов без миниатюр', - failedToGenerateThumbnails: 'Не удалось создать миниатюры: {{error}}', - thumbnailGenerated: 'Миниатюра создана', - failedToGenerateThumbnail: 'Не удалось создать миниатюру: {{error}}', + folderCreated: "Папка создана", + folderDeleted: "Папка удалена", + fileDeleted: "Файл удалён", + filesDeleted: "Удалено файлов: {{count}}", + filesMoved: "Файлы перемещены", + folderLinked: "Папка связана", + folderUnlinked: "Связь с папкой удалена", + externalFolderLinked: "Внешняя папка подключена и просканирована", + folderScanned: "Сканирование завершено: добавлено — {{added}}, удалено — {{removed}}", + addedToQueue: "Добавлено в очередь файлов: {{count}}", + addedToQueuePartial: "Добавлено файлов: {{added}}, ошибок: {{failed}}", + failedToAddToQueue: "Не удалось добавить файлы: {{error}}", + fileRenamed: "Файл переименован", + folderRenamed: "Папка переименована", + thumbnailsGenerated: "Создано миниатюр: {{count}}", + thumbnailsGeneratedPartial: "Создано миниатюр: {{succeeded}}, ошибок: {{failed}}", + noStlMissingThumbnails: "Нет STL-файлов без миниатюр", + failedToGenerateThumbnails: "Не удалось создать миниатюры: {{error}}", + thumbnailGenerated: "Миниатюра создана", + failedToGenerateThumbnail: "Не удалось создать миниатюру: {{error}}", }, }, projects: { - title: 'Проекты', - subtitle: 'Организация и отслеживание проектов 3D-печати', - newProject: 'Новый проект', - editProject: 'Изменить проект', - deleteProject: 'Удалить проект', - projectName: 'Название проекта', - description: 'Описание', - noProjects: 'Проектов пока нет', - noProjectsFiltered: 'Нет проектов со статусом «{{status}}»', - noProjectsFilteredHelp: 'У вас нет проектов со статусом «{{status}}». Они появятся здесь после изменения статуса.', - createFirst: 'Создайте первый проект, чтобы объединять связанные печати, отслеживать ход работ и управлять сборкой.', - createFirstButton: 'Создать первый проект', - create: 'Создать', - files: 'Файлы', - prints: 'Печати', - plates: 'пластин', - parts: 'деталей', - lastModified: 'Последнее изменение', - deleteConfirm: 'Удалить этот проект? Архивные записи и элементы очереди будут отвязаны, но не удалены.', - addFiles: 'Добавить файлы', - removeFile: 'Убрать файл', - viewDetails: 'Подробнее', - namePlaceholder: 'например, Сборка Voron 2.4', - descriptionPlaceholder: 'Необязательное описание...', - urlLabel: 'URL', - urlPlaceholder: 'https://makerworld.com/...', - urlInvalid: 'URL должен начинаться с http:// или https://', - openExternalUrl: 'Открыть страницу проекта', - coverImageLabel: 'Обложка', - coverImageAlt: 'Обложка проекта', - coverImageUpload: 'Загрузить', - coverImageReplace: 'Заменить', - coverImageRemove: 'Удалить', - color: 'Цвет', - targetPlates: 'План по пластинам', - targetPlatesPlaceholder: 'например, 25', - targetPlatesHelp: 'Количество заданий печати', - targetParts: 'План по деталям', - targetPartsPlaceholder: 'например, 150', - targetPartsHelp: 'Общее требуемое количество объектов', - tagsLabel: 'Метки (через запятую)', - tagsPlaceholder: 'например, voron, функциональное, подарок', - dueDate: 'Срок', - priority: 'Приоритет', - priorityLow: 'Низкий', - priorityNormal: 'Обычный', - priorityHigh: 'Высокий', - priorityUrgent: 'Срочный', - statusActive: 'Активный', - statusCompleted: 'Завершён', - statusArchived: 'В архиве', - done: 'Готово', - completed: 'завершено', - failed: 'ошибка', - inQueue: 'в очереди', - noPrintsYet: 'Печатей пока нет', - printJobs: 'Задания печати (пластины)', - partsPrinted: 'Напечатано деталей', - failedParts: 'Неудачных деталей', - import: 'Импорт', - export: 'Экспорт', - importProject: 'Импортировать проект', - exportAll: 'Экспортировать все проекты', - loading: 'Загрузка проектов...', - noEditPermission: 'У вас нет прав на изменение проектов', - noDeletePermission: 'У вас нет прав на удаление проектов', - noCreatePermission: 'У вас нет прав на создание проектов', - noImportPermission: 'У вас нет прав на импорт проектов', - noExportPermission: 'У вас нет прав на экспорт проектов', + title: "Проекты", + subtitle: "Организация и отслеживание проектов 3D-печати", + newProject: "Новый проект", + editProject: "Изменить проект", + deleteProject: "Удалить проект", + projectName: "Название проекта", + description: "Описание", + noProjects: "Проектов пока нет", + noProjectsFiltered: "Нет проектов со статусом «{{status}}»", + noProjectsFilteredHelp: "У вас нет проектов со статусом «{{status}}». Они появятся здесь после изменения статуса.", + createFirst: "Создайте первый проект, чтобы объединять связанные печати, отслеживать ход работ и управлять сборкой.", + createFirstButton: "Создать первый проект", + create: "Создать", + files: "Файлы", + prints: "Печати", + plates: "пластин", + parts: "деталей", + lastModified: "Последнее изменение", + deleteConfirm: "Удалить этот проект? Архивные записи и элементы очереди будут отвязаны, но не удалены.", + addFiles: "Добавить файлы", + removeFile: "Убрать файл", + viewDetails: "Подробнее", + namePlaceholder: "например, Сборка Voron 2.4", + descriptionPlaceholder: "Необязательное описание...", + urlLabel: "URL", + urlPlaceholder: "https://makerworld.com/...", + urlInvalid: "URL должен начинаться с http:// или https://", + openExternalUrl: "Открыть страницу проекта", + coverImageLabel: "Обложка", + coverImageAlt: "Обложка проекта", + coverImageUpload: "Загрузить", + coverImageReplace: "Заменить", + coverImageRemove: "Удалить", + color: "Цвет", + targetPlates: "План по пластинам", + targetPlatesPlaceholder: "например, 25", + targetPlatesHelp: "Количество заданий печати", + targetParts: "План по деталям", + targetPartsPlaceholder: "например, 150", + targetPartsHelp: "Общее требуемое количество объектов", + tagsLabel: "Метки (через запятую)", + tagsPlaceholder: "например, voron, функциональное, подарок", + dueDate: "Срок", + priority: "Приоритет", + priorityLow: "Низкий", + priorityNormal: "Обычный", + priorityHigh: "Высокий", + priorityUrgent: "Срочный", + statusActive: "Активный", + statusCompleted: "Завершён", + statusArchived: "В архиве", + done: "Готово", + completed: "завершено", + failed: "ошибка", + inQueue: "в очереди", + noPrintsYet: "Печатей пока нет", + printJobs: "Задания печати (пластины)", + partsPrinted: "Напечатано деталей", + failedParts: "Неудачных деталей", + import: "Импорт", + export: "Экспорт", + importProject: "Импортировать проект", + exportAll: "Экспортировать все проекты", + loading: "Загрузка проектов...", + noEditPermission: "У вас нет прав на изменение проектов", + noDeletePermission: "У вас нет прав на удаление проектов", + noCreatePermission: "У вас нет прав на создание проектов", + noImportPermission: "У вас нет прав на импорт проектов", + noExportPermission: "У вас нет прав на экспорт проектов", toast: { - created: 'Проект создан', - updated: 'Проект обновлён', - deleted: 'Проект удалён', - imported: 'Проект импортирован', - multipleImported: 'Импортировано проектов: {{count}}', - importFailed: 'Не удалось импортировать проект', - exported: 'Проекты экспортированы (только метаданные)', + created: "Проект создан", + updated: "Проект обновлён", + deleted: "Проект удалён", + imported: "Проект импортирован", + multipleImported: "Импортировано проектов: {{count}}", + importFailed: "Не удалось импортировать проект", + exported: "Проекты экспортированы (только метаданные)", }, }, projectDetail: { - notFound: 'Проект не найден', - backToProjects: 'Вернуться к проектам', - export: 'Экспорт', - exportProject: 'Экспортировать проект', - noExportPermission: 'У вас нет прав на экспорт проектов', - noEditPermission: 'У вас нет прав на изменение проектов', - partOf: 'Входит в:', - priorityLabel: 'Приоритет:', - noPrints: 'В этом проекте пока нет печатей', + notFound: "Проект не найден", + backToProjects: "Вернуться к проектам", + export: "Экспорт", + exportProject: "Экспортировать проект", + noExportPermission: "У вас нет прав на экспорт проектов", + noEditPermission: "У вас нет прав на изменение проектов", + partOf: "Входит в:", + priorityLabel: "Приоритет:", + noPrints: "В этом проекте пока нет печатей", status: { - active: 'Активный', - completed: 'Завершён', - archived: 'В архиве', + active: "Активный", + completed: "Завершён", + archived: "В архиве", }, priority: { - low: 'Низкий', - normal: 'Обычный', - high: 'Высокий', - urgent: 'Срочный', + low: "Низкий", + normal: "Обычный", + high: "Высокий", + urgent: "Срочный", }, dueDate: { - overdue: 'Срок истёк', - today: 'Срок сегодня', - daysLeft: 'Осталось дней: {{count}}', + overdue: "Срок истёк", + today: "Срок сегодня", + daysLeft: "Осталось дней: {{count}}", }, progress: { - platesProgress: 'Выполнение по пластинам', - partsProgress: 'Выполнение по деталям', - printJobs: 'заданий печати', - parts: 'деталей', - percentComplete: 'Выполнено {{percent}}%', - remaining: 'Осталось: {{count}}', + platesProgress: "Выполнение по пластинам", + partsProgress: "Выполнение по деталям", + printJobs: "заданий печати", + parts: "деталей", + percentComplete: "Выполнено {{percent}}%", + remaining: "Осталось: {{count}}", }, stats: { - printJobs: 'Задания печати', - total: 'всего', - failed: 'Неудачных: {{count}}', - partsPrinted: 'Напечатано деталей: {{count}}', - printTime: 'Время печати', - filamentUsed: 'Израсходовано филамента', + printJobs: "Задания печати", + total: "всего", + failed: "Неудачных: {{count}}", + partsPrinted: "Напечатано деталей: {{count}}", + printTime: "Время печати", + filamentUsed: "Израсходовано филамента", }, cost: { - title: 'Учёт стоимости', - filamentCost: 'Стоимость филамента', - energy: 'Электроэнергия', - totalCost: 'Общая стоимость', - total: 'Итого', - includesBom: 'включая спецификацию', - budget: 'Бюджет', - remaining: 'Остаток', + title: "Учёт стоимости", + filamentCost: "Стоимость филамента", + energy: "Электроэнергия", + totalCost: "Общая стоимость", + total: "Итого", + includesBom: "включая спецификацию", + budget: "Бюджет", + remaining: "Остаток", }, subProjects: { - title: 'Подпроекты ({{count}})', + title: "Подпроекты ({{count}})", }, notes: { - title: 'Заметки', - noEditPermission: 'У вас нет прав на изменение заметок', - placeholder: 'Добавьте заметки о проекте...', - empty: 'Заметок пока нет. Нажмите «Изменить», чтобы добавить.', + title: "Заметки", + noEditPermission: "У вас нет прав на изменение заметок", + placeholder: "Добавьте заметки о проекте...", + empty: "Заметок пока нет. Нажмите «Изменить», чтобы добавить.", }, files: { - title: 'Файлы', - linkFolders: 'Свяжите папки из файлового менеджера', - forQuickAccess: 'с этим проектом для быстрого доступа.', - fileCount: 'Файлов: {{count}}', - empty: 'Связанных папок нет. Откройте файловый менеджер и свяжите папку с проектом.', - noFiles: 'В этой папке нет файлов.', + title: "Файлы", + linkFolders: "Свяжите папки из файлового менеджера", + forQuickAccess: "с этим проектом для быстрого доступа.", + fileCount: "Файлов: {{count}}", + empty: "Связанных папок нет. Откройте файловый менеджер и свяжите папку с проектом.", + noFiles: "В этой папке нет файлов.", }, bom: { - title: 'Спецификация', - acquired: 'Приобретено: {{completed}}/{{total}}', - showAll: 'Показать всё', - hideDone: 'Скрыть готовое', - addPart: 'Добавить позицию', - noAddPermission: 'У вас нет прав на добавление позиций', - partNamePlaceholder: 'Название позиции (например, винты M3×8)', - partName: 'Название позиции', - qty: 'Кол-во', - price: 'Цена ({{currency}})', - sourcingUrlPlaceholder: 'Ссылка на поставщика (необязательно)', - remarksPlaceholder: 'Примечание (необязательно)', - deletePart: 'Удалить позицию', - deleteConfirm: 'Удалить «{{name}}»?', - noUpdatePermission: 'У вас нет прав на обновление позиций', - noEditPermission: 'У вас нет прав на изменение позиций', - noDeletePermission: 'У вас нет прав на удаление позиций', - totalCost: 'Общая стоимость:', - empty: 'В спецификации пока нет позиций. Добавьте крепёж, электронику и другие компоненты, закупку которых нужно отслеживать.', + title: "Спецификация", + acquired: "Приобретено: {{completed}}/{{total}}", + showAll: "Показать всё", + hideDone: "Скрыть готовое", + addPart: "Добавить позицию", + noAddPermission: "У вас нет прав на добавление позиций", + partNamePlaceholder: "Название позиции (например, винты M3×8)", + partName: "Название позиции", + qty: "Кол-во", + price: "Цена ({{currency}})", + sourcingUrlPlaceholder: "Ссылка на поставщика (необязательно)", + remarksPlaceholder: "Примечание (необязательно)", + deletePart: "Удалить позицию", + deleteConfirm: "Удалить «{{name}}»?", + noUpdatePermission: "У вас нет прав на обновление позиций", + noEditPermission: "У вас нет прав на изменение позиций", + noDeletePermission: "У вас нет прав на удаление позиций", + totalCost: "Общая стоимость:", + empty: "В спецификации пока нет позиций. Добавьте крепёж, электронику и другие компоненты, закупку которых нужно отслеживать.", }, timeline: { - title: 'Хронология активности', - empty: 'Активности пока нет.', + title: "Хронология активности", + empty: "Активности пока нет.", }, template: { - saveAsTemplate: 'Сохранить как шаблон', - noCreatePermission: 'У вас нет прав на создание шаблонов', + saveAsTemplate: "Сохранить как шаблон", + noCreatePermission: "У вас нет прав на создание шаблонов", }, queue: { - title: 'Очередь', - viewAll: 'Показать всё', - printing: 'Печатается: {{count}}', - queued: 'В очереди: {{count}}', + title: "Очередь", + viewAll: "Показать всё", + printing: "Печатается: {{count}}", + queued: "В очереди: {{count}}", }, prints: { - title: 'Печати ({{count}})', + title: "Печати ({{count}})", }, toast: { - projectUpdated: 'Проект обновлён', - partAdded: 'Позиция добавлена', - partRemoved: 'Позиция удалена', - exportFailed: 'Не удалось экспортировать', - projectExported: 'Проект экспортирован', - templateCreated: 'Шаблон создан', + projectUpdated: "Проект обновлён", + partAdded: "Позиция добавлена", + partRemoved: "Позиция удалена", + exportFailed: "Не удалось экспортировать", + projectExported: "Проект экспортирован", + templateCreated: "Шаблон создан", }, }, system: { - title: 'Информация о системе', - version: 'Версия', - uptime: 'Время работы', - cpuUsage: 'Загрузка процессора', - memoryUsage: 'Использование памяти', - diskUsage: 'Использование диска', - networkInfo: 'Сетевая информация', - logs: 'Журналы', - debugMode: 'Режим отладки', - enableDebug: 'Включить журналирование отладки', - disableDebug: 'Отключить журналирование отладки', - downloadLogs: 'Скачать журналы', - clearLogs: 'Очистить журналы', - dockerInfo: 'Информация о Docker', - containerName: 'Имя контейнера', - imageName: 'Имя образа', - platform: 'Платформа', - architecture: 'Архитектура', + title: "Информация о системе", + version: "Версия", + uptime: "Время работы", + cpuUsage: "Загрузка процессора", + memoryUsage: "Использование памяти", + diskUsage: "Использование диска", + networkInfo: "Сетевая информация", + logs: "Журналы", + debugMode: "Режим отладки", + enableDebug: "Включить журналирование отладки", + disableDebug: "Отключить журналирование отладки", + downloadLogs: "Скачать журналы", + clearLogs: "Очистить журналы", + dockerInfo: "Информация о Docker", + containerName: "Имя контейнера", + imageName: "Имя образа", + platform: "Платформа", + architecture: "Архитектура", }, sponsors: { - sectionTitle: 'Независимый проект на поддержке сообщества', - tagline: 'Bambuddy бесплатен и остаётся таким благодаря добровольной поддержке пользователей. Без венчурного капитала и привязки к облаку.', - viewSupporters: 'Посмотреть сторонников проекта', - toastPrints: 'С Bambuddy выполнено печатей: {{count}}. Проект остаётся бесплатным благодаря поддержке сообщества.', - toastCost: 'С помощью Bambuddy учтено филамента на сумму {{total}}. Посмотрите, кто помогает проекту оставаться независимым.', - toastArchives: 'С Bambuddy архивировано печатей: {{count}}. Посмотрите, кто помогает проекту оставаться независимым.', - toastAnniversary: 'Вы уже год с Bambuddy! Посмотрите, кто помогает проекту оставаться независимым.', - toastVersionUpdate: 'Обновлено до версии {{version}}. Bambuddy остаётся бесплатным благодаря поддержке сообщества.', + sectionTitle: "Независимый проект на поддержке сообщества", + tagline: "Bambuddy бесплатен и остаётся таким благодаря добровольной поддержке пользователей. Без венчурного капитала и привязки к облаку.", + viewSupporters: "Посмотреть сторонников проекта", + toastPrints: "С Bambuddy выполнено печатей: {{count}}. Проект остаётся бесплатным благодаря поддержке сообщества.", + toastCost: "С помощью Bambuddy учтено филамента на сумму {{total}}. Посмотрите, кто помогает проекту оставаться независимым.", + toastArchives: "С Bambuddy архивировано печатей: {{count}}. Посмотрите, кто помогает проекту оставаться независимым.", + toastAnniversary: "Вы уже год с Bambuddy! Посмотрите, кто помогает проекту оставаться независимым.", + toastVersionUpdate: "Обновлено до версии {{version}}. Bambuddy остаётся бесплатным благодаря поддержке сообщества.", + toastBusiness: "Используете Bambuddy для {{count}} принтеров? Для команд доступен план поддержки с приоритетным исправлением ошибок, выставлением счетов и прямой связью с разработчиком.", + businessCta: "Bambuddy для бизнеса", + businessTitle: "Bambuddy для бизнеса", + businessTagline: "Вы используете {{count}} принтеров. Для команд и печатных ферм доступны приоритетная поддержка, коммерческая лицензия и выставление счетов.", }, library: { - title: 'Библиотека филаментов', - addFilament: 'Добавить филамент', - editFilament: 'Изменить филамент', - deleteFilament: 'Удалить филамент', - vendor: 'Производитель', - material: 'Материал', - color: 'Цвет', - kFactor: 'Коэффициент K', - temperature: 'Температура', - noFilaments: 'В библиотеке нет филаментов', - deleteConfirm: 'Удалить этот филамент?', - importFromPrinter: 'Импортировать с принтера', - exportToFile: 'Экспортировать в файл', + title: "Библиотека филаментов", + addFilament: "Добавить филамент", + editFilament: "Изменить филамент", + deleteFilament: "Удалить филамент", + vendor: "Производитель", + material: "Материал", + color: "Цвет", + kFactor: "Коэффициент K", + temperature: "Температура", + noFilaments: "В библиотеке нет филаментов", + deleteConfirm: "Удалить этот филамент?", + importFromPrinter: "Импортировать с принтера", + exportToFile: "Экспортировать в файл", runWithPipeline: { - actionLabel: 'Запустить через конвейер', - noPermission: 'У вас нет прав на запуск конвейеров', - modalTitle: 'Запуск через конвейер', - confirmTitle: 'Подтверждение запуска', - confirmIntro: 'Предварительная проверка обнаружила проблемы', - sourceHint: 'Источник', - pipelineHint: 'Конвейер', - targetHint: 'Назначение', - pipelineListAria: 'Доступные конвейеры', - runAnyway: 'Всё равно запустить', - loading: 'Загрузка…', - empty: 'Сохранённых конвейеров пока нет. Откройте диалог нарезки и нажмите «Сохранить как конвейер».', - noTarget: 'Целевой принтер не задан', - noTargetMessage: 'Для этого конвейера не задан целевой принтер. Откройте его в настройках и выберите принтер.', - copies: 'Копии', - copiesHint: 'макс. {{n}}', - classTarget: 'Любой {{model}}', + actionLabel: "Запустить через конвейер", + noPermission: "У вас нет прав на запуск конвейеров", + modalTitle: "Запуск через конвейер", + confirmTitle: "Подтверждение запуска", + confirmIntro: "Предварительная проверка обнаружила проблемы", + sourceHint: "Источник", + pipelineHint: "Конвейер", + targetHint: "Назначение", + pipelineListAria: "Доступные конвейеры", + runAnyway: "Всё равно запустить", + loading: "Загрузка…", + empty: "Сохранённых конвейеров пока нет. Откройте диалог нарезки и нажмите «Сохранить как конвейер».", + noTarget: "Целевой принтер не задан", + noTargetMessage: "Для этого конвейера не задан целевой принтер. Откройте его в настройках и выберите принтер.", + copies: "Копии", + copiesHint: "макс. {{n}}", + classTarget: "Любой {{model}}", toast: { - started: 'Конвейер запущен', - failed: 'Не удалось запустить конвейер', + started: "Конвейер запущен", + failed: "Не удалось запустить конвейер", }, issue: { - printerNotSet: 'Для этого конвейера не задан целевой принтер.', - printerNotFound: 'Целевой принтер больше не существует.', - printerDisabled: 'Целевой принтер отключён.', - printerOffline: 'Целевой принтер не в сети.', - filamentType: 'Слот филамента {{slot}}: ожидается {{expected}}, в AMS установлен {{actual}}', - filamentColor: 'Слот филамента {{slot}}: цвет отличается (ожидается {{expected}}, в AMS установлен {{actual}})', - amsSlotMissing: 'Слот AMS {{slot}} недоступен на этом принтере', - filamentUnverified: 'Филамент в слоте {{slot}} выбран из облачного или стандартного профиля и не может быть проверен заранее.', - noClassMatches: 'Ни один принтер в этой установке не соответствует целевому классу модели конвейера ({{expected}}).', - classNotSet: 'В качестве цели конвейера выбран класс принтера, но модель не указана.', + printerNotSet: "Для этого конвейера не задан целевой принтер.", + printerNotFound: "Целевой принтер больше не существует.", + printerDisabled: "Целевой принтер отключён.", + printerOffline: "Целевой принтер не в сети.", + filamentType: "Слот филамента {{slot}}: ожидается {{expected}}, в AMS установлен {{actual}}", + filamentColor: "Слот филамента {{slot}}: цвет отличается (ожидается {{expected}}, в AMS установлен {{actual}})", + amsSlotMissing: "Слот AMS {{slot}} недоступен на этом принтере", + filamentUnverified: "Филамент в слоте {{slot}} выбран из облачного или стандартного профиля и не может быть проверен заранее.", + noClassMatches: "Ни один принтер в этой установке не соответствует целевому классу модели конвейера ({{expected}}).", + classNotSet: "В качестве цели конвейера выбран класс принтера, но модель не указана.", }, }, }, slice: { - title: 'Нарезка модели', - action: 'Нарезать', - actionAll: 'Нарезать все пластины ({{count}})', - actionAllTitle: 'Нарезать все пластины в один многостольный результат и сохранить в одном архиве. Выбор филаментов охватывает все слоты, используемые проектом.', - allPlatesToggle: 'Нарезать все пластины ({{count}})', - slicing: 'Нарезка…', - printer: 'Профиль принтера', - process: 'Профиль процесса', - filament: 'Профиль филамента', - filamentSlot: 'Филамент {{index}} ({{type}})', - selectPreset: '— Выберите профиль —', - loadingPresets: 'Загрузка профилей…', - analyzingPlateFilaments: 'Анализ филаментов пластины…', - analyzingPlateFilamentsHint: 'Выполняется предварительная нарезка, чтобы определить, какие слоты AMS используются на этой пластине. Результат кэшируется, поэтому повторное открытие будет мгновенным.', - previewToast: 'Анализ {{name}} — {{elapsed}}', - previewWithProgress: 'Анализ {{name}} — {{stage}} ({{percent}}%) — {{elapsed}}', - notUsedByPlate: '— не используется на этой пластине', - noPresetsForSlot: 'Нет доступных профилей', - otherPrinters: 'Другие принтеры', - presetsLoadFailed: 'Не удалось загрузить профили. Сначала импортируйте их в разделе «Настройки → Профили».', - refreshPresets: 'Обновить', - refreshPresetsTitle: 'Обновить профили — получить свежие облачные и встроенные списки. Используйте после удаления профиля в Bambu Studio или Bambu Handy.', - allPresetsRequired: 'Необходимо выбрать все профили', - enqueuing: 'Отправка задания на нарезку…', - queued: 'В очереди…', - failed: 'Ошибка нарезки. Проверьте журналы вспомогательного сервиса слайсера.', - startedToast: 'Фоновая нарезка {{name}}…', - queuedToast: 'В очереди: {{name}} — {{elapsed}}', - runningToast: 'Нарезка {{name}} — {{elapsed}}', - runningWithProgress: '{{name}} — {{stage}} ({{percent}}%) — {{elapsed}}', - runningWithProgressMultiPlate: 'Пластина {{plateIndex}} из {{plateCount}} • {{name}} — {{stage}} ({{percent}}%) — {{elapsed}}', - completedToast: 'Нарезка {{name}} завершена', - failedTitle: 'Ошибка нарезки', - failedToast: 'Не удалось нарезать {{name}}: {{detail}}', + title: "Нарезка модели", + action: "Нарезать", + actionAll: "Нарезать все пластины ({{count}})", + actionAllTitle: "Нарезать все пластины в один многостольный результат и сохранить в одном архиве. Выбор филаментов охватывает все слоты, используемые проектом.", + allPlatesToggle: "Нарезать все пластины ({{count}})", + slicing: "Нарезка…", + printer: "Профиль принтера", + process: "Профиль процесса", + filament: "Профиль филамента", + filamentSlot: "Филамент {{index}} ({{type}})", + selectPreset: "— Выберите профиль —", + loadingPresets: "Загрузка профилей…", + analyzingPlateFilaments: "Анализ филаментов пластины…", + analyzingPlateFilamentsHint: "Выполняется предварительная нарезка, чтобы определить, какие слоты AMS используются на этой пластине. Результат кэшируется, поэтому повторное открытие будет мгновенным.", + previewToast: "Анализ {{name}} — {{elapsed}}", + previewWithProgress: "Анализ {{name}} — {{stage}} ({{percent}}%) — {{elapsed}}", + notUsedByPlate: "— не используется на этой пластине", + noPresetsForSlot: "Нет доступных профилей", + otherPrinters: "Другие принтеры", + presetsLoadFailed: "Не удалось загрузить профили. Сначала импортируйте их в разделе «Настройки → Профили».", + refreshPresets: "Обновить", + refreshPresetsTitle: "Обновить профили — получить свежие облачные и встроенные списки. Используйте после удаления профиля в Bambu Studio или Bambu Handy.", + allPresetsRequired: "Необходимо выбрать все профили", + enqueuing: "Отправка задания на нарезку…", + queued: "В очереди…", + failed: "Ошибка нарезки. Проверьте журналы вспомогательного сервиса слайсера.", + startedToast: "Фоновая нарезка {{name}}…", + queuedToast: "В очереди: {{name}} — {{elapsed}}", + runningToast: "Нарезка {{name}} — {{elapsed}}", + runningWithProgress: "{{name}} — {{stage}} ({{percent}}%) — {{elapsed}}", + runningWithProgressMultiPlate: "Пластина {{plateIndex}} из {{plateCount}} • {{name}} — {{stage}} ({{percent}}%) — {{elapsed}}", + completedToast: "Нарезка {{name}} завершена", + failedTitle: "Ошибка нарезки", + failedToast: "Не удалось нарезать {{name}}: {{detail}}", tier: { - local: 'Импортированные', - cloud: 'Bambu Cloud', - orcaCloud: 'Orca Cloud', - standard: 'Стандартные', + local: "Импортированные", + cloud: "Bambu Cloud", + orcaCloud: "Orca Cloud", + standard: "Стандартные", }, cloud: { - notAuthenticated: 'Войдите в Bambu Cloud («Настройки → Профили → Bambu Cloud»), чтобы увидеть облачные профили.', - expired: 'Сессия Bambu Cloud истекла. Войдите снова, чтобы обновить облачные профили.', - unreachable: 'Bambu Cloud сейчас недоступен. Локальные и стандартные профили продолжат работать.', + notAuthenticated: "Войдите в Bambu Cloud («Настройки → Профили → Bambu Cloud»), чтобы увидеть облачные профили.", + expired: "Сессия Bambu Cloud истекла. Войдите снова, чтобы обновить облачные профили.", + unreachable: "Bambu Cloud сейчас недоступен. Локальные и стандартные профили продолжат работать.", }, orcaCloud: { - notAuthenticated: 'Войдите в Orca Cloud («Профили → Orca Cloud»), чтобы увидеть профили Orca.', - expired: 'Сессия Orca Cloud истекла. Войдите снова, чтобы обновить профили Orca.', - unreachable: 'Orca Cloud сейчас недоступен. Остальные профили продолжат работать.', + notAuthenticated: "Войдите в Orca Cloud («Профили → Orca Cloud»), чтобы увидеть профили Orca.", + expired: "Сессия Orca Cloud истекла. Войдите снова, чтобы обновить профили Orca.", + unreachable: "Orca Cloud сейчас недоступен. Остальные профили продолжат работать.", }, bedType: { - label: 'Печатная пластина', - auto: 'Авто (из профиля процесса)', - coolPlate: 'Холодная пластина', - coolPlateSuperTack: 'Холодная пластина SuperTack', - engineering: 'Инженерная пластина', - highTemp: 'Высокотемпературная пластина', - texturedPEI: 'Текстурированная PEI-пластина', - smoothPEI: 'Гладкая PEI-пластина', + label: "Печатная пластина", + auto: "Авто (из профиля процесса)", + coolPlate: "Холодная пластина", + coolPlateSuperTack: "Холодная пластина SuperTack", + engineering: "Инженерная пластина", + highTemp: "Высокотемпературная пластина", + texturedPEI: "Текстурированная PEI-пластина", + smoothPEI: "Гладкая PEI-пластина", }, pipelines: { - label: 'Конвейер', - applyAria: 'Применить конвейер', - applyPrompt: 'Применить конвейер…', - empty: 'Нет сохранённых конвейеров', - saveButton: 'Сохранить как конвейер', - saveTitle: 'Сохранить текущий набор из четырёх профилей как повторно используемый конвейер', - namePlaceholder: 'Название конвейера', - nameAria: 'Название нового конвейера', + label: "Конвейер", + applyAria: "Применить конвейер", + applyPrompt: "Применить конвейер…", + empty: "Нет сохранённых конвейеров", + saveButton: "Сохранить как конвейер", + saveTitle: "Сохранить текущий набор из четырёх профилей как повторно используемый конвейер", + namePlaceholder: "Название конвейера", + nameAria: "Название нового конвейера", toast: { - applied: 'Применён конвейер «{{name}}»', - saved: 'Конвейер сохранён', - saveFailed: 'Не удалось сохранить', + applied: "Применён конвейер «{{name}}»", + saved: "Конвейер сохранён", + saveFailed: "Не удалось сохранить", }, }, }, spoolman: { - title: 'Интеграция со Spoolman', - enabled: 'Spoolman включён', - url: 'URL Spoolman', - connected: 'Подключено', - disconnected: 'Нет подключения', - testConnection: 'Проверить подключение', - sync: 'Синхронизировать', - syncing: 'Синхронизация…', - lastSync: 'Последняя синхронизация', - linkToSpoolman: 'Связать со Spoolman', - openInSpoolman: 'Открыть в Spoolman', - unlinkSpool: 'Отвязать катушку', - unlinkConfirmTitle: 'Снять назначение катушки?', - unlinkConfirmMessage: 'Катушка будет удалена из этого слота. Данные самой катушки останутся без изменений.', - selectSpool: 'Выбрать катушку', - noUnlinkedSpools: 'Нет свободных катушек', - linkSuccess: 'Катушка успешно назначена', - linkFailed: 'Не удалось назначить катушку', - unlinkSuccess: 'Назначение катушки снято', - unlinkFailed: 'Не удалось снять назначение катушки', - linkedSpool: 'Назначенная катушка', - spoolId: 'ID катушки', - fillSourceLabel: '(Spoolman)', - weight: 'Масса', - remaining: 'Остаток', - disableWeightSync: 'Отключить синхронизацию расчётной массы AMS', - disableWeightSyncDesc: 'Не обновлять остаток по оценке AMS. Используйте это, если предпочитаете учёт расхода Spoolman процентной оценке AMS. Для новых катушек оценка AMS всё равно будет использована как начальная масса.', - reportPartialUsage: 'Учитывать частичный расход при неудачной печати', - reportPartialUsageDesc: 'Если печать завершилась ошибкой или была отменена, учитывать примерный расход филамента до этого момента по прогрессу слоёв.', + title: "Интеграция со Spoolman", + enabled: "Spoolman включён", + url: "URL Spoolman", + connected: "Подключено", + disconnected: "Нет подключения", + testConnection: "Проверить подключение", + sync: "Синхронизировать", + syncing: "Синхронизация…", + lastSync: "Последняя синхронизация", + linkToSpoolman: "Связать со Spoolman", + openInSpoolman: "Открыть в Spoolman", + unlinkSpool: "Отвязать катушку", + unlinkConfirmTitle: "Снять назначение катушки?", + unlinkConfirmMessage: "Катушка будет удалена из этого слота. Данные самой катушки останутся без изменений.", + selectSpool: "Выбрать катушку", + noUnlinkedSpools: "Нет свободных катушек", + linkSuccess: "Катушка успешно назначена", + linkFailed: "Не удалось назначить катушку", + unlinkSuccess: "Назначение катушки снято", + unlinkFailed: "Не удалось снять назначение катушки", + linkedSpool: "Назначенная катушка", + spoolId: "ID катушки", + fillSourceLabel: "(Spoolman)", + weight: "Масса", + remaining: "Остаток", + disableWeightSync: "Отключить синхронизацию расчётной массы AMS", + disableWeightSyncDesc: "Не обновлять остаток по оценке AMS. Используйте это, если предпочитаете учёт расхода Spoolman процентной оценке AMS. Для новых катушек оценка AMS всё равно будет использована как начальная масса.", + reportPartialUsage: "Учитывать частичный расход при неудачной печати", + reportPartialUsageDesc: "Если печать завершилась ошибкой или была отменена, учитывать примерный расход филамента до этого момента по прогрессу слоёв.", }, locations: { - title: 'Места хранения', - subtitle: 'Управление полками, ящиками и другими физическими местами хранения катушек', - add: 'Добавить место', - addShort: 'Добавить', - edit: 'Изменить место', - name: 'Название', - spools: 'Катушки', - empty: 'Мест хранения пока нет. Создайте первую полку или ящик.', - manage: 'Места хранения', - createPlaceholder: 'например, Полка A или Ящик 1', - nameRequired: 'Укажите название места хранения', - created: 'Место хранения создано', - updated: 'Место хранения обновлено', - deleted: 'Место хранения удалено', - saveFailed: 'Не удалось сохранить место хранения', - deleteFailed: 'Не удалось удалить место хранения', - deleteBlocked: 'Перед удалением переместите из этого места все катушки', - confirmDelete: 'Удалить «{{name}}»?', - confirmDeleteMessage: 'Это место будет удалено из каталога. Сначала необходимо переместить все катушки.', + title: "Места хранения", + subtitle: "Управление полками, ящиками и другими физическими местами хранения катушек", + add: "Добавить место", + addShort: "Добавить", + edit: "Изменить место", + name: "Название", + spools: "Катушки", + empty: "Мест хранения пока нет. Создайте первую полку или ящик.", + manage: "Места хранения", + createPlaceholder: "например, Полка A или Ящик 1", + nameRequired: "Укажите название места хранения", + created: "Место хранения создано", + updated: "Место хранения обновлено", + deleted: "Место хранения удалено", + saveFailed: "Не удалось сохранить место хранения", + deleteFailed: "Не удалось удалить место хранения", + deleteBlocked: "Перед удалением переместите из этого места все катушки", + confirmDelete: "Удалить «{{name}}»?", + confirmDeleteMessage: "Это место будет удалено из каталога. Сначала необходимо переместить все катушки.", }, inventory: { - title: 'Учёт катушек', - subtitle: 'Управление катушками', - addToInventory: 'Добавить в учёт', - addToInventoryPending: 'Добавление…', - addToInventorySuccess: 'Катушка добавлена в учёт', - addToInventoryFailed: 'Не удалось добавить катушку в учёт', - unknownSpoolTitle: 'Обнаружен новый филамент', - unknownSpoolMessage: 'В {{location}} обнаружена катушка с неизвестной RFID-меткой. Добавить её в учёт?', - unknownSpoolSlot: 'Слот', + title: "Учёт катушек", + subtitle: "Управление катушками", + addToInventory: "Добавить в учёт", + addToInventoryPending: "Добавление…", + addToInventorySuccess: "Катушка добавлена в учёт", + addToInventoryFailed: "Не удалось добавить катушку в учёт", + unknownSpoolTitle: "Обнаружен новый филамент", + unknownSpoolMessage: "В {{location}} обнаружена катушка с неизвестной RFID-меткой. Добавить её в учёт?", + unknownSpoolSlot: "Слот", bulk: { - selectAllVisible: 'Выбрать все видимые', - selectRow: 'Выбрать строку', - selectGroup: 'Выбрать группу', - selectionCount: 'Выбрано: {{count}}', - edit: 'Изменить', - printLabels: 'Печать этикеток', - resetUsage: 'Сбросить расход', - restore: 'Восстановить', - archive: 'Архивировать', - delete: 'Удалить', - clearSelection: 'Снять выделение', - editTitle: 'Массовое изменение катушек', - editSubtitle: 'Изменения применятся к {{count}} выбранным катушкам. Обновятся только отмеченные поля.', - editHint: 'Введите значение в поле, чтобы включить его обновление. Будут отправлены только отмеченные строки. Пустое поле не изменяет катушки; очистить значение можно только при редактировании отдельной катушки.', - useCustom: 'Использовать «{{value}}»', - toggleField: 'Включить или отключить обновление этого поля', - changeCount: 'Будет обновлено полей: {{count}}.', - applyPending: 'Применение…', - applyButton: 'Применить к {{count}} катушкам', - deleteTitle: 'Удалить выбранные катушки', - archiveTitle: 'Архивировать выбранные катушки', - restoreTitle: 'Восстановить выбранные катушки', - resetUsageTitle: 'Сбросить расход выбранных катушек', - deleteMessage: 'Безвозвратно удалить {{count}} катушек? Это действие нельзя отменить.', - archiveMessage: 'Архивировать {{count}} катушек? Позже их можно будет восстановить.', - restoreMessage: 'Восстановить {{count}} архивных катушек?', - resetUsageMessage: 'Сбросить счётчик «Израсходовано всего» у {{count}} катушек? Текущий остаток массы сохранится.', - updateSuccess: 'Обновлено катушек: {{count}}', - updateFailed: 'Ошибка массового обновления', - updatePartial: 'Обновлено: {{ok}}, с ошибкой: {{failed}}', - updateAllFailed: 'Не удалось обновить все {{count}} катушек. Выделение сохранено для повторной попытки.', - deleteSuccess: 'Удалено катушек: {{count}}', - deleteFailed: 'Ошибка массового удаления', - deletePartial: 'Удалено: {{ok}}, с ошибкой: {{failed}}', - deleteAllFailed: 'Не удалось удалить все {{count}} катушек. Выделение сохранено для повторной попытки.', - archiveSuccess: 'Архивировано катушек: {{count}}', - archiveFailed: 'Ошибка массового архивирования', - archivePartial: 'Архивировано: {{ok}}, с ошибкой: {{failed}}', - archiveAllFailed: 'Не удалось архивировать все {{count}} катушек. Выделение сохранено для повторной попытки.', - restoreSuccess: 'Восстановлено катушек: {{count}}', - restoreFailed: 'Ошибка массового восстановления', - restorePartial: 'Восстановлено: {{ok}}, с ошибкой: {{failed}}', - restoreAllFailed: 'Не удалось восстановить все {{count}} катушек. Выделение сохранено для повторной попытки.', - invalidHex: 'Введите 6 шестнадцатеричных символов (RRGGBB) или 8 (RRGGBBAA), иначе поле не будет применено.', + selectAllVisible: "Выбрать все видимые", + selectRow: "Выбрать строку", + selectGroup: "Выбрать группу", + selectionCount: "Выбрано: {{count}}", + edit: "Изменить", + printLabels: "Печать этикеток", + resetUsage: "Сбросить расход", + restore: "Восстановить", + archive: "Архивировать", + delete: "Удалить", + clearSelection: "Снять выделение", + editTitle: "Массовое изменение катушек", + editSubtitle: "Изменения применятся к {{count}} выбранным катушкам. Обновятся только отмеченные поля.", + editHint: "Введите значение в поле, чтобы включить его обновление. Будут отправлены только отмеченные строки. Пустое поле не изменяет катушки; очистить значение можно только при редактировании отдельной катушки.", + useCustom: "Использовать «{{value}}»", + toggleField: "Включить или отключить обновление этого поля", + changeCount: "Будет обновлено полей: {{count}}.", + applyPending: "Применение…", + applyButton: "Применить к {{count}} катушкам", + deleteTitle: "Удалить выбранные катушки", + archiveTitle: "Архивировать выбранные катушки", + restoreTitle: "Восстановить выбранные катушки", + resetUsageTitle: "Сбросить расход выбранных катушек", + deleteMessage: "Безвозвратно удалить {{count}} катушек? Это действие нельзя отменить.", + archiveMessage: "Архивировать {{count}} катушек? Позже их можно будет восстановить.", + restoreMessage: "Восстановить {{count}} архивных катушек?", + resetUsageMessage: "Сбросить счётчик «Израсходовано всего» у {{count}} катушек? Текущий остаток массы сохранится.", + updateSuccess: "Обновлено катушек: {{count}}", + updateFailed: "Ошибка массового обновления", + updatePartial: "Обновлено: {{ok}}, с ошибкой: {{failed}}", + updateAllFailed: "Не удалось обновить все {{count}} катушек. Выделение сохранено для повторной попытки.", + deleteSuccess: "Удалено катушек: {{count}}", + deleteFailed: "Ошибка массового удаления", + deletePartial: "Удалено: {{ok}}, с ошибкой: {{failed}}", + deleteAllFailed: "Не удалось удалить все {{count}} катушек. Выделение сохранено для повторной попытки.", + archiveSuccess: "Архивировано катушек: {{count}}", + archiveFailed: "Ошибка массового архивирования", + archivePartial: "Архивировано: {{ok}}, с ошибкой: {{failed}}", + archiveAllFailed: "Не удалось архивировать все {{count}} катушек. Выделение сохранено для повторной попытки.", + restoreSuccess: "Восстановлено катушек: {{count}}", + restoreFailed: "Ошибка массового восстановления", + restorePartial: "Восстановлено: {{ok}}, с ошибкой: {{failed}}", + restoreAllFailed: "Не удалось восстановить все {{count}} катушек. Выделение сохранено для повторной попытки.", + invalidHex: "Введите 6 шестнадцатеричных символов (RRGGBB) или 8 (RRGGBBAA), иначе поле не будет применено.", }, - spoolmanMixedContentTitle: 'Spoolman не загружается через HTTPS: браузер заблокировал смешанное содержимое', - spoolmanMixedContentBody: 'Bambuddy открыт через HTTPS (через обратный прокси), а URL Spoolman всё ещё использует обычный HTTP. Браузер блокирует такое смешанное содержимое, поэтому встроенный интерфейс Spoolman не отображается. Для работы Spoolman должен быть доступен по HTTPS.', - spoolmanMixedContentFixReverseProxy: 'Разместите Spoolman за тем же обратным прокси, что и Bambuddy (Traefik / Nginx / Caddy), включите HTTPS и укажите новый HTTPS-адрес Spoolman в настройках.', - spoolmanMixedContentFixOpenNewTab: 'В качестве обходного решения откройте Spoolman по HTTP в новой вкладке. Ограничение смешанного содержимого действует только для встроенных фреймов.', - spoolmanOpenInNewTab: 'Открыть Spoolman в новой вкладке', + spoolmanMixedContentTitle: "Spoolman не загружается через HTTPS: браузер заблокировал смешанное содержимое", + spoolmanMixedContentBody: "Bambuddy открыт через HTTPS (через обратный прокси), а URL Spoolman всё ещё использует обычный HTTP. Браузер блокирует такое смешанное содержимое, поэтому встроенный интерфейс Spoolman не отображается. Для работы Spoolman должен быть доступен по HTTPS.", + spoolmanMixedContentFixReverseProxy: "Разместите Spoolman за тем же обратным прокси, что и Bambuddy (Traefik / Nginx / Caddy), включите HTTPS и укажите новый HTTPS-адрес Spoolman в настройках.", + spoolmanMixedContentFixOpenNewTab: "В качестве обходного решения откройте Spoolman по HTTP в новой вкладке. Ограничение смешанного содержимого действует только для встроенных фреймов.", + spoolmanOpenInNewTab: "Открыть Spoolman в новой вкладке", labels: { - title: 'Печать этикеток катушек', - selectedCount: 'Выбрано: {{count}}', - pickSpools: 'Выберите катушки для печати этикеток:', - monochrome: 'Монохромный режим (чёрно-белый принтер)', - monochromeHint: 'Убирает образец цвета и расширяет область текста', - searchPlaceholder: 'Поиск по названию, бренду или #ID', - filterByMaterial: 'Материал:', - allMaterials: 'Все', - selectVisible: 'Выбрать все видимые ({{count}})', - deselectVisible: 'Снять выделение с видимых', - clearAll: 'Очистить выбор', - noSpoolsToShow: 'Нет катушек для отображения. Измените фильтр и повторите попытку.', - noMatches: 'Нет катушек, соответствующих поиску или фильтру.', - printOne: 'Напечатать этикетку этой катушки', - printLabels: 'Печать этикеток…', - bulkTitle: 'Выберите катушки для печати этикеток из {{count}} отображаемых', - noSpoolsTitle: 'Нет катушек для печати этикеток', - error: 'Не удалось создать этикетки: {{msg}}', + title: "Печать этикеток катушек", + selectedCount: "Выбрано: {{count}}", + pickSpools: "Выберите катушки для печати этикеток:", + monochrome: "Монохромный режим (чёрно-белый принтер)", + monochromeHint: "Убирает образец цвета и расширяет область текста", + searchPlaceholder: "Поиск по названию, бренду или #ID", + filterByMaterial: "Материал:", + allMaterials: "Все", + selectVisible: "Выбрать все видимые ({{count}})", + deselectVisible: "Снять выделение с видимых", + clearAll: "Очистить выбор", + noSpoolsToShow: "Нет катушек для отображения. Измените фильтр и повторите попытку.", + noMatches: "Нет катушек, соответствующих поиску или фильтру.", + printOne: "Напечатать этикетку этой катушки", + printLabels: "Печать этикеток…", + bulkTitle: "Выберите катушки для печати этикеток из {{count}} отображаемых", + noSpoolsTitle: "Нет катушек для печати этикеток", + error: "Не удалось создать этикетки: {{msg}}", sortBy: { - label: 'Сортировка:', - id: 'По ID', - color: 'По цвету', + label: "Сортировка:", + id: "По ID", + color: "По цвету", }, templates: { amsHolderSmall: { - label: 'Держатель AMS — малый (74 × 33 мм)', - hint: 'Одна этикетка на страницу; соответствует печатной этикетке для модели MakerWorld 752566 (AMS Filament Label Holder).', + label: "Держатель AMS — малый (74 × 33 мм)", + hint: "Одна этикетка на страницу; соответствует печатной этикетке для модели MakerWorld 752566 (AMS Filament Label Holder).", }, amsHolderLarge: { - label: 'Держатель AMS — большой (75 × 55 мм)', - hint: 'Одна этикетка на страницу; подходит для варианта держателя AMS с картонной вставкой. Достаточно места для образца цвета, бренда, материала, ID и QR-кода.', + label: "Держатель AMS — большой (75 × 55 мм)", + hint: "Одна этикетка на страницу; подходит для варианта держателя AMS с картонной вставкой. Достаточно места для образца цвета, бренда, материала, ID и QR-кода.", }, box40x30: { - label: 'Этикетка для коробки (40 × 30 мм)', - hint: 'Одна этикетка на страницу; распространённый размер рулона DK/Brother, подходит для пакетов с филаментом и контейнеров хранения.', + label: "Этикетка для коробки (40 × 30 мм)", + hint: "Одна этикетка на страницу; распространённый размер рулона DK/Brother, подходит для пакетов с филаментом и контейнеров хранения.", }, box: { - label: 'Этикетка для коробки (62 × 29 мм)', - hint: 'Одна этикетка на страницу; размер для малых этикеток Brother PT/QL и Dymo.', + label: "Этикетка для коробки (62 × 29 мм)", + hint: "Одна этикетка на страницу; размер для малых этикеток Brother PT/QL и Dymo.", }, averyL7160: { - label: 'Avery L7160 — лист A4 (38,1 × 63,5 мм × 21)', - hint: 'Европейский формат: 21 этикетка на листе A4.', + label: "Avery L7160 — лист A4 (38,1 × 63,5 мм × 21)", + hint: "Европейский формат: 21 этикетка на листе A4.", }, avery5160: { - label: 'Avery 5160 — лист US Letter (25,4 × 66,7 мм × 30)', - hint: 'Американский формат: 30 этикеток на листе Letter.', + label: "Avery 5160 — лист US Letter (25,4 × 66,7 мм × 30)", + hint: "Американский формат: 30 этикеток на листе Letter.", }, }, }, csv: { - importButton: 'Импорт CSV', - exportButton: 'Экспорт CSV', - modalTitle: 'Импорт катушек из CSV', - selectFile: 'Выберите CSV-файл или перетащите его сюда', - dragHint: 'Заголовок: material (обязательно), brand, subtype, color_name, rgba, …', - parsing: 'Чтение файла…', - previewError: 'Не удалось прочитать CSV-файл', - validCount: 'Корректных: {{count}}', - errorCount: 'Ошибок: {{count}}', - skippedCount: 'Пропущено: {{count}}', - colRow: 'Строка', - colStatus: 'Состояние', - colColor: 'Цвет', - colorResolved: 'Цвет подставлен из каталога', - colorCrossMaterial: 'Цвет взят у другого материала: точного совпадения в каталоге нет', - duplicateExisting: 'Катушка с таким материалом, брендом и цветом уже существует, но запись всё равно будет импортирована как новая катушка', - spoolmanHint: 'В режиме Spoolman используйте встроенный импорт и экспорт CSV в Spoolman.', - importValidRows: 'Импортировать {{count}} корректных строк', - noValidRows: 'Нет корректных строк', - importing: 'Импорт…', - importSuccess: 'Импортировано катушек: {{count}}', - importError: 'Ошибка импорта', - exportError: 'Ошибка экспорта', + importButton: "Импорт CSV", + exportButton: "Экспорт CSV", + modalTitle: "Импорт катушек из CSV", + selectFile: "Выберите CSV-файл или перетащите его сюда", + dragHint: "Заголовок: material (обязательно), brand, subtype, color_name, rgba, …", + parsing: "Чтение файла…", + previewError: "Не удалось прочитать CSV-файл", + validCount: "Корректных: {{count}}", + errorCount: "Ошибок: {{count}}", + skippedCount: "Пропущено: {{count}}", + colRow: "Строка", + colStatus: "Состояние", + colColor: "Цвет", + colorResolved: "Цвет подставлен из каталога", + colorCrossMaterial: "Цвет взят у другого материала: точного совпадения в каталоге нет", + duplicateExisting: "Катушка с таким материалом, брендом и цветом уже существует, но запись всё равно будет импортирована как новая катушка", + spoolmanHint: "В режиме Spoolman используйте встроенный импорт и экспорт CSV в Spoolman.", + importValidRows: "Импортировать {{count}} корректных строк", + noValidRows: "Нет корректных строк", + importing: "Импорт…", + importSuccess: "Импортировано катушек: {{count}}", + importError: "Ошибка импорта", + exportError: "Ошибка экспорта", }, - addSpool: 'Добавить катушку', - editSpool: 'Изменить катушку', - copySpool: 'Копировать катушку', - material: 'Материал', - selectMaterial: 'Выберите материал…', - subtype: 'Подтип', - brand: 'Бренд', - searchBrand: 'Поиск бренда…', - useCustomBrand: 'Использовать «{{brand}}»', - useCustomMaterial: 'Использовать свой материал: {{material}}', - colorName: 'Название цвета', - colorNamePlaceholder: 'Jade White, Fire Red…', - color: 'Цвет', - hexColor: 'Цвет HEX', - pickColor: 'Выбрать свой цвет', - labelWeight: 'Номинальная масса', - coreWeight: 'Масса пустой катушки', - searchSpoolWeight: 'Поиск массы катушки…', - weightUsed: 'Израсходовано', - currentWeight: 'Остаток массы', - measuredWeight: 'Измеренная масса', - spoolName: 'Катушка', - costPerKg: 'Стоимость за кг', - storageLocation: 'Место хранения', - storageLocationPlaceholder: 'например, Полка A, Ящик 1', - openInInventory: 'Открыть в учёте', - measuredWeightError: 'Измеренная масса должна быть от {{min}} до {{max}} г.', - slicerFilament: 'Филамент слайсера', - slicerFilamentName: 'Название пресета слайсера', - slicerPreset: 'Пресет слайсера', - searchPresets: 'Поиск пресетов филамента…', - selectedPreset: 'Выбрано', - noPresetsFound: 'Пресеты не найдены', - tempOverrides: 'Переопределение температур', - note: 'Примечание', - notePlaceholder: 'Дополнительные сведения об этой катушке…', - category: 'Категория', - categoryPlaceholder: 'например, Производство, Прототип, Клиент A', - categoryNone: 'Без категории', - storageLocationNone: 'Место не указано', - lowStockThresholdOverride: 'Порог малого остатка (эта катушка)', - lowStockThresholdOverrideHelp: 'Оставьте пустым, чтобы использовать общий порог ({{global}}%).', - clearRfid: 'Очистить RFID-метку', - rfidCleared: 'RFID-метка очищена', - archive: 'Архивировать', - restore: 'Восстановить', - noSpools: 'Катушек пока нет. Добавьте первую катушку.', - noAvailableSpools: 'Нет доступных катушек. Добавьте катушку в учёт или сначала освободите её в другом слоте.', - kProfiles: 'K-профили', - addKProfile: 'Добавить K-профиль', - assignSpool: 'Назначить катушку', - unassignSpool: 'Снять назначение', - assignSuccess: 'Катушка назначена, слот AMS настроен', - assignPendingInsert: 'Катушка назначена. Слот будет настроен после её установки.', - assignFailed: 'Не удалось назначить катушку', - selectSpool: 'Выберите катушку для назначения этому слоту', - assigned: 'Назначено', - assigning: 'Назначение…', - searchSpools: 'Поиск катушек…', - showAllSpools: 'Показать все катушки', - spoolmanSpools: 'Катушки Spoolman', - allMaterials: 'Все материалы', - filterByBrand: 'Фильтр по бренду…', - showArchived: 'Показывать архивные', - quickAdd: 'Быстрое добавление', - quantity: 'Количество', - stock: 'На складе', - configured: 'Настроено', - spoolsCreated: 'Создано катушек: {{count}}', - spoolsPartiallyCreated: 'Создано {{created}} из {{total}} катушек; часть операций завершилась ошибкой', - spoolCreated: 'Катушка создана', - spoolUpdated: 'Катушка обновлена', - spoolDeleted: 'Катушка удалена', - deepLinkSpoolNotFound: 'Катушка не найдена', - deepLinkFetchFailed: 'Не удалось загрузить катушку. Повторите попытку.', - spoolArchived: 'Катушка архивирована', - spoolRestored: 'Катушка восстановлена', - kProfileSaveFailed: 'Не удалось сохранить настройки K-профиля', - syncWeightSpoolNotFound: 'Катушка не найдена: возможно, она была удалена', - syncWeightSpoolmanUnreachable: 'Spoolman недоступен. Повторите попытку позже.', - syncWeightFailed: 'Не удалось синхронизировать массу', - spoolmanUnreachable: 'Spoolman недоступен. Повторите попытку позже.', - deleteSpoolNotFound: 'Катушка не найдена: возможно, она уже удалена', - deleteFailed: 'Не удалось удалить катушку', - archiveSpoolNotFound: 'Катушка не найдена: возможно, она уже удалена', - archiveFailed: 'Не удалось архивировать катушку', - restoreSpoolNotFound: 'Катушка не найдена: возможно, она уже удалена', - restoreFailed: 'Не удалось восстановить катушку', - saveFailed: 'Не удалось сохранить изменения', - tagClearFailed: 'Не удалось очистить метку', - deleteConfirm: 'Удалить эту катушку без возможности восстановления?', - archiveConfirm: 'Архивировать эту катушку?', - advancedSettings: 'Расширенные настройки', - filamentInfoTab: 'Сведения о филаменте', - paProfileTab: 'Профиль PA', - filamentInfo: 'Филамент', - additional: 'Дополнительно', - loadingPresets: 'Загрузка облачных пресетов…', - cloudConnected: 'Облако подключено', - cloudNotConnected: 'Облако не подключено; используются значения по умолчанию', - recentColors: 'Недавние', - searchColors: 'Поиск цветов…', - searchResults: 'Результаты поиска', - allColors: 'Все цвета', - commonColors: 'Основные цвета', - showLess: 'Показать меньше', - showAll: 'Показать все', - noColorsFound: 'Нет цветов, соответствующих запросу', - noResults: 'Совпадений не найдено', - extraColorsLabel: 'Дополнительные цвета', - extraColorsPlaceholder: 'EC984C,#6CD4BC,A66EB9,D87694', - extraColorsHint: 'Вставьте от 2 до 8 HEX-цветов через запятую. Они будут отображены как градиент.', - extraColorsInvalid: 'Пропущены некорректные HEX-значения: {{tokens}}', - colorEffectLabel: 'Эффект', + addSpool: "Добавить катушку", + editSpool: "Изменить катушку", + copySpool: "Копировать катушку", + material: "Материал", + selectMaterial: "Выберите материал…", + subtype: "Подтип", + brand: "Бренд", + searchBrand: "Поиск бренда…", + useCustomBrand: "Использовать «{{brand}}»", + useCustomMaterial: "Использовать свой материал: {{material}}", + colorName: "Название цвета", + colorNamePlaceholder: "Jade White, Fire Red…", + color: "Цвет", + hexColor: "Цвет HEX", + pickColor: "Выбрать свой цвет", + labelWeight: "Номинальная масса", + coreWeight: "Масса пустой катушки", + searchSpoolWeight: "Поиск массы катушки…", + weightUsed: "Израсходовано", + currentWeight: "Остаток массы", + measuredWeight: "Измеренная масса", + spoolName: "Катушка", + costPerKg: "Стоимость за кг", + storageLocation: "Место хранения", + storageLocationPlaceholder: "например, Полка A, Ящик 1", + openInInventory: "Открыть в учёте", + measuredWeightError: "Измеренная масса должна быть от {{min}} до {{max}} г.", + slicerFilament: "Филамент слайсера", + slicerFilamentName: "Название пресета слайсера", + slicerPreset: "Пресет слайсера", + searchPresets: "Поиск пресетов филамента…", + selectedPreset: "Выбрано", + noPresetsFound: "Пресеты не найдены", + tempOverrides: "Переопределение температур", + note: "Примечание", + notePlaceholder: "Дополнительные сведения об этой катушке…", + category: "Категория", + categoryPlaceholder: "например, Производство, Прототип, Клиент A", + categoryNone: "Без категории", + storageLocationNone: "Место не указано", + lowStockThresholdOverride: "Порог малого остатка (эта катушка)", + lowStockThresholdOverrideHelp: "Оставьте пустым, чтобы использовать общий порог ({{global}}%).", + clearRfid: "Очистить RFID-метку", + rfidCleared: "RFID-метка очищена", + archive: "Архивировать", + restore: "Восстановить", + noSpools: "Катушек пока нет. Добавьте первую катушку.", + noAvailableSpools: "Нет доступных катушек. Добавьте катушку в учёт или сначала освободите её в другом слоте.", + kProfiles: "K-профили", + addKProfile: "Добавить K-профиль", + assignSpool: "Назначить катушку", + unassignSpool: "Снять назначение", + assignSuccess: "Катушка назначена, слот AMS настроен", + assignPendingInsert: "Катушка назначена. Слот будет настроен после её установки.", + assignFailed: "Не удалось назначить катушку", + selectSpool: "Выберите катушку для назначения этому слоту", + assigned: "Назначено", + assigning: "Назначение…", + searchSpools: "Поиск катушек…", + showAllSpools: "Показать все катушки", + spoolmanSpools: "Катушки Spoolman", + allMaterials: "Все материалы", + filterByBrand: "Фильтр по бренду…", + showArchived: "Показывать архивные", + quickAdd: "Быстрое добавление", + quantity: "Количество", + stock: "На складе", + configured: "Настроено", + spoolsCreated: "Создано катушек: {{count}}", + spoolsPartiallyCreated: "Создано {{created}} из {{total}} катушек; часть операций завершилась ошибкой", + spoolCreated: "Катушка создана", + spoolUpdated: "Катушка обновлена", + spoolDeleted: "Катушка удалена", + deepLinkSpoolNotFound: "Катушка не найдена", + deepLinkFetchFailed: "Не удалось загрузить катушку. Повторите попытку.", + spoolArchived: "Катушка архивирована", + spoolRestored: "Катушка восстановлена", + kProfileSaveFailed: "Не удалось сохранить настройки K-профиля", + syncWeightSpoolNotFound: "Катушка не найдена: возможно, она была удалена", + syncWeightSpoolmanUnreachable: "Spoolman недоступен. Повторите попытку позже.", + syncWeightFailed: "Не удалось синхронизировать массу", + spoolmanUnreachable: "Spoolman недоступен. Повторите попытку позже.", + deleteSpoolNotFound: "Катушка не найдена: возможно, она уже удалена", + deleteFailed: "Не удалось удалить катушку", + archiveSpoolNotFound: "Катушка не найдена: возможно, она уже удалена", + archiveFailed: "Не удалось архивировать катушку", + restoreSpoolNotFound: "Катушка не найдена: возможно, она уже удалена", + restoreFailed: "Не удалось восстановить катушку", + saveFailed: "Не удалось сохранить изменения", + tagClearFailed: "Не удалось очистить метку", + deleteConfirm: "Удалить эту катушку без возможности восстановления?", + archiveConfirm: "Архивировать эту катушку?", + advancedSettings: "Расширенные настройки", + filamentInfoTab: "Сведения о филаменте", + paProfileTab: "Профиль PA", + filamentInfo: "Филамент", + additional: "Дополнительно", + loadingPresets: "Загрузка облачных пресетов…", + cloudConnected: "Облако подключено", + cloudNotConnected: "Облако не подключено; используются значения по умолчанию", + recentColors: "Недавние", + searchColors: "Поиск цветов…", + searchResults: "Результаты поиска", + allColors: "Все цвета", + commonColors: "Основные цвета", + showLess: "Показать меньше", + showAll: "Показать все", + noColorsFound: "Нет цветов, соответствующих запросу", + noResults: "Совпадений не найдено", + extraColorsLabel: "Дополнительные цвета", + extraColorsPlaceholder: "EC984C,#6CD4BC,A66EB9,D87694", + extraColorsHint: "Вставьте от 2 до 8 HEX-цветов через запятую. Они будут отображены как градиент.", + extraColorsInvalid: "Пропущены некорректные HEX-значения: {{tokens}}", + colorEffectLabel: "Эффект", colorEffect: { - none: 'Нет', - sparkle: 'Блёстки', - wood: 'Дерево', - marble: 'Мрамор', - glow: 'Свечение', - matte: 'Матовый', - silk: 'Шёлк', - galaxy: 'Галактика', - rainbow: 'Радуга', - metal: 'Металл', - translucent: 'Полупрозрачный', - gradient: 'Градиент', - dualColor: 'Двухцветный', - triColor: 'Трёхцветный', - multicolor: 'Многоцветный', + none: "Нет", + sparkle: "Блёстки", + wood: "Дерево", + marble: "Мрамор", + glow: "Свечение", + matte: "Матовый", + silk: "Шёлк", + galaxy: "Галактика", + rainbow: "Радуга", + metal: "Металл", + translucent: "Полупрозрачный", + gradient: "Градиент", + dualColor: "Двухцветный", + triColor: "Трёхцветный", + multicolor: "Многоцветный", }, - selectMaterialFirst: 'Сначала выберите материал на вкладке «Сведения о филаменте».', - noPrintersConfigured: 'Принтеры не настроены. Добавьте принтеры, чтобы использовать профили PA.', - matchingFilter: 'Совпадающие', - anyBrand: 'Любой бренд', - anyVariant: 'Любой вариант', - autoSelect: 'Автовыбор', - matches: 'совпадений', - match: 'совпадение', - noMatches: 'Нет совпадений', - connected: 'Подключён', - offline: 'Не в сети', - printerOffline: 'Принтер не в сети. Подключите его для просмотра калибровочных профилей.', - noKProfilesMatch: 'Нет K-профилей, соответствующих выбранному филаменту.', - leftNozzle: 'Левое сопло', - rightNozzle: 'Правое сопло', - profilesSelected: 'выбрано калибровочных профилей', - totalInventory: 'Всего в учёте', - totalConsumed: 'Израсходовано всего', - byMaterial: 'По материалам', - inPrinter: 'В принтерах', - lowStock: 'Малый остаток', - sinceTracking: 'С начала учёта', - resetConsumedCounter: 'Сбросить счётчик', - resetConsumedCounterTooltip: 'Обнулить счётчик израсходованных граммов для этой катушки. Остаток массы не изменится.', - resetConsumedCounterConfirm: 'Сбросить счётчик расхода этой катушки до 0? Последующие печати будут учитываться заново. Сама катушка, расчёт остатка и настройки не изменятся.', - resetAllConsumedCounters: 'Сбросить все счётчики', - resetAllConsumedCountersTooltip: 'Обнулить счётчик израсходованных граммов у всех катушек. Остатки массы не изменятся.', - resetAllConsumedCountersConfirm: 'Сбросить счётчик расхода до 0 у всех {{count}} катушек, включая архивные? Показатель «Израсходовано всего» будет очищен, а будущие печати начнут учитываться с нуля. Катушки и их остатки не изменятся.', - consumedCounterReset: 'Счётчик сброшен', - allConsumedCountersReset: 'Счётчик сброшен у {{count}} катушек', - resetConsumedCounterFailed: 'Не удалось сбросить счётчик', - loadedInAms: 'Загружено в AMS/внешний слот', - remaining: 'Остаток', - weightCheck: 'Проверка массы', - lastWeighed: 'Последнее взвешивание', - neverWeighed: 'Не взвешивалась', - search: 'Поиск катушек…', - showing: 'Показано', - to: '–', - of: 'из', - show: 'Показывать', - spools: 'катушек', - spool: 'катушка', - page: 'Страница', - noSpoolsMatch: 'Ничего не найдено', - noSpoolsMatchDesc: 'Измените поисковый запрос или фильтры.', - active: 'Активные', - archived: 'Архивные', - all: 'Все', - used: 'Израсходовано', - new: 'Новые', - clearFilters: 'Сбросить фильтры', - table: 'Таблица', - cards: 'Карточки', - net: 'Нетто', - groupSimilar: 'Группировать', - groupedSpools: 'Одинаковых катушек: {{count}}', - groupedRows: 'строк', - columns: 'Столбцы', - configureColumns: 'Настроить столбцы', - configureColumnsDesc: 'Перетаскивайте столбцы или используйте стрелки для изменения порядка. Видимость переключается значком глаза.', - visible: 'видимых', - reset: 'Сбросить', - cancel: 'Отмена', - applyChanges: 'Применить изменения', - moveUp: 'Переместить вверх', - moveDown: 'Переместить вниз', - hideColumn: 'Скрыть столбец', - showColumn: 'Показать столбец', - linkToSpool: 'Связать с катушкой', - tagLinked: 'Метка связана с катушкой', - tagLinkFailed: 'Не удалось связать метку', - tagAlreadyLinked: 'Метка уже связана с другой катушкой', - unknownTag: 'Обнаружена неизвестная RFID-метка', - usageHistory: 'История расхода', - noUsageHistory: 'Расход пока не зарегистрирован', - printName: 'Название печати', - weightConsumed: 'Израсходованная масса', - clearHistory: 'Очистить', - historyCleared: 'История расхода очищена', - fillSourceLabel: '(Учёт)', - lowStockThresholdError: 'Порог должен быть от 0,1 до 99,9', - assignMismatchTitle: 'Несоответствие материала', - assignMismatchMessage: 'Материал выбранной катушки «{{spoolMaterial}}» не соответствует материалу слота «{{trayMaterial}}» в {{location}}. Всё равно назначить?', - assignMismatchConfirm: 'Всё равно назначить', - assignPartialMismatchMessage: 'Материал катушки «{{spoolMaterial}}» похож, но не совпадает точно с «{{trayMaterial}}» в {{location}}. Продолжить?', - assignProfileMismatchMessage: 'Профиль катушки «{{spoolProfile}}» не соответствует профилю слота «{{trayProfile}}» в {{location}}. Продолжить?', - assignReconfigureNote: 'Слот AMS будет перенастроен на профиль катушки.', - spoolmanFilamentCatalog: 'Каталог филаментов Spoolman', - pickFromSpoolmanCatalog: 'Выбрать из каталога Spoolman…', - spoolmanFilamentSelected: 'Филамент выбран из каталога Spoolman', - spoolmanFilamentUnlinked: 'Связь с каталогом филаментов удалена', - noSpoolmanFilaments: 'В каталоге Spoolman филаменты не найдены', - spoolmanFilamentColorSwatch: 'Цвет филамента', - spoolWeightManagedBySpoolman: 'Масса пустой катушки задаётся в Spoolman для каждого типа филамента', - spoolmanCatalogLoadFailed: 'Не удалось загрузить каталог филаментов Spoolman', + selectMaterialFirst: "Сначала выберите материал на вкладке «Сведения о филаменте».", + noPrintersConfigured: "Принтеры не настроены. Добавьте принтеры, чтобы использовать профили PA.", + matchingFilter: "Совпадающие", + anyBrand: "Любой бренд", + anyVariant: "Любой вариант", + autoSelect: "Автовыбор", + matches: "совпадений", + match: "совпадение", + noMatches: "Нет совпадений", + connected: "Подключён", + offline: "Не в сети", + printerOffline: "Принтер не в сети. Подключите его для просмотра калибровочных профилей.", + noKProfilesMatch: "Нет K-профилей, соответствующих выбранному филаменту.", + leftNozzle: "Левое сопло", + rightNozzle: "Правое сопло", + profilesSelected: "выбрано калибровочных профилей", + totalInventory: "Всего в учёте", + totalConsumed: "Израсходовано всего", + byMaterial: "По материалам", + inPrinter: "В принтерах", + lowStock: "Малый остаток", + sinceTracking: "С начала учёта", + resetConsumedCounter: "Сбросить счётчик", + resetConsumedCounterTooltip: "Обнулить счётчик израсходованных граммов для этой катушки. Остаток массы не изменится.", + resetConsumedCounterConfirm: "Сбросить счётчик расхода этой катушки до 0? Последующие печати будут учитываться заново. Сама катушка, расчёт остатка и настройки не изменятся.", + resetAllConsumedCounters: "Сбросить все счётчики", + resetAllConsumedCountersTooltip: "Обнулить счётчик израсходованных граммов у всех катушек. Остатки массы не изменятся.", + resetAllConsumedCountersConfirm: "Сбросить счётчик расхода до 0 у всех {{count}} катушек, включая архивные? Показатель «Израсходовано всего» будет очищен, а будущие печати начнут учитываться с нуля. Катушки и их остатки не изменятся.", + consumedCounterReset: "Счётчик сброшен", + allConsumedCountersReset: "Счётчик сброшен у {{count}} катушек", + resetConsumedCounterFailed: "Не удалось сбросить счётчик", + loadedInAms: "Загружено в AMS/внешний слот", + remaining: "Остаток", + weightCheck: "Проверка массы", + lastWeighed: "Последнее взвешивание", + neverWeighed: "Не взвешивалась", + search: "Поиск катушек…", + showing: "Показано", + to: "–", + of: "из", + show: "Показывать", + spools: "катушек", + spool: "катушка", + page: "Страница", + noSpoolsMatch: "Ничего не найдено", + noSpoolsMatchDesc: "Измените поисковый запрос или фильтры.", + active: "Активные", + archived: "Архивные", + all: "Все", + used: "Израсходовано", + new: "Новые", + clearFilters: "Сбросить фильтры", + table: "Таблица", + cards: "Карточки", + net: "Нетто", + groupSimilar: "Группировать", + groupedSpools: "Одинаковых катушек: {{count}}", + groupedRows: "строк", + columns: "Столбцы", + configureColumns: "Настроить столбцы", + configureColumnsDesc: "Перетаскивайте столбцы или используйте стрелки для изменения порядка. Видимость переключается значком глаза.", + visible: "видимых", + reset: "Сбросить", + cancel: "Отмена", + applyChanges: "Применить изменения", + moveUp: "Переместить вверх", + moveDown: "Переместить вниз", + hideColumn: "Скрыть столбец", + showColumn: "Показать столбец", + linkToSpool: "Связать с катушкой", + tagLinked: "Метка связана с катушкой", + tagLinkFailed: "Не удалось связать метку", + tagAlreadyLinked: "Метка уже связана с другой катушкой", + unknownTag: "Обнаружена неизвестная RFID-метка", + usageHistory: "История расхода", + noUsageHistory: "Расход пока не зарегистрирован", + printName: "Название печати", + weightConsumed: "Израсходованная масса", + clearHistory: "Очистить", + historyCleared: "История расхода очищена", + fillSourceLabel: "(Учёт)", + lowStockThresholdError: "Порог должен быть от 0,1 до 99,9", + assignMismatchTitle: "Несоответствие материала", + assignMismatchMessage: "Материал выбранной катушки «{{spoolMaterial}}» не соответствует материалу слота «{{trayMaterial}}» в {{location}}. Всё равно назначить?", + assignMismatchConfirm: "Всё равно назначить", + assignPartialMismatchMessage: "Материал катушки «{{spoolMaterial}}» похож, но не совпадает точно с «{{trayMaterial}}» в {{location}}. Продолжить?", + assignProfileMismatchMessage: "Профиль катушки «{{spoolProfile}}» не соответствует профилю слота «{{trayProfile}}» в {{location}}. Продолжить?", + assignReconfigureNote: "Слот AMS будет перенастроен на профиль катушки.", + spoolmanFilamentCatalog: "Каталог филаментов Spoolman", + pickFromSpoolmanCatalog: "Выбрать из каталога Spoolman…", + spoolmanFilamentSelected: "Филамент выбран из каталога Spoolman", + spoolmanFilamentUnlinked: "Связь с каталогом филаментов удалена", + noSpoolmanFilaments: "В каталоге Spoolman филаменты не найдены", + spoolmanFilamentColorSwatch: "Цвет филамента", + spoolWeightManagedBySpoolman: "Масса пустой катушки задаётся в Spoolman для каждого типа филамента", + spoolmanCatalogLoadFailed: "Не удалось загрузить каталог филаментов Spoolman", }, timelapse: { - title: 'Таймлапс', - create: 'Создать таймлапс', - download: 'Скачать', - delete: 'Удалить', - preview: 'Предпросмотр', - frameRate: 'Частота кадров', - quality: 'Качество', - processing: 'Обработка...', - noTimelapses: 'Таймлапсы отсутствуют', + title: "Таймлапс", + create: "Создать таймлапс", + download: "Скачать", + delete: "Удалить", + preview: "Предпросмотр", + frameRate: "Частота кадров", + quality: "Качество", + processing: "Обработка...", + noTimelapses: "Таймлапсы отсутствуют", }, ams: { - title: 'AMS', - slot: 'Слот', - empty: 'Пусто', - emptySlot: 'Пустой слот', - slotEmpty: 'Пусто', - slotUnconfigured: '?', - emptySlotReset: 'Филамент не назначен', - unknown: 'Неизвестно', - humidity: 'Влажность', - temperature: 'Температура', - filamentType: 'Тип филамента', - filamentColor: 'Цвет', - remaining: 'Остаток', - history: 'История AMS', - noHistory: 'История отсутствует', - configureSlot: 'Настроить слот', - externalSpool: 'Внешняя катушка', - profile: 'Профиль', - kFactor: 'Коэффициент K', - fill: 'Заполнить', - configure: 'Настроить', - used: 'использовано', - remainingUnit: 'осталось', + title: "AMS", + slot: "Слот", + empty: "Пусто", + emptySlot: "Пустой слот", + slotEmpty: "Пусто", + slotUnconfigured: "?", + emptySlotReset: "Филамент не назначен", + unknown: "Неизвестно", + humidity: "Влажность", + temperature: "Температура", + filamentType: "Тип филамента", + filamentColor: "Цвет", + remaining: "Остаток", + history: "История AMS", + noHistory: "История отсутствует", + configureSlot: "Настроить слот", + externalSpool: "Внешняя катушка", + profile: "Профиль", + kFactor: "Коэффициент K", + fill: "Заполнить", + configure: "Настроить", + used: "использовано", + remainingUnit: "осталось", }, printModal: { - selectPrinter: 'Выберите принтер', - selectPlate: 'Выберите пластину', - filamentMapping: 'Сопоставление филаментов', - totalCost: 'Общая стоимость:', - slotRemainingShort: ' — осталось {{grams}} г', - printSettings: 'Настройки печати', - bedLeveling: 'Калибровка стола', - flowCalibration: 'Калибровка потока', - vibrationCalibration: 'Калибровка вибраций', - layerInspection: 'Проверка первого слоя', - timelapse: 'Таймлапс', - cancel: 'Отмена', - noPrintersAvailable: 'Нет доступных принтеров', - printerBusy: 'Принтер занят', - printerOffline: 'Принтер не в сети', - sameTypeDifferentColor: 'Тот же тип, другой цвет', - filamentTypeNotLoaded: 'Филамент этого типа не загружен', - whenToPrint: 'Когда печатать', - asap: 'Как можно скорее', - queue: 'В очередь', - schedule: 'По расписанию', - dateTime: 'Дата и время', - invalidDateTime: 'Введите корректные дату и время', - openCalendar: 'Открыть календарь', - requireManualStart: 'Требовать ручного запуска', - requirePreviousSuccess: 'Запускать только после успешной предыдущей печати', - autoOffAfter: 'Выключить принтер после завершения', - helpAsap: 'Задание будет добавлено в начало очереди и запустится, как только подходящий принтер освободится.', - helpSchedule: 'Задание запустится в указанное время, если принтер свободен. Если принтер занят, оно будет ждать освобождения.', - helpQueue: 'Задание будет добавлено в конец очереди.', - leftNozzle: 'Л', - rightNozzle: 'П', - leftNozzleTooltip: 'Левое сопло', - rightNozzleTooltip: 'Правое сопло', - filamentOverride: 'Переопределение филаментов', - filamentOverrideHint: 'При необходимости замените филаменты для назначения по модели. Планировщик будет сопоставлять выбранные филаменты вместо исходных значений из 3MF.', - originalFilament: 'Исходный', - overrideWith: 'Заменить на', - resetToOriginal: 'Вернуть исходный', - insufficientFilamentTitle: 'Недостаточно филамента', - insufficientFilamentMessage: 'На некоторых назначенных катушках осталось меньше филамента, чем требуется для этой печати:', - insufficientFilamentLine: '{{printer}} — {{slot}}: требуется {{required}} г, осталось {{remaining}} г', - printAnyway: 'Всё равно печатать', - forceColorMatch: 'Обязательно учитывать цвет', - staggerPrinterStarts: 'Запускать принтеры с задержкой', - staggerGroupSize: 'Размер группы', - staggerInterval: 'Интервал (мин)', - staggerPreview: '{{printers}} принтеров → {{groups}} групп по {{size}}, запуск каждые {{interval}} мин', - staggerLastGroup: 'последняя группа: {{count}}', - staggerTotal: 'всего: {{minutes}} мин', - staggerToPrinters: 'Распределить запуск для {{count}} принтеров', - gcodeInjection: 'Добавить G-code автозапуска', + selectPrinter: "Выберите принтер", + selectPlate: "Выберите пластину", + filamentMapping: "Сопоставление филаментов", + plateN: "Пластина {{n}}", + plateFilamentsUnreadable: "Не удалось определить филаменты выбранной пластины, поэтому их невозможно сопоставить. Снимите выбор с этой пластины, чтобы добавить остальные в очередь.", + totalCost: "Общая стоимость:", + slotRemainingShort: " — осталось {{grams}} г", + printSettings: "Настройки печати", + bedLeveling: "Калибровка стола", + flowCalibration: "Калибровка потока", + vibrationCalibration: "Калибровка вибраций", + layerInspection: "Проверка первого слоя", + timelapse: "Таймлапс", + cancel: "Отмена", + noPrintersAvailable: "Нет доступных принтеров", + printerBusy: "Принтер занят", + printerOffline: "Принтер не в сети", + sameTypeDifferentColor: "Тот же тип, другой цвет", + filamentTypeNotLoaded: "Филамент этого типа не загружен", + whenToPrint: "Когда печатать", + asap: "Как можно скорее", + queue: "В очередь", + schedule: "По расписанию", + dateTime: "Дата и время", + invalidDateTime: "Введите корректные дату и время", + openCalendar: "Открыть календарь", + requireManualStart: "Требовать ручного запуска", + requirePreviousSuccess: "Запускать только после успешной предыдущей печати", + autoOffAfter: "Выключить принтер после завершения", + helpAsap: "Задание будет добавлено в начало очереди и запустится, как только подходящий принтер освободится.", + helpSchedule: "Задание запустится в указанное время, если принтер свободен. Если принтер занят, оно будет ждать освобождения.", + helpQueue: "Задание будет добавлено в конец очереди.", + leftNozzle: "Л", + rightNozzle: "П", + leftNozzleTooltip: "Левое сопло", + rightNozzleTooltip: "Правое сопло", + filamentOverride: "Переопределение филаментов", + filamentOverrideHint: "При необходимости замените филаменты для назначения по модели. Планировщик будет сопоставлять выбранные филаменты вместо исходных значений из 3MF.", + originalFilament: "Исходный", + overrideWith: "Заменить на", + resetToOriginal: "Вернуть исходный", + insufficientFilamentTitle: "Недостаточно филамента", + waitingForAmsStatus: "Ожидание состояния AMS от принтера {{printer}}…", + insufficientFilamentMessage: "На некоторых назначенных катушках осталось меньше филамента, чем требуется для этой печати:", + insufficientFilamentLine: "{{printer}} — {{slot}}: требуется {{required}} г, осталось {{remaining}} г", + printAnyway: "Всё равно печатать", + forceColorMatch: "Обязательно учитывать цвет", + staggerPrinterStarts: "Запускать принтеры с задержкой", + staggerGroupSize: "Размер группы", + staggerInterval: "Интервал (мин)", + staggerPreview: "{{printers}} принтеров → {{groups}} групп по {{size}}, запуск каждые {{interval}} мин", + staggerLastGroup: "последняя группа: {{count}}", + staggerTotal: "всего: {{minutes}} мин", + staggerToPrinters: "Распределить запуск для {{count}} принтеров", + gcodeInjection: "Добавить G-code автозапуска", }, backup: { - includesEncryptionKey: 'Локальные резервные копии включают файл ключа шифрования MFA (DATA_DIR/.mfa_encryption_key), поэтому ZIP-архив является самодостаточным. Считайте этот ZIP конфиденциальным: любой, у кого есть файл, сможет расшифровать сохранённые в нём секреты клиента OIDC и TOTP.', - title: 'Резервное копирование и восстановление', - createBackup: 'Создать резервную копию', - restoreBackup: 'Восстановить из копии', - restoreDescription: 'Заменить все данные данными из резервной копии', - downloadBackup: 'Скачать резервную копию', - uploadBackup: 'Загрузить резервную копию', - lastBackup: 'Последняя копия', - autoBackup: 'Автоматическое резервное копирование', - backupNow: 'Создать копию сейчас', - restoreWarning: 'Внимание: восстановление из резервной копии перезапишет все текущие данные.', - includeArchives: 'Включить архивы', - includeSettings: 'Включить настройки', - includeProfiles: 'Включить профили', - backupSuccess: 'Резервная копия успешно создана', - restoreSuccess: 'Резервная копия успешно восстановлена', - backupFailed: 'Не удалось создать резервную копию', - restoreFailed: 'Не удалось выполнить восстановление', - restoreNote: 'На время восстановления виртуальный принтер будет остановлен', - githubBackup: 'Резервная копия в Git', - enabled: 'Включено', - cloudLoginRequired: 'Требуется вход в Bambu Cloud. Войдите в разделе «Профили → Облачные профили», чтобы включить резервное копирование в GitHub.', - cloudLoginRequiredShort: 'Требуется вход в облако', - githubDescription: 'Автоматически синхронизировать профили с приватным репозиторием GitHub для резервного копирования и хранения истории версий.', - repoIsPrivate: 'Репозиторий приватный — резервное копирование безопасно.', - repoIsPublicWarning: 'Репозиторий ПУБЛИЧНЫЙ. Резервные копии Bambuddy содержат учётные данные MQTT, токены Home Assistant и Prometheus, адрес электронной почты Bambu Cloud и коды доступа к принтерам из K-профилей. Сохранение заблокировано, пока репозиторий не будет сделан приватным в настройках провайдера.', - repoVisibilityUnknown: 'Не удалось определить видимость репозитория. Bambuddy отказывается создавать резервные копии в репозитории, приватность которого не подтверждена; сохранение будет заблокировано.', - repositoryUrl: 'URL репозитория', - repoUrlPlaceholderGitHub: 'https://github.com/username/repo-name', - repoUrlPlaceholderGitea: 'https://gitea.example.com/username/repo-name', - repoUrlPlaceholderForgejo: 'https://forgejo.example.com/username/repo-name', - repoUrlPlaceholderGitLab: 'https://gitlab.com/username/repo-name', - allowInsecureHttp: 'Разрешить небезопасный HTTP', - allowInsecureHttpHint: 'Включайте для локальных серверов в частной сети без TLS', - personalAccessToken: 'Персональный токен доступа', - tokenSaved: '(сохранён)', - enterNewToken: 'Введите новый токен для обновления', - tokenHint: 'Токен с точечными правами на чтение и запись содержимого', - branch: 'Ветка', - provider: 'Git-провайдер', - providerGitHub: 'GitHub', - providerGitLab: 'GitLab', - providerGitea: 'Gitea', - providerForgejo: 'Forgejo', - manualOnly: 'Только вручную', - hourly: 'Ежечасно', - daily: 'Ежедневно', - weekly: 'Еженедельно', - includeInBackup: 'Включить в резервную копию', - kProfiles: 'K-профили', - kProfilesDescription: 'Калибровки Pressure Advance с подключённых принтеров', - noPrintersConnected: 'Нет подключённых принтеров', - printersConnected: 'Подключено {{connected}} из {{total}}', - cloudProfiles: 'Облачные профили', - cloudProfilesDescription: 'Предустановки филамента, принтера и процесса из Bambu Cloud', - appSettings: 'Настройки приложения', - appSettingsDescription: 'Конфигурация Bambuddy (полная база данных)', - spoolInventory: 'Учёт катушек', - spoolInventoryDescription: 'Катушки филамента, история расхода и учёт стоимости', - printArchives: 'Архив печати', - printArchivesDescription: 'Метаданные истории печати без файлов G-code и 3MF', - lastBackupAt: 'Последняя копия:', - noBackupsYet: 'Резервных копий пока нет', - next: 'Следующая:', - startingBackup: 'Запуск резервного копирования...', - test: 'Проверить', - enableBackup: 'Включить резервное копирование', - testConnection: 'Проверить соединение', - enterRepoUrl: 'Введите URL репозитория', - enterRepoAndToken: 'Введите URL репозитория и токен доступа', - repoRequired: 'Необходимо указать URL репозитория', - tokenRequired: 'Необходимо указать токен доступа', - githubBackupEnabled: 'Резервное копирование в GitHub включено', - tokenUpdated: 'Токен обновлён', - settingsSaved: 'Настройки сохранены', - failedToSave: 'Не удалось сохранить: {{message}}', - backupCompleteFiles: 'Резервное копирование завершено — обновлено файлов: {{count}}', - backupSkippedNoChanges: 'Резервное копирование пропущено — изменений нет', - backupFailed2: 'Ошибка резервного копирования: {{message}}', - clearedLogs: 'Очищено записей журнала: {{count}}', - failedToClearLogs: 'Не удалось очистить журнал: {{message}}', - history: 'История', - clear: 'Очистить', - date: 'Дата', - status: 'Статус', - commit: 'Коммит', - localBackup: 'Локальная резервная копия', - localBackupDescription: 'Создать полную резервную копию данных Bambuddy, включая базу данных, архивы, загрузки и все файлы.', - downloadBackupLabel: 'Скачать резервную копию', - completeBackupZip: 'Полная копия: база данных и все файлы (ZIP)', - download: 'Скачать', - preparingBackup: 'Подготовка резервной копии...', - creatingArchive: 'Создание архива резервной копии... Для больших архивов это может занять некоторое время.', - downloadingFile: 'Скачивание файла резервной копии...', - backupDownloaded: 'Резервная копия успешно скачана', - failedToCreateBackup: 'Не удалось создать резервную копию: {{message}}', - restore: 'Восстановить', - restoreReplacesAll: 'Восстановление заменит все данные.', - restoreReplacesAllDetail: 'Текущая база данных и файлы будут полностью заменены. После восстановления потребуется перезапуск.', - restoreConfirmTitle: 'Восстановление из резервной копии', - restoreConfirmMessage: 'Восстановить данные из файла «{{filename}}»? Текущая база данных и все файлы будут полностью заменены. После восстановления приложение потребуется перезапустить.', - restoreConfirmButton: 'Восстановить', - uploadingFile: 'Загрузка файла резервной копии...', - backupRestoredRestart: 'Резервная копия восстановлена. Перезапустите Bambuddy.', - failedToRestore: 'Не удалось восстановить резервную копию. Проверьте формат файла.', - reloadNow: 'Перезагрузить сейчас', - creatingBackup: 'Создание резервной копии', - restoringBackup: 'Восстановление данных', - preparing: 'Подготовка...', - processing: 'Обработка...', - doNotClosePage: 'Не закрывайте эту страницу и не переходите на другую. Для больших резервных копий операция может занять несколько минут.', - restoring: 'Восстановление...', - restoreComplete: 'Восстановление завершено', - restoreFailed2: 'Ошибка восстановления', - importSettings: 'Импортировать настройки из файла резервной копии', - pleaseWaitRestoring: 'Подождите, пока данные восстанавливаются', - selectBackupFile: 'Нажмите, чтобы выбрать файл резервной копии (.json или .zip)', - duplicateHandling: 'Правила обработки дубликатов:', - matchPrinters: 'Принтеры', - matchPrintersBy: 'сопоставляются по серийному номеру', - matchSmartPlugs: 'Умные розетки', - matchSmartPlugsBy: 'сопоставляются по IP-адресу', - matchNotificationProviders: 'Провайдеры уведомлений', - matchNotificationProvidersBy: 'сопоставляются по имени', - matchFilaments: 'Филаменты', - matchFilamentsBy: 'сопоставляются по имени, типу и бренду', - matchArchives: 'Архивы', - matchArchivesBy: 'сопоставляются по хешу содержимого и всегда пропускаются', - matchPendingUploads: 'Ожидающие загрузки', - matchPendingUploadsBy: 'сопоставляются по имени файла', - matchSettingsTemplates: 'Настройки и шаблоны', - matchSettingsTemplatesBy: 'всегда перезаписываются', - replaceExisting: 'Заменять существующие данные', - keepExisting: 'Сохранять существующие данные', - overwriteDescription: 'Перезаписывать существующие элементы данными из резервной копии', - keepDescription: 'Восстанавливать только отсутствующие элементы', - overwriteCaution: 'Внимание:', - overwriteWarning: 'Перезапись заменит текущие настройки данными из резервной копии. В целях безопасности коды доступа к принтерам никогда не перезаписываются.', - cancel: 'Отмена', - processingBackup: 'Обработка файла резервной копии...', - itemsRestored: 'Восстановлено элементов', - itemsSkipped: 'Пропущено элементов', - restored: 'Восстановлено', - skippedAlreadyExist: 'Пропущено — уже существует', - filesCategory: 'Файлы (3MF, миниатюры и т. п.)', - andMore: '...и ещё {{count}}', - newApiKeysGenerated: 'Созданы новые API-ключи', - keysShownOnce: 'Эти ключи показываются только один раз. Скопируйте их сейчас!', - copy: 'Копировать', - noDataFound: 'В резервной копии не найдено данных для восстановления.', - close: 'Закрыть', - scheduledBackup: 'Резервные копии по расписанию', - scheduledBackupDescription: 'Автоматически создавать снимки резервных копий по расписанию. Каталог назначения можно смонтировать с NAS или внешнего накопителя.', - frequency: 'Периодичность', - backupTime: 'Время', - retention: 'Хранение', - retentionDescription: 'Количество сохраняемых резервных копий', - outputPath: 'Каталог назначения', - outputPathPlaceholder: 'По умолчанию: {{path}}', - outputPathDescription: 'Оставьте пустым, чтобы использовать стандартный каталог', - runNow: 'Запустить сейчас', - backupFiles: 'Файлы резервных копий', - noScheduledBackups: 'Резервных копий пока нет', - deleteBackup: 'Удалить', - deleteBackupConfirm: 'Удалить этот файл резервной копии?', - backupRunning: 'Выполняется резервное копирование...', - scheduledBackupComplete: 'Резервная копия успешно создана', - scheduledBackupFailed: 'Ошибка резервного копирования', - nextBackup: 'Следующая копия', - backupSize: 'Размер', - localTimeHint: 'Местное время ({{tz}})', - defaultPathLabel: 'По умолчанию:', + includesEncryptionKey: "Локальные резервные копии включают файл ключа шифрования MFA (DATA_DIR/.mfa_encryption_key), поэтому ZIP-архив является самодостаточным. Считайте этот ZIP конфиденциальным: любой, у кого есть файл, сможет расшифровать сохранённые в нём секреты клиента OIDC и TOTP.", + title: "Резервное копирование и восстановление", + createBackup: "Создать резервную копию", + restoreBackup: "Восстановить из копии", + restoreDescription: "Заменить все данные данными из резервной копии", + downloadBackup: "Скачать резервную копию", + uploadBackup: "Загрузить резервную копию", + lastBackup: "Последняя копия", + autoBackup: "Автоматическое резервное копирование", + backupNow: "Создать копию сейчас", + restoreWarning: "Внимание: восстановление из резервной копии перезапишет все текущие данные.", + includeArchives: "Включить архивы", + includeSettings: "Включить настройки", + includeProfiles: "Включить профили", + backupSuccess: "Резервная копия успешно создана", + restoreSuccess: "Резервная копия успешно восстановлена", + backupFailed: "Не удалось создать резервную копию", + restoreFailed: "Не удалось выполнить восстановление", + restoreNote: "На время восстановления виртуальный принтер будет остановлен", + githubBackup: "Резервная копия в Git", + enabled: "Включено", + cloudLoginRequired: "Требуется вход в Bambu Cloud. Войдите в разделе «Профили → Облачные профили», чтобы включить резервное копирование в GitHub.", + cloudLoginRequiredShort: "Требуется вход в облако", + githubDescription: "Автоматически синхронизировать профили с приватным репозиторием GitHub для резервного копирования и хранения истории версий.", + repoIsPrivate: "Репозиторий приватный — резервное копирование безопасно.", + repoIsPublicWarning: "Репозиторий ПУБЛИЧНЫЙ. Резервные копии Bambuddy содержат учётные данные MQTT, токены Home Assistant и Prometheus, адрес электронной почты Bambu Cloud и коды доступа к принтерам из K-профилей. Сохранение заблокировано, пока репозиторий не будет сделан приватным в настройках провайдера.", + repoVisibilityUnknown: "Не удалось определить видимость репозитория. Bambuddy отказывается создавать резервные копии в репозитории, приватность которого не подтверждена; сохранение будет заблокировано.", + repositoryUrl: "URL репозитория", + repoUrlPlaceholderGitHub: "https://github.com/username/repo-name", + repoUrlPlaceholderGitea: "https://gitea.example.com/username/repo-name", + repoUrlPlaceholderForgejo: "https://forgejo.example.com/username/repo-name", + repoUrlPlaceholderGitLab: "https://gitlab.com/username/repo-name", + allowInsecureHttp: "Разрешить небезопасный HTTP", + allowInsecureHttpHint: "Включайте для локальных серверов в частной сети без TLS", + personalAccessToken: "Персональный токен доступа", + tokenSaved: "(сохранён)", + enterNewToken: "Введите новый токен для обновления", + tokenHint: "Токен с точечными правами на чтение и запись содержимого", + branch: "Ветка", + provider: "Git-провайдер", + providerGitHub: "GitHub", + providerGitLab: "GitLab", + providerGitea: "Gitea", + providerForgejo: "Forgejo", + manualOnly: "Только вручную", + hourly: "Ежечасно", + daily: "Ежедневно", + weekly: "Еженедельно", + includeInBackup: "Включить в резервную копию", + kProfiles: "K-профили", + kProfilesDescription: "Калибровки Pressure Advance с подключённых принтеров", + noPrintersConnected: "Нет подключённых принтеров", + printersConnected: "Подключено {{connected}} из {{total}}", + cloudProfiles: "Облачные профили", + cloudProfilesDescription: "Предустановки филамента, принтера и процесса из Bambu Cloud", + appSettings: "Настройки приложения", + appSettingsDescription: "Конфигурация Bambuddy (полная база данных)", + spoolInventory: "Учёт катушек", + spoolInventoryDescription: "Катушки филамента, история расхода и учёт стоимости", + printArchives: "Архив печати", + printArchivesDescription: "Метаданные истории печати без файлов G-code и 3MF", + lastBackupAt: "Последняя копия:", + noBackupsYet: "Резервных копий пока нет", + next: "Следующая:", + startingBackup: "Запуск резервного копирования...", + test: "Проверить", + enableBackup: "Включить резервное копирование", + testConnection: "Проверить соединение", + enterRepoUrl: "Введите URL репозитория", + enterRepoAndToken: "Введите URL репозитория и токен доступа", + repoRequired: "Необходимо указать URL репозитория", + tokenRequired: "Необходимо указать токен доступа", + githubBackupEnabled: "Резервное копирование в GitHub включено", + tokenUpdated: "Токен обновлён", + settingsSaved: "Настройки сохранены", + failedToSave: "Не удалось сохранить: {{message}}", + backupCompleteFiles: "Резервное копирование завершено — обновлено файлов: {{count}}", + backupSkippedNoChanges: "Резервное копирование пропущено — изменений нет", + backupFailed2: "Ошибка резервного копирования: {{message}}", + clearedLogs: "Очищено записей журнала: {{count}}", + failedToClearLogs: "Не удалось очистить журнал: {{message}}", + history: "История", + clear: "Очистить", + date: "Дата", + status: "Статус", + commit: "Коммит", + localBackup: "Локальная резервная копия", + localBackupDescription: "Создать полную резервную копию данных Bambuddy, включая базу данных, архивы, загрузки и все файлы.", + downloadBackupLabel: "Скачать резервную копию", + completeBackupZip: "Полная копия: база данных и все файлы (ZIP)", + download: "Скачать", + preparingBackup: "Подготовка резервной копии...", + creatingArchive: "Создание архива резервной копии... Для больших архивов это может занять некоторое время.", + downloadingFile: "Скачивание файла резервной копии...", + backupDownloaded: "Резервная копия успешно скачана", + failedToCreateBackup: "Не удалось создать резервную копию: {{message}}", + restore: "Восстановить", + restoreReplacesAll: "Восстановление заменит все данные.", + restoreReplacesAllDetail: "Текущая база данных и файлы будут полностью заменены. После восстановления потребуется перезапуск.", + restoreConfirmTitle: "Восстановление из резервной копии", + restoreConfirmMessage: "Восстановить данные из файла «{{filename}}»? Текущая база данных и все файлы будут полностью заменены. После восстановления приложение потребуется перезапустить.", + restoreConfirmButton: "Восстановить", + uploadingFile: "Загрузка файла резервной копии...", + backupRestoredRestart: "Резервная копия восстановлена. Перезапустите Bambuddy.", + failedToRestore: "Не удалось восстановить резервную копию. Проверьте формат файла.", + reloadNow: "Перезагрузить сейчас", + creatingBackup: "Создание резервной копии", + restoringBackup: "Восстановление данных", + preparing: "Подготовка...", + processing: "Обработка...", + doNotClosePage: "Не закрывайте эту страницу и не переходите на другую. Для больших резервных копий операция может занять несколько минут.", + restoring: "Восстановление...", + restoreComplete: "Восстановление завершено", + restoreFailed2: "Ошибка восстановления", + importSettings: "Импортировать настройки из файла резервной копии", + pleaseWaitRestoring: "Подождите, пока данные восстанавливаются", + selectBackupFile: "Нажмите, чтобы выбрать файл резервной копии (.json или .zip)", + duplicateHandling: "Правила обработки дубликатов:", + matchPrinters: "Принтеры", + matchPrintersBy: "сопоставляются по серийному номеру", + matchSmartPlugs: "Умные розетки", + matchSmartPlugsBy: "сопоставляются по IP-адресу", + matchNotificationProviders: "Провайдеры уведомлений", + matchNotificationProvidersBy: "сопоставляются по имени", + matchFilaments: "Филаменты", + matchFilamentsBy: "сопоставляются по имени, типу и бренду", + matchArchives: "Архивы", + matchArchivesBy: "сопоставляются по хешу содержимого и всегда пропускаются", + matchPendingUploads: "Ожидающие загрузки", + matchPendingUploadsBy: "сопоставляются по имени файла", + matchSettingsTemplates: "Настройки и шаблоны", + matchSettingsTemplatesBy: "всегда перезаписываются", + replaceExisting: "Заменять существующие данные", + keepExisting: "Сохранять существующие данные", + overwriteDescription: "Перезаписывать существующие элементы данными из резервной копии", + keepDescription: "Восстанавливать только отсутствующие элементы", + overwriteCaution: "Внимание:", + overwriteWarning: "Перезапись заменит текущие настройки данными из резервной копии. В целях безопасности коды доступа к принтерам никогда не перезаписываются.", + cancel: "Отмена", + processingBackup: "Обработка файла резервной копии...", + itemsRestored: "Восстановлено элементов", + itemsSkipped: "Пропущено элементов", + restored: "Восстановлено", + skippedAlreadyExist: "Пропущено — уже существует", + filesCategory: "Файлы (3MF, миниатюры и т. п.)", + andMore: "...и ещё {{count}}", + newApiKeysGenerated: "Созданы новые API-ключи", + keysShownOnce: "Эти ключи показываются только один раз. Скопируйте их сейчас!", + copy: "Копировать", + noDataFound: "В резервной копии не найдено данных для восстановления.", + close: "Закрыть", + scheduledBackup: "Резервные копии по расписанию", + scheduledBackupDescription: "Автоматически создавать снимки резервных копий по расписанию. Каталог назначения можно смонтировать с NAS или внешнего накопителя.", + frequency: "Периодичность", + backupTime: "Время", + retention: "Хранение", + retentionDescription: "Количество сохраняемых резервных копий", + outputPath: "Каталог назначения", + outputPathPlaceholder: "По умолчанию: {{path}}", + outputPathDescription: "Оставьте пустым, чтобы использовать стандартный каталог", + runNow: "Запустить сейчас", + backupFiles: "Файлы резервных копий", + noScheduledBackups: "Резервных копий пока нет", + deleteBackup: "Удалить", + deleteBackupConfirm: "Удалить этот файл резервной копии?", + backupRunning: "Выполняется резервное копирование...", + scheduledBackupComplete: "Резервная копия успешно создана", + scheduledBackupFailed: "Ошибка резервного копирования", + nextBackup: "Следующая копия", + backupSize: "Размер", + localTimeHint: "Местное время ({{tz}})", + defaultPathLabel: "По умолчанию:", + pathCheck: { + title: "Bambuddy не может записывать в этот каталог", + howToFix: "Как исправить:", + sandboxed: "Служба Bambuddy не может записывать в {{path}}. Её модуль systemd работает с ProtectSystem=strict, поэтому все каталоги за пределами каталогов установки, данных и журналов доступны службе только для чтения, даже если из вашей командной оболочки запись разрешена.", + read_only: "{{path}} находится в файловой системе, доступной только для чтения.", + permission_denied: "Bambuddy не разрешена запись в {{path}}. Проверьте владельца каталога и права доступа.", + no_space: "В файловой системе, содержащей {{path}}, закончилось свободное место.", + not_a_directory: "{{path}} существует, но не является каталогом.", + missing: "{{path}} не существует, и создать этот каталог не удалось.", + error: "Bambuddy не может записывать в {{path}}.", + ephemeralTitle: "Эти резервные копии не сохранятся после перезапуска контейнера", + container_ephemeral: "{{path}} находится внутри контейнера Bambuddy, а не на хосте. Записанные туда резервные копии будут потеряны при пересоздании контейнера. Подключите каталог с хоста:", + }, categories: { - settings: 'Настройки', - notification_providers: 'Провайдеры уведомлений', - notification_templates: 'Шаблоны уведомлений', - smart_plugs: 'Умные розетки', - printers: 'Принтеры', - filaments: 'Филаменты', - maintenance_types: 'Виды обслуживания', - archives: 'Архивы', - projects: 'Проекты', - pending_uploads: 'Ожидающие загрузки', - external_links: 'Внешние ссылки', - api_keys: 'API-ключи', + settings: "Настройки", + notification_providers: "Провайдеры уведомлений", + notification_templates: "Шаблоны уведомлений", + smart_plugs: "Умные розетки", + printers: "Принтеры", + filaments: "Филаменты", + maintenance_types: "Виды обслуживания", + archives: "Архивы", + projects: "Проекты", + pending_uploads: "Ожидающие загрузки", + external_links: "Внешние ссылки", + api_keys: "API-ключи", }, }, tags: { - title: 'Метки', - addTag: 'Добавить метку', - editTag: 'Изменить метку', - deleteTag: 'Удалить метку', - tagName: 'Название метки', - tagColor: 'Цвет метки', - noTags: 'Меток нет', - deleteConfirm: 'Удалить эту метку?', - manageTags: 'Управление метками', + title: "Метки", + addTag: "Добавить метку", + editTag: "Изменить метку", + deleteTag: "Удалить метку", + tagName: "Название метки", + tagColor: "Цвет метки", + noTags: "Меток нет", + deleteConfirm: "Удалить эту метку?", + manageTags: "Управление метками", }, uploadModal: { - title: 'Загрузка файлов 3MF', - dragDrop: 'Перетащите файлы .3mf сюда', - or: 'или', - browseFiles: 'Выбрать файлы', - extractionInfo: 'Модель принтера будет автоматически определена по метаданным файла 3MF.', - uploaded: 'загружено', - failed: 'ошибок', - uploading: 'Загрузка...', - upload: 'Загрузить', - uploadFailed: 'Не удалось загрузить', + title: "Загрузка файлов 3MF", + dragDrop: "Перетащите файлы .3mf сюда", + or: "или", + browseFiles: "Выбрать файлы", + extractionInfo: "Модель принтера будет автоматически определена по метаданным файла 3MF.", + uploaded: "загружено", + failed: "ошибок", + uploading: "Загрузка...", + upload: "Загрузить", + uploadFailed: "Не удалось загрузить", }, editArchive: { - title: 'Редактирование архива', - name: 'Название', - namePlaceholder: 'Название печати', - printer: 'Принтер', - noPrinter: 'Без принтера', - project: 'Проект', - noProject: 'Без проекта', - itemsPrinted: 'Напечатано изделий', - itemsPrintedHelp: 'Количество изделий, полученных в этом задании печати', - notes: 'Заметки', - notesPlaceholder: 'Добавьте заметки об этой печати...', - externalLink: 'Внешняя ссылка', - externalLinkPlaceholder: 'https://printables.com/model/...', - externalLinkHelp: 'Ссылка на Printables, Thingiverse или другой источник', - tags: 'Метки', - tagsPlaceholder: 'Добавить метки...', - addMoreTags: 'Добавить ещё метки...', - matchingTags: 'Совпадения для «{{query}}»', - existingTags: 'Существующие метки', - clickToAdd: '(нажмите, чтобы добавить)', - status: 'Статус', - failureReason: 'Причина сбоя', - selectReason: 'Выберите причину...', - photos: 'Фотографии результата печати', - photosHelp: 'Нажмите +, чтобы добавить фотографии результата', - printResult: 'Результат печати', - saving: 'Сохранение...', + title: "Редактирование архива", + name: "Название", + namePlaceholder: "Название печати", + printer: "Принтер", + noPrinter: "Без принтера", + project: "Проект", + noProject: "Без проекта", + itemsPrinted: "Напечатано изделий", + itemsPrintedHelp: "Количество изделий, полученных в этом задании печати", + notes: "Заметки", + notesPlaceholder: "Добавьте заметки об этой печати...", + externalLink: "Внешняя ссылка", + externalLinkPlaceholder: "https://printables.com/model/...", + externalLinkHelp: "Ссылка на Printables, Thingiverse или другой источник", + tags: "Метки", + tagsPlaceholder: "Добавить метки...", + addMoreTags: "Добавить ещё метки...", + matchingTags: "Совпадения для «{{query}}»", + existingTags: "Существующие метки", + clickToAdd: "(нажмите, чтобы добавить)", + status: "Статус", + failureReason: "Причина сбоя", + selectReason: "Выберите причину...", + photos: "Фотографии результата печати", + photosHelp: "Нажмите +, чтобы добавить фотографии результата", + printResult: "Результат печати", + saving: "Сохранение...", failureReasons: { - adhesionFailure: 'Плохая адгезия к столу', - spaghettiDetached: 'Спагетти / отрыв детали', - layerShift: 'Сдвиг слоёв', - cloggedNozzle: 'Засор сопла', - filamentRunout: 'Закончился филамент', - warping: 'Коробление', - stringing: 'Стрингинг', - underExtrusion: 'Недоэкструзия', - powerFailure: 'Сбой питания', - userCancelled: 'Отменено пользователем', - other: 'Другое', + adhesionFailure: "Плохая адгезия к столу", + spaghettiDetached: "Спагетти / отрыв детали", + layerShift: "Сдвиг слоёв", + cloggedNozzle: "Засор сопла", + filamentRunout: "Закончился филамент", + warping: "Коробление", + stringing: "Стрингинг", + underExtrusion: "Недоэкструзия", + powerFailure: "Сбой питания", + userCancelled: "Отменено пользователем", + other: "Другое", }, statuses: { - completed: 'Завершено', - failed: 'Ошибка', - aborted: 'Отменено', - printing: 'Печать', + completed: "Завершено", + failed: "Ошибка", + aborted: "Отменено", + printing: "Печать", + }, + }, + kProfiles: { + title: "K-профили", + noPrintersConfigured: "Принтеры не настроены", + addPrinterInSettings: "Добавьте принтер в настройках, чтобы управлять K-профилями", + noActivePrinters: "Нет активных принтеров", + enablePrinterConnection: "Включите подключение к принтеру, чтобы просматривать его K-профили", + loadingProfiles: "Загрузка K-профилей...", + printerOffline: "Принтер не в сети", + printerOfflineDesc: "Выбранный принтер не подключён. Включите его, чтобы просмотреть K-профили.", + noMatchingProfiles: "Подходящие профили не найдены", + noMatchingProfilesDesc: "Ни один профиль не соответствует условиям поиска", + noKProfiles: "K-профили отсутствуют", + noKProfilesDesc: "Для сопла {{diameter}} мм не найдены профили Pressure Advance", + createFirstProfile: "Создать первый профиль", + printer: "Принтер", + nozzle: "Сопло", + refresh: "Обновить", + addProfile: "Добавить профиль", + export: "Экспорт", + import: "Импорт", + select: "Выбрать", + selectAll: "Выбрать все", + delete: "Удалить", + searchPlaceholder: "Поиск по названию или филаменту...", + allExtruders: "Все экструдеры", + leftOnly: "Только левый", + rightOnly: "Только правый", + allFlow: "Любой тип потока", + hfOnly: "Только HF", + sOnly: "Только S", + sortName: "Сортировка: название", + sortKValue: "Сортировка: K-значение", + sortFilament: "Сортировка: филамент", + leftExtruder: "Левый экструдер", + rightExtruder: "Правый экструдер", + modal: { + addTitle: "Добавить K-профиль", + editTitle: "Изменить K-профиль", + profileName: "Название профиля", + profileNamePlaceholder: "Мой профиль PLA", + kValue: "K-значение", + kValuePlaceholder: "0.020", + kValueHelp: "Типичный диапазон: 0,01–0,06 для PLA, 0,02–0,10 для PETG", + filament: "Филамент", + selectFilament: "Выберите филамент...", + noFilamentsHelp: "Филаменты не найдены. Сначала создайте K-профиль в Bambu Studio.", + flowType: "Тип потока", + highFlow: "Высокопоточный", + standard: "Стандартный", + nozzleSize: "Диаметр сопла", + extruder: "Экструдер", + extruders: "Экструдеры", + left: "Левый", + right: "Правый", + notes: "Заметки (хранятся локально)", + notesPlaceholder: "Добавьте заметки об этом профиле...", + notesHelp: "Заметки сохраняются в Bambuddy, а не на принтере", + syncing: "Синхронизация с принтером...", + savingExtruder: "Сохранение в экструдер {{current}}/{{total}}...", + pleaseWait: "Подождите", + }, + deleteConfirm: { + title: "Удалить профиль", + cannotUndo: "Это действие нельзя отменить", + message: "Удалить профиль «{{name}}» с принтера?", + }, + bulkDelete: { + title: "Удалить профили", + cannotUndo: "Это действие нельзя отменить", + message: "Удалить с принтера выбранные профили ({{count}})?", + }, + toast: { + profileSaved: "K-профиль сохранён", + profilesSaved: "K-профиль сохранён для {{count}} экструдеров", + selectAtLeastOneExtruder: "Выберите хотя бы один экструдер", + profileDeleted: "K-профиль удалён", + profilesDeleted: "Удалено профилей: {{count}}", + exportedProfiles: "Экспортировано профилей: {{count}}", + importedProfiles: "Импортировано {{count}} из {{total}} профилей", + noProfilesToExport: "Нет профилей для экспорта", + invalidFileFormat: "Недопустимый формат файла", + failedToParseImport: "Не удалось разобрать файл импорта", + failedToSaveBatch: "Не удалось сохранить K-профили", + noteSaved: "Заметка сохранена", + failedToSaveNote: "Не удалось сохранить заметку", + }, + permission: { + noRead: "У вас нет прав на обновление профилей", + noCreate: "У вас нет прав на добавление профилей", + noUpdate: "У вас нет прав на изменение K-профилей", + noDelete: "У вас нет прав на удаление K-профилей", + noExport: "У вас нет прав на экспорт профилей", + noImport: "У вас нет прав на импорт профилей", }, }, virtualPrinter: { - title: 'Виртуальный принтер', - running: 'Запущен', - stopped: 'Остановлен', + title: "Виртуальный принтер", + running: "Запущен", + stopped: "Остановлен", description: { - default: 'Включает виртуальный принтер, который отображается в Bambu Studio и OrcaSlicer. Отправленные на него файлы сразу сохраняются в архив без печати.', - proxy: 'Включает прокси, перенаправляющий трафик слайсера на реальный принтер и позволяющий печатать удалённо из любой сети.', + default: "Включает виртуальный принтер, который отображается в Bambu Studio и OrcaSlicer. Отправленные на него файлы сразу сохраняются в архив без печати.", + proxy: "Включает прокси, перенаправляющий трафик слайсера на реальный принтер и позволяющий печатать удалённо из любой сети.", }, enable: { - title: 'Включить виртуальный принтер', - visibleInSlicer: 'Отображается как «Bambuddy» при обнаружении принтеров в слайсере', - proxyingTo: 'Проксирование на {{name}}', - notActive: 'Не активен', + title: "Включить виртуальный принтер", + visibleInSlicer: "Отображается как «Bambuddy» при обнаружении принтеров в слайсере", + proxyingTo: "Проксирование на {{name}}", + notActive: "Не активен", }, model: { - title: 'Модель принтера', - description: 'Выберите модель принтера, которую нужно эмулировать.', - restartWarning: 'При изменении модели виртуальный принтер будет перезапущен', + title: "Модель принтера", + description: "Выберите модель принтера, которую нужно эмулировать.", + restartWarning: "При изменении модели виртуальный принтер будет перезапущен", }, accessCode: { - title: 'Код доступа', - isSet: 'Код доступа задан', - notSet: 'Код доступа не задан — он необходим для включения', - placeholder: 'Введите 8-значный код', - placeholderChange: 'Введите новый код', - hint: 'Код должен состоять ровно из 8 символов. Слайсеры используют его для аутентификации.', - charCount: '({{count}}/8)', - inheritedFromTarget: 'Унаследован от целевого принтера', - derivedFromTargetHint: 'Используется код доступа целевого принтера. Мост перенаправляет аутентификацию слайсера на реальный принтер, поэтому коды должны совпадать. Чтобы изменить значение, измените код доступа самого принтера.', - reveal: 'Показать код доступа', - hide: 'Скрыть код доступа', + title: "Код доступа", + isSet: "Код доступа задан", + notSet: "Код доступа не задан — он необходим для включения", + placeholder: "Введите 8-значный код", + placeholderChange: "Введите новый код", + hint: "Код должен состоять ровно из 8 символов. Слайсеры используют его для аутентификации.", + charCount: "({{count}}/8)", + inheritedFromTarget: "Унаследован от целевого принтера", + derivedFromTargetHint: "Используется код доступа целевого принтера. Мост перенаправляет аутентификацию слайсера на реальный принтер, поэтому коды должны совпадать. Чтобы изменить значение, измените код доступа самого принтера.", + reveal: "Показать код доступа", + hide: "Скрыть код доступа", }, targetPrinter: { - title: 'Целевой принтер', - configured: 'Целевой принтер для прокси настроен', - notConfigured: 'Целевой принтер не выбран — он необходим для режима прокси', - placeholder: 'Выберите принтер...', - hint: 'Выберите принтер, на который будет перенаправляться трафик слайсера. Принтер должен работать в режиме LAN.', - noPrinters: 'Нет настроенных принтеров. Сначала добавьте принтер.', + title: "Целевой принтер", + configured: "Целевой принтер для прокси настроен", + notConfigured: "Целевой принтер не выбран — он необходим для режима прокси", + placeholder: "Выберите принтер...", + hint: "Выберите принтер, на который будет перенаправляться трафик слайсера. Принтер должен работать в режиме LAN.", + noPrinters: "Нет настроенных принтеров. Сначала добавьте принтер.", }, remoteInterface: { - title: 'Переопределение сетевого интерфейса', - configured: 'Переопределение интерфейса активно', - optional: 'Необязательно — используйте, если IP определён неверно, например при нескольких сетевых картах, Docker или VPN', - placeholder: 'Определять автоматически...', - hint: 'Переопределяет IP-адрес, публикуемый через SSDP и используемый в TLS-сертификате. Полезно, если у Bambuddy несколько сетевых интерфейсов.', + title: "Переопределение сетевого интерфейса", + configured: "Переопределение интерфейса активно", + optional: "Необязательно — используйте, если IP определён неверно, например при нескольких сетевых картах, Docker или VPN", + placeholder: "Определять автоматически...", + hint: "Переопределяет IP-адрес, публикуемый через SSDP и используемый в TLS-сертификате. Полезно, если у Bambuddy несколько сетевых интерфейсов.", }, mode: { - title: 'Режим', - archive: 'Архив', - archiveDesc: 'Сразу сохранять файлы в архив', - review: 'Проверка', - reviewDesc: 'Проверять перед добавлением в архив', - queue: 'Очередь', - queueDesc: 'Сохранять в архив и добавлять в очередь', - proxy: 'Прокси', - proxyDesc: 'Перенаправлять на реальный принтер', + title: "Режим", + archive: "Архив", + archiveDesc: "Сразу сохранять файлы в архив", + review: "Проверка", + reviewDesc: "Проверять перед добавлением в архив", + queue: "Очередь", + queueDesc: "Сохранять в архив и добавлять в очередь", + proxy: "Прокси", + proxyDesc: "Перенаправлять на реальный принтер", }, autoDispatch: { - title: 'Автоматический запуск', - description: 'Автоматически запускать задания после добавления в очередь. Если выключено, задания ожидают ручного запуска.', + title: "Автоматический запуск", + description: "Автоматически запускать задания после добавления в очередь. Если выключено, задания ожидают ручного запуска.", }, queueForceColorMatch: { - title: 'Требовать совпадения цвета', - description: 'Не назначать задание принтеру, если загружены филамент другого типа или другого цвета. По умолчанию выключено: без этой проверки очередь сопоставляет только модель принтера и может выбрать принтер с неподходящим цветом.', + title: "Требовать совпадения цвета", + description: "Не назначать задание принтеру, если загружены филамент другого типа или другого цвета. По умолчанию выключено: без этой проверки очередь сопоставляет только модель принтера и может выбрать принтер с неподходящим цветом.", }, gcodeInjection: { - title: 'Вставка G-code', - description: 'Применять к заданиям этого виртуального принтера фрагменты G-code для соответствующей модели, заданные в настройках. По умолчанию выключено.', + title: "Вставка G-code", + description: "Применять к заданиям этого виртуального принтера фрагменты G-code для соответствующей модели, заданные в настройках. По умолчанию выключено.", }, tailscaleDisabled: { - title: 'Интеграция Tailscale', - description: 'Отметить этот виртуальный принтер как доступный через Tailscale. Будет показан Tailscale-адрес хоста, который нужно указать в слайсере. Импорт сертификата центра сертификации выполняется как обычно; этот переключатель на сертификаты не влияет.', + title: "Интеграция Tailscale", + description: "Отметить этот виртуальный принтер как доступный через Tailscale. Будет показан Tailscale-адрес хоста, который нужно указать в слайсере. Импорт сертификата центра сертификации выполняется как обычно; этот переключатель на сертификаты не влияет.", }, setupRequired: { - title: 'Требуется настройка', - description: 'Для работы виртуального принтера требуется дополнительная настройка системы: перенаправление портов, правила межсетевого экрана и параметры, зависящие от платформы.', - readGuide: 'Перед включением прочитайте руководство по настройке', + title: "Требуется настройка", + description: "Для работы виртуального принтера требуется дополнительная настройка системы: перенаправление портов, правила межсетевого экрана и параметры, зависящие от платформы.", + readGuide: "Перед включением прочитайте руководство по настройке", }, archiveNameSource: { - title: 'Источник названия архива', - description: 'Выберите способ формирования названия новых архивов для файлов, полученных через виртуальный принтер. «Метаданные» использует поле Title, встроенное слайсером в 3MF. «Имя файла» использует имя, переданное Bambu Studio по FTP. Bambu Studio заменяет введённое в окне отправки название значением Title из 3MF, если оно присутствует, поэтому оба режима часто дают одинаковый результат.', - metadata: 'Метаданные', - filename: 'Имя файла', + title: "Источник названия архива", + description: "Выберите способ формирования названия новых архивов для файлов, полученных через виртуальный принтер. «Метаданные» использует поле Title, встроенное слайсером в 3MF. «Имя файла» использует имя, переданное Bambu Studio по FTP. Bambu Studio заменяет введённое в окне отправки название значением Title из 3MF, если оно присутствует, поэтому оба режима часто дают одинаковый результат.", + metadata: "Метаданные", + filename: "Имя файла", }, caCert: { - title: 'Сертификат для слайсера', - description: 'Виртуальные принтеры используют TLS-сертификат, подписанный центром сертификации Bambuddy. Один раз импортируйте этот сертификат CA в хранилище доверенных сертификатов слайсера, чтобы он принимал соединение.', - copy: 'Копировать', - copied: 'Скопировано', - download: 'Скачать', - fingerprint: 'SHA-256', + title: "Сертификат для слайсера", + description: "Виртуальные принтеры используют TLS-сертификат, подписанный центром сертификации Bambuddy. Один раз импортируйте этот сертификат CA в хранилище доверенных сертификатов слайсера, чтобы он принимал соединение.", + copy: "Копировать", + copied: "Скопировано", + download: "Скачать", + fingerprint: "SHA-256", }, howItWorks: { - title: 'Как это работает', - step1: 'В одной локальной сети виртуальные принтеры автоматически появляются в Bambu Studio и OrcaSlicer. Из других сетей добавьте их вручную по IP-адресу и коду доступа.', - step2: 'В режимах «Архив», «Проверка» и «Очередь» используйте кнопку «Отправить» в слайсере для загрузки 3MF в Bambuddy. Слайсер покажет сообщение об успешной печати, но файл будет только сохранён, без запуска печати.', - step3: 'В режиме прокси виртуальный принтер перенаправляет весь трафик на реальный принтер — печать запускается сразу, как при прямом подключении.', + title: "Как это работает", + step1: "В одной локальной сети виртуальные принтеры автоматически появляются в Bambu Studio и OrcaSlicer. Из других сетей добавьте их вручную по IP-адресу и коду доступа.", + step2: "В режимах «Архив», «Проверка» и «Очередь» используйте кнопку «Отправить» в слайсере для загрузки 3MF в Bambuddy. Слайсер покажет сообщение об успешной печати, но файл будет только сохранён, без запуска печати.", + step3: "В режиме прокси виртуальный принтер перенаправляет весь трафик на реальный принтер — печать запускается сразу, как при прямом подключении.", }, status: { - title: 'Сведения о состоянии', - printerName: 'Имя принтера', - model: 'Модель', - serialNumber: 'Серийный номер', - mode: 'Режим', - pendingFiles: 'Ожидающие файлы', - targetPrinter: 'Целевой принтер', - ftpPort: 'Порт FTP', - mqttPort: 'Порт MQTT', - ftpConnections: 'Соединения FTP', - mqttConnections: 'Соединения MQTT', + title: "Сведения о состоянии", + printerName: "Имя принтера", + model: "Модель", + serialNumber: "Серийный номер", + mode: "Режим", + pendingFiles: "Ожидающие файлы", + targetPrinter: "Целевой принтер", + ftpPort: "Порт FTP", + mqttPort: "Порт MQTT", + ftpConnections: "Соединения FTP", + mqttConnections: "Соединения MQTT", }, toast: { - updated: 'Настройки виртуального принтера обновлены', - failedToUpdate: 'Не удалось обновить настройки', - copyFailed: 'Не удалось скопировать — выделите текст вручную', - accessCodeRequired: 'Сначала задайте код доступа', - targetPrinterRequired: 'Сначала выберите целевой принтер', - bindIpRequired: 'Сначала задайте IP-адрес привязки', - accessCodeEmpty: 'Код доступа не может быть пустым', - accessCodeLength: 'Код доступа должен состоять ровно из 8 символов', - targetCodeChangedRebind: 'Код доступа приведён в соответствие с новым целевым принтером. Удалите и снова добавьте устройство в слайсере, чтобы он получил новый код.', - created: 'Виртуальный принтер создан', - failedToCreate: 'Не удалось создать виртуальный принтер', - deleted: 'Виртуальный принтер удалён', - failedToDelete: 'Не удалось удалить виртуальный принтер', + updated: "Настройки виртуального принтера обновлены", + failedToUpdate: "Не удалось обновить настройки", + copyFailed: "Не удалось скопировать — выделите текст вручную", + accessCodeRequired: "Сначала задайте код доступа", + targetPrinterRequired: "Сначала выберите целевой принтер", + bindIpRequired: "Сначала задайте IP-адрес привязки", + accessCodeEmpty: "Код доступа не может быть пустым", + accessCodeLength: "Код доступа должен состоять ровно из 8 символов", + targetCodeChangedRebind: "Код доступа приведён в соответствие с новым целевым принтером. Удалите и снова добавьте устройство в слайсере, чтобы он получил новый код.", + created: "Виртуальный принтер создан", + failedToCreate: "Не удалось создать виртуальный принтер", + deleted: "Виртуальный принтер удалён", + failedToDelete: "Не удалось удалить виртуальный принтер", }, list: { - title: 'Виртуальные принтеры', - add: 'Добавить', - addFirst: 'Добавить виртуальный принтер', - empty: 'Виртуальные принтеры не настроены. Добавьте первый принтер.', + title: "Виртуальные принтеры", + add: "Добавить", + addFirst: "Добавить виртуальный принтер", + empty: "Виртуальные принтеры не настроены. Добавьте первый принтер.", }, bindIp: { - title: 'Интерфейс привязки', - placeholder: 'Выберите интерфейс...', - hint: 'Сетевой интерфейс, к которому будет привязан этот виртуальный принтер. Для каждого принтера он должен быть уникальным.', + title: "Интерфейс привязки", + placeholder: "Выберите интерфейс...", + hint: "Сетевой интерфейс, к которому будет привязан этот виртуальный принтер. Для каждого принтера он должен быть уникальным.", }, proxy: { - accessCodeHint: 'В режиме прокси используйте в слайсере код доступа целевого принтера. Соединение прозрачно перенаправляется на реальный принтер.', + accessCodeHint: "В режиме прокси используйте в слайсере код доступа целевого принтера. Соединение прозрачно перенаправляется на реальный принтер.", }, addDialog: { - title: 'Добавление виртуального принтера', - name: 'Название', - hint: 'После создания можно настроить код доступа, целевой принтер и остальные параметры.', - create: 'Создать', + title: "Добавление виртуального принтера", + name: "Название", + hint: "После создания можно настроить код доступа, целевой принтер и остальные параметры.", + create: "Создать", }, deleteConfirm: { - title: 'Удаление виртуального принтера', - message: 'Удалить «{{name}}»? Все службы этого виртуального принтера будут остановлены.', + title: "Удаление виртуального принтера", + message: "Удалить «{{name}}»? Все службы этого виртуального принтера будут остановлены.", }, }, modelViewer: { - openInSlicer: 'Открыть в слайсере', + openInSlicer: "Открыть в слайсере", tabs: { - model: '3D-модель', - gcode: 'Предпросмотр G-code', + model: "3D-модель", + gcode: "Предпросмотр G-code", }, - notAvailable: 'недоступно', - notSliced: 'не нарезано', - plates: 'Пластины', - allPlates: 'Все пластины', - plateNumber: 'Пластина {{number}}', - plateCount: '{{count}} пластина', - plateCount_other: '{{count}} пластин', - objectCount: '{{count}} объект', - objectCount_other: '{{count}} объектов', - filamentCount: '{{count}} филамент', - filamentCount_other: '{{count}} филаментов', - eta: 'Осталось {{minutes}} мин', - noPreview: 'Для этого файла нет предпросмотра', + notAvailable: "недоступно", + notSliced: "не нарезано", + plates: "Пластины", + allPlates: "Все пластины", + plateNumber: "Пластина {{number}}", + plateCount: "{{count}} пластина", + plateCount_other: "{{count}} пластин", + objectCount: "{{count}} объект", + objectCount_other: "{{count}} объектов", + filamentCount: "{{count}} филамент", + filamentCount_other: "{{count}} филаментов", + eta: "Осталось {{minutes}} мин", + noPreview: "Для этого файла нет предпросмотра", pagination: { - pageOf: 'Страница {{current}} из {{total}}', - prev: 'Назад', - next: 'Далее', + pageOf: "Страница {{current}} из {{total}}", + prev: "Назад", + next: "Далее", }, errors: { - failedToLoad: 'Не удалось загрузить файл', - noMeshes: 'В файле 3MF не найдены сеточные модели', - unsupportedFormat: 'Неподдерживаемый формат файла', + failedToLoad: "Не удалось загрузить файл", + noMeshes: "В файле 3MF не найдены сеточные модели", + unsupportedFormat: "Неподдерживаемый формат файла", }, }, maintenanceDescriptions: { - lubricateCarbonRods: 'Смазать углепластиковые направляющие для плавного перемещения', - lubricateRails: 'Смазать линейные направляющие для плавного перемещения', - cleanNozzle: 'Очистить хотэнд и сопло для предотвращения засоров', - checkBelts: 'Проверить натяжение ремней для сохранения точности печати', - cleanBuildPlate: 'Очистить печатную пластину для улучшения адгезии', - checkExtruder: 'Проверить шестерни экструдера на износ', - checkCooling: 'Проверить исправность вентиляторов охлаждения', - generalInspection: 'Общий осмотр принтера', - cleanCarbonRods: 'Очистить углепластиковые направляющие для уменьшения трения', - lubricateSteelRods: 'Смазать стальные направляющие для плавного перемещения', - cleanSteelRods: 'Очистить стальные направляющие для уменьшения трения', - cleanLinearRails: 'Протереть линейные направляющие от пыли и загрязнений', - checkPtfeTube: 'Проверить PTFE-трубку на износ и повреждения', - replaceHepaFilter: 'Заменить HEPA-фильтр', - replaceCarbonFilter: 'Заменить угольный фильтр', - lubricateLeftNozzleRail: 'Смазать направляющую левого сопла (серия H2)', + lubricateCarbonRods: "Смазать углепластиковые направляющие для плавного перемещения", + lubricateRails: "Смазать линейные направляющие для плавного перемещения", + cleanNozzle: "Очистить хотэнд и сопло для предотвращения засоров", + checkBelts: "Проверить натяжение ремней для сохранения точности печати", + cleanBuildPlate: "Очистить печатную пластину для улучшения адгезии", + checkExtruder: "Проверить шестерни экструдера на износ", + checkCooling: "Проверить исправность вентиляторов охлаждения", + generalInspection: "Общий осмотр принтера", + cleanCarbonRods: "Очистить углепластиковые направляющие для уменьшения трения", + lubricateSteelRods: "Смазать стальные направляющие для плавного перемещения", + cleanSteelRods: "Очистить стальные направляющие для уменьшения трения", + cleanLinearRails: "Протереть линейные направляющие от пыли и загрязнений", + checkPtfeTube: "Проверить PTFE-трубку на износ и повреждения", + replaceHepaFilter: "Заменить HEPA-фильтр", + replaceCarbonFilter: "Заменить угольный фильтр", + lubricateLeftNozzleRail: "Смазать направляющую левого сопла (серия H2)", }, smartPlugs: { - offline: 'Не в сети', - admin: 'Управление', - openPlugAdminPage: 'Открыть страницу управления розеткой', - deleteSmartPlug: 'Удалить умную розетку', - turnOnSmartPlug: 'Включить умную розетку', - turnOffSmartPlug: 'Выключить умную розетку', - turnOn: 'Включить', - turnOff: 'Выключить', + offline: "Не в сети", + admin: "Управление", + openPlugAdminPage: "Открыть страницу управления розеткой", + deleteSmartPlug: "Удалить умную розетку", + turnOnSmartPlug: "Включить умную розетку", + turnOffSmartPlug: "Выключить умную розетку", + turnOn: "Включить", + turnOff: "Выключить", addSmartPlug: { - scanningNetwork: 'Сканирование сети...', - chooseEntity: 'Выберите сущность...', - connectionFailed: 'Ошибка подключения', - searchEntities: 'Поиск сущностей...', - searchPowerSensors: 'Поиск датчиков мощности...', - searchEnergySensors: 'Поиск датчиков энергии...', + scanningNetwork: "Сканирование сети...", + chooseEntity: "Выберите сущность...", + connectionFailed: "Ошибка подключения", + searchEntities: "Поиск сущностей...", + searchPowerSensors: "Поиск датчиков мощности...", + searchEnergySensors: "Поиск датчиков энергии...", placeholders: { - plugName: 'Розетка в гостиной', - mqttStateOnValue: 'ON, true, 1', - mqttSameAsPower: 'Та же тема, что для мощности, или другая', + plugName: "Розетка в гостиной", + mqttStateOnValue: "ON, true, 1", + mqttSameAsPower: "Та же тема, что для мощности, или другая", }, }, - linkedTo: 'Связана с:', - monitorOnly: 'Только мониторинг', - alerts: 'Оповещения', - scheduleOn: 'Включение в {{time}}', - scheduleOff: 'Выключение в {{time}}', - on: 'Включена', - off: 'Выключена', - power: 'Мощность', - kwhToday: 'кВт·ч сегодня', - settings: 'Настройки', - automationSettings: 'Настройки автоматизации', - showInSwitchbar: 'Показывать на панели переключателей', - quickAccessSidebar: 'Быстрый доступ с боковой панели', - enabled: 'Включено', - enableAutomation: 'Включить автоматизацию для этой розетки', - autoOn: 'Автовключение', - autoOnDescription: 'Включать при начале печати', - autoOff: 'Автовыключение', - autoOffDescription: 'Выключать после завершения печати (однократно)', - autoOffPersistent: 'Оставлять включённым', - autoOffPersistentDescription: 'Не сбрасывать автовыключение после печати', - autoOffAfterDrying: 'Выключать после сушки', - autoOffAfterDryingDescription: 'Выключать после завершения сушки в AMS', - delayAfterDryingMinutes: 'Задержка после сушки (мин)', - turnOffDelayMode: 'Условие отложенного выключения', - time: 'Время', - temp: 'Температура', - delayMinutes: 'Задержка (мин)', - tempThreshold: 'Порог температуры (°C)', - tempThresholdDescription: 'Выключать, когда сопло остынет ниже этой температуры', - edit: 'Изменить', - deleteConfirm: 'Удалить «{{name}}»? Это действие нельзя отменить.', - turnOnConfirm: 'Включить «{{name}}»?', - turnOffConfirm: 'Выключить «{{name}}»? Питание подключённого устройства будет отключено.', - failedToTurn: 'Не удалось {{action}} «{{name}}»', - unknown: 'Неизвестно', - addTitle: 'Добавление умной розетки', - editTitle: 'Редактирование умной розетки', - stopScanning: 'Остановить сканирование', - discoverTasmota: 'Найти устройства Tasmota', - foundDevices: 'Найдено устройств: {{count}}. Нажмите для выбора:', - noDevicesFound: 'Устройства Tasmota в сети не найдены', - haNotConfigured: 'Home Assistant не настроен. Настройте его в разделе', - haSettingsPath: 'Настройки → Сеть → Home Assistant', - selectEntity: 'Выберите сущность *', - ipAddress: 'IP-адрес *', - nameLabel: 'Название *', - username: 'Имя пользователя', - password: 'Пароль', - authHint: 'Оставьте пустым, если устройство Tasmota не требует аутентификации', - linkToPrinter: 'Связать с принтером', - noPrinter: 'Без принтера — только ручное управление', - linkingDescription: 'Связь позволяет автоматически включать и выключать питание при начале и завершении печати', - powerAlerts: 'Оповещения о мощности', - alertAbove: 'Оповещать выше (Вт)', - alertBelow: 'Оповещать ниже (Вт)', - alertDescription: 'Отправлять уведомление при пересечении заданных порогов мощности. Оставьте поле пустым, чтобы отключить соответствующий порог.', - dailySchedule: 'Ежедневное расписание', - turnOnAt: 'Включать в', - turnOffAt: 'Выключать в', - scheduleDescription: 'Ежедневно включать и выключать розетку в указанное время. Оставьте поле пустым, чтобы не выполнять соответствующее действие.', - showOnPrinterCard: 'Показывать в карточке принтера', - displayOnPrinterCard: 'Показывать кнопку в карточке принтера', - connectedResult: 'Подключено!', - deviceLabel: 'Устройство: {{name}} — ', - stateLabel: 'Состояние: {{state}}', - test: 'Проверить', - delete: 'Удалить', - save: 'Сохранить', - add: 'Добавить', - cancel: 'Отмена', - failedToStartScan: 'Не удалось запустить сканирование', - nameRequired: 'Укажите название', - entityRequired: 'Для розеток Home Assistant необходимо выбрать сущность', - mqttTopicRequired: 'Для мониторинга мощности, энергии или состояния необходимо настроить хотя бы одну тему MQTT', - loadingEntities: 'Загрузка сущностей...', - loading: 'Загрузка...', - failedToLoadEntities: 'Не удалось загрузить сущности: {{error}}', - noEntitiesMatching: 'Сущности по запросу «{{search}}» не найдены', - noEntitiesAvailable: 'Нет доступных сущностей', - searchingEntities: 'Поиск по всем сущностям, найдено: {{count}}', - showingEntities: 'Показаны switch, light и input_boolean, доступно: {{count}}', - energyMonitoringOptional: 'Мониторинг энергии (необязательно)', - energyMonitoringHint: 'Найдите и выберите датчики мощности и энергии.', - powerSensorW: 'Датчик мощности (Вт)', - energyTodayKwh: 'Энергия за сегодня (кВт·ч)', - totalEnergyKwh: 'Общая энергия (кВт·ч)', - noMatchingSensors: 'Подходящие датчики не найдены', - none: 'Нет', - mqttNotConfigured: 'Брокер MQTT не настроен. Укажите его адрес в разделе', - mqttSettingsPath: 'Настройки → Сеть → Публикация MQTT', - mqttNotConfiguredSuffix: 'Включать публикацию не обязательно — достаточно заполнить параметры брокера.', - mqttMonitorOnlyDescription: 'Розетки MQTT получают данные мощности и энергии через подписку MQTT. Управление включением и выключением недоступно — используйте брокер MQTT или систему домашней автоматизации.', - powerMonitoring: 'Мониторинг мощности', - energyMonitoring: 'Мониторинг энергии', - stateMonitoring: 'Мониторинг состояния', - optional: 'необязательно', - topic: 'Тема', - jsonPath: 'Путь JSON', - multiplier: 'Множитель', - onValue: 'Значение «включено»', - mqttPowerHint: 'Путь JSON извлекает значение из полезной нагрузки JSON, например «power_l1». Оставьте пустым, если тема публикует число напрямую.\nДля преобразования мВт→Вт используйте 0.001, для кВт→Вт — 1000.', - mqttEnergyHint: 'Путь JSON извлекает значение из полезной нагрузки JSON. Оставьте пустым для числового значения напрямую.\nДля преобразования Вт·ч→кВт·ч используйте 0.001, для МВт·ч→кВт·ч — 1000.', - mqttStateHint: 'Путь JSON извлекает значение из полезной нагрузки JSON. Оставьте пустым для значения напрямую.\nЗначение «включено» — точная строка, означающая включённое состояние. Оставьте пустым для автоматического распознавания ON, true и 1.', - restControl: 'Управление', - restOnUrl: 'URL включения', - restOffUrl: 'URL выключения', - restOnBody: 'Тело запроса включения', - restOffBody: 'Тело запроса выключения', - restMethod: 'Метод HTTP', - restHeaders: 'Пользовательские заголовки (JSON)', - restStatusUrl: 'URL состояния', - restStatusPath: 'Путь JSON состояния', - restStatusOnValue: 'Значение «включено»', - restPowerUrl: 'URL мощности', - restPowerPath: 'Путь JSON мощности', - restPowerMultiplier: 'Множитель мощности', - restEnergyUrl: 'URL энергии', - restEnergyPath: 'Путь JSON энергии', - restEnergyMultiplier: 'Множитель энергии', - restUrlRequired: 'Для REST-розетки необходимо указать хотя бы один URL: включения или выключения', - restHeadersHint: 'например, {"Authorization": "Bearer your-token"}', - restBodyHint: 'например, ON или {"state": "on"}', - restStatusHint: 'URL для опроса текущего состояния', - restPathHint: 'например, state или data.power.status', - restPowerUrlHint: 'Отдельный URL данных мощности; если пусто, используется URL состояния', - restEnergyUrlHint: 'Отдельный URL данных энергии; если пусто, используется URL состояния', - restEnergyHint: 'Для каждого значения можно использовать отдельный URL либо URL состояния. Множители используются для преобразования единиц, например 0.001 для Вт·ч→кВт·ч.', - testConnection: 'Проверить подключение', - connectionSuccess: 'Подключение успешно', - noSwitchesInSwitchbar: 'На панели переключателей нет устройств', - enableSwitchbarHint: 'Включите «Показывать на панели переключателей» в разделе «Настройки → Умные розетки»', + linkedTo: "Связана с:", + monitorOnly: "Только мониторинг", + alerts: "Оповещения", + scheduleOn: "Включение в {{time}}", + scheduleOff: "Выключение в {{time}}", + on: "Включена", + off: "Выключена", + power: "Мощность", + kwhToday: "кВт·ч сегодня", + settings: "Настройки", + automationSettings: "Настройки автоматизации", + showInSwitchbar: "Показывать на панели переключателей", + quickAccessSidebar: "Быстрый доступ с боковой панели", + enabled: "Включено", + enableAutomation: "Включить автоматизацию для этой розетки", + autoOn: "Автовключение", + autoOnDescription: "Включать при начале печати", + autoOff: "Автовыключение", + autoOffDescription: "Выключать после завершения печати (однократно)", + autoOffPersistent: "Оставлять включённым", + autoOffPersistentDescription: "Не сбрасывать автовыключение после печати", + autoOffAfterDrying: "Выключать после сушки", + autoOffAfterDryingDescription: "Выключать после завершения сушки в AMS", + delayAfterDryingMinutes: "Задержка после сушки (мин)", + turnOffDelayMode: "Условие отложенного выключения", + time: "Время", + temp: "Температура", + delayMinutes: "Задержка (мин)", + tempThreshold: "Порог температуры (°C)", + tempThresholdDescription: "Выключать, когда сопло остынет ниже этой температуры", + edit: "Изменить", + deleteConfirm: "Удалить «{{name}}»? Это действие нельзя отменить.", + turnOnConfirm: "Включить «{{name}}»?", + turnOffConfirm: "Выключить «{{name}}»? Питание подключённого устройства будет отключено.", + failedToTurn: "Не удалось {{action}} «{{name}}»", + unknown: "Неизвестно", + addTitle: "Добавление умной розетки", + editTitle: "Редактирование умной розетки", + stopScanning: "Остановить сканирование", + discoverTasmota: "Найти устройства Tasmota", + foundDevices: "Найдено устройств: {{count}}. Нажмите для выбора:", + noDevicesFound: "Устройства Tasmota в сети не найдены", + haNotConfigured: "Home Assistant не настроен. Настройте его в разделе", + haSettingsPath: "Настройки → Сеть → Home Assistant", + selectEntity: "Выберите сущность *", + ipAddress: "IP-адрес *", + nameLabel: "Название *", + username: "Имя пользователя", + password: "Пароль", + authHint: "Оставьте пустым, если устройство Tasmota не требует аутентификации", + linkToPrinter: "Связать с принтером", + noPrinter: "Без принтера — только ручное управление", + linkingDescription: "Связь позволяет автоматически включать и выключать питание при начале и завершении печати", + powerAlerts: "Оповещения о мощности", + alertAbove: "Оповещать выше (Вт)", + alertBelow: "Оповещать ниже (Вт)", + alertDescription: "Отправлять уведомление при пересечении заданных порогов мощности. Оставьте поле пустым, чтобы отключить соответствующий порог.", + dailySchedule: "Ежедневное расписание", + turnOnAt: "Включать в", + turnOffAt: "Выключать в", + scheduleDescription: "Ежедневно включать и выключать розетку в указанное время. Оставьте поле пустым, чтобы не выполнять соответствующее действие.", + showOnPrinterCard: "Показывать в карточке принтера", + displayOnPrinterCard: "Показывать кнопку в карточке принтера", + connectedResult: "Подключено!", + deviceLabel: "Устройство: {{name}} — ", + stateLabel: "Состояние: {{state}}", + test: "Проверить", + delete: "Удалить", + save: "Сохранить", + add: "Добавить", + cancel: "Отмена", + failedToStartScan: "Не удалось запустить сканирование", + nameRequired: "Укажите название", + entityRequired: "Для розеток Home Assistant необходимо выбрать сущность", + mqttTopicRequired: "Для мониторинга мощности, энергии или состояния необходимо настроить хотя бы одну тему MQTT", + loadingEntities: "Загрузка сущностей...", + loading: "Загрузка...", + failedToLoadEntities: "Не удалось загрузить сущности: {{error}}", + noEntitiesMatching: "Сущности по запросу «{{search}}» не найдены", + noEntitiesAvailable: "Нет доступных сущностей", + searchingEntities: "Поиск по всем сущностям, найдено: {{count}}", + showingEntities: "Показаны switch, light и input_boolean, доступно: {{count}}", + energyMonitoringOptional: "Мониторинг энергии (необязательно)", + energyMonitoringHint: "Найдите и выберите датчики мощности и энергии.", + powerSensorW: "Датчик мощности (Вт)", + energyTodayKwh: "Энергия за сегодня (кВт·ч)", + totalEnergyKwh: "Общая энергия (кВт·ч)", + noMatchingSensors: "Подходящие датчики не найдены", + none: "Нет", + mqttNotConfigured: "Брокер MQTT не настроен. Укажите его адрес в разделе", + mqttSettingsPath: "Настройки → Сеть → Публикация MQTT", + mqttNotConfiguredSuffix: "Включать публикацию не обязательно — достаточно заполнить параметры брокера.", + mqttMonitorOnlyDescription: "Розетки MQTT получают данные мощности и энергии через подписку MQTT. Управление включением и выключением недоступно — используйте брокер MQTT или систему домашней автоматизации.", + powerMonitoring: "Мониторинг мощности", + energyMonitoring: "Мониторинг энергии", + stateMonitoring: "Мониторинг состояния", + optional: "необязательно", + topic: "Тема", + jsonPath: "Путь JSON", + multiplier: "Множитель", + onValue: "Значение «включено»", + mqttPowerHint: "Путь JSON извлекает значение из полезной нагрузки JSON, например «power_l1». Оставьте пустым, если тема публикует число напрямую.\nДля преобразования мВт→Вт используйте 0.001, для кВт→Вт — 1000.", + mqttEnergyHint: "Путь JSON извлекает значение из полезной нагрузки JSON. Оставьте пустым для числового значения напрямую.\nДля преобразования Вт·ч→кВт·ч используйте 0.001, для МВт·ч→кВт·ч — 1000.", + mqttStateHint: "Путь JSON извлекает значение из полезной нагрузки JSON. Оставьте пустым для значения напрямую.\nЗначение «включено» — точная строка, означающая включённое состояние. Оставьте пустым для автоматического распознавания ON, true и 1.", + restControl: "Управление", + restOnUrl: "URL включения", + restOffUrl: "URL выключения", + restOnBody: "Тело запроса включения", + restOffBody: "Тело запроса выключения", + restMethod: "Метод HTTP", + restHeaders: "Пользовательские заголовки (JSON)", + restStatusUrl: "URL состояния", + restStatusPath: "Путь JSON состояния", + restStatusOnValue: "Значение «включено»", + restPowerUrl: "URL мощности", + restPowerPath: "Путь JSON мощности", + restPowerMultiplier: "Множитель мощности", + restEnergyUrl: "URL энергии", + restEnergyPath: "Путь JSON энергии", + restEnergyTotalPath: "Путь JSON полной энергии", + restEnergyTotalMultiplier: "Множитель полной энергии", + restEnergyTotalPathHint: "например, aenergy.total", + restEnergyTotalHint: "Многие розетки, включая все устройства Shelly, передают только накопительный счётчик энергии, который никогда не сбрасывается. Его следует указывать здесь, а не в поле выше: если считать его суточным расходом, значение не сбросится в полночь, а данные за вчера и общий расход останутся пустыми. Bambuddy вычисляет расход за сегодня и вчера по этому счётчику; для накопления данных потребуется один-два дня. Shelly передаёт ватт-часы, поэтому используйте множитель 0.001.", + restEnergyMultiplier: "Множитель энергии", + restUrlRequired: "Для REST-розетки необходимо указать хотя бы один URL: включения или выключения", + restHeadersHint: "например, {\"Authorization\": \"Bearer your-token\"}", + restBodyHint: "например, ON или {\"state\": \"on\"}", + restStatusHint: "URL для опроса текущего состояния", + restPathHint: "например, state или data.power.status", + restPowerUrlHint: "Отдельный URL данных мощности; если пусто, используется URL состояния", + restEnergyUrlHint: "Отдельный URL данных энергии; если пусто, используется URL состояния", + restEnergyHint: "Для каждого значения можно использовать отдельный URL либо URL состояния. Множители используются для преобразования единиц, например 0.001 для Вт·ч→кВт·ч.", + testConnection: "Проверить подключение", + connectionSuccess: "Подключение успешно", + noSwitchesInSwitchbar: "На панели переключателей нет устройств", + enableSwitchbarHint: "Включите «Показывать на панели переключателей» в разделе «Настройки → Умные розетки»", }, notifications: { providerTypes: { - callmebot: 'CallMeBot/WhatsApp', - ntfy: 'ntfy', - pushover: 'Pushover', - telegram: 'Telegram', - email: 'Электронная почта', - discord: 'Discord', - webhook: 'Вебхук', - homeassistant: 'Home Assistant', + callmebot: "CallMeBot/WhatsApp", + ntfy: "ntfy", + pushover: "Pushover", + telegram: "Telegram", + email: "Электронная почта", + discord: "Discord", + webhook: "Вебхук", + homeassistant: "Home Assistant", }, providerDescriptions: { - email: 'Уведомления по электронной почте через SMTP', - telegram: 'Уведомления через Telegram-бота', - discord: 'Отправка в канал Discord через вебхук', - ntfy: 'Бесплатные push-уведомления с возможностью самостоятельного размещения сервера', - pushover: 'Простые и надёжные push-уведомления', - callmebot: 'Бесплатные уведомления WhatsApp через CallMeBot', - webhook: 'Универсальный HTTP POST-запрос на любой URL', - homeassistant: 'Постоянные уведомления на панели Home Assistant', + email: "Уведомления по электронной почте через SMTP", + telegram: "Уведомления через Telegram-бота", + discord: "Отправка в канал Discord через вебхук", + ntfy: "Бесплатные push-уведомления с возможностью самостоятельного размещения сервера", + pushover: "Простые и надёжные push-уведомления", + callmebot: "Бесплатные уведомления WhatsApp через CallMeBot", + webhook: "Универсальный HTTP POST-запрос на любой URL", + homeassistant: "Постоянные уведомления на панели Home Assistant", }, - lastSuccess: 'Последняя отправка: {{date}}', - error: 'Ошибка', - printer: 'Принтер:', - allPrinters: 'Все принтеры', - sendTestNotification: 'Отправить тестовое уведомление', - eventSettings: 'Настройки событий', - enabled: 'Включено', - sendFromProvider: 'Отправлять уведомления через этого провайдера', - printEvents: 'События печати', - printerStatus: 'Состояние принтера', - amsAlarms: 'Предупреждения AMS', - amsHtAlarms: 'Предупреждения AMS-HT', - printQueue: 'Очередь печати', - start: 'Запуск', - plateCheck: 'Проверка пластины', - complete: 'Завершено', - failed: 'Ошибка', - stopped: 'Остановлено', - progress: 'Ход печати', - offline: 'Не в сети', - lowFilament: 'Мало филамента', - maintenance: 'Обслуживание', - amsHumidity: 'Влажность AMS', - amsTemp: 'Температура AMS', - amsHtHumidity: 'Влажность AMS-HT', - amsHtTemp: 'Температура AMS-HT', - bedCooled: 'Стол остыл', - firstLayer: 'Первый слой', - quiet: 'Тихий режим', - digest: 'Сводка в {{time}}', - printStarted: 'Печать началась', - plateNotEmpty: 'Пластина не пуста', - plateNotEmptyDescription: 'Перед печатью на пластине обнаружены объекты', - printCompleted: 'Печать завершена', - bedCooledLabel: 'Стол остыл', - bedCooledDescription: 'После печати температура стола опустилась ниже заданного порога', - firstLayerCompleteLabel: 'Первый слой завершён', - firstLayerCompleteDescription: 'Уведомить со снимком после завершения первого слоя', - missingSpoolAssignmentLabel: 'Катушка не назначена', - missingSpoolAssignmentDescription: 'Уведомить при запуске печати, если для необходимых слотов не назначены катушки', - printFailed: 'Ошибка печати', - printStopped: 'Печать остановлена', - progressMilestones: 'Этапы выполнения', - progressMilestonesDescription: 'Уведомлять при 25%, 50% и 75%', - printerOffline: 'Принтер не в сети', - printerError: 'Ошибка принтера', - aiFailureDetection: 'Обнаружение сбоев с помощью ИИ', - aiFailureDetectionDescription: 'Уведомить, если Obico AI обнаружит возможный сбой печати', - lowFilamentLabel: 'Заканчивается филамент', - maintenanceDue: 'Требуется обслуживание', - maintenanceDueDescription: 'Уведомить, когда принтеру потребуется обслуживание', - amsHumidityHigh: 'Высокая влажность в AMS', - amsHumidityHighDescription: 'Влажность в обычной AMS превысила заданный порог', - amsTemperatureHigh: 'Высокая температура в AMS', - amsTemperatureHighDescription: 'Температура в обычной AMS превысила заданный порог', - amsHtHumidityHigh: 'Высокая влажность в AMS-HT', - amsHtHumidityHighDescription: 'Влажность в AMS-HT превысила заданный порог', - amsHtTemperatureHigh: 'Высокая температура в AMS-HT', - amsHtTemperatureHighDescription: 'Температура в AMS-HT превысила заданный порог', - inventoryAlerts: 'Уведомления о запасах', - stockReorderAlert: 'Пора заказывать', - stockReorderAlertDescription: 'Запас по SKU достиг точки повторного заказа', - stockBreakAlert: 'Риск исчерпания запаса', - stockBreakAlertDescription: 'Запас закончится раньше, чем поступит пополнение', - jobAdded: 'Задание добавлено', - jobAddedDescription: 'Задание добавлено в очередь', - jobAssigned: 'Задание назначено', - jobAssignedDescription: 'Задание по модели назначено принтеру', - jobStarted: 'Задание запущено', - jobStartedDescription: 'Задание из очереди отправлено на печать', - jobWaiting: 'Задание ожидает', - jobWaitingDescription: 'Задание ожидает филамент или свободный принтер', - jobSkipped: 'Задание пропущено', - jobSkippedDescription: 'Задание пропущено из-за ошибки предыдущего', - jobFailed: 'Не удалось запустить задание', - jobFailedDescription: 'Задание не удалось отправить на печать', - queueComplete: 'Очередь завершена', - queueCompleteDescription: 'Все задания в очереди завершены', - quietHours: 'Тихие часы', - noNotificationsDuring: 'Не отправлять уведомления в это время', - editProviderToChangeQuietHours: 'Измените провайдера, чтобы настроить тихие часы', - dailyDigest: 'Ежедневная сводка', - batchNotifications: 'Объединять уведомления в одну ежедневную сводку', - sendAt: 'Отправлять в {{time}}', - editProviderToChangeDigestTime: 'Измените провайдера, чтобы настроить время сводки', - edit: 'Изменить', - deleteProvider: 'Удалить провайдера уведомлений', - deleteConfirm: 'Удалить «{{name}}»? Это действие нельзя отменить.', - delete: 'Удалить', - addTitle: 'Добавить провайдера уведомлений', - editTitle: 'Изменить провайдера уведомлений', - nameLabel: 'Название *', - namePlaceholder: 'Мои уведомления', - providerTypeLabel: 'Тип провайдера *', - configuration: 'Настройка', - testConfiguration: 'Проверить настройку', - printerFilter: 'Фильтр принтеров', - onlyFromPrinter: 'Отправлять уведомления только о событиях этого принтера', - quietHoursDnd: 'Тихие часы (не беспокоить)', - quietStart: 'Начало', - quietEnd: 'Окончание', - dailyDigestLabel: 'Ежедневная сводка', - sendDigestAt: 'Отправлять сводку в', - digestCollected: 'События будут собраны и отправлены одной сводкой в указанное время', - notificationEvents: 'События для уведомлений', - progressPercent: '(25%, 50%, 75%)', - bedCooledAfterPrint: '(после завершения печати)', + lastSuccess: "Последняя отправка: {{date}}", + error: "Ошибка", + printer: "Принтер:", + allPrinters: "Все принтеры", + sendTestNotification: "Отправить тестовое уведомление", + eventSettings: "Настройки событий", + enabled: "Включено", + sendFromProvider: "Отправлять уведомления через этого провайдера", + printEvents: "События печати", + printerStatus: "Состояние принтера", + amsAlarms: "Предупреждения AMS", + amsHtAlarms: "Предупреждения AMS-HT", + printQueue: "Очередь печати", + start: "Запуск", + plateCheck: "Проверка пластины", + complete: "Завершено", + failed: "Ошибка", + stopped: "Остановлено", + progress: "Ход печати", + offline: "Не в сети", + lowFilament: "Мало филамента", + maintenance: "Обслуживание", + amsHumidity: "Влажность AMS", + amsTemp: "Температура AMS", + amsHtHumidity: "Влажность AMS-HT", + amsHtTemp: "Температура AMS-HT", + bedCooled: "Стол остыл", + firstLayer: "Первый слой", + quiet: "Тихий режим", + digest: "Сводка в {{time}}", + printStarted: "Печать началась", + plateNotEmpty: "Пластина не пуста", + plateNotEmptyDescription: "Перед печатью на пластине обнаружены объекты", + printCompleted: "Печать завершена", + bedCooledLabel: "Стол остыл", + bedCooledDescription: "После печати температура стола опустилась ниже заданного порога", + firstLayerCompleteLabel: "Первый слой завершён", + firstLayerCompleteDescription: "Уведомить со снимком после завершения первого слоя", + missingSpoolAssignmentLabel: "Катушка не назначена", + missingSpoolAssignmentDescription: "Уведомить при запуске печати, если для необходимых слотов не назначены катушки", + printFailed: "Ошибка печати", + printStopped: "Печать остановлена", + progressMilestones: "Этапы выполнения", + progressMilestonesDescription: "Уведомлять при 25%, 50% и 75%", + printerOffline: "Принтер не в сети", + printerError: "Ошибка принтера", + aiFailureDetection: "Обнаружение сбоев с помощью ИИ", + aiFailureDetectionDescription: "Уведомить, если Obico AI обнаружит возможный сбой печати", + lowFilamentLabel: "Заканчивается филамент", + maintenanceDue: "Требуется обслуживание", + maintenanceDueDescription: "Уведомить, когда принтеру потребуется обслуживание", + amsHumidityHigh: "Высокая влажность в AMS", + amsHumidityHighDescription: "Влажность в обычной AMS превысила заданный порог", + amsTemperatureHigh: "Высокая температура в AMS", + amsTemperatureHighDescription: "Температура в обычной AMS превысила заданный порог", + amsHtHumidityHigh: "Высокая влажность в AMS-HT", + amsHtHumidityHighDescription: "Влажность в AMS-HT превысила заданный порог", + amsHtTemperatureHigh: "Высокая температура в AMS-HT", + amsHtTemperatureHighDescription: "Температура в AMS-HT превысила заданный порог", + inventoryAlerts: "Уведомления о запасах", + stockReorderAlert: "Пора заказывать", + stockReorderAlertDescription: "Запас по SKU достиг точки повторного заказа", + stockBreakAlert: "Риск исчерпания запаса", + stockBreakAlertDescription: "Запас закончится раньше, чем поступит пополнение", + jobAdded: "Задание добавлено", + jobAddedDescription: "Задание добавлено в очередь", + jobAssigned: "Задание назначено", + jobAssignedDescription: "Задание по модели назначено принтеру", + jobStarted: "Задание запущено", + jobStartedDescription: "Задание из очереди отправлено на печать", + jobWaiting: "Задание ожидает", + jobWaitingDescription: "Задание ожидает филамент или свободный принтер", + jobSkipped: "Задание пропущено", + jobSkippedDescription: "Задание пропущено из-за ошибки предыдущего", + jobFailed: "Не удалось запустить задание", + jobFailedDescription: "Задание не удалось отправить на печать", + queueComplete: "Очередь завершена", + queueCompleteDescription: "Все задания в очереди завершены", + quietHours: "Тихие часы", + noNotificationsDuring: "Не отправлять уведомления в это время", + editProviderToChangeQuietHours: "Измените провайдера, чтобы настроить тихие часы", + dailyDigest: "Ежедневная сводка", + batchNotifications: "Объединять уведомления в одну ежедневную сводку", + sendAt: "Отправлять в {{time}}", + editProviderToChangeDigestTime: "Измените провайдера, чтобы настроить время сводки", + edit: "Изменить", + deleteProvider: "Удалить провайдера уведомлений", + deleteConfirm: "Удалить «{{name}}»? Это действие нельзя отменить.", + delete: "Удалить", + addTitle: "Добавить провайдера уведомлений", + editTitle: "Изменить провайдера уведомлений", + nameLabel: "Название *", + namePlaceholder: "Мои уведомления", + providerTypeLabel: "Тип провайдера *", + configuration: "Настройка", + testConfiguration: "Проверить настройку", + printerFilter: "Фильтр принтеров", + onlyFromPrinter: "Отправлять уведомления только о событиях этого принтера", + quietHoursDnd: "Тихие часы (не беспокоить)", + quietStart: "Начало", + quietEnd: "Окончание", + dailyDigestLabel: "Ежедневная сводка", + sendDigestAt: "Отправлять сводку в", + digestCollected: "События будут собраны и отправлены одной сводкой в указанное время", + notificationEvents: "События для уведомлений", + progressPercent: "(25%, 50%, 75%)", + bedCooledAfterPrint: "(после завершения печати)", eventPriority: { - sectionTitle: 'Приоритет ntfy', - helpNtfy: 'Выберите приоритет для каждого включённого события. ntfy использует его для усиления оповещений: звука, видимости и поведения push-уведомлений. Для неуказанных событий используется приоритет по умолчанию сервера ntfy.', - min: 'Минимальный', - low: 'Низкий', - default: 'Обычный', - high: 'Высокий', - urgent: 'Срочный', + sectionTitle: "Приоритет ntfy", + helpNtfy: "Выберите приоритет для каждого включённого события. ntfy использует его для усиления оповещений: звука, видимости и поведения push-уведомлений. Для неуказанных событий используется приоритет по умолчанию сервера ntfy.", + min: "Минимальный", + low: "Низкий", + default: "Обычный", + high: "Высокий", + urgent: "Срочный", }, - cancel: 'Отмена', - save: 'Сохранить', - add: 'Добавить', - nameRequired: 'Укажите название', - fieldRequired: 'Поле «{{field}}» обязательно', - phoneNumber: 'Номер телефона', - apiKey: 'Ключ API', - serverUrl: 'URL сервера', - topic: 'Тема', - authToken: 'Токен авторизации', - userKey: 'Ключ пользователя', - appToken: 'Токен приложения', - priority: 'Приоритет', - botToken: 'Токен бота', - chatId: 'ID чата', - smtpServer: 'SMTP-сервер', - smtpPort: 'Порт SMTP', - security: 'Защита соединения', - authentication: 'Аутентификация', - username: 'Имя пользователя', - password: 'Пароль', - fromEmail: 'Адрес отправителя', - toEmail: 'Адрес получателя', - webhookUrl: 'URL вебхука', - payloadFormat: 'Формат данных', - authorization: 'Авторизация', - titleFieldName: 'Имя поля заголовка', - messageFieldName: 'Имя поля сообщения', - editTemplate: 'Изменить шаблон: {{name}}', - titleLabel: 'Заголовок', - bodyLabel: 'Текст', - titlePlaceholder: 'Заголовок уведомления...', - bodyPlaceholder: 'Текст уведомления...', - availableVariables: 'Доступные переменные', - clickToInsert: 'Нажмите, чтобы вставить в позицию курсора в тексте', - livePreview: 'Предварительный просмотр', - hide: 'Скрыть', - show: 'Показать', - loadingPreview: 'Загрузка предварительного просмотра...', - enterTemplateContent: 'Введите содержимое шаблона для предварительного просмотра', - titlePreview: 'Заголовок:', - bodyPreview: 'Текст:', - resetToDefault: 'Восстановить стандартный шаблон', - titleRequired: 'Укажите заголовок', - bodyRequired: 'Введите текст уведомления', - notificationLog: 'Журнал уведомлений', - showFailedOnly: 'Только неудачные', - last24Hours: 'Последние 24 часа', - last7Days: 'Последние 7 дней', - last30Days: 'Последние 30 дней', - last90Days: 'Последние 90 дней', - justNow: 'Только что', - noFailedNotifications: 'Неудачных уведомлений нет', - noNotificationsLogged: 'В журнале пока нет уведомлений', - unknownProvider: 'Неизвестный провайдер', - logTitle: 'Заголовок', - logMessage: 'Сообщение', - logError: 'Ошибка', - logProvider: 'Провайдер: {{type}}', - logTime: 'Время: {{time}}', - refresh: 'Обновить', - clearOld: 'Удалить старые', - statsSummary: 'За последние {{days}} дней:', - statsNotifications: 'уведомлений', - statsSent: 'отправлено: {{count}}', - statsFailed: 'ошибок: {{count}}', + cancel: "Отмена", + save: "Сохранить", + add: "Добавить", + nameRequired: "Укажите название", + fieldRequired: "Поле «{{field}}» обязательно", + phoneNumber: "Номер телефона", + apiKey: "Ключ API", + serverUrl: "URL сервера", + topic: "Тема", + authToken: "Токен авторизации", + userKey: "Ключ пользователя", + appToken: "Токен приложения", + priority: "Приоритет", + pushoverRetry: "Повтор экстренного уведомления (с)", + pushoverExpire: "Срок действия экстренного уведомления (с)", + botToken: "Токен бота", + chatId: "ID чата", + smtpServer: "SMTP-сервер", + smtpPort: "Порт SMTP", + security: "Защита соединения", + authentication: "Аутентификация", + username: "Имя пользователя", + password: "Пароль", + fromEmail: "Адрес отправителя", + toEmail: "Адрес получателя", + webhookUrl: "URL вебхука", + payloadFormat: "Формат данных", + authorization: "Авторизация", + titleFieldName: "Имя поля заголовка", + messageFieldName: "Имя поля сообщения", + editTemplate: "Изменить шаблон: {{name}}", + titleLabel: "Заголовок", + bodyLabel: "Текст", + titlePlaceholder: "Заголовок уведомления...", + bodyPlaceholder: "Текст уведомления...", + availableVariables: "Доступные переменные", + clickToInsert: "Нажмите, чтобы вставить в позицию курсора в тексте", + livePreview: "Предварительный просмотр", + hide: "Скрыть", + show: "Показать", + loadingPreview: "Загрузка предварительного просмотра...", + enterTemplateContent: "Введите содержимое шаблона для предварительного просмотра", + titlePreview: "Заголовок:", + bodyPreview: "Текст:", + resetToDefault: "Восстановить стандартный шаблон", + titleRequired: "Укажите заголовок", + bodyRequired: "Введите текст уведомления", + notificationLog: "Журнал уведомлений", + showFailedOnly: "Только неудачные", + last24Hours: "Последние 24 часа", + last7Days: "Последние 7 дней", + last30Days: "Последние 30 дней", + last90Days: "Последние 90 дней", + justNow: "Только что", + noFailedNotifications: "Неудачных уведомлений нет", + noNotificationsLogged: "В журнале пока нет уведомлений", + unknownProvider: "Неизвестный провайдер", + logTitle: "Заголовок", + logMessage: "Сообщение", + logError: "Ошибка", + logProvider: "Провайдер: {{type}}", + logTime: "Время: {{time}}", + refresh: "Обновить", + clearOld: "Удалить старые", + statsSummary: "За последние {{days}} дней:", + statsNotifications: "уведомлений", + statsSent: "отправлено: {{count}}", + statsFailed: "ошибок: {{count}}", eventTypes: { - print_start: 'Печать началась', - print_complete: 'Печать завершена', - print_failed: 'Ошибка печати', - print_stopped: 'Печать остановлена', - print_progress: 'Ход печати', - printer_offline: 'Принтер не в сети', - printer_error: 'Ошибка принтера', - filament_low: 'Заканчивается филамент', - maintenance_due: 'Требуется обслуживание', - test: 'Тест', + print_start: "Печать началась", + print_complete: "Печать завершена", + print_failed: "Ошибка печати", + print_stopped: "Печать остановлена", + print_progress: "Ход печати", + printer_offline: "Принтер не в сети", + printer_error: "Ошибка принтера", + filament_low: "Заканчивается филамент", + maintenance_due: "Требуется обслуживание", + test: "Тест", }, userEmail: { - title: 'Уведомления', - emailNotifications: 'Уведомления по электронной почте', - emailNotificationsDesc: 'Получать письма о собственных заданиях печати. Для отправки используются системные параметры SMTP из раздела расширенной аутентификации.', - sendingTo: 'Уведомления будут отправляться на адрес', - noEmailWarning: 'Для вашей учётной записи не указан адрес электронной почты. Обратитесь к администратору.', - printJobNotifications: 'Уведомления о заданиях печати', - printJobNotificationsDesc: 'Выберите события, при которых будут отправляться письма о запущенных вами заданиях.', - printJobStarts: 'Запуск задания печати', - printJobStartsDesc: 'Уведомлять о начале вашего задания печати.', - printJobFinishes: 'Завершение задания печати', - printJobFinishesDesc: 'Уведомлять об успешном завершении вашего задания печати.', - printErrors: 'Ошибки печати', - printErrorsDesc: 'Уведомлять, если задание завершилось ошибкой или при печати возникла проблема.', - printJobStops: 'Остановка задания печати', - printJobStopsDesc: 'Уведомлять об отмене или остановке вашего задания печати.', - saveSuccess: 'Настройки уведомлений сохранены.', - saveError: 'Не удалось сохранить настройки уведомлений.', + title: "Уведомления", + emailNotifications: "Уведомления по электронной почте", + emailNotificationsDesc: "Получать письма о собственных заданиях печати. Для отправки используются системные параметры SMTP из раздела расширенной аутентификации.", + sendingTo: "Уведомления будут отправляться на адрес", + noEmailWarning: "Для вашей учётной записи не указан адрес электронной почты. Обратитесь к администратору.", + printJobNotifications: "Уведомления о заданиях печати", + printJobNotificationsDesc: "Выберите события, при которых будут отправляться письма о запущенных вами заданиях.", + printJobStarts: "Запуск задания печати", + printJobStartsDesc: "Уведомлять о начале вашего задания печати.", + printJobFinishes: "Завершение задания печати", + printJobFinishesDesc: "Уведомлять об успешном завершении вашего задания печати.", + printErrors: "Ошибки печати", + printErrorsDesc: "Уведомлять, если задание завершилось ошибкой или при печати возникла проблема.", + printJobStops: "Остановка задания печати", + printJobStopsDesc: "Уведомлять об отмене или остановке вашего задания печати.", + saveSuccess: "Настройки уведомлений сохранены.", + saveError: "Не удалось сохранить настройки уведомлений.", }, }, richTextEditor: { - bold: 'Полужирный', - italic: 'Курсив', - underline: 'Подчёркивание', - bulletList: 'Маркированный список', - numberedList: 'Нумерованный список', - alignLeft: 'По левому краю', - alignCenter: 'По центру', - alignRight: 'По правому краю', - addLink: 'Добавить ссылку', - removeLink: 'Удалить ссылку', + bold: "Полужирный", + italic: "Курсив", + underline: "Подчёркивание", + bulletList: "Маркированный список", + numberedList: "Нумерованный список", + alignLeft: "По левому краю", + alignCenter: "По центру", + alignRight: "По правому краю", + addLink: "Добавить ссылку", + removeLink: "Удалить ссылку", }, externalLinks: { - title: 'Ссылки на боковой панели', - sidebarLayout: 'Боковая панель', - sidebarLayoutDescription: 'Показывайте или скрывайте встроенные страницы, добавляйте внешние ссылки и перетаскивайте пункты, меняя порядок навигации.', - systemPages: 'Страницы Bambuddy', - externalLinks: 'Внешние ссылки', - visibleInSidebar: 'Показывается на боковой панели', - hiddenFromSidebar: 'Скрыто с боковой панели', - requiredInSidebar: 'Обязательный пункт боковой панели', - hidePage: 'Скрыть страницу', - showPage: 'Показать страницу', - settingsCannotBeHidden: 'Настройки нельзя скрыть', - noLinksConfigured: 'Внешние ссылки не настроены', - deleteLink: 'Удалить ссылку', - removeCustomIcon: 'Удалить пользовательский значок', - openInNewTab: 'Открывать в новой вкладке', + title: "Ссылки на боковой панели", + sidebarLayout: "Боковая панель", + sidebarLayoutDescription: "Показывайте или скрывайте встроенные страницы, добавляйте внешние ссылки и перетаскивайте пункты, меняя порядок навигации.", + systemPages: "Страницы Bambuddy", + externalLinks: "Внешние ссылки", + visibleInSidebar: "Показывается на боковой панели", + hiddenFromSidebar: "Скрыто с боковой панели", + requiredInSidebar: "Обязательный пункт боковой панели", + hidePage: "Скрыть страницу", + showPage: "Показать страницу", + settingsCannotBeHidden: "Настройки нельзя скрыть", + noLinksConfigured: "Внешние ссылки не настроены", + deleteLink: "Удалить ссылку", + removeCustomIcon: "Удалить пользовательский значок", + openInNewTab: "Открывать в новой вкладке", placeholders: { - linkName: 'Моя ссылка', + linkName: "Моя ссылка", }, }, keyboardShortcuts: { - title: 'Горячие клавиши', - navigation: 'Навигация', - archivesSection: 'Архив', - kProfilesSection: 'K-профили', - generalSection: 'Общие', + title: "Горячие клавиши", + navigation: "Навигация", + archivesSection: "Архив", + kProfilesSection: "K-профили", + generalSection: "Общие", shortcuts: { - goToPrinters: 'Перейти к принтерам', - goToArchives: 'Перейти в архив', - goToQueue: 'Перейти к очереди', - goToStats: 'Перейти к статистике', - goToProfiles: 'Перейти к облачным профилям', - goToSettings: 'Перейти к настройкам', - focusSearch: 'Перейти в строку поиска', - openUploadModal: 'Открыть окно загрузки', - clearSelection: 'Снять выделение / убрать фокус с поля', - contextMenu: 'Контекстное меню карточки', - refreshProfiles: 'Обновить профили', - newProfile: 'Новый профиль', - exitSelectionMode: 'Выйти из режима выделения', - showHelp: 'Показать эту справку', + goToPrinters: "Перейти к принтерам", + goToArchives: "Перейти в архив", + goToQueue: "Перейти к очереди", + goToStats: "Перейти к статистике", + goToProfiles: "Перейти к облачным профилям", + goToSettings: "Перейти к настройкам", + focusSearch: "Перейти в строку поиска", + openUploadModal: "Открыть окно загрузки", + clearSelection: "Снять выделение / убрать фокус с поля", + contextMenu: "Контекстное меню карточки", + refreshProfiles: "Обновить профили", + newProfile: "Новый профиль", + exitSelectionMode: "Выйти из режима выделения", + showHelp: "Показать эту справку", }, - footer: 'Нажмите Esc или щёлкните снаружи, чтобы закрыть', + footer: "Нажмите Esc или щёлкните снаружи, чтобы закрыть", }, notificationLog: { - title: 'Журнал уведомлений', + title: "Журнал уведомлений", events: { - printStarted: 'Печать началась', - printComplete: 'Печать завершена', - printFailed: 'Ошибка печати', - printStopped: 'Печать остановлена', - progress: 'Ход выполнения', - printerOffline: 'Принтер не в сети', - printerError: 'Ошибка принтера', - lowFilament: 'Заканчивается филамент', - maintenanceDue: 'Требуется обслуживание', - test: 'Тест', + printStarted: "Печать началась", + printComplete: "Печать завершена", + printFailed: "Ошибка печати", + printStopped: "Печать остановлена", + progress: "Ход выполнения", + printerOffline: "Принтер не в сети", + printerError: "Ошибка принтера", + lowFilament: "Заканчивается филамент", + maintenanceDue: "Требуется обслуживание", + test: "Тест", }, timeAgo: { - justNow: 'Только что', - minutesAgo: '{{minutes}} мин назад', - hoursAgo: '{{hours}} ч назад', + justNow: "Только что", + minutesAgo: "{{minutes}} мин назад", + hoursAgo: "{{hours}} ч назад", }, }, restoreBackup: { - title: 'Восстановление резервной копии', - restoring: 'Восстановление...', - restoreComplete: 'Восстановление завершено', - restoreFailed: 'Ошибка восстановления', - importSettings: 'Импортировать настройки из файла резервной копии', - pleaseWait: 'Подождите, пока данные восстанавливаются', - clickToSelect: 'Нажмите, чтобы выбрать файл резервной копии (.json или .zip)', - howDuplicateHandling: 'Как обрабатываются совпадения:', + title: "Восстановление резервной копии", + restoring: "Восстановление...", + restoreComplete: "Восстановление завершено", + restoreFailed: "Ошибка восстановления", + importSettings: "Импортировать настройки из файла резервной копии", + pleaseWait: "Подождите, пока данные восстанавливаются", + clickToSelect: "Нажмите, чтобы выбрать файл резервной копии (.json или .zip)", + howDuplicateHandling: "Как обрабатываются совпадения:", categories: { - printers: 'Принтеры', - smartPlugs: 'Умные розетки', - notificationProviders: 'Провайдеры уведомлений', - filaments: 'Филаменты', - archives: 'Архив', - pendingUploads: 'Ожидающие загрузки', - settingsTemplates: 'Настройки и шаблоны', + printers: "Принтеры", + smartPlugs: "Умные розетки", + notificationProviders: "Провайдеры уведомлений", + filaments: "Филаменты", + archives: "Архив", + pendingUploads: "Ожидающие загрузки", + settingsTemplates: "Настройки и шаблоны", }, matchingInfo: { - printers: 'сопоставляются по серийному номеру', - smartPlugs: 'сопоставляются по IP-адресу', - notificationProviders: 'сопоставляются по названию', - filaments: 'сопоставляются по названию, типу и бренду', - archives: 'сопоставляются по хешу содержимого', - pendingUploads: 'сопоставляются по имени файла', - settingsTemplates: 'всегда перезаписываются', + printers: "сопоставляются по серийному номеру", + smartPlugs: "сопоставляются по IP-адресу", + notificationProviders: "сопоставляются по названию", + filaments: "сопоставляются по названию, типу и бренду", + archives: "сопоставляются по хешу содержимого", + pendingUploads: "сопоставляются по имени файла", + settingsTemplates: "всегда перезаписываются", }, - replaceExisting: 'Заменить существующие данные', - keepExisting: 'Сохранить существующие данные', - replaceDescription: 'Перезаписать уже существующие элементы данными из резервной копии', - keepDescription: 'Восстановить только отсутствующие элементы', - caution: 'Внимание:', - cautionText: 'При перезаписи текущая конфигурация будет заменена данными из резервной копии. В целях безопасности коды доступа к принтерам никогда не перезаписываются.', - itemsRestored: 'Восстановлено элементов', - itemsSkipped: 'Пропущено элементов', - restored: 'Восстановлено', - skipped: 'Пропущено (уже существует)', - filesLabel: 'Файлы (3MF, миниатюры и т. п.)', - newApiKeysGenerated: 'Созданы новые ключи API', - newApiKeysWarning: 'Эти ключи показываются только один раз. Скопируйте их сейчас!', - processingBackup: 'Обработка резервной копии...', - noDataFound: 'В резервной копии не найдено данных для восстановления.', - failedToRestore: 'Не удалось восстановить резервную копию. Проверьте формат файла.', + replaceExisting: "Заменить существующие данные", + keepExisting: "Сохранить существующие данные", + replaceDescription: "Перезаписать уже существующие элементы данными из резервной копии", + keepDescription: "Восстановить только отсутствующие элементы", + caution: "Внимание:", + cautionText: "При перезаписи текущая конфигурация будет заменена данными из резервной копии. В целях безопасности коды доступа к принтерам никогда не перезаписываются.", + itemsRestored: "Восстановлено элементов", + itemsSkipped: "Пропущено элементов", + restored: "Восстановлено", + skipped: "Пропущено (уже существует)", + filesLabel: "Файлы (3MF, миниатюры и т. п.)", + newApiKeysGenerated: "Созданы новые ключи API", + newApiKeysWarning: "Эти ключи показываются только один раз. Скопируйте их сейчас!", + processingBackup: "Обработка резервной копии...", + noDataFound: "В резервной копии не найдено данных для восстановления.", + failedToRestore: "Не удалось восстановить резервную копию. Проверьте формат файла.", }, backupExport: { - title: 'Экспорт резервной копии', - selectData: 'Выберите данные для включения', - selectAll: 'Выбрать всё', - selectNone: 'Снять выделение', + title: "Экспорт резервной копии", + selectData: "Выберите данные для включения", + selectAll: "Выбрать всё", + selectNone: "Снять выделение", categoryDescriptions: { - settings: 'Язык, тема и настройки обновлений', - notifications: 'ntfy, Pushover, Discord и другие', - templates: 'Пользовательские шаблоны сообщений', - smartPlugs: 'Конфигурация розеток Tasmota', - externalLinks: 'Ссылки на внешние сервисы в боковой панели', - printers: 'Сведения о принтерах (без кодов доступа)', - plateDetection: 'Эталонные изображения пустых пластин', - filaments: 'Типы и стоимость филамента', - maintenance: 'Пользовательские графики обслуживания', - archives: 'Все данные печати и файлы (3MF, миниатюры, фотографии)', - projects: 'Проекты, позиции спецификаций и вложения', - pendingUploads: 'Загрузки виртуальных принтеров, ожидающие проверки', - apiKeys: 'Ключи API вебхуков (при импорте создаются новые)', + settings: "Язык, тема и настройки обновлений", + notifications: "ntfy, Pushover, Discord и другие", + templates: "Пользовательские шаблоны сообщений", + smartPlugs: "Конфигурация розеток Tasmota", + externalLinks: "Ссылки на внешние сервисы в боковой панели", + printers: "Сведения о принтерах (без кодов доступа)", + plateDetection: "Эталонные изображения пустых пластин", + filaments: "Типы и стоимость филамента", + maintenance: "Пользовательские графики обслуживания", + archives: "Все данные печати и файлы (3MF, миниатюры, фотографии)", + projects: "Проекты, позиции спецификаций и вложения", + pendingUploads: "Загрузки виртуальных принтеров, ожидающие проверки", + apiKeys: "Ключи API вебхуков (при импорте создаются новые)", }, - requiresPrinters: 'Необходимо выбрать раздел «Принтеры»', - zipFileWarning: 'Будет создан ZIP-файл.', - zipFileDescription: 'В него войдут все файлы 3MF, миниатюры, таймлапсы и фотографии. Операция может занять некоторое время, а файл — получиться большим.', - includeAccessCodes: 'Включить коды доступа', - includeAccessCodesDescription: 'Для переноса на другой компьютер', - includeAccessCodesWarning: 'Коды доступа попадут в файл открытым текстом. Храните резервную копию в безопасном месте!', - categoriesSelected: 'Выбрано категорий: {{selectedCount}}', + requiresPrinters: "Необходимо выбрать раздел «Принтеры»", + zipFileWarning: "Будет создан ZIP-файл.", + zipFileDescription: "В него войдут все файлы 3MF, миниатюры, таймлапсы и фотографии. Операция может занять некоторое время, а файл — получиться большим.", + includeAccessCodes: "Включить коды доступа", + includeAccessCodesDescription: "Для переноса на другой компьютер", + includeAccessCodesWarning: "Коды доступа попадут в файл открытым текстом. Храните резервную копию в безопасном месте!", + categoriesSelected: "Выбрано категорий: {{selectedCount}}", }, pendingUploads: { placeholders: { - notes: 'Добавьте примечания об этой печати...', + notes: "Добавьте примечания об этой печати...", }, - discardUpload: 'Отклонить загрузку', - archiveAllUploads: 'Добавить все загрузки в архив', - discardAllUploads: 'Отклонить все загрузки', - archive: 'В архив', + discardUpload: "Отклонить загрузку", + archiveAllUploads: "Добавить все загрузки в архив", + discardAllUploads: "Отклонить все загрузки", + archive: "В архив", timeAgo: { - justNow: 'Только что', - minutesAgo: '{{minutes}} мин назад', - hoursAgo: '{{hours}} ч назад', - daysAgo: '{{days}} дн. назад', + justNow: "Только что", + minutesAgo: "{{minutes}} мин назад", + hoursAgo: "{{hours}} ч назад", + daysAgo: "{{days}} дн. назад", }, }, apiBrowser: { placeholders: { - requestBody: 'Тело запроса JSON...', - searchEndpoints: 'Поиск конечных точек...', + requestBody: "Тело запроса JSON...", + searchEndpoints: "Поиск конечных точек...", }, }, configureAmsSlot: { - title: 'Настройка слота AMS', - slotConfigured: 'Слот настроен!', - configuringSlot: 'Настраивается слот:', - slotLabel: '{{ams}}, слот {{slot}}', - searchPresets: 'Поиск профилей...', - colorPlaceholder: 'Название или HEX-код цвета (например: brown, FF8800)', - clearCustomColor: 'Очистить пользовательский цвет', - noCloudPresets: 'Облачных профилей нет. Войдите в Bambu Cloud для синхронизации.', - noPresetsAvailable: 'Нет доступных профилей. Войдите в Bambu Cloud или импортируйте локальные профили.', - noMatchingPresets: 'Подходящие профили не найдены.', - custom: 'Пользовательский', - builtin: 'Встроенный', - orcaCloud: 'Orca Cloud', - bambuCloud: 'Bambu Cloud', - settingsSentToPrinter: 'Настройки отправлены на принтер', - filamentProfile: 'Профиль филамента', - kProfileLabel: 'K-профиль (Pressure Advance)', - filteringFor: 'Фильтр по материалу: {{material}}', - noKProfile: 'Без K-профиля (стандартное значение 0,020)', - noMatchingKProfiles: 'Подходящие K-профили не найдены. Будет использовано стандартное значение K=0,020.', - selectFilamentFirst: 'Сначала выберите профиль филамента', - kFromCalibration: 'K={{value}} из калибровки принтера', - customColorLabel: 'Пользовательский цвет (необязательно)', - presetColors: 'Цвета {{name}}:', - showLessColors: 'Показать меньше цветов', - showMoreColors: 'Показать больше цветов', - clear: 'Очистить', - hexLabel: 'HEX: #{{hex}}', - resetting: 'Сброс...', - resetSlot: 'Сбросить слот', - cancel: 'Отмена', - configuring: 'Настройка...', - configureSlot: 'Настроить слот', + title: "Настройка слота AMS", + slotConfigured: "Слот настроен!", + configuringSlot: "Настраивается слот:", + slotLabel: "{{ams}}, слот {{slot}}", + searchPresets: "Поиск профилей...", + colorPlaceholder: "Название или HEX-код цвета (например: brown, FF8800)", + clearCustomColor: "Очистить пользовательский цвет", + noCloudPresets: "Облачных профилей нет. Войдите в Bambu Cloud для синхронизации.", + noPresetsAvailable: "Нет доступных профилей. Войдите в Bambu Cloud или импортируйте локальные профили.", + noMatchingPresets: "Подходящие профили не найдены.", + custom: "Пользовательский", + builtin: "Встроенный", + orcaCloud: "Orca Cloud", + bambuCloud: "Bambu Cloud", + settingsSentToPrinter: "Настройки отправлены на принтер", + filamentProfile: "Профиль филамента", + kProfileLabel: "K-профиль (Pressure Advance)", + filteringFor: "Фильтр по материалу: {{material}}", + noKProfile: "Без K-профиля (стандартное значение 0,020)", + noMatchingKProfiles: "Подходящие K-профили не найдены. Будет использовано стандартное значение K=0,020.", + selectFilamentFirst: "Сначала выберите профиль филамента", + kFromCalibration: "K={{value}} из калибровки принтера", + customColorLabel: "Пользовательский цвет (необязательно)", + presetColors: "Цвета {{name}}:", + showLessColors: "Показать меньше цветов", + showMoreColors: "Показать больше цветов", + clear: "Очистить", + hexLabel: "HEX: #{{hex}}", + resetting: "Сброс...", + resetSlot: "Сбросить слот", + cancel: "Отмена", + configuring: "Настройка...", + configureSlot: "Настроить слот", }, githubBackup: { - title: 'Резервное копирование в Git', - history: 'История', - downloadBackup: 'Скачать резервную копию', - restoreBackup: 'Восстановить резервную копию', - noBackupsYet: 'Резервных копий пока нет', + title: "Резервное копирование в Git", + history: "История", + downloadBackup: "Скачать резервную копию", + restoreBackup: "Восстановить резервную копию", + noBackupsYet: "Резервных копий пока нет", }, emailSettings: { placeholders: { - fromName: 'BamBuddy', + fromName: "BamBuddy", }, }, tagManagement: { - searchTags: 'Поиск меток...', - renameTag: 'Переименовать метку', - deleteTag: 'Удалить метку', + searchTags: "Поиск меток...", + renameTag: "Переименовать метку", + deleteTag: "Удалить метку", }, notificationTemplates: { placeholders: { - title: 'Заголовок уведомления...', - body: 'Текст уведомления...', + title: "Заголовок уведомления...", + body: "Текст уведомления...", }, }, batchTag: { placeholders: { - newTag: 'Введите новую метку...', + newTag: "Введите новую метку...", }, }, photoGallery: { - deletePhoto: 'Удалить фотографию', + deletePhoto: "Удалить фотографию", }, filamentHoverCard: { - copySpoolUuid: 'Скопировать UUID катушки', + copySpoolUuid: "Скопировать UUID катушки", }, kProfilesView: { - hasNote: 'Есть примечание', - copyProfile: 'Копировать профиль', + hasNote: "Есть примечание", + copyProfile: "Копировать профиль", }, layout: { - openMenu: 'Открыть меню', - noPermissionSystemInfo: 'У вас нет разрешения на просмотр системной информации', + openMenu: "Открыть меню", + noPermissionSystemInfo: "У вас нет разрешения на просмотр системной информации", }, dashboard: { - dragToReorder: 'Перетащите, чтобы изменить порядок', - hideWidget: 'Скрыть виджет', + dragToReorder: "Перетащите, чтобы изменить порядок", + hideWidget: "Скрыть виджет", }, notificationProviderCard: { - deleteNotificationProvider: 'Удалить провайдера уведомлений', + deleteNotificationProvider: "Удалить провайдера уведомлений", }, fileManagerModal: { - closeFileManager: 'Закрыть файловый менеджер', - sortFiles: 'Сортировать файлы', - goToParentFolder: 'Перейти в родительскую папку', - threeView: 'Просмотр 3D', + closeFileManager: "Закрыть файловый менеджер", + sortFiles: "Сортировать файлы", + goToParentFolder: "Перейти в родительскую папку", + threeView: "Просмотр 3D", }, embeddedCameraViewer: { - refreshStream: 'Обновить поток', - close: 'Закрыть', - zoomOut: 'Уменьшить', - resetZoom: 'Сбросить масштаб', - zoomIn: 'Увеличить', - dragToResize: 'Перетащите, чтобы изменить размер', + refreshStream: "Обновить поток", + close: "Закрыть", + zoomOut: "Уменьшить", + resetZoom: "Сбросить масштаб", + zoomIn: "Увеличить", + dragToResize: "Перетащите, чтобы изменить размер", }, timelapseViewer: { - skipBack5s: 'Назад на 5 с', - skipForward5s: 'Вперёд на 5 с', + skipBack5s: "Назад на 5 с", + skipForward5s: "Вперёд на 5 с", }, notificationProviders: { descriptions: { - email: 'Уведомления по электронной почте через SMTP', - telegram: 'Уведомления через Telegram-бота', - discord: 'Отправка в канал Discord через вебхук', - ntfy: 'Бесплатные push-уведомления с возможностью самостоятельного размещения сервера', - pushover: 'Простые и надёжные push-уведомления', - callmebot: 'Бесплатные уведомления WhatsApp через CallMeBot', - webhook: 'Обычный HTTP POST на любой URL', + email: "Уведомления по электронной почте через SMTP", + telegram: "Уведомления через Telegram-бота", + discord: "Отправка в канал Discord через вебхук", + ntfy: "Бесплатные push-уведомления с возможностью самостоятельного размещения сервера", + pushover: "Простые и надёжные push-уведомления", + callmebot: "Бесплатные уведомления WhatsApp через CallMeBot", + webhook: "Обычный HTTP POST на любой URL", }, }, logViewer: { - searchPlaceholder: 'Поиск по сообщению или имени логгера...', - noLogEntries: 'Записи журнала не найдены', + searchPlaceholder: "Поиск по сообщению или имени логгера...", + noLogEntries: "Записи журнала не найдены", }, switchbarPopover: { - noSwitchesInSwitchbar: 'На панели переключателей нет устройств', + noSwitchesInSwitchbar: "На панели переключателей нет устройств", }, projectPageModal: { placeholders: { - title: 'Название', - designer: 'Автор', - license: 'Лицензия', - description: 'Введите описание...', - profileTitle: 'Название профиля', - profileDescription: 'Описание профиля...', + title: "Название", + designer: "Автор", + license: "Лицензия", + description: "Введите описание...", + profileTitle: "Название профиля", + profileDescription: "Описание профиля...", }, }, spoolmanSettings: { }, time: { - unknown: '—', - waiting: 'Ожидание', - justNow: 'Только что', - now: 'Сейчас', - minsAgo: '{{count}} мин назад', - inMins: 'через {{count}} мин', - hoursAgo: '{{count}} ч назад', - inHours: 'через {{count}} ч', - daysAgo: '{{count}} дн. назад', - inDays: 'через {{count}} дн.', - }, - setup: { - title: 'Настройка Bambuddy', - subtitle: 'Настройте аутентификацию для этого экземпляра Bambuddy', - enableAuth: 'Включить аутентификацию', - adminAccount: 'Учётная запись администратора', - adminAccountDesc: 'Если администраторы уже существуют, аутентификация будет включена с использованием существующих учётных записей. Оставьте поля пустыми или введите данные для создания нового администратора.', - adminUsername: 'Имя администратора', - adminPassword: 'Пароль администратора', - optionalIfAdminExists: '(необязательно, если администратор уже существует)', - adminUsernamePlaceholder: 'Введите имя администратора (необязательно)', - adminPasswordPlaceholder: 'Введите пароль администратора (необязательно)', - confirmPassword: 'Подтвердите пароль', - confirmPasswordPlaceholder: 'Повторите пароль администратора', - settingUp: 'Настройка...', - completeSetup: 'Завершить настройку', - toast: { - authEnabledAdminCreated: 'Аутентификация включена, администратор создан', - authEnabledExistingAdmins: 'Аутентификация включена для существующих администраторов', - setupCompleted: 'Настройка завершена', - enterBothCredentials: 'Введите имя и пароль администратора либо оставьте оба поля пустыми', - passwordsDoNotMatch: 'Пароли не совпадают', - passwordTooShort: 'Пароль должен содержать не менее 6 символов', - }, - }, - failureDetection: { - title: 'Обнаружение сбоев с помощью ИИ', - description: 'Контролируйте печать через собственный сервер Obico ML API и автоматически реагируйте на обнаруженные сбои.', - mlUrl: 'URL Obico ML API', - mlUrlHint: 'Базовый URL собственного контейнера Obico ml_api, например http://192.168.1.10:3333.', - test: 'Проверить', - testSuccess: 'ML API доступен и работает.', - testFailed: 'Не удалось подключиться к ML API.', - sensitivity: 'Чувствительность', - sensitivityLow: 'Низкая (меньше ложных срабатываний)', - sensitivityMedium: 'Средняя (сбалансированная)', - sensitivityHigh: 'Высокая (раннее обнаружение, больше ложных срабатываний)', - sensitivityHint: 'Определяет пороги уверенности, при которых создаются предупреждения и фиксируются сбои.', - action: 'Действие при обнаружении сбоя', - actionNotify: 'Только уведомить', - actionPause: 'Приостановить печать', - actionPauseOff: 'Приостановить печать и отключить питание', - pollInterval: 'Интервал проверки (секунды)', - pollIntervalHint: 'Как часто проверять каждый принтер во время печати. Минимум 5 с, максимум 120 с.', - externalUrlMissing: 'Внешний URL не задан.', - externalUrlHint: 'ML API получает снимок камеры по URL. Укажите внешний URL в общих настройках, чтобы контейнер ML API мог обращаться к Bambuddy.', - perPrinterTitle: 'Контролируемые принтеры', - perPrinterHint: 'Выберите принтеры, за которыми будет следить служба обнаружения.', - monitorAll: 'Контролировать все подключённые принтеры', - statusTitle: 'Состояние', - serviceRunning: 'Служба работает', - thresholds: 'Низкий / высокий порог', - activePrinters: 'Активные задания', - noActivePrints: 'Сейчас ничего не печатается.', - historyTitle: 'Последние обнаружения', - noHistory: 'Обнаружений пока нет.', - }, - cameraTokens: { - title: 'Токены API камеры', - navTitle: 'Токены API камеры', - description: 'Долгоживущие токены для встраивания видеопотока в Home Assistant, Frigate, киоски и другие системы, которым нужен постоянный URL. Каждый токен даёт доступ только к потоку камеры и может быть отозван в любой момент.', - loading: 'Загрузка...', - confirmRevoke: { - title: 'Отозвать этот токен?', - body: 'Все устройства, использующие «{{name}}», немедленно потеряют доступ. Отменить это действие нельзя.', - cancel: 'Отмена', - confirm: 'Отозвать', - }, - create: { - title: 'Создать токен', - nameLabel: 'Название токена', - namePlaceholder: 'Например, Home Assistant', - daysLabel: 'Срок действия, дней', - submit: 'Создать', - hint: 'Максимальный срок действия — 365 дней. Значение токена показывается только один раз при создании — скопируйте его сейчас.', - }, - created: { - title: 'Токен создан — скопируйте его сейчас', - warning: 'Это единственный раз, когда токен отображается. После закрытия окна посмотреть его снова будет невозможно.', - copy: 'Копировать', - dismiss: 'Я сохранил токен', - }, - list: { - myTitle: 'Мои токены', - allTitle: 'Все пользователи (режим администратора)', - empty: 'Токенов пока нет.', - name: 'Название', - owner: 'Владелец', - prefix: 'Префикс', - created: 'Создан', - expires: 'Истекает', - lastUsed: 'Последнее использование', - revoke: 'Отозвать', - expired: 'Истёк', - }, - toast: { - created: 'Токен создан', - createFailed: 'Не удалось создать токен', - revoked: 'Токен отозван', - revokeFailed: 'Не удалось отозвать токен', - loadFailed: 'Не удалось загрузить токены', - copied: 'Скопировано в буфер обмена', - copyFailed: 'Не удалось скопировать — выделите и скопируйте вручную', - }, - }, - makerworld: { - title: 'MakerWorld', - description: 'Вставьте URL модели MakerWorld, чтобы импортировать и напечатать её прямо из Bambuddy — без перехода в приложение Bambu Handy.', - pasteUrlHeader: 'Импорт из MakerWorld', - pasteUrlPlaceholder: 'https://makerworld.com/en/models/… или любая ссылка MakerWorld', - resolveButton: 'Получить данные', - signInRequiredTitle: 'Для загрузки требуется вход в Bambu Cloud', - signInRequiredBody: 'Просматривать сведения о модели можно анонимно, но для загрузки файлов 3MF MakerWorld требует учётную запись Bambu Cloud.', - openCloudSettings: 'Открыть настройки облака', - untitledModel: 'Модель без названия', - byCreator: 'Автор: {{name}}', - downloadsCount: 'Загрузок: {{count}}', - licensePrefix: 'Лицензия', - alreadyImported: 'Уже в библиотеке', - openOnMakerworld: 'Открыть на MakerWorld', - alreadyInLibrary: 'Эта модель уже есть в библиотеке — найдите её в разделе «Файлы → MakerWorld».', - importSuccess: 'Импортирован файл {{filename}} — сохранён в «Файлы → MakerWorld».', - platesHeader: 'Пластины ({{count}})', - plateDefaultName: 'Пластина {{n}}', - materialCount: 'Филаментов: {{count}}', - amsRequired: 'Требуется AMS', - slicedFor: 'Нарезано для {{printer}}', - alsoCompatible: 'Также отмечено как совместимое: {{printers}}', - importToLibrary: 'Сохранить', - sliceIn: 'Сохранить и нарезать в {{slicer}}', - disclaimer: 'Интеграция с MakerWorld использует документированные сообществом конечные точки API. Bambuddy не связан с MakerWorld или Bambu Lab и не одобрен ими.', - lastImportSuccess: 'Импортировано в библиотеку', - lastImportAlreadyInLibrary: 'Уже в библиотеке', - viewInLibrary: 'Открыть в файловом менеджере', - openInBambuStudio: 'Открыть в Bambu Studio', - openInOrcaSlicer: 'Открыть в OrcaSlicer', - importTo: 'Импортировать в файловый менеджер', - recentImportsHeader: 'Недавние импорты', - phaseResolving: 'Получение данных', - phaseDownloading: 'Загрузка', - folderAuto: 'MakerWorld (по умолчанию)', - importAll: 'Импортировать всё', - importAllProgress: 'Импорт {{current}} из {{total}}', - openGallery: 'Открыть галерею изображений', - galleryPrev: 'Предыдущее изображение', - galleryNext: 'Следующее изображение', - deleteImport: 'Удалить из библиотеки', - importDeleting: 'Удаление…', - importDeleted: 'Удалено из библиотеки', - confirmDelete: 'Удалить {{filename}} из библиотеки? Локальный файл будет удалён, но пластину можно снова импортировать из MakerWorld.', - errors: { - resolveFailed: 'Не удалось обработать эту ссылку MakerWorld.', - downloadFailed: 'Ошибка загрузки. Повторите попытку.', - deleteFailed: 'Не удалось удалить файл из библиотеки.', - }, - }, - diagnostic: { - modalTitle: 'Диагностика подключения — {{name}}', - running: 'Выполняется диагностика...', - runningElapsed: 'Выполняется диагностика... ({{elapsed}} с)', - waitingForReportHint: 'Ожидание публикации отчёта о состоянии принтера — это может занять до {{max}} секунд.', - runFailed: 'Не удалось выполнить диагностику: {{error}}', - retry: 'Запустить снова', - runButton: 'Запустить диагностику', - sectionTitle: 'Диагностика подключения', - sectionDescription: 'Проверьте, почему принтер не подключается или не печатает: доступность портов, режим разработчика LAN, сетевой режим Docker и учётные данные.', - noPrinters: 'Принтеры не настроены.', - overall: { - ok: 'Проблем не обнаружено — подключение к принтеру исправно.', - warnings: 'Принтер должен работать, но некоторые пункты требуют внимания.', - problems: 'Обнаружены проблемы, объясняющие, почему принтер не подключается или не печатает.', - }, - check: { - port_mqtt: { - title: 'Порт управления (MQTT 8883)', - pass: 'Доступен — принтер принимает управляющие подключения.', - fail: 'Порт 8883 недоступен. Принтер выключен, имеет другой IP-адрес либо подключение блокирует межсетевой экран. Проверьте IP-адрес принтера и доступность порта 8883.', - }, - port_ftps: { - title: 'Порт передачи файлов (FTPS 990)', - pass: 'Доступен — отправка файлов печати будет работать.', - warn: 'Порт 990 недоступен. Мониторинг может работать, но отправка заданий на принтер завершится ошибкой. Убедитесь, что порт 990 не заблокирован.', - }, - external_storage: { - title: 'Сохранять отправленные файлы во внешнем хранилище (шаг 4 установки)', - pass: 'Принтер сообщает, что параметр включён: отправленные файлы будут сохраняться на SD-карте, а в архиве появятся миниатюры и метаданные слайсера.', - fail: 'Принтер сообщает, что параметр выключен. Включите «Сохранять отправленные файлы во внешнем хранилище»: в новых прошивках (P2S 01.02 / Bambu Studio 2.6+) переключатель находится в настройках печати принтера, в старых — на вкладке «Устройство» Bambu Studio / OrcaSlicer. Без этого у всех архивных заданий не будет миниатюр и метаданных слайсера.', - skip: 'Не проверено — требуется активное подключение MQTT. В старых версиях слайсеров этот параметр существует только в слайсере и принтер его не сообщает, поэтому проверка может пройти даже при выключенном параметре. Проверьте шаг 4 установки вручную.', - }, - port_rtsps: { - title: 'Порт камеры ({{protocol}} {{port}})', - pass: 'Доступен — видеопоток камеры будет работать.', - warn: 'Порт {{port}} недоступен. Просмотр камеры в реальном времени работать не будет. На печать это не влияет.', - }, - network_mode: { - title: 'Сетевой режим Docker', - pass: 'Используется сетевой режим host.', - warn: 'Bambuddy запущен в Docker с сетью bridge. Для обнаружения принтеров и виртуального принтера требуется режим host — пересоздайте контейнер с параметром «network_mode: host».', - skip: 'Bambuddy запущен не в Docker — проверка неприменима.', - }, - subnet: { - title: 'Подсеть', - pass: 'Принтер и Bambuddy находятся в одной подсети.', - warn: 'Принтер ({{printer_ip}}) и Bambuddy ({{host_ip}}) находятся в разных подсетях. Без настроенной маршрутизации между подсетями они могут быть недоступны друг для друга.', - skip: 'Не удалось определить подсеть — проверка пропущена.', - }, - mqtt_auth: { - title: 'Учётные данные принтера', - pass: 'Принтер принял подключение.', - fail: 'Принтер доступен, но отклонил подключение. Скорее всего, неверны код доступа или серийный номер. Код доступа изменяется при каждом переключении режима разработчика — снова скопируйте его с экрана принтера.', - skip: 'Не проверено — принтер недоступен.', - }, - developer_mode: { - title: 'Режим разработчика LAN', - pass: 'Режим разработчика включён.', - fail: 'Режим разработчика на принтере ВЫКЛЮЧЕН. Включите его в настройках LAN принтера и подтвердите кнопкой OK. Без него задания печати не запускаются.', - skip: 'Не удалось проверить — требуется активное подключение к принтеру.', - }, - printer_publishing: { - title: 'Принтер публикует состояние', - pass: 'Принтер публикует обновления состояния — AMS, филаменты и K-профили будут корректно передаваться в слайсер.', - fail: 'MQTT-брокер принял подключение, но принтер не опубликовал ни одного отчёта о состоянии. Почти всегда причина в неверном регистре или ошибке в серийном номере: тема device//report чувствительна к регистру. Сверьте серийный номер в настройках принтера с номером на его экране.', - skip: 'Не удалось проверить — требуется активное подключение к принтеру.', - }, - }, - }, - libraryTrash: { - title: 'Корзина', - headerButton: 'Корзина', - headerTooltip: 'Просмотреть файлы, перемещённые в корзину', - backToFiles: 'Вернуться к файлам', - subtitleAdmin: 'Удалённые файлы хранятся здесь {{days}} дней, затем удаляются автоматически. Показаны файлы всех пользователей.', - subtitleUser: 'Удалённые файлы хранятся здесь {{days}} дней, затем удаляются автоматически.', - loading: 'Загрузка корзины…', - loadError: 'Не удалось загрузить корзину.', - empty: 'Корзина пуста.', - summary: '{{count}} файлов · {{size}}', - emptyTrash: 'Очистить корзину', - restore: 'Восстановить', - purgeNow: 'Удалить сейчас', - autoPurgeIn: 'Автоудаление через {{when}}', - days: 'дней', - retentionLabel: 'Автоудаление через', - selectAll: 'Выбрать всё', - selectOne: 'Выбрать {{filename}}', - selectionCount: 'Выбрано: {{count}}', - bulkRestore: 'Восстановить выбранные', - bulkPurge: 'Удалить выбранные', - col: { - filename: 'Файл', - folder: 'Папка', - size: 'Размер', - deleted: 'Перемещён в корзину', - autoPurge: 'Автоудаление', - owner: 'Владелец', - actions: 'Действия', - }, - confirm: { - purgeTitle: 'Удалить навсегда?', - purgeBody: 'Файл {{filename}} будет удалён с диска без возможности восстановления.', - emptyTitle: 'Очистить корзину?', - emptyBody: 'Все файлы ({{count}}) будут удалены с диска. Отменить это действие нельзя.', - bulkPurgeTitle: 'Удалить выбранные файлы навсегда?', - bulkPurgeBody: 'Выбранные файлы ({{count}}) будут удалены с диска без возможности восстановления.', - cta: 'Удалить навсегда', - }, - toast: { - restored: 'Файл восстановлен.', - restoreFailed: 'Не удалось восстановить файл.', - purged: 'Файл удалён навсегда.', - purgeFailed: 'Не удалось удалить файл.', - emptied: 'Из корзины удалено файлов: {{count}}.', - emptyFailed: 'Не удалось очистить корзину.', - retentionSaved: 'Срок автоудаления: {{days}} дней.', - retentionFailed: 'Не удалось сохранить срок хранения.', - bulkRestored: 'Восстановлено файлов: {{count}}.', - bulkPurged: 'Удалено файлов: {{count}}.', - }, - }, - bugReport: { - title: 'Сообщить об ошибке', - description: 'Описание', - descriptionPlaceholder: 'Что пошло не так? Опишите проблему...', - email: 'Электронная почта (необязательно)', - emailPlaceholder: 'your@email.com', - emailPrivacy: 'Указанный адрес будет добавлен в сворачиваемый раздел задачи GitHub, чтобы сопровождающий мог с вами связаться.', - screenshot: 'Снимок экрана', - uploadOrPaste: 'Загрузите, вставьте или перетащите изображение', - dataCollectedSummary: 'Какие данные включаются в отчёт?', - dataIncluded: 'Включаются:', - dataIncludedList: 'Версия приложения, ОС, архитектура, версия Python, статистика базы данных (только количества), модели принтеров, количество сопел, версии прошивок, состояние подключений, состояние интеграций (Spoolman, MQTT, HA), неконфиденциальные настройки, количество сетевых интерфейсов, сведения о Docker и версии зависимостей.', - dataNeverIncluded: 'Никогда не включаются:', - dataNeverIncludedList: 'Имена принтеров, серийные номера, коды доступа, пароли, IP-адреса, адреса электронной почты, ключи API, токены, URL вебхуков, имена хостов и пользователей.', - submit: 'Отправить', - startLogging: 'Начать отладочную запись', - stepEnableLogging: 'Отладочная запись включена', - stepReproduce: 'Теперь воспроизведите проблему', - stepStopLogging: 'Остановить и отправить отчёт', - stopAndSubmit: 'Остановить и отправить', - maxDuration: 'Автоматическая остановка через {{minutes}} мин', - stoppingLogs: 'Сбор журналов и отправка...', - submitting: 'Отправка отчёта об ошибке...', - submittingStepConnection: 'Проверка подключений к принтерам', - submittingStepVirtualPrinters: 'Проверка настройки виртуальных принтеров', - submittingStepLogScan: 'Поиск известных проблем в последних журналах', - submittingStepSubmit: 'Отправка отчёта в GitHub', - submitSuccess: 'Отчёт об ошибке успешно отправлен!', - submitFailed: 'Не удалось отправить отчёт об ошибке', - diagnosticChecking: 'Проверка подключений к принтерам...', - diagnosticHealthy: 'Проверка подключения пройдена — проблем с принтерами не обнаружено.', - diagnosticSummary: 'Проблемы с подключением обнаружены у {{problems}} из {{total}} принтеров', - diagnosticIntro: 'У одного или нескольких принтеров есть проблема подключения, которая может быть причиной сбоя. Разверните сведения о принтере ниже и посмотрите способ исправления — возможно, отчёт об ошибке не потребуется. При необходимости его всё равно можно отправить ниже.', - logHealthSummary: 'В журналах найдены известные проблемы', - logHealthIntro: 'Последние записи журнала совпадают с известными проблемами. Посмотрите способы исправления ниже — возможно, отчёт об ошибке не потребуется. При необходимости его всё равно можно отправить.', - thankYou: 'Спасибо!', - submitted: 'Отчёт об ошибке отправлен.', - viewIssue: 'Открыть задачу', - unexpectedError: 'Произошла непредвиденная ошибка', - }, - forecast: { - title: 'Прогноз', - noSpools: 'Активные катушки не найдены. Добавьте катушки в учёт, чтобы увидеть прогноз.', - noUsageData: 'Нет данных о расходе — невозможно спрогнозировать остатки.', - sku: 'Артикул', - material: 'Материал', - stock: 'Запас', - dailyRate: 'Расход', - daysLeft: 'Осталось дней', - emptyBy: 'Закончится к', - reorderBy: 'Заказать до', - actions: 'Действия', - trend: 'Тренд', - estimated: 'Оценка', - noData: 'Нет данных', - timeframe: 'Период', - chartTitle: 'Прогноз запаса — 5 основных материалов', - dashedLinesROP: 'Пунктирные линии — точки заказа', - stockLevel: 'Уровень запаса', - reorderPoint: 'Точка заказа', - safetyMargin: 'Страховой запас', - trendLegend: 'Тренд (по истории, уровень обслуживания 95%)', - estimatedLegend: 'Оценка (по изменению массы)', - noDataLegend: 'Нет данных', - ropLabel: 'ТЗ', - ssLabel: 'СЗ', - safetyStockLegend: 'Страховой запас', - stockArrivalLegend: 'Поступление', - stockoutLegend: 'Исчерпание запаса', - alertCount_one: '{{count}} предупреждение', - alertCount_other: '{{count}} предупреждений', - order: 'Заказать', - save: 'Сохранить', - cancel: 'Отмена', - settingsSaved: 'Настройки сохранены', - failedSaveSettings: 'Не удалось сохранить настройки', - globalLeadTimeSaved: 'Общий срок поставки сохранён', - globalLeadTime: 'Общий срок поставки', - globalLeadTimeHint: 'Минимальный общий срок поставки, используемый при расчёте точки заказа для всех артикулов', - skuLeadTimeOverride: 'Переопределение срока поставки', - skuLeadTimeHint: '0 — использовать общий срок поставки. Значение больше 0 переопределяет его для этого артикула.', - safetyMarginLabel: 'Страховой запас', - effectiveLeadTime: 'Фактический срок поставки', - effectiveLeadTimeHint: 'макс. (общий {{global}} дн., для артикула {{sku}} дн.)', - reorderPointHint: 'средний расход × срок поставки + страховой запас — заказывайте, когда остаток достигнет этого уровня', - safetyMarginHint: 'Статистический страховой запас (z=1,65 × σ × √LT) плюс заданный пользователем резерв', - safetyMarginHintDays: 'Резерв поверх статистического страхового запаса.{{approx}}', - safetyMarginHintDaysApprox: ' ≈ {{g}} г при текущем расходе.', - safetyMarginHintG: 'Фиксированный весовой резерв поверх статистического страхового запаса.{{approx}}', - safetyMarginHintGApprox: ' ≈ {{days}} дн. при текущем расходе.', - individualSpools: 'Отдельные катушки', - labelWeight: 'Метка', - spoolCount_one: '{{count}} катушка', - spoolCount_other: '{{count}} катушек', - stockBreakRisk: 'Риск исчерпания запаса', - stockBreakDetail: 'Осталось {{days}} дн., срок поставки {{lt}} дн.', - stockBreakBefore: 'Запас закончится до пополнения', - reorderNow: 'Заказать сейчас', - reorderTriggerPassed: 'Дата заказа {{date}} уже прошла.', - shoppingList: 'Список покупок', - shoppingListItems_one: '({{count}} позиция)', - shoppingListItems_other: '({{count}} позиций)', - shoppingListEmpty: 'Список покупок пуст. Нажмите значок корзины в нужной строке, чтобы добавить позицию.', - addToCart: 'Добавить в список покупок', - alertsSnoozed: 'Отключить предупреждения для артикула', - alertsEnabled: 'Включить предупреждения для артикула', - addedToCart: 'Добавлено в список покупок', - failedAddItem: 'Не удалось добавить позицию', - listView: 'Список', - logisticsView: 'Логистика', - qty: 'Кол-во', - weight: 'Масса', - leadTime: 'Срок поставки', - expectedRestock: 'Ожидаемое пополнение', - status: 'Состояние', - note: 'Примечание', - pending: 'Ожидается', - purchased: 'Куплено', - received: 'Получено', - markPurchased: 'Отметить как купленное', - markReceived: 'Отметить как полученное — катушки будут добавлены в запас', - resetToPending: 'Вернуть в ожидание', - remove: 'Удалить', - clearAll: 'Очистить всё', - downloadCsv: 'CSV', - addToCartTitle: 'Добавить в список покупок', - byQuantity: 'По количеству', - byDuration: 'По сроку', - numberOfSpools: 'Количество катушек', - lastHowManyDays: 'На сколько дней должно хватить?', - noUsageQty: 'Нет данных о расходе — количество установлено равным 1.', - noteOptional: 'Примечание (необязательно)', - notePlaceholder: 'например, для проекта X, срочно…', - addNSpools_one: 'Добавить {{count}} катушку', - addNSpools_other: 'Добавить {{count}} катушек', - onArrival: 'После поступления', - stockBreakIn: 'Запас закончится через {{days}} дн.', - stockRunsOutBefore: 'Запас закончится раньше, чем истекут {{lt}} дн. срока поставки.', - atRate: 'При расходе {{rate}} г/день требуется', - moreSpools_one: 'ещё {{count}} катушка', - moreSpools_other: 'ещё {{count}} катушек', - bridgeGap: 'чтобы перекрыть дефицит.', - noReadAccess: 'У вас нет прав на просмотр прогноза запасов.', - noWriteAccess: 'У вас нет прав на изменение настроек прогноза.', - }, - kProfiles: { - title: 'K-профили', - noPrintersConfigured: 'Принтеры не настроены', - addPrinterInSettings: 'Добавьте принтер в настройках, чтобы управлять K-профилями', - noActivePrinters: 'Нет активных принтеров', - enablePrinterConnection: 'Включите подключение к принтеру, чтобы просматривать его K-профили', - loadingProfiles: 'Загрузка K-профилей...', - printerOffline: 'Принтер не в сети', - printerOfflineDesc: 'Выбранный принтер не подключён. Включите его, чтобы просмотреть K-профили.', - noMatchingProfiles: 'Подходящие профили не найдены', - noMatchingProfilesDesc: 'Ни один профиль не соответствует условиям поиска', - noKProfiles: 'K-профили отсутствуют', - noKProfilesDesc: 'Для сопла {{diameter}} мм не найдены профили Pressure Advance', - createFirstProfile: 'Создать первый профиль', - printer: 'Принтер', - nozzle: 'Сопло', - refresh: 'Обновить', - addProfile: 'Добавить профиль', - export: 'Экспорт', - import: 'Импорт', - select: 'Выбрать', - selectAll: 'Выбрать все', - delete: 'Удалить', - searchPlaceholder: 'Поиск по названию или филаменту...', - allExtruders: 'Все экструдеры', - leftOnly: 'Только левый', - rightOnly: 'Только правый', - allFlow: 'Любой тип потока', - hfOnly: 'Только HF', - sOnly: 'Только S', - sortName: 'Сортировка: название', - sortKValue: 'Сортировка: K-значение', - sortFilament: 'Сортировка: филамент', - leftExtruder: 'Левый экструдер', - rightExtruder: 'Правый экструдер', - modal: { - addTitle: 'Добавить K-профиль', - editTitle: 'Изменить K-профиль', - profileName: 'Название профиля', - profileNamePlaceholder: 'Мой профиль PLA', - kValue: 'K-значение', - kValuePlaceholder: '0.020', - kValueHelp: 'Типичный диапазон: 0,01–0,06 для PLA, 0,02–0,10 для PETG', - filament: 'Филамент', - selectFilament: 'Выберите филамент...', - noFilamentsHelp: 'Филаменты не найдены. Сначала создайте K-профиль в Bambu Studio.', - flowType: 'Тип потока', - highFlow: 'Высокопоточный', - standard: 'Стандартный', - nozzleSize: 'Диаметр сопла', - extruder: 'Экструдер', - extruders: 'Экструдеры', - left: 'Левый', - right: 'Правый', - notes: 'Заметки (хранятся локально)', - notesPlaceholder: 'Добавьте заметки об этом профиле...', - notesHelp: 'Заметки сохраняются в Bambuddy, а не на принтере', - syncing: 'Синхронизация с принтером...', - savingExtruder: 'Сохранение в экструдер {{current}}/{{total}}...', - pleaseWait: 'Подождите', - }, - deleteConfirm: { - title: 'Удалить профиль', - cannotUndo: 'Это действие нельзя отменить', - message: 'Удалить профиль «{{name}}» с принтера?', - }, - bulkDelete: { - title: 'Удалить профили', - cannotUndo: 'Это действие нельзя отменить', - message: 'Удалить с принтера выбранные профили ({{count}})?', - }, - toast: { - profileSaved: 'K-профиль сохранён', - profilesSaved: 'K-профиль сохранён для {{count}} экструдеров', - selectAtLeastOneExtruder: 'Выберите хотя бы один экструдер', - profileDeleted: 'K-профиль удалён', - profilesDeleted: 'Удалено профилей: {{count}}', - exportedProfiles: 'Экспортировано профилей: {{count}}', - importedProfiles: 'Импортировано {{count}} из {{total}} профилей', - noProfilesToExport: 'Нет профилей для экспорта', - invalidFileFormat: 'Недопустимый формат файла', - failedToParseImport: 'Не удалось разобрать файл импорта', - failedToSaveBatch: 'Не удалось сохранить K-профили', - noteSaved: 'Заметка сохранена', - failedToSaveNote: 'Не удалось сохранить заметку', - }, - permission: { - noRead: 'У вас нет прав на обновление профилей', - noCreate: 'У вас нет прав на добавление профилей', - noUpdate: 'У вас нет прав на изменение K-профилей', - noDelete: 'У вас нет прав на удаление K-профилей', - noExport: 'У вас нет прав на экспорт профилей', - noImport: 'У вас нет прав на импорт профилей', - }, + unknown: "—", + waiting: "Ожидание", + justNow: "Только что", + now: "Сейчас", + minsAgo: "{{count}} мин назад", + inMins: "через {{count}} мин", + hoursAgo: "{{count}} ч назад", + inHours: "через {{count}} ч", + daysAgo: "{{count}} дн. назад", + inDays: "через {{count}} дн.", }, spoolbuddy: { nav: { - dashboard: 'Панель', - ams: 'AMS', - inventory: 'Катушки', - writeTag: 'Запись метки', - settings: 'Настройки', + dashboard: "Панель", + ams: "AMS", + inventory: "Катушки", + writeTag: "Запись метки", + settings: "Настройки", }, status: { - nfcReady: 'NFC готов', - nfcOff: 'NFC выключен', - offline: 'Не в сети', - online: 'В сети', - noPrinters: 'Нет принтеров', - deviceOffline: 'Устройство не в сети', - waitingConnection: 'Ожидание подключения устройства...', - systemReady: 'Система готова', - status: 'Состояние', + nfcReady: "NFC готов", + nfcOff: "NFC выключен", + offline: "Не в сети", + online: "В сети", + noPrinters: "Нет принтеров", + deviceOffline: "Устройство не в сети", + waitingConnection: "Ожидание подключения устройства...", + systemReady: "Система готова", + status: "Состояние", }, dashboard: { - readyToScan: 'Готово к сканированию', - idleMessage: 'Поставьте катушку на весы для идентификации', - nfcHint: 'Метка NFC будет считана автоматически', - device: 'Устройство', - syncWeight: 'Синхронизировать массу', - weightSynced: 'Синхронизировано!', - unknownTag: 'Неизвестная метка', - newTag: 'Обнаружена новая метка', - onScale: 'на весах', - linkSpool: 'Связать с катушкой', - linkTagTitle: 'Связать метку с катушкой', - linkTag: 'Связать метку', - selectSpool: 'Выберите катушку, с которой нужно связать эту метку:', - noUntagged: 'Катушки без меток не найдены', - tagDetected: 'Метка обнаружена', - noTag: 'Метка отсутствует', - tagId: 'Метка', - grossWeight: 'Масса брутто', - spoolSize: 'Размер катушки', - close: 'Закрыть', - currentSpool: 'Текущая катушка', - plateReady: 'Пластина готова: {{name}}', - plateReadyLabel: 'Пластины, готовые к очистке', - plateClearAction: 'Очищено', - plateClearedToast: 'Пластина отмечена как очищенная', - plateClearFailed: 'Не удалось отметить пластину как очищенную', + readyToScan: "Готово к сканированию", + idleMessage: "Поставьте катушку на весы для идентификации", + nfcHint: "Метка NFC будет считана автоматически", + device: "Устройство", + syncWeight: "Синхронизировать массу", + weightSynced: "Синхронизировано!", + unknownTag: "Неизвестная метка", + newTag: "Обнаружена новая метка", + onScale: "на весах", + linkSpool: "Связать с катушкой", + linkTagTitle: "Связать метку с катушкой", + linkTag: "Связать метку", + selectSpool: "Выберите катушку, с которой нужно связать эту метку:", + noUntagged: "Катушки без меток не найдены", + tagDetected: "Метка обнаружена", + noTag: "Метка отсутствует", + tagId: "Метка", + grossWeight: "Масса брутто", + spoolSize: "Размер катушки", + close: "Закрыть", + currentSpool: "Текущая катушка", + plateReady: "Пластина готова: {{name}}", + plateReadyLabel: "Пластины, готовые к очистке", + plateClearAction: "Очищено", + plateClearedToast: "Пластина отмечена как очищенная", + plateClearFailed: "Не удалось отметить пластину как очищенную", }, modal: { - spoolDetected: 'Катушка обнаружена', - assignToAms: 'Назначить в AMS', - syncWeight: 'Синхронизировать массу', - weightSynced: 'Синхронизировано!', - syncing: 'Синхронизация...', - newTagDetected: 'Обнаружена новая метка', - addToInventory: 'Добавить в учёт', - assignToAmsTitle: 'Назначить в AMS', - selectSlot: 'Выберите слот', - assign: 'Назначить', - assigning: 'Назначение...', - assignSuccess: 'Назначено!', - assignPendingInsert: 'Назначено. Слот будет настроен после установки катушки.', - assignError: 'Не удалось назначить катушку. Повторите попытку.', - noPrinterSelected: 'Выберите принтер...', - noAmsDetected: 'На этом принтере AMS не обнаружена', - slot: 'Слот', + spoolDetected: "Катушка обнаружена", + assignToAms: "Назначить в AMS", + syncWeight: "Синхронизировать массу", + weightSynced: "Синхронизировано!", + syncing: "Синхронизация...", + newTagDetected: "Обнаружена новая метка", + addToInventory: "Добавить в учёт", + assignToAmsTitle: "Назначить в AMS", + selectSlot: "Выберите слот", + assign: "Назначить", + assigning: "Назначение...", + assignSuccess: "Назначено!", + assignPendingInsert: "Назначено. Слот будет настроен после установки катушки.", + assignError: "Не удалось назначить катушку. Повторите попытку.", + noPrinterSelected: "Выберите принтер...", + noAmsDetected: "На этом принтере AMS не обнаружена", + slot: "Слот", }, weight: { - noReading: 'Нет показаний', - stable: 'Стабильно', - measuring: 'Измерение...', - tare: 'Тарировать', - calibrate: 'Калибровать', + noReading: "Нет показаний", + stable: "Стабильно", + measuring: "Измерение...", + tare: "Тарировать", + calibrate: "Калибровать", }, spool: { - remaining: 'Остаток', - material: 'Материал', - brand: 'Бренд', - color: 'Цвет', - coreWeight: 'Сердечник', - labelWeight: 'По этикетке', - scaleWeight: 'По весам', - netWeight: 'Нетто', - lastUsed: 'Последнее использование', + remaining: "Остаток", + material: "Материал", + brand: "Бренд", + color: "Цвет", + coreWeight: "Сердечник", + labelWeight: "По этикетке", + scaleWeight: "По весам", + netWeight: "Нетто", + lastUsed: "Последнее использование", }, ams: { - noData: 'AMS не обнаружена', - connectAms: 'Подключите AMS, чтобы увидеть слоты филамента', - noPrinter: 'Принтер не выбран', - selectPrinter: 'Выберите принтер на верхней панели', - printerDisconnected: 'Принтер отключён', - humidity: 'Влажность', - level: 'Уровень', - active: 'Активен', - slot: 'Слот', - empty: 'Пусто', + noData: "AMS не обнаружена", + connectAms: "Подключите AMS, чтобы увидеть слоты филамента", + noPrinter: "Принтер не выбран", + selectPrinter: "Выберите принтер на верхней панели", + printerDisconnected: "Принтер отключён", + humidity: "Влажность", + level: "Уровень", + active: "Активен", + slot: "Слот", + empty: "Пусто", }, inventory: { - search: 'Поиск катушек...', - empty: 'В учёте нет катушек', - noResults: 'Подходящие катушки не найдены', - spools: 'катушек', - addSpool: 'Добавить катушку', + search: "Поиск катушек...", + empty: "В учёте нет катушек", + noResults: "Подходящие катушки не найдены", + spools: "катушек", + addSpool: "Добавить катушку", }, settings: { - tabDevice: 'Устройство', - tabDisplay: 'Экран', - tabScale: 'Весы', - tabUpdates: 'Обновления', - nfcReader: 'Считыватель NFC', - type: 'Тип', - connection: 'Подключение', - notConnected: 'Н/Д', - deviceInfo: 'Сведения об устройстве', - hostname: 'Хост', - uptime: 'Время работы', - systemConfig: 'Бэкенд и авторизация', - backendUrl: 'URL бэкенда Bambuddy', - apiToken: 'Токен API', - apiTokenPlaceholder: 'Введите токен API', - saveConfig: 'Сохранить конфигурацию', - systemQueued: 'Конфигурация поставлена в очередь.', - nfcDiagnostic: 'Диагностика NFC', - scaleDiagnostic: 'Диагностика весов', - readTagDiagnostic: 'Диагностика чтения метки', - testNfc: 'Проверить считыватель', - testScale: 'Проверить точность', - testReadTag: 'Считать метку', - systemFieldsRequired: 'Необходимо указать URL бэкенда.', - brightness: 'Яркость', - saved: 'Сохранено', - noBacklight: 'Подсветка DSI не обнаружена. Для управления яркостью требуется дисплей DSI.', - screenBlank: 'Тайм-аут отключения экрана', - screenBlankDesc: 'Экран выключается при бездействии. Коснитесь его для пробуждения.', - displayNote: 'Яркость регулируется программным фильтром.', - scaleCalibration: 'Калибровка весов', - currentWeight: 'Текущая масса', - tareOffset: 'Тара', - calFactor: 'Коэффициент', - knownWeight: 'Эталонная масса', - calStep1: 'Уберите всё с весов и нажмите «Установить ноль».', - calStep2: 'Положите на весы груз известной массы.', - setZero: 'Установить ноль', - calibrateNow: 'Калибровать', - calibrated: 'Откалибровано', - tareSet: 'Команда тарирования отправлена. Ожидание устройства...', - tareComplete: 'Тарирование завершено!', - tareTimedOut: 'Истекло время ожидания тарирования — демон SpoolBuddy запущен?', - tareFailed: 'Не удалось отправить команду тарирования', - zeroSet: 'Нулевая точка установлена. Положите на весы груз известной массы.', - calibrationDone: 'Калибровка завершена!', - calibrationFailed: 'Ошибка калибровки', - lastCalibrated: 'Последняя калибровка', - stable: 'Стабильно', - settling: 'Стабилизация...', - firmware: 'Прошивка', - scale: 'Весы', - noDevice: 'Устройство SpoolBuddy не найдено', - daemonVersion: 'Версия демона', - currentVersion: 'Текущая', - versionPending: 'Ожидание демона...', - checking: 'Проверка...', - checkUpdates: 'Проверить обновления', - updateAvailable: 'Доступно обновление', - updateInstructions: 'Обновление по SSH: запустите установочный скрипт SpoolBuddy.', - upToDate: 'Установлена актуальная версия', - includeBeta: 'Включать бета-версии', + tabDevice: "Устройство", + tabDisplay: "Экран", + tabScale: "Весы", + tabUpdates: "Обновления", + nfcReader: "Считыватель NFC", + type: "Тип", + connection: "Подключение", + notConnected: "Н/Д", + deviceInfo: "Сведения об устройстве", + hostname: "Хост", + uptime: "Время работы", + systemConfig: "Бэкенд и авторизация", + backendUrl: "URL бэкенда Bambuddy", + apiToken: "Токен API", + apiTokenPlaceholder: "Введите токен API", + saveConfig: "Сохранить конфигурацию", + systemQueued: "Конфигурация поставлена в очередь.", + nfcDiagnostic: "Диагностика NFC", + scaleDiagnostic: "Диагностика весов", + readTagDiagnostic: "Диагностика чтения метки", + testNfc: "Проверить считыватель", + testScale: "Проверить точность", + testReadTag: "Считать метку", + systemFieldsRequired: "Необходимо указать URL бэкенда.", + brightness: "Яркость", + saved: "Сохранено", + noBacklight: "Подсветка DSI не обнаружена. Для управления яркостью требуется дисплей DSI.", + screenBlank: "Тайм-аут отключения экрана", + screenBlankDesc: "Экран выключается при бездействии. Коснитесь его для пробуждения.", + displayNote: "Яркость регулируется программным фильтром.", + scaleCalibration: "Калибровка весов", + currentWeight: "Текущая масса", + tareOffset: "Тара", + calFactor: "Коэффициент", + knownWeight: "Эталонная масса", + calStep1: "Уберите всё с весов и нажмите «Установить ноль».", + calStep2: "Положите на весы груз известной массы.", + setZero: "Установить ноль", + calibrateNow: "Калибровать", + calibrated: "Откалибровано", + tareSet: "Команда тарирования отправлена. Ожидание устройства...", + tareComplete: "Тарирование завершено!", + tareTimedOut: "Истекло время ожидания тарирования — демон SpoolBuddy запущен?", + tareFailed: "Не удалось отправить команду тарирования", + zeroSet: "Нулевая точка установлена. Положите на весы груз известной массы.", + calibrationDone: "Калибровка завершена!", + calibrationFailed: "Ошибка калибровки", + lastCalibrated: "Последняя калибровка", + stable: "Стабильно", + settling: "Стабилизация...", + firmware: "Прошивка", + scale: "Весы", + noDevice: "Устройство SpoolBuddy не найдено", + daemonVersion: "Версия демона", + currentVersion: "Текущая", + versionPending: "Ожидание демона...", + checking: "Проверка...", + checkUpdates: "Проверить обновления", + updateAvailable: "Доступно обновление", + updateInstructions: "Обновление по SSH: запустите установочный скрипт SpoolBuddy.", + upToDate: "Установлена актуальная версия", + includeBeta: "Включать бета-версии", }, writeTag: { - tabExisting: 'Существующая катушка', - tabNew: 'Новая катушка', - tabReplace: 'Заменить метку', - searchPlaceholder: 'Поиск по материалу, цвету или бренду...', - noUntaggedSpools: 'Нет катушек без меток', - noTaggedSpools: 'Нет катушек с метками', - selectSpool: 'Выберите катушку, затем положите чистую метку NTAG на считыватель', - placeTag: 'Положите метку NTAG на считыватель', - tagReady: 'Метка обнаружена — можно записывать', - writeTag: 'Записать метку', - replaceTag: 'Заменить метку', - writing: 'Запись метки...', - waiting: 'Ожидание SpoolBuddy...', - writeSuccess: 'Метка успешно записана!', - writeFailed: 'Ошибка записи', - queueFailed: 'Не удалось поставить команду записи в очередь', - tryAgain: 'Повторить', - cancel: 'Отмена', - replaceWarning: 'Старая метка будет отвязана и заменена новой.', - deviceOffline: 'SpoolBuddy не в сети', - material: 'Материал', - colorName: 'Название цвета', - color: 'Цвет', - brand: 'Бренд', - weight: 'Масса (г)', - createSpool: 'Создать катушку', - creating: 'Создание...', - spoolCreated: 'Катушка создана! Можно записывать метку.', - createFailed: 'Не удалось создать катушку', - incompleteDataWarning: 'Метка записана с неполными данными Spoolman', + tabExisting: "Существующая катушка", + tabNew: "Новая катушка", + tabReplace: "Заменить метку", + searchPlaceholder: "Поиск по материалу, цвету или бренду...", + noUntaggedSpools: "Нет катушек без меток", + noTaggedSpools: "Нет катушек с метками", + selectSpool: "Выберите катушку, затем положите чистую метку NTAG на считыватель", + placeTag: "Положите метку NTAG на считыватель", + tagReady: "Метка обнаружена — можно записывать", + writeTag: "Записать метку", + replaceTag: "Заменить метку", + writing: "Запись метки...", + waiting: "Ожидание SpoolBuddy...", + writeSuccess: "Метка успешно записана!", + writeFailed: "Ошибка записи", + queueFailed: "Не удалось поставить команду записи в очередь", + tryAgain: "Повторить", + cancel: "Отмена", + replaceWarning: "Старая метка будет отвязана и заменена новой.", + deviceOffline: "SpoolBuddy не в сети", + material: "Материал", + colorName: "Название цвета", + color: "Цвет", + brand: "Бренд", + weight: "Масса (г)", + createSpool: "Создать катушку", + creating: "Создание...", + spoolCreated: "Катушка создана! Можно записывать метку.", + createFailed: "Не удалось создать катушку", + incompleteDataWarning: "Метка записана с неполными данными Spoolman", }, quickMenu: { - printerPower: 'Питание принтера', - systemControls: 'Система', - restartDaemon: 'Перезапустить демон', - restartBrowser: 'Перезапустить браузер', - reboot: 'Перезагрузить', - shutdown: 'Выключить', - swipeToClose: 'Проведите вниз, чтобы закрыть', - confirmTitle: 'Подтверждение', - confirmShutdown: 'Выключить SpoolBuddy? Для повторного включения потребуется физический доступ.', - confirmReboot: 'Перезагрузить SpoolBuddy?', - confirmRestartDaemon: 'Перезапустить демон SpoolBuddy? NFC и весы временно будут недоступны.', - confirmRestartBrowser: 'Перезапустить браузер киоска? Экран ненадолго погаснет.', - confirm: 'Подтвердить', - confirmPlugOn: 'Включить {{name}}?', - confirmPlugOff: 'Выключить {{name}}?', - turnOn: 'Включить', - turnOff: 'Выключить', + printerPower: "Питание принтера", + systemControls: "Система", + restartDaemon: "Перезапустить демон", + restartBrowser: "Перезапустить браузер", + reboot: "Перезагрузить", + shutdown: "Выключить", + swipeToClose: "Проведите вниз, чтобы закрыть", + confirmTitle: "Подтверждение", + confirmShutdown: "Выключить SpoolBuddy? Для повторного включения потребуется физический доступ.", + confirmReboot: "Перезагрузить SpoolBuddy?", + confirmRestartDaemon: "Перезапустить демон SpoolBuddy? NFC и весы временно будут недоступны.", + confirmRestartBrowser: "Перезапустить браузер киоска? Экран ненадолго погаснет.", + confirm: "Подтвердить", + confirmPlugOn: "Включить {{name}}?", + confirmPlugOff: "Выключить {{name}}?", + turnOn: "Включить", + turnOff: "Выключить", + }, + }, + diagnostic: { + modalTitle: "Диагностика подключения — {{name}}", + running: "Выполняется диагностика...", + runningElapsed: "Выполняется диагностика... ({{elapsed}} с)", + waitingForReportHint: "Ожидание публикации отчёта о состоянии принтера — это может занять до {{max}} секунд.", + runFailed: "Не удалось выполнить диагностику: {{error}}", + retry: "Запустить снова", + runButton: "Запустить диагностику", + sectionTitle: "Диагностика подключения", + sectionDescription: "Проверьте, почему принтер не подключается или не печатает: доступность портов, режим разработчика LAN, сетевой режим Docker и учётные данные.", + noPrinters: "Принтеры не настроены.", + overall: { + ok: "Проблем не обнаружено — подключение к принтеру исправно.", + warnings: "Принтер должен работать, но некоторые пункты требуют внимания.", + problems: "Обнаружены проблемы, объясняющие, почему принтер не подключается или не печатает.", + }, + check: { + port_mqtt: { + title: "Порт управления (MQTT 8883)", + pass: "Доступен — принтер принимает управляющие подключения.", + fail: "Порт 8883 недоступен. Принтер выключен, имеет другой IP-адрес либо подключение блокирует межсетевой экран. Проверьте IP-адрес принтера и доступность порта 8883.", + }, + port_ftps: { + title: "Порт передачи файлов (FTPS 990)", + pass: "Доступен — отправка файлов печати будет работать.", + warn: "Порт 990 недоступен. Мониторинг может работать, но отправка заданий на принтер завершится ошибкой. Убедитесь, что порт 990 не заблокирован.", + }, + external_storage: { + title: "Сохранять отправленные файлы во внешнем хранилище (шаг 4 установки)", + pass: "Принтер сообщает, что параметр включён: отправленные файлы будут сохраняться на SD-карте, а в архиве появятся миниатюры и метаданные слайсера.", + fail: "Принтер сообщает, что параметр выключен. Включите «Сохранять отправленные файлы во внешнем хранилище»: в новых прошивках (P2S 01.02 / Bambu Studio 2.6+) переключатель находится в настройках печати принтера, в старых — на вкладке «Устройство» Bambu Studio / OrcaSlicer. Без этого у всех архивных заданий не будет миниатюр и метаданных слайсера.", + skip: "Не проверено — требуется активное подключение MQTT. В старых версиях слайсеров этот параметр существует только в слайсере и принтер его не сообщает, поэтому проверка может пройти даже при выключенном параметре. Проверьте шаг 4 установки вручную.", + skip_unsupported_model: "В этой модели есть слот SD-карты, но включить соответствующую функцию невозможно: текущая прошивка серии P1 не предоставляет переключатель в Bambu Studio, а у принтера нет экрана. Исправлять здесь нечего. Пока Bambu Lab не добавит поддержку в прошивку, архивные задания могут сохраняться без миниатюр и метаданных слайсера.", + }, + port_rtsps: { + title: "Порт камеры ({{protocol}} {{port}})", + pass: "Доступен — видеопоток камеры будет работать.", + warn: "Порт {{port}} недоступен. Просмотр камеры в реальном времени работать не будет. На печать это не влияет.", + }, + network_mode: { + title: "Сетевой режим Docker", + pass: "Используется сетевой режим host.", + warn: "Bambuddy запущен в Docker с сетью bridge. Для обнаружения принтеров и виртуального принтера требуется режим host — пересоздайте контейнер с параметром «network_mode: host».", + skip: "Bambuddy запущен не в Docker — проверка неприменима.", + }, + subnet: { + title: "Подсеть", + pass: "Принтер и Bambuddy находятся в одной подсети.", + warn: "Принтер ({{printer_ip}}) и Bambuddy ({{host_ip}}) находятся в разных подсетях. Без настроенной маршрутизации между подсетями они могут быть недоступны друг для друга.", + skip: "Не удалось определить подсеть — проверка пропущена.", + }, + mqtt_auth: { + title: "Учётные данные принтера", + pass: "Принтер принял подключение.", + fail: "Принтер доступен, но отклонил подключение. Скорее всего, неверны код доступа или серийный номер. Код доступа изменяется при каждом переключении режима разработчика — снова скопируйте его с экрана принтера.", + skip: "Не проверено — принтер недоступен.", + }, + developer_mode: { + title: "Режим разработчика LAN", + pass: "Режим разработчика включён.", + fail: "Режим разработчика на принтере ВЫКЛЮЧЕН. Включите его в настройках LAN принтера и подтвердите кнопкой OK. Без него задания печати не запускаются.", + skip: "Не удалось проверить — требуется активное подключение к принтеру.", + }, + printer_publishing: { + title: "Принтер публикует состояние", + pass: "Принтер публикует обновления состояния — AMS, филаменты и K-профили будут корректно передаваться в слайсер.", + fail: "MQTT-брокер принял подключение, но принтер не опубликовал ни одного отчёта о состоянии. Почти всегда причина в неверном регистре или ошибке в серийном номере: тема device//report чувствительна к регистру. Сверьте серийный номер в настройках принтера с номером на его экране.", + skip: "Не удалось проверить — требуется активное подключение к принтеру.", + }, }, }, systemHealth: { - sectionTitle: 'Состояние системы', - sectionDescription: 'Проверяет последние журналы на известные проблемы, которые обычно можно исправить самостоятельно до обращения в поддержку.', - rescan: 'Проверить снова', - clean: 'В последних {{times}} записях журнала известные проблемы не найдены.', - logUnavailable: 'Запись журналов в файл отключена, поэтому выполнить проверку невозможно. Включите файловое журналирование.', - learnMore: 'Как исправить', - fixLabel: 'Исправление:', - occurrences: 'Обнаружено {{times}} раз; последний раз — {{lastSeen}}', + sectionTitle: "Состояние системы", + sectionDescription: "Проверяет последние журналы на известные проблемы, которые обычно можно исправить самостоятельно до обращения в поддержку.", + rescan: "Проверить снова", + clean: "В последних {{times}} записях журнала известные проблемы не найдены.", + logUnavailable: "Запись журналов в файл отключена, поэтому выполнить проверку невозможно. Включите файловое журналирование.", + learnMore: "Как исправить", + fixLabel: "Исправление:", + occurrences: "Обнаружено {{times}} раз; последний раз — {{lastSeen}}", category: { - layer8: 'Можно исправить самостоятельно', - environment: 'Окружение', - bug: 'Сообщите об ошибке', + layer8: "Можно исправить самостоятельно", + environment: "Окружение", + bug: "Сообщите об ошибке", }, signature: { - 'ftp-auth-rejected': { - name: 'Принтер отклонил код доступа', - cause: 'Принтер отклонил вход в службу передачи файлов. Код доступа неверен либо изменился после переключения режима разработчика.', - fix: 'Снова скопируйте код доступа с экрана принтера в настройках LAN и обновите его в настройках принтера Bambuddy.', + "ftp-auth-rejected": { + name: "Принтер отклонил код доступа", + cause: "Принтер отклонил вход в службу передачи файлов. Код доступа неверен либо изменился после переключения режима разработчика.", + fix: "Снова скопируйте код доступа с экрана принтера в настройках LAN и обновите его в настройках принтера Bambuddy.", }, - 'ftp-connection-timeout': { - name: 'Истекло время подключения к службе передачи файлов', - cause: 'Bambuddy не смог подключиться к порту передачи файлов принтера (FTPS 990). Порт заблокирован, принтер выключен либо находится в другой подсети.', - fix: 'Убедитесь, что порт 990 между Bambuddy и принтером не блокируется и оба устройства находятся в доступных друг другу сетях.', + "ftp-connection-timeout": { + name: "Истекло время подключения к службе передачи файлов", + cause: "Bambuddy не смог подключиться к порту передачи файлов принтера (FTPS 990). Порт заблокирован, принтер выключен либо находится в другой подсети.", + fix: "Убедитесь, что порт 990 между Bambuddy и принтером не блокируется и оба устройства находятся в доступных друг другу сетях.", }, - 'ftp-ssl-error': { - name: 'Ошибка защищённого соединения с файловой службой', - cause: 'Не удалось выполнить TLS-рукопожатие с сервером передачи файлов принтера. Частые причины — межсетевой экран или устаревшая прошивка принтера.', - fix: 'Обновите прошивку принтера и убедитесь, что межсетевой экран или прокси не перехватывает соединение на порту 990.', + "ftp-ssl-error": { + name: "Ошибка защищённого соединения с файловой службой", + cause: "Не удалось выполнить TLS-рукопожатие с сервером передачи файлов принтера. Частые причины — межсетевой экран или устаревшая прошивка принтера.", + fix: "Обновите прошивку принтера и убедитесь, что межсетевой экран или прокси не перехватывает соединение на порту 990.", }, - 'mqtt-connection-flapping': { - name: 'Подключение к принтеру постоянно обрывается', - cause: 'Управляющее соединение MQTT 8883 постоянно разрывается и восстанавливается. Обычно причина в нестабильной сети или частично заблокированном порте.', - fix: 'Проверьте уровень Wi‑Fi у принтера, по возможности используйте проводное подключение и убедитесь в стабильной доступности порта 8883.', + "mqtt-connection-flapping": { + name: "Подключение к принтеру постоянно обрывается", + cause: "Управляющее соединение MQTT 8883 постоянно разрывается и восстанавливается. Обычно причина в нестабильной сети или частично заблокированном порте.", + fix: "Проверьте уровень Wi‑Fi у принтера, по возможности используйте проводное подключение и убедитесь в стабильной доступности порта 8883.", }, - 'camera-connection-refused': { - name: 'Видеопоток камеры недоступен', - cause: 'Камера недоступна по порту RTSPS 322. Порт заблокирован либо на принтере выключены камера или просмотр по LAN.', - fix: 'Включите камеру и просмотр по LAN на принтере и убедитесь, что порт 322 не заблокирован. На печать это не влияет.', + "camera-connection-refused": { + name: "Видеопоток камеры недоступен", + cause: "Камера недоступна по порту RTSPS 322. Порт заблокирован либо на принтере выключены камера или просмотр по LAN.", + fix: "Включите камеру и просмотр по LAN на принтере и убедитесь, что порт 322 не заблокирован. На печать это не влияет.", }, - 'database-locked': { - name: 'Конфликт записи в базу данных', - cause: 'Под нагрузкой SQLite выдаёт ошибки «database is locked». Это часто происходит при одновременной работе с несколькими принтерами.', - fix: 'Переведите Bambuddy на внешнюю базу PostgreSQL. См. руководство по PostgreSQL в документации.', + "database-locked": { + name: "Конфликт записи в базу данных", + cause: "Под нагрузкой SQLite выдаёт ошибки «database is locked». Это часто происходит при одновременной работе с несколькими принтерами.", + fix: "Переведите Bambuddy на внешнюю базу PostgreSQL. См. руководство по PostgreSQL в документации.", }, }, }, vpDiagnostic: { - title: 'Проверка настройки — {{name}}', - runButton: 'Запустить проверку', - running: 'Выполняется проверка настройки...', - runFailed: 'Не удалось выполнить проверку: {{error}}', - retry: 'Запустить снова', + title: "Проверка настройки — {{name}}", + runButton: "Запустить проверку", + running: "Выполняется проверка настройки...", + runFailed: "Не удалось выполнить проверку: {{error}}", + retry: "Запустить снова", overall: { - ok: 'Все проверки пройдены — виртуальный принтер настроен правильно.', - warnings: 'Виртуальный принтер должен работать, но некоторые пункты требуют внимания.', - problems: 'Обнаружены проблемы, из-за которых слайсер не видит или не может использовать виртуальный принтер.', + ok: "Все проверки пройдены — виртуальный принтер настроен правильно.", + warnings: "Виртуальный принтер должен работать, но некоторые пункты требуют внимания.", + problems: "Обнаружены проблемы, из-за которых слайсер не видит или не может использовать виртуальный принтер.", }, check: { enabled: { - title: 'Виртуальный принтер включён', - fail: 'Виртуальный принтер выключен. Включите его, чтобы он обнаруживался в сети.', + title: "Виртуальный принтер включён", + fail: "Виртуальный принтер выключен. Включите его, чтобы он обнаруживался в сети.", }, running: { - title: 'Службы запущены', - fail: 'Виртуальный принтер включён, но его службы не работают. Проверьте журнал Bambuddy: обычно запуску мешает конфликт IP-адреса привязки или ошибка прав доступа.', + title: "Службы запущены", + fail: "Виртуальный принтер включён, но его службы не работают. Проверьте журнал Bambuddy: обычно запуску мешает конфликт IP-адреса привязки или ошибка прав доступа.", }, bind_interface: { - title: 'Сетевой интерфейс привязки', - fail: 'Интерфейс привязки не задан или больше не существует на этом хосте. Выберите актуальный интерфейс в списке «Интерфейс привязки».', + title: "Сетевой интерфейс привязки", + fail: "Интерфейс привязки не задан или больше не существует на этом хосте. Выберите актуальный интерфейс в списке «Интерфейс привязки».", }, access_code: { - title: 'Код доступа задан', - fail: 'Код доступа не задан. В слайсере необходимо указать тот же восьмизначный код, который задан здесь.', + title: "Код доступа задан", + fail: "Код доступа не задан. В слайсере необходимо указать тот же восьмизначный код, который задан здесь.", }, target_printer: { - title: 'Целевой принтер', - fail: 'Целевой принтер не выбран. Для режима прокси необходимо указать реальный принтер.', - warn: 'Целевой принтер сейчас не в сети. Перенаправление возобновится после его подключения.', + title: "Целевой принтер", + fail: "Целевой принтер не выбран. Для режима прокси необходимо указать реальный принтер.", + warn: "Целевой принтер сейчас не в сети. Перенаправление возобновится после его подключения.", }, port_ftps: { - title: 'Служба загрузки файлов (порт {{port}})', - fail: 'На порту {{port}} выбранного IP-адреса никто не слушает, поэтому слайсер не сможет загружать файлы. Обычно причина в конфликте порта на этом интерфейсе.', + title: "Служба загрузки файлов (порт {{port}})", + fail: "На порту {{port}} выбранного IP-адреса никто не слушает, поэтому слайсер не сможет загружать файлы. Обычно причина в конфликте порта на этом интерфейсе.", }, port_mqtt: { - title: 'Служба управления (порт {{port}})', - fail: 'На порту {{port}} выбранного IP-адреса никто не слушает, поэтому слайсер не сможет подключиться и отображать состояние.', + title: "Служба управления (порт {{port}})", + fail: "На порту {{port}} выбранного IP-адреса никто не слушает, поэтому слайсер не сможет подключиться и отображать состояние.", }, port_bind: { - title: 'Служба обнаружения (порт {{port}})', - fail: 'На порту {{port}} выбранного IP-адреса никто не слушает, поэтому сетевое обнаружение слайсером не работает.', + title: "Служба обнаружения (порт {{port}})", + fail: "На порту {{port}} выбранного IP-адреса никто не слушает, поэтому сетевое обнаружение слайсером не работает.", }, certificate: { - title: 'Сертификат TLS', - pass: 'Сертификат готов. Убедитесь, что сертификат центра сертификации Bambuddy, указанный выше, импортирован в доверенное хранилище слайсера.', - fail: 'Сертификат TLS для этого виртуального принтера отсутствует. Проверьте права записи в каталог данных Bambuddy.', + title: "Сертификат TLS", + pass: "Сертификат готов. Убедитесь, что сертификат центра сертификации Bambuddy, указанный выше, импортирован в доверенное хранилище слайсера.", + fail: "Сертификат TLS для этого виртуального принтера отсутствует. Проверьте права записи в каталог данных Bambuddy.", }, }, }, + bugReport: { + title: "Сообщить об ошибке", + description: "Описание", + descriptionPlaceholder: "Что пошло не так? Опишите проблему...", + email: "Электронная почта (необязательно)", + emailPlaceholder: "your@email.com", + emailPrivacy: "Указанный адрес будет добавлен в сворачиваемый раздел задачи GitHub, чтобы сопровождающий мог с вами связаться.", + screenshot: "Снимок экрана", + uploadOrPaste: "Загрузите, вставьте или перетащите изображение", + dataCollectedSummary: "Какие данные включаются в отчёт?", + dataIncluded: "Включаются:", + dataIncludedList: "Версия приложения, ОС, архитектура, версия Python, статистика базы данных (только количества), модели принтеров, количество сопел, версии прошивок, состояние подключений, состояние интеграций (Spoolman, MQTT, HA), неконфиденциальные настройки, количество сетевых интерфейсов, сведения о Docker и версии зависимостей.", + dataNeverIncluded: "Никогда не включаются:", + dataNeverIncludedList: "Имена принтеров, серийные номера, коды доступа, пароли, IP-адреса, адреса электронной почты, ключи API, токены, URL вебхуков, имена хостов и пользователей.", + submit: "Отправить", + startLogging: "Начать отладочную запись", + stepEnableLogging: "Отладочная запись включена", + stepReproduce: "Теперь воспроизведите проблему", + stepStopLogging: "Остановить и отправить отчёт", + stopAndSubmit: "Остановить и отправить", + maxDuration: "Автоматическая остановка через {{minutes}} мин", + stoppingLogs: "Сбор журналов и отправка...", + submitting: "Отправка отчёта об ошибке...", + submittingStepConnection: "Проверка подключений к принтерам", + submittingStepVirtualPrinters: "Проверка настройки виртуальных принтеров", + submittingStepLogScan: "Поиск известных проблем в последних журналах", + submittingStepSubmit: "Отправка отчёта в GitHub", + submitSuccess: "Отчёт об ошибке успешно отправлен!", + submitFailed: "Не удалось отправить отчёт об ошибке", + diagnosticChecking: "Проверка подключений к принтерам...", + diagnosticHealthy: "Проверка подключения пройдена — проблем с принтерами не обнаружено.", + diagnosticSummary: "Проблемы с подключением обнаружены у {{problems}} из {{total}} принтеров", + diagnosticIntro: "У одного или нескольких принтеров есть проблема подключения, которая может быть причиной сбоя. Разверните сведения о принтере ниже и посмотрите способ исправления — возможно, отчёт об ошибке не потребуется. При необходимости его всё равно можно отправить ниже.", + logHealthSummary: "В журналах найдены известные проблемы", + logHealthIntro: "Последние записи журнала совпадают с известными проблемами. Посмотрите способы исправления ниже — возможно, отчёт об ошибке не потребуется. При необходимости его всё равно можно отправить.", + thankYou: "Спасибо!", + submitted: "Отчёт об ошибке отправлен.", + viewIssue: "Открыть задачу", + unexpectedError: "Произошла непредвиденная ошибка", + }, + failureDetection: { + title: "Обнаружение сбоев с помощью ИИ", + description: "Контролируйте печать через собственный сервер Obico ML API и автоматически реагируйте на обнаруженные сбои.", + mlUrl: "URL Obico ML API", + mlUrlHint: "Базовый URL собственного контейнера Obico ml_api, например http://192.168.1.10:3333.", + test: "Проверить", + testSuccess: "ML API доступен и работает.", + testFailed: "Не удалось подключиться к ML API.", + sensitivity: "Чувствительность", + sensitivityLow: "Низкая (меньше ложных срабатываний)", + sensitivityMedium: "Средняя (сбалансированная)", + sensitivityHigh: "Высокая (раннее обнаружение, больше ложных срабатываний)", + sensitivityHint: "Определяет пороги уверенности, при которых создаются предупреждения и фиксируются сбои.", + action: "Действие при обнаружении сбоя", + actionNotify: "Только уведомить", + actionPause: "Приостановить печать", + actionPauseOff: "Приостановить печать и отключить питание", + pollInterval: "Интервал проверки (секунды)", + pollIntervalHint: "Как часто проверять каждый принтер во время печати. Минимум 5 с, максимум 120 с.", + externalUrlMissing: "Внешний URL не задан.", + externalUrlHint: "ML API получает снимок камеры по URL. Укажите внешний URL в общих настройках, чтобы контейнер ML API мог обращаться к Bambuddy.", + perPrinterTitle: "Контролируемые принтеры", + perPrinterHint: "Выберите принтеры, за которыми будет следить служба обнаружения.", + monitorAll: "Контролировать все подключённые принтеры", + statusTitle: "Состояние", + serviceRunning: "Служба работает", + thresholds: "Низкий / высокий порог", + activePrinters: "Активные задания", + noActivePrints: "Сейчас ничего не печатается.", + historyTitle: "Последние обнаружения", + noHistory: "Обнаружений пока нет.", + }, + makerworld: { + title: "MakerWorld", + description: "Вставьте URL модели MakerWorld, чтобы импортировать и напечатать её прямо из Bambuddy — без перехода в приложение Bambu Handy.", + pasteUrlHeader: "Импорт из MakerWorld", + pasteUrlPlaceholder: "https://makerworld.com/en/models/… или любая ссылка MakerWorld", + resolveButton: "Получить данные", + signInRequiredTitle: "Для загрузки требуется вход в Bambu Cloud", + signInRequiredBody: "Просматривать сведения о модели можно анонимно, но для загрузки файлов 3MF MakerWorld требует учётную запись Bambu Cloud.", + signInExpiredTitle: "Срок авторизации в Bambu Cloud истёк", + signInExpiredBody: "Вы по-прежнему авторизованы в Bambuddy, но Bambu Lab перестал принимать сохранённый токен, поэтому загрузки завершатся ошибкой. Выполните вход в Bambu Cloud снова.", + openCloudSettings: "Открыть настройки облака", + untitledModel: "Модель без названия", + byCreator: "Автор: {{name}}", + downloadsCount: "Загрузок: {{count}}", + licensePrefix: "Лицензия", + alreadyImported: "Уже в библиотеке", + openOnMakerworld: "Открыть на MakerWorld", + alreadyInLibrary: "Эта модель уже есть в библиотеке — найдите её в разделе «Файлы → MakerWorld».", + importSuccess: "Импортирован файл {{filename}} — сохранён в «Файлы → MakerWorld».", + platesHeader: "Пластины ({{count}})", + plateDefaultName: "Пластина {{n}}", + materialCount: "Филаментов: {{count}}", + amsRequired: "Требуется AMS", + slicedFor: "Нарезано для {{printer}}", + alsoCompatible: "Также отмечено как совместимое: {{printers}}", + importToLibrary: "Сохранить", + sliceIn: "Сохранить и нарезать в {{slicer}}", + disclaimer: "Интеграция с MakerWorld использует документированные сообществом конечные точки API. Bambuddy не связан с MakerWorld или Bambu Lab и не одобрен ими.", + lastImportSuccess: "Импортировано в библиотеку", + lastImportAlreadyInLibrary: "Уже в библиотеке", + viewInLibrary: "Открыть в файловом менеджере", + openInBambuStudio: "Открыть в Bambu Studio", + openInOrcaSlicer: "Открыть в OrcaSlicer", + importTo: "Импортировать в файловый менеджер", + recentImportsHeader: "Недавние импорты", + phaseResolving: "Получение данных", + phaseDownloading: "Загрузка", + folderAuto: "MakerWorld (по умолчанию)", + importAll: "Импортировать всё", + importAllProgress: "Импорт {{current}} из {{total}}", + openGallery: "Открыть галерею изображений", + galleryPrev: "Предыдущее изображение", + galleryNext: "Следующее изображение", + deleteImport: "Удалить из библиотеки", + importDeleting: "Удаление…", + importDeleted: "Удалено из библиотеки", + confirmDelete: "Удалить {{filename}} из библиотеки? Локальный файл будет удалён, но пластину можно снова импортировать из MakerWorld.", + errors: { + resolveFailed: "Не удалось обработать эту ссылку MakerWorld.", + downloadFailed: "Ошибка загрузки. Повторите попытку.", + deleteFailed: "Не удалось удалить файл из библиотеки.", + }, + }, gcodeViewer: { - back: 'Назад', - backToArchives: 'Вернуться в архив печати', - backToFiles: 'Вернуться в файловый менеджер', + back: "Назад", + backToArchives: "Вернуться в архив печати", + backToFiles: "Вернуться в файловый менеджер", + }, + libraryTrash: { + title: "Корзина", + headerButton: "Корзина", + headerTooltip: "Просмотреть файлы, перемещённые в корзину", + backToFiles: "Вернуться к файлам", + subtitleAdmin: "Удалённые файлы хранятся здесь {{days}} дней, затем удаляются автоматически. Показаны файлы всех пользователей.", + subtitleUser: "Удалённые файлы хранятся здесь {{days}} дней, затем удаляются автоматически.", + loading: "Загрузка корзины…", + loadError: "Не удалось загрузить корзину.", + empty: "Корзина пуста.", + summary: "{{count}} файлов · {{size}}", + emptyTrash: "Очистить корзину", + restore: "Восстановить", + purgeNow: "Удалить сейчас", + autoPurgeIn: "Автоудаление через {{when}}", + days: "дней", + retentionLabel: "Автоудаление через", + selectAll: "Выбрать всё", + selectOne: "Выбрать {{filename}}", + selectionCount: "Выбрано: {{count}}", + bulkRestore: "Восстановить выбранные", + bulkPurge: "Удалить выбранные", + col: { + filename: "Файл", + folder: "Папка", + size: "Размер", + deleted: "Перемещён в корзину", + autoPurge: "Автоудаление", + owner: "Владелец", + actions: "Действия", + }, + confirm: { + purgeTitle: "Удалить навсегда?", + purgeBody: "Файл {{filename}} будет удалён с диска без возможности восстановления.", + emptyTitle: "Очистить корзину?", + emptyBody: "Все файлы ({{count}}) будут удалены с диска. Отменить это действие нельзя.", + bulkPurgeTitle: "Удалить выбранные файлы навсегда?", + bulkPurgeBody: "Выбранные файлы ({{count}}) будут удалены с диска без возможности восстановления.", + cta: "Удалить навсегда", + }, + toast: { + restored: "Файл восстановлен.", + restoreFailed: "Не удалось восстановить файл.", + purged: "Файл удалён навсегда.", + purgeFailed: "Не удалось удалить файл.", + emptied: "Из корзины удалено файлов: {{count}}.", + emptyFailed: "Не удалось очистить корзину.", + retentionSaved: "Срок автоудаления: {{days}} дней.", + retentionFailed: "Не удалось сохранить срок хранения.", + bulkRestored: "Восстановлено файлов: {{count}}.", + bulkPurged: "Удалено файлов: {{count}}.", + }, }, libraryPurge: { - title: 'Очистить старые файлы', - headerButton: 'Очистить старые', - headerTooltip: 'Массово переместить старые файлы в корзину', - description: 'За один проход убирает старые файлы из библиотеки. Для печатавшихся файлов возраст считается от даты последней печати, для ни разу не печатавшихся — от даты загрузки.', - ageLabel: 'Переместить файлы старше', - days: 'дней', - includeNeverPrinted: 'Включить файлы, которые ни разу не печатались', - effectsTitle: 'Что произойдёт после нажатия «Очистить»', - effect1: 'Подходящие файлы будут перемещены в корзину, но пока не удалены с диска.', - effect2: 'Их можно восстановить из корзины до окончания срока хранения.', - effect3: 'После окончания срока хранения очистка корзины окончательно удалит их с диска.', - effect4: 'Файлы во внешних подключённых папках пропускаются: Bambuddy не удаляет чужие данные.', - previewLoading: 'Подсчёт подходящих файлов…', - previewFailed: 'Не удалось выполнить предварительный расчёт.', - previewSummary: 'В корзину будет перемещено: {{count}} файлов · {{size}}', - andMore: '…и ещё {{count}}', - warning: 'Файлы в корзине продолжают занимать место до окончания срока хранения. Чтобы освободить диск сразу, после операции очистите корзину.', - confirmCta: 'Переместить в корзину: {{count}}', - purging: 'Перемещение в корзину…', + title: "Очистить старые файлы", + headerButton: "Очистить старые", + headerTooltip: "Массово переместить старые файлы в корзину", + description: "За один проход убирает старые файлы из библиотеки. Для печатавшихся файлов возраст считается от даты последней печати, для ни разу не печатавшихся — от даты загрузки.", + ageLabel: "Переместить файлы старше", + days: "дней", + includeNeverPrinted: "Включить файлы, которые ни разу не печатались", + effectsTitle: "Что произойдёт после нажатия «Очистить»", + effect1: "Подходящие файлы будут перемещены в корзину, но пока не удалены с диска.", + effect2: "Их можно восстановить из корзины до окончания срока хранения.", + effect3: "После окончания срока хранения очистка корзины окончательно удалит их с диска.", + effect4: "Файлы во внешних подключённых папках пропускаются: Bambuddy не удаляет чужие данные.", + previewLoading: "Подсчёт подходящих файлов…", + previewFailed: "Не удалось выполнить предварительный расчёт.", + previewSummary: "В корзину будет перемещено: {{count}} файлов · {{size}}", + andMore: "…и ещё {{count}}", + warning: "Файлы в корзине продолжают занимать место до окончания срока хранения. Чтобы освободить диск сразу, после операции очистите корзину.", + confirmCta: "Переместить в корзину: {{count}}", + purging: "Перемещение в корзину…", toast: { - success: 'Перемещено в корзину файлов: {{count}}.', - failed: 'Не удалось очистить файлы.', + success: "Перемещено в корзину файлов: {{count}}.", + failed: "Не удалось очистить файлы.", }, }, libraryAutoPurge: { - enableLabel: 'Автоматически очищать старые файлы', - enableDescription: 'Раз в сутки запускает административную очистку. Сначала файлы попадают в корзину и не удаляются немедленно.', - ageLabel: 'Автоматически очищать файлы старше', - ageDescription: 'Минимум 7 дней, максимум 10 лет. Используется тот же способ расчёта возраста, что и при ручной очистке.', - days: 'дней', - includeNeverPrinted: 'Включить файлы, которые ни разу не печатались', - saveFailed: 'Не удалось сохранить настройки автоочистки.', + enableLabel: "Автоматически очищать старые файлы", + enableDescription: "Раз в сутки запускает административную очистку. Сначала файлы попадают в корзину и не удаляются немедленно.", + ageLabel: "Автоматически очищать файлы старше", + ageDescription: "Минимум 7 дней, максимум 10 лет. Используется тот же способ расчёта возраста, что и при ручной очистке.", + days: "дней", + includeNeverPrinted: "Включить файлы, которые ни разу не печатались", + saveFailed: "Не удалось сохранить настройки автоочистки.", }, archivePurge: { - headerButton: 'Очистить старые', - headerTooltip: 'Массово удалить старые записи архива', - title: 'Очистить старые записи архива', - description: 'Удаляет старую историю печати. Возраст записи считается от последнего завершения печати; повторная печать обновляет дату, поэтому используемые записи не удаляются.', - ageLabel: 'Удалить записи, которые не печатались последние', - days: 'дней', - effectsTitle: 'Что произойдёт после нажатия «Очистить»', - effect1: 'Каждая подходящая запись будет скрыта из списков, а связанные файлы удалены с диска: 3MF, миниатюра, таймлапс, исходный 3MF, проект F3D и фотографии.', - effect2: 'Строка архива останется в базе, поэтому быстрая статистика сохранит вклад по филаменту, времени, стоимости и энергии — как при обычном удалении отдельной записи.', - effect3: 'Установите флажок «Также удалить из статистики», чтобы исключить вклад из быстрой статистики. Это действие необратимо.', - effect4: 'Повторная печать обновляет дату записи, поэтому используемые архивы не удаляются.', - purgeStatsLabel: 'Также удалить из статистики', - purgeStatsHint: 'Исключает соответствующие записи из быстрой статистики по филаменту, времени, стоимости и энергии. Без этого удаляются только файлы, а статистика сохраняется.', - previewLoading: 'Подсчёт подходящих записей…', - previewFailed: 'Не удалось выполнить предварительный расчёт.', - previewSummary: 'Будет удалено: {{count}} записей · {{size}}', - andMore: '…и ещё {{count}}', - warning: 'Файлы будут удалены с диска без возможности восстановления. Перед продолжением скачайте или добавьте в избранное всё, что нужно сохранить.', - confirmCta: 'Удалить записей: {{count}}', - purging: 'Удаление…', + headerButton: "Очистить старые", + headerTooltip: "Массово удалить старые записи архива", + title: "Очистить старые записи архива", + description: "Удаляет старую историю печати. Возраст записи считается от последнего завершения печати; повторная печать обновляет дату, поэтому используемые записи не удаляются.", + ageLabel: "Удалить записи, которые не печатались последние", + days: "дней", + effectsTitle: "Что произойдёт после нажатия «Очистить»", + effect1: "Каждая подходящая запись будет скрыта из списков, а связанные файлы удалены с диска: 3MF, миниатюра, таймлапс, исходный 3MF, проект F3D и фотографии.", + effect2: "Строка архива останется в базе, поэтому быстрая статистика сохранит вклад по филаменту, времени, стоимости и энергии — как при обычном удалении отдельной записи.", + effect3: "Установите флажок «Также удалить из статистики», чтобы исключить вклад из быстрой статистики. Это действие необратимо.", + effect4: "Повторная печать обновляет дату записи, поэтому используемые архивы не удаляются.", + purgeStatsLabel: "Также удалить из статистики", + purgeStatsHint: "Исключает соответствующие записи из быстрой статистики по филаменту, времени, стоимости и энергии. Без этого удаляются только файлы, а статистика сохраняется.", + previewLoading: "Подсчёт подходящих записей…", + previewFailed: "Не удалось выполнить предварительный расчёт.", + previewSummary: "Будет удалено: {{count}} записей · {{size}}", + andMore: "…и ещё {{count}}", + warning: "Файлы будут удалены с диска без возможности восстановления. Перед продолжением скачайте или добавьте в избранное всё, что нужно сохранить.", + confirmCta: "Удалить записей: {{count}}", + purging: "Удаление…", toast: { - success: 'Удалено записей архива: {{count}}.', - failed: 'Не удалось очистить архив.', + success: "Удалено записей архива: {{count}}.", + failed: "Не удалось очистить архив.", }, }, archiveAutoPurge: { - enableLabel: 'Автоматически очищать старые записи архива', - enableDescription: 'Раз в сутки скрывает записи из списков и удаляет их файлы с диска, если они не печатались в течение заданного срока. Повторная печать сбрасывает отсчёт.', - ageLabel: 'Автоматически удалять записи, которые не печатались последние', - ageDescription: 'Минимум 7 дней, максимум 10 лет. Срок считается от последнего завершения печати; повторная печать обновляет дату. Удаляются 3MF, миниатюра, таймлапс, исходный 3MF, F3D и фотографии.', - days: 'дней', - purgeStatsLabel: 'Также удалить из статистики', - purgeStatsDescription: 'Если включено, ежедневная очистка исключает удалённые записи из быстрой статистики по филаменту, времени, стоимости и энергии. По умолчанию выключено: статистика сохраняется, удаляются только файлы.', - runNow: 'Очистить архив сейчас', - saveFailed: 'Не удалось сохранить настройки автоочистки.', + enableLabel: "Автоматически очищать старые записи архива", + enableDescription: "Раз в сутки скрывает записи из списков и удаляет их файлы с диска, если они не печатались в течение заданного срока. Повторная печать сбрасывает отсчёт.", + ageLabel: "Автоматически удалять записи, которые не печатались последние", + ageDescription: "Минимум 7 дней, максимум 10 лет. Срок считается от последнего завершения печати; повторная печать обновляет дату. Удаляются 3MF, миниатюра, таймлапс, исходный 3MF, F3D и фотографии.", + days: "дней", + purgeStatsLabel: "Также удалить из статистики", + purgeStatsDescription: "Если включено, ежедневная очистка исключает удалённые записи из быстрой статистики по филаменту, времени, стоимости и энергии. По умолчанию выключено: статистика сохраняется, удаляются только файлы.", + runNow: "Очистить архив сейчас", + saveFailed: "Не удалось сохранить настройки автоочистки.", + }, + cameraTokens: { + scope: { + camera_stream: "Поток камеры", + camwall: "Стена камер", + }, + title: "Токены API камеры", + navTitle: "Токены API камеры", + description: "Долгоживущие токены для встраивания видеопотока в Home Assistant, Frigate, киоски и другие системы, которым нужен постоянный URL. Каждый токен даёт доступ только к потоку камеры и может быть отозван в любой момент.", + loading: "Загрузка...", + confirmRevoke: { + title: "Отозвать этот токен?", + body: "Все устройства, использующие «{{name}}», немедленно потеряют доступ. Отменить это действие нельзя.", + cancel: "Отмена", + confirm: "Отозвать", + }, + create: { + scopeLabel: "Область действия", + hintCameraStream: "Токен потока камеры позволяет получать только видеопотоки и снимки. Используйте его для Home Assistant, Frigate и других систем, встраивающих изображение с одной камеры.", + hintCamWall: "Токен стены камер открывает страницу /camwall на экране без авторизации. Он позволяет видеть названия и состояния всех принтеров, а также видеопотоки с их камер. Имена файлов, адреса и коды доступа недоступны.", + title: "Создать токен", + nameLabel: "Название токена", + namePlaceholder: "Например, Home Assistant", + daysLabel: "Срок действия, дней", + submit: "Создать", + hint: "Максимальный срок действия — 365 дней. Значение токена показывается только один раз при создании — скопируйте его сейчас.", + }, + created: { + camWallUrlTitle: "URL стены камер для этого экрана", + camWallUrlHint: "Откройте этот адрес на экране. Любой, кто получит URL, сможет наблюдать за стеной камер, поэтому обращайтесь с ним как с ключом. Отзовите токен, чтобы отключить экран.", + title: "Токен создан — скопируйте его сейчас", + warning: "Это единственный раз, когда токен отображается. После закрытия окна посмотреть его снова будет невозможно.", + copy: "Копировать", + dismiss: "Я сохранил токен", + }, + list: { + scope: "Область действия", + myTitle: "Мои токены", + allTitle: "Все пользователи (режим администратора)", + empty: "Токенов пока нет.", + name: "Название", + owner: "Владелец", + prefix: "Префикс", + created: "Создан", + expires: "Истекает", + lastUsed: "Последнее использование", + revoke: "Отозвать", + expired: "Истёк", + }, + toast: { + created: "Токен создан", + createFailed: "Не удалось создать токен", + revoked: "Токен отозван", + revokeFailed: "Не удалось отозвать токен", + loadFailed: "Не удалось загрузить токены", + copied: "Скопировано в буфер обмена", + copyFailed: "Не удалось скопировать — выделите и скопируйте вручную", + }, + }, + forecast: { + title: "Прогноз", + noSpools: "Активные катушки не найдены. Добавьте катушки в учёт, чтобы увидеть прогноз.", + noUsageData: "Нет данных о расходе — невозможно спрогнозировать остатки.", + sku: "Артикул", + material: "Материал", + stock: "Запас", + dailyRate: "Расход", + daysLeft: "Осталось дней", + emptyBy: "Закончится к", + reorderBy: "Заказать до", + actions: "Действия", + trend: "Тренд", + estimated: "Оценка", + noData: "Нет данных", + timeframe: "Период", + chartTitle: "Прогноз запаса — 5 основных материалов", + dashedLinesROP: "Пунктирные линии — точки заказа", + stockLevel: "Уровень запаса", + reorderPoint: "Точка заказа", + safetyMargin: "Страховой запас", + trendLegend: "Тренд (по истории, уровень обслуживания 95%)", + estimatedLegend: "Оценка (по изменению массы)", + noDataLegend: "Нет данных", + ropLabel: "ТЗ", + ssLabel: "СЗ", + safetyStockLegend: "Страховой запас", + stockArrivalLegend: "Поступление", + stockoutLegend: "Исчерпание запаса", + alertCount_one: "{{count}} предупреждение", + alertCount_other: "{{count}} предупреждений", + order: "Заказать", + save: "Сохранить", + cancel: "Отмена", + settingsSaved: "Настройки сохранены", + failedSaveSettings: "Не удалось сохранить настройки", + globalLeadTimeSaved: "Общий срок поставки сохранён", + globalLeadTime: "Общий срок поставки", + globalLeadTimeHint: "Минимальный общий срок поставки, используемый при расчёте точки заказа для всех артикулов", + skuLeadTimeOverride: "Переопределение срока поставки", + skuLeadTimeHint: "0 — использовать общий срок поставки. Значение больше 0 переопределяет его для этого артикула.", + safetyMarginLabel: "Страховой запас", + effectiveLeadTime: "Фактический срок поставки", + effectiveLeadTimeHint: "макс. (общий {{global}} дн., для артикула {{sku}} дн.)", + reorderPointHint: "средний расход × срок поставки + страховой запас — заказывайте, когда остаток достигнет этого уровня", + safetyMarginHint: "Статистический страховой запас (z=1,65 × σ × √LT) плюс заданный пользователем резерв", + safetyMarginHintDays: "Резерв поверх статистического страхового запаса.{{approx}}", + safetyMarginHintDaysApprox: " ≈ {{g}} г при текущем расходе.", + safetyMarginHintG: "Фиксированный весовой резерв поверх статистического страхового запаса.{{approx}}", + safetyMarginHintGApprox: " ≈ {{days}} дн. при текущем расходе.", + individualSpools: "Отдельные катушки", + labelWeight: "Метка", + spoolCount_one: "{{count}} катушка", + spoolCount_other: "{{count}} катушек", + stockBreakRisk: "Риск исчерпания запаса", + stockBreakDetail: "Осталось {{days}} дн., срок поставки {{lt}} дн.", + stockBreakBefore: "Запас закончится до пополнения", + reorderNow: "Заказать сейчас", + reorderTriggerPassed: "Дата заказа {{date}} уже прошла.", + shoppingList: "Список покупок", + shoppingListItems_one: "({{count}} позиция)", + shoppingListItems_other: "({{count}} позиций)", + shoppingListEmpty: "Список покупок пуст. Нажмите значок корзины в нужной строке, чтобы добавить позицию.", + addToCart: "Добавить в список покупок", + alertsSnoozed: "Отключить предупреждения для артикула", + alertsEnabled: "Включить предупреждения для артикула", + addedToCart: "Добавлено в список покупок", + failedAddItem: "Не удалось добавить позицию", + listView: "Список", + logisticsView: "Логистика", + qty: "Кол-во", + weight: "Масса", + leadTime: "Срок поставки", + expectedRestock: "Ожидаемое пополнение", + status: "Состояние", + note: "Примечание", + pending: "Ожидается", + purchased: "Куплено", + received: "Получено", + markPurchased: "Отметить как купленное", + markReceived: "Отметить как полученное — катушки будут добавлены в запас", + resetToPending: "Вернуть в ожидание", + remove: "Удалить", + clearAll: "Очистить всё", + downloadCsv: "CSV", + addToCartTitle: "Добавить в список покупок", + byQuantity: "По количеству", + byDuration: "По сроку", + numberOfSpools: "Количество катушек", + lastHowManyDays: "На сколько дней должно хватить?", + noUsageQty: "Нет данных о расходе — количество установлено равным 1.", + noteOptional: "Примечание (необязательно)", + notePlaceholder: "например, для проекта X, срочно…", + addNSpools_one: "Добавить {{count}} катушку", + addNSpools_other: "Добавить {{count}} катушек", + onArrival: "После поступления", + stockBreakIn: "Запас закончится через {{days}} дн.", + stockRunsOutBefore: "Запас закончится раньше, чем истекут {{lt}} дн. срока поставки.", + atRate: "При расходе {{rate}} г/день требуется", + moreSpools_one: "ещё {{count}} катушка", + moreSpools_other: "ещё {{count}} катушек", + bridgeGap: "чтобы перекрыть дефицит.", + noReadAccess: "У вас нет прав на просмотр прогноза запасов.", + noWriteAccess: "У вас нет прав на изменение настроек прогноза.", }, };