diff --git a/frontend/src/app.css b/frontend/src/app.css index c903dd5..f1c1ca1 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -31,8 +31,8 @@ --color-glow-strong: rgba(13, 148, 136, 0.3); --color-sidebar: #ffffff; --color-sidebar-active: rgba(13, 148, 136, 0.08); - --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - --font-mono: ui-monospace, 'Cascadia Code', 'Consolas', monospace; + --font-sans: 'DM Sans', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + --font-mono: 'JetBrains Mono', ui-monospace, 'Cascadia Code', 'Consolas', monospace; --radius: 0.625rem; } diff --git a/frontend/src/lib/components/ConfirmModal.svelte b/frontend/src/lib/components/ConfirmModal.svelte new file mode 100644 index 0000000..f501e07 --- /dev/null +++ b/frontend/src/lib/components/ConfirmModal.svelte @@ -0,0 +1,73 @@ + + + +
+
+ +
+

{message}

+
+
+ + +
+
+ + diff --git a/frontend/src/lib/components/JinjaEditor.svelte b/frontend/src/lib/components/JinjaEditor.svelte new file mode 100644 index 0000000..a65eb05 --- /dev/null +++ b/frontend/src/lib/components/JinjaEditor.svelte @@ -0,0 +1,162 @@ + + +
diff --git a/frontend/src/lib/i18n/en.json b/frontend/src/lib/i18n/en.json new file mode 100644 index 0000000..116387d --- /dev/null +++ b/frontend/src/lib/i18n/en.json @@ -0,0 +1,448 @@ +{ + "app": { + "name": "Notify Bridge", + "tagline": "Service notifications" + }, + "nav": { + "dashboard": "Dashboard", + "providers": "Providers", + "trackers": "Trackers", + "trackingConfigs": "Tracking", + "templateConfigs": "Templates", + "telegramBots": "Bots", + "targets": "Targets", + "users": "Users", + "logout": "Logout" + }, + "auth": { + "signIn": "Sign in", + "signInTitle": "Sign in to your account", + "signingIn": "Signing in...", + "username": "Username", + "password": "Password", + "confirmPassword": "Confirm password", + "setupTitle": "Welcome", + "setupDescription": "Create your admin account to get started", + "createAccount": "Create account", + "creatingAccount": "Creating account...", + "passwordMismatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 6 characters", + "or": "or" + }, + "dashboard": { + "title": "Dashboard", + "description": "Overview of your Notify Bridge setup", + "providers": "Providers", + "activeTrackers": "Active Trackers", + "targets": "Targets", + "recentEvents": "Recent Events", + "noEvents": "No events yet. Create a tracker to start monitoring.", + "loading": "Loading...", + "justNow": "just now", + "minutesAgo": "{n}m ago", + "hoursAgo": "{n}h ago", + "daysAgo": "{n}d ago", + "assetsAdded": "assets added", + "assetsRemoved": "assets removed", + "collectionRenamed": "collection renamed", + "collectionDeleted": "collection deleted", + "sharingChanged": "sharing changed" + }, + "providers": { + "title": "Providers", + "description": "Manage service provider connections", + "addProvider": "Add Provider", + "cancel": "Cancel", + "type": "Provider Type", + "name": "Name", + "url": "Provider URL", + "urlPlaceholder": "http://provider:2283", + "apiKey": "API Key", + "apiKeyKeep": "API Key (leave empty to keep current)", + "connecting": "Connecting...", + "noProviders": "No providers configured yet.", + "delete": "Delete", + "confirmDelete": "Delete this provider?", + "online": "Online", + "offline": "Offline", + "checking": "Checking...", + "loadError": "Failed to load providers.", + "externalDomain": "External Domain", + "optional": "optional" + }, + "trackers": { + "title": "Trackers", + "description": "Monitor albums for changes", + "newTracker": "New Tracker", + "cancel": "Cancel", + "name": "Name", + "namePlaceholder": "Family photos tracker", + "server": "Provider", + "selectServer": "Select provider...", + "albums": "Albums", + "eventTypes": "Event Types", + "notificationTargets": "Notification Targets", + "scanInterval": "Scan Interval (seconds)", + "createTracker": "Create Tracker", + "noTrackers": "No trackers yet. Add a provider first, then create a tracker.", + "active": "Active", + "paused": "Paused", + "pause": "Pause", + "resume": "Resume", + "delete": "Delete", + "confirmDelete": "Delete this tracker?", + "albums_count": "album(s)", + "every": "every", + "trackImages": "Track images", + "trackVideos": "Track videos", + "favoritesOnly": "Favorites only", + "includePeople": "Include people in notifications", + "includeAssetDetails": "Include asset details", + "maxAssetsToShow": "Max assets to show", + "sortBy": "Sort by", + "sortOrder": "Sort order", + "sortNone": "Original order", + "sortDate": "Date", + "sortRating": "Rating", + "sortName": "Name", + "sortRandom": "Random", + "ascending": "Ascending", + "descending": "Descending", + "quietHoursStart": "Quiet hours start", + "quietHoursEnd": "Quiet hours end", + "batchDuration": "Batch duration (seconds)", + "linkedTargets": "targets", + "noLinkedTargets": "No targets linked. Add a target below.", + "addTarget": "Add target" + }, + "templates": { + "title": "Templates", + "description": "Jinja2 message templates for notifications", + "newTemplate": "New Template", + "cancel": "Cancel", + "name": "Name", + "body": "Template Body (Jinja2)", + "variables": "Variables", + "preview": "Preview", + "edit": "Edit", + "delete": "Delete", + "confirmDelete": "Delete this template?", + "create": "Create Template", + "update": "Update Template", + "noTemplates": "No templates yet. A default template will be used if none is configured.", + "eventType": "Event type", + "allEvents": "All events", + "assetsAdded": "Assets added", + "assetsRemoved": "Assets removed", + "albumRenamed": "Album renamed", + "albumDeleted": "Album deleted" + }, + "targets": { + "title": "Targets", + "description": "Notification destinations (Telegram, webhooks)", + "addTarget": "Add Target", + "cancel": "Cancel", + "type": "Type", + "name": "Name", + "namePlaceholder": "My notifications", + "botToken": "Bot Token", + "chatId": "Chat ID", + "webhookUrl": "Webhook URL", + "create": "Add Target", + "test": "Test", + "delete": "Delete", + "confirmDelete": "Delete this target?", + "noTargets": "No notification targets configured yet.", + "testSent": "Test sent successfully!", + "aiCaptions": "Enable AI captions", + "telegramSettings": "Telegram Settings", + "maxMedia": "Max media to send", + "maxGroupSize": "Max group size", + "chunkDelay": "Delay between groups (ms)", + "maxAssetSize": "Max asset size (MB)", + "videoWarning": "Video size warning", + "disableUrlPreview": "Disable link previews", + "sendLargeAsDocuments": "Send large photos as documents" + }, + "users": { + "title": "Users", + "description": "Manage user accounts (admin only)", + "addUser": "Add User", + "cancel": "Cancel", + "username": "Username", + "password": "Password", + "role": "Role", + "roleUser": "User", + "roleAdmin": "Admin", + "create": "Create User", + "delete": "Delete", + "confirmDelete": "Delete this user?", + "joined": "joined" + }, + "telegramBot": { + "title": "Telegram Bots", + "description": "Register and manage Telegram bots", + "addBot": "Add Bot", + "name": "Display name", + "namePlaceholder": "Family notifications bot", + "token": "Bot Token", + "tokenPlaceholder": "123456:ABC-DEF...", + "noBots": "No bots registered yet.", + "chats": "Chats", + "noChats": "No chats found. Send a message to the bot first.", + "refreshChats": "Refresh", + "selectBot": "Select bot", + "selectChat": "Select chat", + "private": "Private", + "group": "Group", + "supergroup": "Supergroup", + "channel": "Channel", + "confirmDelete": "Delete this bot?", + "commands": "Commands", + "enabledCommands": "Enabled Commands", + "defaultCount": "Default result count", + "responseMode": "Response mode", + "modeMedia": "Media (send photos)", + "modeText": "Text (send links)", + "botLocale": "Bot language", + "rateLimits": "Rate Limits", + "rateSearch": "Search cooldown", + "rateFind": "Find cooldown", + "rateDefault": "Default cooldown", + "syncCommands": "Sync to Telegram", + "discoverChats": "Discover chats from Telegram", + "clickToCopy": "Click to copy chat ID", + "chatsDiscovered": "Chats discovered", + "chatDeleted": "Chat removed" + }, + "trackingConfig": { + "title": "Tracking Configs", + "description": "Define what events and assets to react to", + "newConfig": "New Config", + "name": "Name", + "namePlaceholder": "Default tracking", + "noConfigs": "No tracking configs yet.", + "eventTracking": "Event Tracking", + "assetsAdded": "Assets added", + "assetsRemoved": "Assets removed", + "albumRenamed": "Album renamed", + "albumDeleted": "Album deleted", + "sharingChanged": "Sharing changed", + "trackImages": "Track images", + "trackVideos": "Track videos", + "favoritesOnly": "Favorites only", + "assetDisplay": "Asset Display", + "includePeople": "Include people", + "includeDetails": "Include asset details", + "maxAssets": "Max assets to show", + "sortBy": "Sort by", + "sortOrder": "Sort order", + "periodicSummary": "Periodic Summary", + "enabled": "Enabled", + "intervalDays": "Interval (days)", + "startDate": "Start date", + "times": "Times (HH:MM)", + "scheduledAssets": "Scheduled Assets", + "albumMode": "Album mode", + "limit": "Limit", + "assetType": "Asset type", + "minRating": "Min rating", + "memoryMode": "Memory Mode (On This Day)", + "test": "Test", + "confirmDelete": "Delete this tracking config?", + "sortNone": "None", + "sortDate": "Date", + "sortRating": "Rating", + "sortName": "Name", + "orderDesc": "Descending", + "orderAsc": "Ascending", + "albumModePerAlbum": "Per album", + "albumModeCombined": "Combined", + "albumModeRandom": "Random", + "assetTypeAll": "All", + "assetTypePhoto": "Photo", + "assetTypeVideo": "Video" + }, + "templateConfig": { + "title": "Template Configs", + "description": "Define how notification messages are formatted", + "newConfig": "New Config", + "name": "Name", + "namePlaceholder": "Default EN", + "descriptionPlaceholder": "e.g. English templates for family notifications", + "noConfigs": "No template configs yet.", + "eventMessages": "Event Messages", + "assetsAdded": "Assets added", + "assetsRemoved": "Assets removed", + "albumRenamed": "Album renamed", + "albumDeleted": "Album deleted", + "sharingChanged": "Sharing changed", + "assetFormatting": "Asset Formatting", + "imageTemplate": "Image item", + "videoTemplate": "Video item", + "assetsWrapper": "Assets wrapper", + "moreMessage": "More message", + "peopleFormat": "People format", + "dateLocation": "Date & Location", + "dateFormat": "Date format", + "commonDate": "Common date", + "uniqueDate": "Per-asset date", + "locationFormat": "Location format", + "commonLocation": "Common location", + "uniqueLocation": "Per-asset location", + "favoriteIndicator": "Favorite indicator", + "scheduledMessages": "Scheduled Messages", + "periodicSummary": "Periodic summary", + "periodicAlbum": "Per-album item", + "scheduledAssets": "Scheduled assets", + "memoryMode": "Memory mode", + "settings": "Settings", + "previewAs": "Preview as", + "preview": "Preview", + "variables": "Variables", + "assetFields": "Asset fields (in {% for asset in added_assets %})", + "albumFields": "Album fields (in {% for album in albums %})", + "confirmDelete": "Delete this template config?" + }, + "templateVars": { + "message_assets_added": { "description": "Notification when new assets are added to an album" }, + "message_assets_removed": { "description": "Notification when assets are removed from an album" }, + "message_album_renamed": { "description": "Notification when an album is renamed" }, + "message_album_deleted": { "description": "Notification when an album is deleted" }, + "periodic_summary_message": { "description": "Periodic album summary (scheduler not yet implemented)" }, + "scheduled_assets_message": { "description": "Scheduled asset delivery (scheduler not yet implemented)" }, + "memory_mode_message": { "description": "\"On This Day\" memories (scheduler not yet implemented)" }, + "album_id": "Album ID (UUID)", + "album_name": "Album name", + "album_url": "Public share URL (empty if not shared)", + "added_count": "Number of assets added", + "removed_count": "Number of assets removed", + "change_type": "Type of change (assets_added, assets_removed, album_renamed, album_deleted)", + "people": "Detected people names (list, use {{ people | join(', ') }})", + "added_assets": "List of asset dicts (use {% for asset in added_assets %})", + "removed_assets": "List of removed asset IDs (strings)", + "shared": "Whether album is shared (boolean)", + "target_type": "Target type: 'telegram' or 'webhook'", + "has_videos": "Whether added assets contain videos (boolean)", + "has_photos": "Whether added assets contain photos (boolean)", + "old_name": "Previous album name (rename events)", + "new_name": "New album name (rename events)", + "old_shared": "Was album shared before rename (boolean)", + "new_shared": "Is album shared after rename (boolean)", + "albums": "List of album dicts (use {% for album in albums %})", + "assets": "List of asset dicts (use {% for asset in assets %})", + "date": "Current date string", + "asset_id": "Asset ID (UUID)", + "asset_filename": "Original filename", + "asset_type": "IMAGE or VIDEO", + "asset_created_at": "Creation date/time (ISO 8601)", + "asset_owner": "Owner display name", + "asset_owner_id": "Owner user ID", + "asset_description": "User or EXIF description", + "asset_people": "People detected in this asset (list)", + "asset_is_favorite": "Whether asset is favorited (boolean)", + "asset_rating": "Star rating (1-5 or null)", + "asset_latitude": "GPS latitude (float or null)", + "asset_longitude": "GPS longitude (float or null)", + "asset_city": "City name", + "asset_state": "State/region name", + "asset_country": "Country name", + "asset_url": "Public viewer URL (if shared)", + "asset_download_url": "Direct download URL (if shared)", + "asset_photo_url": "Preview image URL (images only, if shared)", + "asset_playback_url": "Video playback URL (videos only, if shared)", + "album_name_field": "Album name (in album list)", + "album_asset_count": "Total assets in album", + "album_url_field": "Album share URL", + "album_shared": "Whether album is shared" + }, + "hints": { + "periodicSummary": "Sends a scheduled summary of all tracked albums at specified times. Great for daily/weekly digests.", + "scheduledAssets": "Sends random or selected photos from tracked albums on a schedule. Like a daily photo pick.", + "memoryMode": "\"On This Day\" — sends photos taken on this date in previous years. Nostalgic flashbacks.", + "favoritesOnly": "Only include assets marked as favorites.", + "maxAssets": "Maximum number of asset details to include in a single notification message.", + "periodicStartDate": "The reference date for calculating periodic intervals. Summaries are sent every N days from this date.", + "times": "Time(s) of day to send notifications, in HH:MM format. Use commas for multiple times: 09:00,18:00", + "albumMode": "Per album: separate notification per album. Combined: one notification with all albums. Random: pick one album randomly.", + "minRating": "Only include assets with at least this star rating (0 = no filter).", + "eventMessages": "Templates for real-time event notifications. Use {variables} for dynamic content.", + "assetFormatting": "How individual assets are formatted within notification messages.", + "dateLocation": "Date and location formatting in notifications. Uses strftime syntax for dates.", + "scheduledMessages": "Templates for periodic summaries, scheduled photo picks, and On This Day memories.", + "aiCaptions": "Use Claude AI to generate a natural-language caption for notifications instead of the template.", + "maxMedia": "Maximum number of photos/videos to attach per notification (0 = text only).", + "groupSize": "Telegram media groups can contain 2-10 items. Larger batches are split into chunks.", + "chunkDelay": "Delay in milliseconds between sending media chunks. Prevents Telegram rate limiting.", + "maxAssetSize": "Skip assets larger than this size in MB. Telegram limits files to 50 MB.", + "trackingConfig": "Controls which events trigger notifications and how assets are filtered.", + "templateConfig": "Controls the message format. Uses default templates if not set.", + "scanInterval": "How often to poll the provider for changes, in seconds. Lower = faster detection but more API calls.", + "batchDuration": "Time to accumulate changes before dispatching notifications. 0 = send immediately.", + "defaultCount": "How many results to return when the user doesn't specify a count (1-20).", + "responseMode": "Media: send actual photos. Text: send filenames/links only. Media mode uses more bandwidth.", + "botLocale": "Language for command descriptions in Telegram's menu and bot response messages.", + "rateLimits": "Cooldown in seconds between uses of each command category per chat. 0 = no limit." + }, + "snack": { + "providerSaved": "Provider saved", + "providerDeleted": "Provider deleted", + "trackerCreated": "Tracker created", + "trackerUpdated": "Tracker updated", + "trackerDeleted": "Tracker deleted", + "trackerPaused": "Tracker paused", + "trackerResumed": "Tracker resumed", + "targetSaved": "Target saved", + "targetDeleted": "Target deleted", + "targetTestSent": "Test notification sent", + "templateSaved": "Template config saved", + "templateDeleted": "Template config deleted", + "trackingConfigSaved": "Tracking config saved", + "trackingConfigDeleted": "Tracking config deleted", + "botRegistered": "Bot registered", + "botDeleted": "Bot deleted", + "userCreated": "User created", + "userDeleted": "User deleted", + "passwordChanged": "Password changed", + "copied": "Copied to clipboard", + "genericError": "Something went wrong", + "commandsSaved": "Commands config saved", + "commandsSynced": "Commands synced to Telegram", + "targetLinked": "Target linked", + "targetUnlinked": "Target unlinked", + "botUpdated": "Bot updated" + }, + "common": { + "loading": "Loading...", + "save": "Save", + "cancel": "Cancel", + "delete": "Delete", + "edit": "Edit", + "description": "Description", + "close": "Close", + "confirm": "Confirm", + "error": "Error", + "success": "Success", + "none": "None", + "noneDefault": "None (default)", + "loadError": "Failed to load data", + "headersInvalid": "Invalid JSON", + "language": "Language", + "theme": "Theme", + "light": "Light", + "dark": "Dark", + "system": "System", + "test": "Test", + "create": "Create", + "changePassword": "Change Password", + "currentPassword": "Current password", + "newPassword": "New password", + "passwordChanged": "Password changed successfully", + "expand": "Expand", + "collapse": "Collapse", + "syntaxError": "Syntax error", + "undefinedVar": "Unknown variable", + "line": "line", + "add": "Add" + } +} diff --git a/frontend/src/lib/i18n/ru.json b/frontend/src/lib/i18n/ru.json new file mode 100644 index 0000000..332f1fe --- /dev/null +++ b/frontend/src/lib/i18n/ru.json @@ -0,0 +1,448 @@ +{ + "app": { + "name": "Notify Bridge", + "tagline": "Уведомления о сервисах" + }, + "nav": { + "dashboard": "Главная", + "providers": "Провайдеры", + "trackers": "Трекеры", + "trackingConfigs": "Отслеживание", + "templateConfigs": "Шаблоны", + "telegramBots": "Боты", + "targets": "Получатели", + "users": "Пользователи", + "logout": "Выход" + }, + "auth": { + "signIn": "Войти", + "signInTitle": "Вход в аккаунт", + "signingIn": "Вход...", + "username": "Имя пользователя", + "password": "Пароль", + "confirmPassword": "Подтвердите пароль", + "setupTitle": "Добро пожаловать", + "setupDescription": "Создайте учётную запись администратора", + "createAccount": "Создать аккаунт", + "creatingAccount": "Создание...", + "passwordMismatch": "Пароли не совпадают", + "passwordTooShort": "Пароль должен быть не менее 6 символов", + "or": "или" + }, + "dashboard": { + "title": "Главная", + "description": "Обзор настроек Notify Bridge", + "providers": "Провайдеры", + "activeTrackers": "Активные трекеры", + "targets": "Получатели", + "recentEvents": "Последние события", + "noEvents": "Событий пока нет. Создайте трекер для отслеживания.", + "loading": "Загрузка...", + "justNow": "только что", + "minutesAgo": "{n} мин назад", + "hoursAgo": "{n} ч назад", + "daysAgo": "{n} д назад", + "assetsAdded": "добавлены файлы", + "assetsRemoved": "удалены файлы", + "collectionRenamed": "альбом переименован", + "collectionDeleted": "альбом удалён", + "sharingChanged": "изменение доступа" + }, + "providers": { + "title": "Провайдеры", + "description": "Управление подключениями к сервисам", + "addProvider": "Добавить провайдер", + "cancel": "Отмена", + "type": "Тип провайдера", + "name": "Название", + "url": "URL провайдера", + "urlPlaceholder": "http://provider:2283", + "apiKey": "API ключ", + "apiKeyKeep": "API ключ (оставьте пустым, чтобы сохранить текущий)", + "connecting": "Подключение...", + "noProviders": "Провайдеры не настроены.", + "delete": "Удалить", + "confirmDelete": "Удалить этот провайдер?", + "online": "В сети", + "offline": "Не в сети", + "checking": "Проверка...", + "loadError": "Не удалось загрузить провайдеры.", + "externalDomain": "Внешний домен", + "optional": "необязательно" + }, + "trackers": { + "title": "Трекеры", + "description": "Отслеживание изменений в альбомах", + "newTracker": "Новый трекер", + "cancel": "Отмена", + "name": "Название", + "namePlaceholder": "Трекер семейных фото", + "server": "Провайдер", + "selectServer": "Выберите провайдер...", + "albums": "Альбомы", + "eventTypes": "Типы событий", + "notificationTargets": "Получатели уведомлений", + "scanInterval": "Интервал проверки (секунды)", + "createTracker": "Создать трекер", + "noTrackers": "Трекеров пока нет. Сначала добавьте провайдер, затем создайте трекер.", + "active": "Активен", + "paused": "Приостановлен", + "pause": "Пауза", + "resume": "Возобновить", + "delete": "Удалить", + "confirmDelete": "Удалить этот трекер?", + "albums_count": "альбом(ов)", + "every": "каждые", + "trackImages": "Отслеживать фото", + "trackVideos": "Отслеживать видео", + "favoritesOnly": "Только избранные", + "includePeople": "Включать людей в уведомления", + "includeAssetDetails": "Включать детали файлов", + "maxAssetsToShow": "Макс. файлов в уведомлении", + "sortBy": "Сортировка", + "sortOrder": "Порядок", + "sortNone": "Исходный порядок", + "sortDate": "Дата", + "sortRating": "Рейтинг", + "sortName": "Имя", + "sortRandom": "Случайный", + "ascending": "По возрастанию", + "descending": "По убыванию", + "quietHoursStart": "Тихие часы начало", + "quietHoursEnd": "Тихие часы конец", + "batchDuration": "Длительность пакета (секунды)", + "linkedTargets": "получатели", + "noLinkedTargets": "Нет привязанных получателей. Добавьте получателя ниже.", + "addTarget": "Добавить получателя" + }, + "templates": { + "title": "Шаблоны", + "description": "Шаблоны сообщений Jinja2 для уведомлений", + "newTemplate": "Новый шаблон", + "cancel": "Отмена", + "name": "Название", + "body": "Текст шаблона (Jinja2)", + "variables": "Переменные", + "preview": "Предпросмотр", + "edit": "Редактировать", + "delete": "Удалить", + "confirmDelete": "Удалить этот шаблон?", + "create": "Создать шаблон", + "update": "Обновить шаблон", + "noTemplates": "Шаблонов пока нет. Без шаблона будет использован шаблон по умолчанию.", + "eventType": "Тип события", + "allEvents": "Все события", + "assetsAdded": "Добавлены файлы", + "assetsRemoved": "Удалены файлы", + "albumRenamed": "Альбом переименован", + "albumDeleted": "Альбом удалён" + }, + "targets": { + "title": "Получатели", + "description": "Адреса уведомлений (Telegram, вебхуки)", + "addTarget": "Добавить получателя", + "cancel": "Отмена", + "type": "Тип", + "name": "Название", + "namePlaceholder": "Мои уведомления", + "botToken": "Токен бота", + "chatId": "ID чата", + "webhookUrl": "URL вебхука", + "create": "Добавить", + "test": "Тест", + "delete": "Удалить", + "confirmDelete": "Удалить этого получателя?", + "noTargets": "Получатели уведомлений не настроены.", + "testSent": "Тестовое уведомление отправлено!", + "aiCaptions": "Включить AI подписи", + "telegramSettings": "Настройки Telegram", + "maxMedia": "Макс. медиафайлов", + "maxGroupSize": "Макс. размер группы", + "chunkDelay": "Задержка между группами (мс)", + "maxAssetSize": "Макс. размер файла (МБ)", + "videoWarning": "Предупреждение о размере видео", + "disableUrlPreview": "Отключить превью ссылок", + "sendLargeAsDocuments": "Отправлять большие фото как документы" + }, + "users": { + "title": "Пользователи", + "description": "Управление аккаунтами (только админ)", + "addUser": "Добавить пользователя", + "cancel": "Отмена", + "username": "Имя пользователя", + "password": "Пароль", + "role": "Роль", + "roleUser": "Пользователь", + "roleAdmin": "Администратор", + "create": "Создать", + "delete": "Удалить", + "confirmDelete": "Удалить этого пользователя?", + "joined": "зарегистрирован" + }, + "telegramBot": { + "title": "Telegram боты", + "description": "Регистрация и управление Telegram ботами", + "addBot": "Добавить бота", + "name": "Отображаемое имя", + "namePlaceholder": "Бот семейных уведомлений", + "token": "Токен бота", + "tokenPlaceholder": "123456:ABC-DEF...", + "noBots": "Ботов пока нет.", + "chats": "Чаты", + "noChats": "Чатов не найдено. Сначала отправьте сообщение боту.", + "refreshChats": "Обновить", + "selectBot": "Выберите бота", + "selectChat": "Выберите чат", + "private": "Личный", + "group": "Группа", + "supergroup": "Супергруппа", + "channel": "Канал", + "confirmDelete": "Удалить этого бота?", + "commands": "Команды", + "enabledCommands": "Включённые команды", + "defaultCount": "Кол-во результатов", + "responseMode": "Режим ответа", + "modeMedia": "Медиа (отправка фото)", + "modeText": "Текст (ссылки)", + "botLocale": "Язык бота", + "rateLimits": "Ограничения частоты", + "rateSearch": "Кулдаун поиска", + "rateFind": "Кулдаун поиска файлов", + "rateDefault": "Кулдаун по умолчанию", + "syncCommands": "Синхронизировать с Telegram", + "discoverChats": "Обнаружить чаты из Telegram", + "clickToCopy": "Нажмите, чтобы скопировать ID чата", + "chatsDiscovered": "Чаты обнаружены", + "chatDeleted": "Чат удалён" + }, + "trackingConfig": { + "title": "Конфигурации отслеживания", + "description": "Определите, на какие события и файлы реагировать", + "newConfig": "Новая конфигурация", + "name": "Название", + "namePlaceholder": "Основное отслеживание", + "noConfigs": "Конфигураций отслеживания пока нет.", + "eventTracking": "Отслеживание событий", + "assetsAdded": "Добавлены файлы", + "assetsRemoved": "Удалены файлы", + "albumRenamed": "Альбом переименован", + "albumDeleted": "Альбом удалён", + "sharingChanged": "Изменение доступа", + "trackImages": "Фото", + "trackVideos": "Видео", + "favoritesOnly": "Только избранные", + "assetDisplay": "Отображение файлов", + "includePeople": "Включать людей", + "includeDetails": "Включать детали", + "maxAssets": "Макс. файлов", + "sortBy": "Сортировка", + "sortOrder": "Порядок", + "periodicSummary": "Периодическая сводка", + "enabled": "Включено", + "intervalDays": "Интервал (дни)", + "startDate": "Дата начала", + "times": "Время (ЧЧ:ММ)", + "scheduledAssets": "Запланированные фото", + "albumMode": "Режим альбомов", + "limit": "Лимит", + "assetType": "Тип файлов", + "minRating": "Мин. рейтинг", + "memoryMode": "Воспоминания (В этот день)", + "test": "Тест", + "confirmDelete": "Удалить эту конфигурацию отслеживания?", + "sortNone": "Нет", + "sortDate": "Дата", + "sortRating": "Рейтинг", + "sortName": "Имя", + "orderDesc": "По убыванию", + "orderAsc": "По возрастанию", + "albumModePerAlbum": "По альбомам", + "albumModeCombined": "Объединённый", + "albumModeRandom": "Случайный", + "assetTypeAll": "Все", + "assetTypePhoto": "Фото", + "assetTypeVideo": "Видео" + }, + "templateConfig": { + "title": "Конфигурации шаблонов", + "description": "Определите формат уведомлений", + "newConfig": "Новая конфигурация", + "name": "Название", + "namePlaceholder": "По умолчанию RU", + "descriptionPlaceholder": "напр. Русские шаблоны для семейных уведомлений", + "noConfigs": "Конфигураций шаблонов пока нет.", + "eventMessages": "Сообщения о событиях", + "assetsAdded": "Добавлены файлы", + "assetsRemoved": "Удалены файлы", + "albumRenamed": "Альбом переименован", + "albumDeleted": "Альбом удалён", + "sharingChanged": "Изменение доступа", + "assetFormatting": "Форматирование файлов", + "imageTemplate": "Шаблон фото", + "videoTemplate": "Шаблон видео", + "assetsWrapper": "Обёртка списка", + "moreMessage": "Сообщение \"ещё\"", + "peopleFormat": "Формат людей", + "dateLocation": "Дата и место", + "dateFormat": "Формат даты", + "commonDate": "Общая дата", + "uniqueDate": "Дата файла", + "locationFormat": "Формат места", + "commonLocation": "Общее место", + "uniqueLocation": "Место файла", + "favoriteIndicator": "Индикатор избранного", + "scheduledMessages": "Запланированные сообщения", + "periodicSummary": "Периодическая сводка", + "periodicAlbum": "Элемент альбома", + "scheduledAssets": "Запланированные фото", + "memoryMode": "Воспоминания", + "settings": "Настройки", + "previewAs": "Предпросмотр как", + "preview": "Предпросмотр", + "variables": "Переменные", + "assetFields": "Поля файла (в {% for asset in added_assets %})", + "albumFields": "Поля альбома (в {% for album in albums %})", + "confirmDelete": "Удалить эту конфигурацию шаблона?" + }, + "templateVars": { + "message_assets_added": { "description": "Уведомление о добавлении файлов в альбом" }, + "message_assets_removed": { "description": "Уведомление об удалении файлов из альбома" }, + "message_album_renamed": { "description": "Уведомление о переименовании альбома" }, + "message_album_deleted": { "description": "Уведомление об удалении альбома" }, + "periodic_summary_message": { "description": "Периодическая сводка альбомов (планировщик не реализован)" }, + "scheduled_assets_message": { "description": "Запланированная подборка фото (планировщик не реализован)" }, + "memory_mode_message": { "description": "«В этот день» — воспоминания (планировщик не реализован)" }, + "album_id": "ID альбома (UUID)", + "album_name": "Название альбома", + "album_url": "Публичная ссылка (пусто, если не расшарен)", + "added_count": "Количество добавленных файлов", + "removed_count": "Количество удалённых файлов", + "change_type": "Тип изменения (assets_added, assets_removed, album_renamed, album_deleted)", + "people": "Обнаруженные люди (список, {{ people | join(', ') }})", + "added_assets": "Список файлов ({% for asset in added_assets %})", + "removed_assets": "Список ID удалённых файлов (строки)", + "shared": "Общий альбом (boolean)", + "target_type": "Тип получателя: 'telegram' или 'webhook'", + "has_videos": "Содержат ли добавленные файлы видео (boolean)", + "has_photos": "Содержат ли добавленные файлы фото (boolean)", + "old_name": "Прежнее название альбома (при переименовании)", + "new_name": "Новое название альбома (при переименовании)", + "old_shared": "Был ли общим до переименования (boolean)", + "new_shared": "Является ли общим после переименования (boolean)", + "albums": "Список альбомов ({% for album in albums %})", + "assets": "Список файлов ({% for asset in assets %})", + "date": "Текущая дата", + "asset_id": "ID файла (UUID)", + "asset_filename": "Имя файла", + "asset_type": "IMAGE или VIDEO", + "asset_created_at": "Дата создания (ISO 8601)", + "asset_owner": "Имя владельца", + "asset_owner_id": "ID владельца", + "asset_description": "Описание (EXIF или пользовательское)", + "asset_people": "Люди на этом файле (список)", + "asset_is_favorite": "В избранном (boolean)", + "asset_rating": "Рейтинг (1-5 или null)", + "asset_latitude": "GPS широта (float или null)", + "asset_longitude": "GPS долгота (float или null)", + "asset_city": "Город", + "asset_state": "Регион", + "asset_country": "Страна", + "asset_url": "Ссылка для просмотра (если расшарен)", + "asset_download_url": "Ссылка для скачивания (если расшарен)", + "asset_photo_url": "URL превью (только фото, если расшарен)", + "asset_playback_url": "URL видео (только видео, если расшарен)", + "album_name_field": "Название альбома (в списке альбомов)", + "album_asset_count": "Всего файлов в альбоме", + "album_url_field": "Ссылка на альбом", + "album_shared": "Общий альбом" + }, + "hints": { + "periodicSummary": "Отправляет плановую сводку по всем отслеживаемым альбомам в указанное время. Подходит для ежедневных/еженедельных дайджестов.", + "scheduledAssets": "Отправляет случайные или выбранные фото из альбомов по расписанию. Как ежедневная подборка фото.", + "memoryMode": "\"В этот день\" — отправляет фото, сделанные в этот день в прошлые годы. Ностальгические воспоминания.", + "favoritesOnly": "Включать только ассеты, отмеченные как избранные.", + "maxAssets": "Максимальное количество ассетов в одном уведомлении.", + "periodicStartDate": "Опорная дата для расчёта интервалов. Сводки отправляются каждые N дней от этой даты.", + "times": "Время отправки уведомлений в формате ЧЧ:ММ. Для нескольких значений через запятую: 09:00,18:00", + "albumMode": "По альбому: отдельное уведомление для каждого. Объединённый: одно уведомление со всеми. Случайный: выбирается один альбом.", + "minRating": "Включать только ассеты с рейтингом не ниже указанного (0 = без фильтра).", + "eventMessages": "Шаблоны уведомлений о событиях в реальном времени. Используйте {переменные} для динамического контента.", + "assetFormatting": "Форматирование отдельных ассетов в сообщениях уведомлений.", + "dateLocation": "Форматирование даты и местоположения. Использует синтаксис strftime для дат.", + "scheduledMessages": "Шаблоны для периодических сводок, подборок фото и воспоминаний «В этот день».", + "aiCaptions": "Использовать Claude AI для генерации описания уведомления вместо шаблона.", + "maxMedia": "Максимальное количество фото/видео в одном уведомлении (0 = только текст).", + "groupSize": "Медиагруппы Telegram содержат 2-10 элементов. Большие пакеты разбиваются на части.", + "chunkDelay": "Задержка в миллисекундах между отправкой порций медиа. Предотвращает ограничение Telegram.", + "maxAssetSize": "Пропускать файлы больше указанного размера в МБ. Лимит Telegram — 50 МБ.", + "trackingConfig": "Управляет тем, какие события вызывают уведомления и как фильтруются ассеты.", + "templateConfig": "Управляет форматом сообщений. Используются шаблоны по умолчанию, если не задано.", + "scanInterval": "Как часто опрашивать провайдер на предмет изменений (в секундах). Меньше = быстрее обнаружение, но больше запросов к API.", + "batchDuration": "Время накопления изменений перед отправкой уведомлений. 0 = отправлять сразу.", + "defaultCount": "Сколько результатов возвращать, если пользователь не указал количество (1-20).", + "responseMode": "Медиа: отправка фото. Текст: только имена файлов/ссылки. Медиа-режим использует больше трафика.", + "botLocale": "Язык описаний команд в меню Telegram и ответов бота.", + "rateLimits": "Кулдаун в секундах между использованиями команд в каждом чате. 0 = без ограничений." + }, + "snack": { + "providerSaved": "Провайдер сохранён", + "providerDeleted": "Провайдер удалён", + "trackerCreated": "Трекер создан", + "trackerUpdated": "Трекер обновлён", + "trackerDeleted": "Трекер удалён", + "trackerPaused": "Трекер приостановлен", + "trackerResumed": "Трекер возобновлён", + "targetSaved": "Цель сохранена", + "targetDeleted": "Цель удалена", + "targetTestSent": "Тестовое уведомление отправлено", + "templateSaved": "Шаблон сохранён", + "templateDeleted": "Шаблон удалён", + "trackingConfigSaved": "Конфигурация сохранена", + "trackingConfigDeleted": "Конфигурация удалена", + "botRegistered": "Бот зарегистрирован", + "botDeleted": "Бот удалён", + "userCreated": "Пользователь создан", + "userDeleted": "Пользователь удалён", + "passwordChanged": "Пароль изменён", + "copied": "Скопировано", + "genericError": "Что-то пошло не так", + "commandsSaved": "Конфигурация команд сохранена", + "commandsSynced": "Команды синхронизированы с Telegram", + "targetLinked": "Получатель привязан", + "targetUnlinked": "Получатель отвязан", + "botUpdated": "Бот обновлён" + }, + "common": { + "loading": "Загрузка...", + "save": "Сохранить", + "cancel": "Отмена", + "delete": "Удалить", + "edit": "Редактировать", + "description": "Описание", + "close": "Закрыть", + "confirm": "Подтвердить", + "error": "Ошибка", + "success": "Успешно", + "none": "Нет", + "noneDefault": "Нет (по умолчанию)", + "loadError": "Не удалось загрузить данные", + "headersInvalid": "Невалидный JSON", + "language": "Язык", + "theme": "Тема", + "light": "Светлая", + "dark": "Тёмная", + "system": "Системная", + "test": "Тест", + "create": "Создать", + "changePassword": "Сменить пароль", + "currentPassword": "Текущий пароль", + "newPassword": "Новый пароль", + "passwordChanged": "Пароль успешно изменён", + "expand": "Развернуть", + "collapse": "Свернуть", + "syntaxError": "Ошибка синтаксиса", + "undefinedVar": "Неизвестная переменная", + "line": "строка", + "add": "Добавить" + } +} diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte index 22b9798..396bced 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -229,13 +229,13 @@