Security: - Replace inline onclick handlers with data-attribute event delegation (XSS fix) - Remove auth tokens from URL query params; use Authorization header + blob URLs - Defer artwork blob URL revocation to prevent ERR_FILE_NOT_FOUND Reliability: - Merge duplicate DOMContentLoaded listeners - WebSocket exponential backoff reconnect (3s base, 30s max, 20 attempts) - Connection banner with manual reconnect button after failures UX: - Toast notifications now stack (multiple visible simultaneously) - Custom styled confirm dialog replacing native confirm() - Drag-to-seek on progress bars (mouse + touch) - Keyboard shortcuts: Space, arrows, M for media controls - Browser search matches both filename and title - Path separator auto-detection (Unix/Windows) Accessibility: - WAI-ARIA Tabs pattern (tablist, tab, tabpanel roles) - Arrow/Home/End keyboard navigation in tab bar - ARIA slider roles on progress bars with live value updates - aria-label on volume sliders, aria-live on status dot Performance: - Thumbnail cache (Map, max 200 entries, LRU eviction) - Skip revocation of cached blob URLs during grid re-render - Blob URL cleanup on page unload Visual polish: - Vinyl mode uses CSS custom properties (works in light + dark themes) - Light theme shadow overrides for containers, dialogs, toasts - Optimized system font stack Code quality: - Scoped button reset, merged duplicate CSS selectors - WCAG AA contrast fix for --text-muted - Normalized CSS to consistent 4-space indentation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
166 lines
11 KiB
JSON
166 lines
11 KiB
JSON
{
|
||
"app.title": "Медиа Сервер",
|
||
"auth.message": "Введите API токен для подключения к медиа серверу.",
|
||
"auth.placeholder": "Введите API токен",
|
||
"auth.connect": "Подключиться",
|
||
"auth.help": "Чтобы получить токен, выполните:",
|
||
"auth.logout": "Выйти",
|
||
"auth.logout.title": "Очистить сохраненный токен",
|
||
"auth.invalid": "Неверный токен. Пожалуйста, попробуйте снова.",
|
||
"auth.cleared": "Токен очищен. Пожалуйста, введите новый токен.",
|
||
"auth.required": "Пожалуйста, введите токен",
|
||
"player.theme": "Переключить тему",
|
||
"player.locale": "Изменить язык",
|
||
"player.previous": "Предыдущий",
|
||
"player.play": "Воспроизвести/Пауза",
|
||
"player.next": "Следующий",
|
||
"player.mute": "Без звука",
|
||
"player.status.connected": "Подключено",
|
||
"player.status.disconnected": "Отключено",
|
||
"player.no_media": "Медиа не воспроизводится",
|
||
"player.title_unavailable": "Название недоступно",
|
||
"player.source": "Источник:",
|
||
"player.unknown_source": "Неизвестно",
|
||
"player.vinyl": "Режим винила",
|
||
"state.playing": "Воспроизведение",
|
||
"state.paused": "Пауза",
|
||
"state.stopped": "Остановлено",
|
||
"state.idle": "Ожидание",
|
||
"scripts.quick_actions": "Быстрые Действия",
|
||
"scripts.no_scripts": "Скрипты не настроены",
|
||
"scripts.management": "Управление Скриптами",
|
||
"scripts.add": "Добавить",
|
||
"scripts.table.name": "Имя",
|
||
"scripts.table.label": "Метка",
|
||
"scripts.table.command": "Команда",
|
||
"scripts.table.timeout": "Таймаут",
|
||
"scripts.table.actions": "Действия",
|
||
"scripts.empty": "Скрипты не настроены. Нажмите 'Добавить' для создания.",
|
||
"scripts.dialog.add": "Добавить Скрипт",
|
||
"scripts.dialog.edit": "Редактировать Скрипт",
|
||
"scripts.field.name": "Имя Скрипта *",
|
||
"scripts.field.label": "Метка",
|
||
"scripts.field.command": "Команда *",
|
||
"scripts.field.description": "Описание",
|
||
"scripts.field.icon": "Иконка (MDI)",
|
||
"scripts.field.timeout": "Таймаут (секунды)",
|
||
"scripts.placeholder.name": "Только буквы, цифры и подчеркивания",
|
||
"scripts.placeholder.label": "Человеко-читаемое имя",
|
||
"scripts.placeholder.command": "например, shutdown /s /t 0",
|
||
"scripts.placeholder.description": "Что делает этот скрипт?",
|
||
"scripts.placeholder.icon": "например, mdi:power",
|
||
"scripts.button.cancel": "Отмена",
|
||
"scripts.button.save": "Сохранить",
|
||
"scripts.button.edit": "Редактировать",
|
||
"scripts.button.delete": "Удалить",
|
||
"scripts.msg.executed": "{name} выполнен успешно",
|
||
"scripts.msg.execute_failed": "Не удалось выполнить {name}",
|
||
"scripts.msg.execute_error": "Ошибка выполнения {name}",
|
||
"scripts.msg.created": "Скрипт создан успешно",
|
||
"scripts.msg.updated": "Скрипт обновлен успешно",
|
||
"scripts.msg.create_failed": "Не удалось создать скрипт",
|
||
"scripts.msg.update_failed": "Не удалось обновить скрипт",
|
||
"scripts.msg.deleted": "Скрипт удален успешно",
|
||
"scripts.msg.delete_failed": "Не удалось удалить скрипт",
|
||
"scripts.msg.not_found": "Скрипт не найден",
|
||
"scripts.msg.load_failed": "Не удалось загрузить данные скрипта",
|
||
"scripts.msg.list_failed": "Не удалось загрузить скрипты",
|
||
"scripts.confirm.delete": "Вы уверены, что хотите удалить скрипт \"{name}\"?",
|
||
"scripts.execution.title": "Результат выполнения",
|
||
"scripts.execution.output": "Вывод",
|
||
"scripts.execution.error_output": "Вывод ошибок",
|
||
"scripts.execution.close": "Закрыть",
|
||
"scripts.confirm.unsaved": "У вас есть несохраненные изменения. Вы уверены, что хотите отменить их?",
|
||
"callbacks.management": "Управление Обратными Вызовами",
|
||
"callbacks.description": "Обратные вызовы - это скрипты, автоматически запускаемые при событиях управления медиа (воспроизведение, пауза, остановка и т.д.)",
|
||
"callbacks.add": "Добавить",
|
||
"callbacks.table.event": "Событие",
|
||
"callbacks.table.command": "Команда",
|
||
"callbacks.table.timeout": "Таймаут",
|
||
"callbacks.table.actions": "Действия",
|
||
"callbacks.empty": "Обратные вызовы не настроены. Нажмите 'Добавить' для создания.",
|
||
"callbacks.dialog.add": "Добавить Обратный Вызов",
|
||
"callbacks.dialog.edit": "Редактировать Обратный Вызов",
|
||
"callbacks.field.event": "Событие *",
|
||
"callbacks.field.command": "Команда *",
|
||
"callbacks.field.timeout": "Таймаут (секунды)",
|
||
"callbacks.field.workdir": "Рабочая Директория",
|
||
"callbacks.placeholder.event": "Выберите событие...",
|
||
"callbacks.placeholder.command": "например, shutdown /s /t 0",
|
||
"callbacks.placeholder.workdir": "Опционально",
|
||
"callbacks.button.cancel": "Отмена",
|
||
"callbacks.button.save": "Сохранить",
|
||
"callbacks.button.edit": "Редактировать",
|
||
"callbacks.button.delete": "Удалить",
|
||
"callbacks.event.on_play": "on_play - После успешного воспроизведения",
|
||
"callbacks.event.on_pause": "on_pause - После успешной паузы",
|
||
"callbacks.event.on_stop": "on_stop - После успешной остановки",
|
||
"callbacks.event.on_next": "on_next - После успешного перехода к следующему",
|
||
"callbacks.event.on_previous": "on_previous - После успешного перехода к предыдущему",
|
||
"callbacks.event.on_volume": "on_volume - После изменения громкости",
|
||
"callbacks.event.on_mute": "on_mute - После переключения звука",
|
||
"callbacks.event.on_seek": "on_seek - После успешной перемотки",
|
||
"callbacks.event.on_turn_on": "on_turn_on - Действие только для обратных вызовов",
|
||
"callbacks.event.on_turn_off": "on_turn_off - Действие только для обратных вызовов",
|
||
"callbacks.event.on_toggle": "on_toggle - Действие только для обратных вызовов",
|
||
"callbacks.msg.created": "Обратный вызов создан успешно",
|
||
"callbacks.msg.updated": "Обратный вызов обновлен успешно",
|
||
"callbacks.msg.create_failed": "Не удалось создать обратный вызов",
|
||
"callbacks.msg.update_failed": "Не удалось обновить обратный вызов",
|
||
"callbacks.msg.deleted": "Обратный вызов удален успешно",
|
||
"callbacks.msg.delete_failed": "Не удалось удалить обратный вызов",
|
||
"callbacks.msg.not_found": "Обратный вызов не найден",
|
||
"callbacks.msg.load_failed": "Не удалось загрузить данные обратного вызова",
|
||
"callbacks.msg.list_failed": "Не удалось загрузить обратные вызовы",
|
||
"callbacks.confirm.delete": "Вы уверены, что хотите удалить обратный вызов \"{name}\"?",
|
||
"callbacks.confirm.unsaved": "У вас есть несохраненные изменения. Вы уверены, что хотите отменить их?",
|
||
"tab.player": "Плеер",
|
||
"tab.browser": "Браузер",
|
||
"tab.quick_actions": "Действия",
|
||
"tab.scripts": "Скрипты",
|
||
"tab.callbacks": "Колбэки",
|
||
"browser.title": "Медиа Браузер",
|
||
"browser.home": "Главная",
|
||
"browser.manage_folders": "Управление папками",
|
||
"browser.select_folder": "Выберите папку...",
|
||
"browser.select_folder_option": "Выберите папку...",
|
||
"browser.no_folder_selected": "Выберите папку для просмотра медиафайлов",
|
||
"browser.no_items": "В этой папке не найдено медиафайлов",
|
||
"browser.view_grid": "Сетка",
|
||
"browser.view_compact": "Компактный вид",
|
||
"browser.view_list": "Список",
|
||
"browser.search": "Поиск...",
|
||
"browser.items_per_page": "Элементов на странице:",
|
||
"browser.page": "Страница",
|
||
"browser.previous": "Предыдущая",
|
||
"browser.next": "Следующая",
|
||
"browser.download": "Скачать",
|
||
"browser.play_success": "Воспроизведение {filename}",
|
||
"browser.play_error": "Не удалось воспроизвести файл",
|
||
"browser.play_all": "Воспроизвести все",
|
||
"browser.play_all_success": "Воспроизведение {count} файлов",
|
||
"browser.play_all_error": "Не удалось воспроизвести папку",
|
||
"browser.error_loading": "Ошибка загрузки каталога",
|
||
"browser.error_loading_folders": "Не удалось загрузить медиа папки",
|
||
"browser.manage_folders_hint": "Управление папками скоро появится! Пока редактируйте config.yaml для добавления медиа папок.",
|
||
"browser.folder_dialog.title_add": "Добавить медиа папку",
|
||
"browser.folder_dialog.title_edit": "Редактировать медиа папку",
|
||
"browser.folder_dialog.folder_id": "ID папки *",
|
||
"browser.folder_dialog.folder_id_help": "Только буквы, цифры и подчеркивание",
|
||
"browser.folder_dialog.label": "Метка *",
|
||
"browser.folder_dialog.label_help": "Отображаемое имя папки",
|
||
"browser.folder_dialog.path": "Путь *",
|
||
"browser.folder_dialog.path_help": "Абсолютный путь к медиа каталогу",
|
||
"browser.folder_dialog.enabled": "Включено",
|
||
"browser.folder_dialog.cancel": "Отмена",
|
||
"browser.folder_dialog.save": "Сохранить",
|
||
"browser.download_error": "Не удалось скачать файл",
|
||
"connection.reconnecting": "Соединение потеряно. Переподключение (попытка {attempt})...",
|
||
"connection.lost": "Соединение потеряно. Сервер может быть недоступен.",
|
||
"connection.reconnect": "Переподключиться",
|
||
"dialog.cancel": "Отмена",
|
||
"dialog.confirm": "Подтвердить",
|
||
"footer.created_by": "Создано",
|
||
"footer.source_code": "Исходный код"
|
||
}
|