diff --git a/frontend/js/i18n/de.js b/frontend/js/i18n/de.js
index 7b9769a..0a578f3 100644
--- a/frontend/js/i18n/de.js
+++ b/frontend/js/i18n/de.js
@@ -194,4 +194,250 @@ export default {
'content.toast.folder_deleted': 'Ordner gelöscht',
'content.toast.moved': 'Verschoben',
'content.toast.moved_to_root': 'In den Hauptordner verschoben',
+
+ // Device detail
+ 'device.back': 'Zurück zu Bildschirmen',
+ 'device.owner_label': 'Besitzer: {owner}',
+ 'device.rename': 'Umbenennen',
+ 'device.screenshot_btn': 'Screenshot',
+ 'device.remove': 'Entfernen',
+ 'device.click_to_confirm': 'Erneut klicken zum Bestätigen',
+ 'device.prompt_new_name': 'Neuen Namen eingeben:',
+ 'device.confirm_discard_draft': 'Alle nicht veröffentlichten Änderungen verwerfen und zur letzten veröffentlichten Version zurückkehren?',
+ 'device.failed_load': 'Gerät konnte nicht geladen werden',
+ 'device.no_screenshot': 'Kein Screenshot verfügbar. Klicken Sie auf „Screenshot", um einen aufzunehmen.',
+ 'device.no_content_assigned': 'Kein Inhalt zugewiesen',
+ 'device.now_playing_id': 'Wiedergabe: {id}',
+ 'device.playlist_count_one': '1 Element in der Playlist',
+ 'device.playlist_count_other': '{n} Elemente in der Playlist',
+ 'device.tab.now_playing': 'Aktuelle Wiedergabe',
+ 'device.tab.now_playing_tip': 'Live-Screenshot dessen, was aktuell auf diesem Gerät angezeigt wird.',
+ 'device.tab.playlist': 'Playlist',
+ 'device.tab.playlist_tip': 'Diesem Gerät zugewiesener Inhalt. Ziehen zum Sortieren. Medien, Widgets oder Kioskseiten hinzufügen.',
+ 'device.tab.info': 'Geräteinfos',
+ 'device.tab.info_tip': 'Hardware-Telemetrie, Ausrichtung, Notizen und Gerätesteuerung.',
+ 'device.tab.remote': 'Fernsteuerung',
+ 'device.tab.remote_tip': 'Bildschirm in Echtzeit anzeigen und Tastendrücke senden. Funktioniert mit Android-APK und Web-Player.',
+ 'device.draft.banner_title': 'Unveröffentlichte Änderungen',
+ 'device.draft.devices_showing_published': 'Geräte zeigen weiterhin die zuletzt veröffentlichte Version.',
+ 'device.draft.never_published': 'Diese Playlist wurde noch nie veröffentlicht. Geräte zeigen nichts an, bis Sie veröffentlichen.',
+ 'device.draft.discard': 'Verwerfen',
+ 'device.draft.publish': 'Veröffentlichen',
+ 'device.draft.publishing': 'Wird veröffentlicht...',
+ 'device.layout.label': 'Bildschirm-Layout',
+ 'device.layout.fullscreen_default': 'Vollbild (Standard)',
+ 'device.layout.zones_count': '{name} ({n} Zonen)',
+ 'device.layout.template_zones_count': '[Vorlage] {name} ({n} Zonen)',
+ 'device.layout.apply': 'Anwenden',
+ 'device.playlist.label': 'Playlist',
+ 'device.playlist.no_playlist': 'Keine Playlist',
+ 'device.playlist.copy_to_btn': 'Kopieren nach...',
+ 'device.playlist.add_content_btn': 'Inhalt hinzufügen',
+ 'device.playlist.empty_title': 'Kein Inhalt zugewiesen',
+ 'device.playlist.empty_desc': 'Fügen Sie Inhalt aus Ihrer Bibliothek zur Playlist dieses Bildschirms hinzu.',
+ 'device.playlist_picker.with_count': '{name} — {n} Elemente',
+ 'device.playlist_picker.with_auto': '{name} (auto) — {n} Elemente',
+ 'device.info.status': 'Status',
+ 'device.info.ip_address': 'IP-Adresse',
+ 'device.info.battery': 'Akku',
+ 'device.info.storage': 'Speicher',
+ 'device.info.size_free': '{size} frei',
+ 'device.info.player_type': 'Player-Typ',
+ 'device.info.web_player': 'Web-Player',
+ 'device.info.wifi': 'WLAN',
+ 'device.info.uptime': 'Betriebszeit',
+ 'device.info.android_version': 'Android-Version',
+ 'device.info.app_version': 'App-Version',
+ 'device.info.screen_resolution': 'Bildschirmauflösung',
+ 'device.info.ram': 'RAM',
+ 'device.info.cpu_usage': 'CPU-Auslastung',
+ 'device.timeline.title': 'Verfügbarkeit (letzte 24 Stunden)',
+ 'device.timeline.h24_ago': 'vor 24h',
+ 'device.timeline.now': 'Jetzt',
+ 'device.timeline.online': 'Online',
+ 'device.timeline.offline': 'Offline',
+ 'device.timeline.no_data': 'Keine Daten',
+ 'device.timeline.uptime_pct_tracked': '{pct}% Verfügbarkeit ({n}min erfasst)',
+ 'device.timeline.uptime_pct_no_data': '{pct}% Verfügbarkeit (keine Daten)',
+ 'device.form.orientation_label': 'Ausrichtung / Drehung',
+ 'device.form.orientation.landscape': 'Querformat (0°)',
+ 'device.form.orientation.portrait': 'Hochformat (90° im UZS)',
+ 'device.form.orientation.landscape_flipped': 'Querformat gedreht (180°)',
+ 'device.form.orientation.portrait_flipped': 'Hochformat gedreht (270° im UZS)',
+ 'device.form.default_content_label': 'Standardinhalt',
+ 'device.form.default_content_none': 'Keiner ("Warten..." anzeigen)',
+ 'device.form.notes_label': 'Notizen',
+ 'device.form.notes_placeholder': 'Standort, Setup-Details usw.',
+ 'device.form.save_settings': 'Einstellungen speichern',
+ 'device.ctl.reboot_device': 'Gerät neu starten',
+ 'device.ctl.screen_off': 'Bildschirm aus',
+ 'device.ctl.screen_on': 'Bildschirm an',
+ 'device.ctl.launch_player': 'Player starten',
+ 'device.ctl.force_update': 'Update erzwingen',
+ 'device.ctl.shutdown': 'Herunterfahren',
+ 'device.remote.start_prompt': 'Auf „Fernsteuerung starten" klicken zum Beginnen',
+ 'device.remote.start': 'Fernsteuerung starten',
+ 'device.remote.stop': 'Fernsteuerung beenden',
+ 'device.remote.vol_up': 'Vol +',
+ 'device.remote.vol_down': 'Vol -',
+ 'device.remote.home': 'Start',
+ 'device.remote.back': 'Zurück',
+ 'device.remote.recents': 'Zuletzt',
+ 'device.remote.power': 'Power',
+ 'device.remote.ok': 'OK',
+ 'device.remote.settings': 'Einstellungen',
+ 'device.remote.scrn_off': 'Bs. aus',
+ 'device.remote.scrn_on': 'Bs. an',
+ 'device.remote.enable_system_view': 'Systemansicht aktivieren',
+ 'device.remote.system_view_tooltip': 'Fordert den Gerätenutzer auf, Vollbildaufnahme zu erlauben - aktiviert Fernansicht von Startbildschirm, Einstellungen und anderen Apps',
+ 'device.remote.system_view_hint': 'Einmalige Genehmigung am Gerät erforderlich',
+ 'device.remote.waiting_for_approval': 'Warte auf Genehmigung am Gerät...',
+ 'device.remote.system_view_enabled': 'Systemansicht aktiviert',
+ 'device.remote.unlocked_hint': 'Navigation und Systemsteuerung freigeschaltet',
+ 'device.pl_item.widget_with_type': 'Widget ({type})',
+ 'device.pl_item.youtube': 'YouTube',
+ 'device.pl_item.video': 'Video',
+ 'device.pl_item.image': 'Bild',
+ 'device.pl_item.zone_label': 'Zone: {id}',
+ 'device.pl_item.no_zone': 'Keine Zone',
+ 'device.pl_item.mute': 'Stummschalten',
+ 'device.pl_item.unmute': 'Stummschaltung aufheben',
+ 'device.pl_item.remove': 'Entfernen',
+ 'device.copy.no_other_devices': 'Keine anderen Geräte zum Kopieren',
+ 'device.copy.prompt': 'Playlist auf welches Gerät kopieren?\n\n{list}\n\nNummer eingeben:',
+ 'device.copy.invalid_selection': 'Ungültige Auswahl',
+ 'device.copy.toast': '{n} Elemente nach {device} kopiert',
+ 'device.assign.empty_all': 'Noch keine Inhalte, Widgets oder Kioskseiten. Erstellen Sie zuerst etwas!',
+ 'device.assign.modal_title': 'Zur Playlist hinzufügen',
+ 'device.assign.zone_label': 'Zone',
+ 'device.assign.zone_default': 'Standard (Vollbild)',
+ 'device.assign.duration_label': 'Anzeigedauer (Sekunden, für Bilder/Widgets)',
+ 'device.assign.tab.media': 'Medien ({n})',
+ 'device.assign.tab.widgets': 'Widgets ({n})',
+ 'device.assign.tab.kiosk': 'Kiosk ({n})',
+ 'device.assign.no_media': 'Noch keine Medien hochgeladen',
+ 'device.assign.no_widgets': 'Noch keine Widgets erstellt.',
+ 'device.assign.no_kiosk': 'Noch keine Kioskseiten.',
+ 'device.assign.create_one': 'Eines erstellen',
+ 'device.assign.add_selected': 'Auswahl hinzufügen',
+ 'device.assign.select_first': 'Erst etwas auswählen',
+ 'device.assign.kiosk_widget_name': 'Kiosk: {name}',
+ 'device.toast.screenshot_requested': 'Screenshot angefordert',
+ 'device.toast.renamed': 'Bildschirm umbenannt',
+ 'device.toast.removing': 'Wird entfernt...',
+ 'device.toast.removed': 'Bildschirm entfernt',
+ 'device.toast.settings_saved': 'Einstellungen gespeichert',
+ 'device.toast.published': 'Playlist veröffentlicht — Geräte aktualisiert',
+ 'device.toast.draft_discarded': 'Entwurfsänderungen verworfen',
+ 'device.toast.playlist_changed': 'Playlist geändert',
+ 'device.toast.layout_applied': 'Layout angewendet',
+ 'device.toast.switched_to_fullscreen': 'Auf Vollbild umgestellt',
+ 'device.toast.added_to_playlist': 'Zur Playlist hinzugefügt',
+ 'device.toast.unmuted': 'Stummschaltung aufgehoben',
+ 'device.toast.muted': 'Stummgeschaltet',
+ 'device.toast.zone_updated': 'Zone aktualisiert',
+ 'device.toast.removed_from_playlist': 'Inhalt aus Playlist entfernt',
+ 'device.toast.playlist_reordered': 'Playlist neu sortiert',
+ 'device.toast.reboot_sent': 'Neustart-Befehl gesendet',
+ 'device.toast.shutdown_sent': 'Herunterfahren-Befehl gesendet',
+ 'device.toast.screen_off_sent': 'Befehl Bildschirm aus gesendet',
+ 'device.toast.screen_on_sent': 'Befehl Bildschirm an gesendet',
+ 'device.toast.launch_sent': 'Start-Befehl gesendet',
+ 'device.toast.update_triggered': 'Update-Prüfung ausgelöst',
+ 'device.toast.remote_started': 'Fernsteuerungssitzung gestartet',
+
+ // Settings
+ 'settings.title': 'Einstellungen',
+ 'settings.subtitle': 'Serverkonfiguration und Setup-Informationen',
+ 'settings.account': 'Konto',
+ 'settings.save_profile': 'Profil speichern',
+ 'settings.change_password': 'Passwort ändern',
+ 'settings.password_min_8': 'Muss mindestens 8 Zeichen lang sein.',
+ 'settings.current_password': 'Aktuelles Passwort',
+ 'settings.new_password': 'Neues Passwort',
+ 'settings.confirm_new_password': 'Neues Passwort bestätigen',
+ 'settings.sso_note': 'Sie melden sich über {provider} an. Verwalten Sie Ihr Passwort dort.',
+ 'settings.license': 'Lizenz',
+ 'settings.license_mit': 'MIT-Lizenz - alle Funktionen enthalten.',
+ 'settings.platform_admin_link': 'Plattform-Admin-Tools sind auf der Seite',
+ 'settings.platform_admin_page_suffix': '.',
+ 'settings.user_management': 'Benutzerverwaltung',
+ 'settings.loading_users': 'Benutzer werden geladen...',
+ 'settings.white_label': 'White Label / Branding',
+ 'settings.white_label_desc': 'Passen Sie das Erscheinungsbild des Dashboards und Players für Ihre Kunden an.',
+ 'settings.brand_name': 'Markenname',
+ 'settings.logo_url': 'Logo-URL',
+ 'settings.primary_color': 'Primärfarbe',
+ 'settings.bg_color': 'Hintergrundfarbe',
+ 'settings.custom_domain': 'Benutzerdefinierte Domain',
+ 'settings.favicon_url': 'Favicon-URL',
+ 'settings.custom_css': 'Benutzerdefiniertes CSS (optional)',
+ 'settings.hide_branding': '„ScreenTinker"-Branding ausblenden',
+ 'settings.save_branding': 'Branding speichern',
+ 'settings.preview': 'Vorschau',
+ 'settings.white_label_enterprise_only': 'Benutzerdefiniertes Branding ist im Enterprise-Plan verfügbar',
+ 'settings.view_plans': 'Pläne ansehen',
+ 'settings.server_info': 'Serverinformationen',
+ 'settings.server_url': 'Server-URL',
+ 'settings.api_endpoint': 'API-Endpunkt',
+ 'settings.server_url_hint': 'Verwenden Sie diese URL beim Einrichten der Android-App',
+ 'settings.setup_guide': 'Setup-Anleitung',
+ 'settings.setup_step_1': 'Installieren Sie die ScreenTinker APK auf Ihrem TV via Sideloading',
+ 'settings.setup_step_2_prefix': 'Öffnen Sie die App und geben Sie diese Server-URL ein:',
+ 'settings.setup_step_3': 'Die App zeigt einen 6-stelligen Kopplungscode an',
+ 'settings.setup_step_4': 'Klicken Sie auf „Bildschirm hinzufügen" und geben Sie den Code ein',
+ 'settings.setup_step_5': 'Inhalte in die Inhaltsbibliothek hochladen',
+ 'settings.setup_step_6': 'Inhalte der Playlist des Bildschirms zuweisen',
+ 'settings.your_data': 'Ihre Daten',
+ 'settings.your_data_desc': 'Exportieren oder importieren Sie Ihre Geräte, Inhalte, Layouts, Zeitpläne und alle Einstellungen. Nutzen Sie dies zur Migration zwischen Cloud und Selfhosted.',
+ 'settings.export_my_data': 'Meine Daten exportieren',
+ 'settings.include_media_zip': 'Mediendateien einschließen (ZIP)',
+ 'settings.import_data': 'Daten importieren',
+ 'settings.language': 'Sprache',
+ 'settings.about': 'Über',
+ 'settings.about_tagline': 'Digital-Signage-Verwaltungssystem.',
+ 'settings.third_party_licenses': 'Drittanbieter-Lizenzen',
+ 'settings.import.reading_file': 'Datei wird gelesen...',
+ 'settings.import.zip_detected': 'ZIP-Export erkannt: {name} ({size} MB)
Enthält Daten + Mediendateien.',
+ 'settings.import.confirm': 'Import bestätigen',
+ 'settings.import.invalid_file': 'Ungültige Datei. Muss ein ScreenTinker-Export-JSON oder ZIP sein.',
+ 'settings.import.summary_devices': '{n} Geräte',
+ 'settings.import.summary_content': '{n} Inhalte',
+ 'settings.import.summary_widgets': '{n} Widgets',
+ 'settings.import.summary_layouts': '{n} Layouts',
+ 'settings.import.summary_schedules': '{n} Zeitpläne',
+ 'settings.import.summary_walls': '{n} Videowände',
+ 'settings.import.summary_kiosk': '{n} Kioskseiten',
+ 'settings.import.found_summary': 'Gefunden: {summary}.
Von: {email} (exportiert {date})',
+ 'settings.import.empty_export': 'leerer Export',
+ 'settings.import.uploading_zip': 'Wird hochgeladen und importiert... Bei großen Dateien kann dies einen Moment dauern.',
+ 'settings.import.importing': 'Wird importiert...',
+ 'settings.import.complete': 'Import abgeschlossen: {imported}.',
+ 'settings.import.pairing_codes_title': 'Geräte-Kopplungscodes:',
+ 'settings.import.pairing_codes_hint': 'Geben Sie diese Codes an jedem Gerät ein, um sie neu zu verknüpfen. Zuweisungen und Zeitpläne bleiben erhalten.',
+ 'settings.import.failed': 'Import fehlgeschlagen',
+ 'settings.import.failed_with_error': 'Import fehlgeschlagen: {error}',
+ 'settings.import.read_failed': 'Datei konnte nicht gelesen werden: {error}',
+ 'settings.toast.support_token_generated': 'Support-Token erstellt (gültig {hours}h)',
+ 'settings.toast.import_success': 'Daten erfolgreich importiert',
+ 'settings.toast.name_required': 'Name darf nicht leer sein',
+ 'settings.toast.profile_saved': 'Profil gespeichert',
+ 'settings.toast.current_password_required': 'Geben Sie Ihr aktuelles Passwort ein',
+ 'settings.toast.new_password_min_8': 'Neues Passwort muss mindestens 8 Zeichen lang sein',
+ 'settings.toast.passwords_dont_match': 'Neue Passwörter stimmen nicht überein',
+ 'settings.toast.password_changed': 'Passwort geändert',
+ 'settings.toast.branding_saved': 'Branding gespeichert',
+ 'settings.toast.preview_applied': 'Vorschau angewendet (zum Zurücksetzen neu laden)',
+ 'settings.toast.plan_updated': 'Plan aktualisiert',
+ 'settings.toast.user_removed': 'Benutzer entfernt',
+ 'settings.user.col_user': 'Benutzer',
+ 'settings.user.col_auth': 'Auth',
+ 'settings.user.col_role': 'Rolle',
+ 'settings.user.col_plan': 'Plan',
+ 'settings.user.col_actions': 'Aktionen',
+ 'settings.user.remove': 'Entfernen',
+ 'settings.user.you': 'Sie',
+ 'settings.user.confirm': 'Bestätigen?',
+ 'settings.user.count_one': '1 Benutzer registriert',
+ 'settings.user.count_other': '{n} Benutzer registriert',
};
diff --git a/frontend/js/i18n/en.js b/frontend/js/i18n/en.js
index 3b1a799..dc73d72 100644
--- a/frontend/js/i18n/en.js
+++ b/frontend/js/i18n/en.js
@@ -205,4 +205,266 @@ export default {
'content.toast.folder_deleted': 'Folder deleted',
'content.toast.moved': 'Moved',
'content.toast.moved_to_root': 'Moved to root',
+
+ // Device detail
+ 'device.back': 'Back to Displays',
+ 'device.owner_label': 'Owner: {owner}',
+ 'device.rename': 'Rename',
+ 'device.screenshot_btn': 'Screenshot',
+ 'device.remove': 'Remove',
+ 'device.click_to_confirm': 'Click again to confirm',
+ 'device.prompt_new_name': 'Enter new name:',
+ 'device.confirm_discard_draft': 'Discard all unpublished changes and revert to the last published version?',
+ 'device.failed_load': 'Failed to load device',
+ 'device.no_screenshot': 'No screenshot available. Click "Screenshot" to capture one.',
+ 'device.no_content_assigned': 'No content assigned',
+ 'device.now_playing_id': 'Playing: {id}',
+ 'device.playlist_count_one': '1 item in playlist',
+ 'device.playlist_count_other': '{n} items in playlist',
+ // Tabs
+ 'device.tab.now_playing': 'Now Playing',
+ 'device.tab.now_playing_tip': "Live screenshot of what's currently displaying on this device.",
+ 'device.tab.playlist': 'Playlist',
+ 'device.tab.playlist_tip': 'Content assigned to this device. Drag items to reorder. Add media, widgets, or kiosk pages.',
+ 'device.tab.info': 'Device Info',
+ 'device.tab.info_tip': 'Hardware telemetry, orientation settings, notes, and device controls.',
+ 'device.tab.remote': 'Remote Control',
+ 'device.tab.remote_tip': 'View the device screen in real-time and send key presses. Works on Android APK and web player.',
+ // Draft banner
+ 'device.draft.banner_title': 'Unpublished changes',
+ 'device.draft.devices_showing_published': 'Devices are still showing the last published version.',
+ 'device.draft.never_published': 'This playlist has never been published. Devices will show nothing until you publish.',
+ 'device.draft.discard': 'Discard',
+ 'device.draft.publish': 'Publish',
+ 'device.draft.publishing': 'Publishing...',
+ // Layout selector
+ 'device.layout.label': 'Screen Layout',
+ 'device.layout.fullscreen_default': 'Fullscreen (default)',
+ 'device.layout.zones_count': '{name} ({n} zones)',
+ 'device.layout.template_zones_count': '[Template] {name} ({n} zones)',
+ 'device.layout.apply': 'Apply',
+ // Playlist tab
+ 'device.playlist.label': 'Playlist',
+ 'device.playlist.no_playlist': 'No playlist',
+ 'device.playlist.copy_to_btn': 'Copy To...',
+ 'device.playlist.add_content_btn': 'Add Content',
+ 'device.playlist.empty_title': 'No content assigned',
+ 'device.playlist.empty_desc': "Add content from your library to this display's playlist.",
+ 'device.playlist_picker.with_count': '{name} — {n} items',
+ 'device.playlist_picker.with_auto': '{name} (auto) — {n} items',
+ // Info cards
+ 'device.info.status': 'Status',
+ 'device.info.ip_address': 'IP Address',
+ 'device.info.battery': 'Battery',
+ 'device.info.storage': 'Storage',
+ 'device.info.size_free': '{size} free',
+ 'device.info.player_type': 'Player Type',
+ 'device.info.web_player': 'Web Player',
+ 'device.info.wifi': 'WiFi',
+ 'device.info.uptime': 'Uptime',
+ 'device.info.android_version': 'Android Version',
+ 'device.info.app_version': 'App Version',
+ 'device.info.screen_resolution': 'Screen Resolution',
+ 'device.info.ram': 'RAM',
+ 'device.info.cpu_usage': 'CPU Usage',
+ // Uptime timeline
+ 'device.timeline.title': 'Uptime Timeline (Last 24 Hours)',
+ 'device.timeline.h24_ago': '24h ago',
+ 'device.timeline.now': 'Now',
+ 'device.timeline.online': 'Online',
+ 'device.timeline.offline': 'Offline',
+ 'device.timeline.no_data': 'No data',
+ 'device.timeline.uptime_pct_tracked': '{pct}% uptime ({n}min tracked)',
+ 'device.timeline.uptime_pct_no_data': '{pct}% uptime (no data)',
+ // Form
+ 'device.form.orientation_label': 'Orientation / Rotation',
+ 'device.form.orientation.landscape': 'Landscape (0°)',
+ 'device.form.orientation.portrait': 'Portrait (90° CW)',
+ 'device.form.orientation.landscape_flipped': 'Landscape Flipped (180°)',
+ 'device.form.orientation.portrait_flipped': 'Portrait Flipped (270° CW)',
+ 'device.form.default_content_label': 'Default Content',
+ 'device.form.default_content_none': 'None (show "Waiting...")',
+ 'device.form.notes_label': 'Notes',
+ 'device.form.notes_placeholder': 'Location, setup details, etc.',
+ 'device.form.save_settings': 'Save Settings',
+ // Control buttons
+ 'device.ctl.reboot_device': 'Reboot Device',
+ 'device.ctl.screen_off': 'Screen Off',
+ 'device.ctl.screen_on': 'Screen On',
+ 'device.ctl.launch_player': 'Launch Player',
+ 'device.ctl.force_update': 'Force Update',
+ 'device.ctl.shutdown': 'Shutdown',
+ // Remote tab
+ 'device.remote.start_prompt': 'Click "Start Remote" to begin',
+ 'device.remote.start': 'Start Remote',
+ 'device.remote.stop': 'Stop Remote',
+ 'device.remote.vol_up': 'Vol +',
+ 'device.remote.vol_down': 'Vol -',
+ 'device.remote.home': 'Home',
+ 'device.remote.back': 'Back',
+ 'device.remote.recents': 'Recents',
+ 'device.remote.power': 'Power',
+ 'device.remote.ok': 'OK',
+ 'device.remote.settings': 'Settings',
+ 'device.remote.scrn_off': 'Scrn Off',
+ 'device.remote.scrn_on': 'Scrn On',
+ 'device.remote.enable_system_view': 'Enable System View',
+ 'device.remote.system_view_tooltip': 'Prompts the device user to allow full screen capture - enables remote view of home screen, settings, and other apps',
+ 'device.remote.system_view_hint': 'Requires one-time approval on device',
+ 'device.remote.waiting_for_approval': 'Waiting for device approval...',
+ 'device.remote.system_view_enabled': 'System View Enabled',
+ 'device.remote.unlocked_hint': 'Navigation and system controls unlocked',
+ // Playlist item
+ 'device.pl_item.widget_with_type': 'Widget ({type})',
+ 'device.pl_item.youtube': 'YouTube',
+ 'device.pl_item.video': 'Video',
+ 'device.pl_item.image': 'Image',
+ 'device.pl_item.zone_label': 'Zone: {id}',
+ 'device.pl_item.no_zone': 'No zone',
+ 'device.pl_item.mute': 'Mute',
+ 'device.pl_item.unmute': 'Unmute',
+ 'device.pl_item.remove': 'Remove',
+ // Copy playlist
+ 'device.copy.no_other_devices': 'No other devices to copy to',
+ 'device.copy.prompt': 'Copy playlist to which device?\n\n{list}\n\nEnter number:',
+ 'device.copy.invalid_selection': 'Invalid selection',
+ 'device.copy.toast': 'Copied {n} items to {device}',
+ // Add-content modal
+ 'device.assign.empty_all': 'No content, widgets, or kiosk pages yet. Create something first!',
+ 'device.assign.modal_title': 'Add to Playlist',
+ 'device.assign.zone_label': 'Zone',
+ 'device.assign.zone_default': 'Default (fullscreen)',
+ 'device.assign.duration_label': 'Display Duration (seconds, for images/widgets)',
+ 'device.assign.tab.media': 'Media ({n})',
+ 'device.assign.tab.widgets': 'Widgets ({n})',
+ 'device.assign.tab.kiosk': 'Kiosk ({n})',
+ 'device.assign.no_media': 'No media uploaded yet',
+ 'device.assign.no_widgets': 'No widgets created yet.',
+ 'device.assign.no_kiosk': 'No kiosk pages yet.',
+ 'device.assign.create_one': 'Create one',
+ 'device.assign.add_selected': 'Add Selected',
+ 'device.assign.select_first': 'Select something first',
+ 'device.assign.kiosk_widget_name': 'Kiosk: {name}',
+ // Toasts
+ 'device.toast.screenshot_requested': 'Screenshot requested',
+ 'device.toast.renamed': 'Display renamed',
+ 'device.toast.removing': 'Removing...',
+ 'device.toast.removed': 'Display removed',
+ 'device.toast.settings_saved': 'Settings saved',
+ 'device.toast.published': 'Playlist published — devices updated',
+ 'device.toast.draft_discarded': 'Draft changes discarded',
+ 'device.toast.playlist_changed': 'Playlist changed',
+ 'device.toast.layout_applied': 'Layout applied',
+ 'device.toast.switched_to_fullscreen': 'Switched to fullscreen',
+ 'device.toast.added_to_playlist': 'Added to playlist',
+ 'device.toast.unmuted': 'Unmuted',
+ 'device.toast.muted': 'Muted',
+ 'device.toast.zone_updated': 'Zone updated',
+ 'device.toast.removed_from_playlist': 'Content removed from playlist',
+ 'device.toast.playlist_reordered': 'Playlist reordered',
+ 'device.toast.reboot_sent': 'Reboot command sent',
+ 'device.toast.shutdown_sent': 'Shutdown command sent',
+ 'device.toast.screen_off_sent': 'Screen off command sent',
+ 'device.toast.screen_on_sent': 'Screen on command sent',
+ 'device.toast.launch_sent': 'Launch command sent',
+ 'device.toast.update_triggered': 'Update check triggered',
+ 'device.toast.remote_started': 'Remote session started',
+
+ // Settings
+ 'settings.title': 'Settings',
+ 'settings.subtitle': 'Server configuration and setup information',
+ 'settings.account': 'Account',
+ 'settings.save_profile': 'Save Profile',
+ 'settings.change_password': 'Change Password',
+ 'settings.password_min_8': 'Must be at least 8 characters.',
+ 'settings.current_password': 'Current Password',
+ 'settings.new_password': 'New Password',
+ 'settings.confirm_new_password': 'Confirm New Password',
+ 'settings.sso_note': 'You sign in via {provider}. Manage your password there.',
+ 'settings.license': 'License',
+ 'settings.license_mit': 'MIT License - all features included.',
+ 'settings.platform_admin_link': 'Platform admin tools are in the',
+ 'settings.platform_admin_page_suffix': 'page.',
+ 'settings.user_management': 'User Management',
+ 'settings.loading_users': 'Loading users...',
+ 'settings.white_label': 'White Label / Branding',
+ 'settings.white_label_desc': 'Customize the look of your dashboard and player for your clients.',
+ 'settings.brand_name': 'Brand Name',
+ 'settings.logo_url': 'Logo URL',
+ 'settings.primary_color': 'Primary Color',
+ 'settings.bg_color': 'Background Color',
+ 'settings.custom_domain': 'Custom Domain',
+ 'settings.favicon_url': 'Favicon URL',
+ 'settings.custom_css': 'Custom CSS (optional)',
+ 'settings.hide_branding': 'Hide "ScreenTinker" branding',
+ 'settings.save_branding': 'Save Branding',
+ 'settings.preview': 'Preview',
+ 'settings.white_label_enterprise_only': 'Custom branding is available on the Enterprise plan',
+ 'settings.view_plans': 'View Plans',
+ 'settings.server_info': 'Server Information',
+ 'settings.server_url': 'Server URL',
+ 'settings.api_endpoint': 'API Endpoint',
+ 'settings.server_url_hint': 'Use this URL when setting up the Android app',
+ 'settings.setup_guide': 'Setup Guide',
+ 'settings.setup_step_1': 'Install the ScreenTinker APK on your TV via sideloading',
+ 'settings.setup_step_2_prefix': 'Open the app and enter this server URL:',
+ 'settings.setup_step_3': 'The app will display a 6-digit pairing code',
+ 'settings.setup_step_4': 'Click "Add Display" on the dashboard and enter the pairing code',
+ 'settings.setup_step_5': 'Upload content in the Content Library',
+ 'settings.setup_step_6': "Assign content to the display's Playlist",
+ 'settings.your_data': 'Your Data',
+ 'settings.your_data_desc': 'Export or import your devices, content, layouts, schedules, and all settings. Use this to migrate between cloud and self-hosted instances.',
+ 'settings.export_my_data': 'Export My Data',
+ 'settings.include_media_zip': 'Include media files (ZIP)',
+ 'settings.import_data': 'Import Data',
+ 'settings.language': 'Language',
+ 'settings.about': 'About',
+ 'settings.about_tagline': 'Digital signage management system.',
+ 'settings.third_party_licenses': 'Third-Party Licenses',
+ // Import flow
+ 'settings.import.reading_file': 'Reading file...',
+ 'settings.import.zip_detected': 'ZIP export detected: {name} ({size} MB)
Contains data + media files.',
+ 'settings.import.confirm': 'Confirm Import',
+ 'settings.import.invalid_file': 'Invalid file. Must be a ScreenTinker export JSON or ZIP.',
+ 'settings.import.summary_devices': '{n} devices',
+ 'settings.import.summary_content': '{n} content items',
+ 'settings.import.summary_widgets': '{n} widgets',
+ 'settings.import.summary_layouts': '{n} layouts',
+ 'settings.import.summary_schedules': '{n} schedules',
+ 'settings.import.summary_walls': '{n} video walls',
+ 'settings.import.summary_kiosk': '{n} kiosk pages',
+ 'settings.import.found_summary': 'Found: {summary}.
From: {email} (exported {date})',
+ 'settings.import.empty_export': 'empty export',
+ 'settings.import.uploading_zip': 'Uploading and importing... This may take a moment for large files.',
+ 'settings.import.importing': 'Importing...',
+ 'settings.import.complete': 'Import complete: {imported}.',
+ 'settings.import.pairing_codes_title': 'Device Pairing Codes:',
+ 'settings.import.pairing_codes_hint': 'Enter these codes on each device to re-link them. All assignments and schedules will be preserved.',
+ 'settings.import.failed': 'Import failed',
+ 'settings.import.failed_with_error': 'Import failed: {error}',
+ 'settings.import.read_failed': 'Failed to read file: {error}',
+ // Settings toasts
+ 'settings.toast.support_token_generated': 'Support token generated (valid {hours}h)',
+ 'settings.toast.import_success': 'Data imported successfully',
+ 'settings.toast.name_required': 'Name cannot be empty',
+ 'settings.toast.profile_saved': 'Profile saved',
+ 'settings.toast.current_password_required': 'Enter your current password',
+ 'settings.toast.new_password_min_8': 'New password must be at least 8 characters',
+ 'settings.toast.passwords_dont_match': 'New passwords do not match',
+ 'settings.toast.password_changed': 'Password changed',
+ 'settings.toast.branding_saved': 'Branding saved',
+ 'settings.toast.preview_applied': 'Preview applied (refresh to reset)',
+ 'settings.toast.plan_updated': 'Plan updated',
+ 'settings.toast.user_removed': 'User removed',
+ // User management table
+ 'settings.user.col_user': 'User',
+ 'settings.user.col_auth': 'Auth',
+ 'settings.user.col_role': 'Role',
+ 'settings.user.col_plan': 'Plan',
+ 'settings.user.col_actions': 'Actions',
+ 'settings.user.remove': 'Remove',
+ 'settings.user.you': 'You',
+ 'settings.user.confirm': 'Confirm?',
+ 'settings.user.count_one': '1 user registered',
+ 'settings.user.count_other': '{n} users registered',
};
diff --git a/frontend/js/i18n/es.js b/frontend/js/i18n/es.js
index b117b4c..827007e 100644
--- a/frontend/js/i18n/es.js
+++ b/frontend/js/i18n/es.js
@@ -193,4 +193,250 @@ export default {
'content.toast.folder_deleted': 'Carpeta eliminada',
'content.toast.moved': 'Movido',
'content.toast.moved_to_root': 'Movido a la raíz',
+
+ // Device detail
+ 'device.back': 'Volver a Pantallas',
+ 'device.owner_label': 'Propietario: {owner}',
+ 'device.rename': 'Renombrar',
+ 'device.screenshot_btn': 'Captura',
+ 'device.remove': 'Eliminar',
+ 'device.click_to_confirm': 'Haz clic de nuevo para confirmar',
+ 'device.prompt_new_name': 'Ingresa el nuevo nombre:',
+ 'device.confirm_discard_draft': '¿Descartar todos los cambios no publicados y volver a la última versión publicada?',
+ 'device.failed_load': 'Error al cargar el dispositivo',
+ 'device.no_screenshot': 'No hay captura disponible. Haz clic en "Captura" para tomar una.',
+ 'device.no_content_assigned': 'Sin contenido asignado',
+ 'device.now_playing_id': 'Reproduciendo: {id}',
+ 'device.playlist_count_one': '1 elemento en la lista',
+ 'device.playlist_count_other': '{n} elementos en la lista',
+ 'device.tab.now_playing': 'Reproducción actual',
+ 'device.tab.now_playing_tip': 'Captura en vivo de lo que se muestra ahora en este dispositivo.',
+ 'device.tab.playlist': 'Lista',
+ 'device.tab.playlist_tip': 'Contenido asignado a este dispositivo. Arrastra para reordenar. Agrega medios, widgets o páginas de kiosco.',
+ 'device.tab.info': 'Información del dispositivo',
+ 'device.tab.info_tip': 'Telemetría de hardware, orientación, notas y controles del dispositivo.',
+ 'device.tab.remote': 'Control remoto',
+ 'device.tab.remote_tip': 'Visualiza la pantalla del dispositivo en tiempo real y envía pulsaciones. Funciona en la APK de Android y el reproductor web.',
+ 'device.draft.banner_title': 'Cambios sin publicar',
+ 'device.draft.devices_showing_published': 'Los dispositivos siguen mostrando la última versión publicada.',
+ 'device.draft.never_published': 'Esta lista nunca se ha publicado. Los dispositivos no mostrarán nada hasta que la publiques.',
+ 'device.draft.discard': 'Descartar',
+ 'device.draft.publish': 'Publicar',
+ 'device.draft.publishing': 'Publicando...',
+ 'device.layout.label': 'Diseño de pantalla',
+ 'device.layout.fullscreen_default': 'Pantalla completa (predeterminado)',
+ 'device.layout.zones_count': '{name} ({n} zonas)',
+ 'device.layout.template_zones_count': '[Plantilla] {name} ({n} zonas)',
+ 'device.layout.apply': 'Aplicar',
+ 'device.playlist.label': 'Lista',
+ 'device.playlist.no_playlist': 'Sin lista',
+ 'device.playlist.copy_to_btn': 'Copiar a...',
+ 'device.playlist.add_content_btn': 'Agregar contenido',
+ 'device.playlist.empty_title': 'Sin contenido asignado',
+ 'device.playlist.empty_desc': 'Agrega contenido de tu biblioteca a la lista de esta pantalla.',
+ 'device.playlist_picker.with_count': '{name} — {n} elementos',
+ 'device.playlist_picker.with_auto': '{name} (auto) — {n} elementos',
+ 'device.info.status': 'Estado',
+ 'device.info.ip_address': 'Dirección IP',
+ 'device.info.battery': 'Batería',
+ 'device.info.storage': 'Almacenamiento',
+ 'device.info.size_free': '{size} libres',
+ 'device.info.player_type': 'Tipo de reproductor',
+ 'device.info.web_player': 'Reproductor web',
+ 'device.info.wifi': 'WiFi',
+ 'device.info.uptime': 'Tiempo activo',
+ 'device.info.android_version': 'Versión de Android',
+ 'device.info.app_version': 'Versión de la app',
+ 'device.info.screen_resolution': 'Resolución de pantalla',
+ 'device.info.ram': 'RAM',
+ 'device.info.cpu_usage': 'Uso de CPU',
+ 'device.timeline.title': 'Línea de tiempo (últimas 24 horas)',
+ 'device.timeline.h24_ago': 'hace 24h',
+ 'device.timeline.now': 'Ahora',
+ 'device.timeline.online': 'En línea',
+ 'device.timeline.offline': 'Desconectado',
+ 'device.timeline.no_data': 'Sin datos',
+ 'device.timeline.uptime_pct_tracked': '{pct}% activo ({n}min registrados)',
+ 'device.timeline.uptime_pct_no_data': '{pct}% activo (sin datos)',
+ 'device.form.orientation_label': 'Orientación / Rotación',
+ 'device.form.orientation.landscape': 'Horizontal (0°)',
+ 'device.form.orientation.portrait': 'Vertical (90° SR)',
+ 'device.form.orientation.landscape_flipped': 'Horizontal invertido (180°)',
+ 'device.form.orientation.portrait_flipped': 'Vertical invertido (270° SR)',
+ 'device.form.default_content_label': 'Contenido predeterminado',
+ 'device.form.default_content_none': 'Ninguno (mostrar "Esperando...")',
+ 'device.form.notes_label': 'Notas',
+ 'device.form.notes_placeholder': 'Ubicación, detalles de instalación, etc.',
+ 'device.form.save_settings': 'Guardar configuración',
+ 'device.ctl.reboot_device': 'Reiniciar dispositivo',
+ 'device.ctl.screen_off': 'Apagar pantalla',
+ 'device.ctl.screen_on': 'Encender pantalla',
+ 'device.ctl.launch_player': 'Iniciar reproductor',
+ 'device.ctl.force_update': 'Forzar actualización',
+ 'device.ctl.shutdown': 'Apagar',
+ 'device.remote.start_prompt': 'Haz clic en "Iniciar control remoto" para comenzar',
+ 'device.remote.start': 'Iniciar control remoto',
+ 'device.remote.stop': 'Detener control remoto',
+ 'device.remote.vol_up': 'Vol +',
+ 'device.remote.vol_down': 'Vol -',
+ 'device.remote.home': 'Inicio',
+ 'device.remote.back': 'Atrás',
+ 'device.remote.recents': 'Recientes',
+ 'device.remote.power': 'Encendido',
+ 'device.remote.ok': 'OK',
+ 'device.remote.settings': 'Ajustes',
+ 'device.remote.scrn_off': 'Pant. off',
+ 'device.remote.scrn_on': 'Pant. on',
+ 'device.remote.enable_system_view': 'Habilitar vista de sistema',
+ 'device.remote.system_view_tooltip': 'Solicita al usuario del dispositivo permitir captura de pantalla completa - habilita ver pantalla de inicio, ajustes y otras apps',
+ 'device.remote.system_view_hint': 'Requiere aprobación única en el dispositivo',
+ 'device.remote.waiting_for_approval': 'Esperando aprobación del dispositivo...',
+ 'device.remote.system_view_enabled': 'Vista de sistema habilitada',
+ 'device.remote.unlocked_hint': 'Navegación y controles del sistema desbloqueados',
+ 'device.pl_item.widget_with_type': 'Widget ({type})',
+ 'device.pl_item.youtube': 'YouTube',
+ 'device.pl_item.video': 'Video',
+ 'device.pl_item.image': 'Imagen',
+ 'device.pl_item.zone_label': 'Zona: {id}',
+ 'device.pl_item.no_zone': 'Sin zona',
+ 'device.pl_item.mute': 'Silenciar',
+ 'device.pl_item.unmute': 'Activar audio',
+ 'device.pl_item.remove': 'Eliminar',
+ 'device.copy.no_other_devices': 'No hay otros dispositivos a los que copiar',
+ 'device.copy.prompt': '¿A qué dispositivo copiar la lista?\n\n{list}\n\nIngresa el número:',
+ 'device.copy.invalid_selection': 'Selección no válida',
+ 'device.copy.toast': 'Se copiaron {n} elementos a {device}',
+ 'device.assign.empty_all': 'Aún no hay contenido, widgets ni páginas de kiosco. ¡Crea algo primero!',
+ 'device.assign.modal_title': 'Agregar a la lista',
+ 'device.assign.zone_label': 'Zona',
+ 'device.assign.zone_default': 'Predeterminada (pantalla completa)',
+ 'device.assign.duration_label': 'Duración (segundos, para imágenes/widgets)',
+ 'device.assign.tab.media': 'Medios ({n})',
+ 'device.assign.tab.widgets': 'Widgets ({n})',
+ 'device.assign.tab.kiosk': 'Kiosco ({n})',
+ 'device.assign.no_media': 'Aún no se ha subido ningún medio',
+ 'device.assign.no_widgets': 'Aún no hay widgets creados.',
+ 'device.assign.no_kiosk': 'Aún no hay páginas de kiosco.',
+ 'device.assign.create_one': 'Crea uno',
+ 'device.assign.add_selected': 'Agregar selección',
+ 'device.assign.select_first': 'Primero selecciona algo',
+ 'device.assign.kiosk_widget_name': 'Kiosco: {name}',
+ 'device.toast.screenshot_requested': 'Captura solicitada',
+ 'device.toast.renamed': 'Pantalla renombrada',
+ 'device.toast.removing': 'Eliminando...',
+ 'device.toast.removed': 'Pantalla eliminada',
+ 'device.toast.settings_saved': 'Configuración guardada',
+ 'device.toast.published': 'Lista publicada — dispositivos actualizados',
+ 'device.toast.draft_discarded': 'Cambios del borrador descartados',
+ 'device.toast.playlist_changed': 'Lista cambiada',
+ 'device.toast.layout_applied': 'Diseño aplicado',
+ 'device.toast.switched_to_fullscreen': 'Cambiado a pantalla completa',
+ 'device.toast.added_to_playlist': 'Agregado a la lista',
+ 'device.toast.unmuted': 'Audio activado',
+ 'device.toast.muted': 'Silenciado',
+ 'device.toast.zone_updated': 'Zona actualizada',
+ 'device.toast.removed_from_playlist': 'Contenido eliminado de la lista',
+ 'device.toast.playlist_reordered': 'Lista reordenada',
+ 'device.toast.reboot_sent': 'Comando de reinicio enviado',
+ 'device.toast.shutdown_sent': 'Comando de apagado enviado',
+ 'device.toast.screen_off_sent': 'Comando para apagar pantalla enviado',
+ 'device.toast.screen_on_sent': 'Comando para encender pantalla enviado',
+ 'device.toast.launch_sent': 'Comando de inicio enviado',
+ 'device.toast.update_triggered': 'Verificación de actualización iniciada',
+ 'device.toast.remote_started': 'Sesión de control remoto iniciada',
+
+ // Settings
+ 'settings.title': 'Configuración',
+ 'settings.subtitle': 'Configuración del servidor e información de instalación',
+ 'settings.account': 'Cuenta',
+ 'settings.save_profile': 'Guardar perfil',
+ 'settings.change_password': 'Cambiar contraseña',
+ 'settings.password_min_8': 'Debe tener al menos 8 caracteres.',
+ 'settings.current_password': 'Contraseña actual',
+ 'settings.new_password': 'Contraseña nueva',
+ 'settings.confirm_new_password': 'Confirmar contraseña nueva',
+ 'settings.sso_note': 'Inicias sesión con {provider}. Gestiona tu contraseña ahí.',
+ 'settings.license': 'Licencia',
+ 'settings.license_mit': 'Licencia MIT - todas las funciones incluidas.',
+ 'settings.platform_admin_link': 'Las herramientas de administración están en la',
+ 'settings.platform_admin_page_suffix': '.',
+ 'settings.user_management': 'Gestión de usuarios',
+ 'settings.loading_users': 'Cargando usuarios...',
+ 'settings.white_label': 'Marca blanca / Branding',
+ 'settings.white_label_desc': 'Personaliza la apariencia del panel y el reproductor para tus clientes.',
+ 'settings.brand_name': 'Nombre de la marca',
+ 'settings.logo_url': 'URL del logotipo',
+ 'settings.primary_color': 'Color principal',
+ 'settings.bg_color': 'Color de fondo',
+ 'settings.custom_domain': 'Dominio personalizado',
+ 'settings.favicon_url': 'URL del favicon',
+ 'settings.custom_css': 'CSS personalizado (opcional)',
+ 'settings.hide_branding': 'Ocultar la marca "ScreenTinker"',
+ 'settings.save_branding': 'Guardar branding',
+ 'settings.preview': 'Previsualizar',
+ 'settings.white_label_enterprise_only': 'El branding personalizado está disponible en el plan Enterprise',
+ 'settings.view_plans': 'Ver planes',
+ 'settings.server_info': 'Información del servidor',
+ 'settings.server_url': 'URL del servidor',
+ 'settings.api_endpoint': 'Endpoint de la API',
+ 'settings.server_url_hint': 'Usa esta URL al configurar la app de Android',
+ 'settings.setup_guide': 'Guía de instalación',
+ 'settings.setup_step_1': 'Instala el APK de ScreenTinker en tu TV mediante sideloading',
+ 'settings.setup_step_2_prefix': 'Abre la app e ingresa esta URL del servidor:',
+ 'settings.setup_step_3': 'La app mostrará un código de vinculación de 6 dígitos',
+ 'settings.setup_step_4': 'Haz clic en "Agregar pantalla" en el panel e ingresa el código',
+ 'settings.setup_step_5': 'Sube contenido en la Biblioteca de contenido',
+ 'settings.setup_step_6': 'Asigna contenido a la lista de la pantalla',
+ 'settings.your_data': 'Tus datos',
+ 'settings.your_data_desc': 'Exporta o importa tus dispositivos, contenido, diseños, horarios y toda la configuración. Úsalo para migrar entre instancias en la nube y autoalojadas.',
+ 'settings.export_my_data': 'Exportar mis datos',
+ 'settings.include_media_zip': 'Incluir archivos multimedia (ZIP)',
+ 'settings.import_data': 'Importar datos',
+ 'settings.language': 'Idioma',
+ 'settings.about': 'Acerca de',
+ 'settings.about_tagline': 'Sistema de gestión de señalización digital.',
+ 'settings.third_party_licenses': 'Licencias de terceros',
+ 'settings.import.reading_file': 'Leyendo archivo...',
+ 'settings.import.zip_detected': 'Exportación ZIP detectada: {name} ({size} MB)
Contiene datos + archivos multimedia.',
+ 'settings.import.confirm': 'Confirmar importación',
+ 'settings.import.invalid_file': 'Archivo no válido. Debe ser un JSON o ZIP de exportación de ScreenTinker.',
+ 'settings.import.summary_devices': '{n} dispositivos',
+ 'settings.import.summary_content': '{n} elementos de contenido',
+ 'settings.import.summary_widgets': '{n} widgets',
+ 'settings.import.summary_layouts': '{n} diseños',
+ 'settings.import.summary_schedules': '{n} horarios',
+ 'settings.import.summary_walls': '{n} muros de video',
+ 'settings.import.summary_kiosk': '{n} páginas de kiosco',
+ 'settings.import.found_summary': 'Encontrado: {summary}.
De: {email} (exportado {date})',
+ 'settings.import.empty_export': 'exportación vacía',
+ 'settings.import.uploading_zip': 'Subiendo e importando... Esto puede tardar para archivos grandes.',
+ 'settings.import.importing': 'Importando...',
+ 'settings.import.complete': 'Importación completa: {imported}.',
+ 'settings.import.pairing_codes_title': 'Códigos de vinculación:',
+ 'settings.import.pairing_codes_hint': 'Ingresa estos códigos en cada dispositivo para volver a vincularlos. Las asignaciones y horarios se conservarán.',
+ 'settings.import.failed': 'Error al importar',
+ 'settings.import.failed_with_error': 'Error al importar: {error}',
+ 'settings.import.read_failed': 'Error al leer el archivo: {error}',
+ 'settings.toast.support_token_generated': 'Token de soporte generado (válido {hours}h)',
+ 'settings.toast.import_success': 'Datos importados correctamente',
+ 'settings.toast.name_required': 'El nombre no puede estar vacío',
+ 'settings.toast.profile_saved': 'Perfil guardado',
+ 'settings.toast.current_password_required': 'Ingresa tu contraseña actual',
+ 'settings.toast.new_password_min_8': 'La nueva contraseña debe tener al menos 8 caracteres',
+ 'settings.toast.passwords_dont_match': 'Las nuevas contraseñas no coinciden',
+ 'settings.toast.password_changed': 'Contraseña cambiada',
+ 'settings.toast.branding_saved': 'Branding guardado',
+ 'settings.toast.preview_applied': 'Previsualización aplicada (recarga para restablecer)',
+ 'settings.toast.plan_updated': 'Plan actualizado',
+ 'settings.toast.user_removed': 'Usuario eliminado',
+ 'settings.user.col_user': 'Usuario',
+ 'settings.user.col_auth': 'Auth',
+ 'settings.user.col_role': 'Rol',
+ 'settings.user.col_plan': 'Plan',
+ 'settings.user.col_actions': 'Acciones',
+ 'settings.user.remove': 'Eliminar',
+ 'settings.user.you': 'Tú',
+ 'settings.user.confirm': '¿Confirmar?',
+ 'settings.user.count_one': '1 usuario registrado',
+ 'settings.user.count_other': '{n} usuarios registrados',
};
diff --git a/frontend/js/i18n/fr.js b/frontend/js/i18n/fr.js
index ed24b1b..556a2ac 100644
--- a/frontend/js/i18n/fr.js
+++ b/frontend/js/i18n/fr.js
@@ -194,4 +194,250 @@ export default {
'content.toast.folder_deleted': 'Dossier supprimé',
'content.toast.moved': 'Déplacé',
'content.toast.moved_to_root': 'Déplacé à la racine',
+
+ // Device detail
+ 'device.back': 'Retour aux écrans',
+ 'device.owner_label': 'Propriétaire : {owner}',
+ 'device.rename': 'Renommer',
+ 'device.screenshot_btn': 'Capture',
+ 'device.remove': 'Retirer',
+ 'device.click_to_confirm': 'Cliquez à nouveau pour confirmer',
+ 'device.prompt_new_name': 'Saisissez le nouveau nom :',
+ 'device.confirm_discard_draft': 'Annuler toutes les modifications non publiées et revenir à la dernière version publiée ?',
+ 'device.failed_load': 'Échec du chargement de l\'appareil',
+ 'device.no_screenshot': 'Aucune capture disponible. Cliquez sur « Capture » pour en prendre une.',
+ 'device.no_content_assigned': 'Aucun contenu attribué',
+ 'device.now_playing_id': 'En lecture : {id}',
+ 'device.playlist_count_one': '1 élément dans la liste',
+ 'device.playlist_count_other': '{n} éléments dans la liste',
+ 'device.tab.now_playing': 'En cours de lecture',
+ 'device.tab.now_playing_tip': 'Capture en direct de ce qui s\'affiche sur cet appareil.',
+ 'device.tab.playlist': 'Liste de lecture',
+ 'device.tab.playlist_tip': 'Contenu attribué à cet appareil. Glissez pour réorganiser. Ajoutez des médias, widgets ou pages kiosque.',
+ 'device.tab.info': 'Infos appareil',
+ 'device.tab.info_tip': 'Télémétrie matérielle, orientation, notes et contrôles de l\'appareil.',
+ 'device.tab.remote': 'Contrôle à distance',
+ 'device.tab.remote_tip': 'Visualisez l\'écran en temps réel et envoyez des touches. Fonctionne sur l\'APK Android et le lecteur web.',
+ 'device.draft.banner_title': 'Modifications non publiées',
+ 'device.draft.devices_showing_published': 'Les appareils affichent encore la dernière version publiée.',
+ 'device.draft.never_published': 'Cette liste n\'a jamais été publiée. Les appareils n\'afficheront rien jusqu\'à publication.',
+ 'device.draft.discard': 'Annuler',
+ 'device.draft.publish': 'Publier',
+ 'device.draft.publishing': 'Publication...',
+ 'device.layout.label': 'Mise en page',
+ 'device.layout.fullscreen_default': 'Plein écran (par défaut)',
+ 'device.layout.zones_count': '{name} ({n} zones)',
+ 'device.layout.template_zones_count': '[Modèle] {name} ({n} zones)',
+ 'device.layout.apply': 'Appliquer',
+ 'device.playlist.label': 'Liste de lecture',
+ 'device.playlist.no_playlist': 'Aucune liste',
+ 'device.playlist.copy_to_btn': 'Copier vers...',
+ 'device.playlist.add_content_btn': 'Ajouter du contenu',
+ 'device.playlist.empty_title': 'Aucun contenu attribué',
+ 'device.playlist.empty_desc': 'Ajoutez du contenu de votre bibliothèque à la liste de cet écran.',
+ 'device.playlist_picker.with_count': '{name} — {n} éléments',
+ 'device.playlist_picker.with_auto': '{name} (auto) — {n} éléments',
+ 'device.info.status': 'Statut',
+ 'device.info.ip_address': 'Adresse IP',
+ 'device.info.battery': 'Batterie',
+ 'device.info.storage': 'Stockage',
+ 'device.info.size_free': '{size} libres',
+ 'device.info.player_type': 'Type de lecteur',
+ 'device.info.web_player': 'Lecteur web',
+ 'device.info.wifi': 'WiFi',
+ 'device.info.uptime': 'Disponibilité',
+ 'device.info.android_version': 'Version d\'Android',
+ 'device.info.app_version': 'Version de l\'app',
+ 'device.info.screen_resolution': 'Résolution',
+ 'device.info.ram': 'RAM',
+ 'device.info.cpu_usage': 'Utilisation CPU',
+ 'device.timeline.title': 'Disponibilité (24 dernières heures)',
+ 'device.timeline.h24_ago': 'il y a 24h',
+ 'device.timeline.now': 'Maintenant',
+ 'device.timeline.online': 'En ligne',
+ 'device.timeline.offline': 'Hors ligne',
+ 'device.timeline.no_data': 'Aucune donnée',
+ 'device.timeline.uptime_pct_tracked': '{pct}% disponible ({n}min suivies)',
+ 'device.timeline.uptime_pct_no_data': '{pct}% disponible (aucune donnée)',
+ 'device.form.orientation_label': 'Orientation / Rotation',
+ 'device.form.orientation.landscape': 'Paysage (0°)',
+ 'device.form.orientation.portrait': 'Portrait (90° SH)',
+ 'device.form.orientation.landscape_flipped': 'Paysage retourné (180°)',
+ 'device.form.orientation.portrait_flipped': 'Portrait retourné (270° SH)',
+ 'device.form.default_content_label': 'Contenu par défaut',
+ 'device.form.default_content_none': 'Aucun (afficher « En attente... »)',
+ 'device.form.notes_label': 'Notes',
+ 'device.form.notes_placeholder': 'Emplacement, détails d\'installation, etc.',
+ 'device.form.save_settings': 'Enregistrer les paramètres',
+ 'device.ctl.reboot_device': 'Redémarrer l\'appareil',
+ 'device.ctl.screen_off': 'Éteindre l\'écran',
+ 'device.ctl.screen_on': 'Allumer l\'écran',
+ 'device.ctl.launch_player': 'Lancer le lecteur',
+ 'device.ctl.force_update': 'Forcer la mise à jour',
+ 'device.ctl.shutdown': 'Arrêter',
+ 'device.remote.start_prompt': 'Cliquez sur « Démarrer » pour commencer',
+ 'device.remote.start': 'Démarrer',
+ 'device.remote.stop': 'Arrêter',
+ 'device.remote.vol_up': 'Vol +',
+ 'device.remote.vol_down': 'Vol -',
+ 'device.remote.home': 'Accueil',
+ 'device.remote.back': 'Retour',
+ 'device.remote.recents': 'Récents',
+ 'device.remote.power': 'Marche',
+ 'device.remote.ok': 'OK',
+ 'device.remote.settings': 'Paramètres',
+ 'device.remote.scrn_off': 'Écr. off',
+ 'device.remote.scrn_on': 'Écr. on',
+ 'device.remote.enable_system_view': 'Activer la vue système',
+ 'device.remote.system_view_tooltip': 'Demande à l\'utilisateur d\'autoriser la capture plein écran - permet de voir l\'écran d\'accueil, les paramètres et d\'autres apps',
+ 'device.remote.system_view_hint': 'Approbation unique requise sur l\'appareil',
+ 'device.remote.waiting_for_approval': 'Attente de l\'approbation de l\'appareil...',
+ 'device.remote.system_view_enabled': 'Vue système activée',
+ 'device.remote.unlocked_hint': 'Navigation et contrôles système débloqués',
+ 'device.pl_item.widget_with_type': 'Widget ({type})',
+ 'device.pl_item.youtube': 'YouTube',
+ 'device.pl_item.video': 'Vidéo',
+ 'device.pl_item.image': 'Image',
+ 'device.pl_item.zone_label': 'Zone : {id}',
+ 'device.pl_item.no_zone': 'Aucune zone',
+ 'device.pl_item.mute': 'Muet',
+ 'device.pl_item.unmute': 'Réactiver le son',
+ 'device.pl_item.remove': 'Retirer',
+ 'device.copy.no_other_devices': 'Aucun autre appareil vers lequel copier',
+ 'device.copy.prompt': 'Copier la liste vers quel appareil ?\n\n{list}\n\nSaisissez le numéro :',
+ 'device.copy.invalid_selection': 'Sélection invalide',
+ 'device.copy.toast': '{n} éléments copiés vers {device}',
+ 'device.assign.empty_all': 'Pas encore de contenu, widgets ou pages kiosque. Créez-en un d\'abord !',
+ 'device.assign.modal_title': 'Ajouter à la liste',
+ 'device.assign.zone_label': 'Zone',
+ 'device.assign.zone_default': 'Par défaut (plein écran)',
+ 'device.assign.duration_label': 'Durée (secondes, pour images/widgets)',
+ 'device.assign.tab.media': 'Médias ({n})',
+ 'device.assign.tab.widgets': 'Widgets ({n})',
+ 'device.assign.tab.kiosk': 'Kiosque ({n})',
+ 'device.assign.no_media': 'Aucun média téléversé',
+ 'device.assign.no_widgets': 'Aucun widget créé.',
+ 'device.assign.no_kiosk': 'Aucune page kiosque.',
+ 'device.assign.create_one': 'Créez-en un',
+ 'device.assign.add_selected': 'Ajouter la sélection',
+ 'device.assign.select_first': 'Sélectionnez d\'abord un élément',
+ 'device.assign.kiosk_widget_name': 'Kiosque : {name}',
+ 'device.toast.screenshot_requested': 'Capture demandée',
+ 'device.toast.renamed': 'Écran renommé',
+ 'device.toast.removing': 'Suppression...',
+ 'device.toast.removed': 'Écran retiré',
+ 'device.toast.settings_saved': 'Paramètres enregistrés',
+ 'device.toast.published': 'Liste publiée — appareils mis à jour',
+ 'device.toast.draft_discarded': 'Modifications du brouillon annulées',
+ 'device.toast.playlist_changed': 'Liste modifiée',
+ 'device.toast.layout_applied': 'Mise en page appliquée',
+ 'device.toast.switched_to_fullscreen': 'Passé en plein écran',
+ 'device.toast.added_to_playlist': 'Ajouté à la liste',
+ 'device.toast.unmuted': 'Son réactivé',
+ 'device.toast.muted': 'Son coupé',
+ 'device.toast.zone_updated': 'Zone mise à jour',
+ 'device.toast.removed_from_playlist': 'Contenu retiré de la liste',
+ 'device.toast.playlist_reordered': 'Liste réorganisée',
+ 'device.toast.reboot_sent': 'Commande de redémarrage envoyée',
+ 'device.toast.shutdown_sent': 'Commande d\'arrêt envoyée',
+ 'device.toast.screen_off_sent': 'Commande d\'extinction de l\'écran envoyée',
+ 'device.toast.screen_on_sent': 'Commande d\'allumage de l\'écran envoyée',
+ 'device.toast.launch_sent': 'Commande de lancement envoyée',
+ 'device.toast.update_triggered': 'Vérification de mise à jour déclenchée',
+ 'device.toast.remote_started': 'Session de contrôle à distance démarrée',
+
+ // Settings
+ 'settings.title': 'Paramètres',
+ 'settings.subtitle': 'Configuration du serveur et informations d\'installation',
+ 'settings.account': 'Compte',
+ 'settings.save_profile': 'Enregistrer le profil',
+ 'settings.change_password': 'Changer le mot de passe',
+ 'settings.password_min_8': 'Doit contenir au moins 8 caractères.',
+ 'settings.current_password': 'Mot de passe actuel',
+ 'settings.new_password': 'Nouveau mot de passe',
+ 'settings.confirm_new_password': 'Confirmer le nouveau mot de passe',
+ 'settings.sso_note': 'Vous vous connectez via {provider}. Gérez votre mot de passe là-bas.',
+ 'settings.license': 'Licence',
+ 'settings.license_mit': 'Licence MIT - toutes les fonctionnalités incluses.',
+ 'settings.platform_admin_link': 'Les outils d\'administration sont sur la page',
+ 'settings.platform_admin_page_suffix': '.',
+ 'settings.user_management': 'Gestion des utilisateurs',
+ 'settings.loading_users': 'Chargement des utilisateurs...',
+ 'settings.white_label': 'Marque blanche / Branding',
+ 'settings.white_label_desc': 'Personnalisez l\'apparence du tableau de bord et du lecteur pour vos clients.',
+ 'settings.brand_name': 'Nom de la marque',
+ 'settings.logo_url': 'URL du logo',
+ 'settings.primary_color': 'Couleur principale',
+ 'settings.bg_color': 'Couleur de fond',
+ 'settings.custom_domain': 'Domaine personnalisé',
+ 'settings.favicon_url': 'URL du favicon',
+ 'settings.custom_css': 'CSS personnalisé (facultatif)',
+ 'settings.hide_branding': 'Masquer la marque « ScreenTinker »',
+ 'settings.save_branding': 'Enregistrer le branding',
+ 'settings.preview': 'Aperçu',
+ 'settings.white_label_enterprise_only': 'Le branding personnalisé est disponible sur le plan Enterprise',
+ 'settings.view_plans': 'Voir les plans',
+ 'settings.server_info': 'Informations du serveur',
+ 'settings.server_url': 'URL du serveur',
+ 'settings.api_endpoint': 'Point de terminaison API',
+ 'settings.server_url_hint': 'Utilisez cette URL pour configurer l\'app Android',
+ 'settings.setup_guide': 'Guide d\'installation',
+ 'settings.setup_step_1': 'Installez l\'APK ScreenTinker sur votre TV via le sideloading',
+ 'settings.setup_step_2_prefix': 'Ouvrez l\'app et saisissez cette URL du serveur :',
+ 'settings.setup_step_3': 'L\'app affichera un code d\'appairage à 6 chiffres',
+ 'settings.setup_step_4': 'Cliquez sur « Ajouter un écran » et saisissez le code',
+ 'settings.setup_step_5': 'Téléversez du contenu dans la Bibliothèque',
+ 'settings.setup_step_6': 'Attribuez du contenu à la liste de l\'écran',
+ 'settings.your_data': 'Vos données',
+ 'settings.your_data_desc': 'Exportez ou importez vos appareils, contenu, mises en page, calendriers et tous les paramètres. Utilisez cela pour migrer entre cloud et auto-hébergé.',
+ 'settings.export_my_data': 'Exporter mes données',
+ 'settings.include_media_zip': 'Inclure les fichiers médias (ZIP)',
+ 'settings.import_data': 'Importer des données',
+ 'settings.language': 'Langue',
+ 'settings.about': 'À propos',
+ 'settings.about_tagline': 'Système de gestion d\'affichage dynamique.',
+ 'settings.third_party_licenses': 'Licences tierces',
+ 'settings.import.reading_file': 'Lecture du fichier...',
+ 'settings.import.zip_detected': 'Export ZIP détecté : {name} ({size} Mo)
Contient données + fichiers médias.',
+ 'settings.import.confirm': 'Confirmer l\'import',
+ 'settings.import.invalid_file': 'Fichier invalide. Doit être un JSON ou ZIP d\'export ScreenTinker.',
+ 'settings.import.summary_devices': '{n} appareils',
+ 'settings.import.summary_content': '{n} contenus',
+ 'settings.import.summary_widgets': '{n} widgets',
+ 'settings.import.summary_layouts': '{n} mises en page',
+ 'settings.import.summary_schedules': '{n} calendriers',
+ 'settings.import.summary_walls': '{n} murs vidéo',
+ 'settings.import.summary_kiosk': '{n} pages kiosque',
+ 'settings.import.found_summary': 'Trouvé : {summary}.
De : {email} (exporté {date})',
+ 'settings.import.empty_export': 'export vide',
+ 'settings.import.uploading_zip': 'Téléversement et import... Cela peut prendre du temps pour les gros fichiers.',
+ 'settings.import.importing': 'Import...',
+ 'settings.import.complete': 'Import terminé : {imported}.',
+ 'settings.import.pairing_codes_title': 'Codes d\'appairage :',
+ 'settings.import.pairing_codes_hint': 'Saisissez ces codes sur chaque appareil pour les relier. Les attributions et calendriers seront préservés.',
+ 'settings.import.failed': 'Échec de l\'import',
+ 'settings.import.failed_with_error': 'Échec de l\'import : {error}',
+ 'settings.import.read_failed': 'Échec de lecture du fichier : {error}',
+ 'settings.toast.support_token_generated': 'Jeton de support généré (valide {hours}h)',
+ 'settings.toast.import_success': 'Données importées avec succès',
+ 'settings.toast.name_required': 'Le nom ne peut pas être vide',
+ 'settings.toast.profile_saved': 'Profil enregistré',
+ 'settings.toast.current_password_required': 'Saisissez votre mot de passe actuel',
+ 'settings.toast.new_password_min_8': 'Le nouveau mot de passe doit comporter au moins 8 caractères',
+ 'settings.toast.passwords_dont_match': 'Les nouveaux mots de passe ne correspondent pas',
+ 'settings.toast.password_changed': 'Mot de passe changé',
+ 'settings.toast.branding_saved': 'Branding enregistré',
+ 'settings.toast.preview_applied': 'Aperçu appliqué (rechargez pour réinitialiser)',
+ 'settings.toast.plan_updated': 'Plan mis à jour',
+ 'settings.toast.user_removed': 'Utilisateur retiré',
+ 'settings.user.col_user': 'Utilisateur',
+ 'settings.user.col_auth': 'Auth',
+ 'settings.user.col_role': 'Rôle',
+ 'settings.user.col_plan': 'Plan',
+ 'settings.user.col_actions': 'Actions',
+ 'settings.user.remove': 'Retirer',
+ 'settings.user.you': 'Vous',
+ 'settings.user.confirm': 'Confirmer ?',
+ 'settings.user.count_one': '1 utilisateur inscrit',
+ 'settings.user.count_other': '{n} utilisateurs inscrits',
};
diff --git a/frontend/js/i18n/pt.js b/frontend/js/i18n/pt.js
index efcdc58..623d612 100644
--- a/frontend/js/i18n/pt.js
+++ b/frontend/js/i18n/pt.js
@@ -194,4 +194,250 @@ export default {
'content.toast.folder_deleted': 'Pasta excluída',
'content.toast.moved': 'Movido',
'content.toast.moved_to_root': 'Movido para a raiz',
+
+ // Device detail
+ 'device.back': 'Voltar para Telas',
+ 'device.owner_label': 'Proprietário: {owner}',
+ 'device.rename': 'Renomear',
+ 'device.screenshot_btn': 'Captura',
+ 'device.remove': 'Remover',
+ 'device.click_to_confirm': 'Clique novamente para confirmar',
+ 'device.prompt_new_name': 'Digite o novo nome:',
+ 'device.confirm_discard_draft': 'Descartar todas as alterações não publicadas e voltar à última versão publicada?',
+ 'device.failed_load': 'Falha ao carregar o dispositivo',
+ 'device.no_screenshot': 'Sem captura disponível. Clique em "Captura" para tirar uma.',
+ 'device.no_content_assigned': 'Sem conteúdo atribuído',
+ 'device.now_playing_id': 'Reproduzindo: {id}',
+ 'device.playlist_count_one': '1 item na playlist',
+ 'device.playlist_count_other': '{n} itens na playlist',
+ 'device.tab.now_playing': 'Reproduzindo agora',
+ 'device.tab.now_playing_tip': 'Captura ao vivo do que está sendo exibido neste dispositivo.',
+ 'device.tab.playlist': 'Playlist',
+ 'device.tab.playlist_tip': 'Conteúdo atribuído a este dispositivo. Arraste para reordenar. Adicione mídia, widgets ou páginas de quiosque.',
+ 'device.tab.info': 'Informações do dispositivo',
+ 'device.tab.info_tip': 'Telemetria de hardware, orientação, notas e controles do dispositivo.',
+ 'device.tab.remote': 'Controle remoto',
+ 'device.tab.remote_tip': 'Visualize a tela em tempo real e envie pressionamentos de tecla. Funciona no APK do Android e no player web.',
+ 'device.draft.banner_title': 'Alterações não publicadas',
+ 'device.draft.devices_showing_published': 'Os dispositivos ainda exibem a última versão publicada.',
+ 'device.draft.never_published': 'Esta playlist nunca foi publicada. Os dispositivos não exibirão nada até você publicar.',
+ 'device.draft.discard': 'Descartar',
+ 'device.draft.publish': 'Publicar',
+ 'device.draft.publishing': 'Publicando...',
+ 'device.layout.label': 'Layout da tela',
+ 'device.layout.fullscreen_default': 'Tela cheia (padrão)',
+ 'device.layout.zones_count': '{name} ({n} zonas)',
+ 'device.layout.template_zones_count': '[Modelo] {name} ({n} zonas)',
+ 'device.layout.apply': 'Aplicar',
+ 'device.playlist.label': 'Playlist',
+ 'device.playlist.no_playlist': 'Sem playlist',
+ 'device.playlist.copy_to_btn': 'Copiar para...',
+ 'device.playlist.add_content_btn': 'Adicionar conteúdo',
+ 'device.playlist.empty_title': 'Sem conteúdo atribuído',
+ 'device.playlist.empty_desc': 'Adicione conteúdo da sua biblioteca à playlist desta tela.',
+ 'device.playlist_picker.with_count': '{name} — {n} itens',
+ 'device.playlist_picker.with_auto': '{name} (auto) — {n} itens',
+ 'device.info.status': 'Status',
+ 'device.info.ip_address': 'Endereço IP',
+ 'device.info.battery': 'Bateria',
+ 'device.info.storage': 'Armazenamento',
+ 'device.info.size_free': '{size} livres',
+ 'device.info.player_type': 'Tipo de player',
+ 'device.info.web_player': 'Player web',
+ 'device.info.wifi': 'Wi-Fi',
+ 'device.info.uptime': 'Tempo ativo',
+ 'device.info.android_version': 'Versão do Android',
+ 'device.info.app_version': 'Versão do app',
+ 'device.info.screen_resolution': 'Resolução',
+ 'device.info.ram': 'RAM',
+ 'device.info.cpu_usage': 'Uso de CPU',
+ 'device.timeline.title': 'Linha do tempo (últimas 24 horas)',
+ 'device.timeline.h24_ago': 'há 24h',
+ 'device.timeline.now': 'Agora',
+ 'device.timeline.online': 'Online',
+ 'device.timeline.offline': 'Offline',
+ 'device.timeline.no_data': 'Sem dados',
+ 'device.timeline.uptime_pct_tracked': '{pct}% ativo ({n}min monitorados)',
+ 'device.timeline.uptime_pct_no_data': '{pct}% ativo (sem dados)',
+ 'device.form.orientation_label': 'Orientação / Rotação',
+ 'device.form.orientation.landscape': 'Paisagem (0°)',
+ 'device.form.orientation.portrait': 'Retrato (90° SH)',
+ 'device.form.orientation.landscape_flipped': 'Paisagem invertida (180°)',
+ 'device.form.orientation.portrait_flipped': 'Retrato invertido (270° SH)',
+ 'device.form.default_content_label': 'Conteúdo padrão',
+ 'device.form.default_content_none': 'Nenhum (mostrar "Aguardando...")',
+ 'device.form.notes_label': 'Notas',
+ 'device.form.notes_placeholder': 'Localização, detalhes de instalação, etc.',
+ 'device.form.save_settings': 'Salvar configurações',
+ 'device.ctl.reboot_device': 'Reiniciar dispositivo',
+ 'device.ctl.screen_off': 'Desligar tela',
+ 'device.ctl.screen_on': 'Ligar tela',
+ 'device.ctl.launch_player': 'Iniciar player',
+ 'device.ctl.force_update': 'Forçar atualização',
+ 'device.ctl.shutdown': 'Desligar',
+ 'device.remote.start_prompt': 'Clique em "Iniciar controle remoto" para começar',
+ 'device.remote.start': 'Iniciar controle remoto',
+ 'device.remote.stop': 'Parar controle remoto',
+ 'device.remote.vol_up': 'Vol +',
+ 'device.remote.vol_down': 'Vol -',
+ 'device.remote.home': 'Início',
+ 'device.remote.back': 'Voltar',
+ 'device.remote.recents': 'Recentes',
+ 'device.remote.power': 'Energia',
+ 'device.remote.ok': 'OK',
+ 'device.remote.settings': 'Configurações',
+ 'device.remote.scrn_off': 'Tela off',
+ 'device.remote.scrn_on': 'Tela on',
+ 'device.remote.enable_system_view': 'Ativar visão do sistema',
+ 'device.remote.system_view_tooltip': 'Solicita ao usuário do dispositivo permitir captura de tela cheia - habilita visualização remota da tela inicial, configurações e outros apps',
+ 'device.remote.system_view_hint': 'Aprovação única necessária no dispositivo',
+ 'device.remote.waiting_for_approval': 'Aguardando aprovação do dispositivo...',
+ 'device.remote.system_view_enabled': 'Visão do sistema ativada',
+ 'device.remote.unlocked_hint': 'Navegação e controles do sistema desbloqueados',
+ 'device.pl_item.widget_with_type': 'Widget ({type})',
+ 'device.pl_item.youtube': 'YouTube',
+ 'device.pl_item.video': 'Vídeo',
+ 'device.pl_item.image': 'Imagem',
+ 'device.pl_item.zone_label': 'Zona: {id}',
+ 'device.pl_item.no_zone': 'Sem zona',
+ 'device.pl_item.mute': 'Silenciar',
+ 'device.pl_item.unmute': 'Ativar áudio',
+ 'device.pl_item.remove': 'Remover',
+ 'device.copy.no_other_devices': 'Não há outros dispositivos para copiar',
+ 'device.copy.prompt': 'Copiar playlist para qual dispositivo?\n\n{list}\n\nDigite o número:',
+ 'device.copy.invalid_selection': 'Seleção inválida',
+ 'device.copy.toast': '{n} itens copiados para {device}',
+ 'device.assign.empty_all': 'Ainda não há conteúdo, widgets ou páginas de quiosque. Crie algo primeiro!',
+ 'device.assign.modal_title': 'Adicionar à playlist',
+ 'device.assign.zone_label': 'Zona',
+ 'device.assign.zone_default': 'Padrão (tela cheia)',
+ 'device.assign.duration_label': 'Duração (segundos, para imagens/widgets)',
+ 'device.assign.tab.media': 'Mídia ({n})',
+ 'device.assign.tab.widgets': 'Widgets ({n})',
+ 'device.assign.tab.kiosk': 'Quiosque ({n})',
+ 'device.assign.no_media': 'Nenhuma mídia enviada ainda',
+ 'device.assign.no_widgets': 'Nenhum widget criado ainda.',
+ 'device.assign.no_kiosk': 'Nenhuma página de quiosque ainda.',
+ 'device.assign.create_one': 'Crie um',
+ 'device.assign.add_selected': 'Adicionar selecionados',
+ 'device.assign.select_first': 'Selecione algo primeiro',
+ 'device.assign.kiosk_widget_name': 'Quiosque: {name}',
+ 'device.toast.screenshot_requested': 'Captura solicitada',
+ 'device.toast.renamed': 'Tela renomeada',
+ 'device.toast.removing': 'Removendo...',
+ 'device.toast.removed': 'Tela removida',
+ 'device.toast.settings_saved': 'Configurações salvas',
+ 'device.toast.published': 'Playlist publicada — dispositivos atualizados',
+ 'device.toast.draft_discarded': 'Alterações do rascunho descartadas',
+ 'device.toast.playlist_changed': 'Playlist alterada',
+ 'device.toast.layout_applied': 'Layout aplicado',
+ 'device.toast.switched_to_fullscreen': 'Alterado para tela cheia',
+ 'device.toast.added_to_playlist': 'Adicionado à playlist',
+ 'device.toast.unmuted': 'Áudio ativado',
+ 'device.toast.muted': 'Silenciado',
+ 'device.toast.zone_updated': 'Zona atualizada',
+ 'device.toast.removed_from_playlist': 'Conteúdo removido da playlist',
+ 'device.toast.playlist_reordered': 'Playlist reordenada',
+ 'device.toast.reboot_sent': 'Comando de reinício enviado',
+ 'device.toast.shutdown_sent': 'Comando de desligamento enviado',
+ 'device.toast.screen_off_sent': 'Comando para desligar tela enviado',
+ 'device.toast.screen_on_sent': 'Comando para ligar tela enviado',
+ 'device.toast.launch_sent': 'Comando de início enviado',
+ 'device.toast.update_triggered': 'Verificação de atualização disparada',
+ 'device.toast.remote_started': 'Sessão de controle remoto iniciada',
+
+ // Settings
+ 'settings.title': 'Configurações',
+ 'settings.subtitle': 'Configuração do servidor e informações de instalação',
+ 'settings.account': 'Conta',
+ 'settings.save_profile': 'Salvar perfil',
+ 'settings.change_password': 'Alterar senha',
+ 'settings.password_min_8': 'Deve ter no mínimo 8 caracteres.',
+ 'settings.current_password': 'Senha atual',
+ 'settings.new_password': 'Nova senha',
+ 'settings.confirm_new_password': 'Confirmar nova senha',
+ 'settings.sso_note': 'Você entra via {provider}. Gerencie sua senha lá.',
+ 'settings.license': 'Licença',
+ 'settings.license_mit': 'Licença MIT - todos os recursos incluídos.',
+ 'settings.platform_admin_link': 'As ferramentas de admin da plataforma estão na página',
+ 'settings.platform_admin_page_suffix': '.',
+ 'settings.user_management': 'Gestão de usuários',
+ 'settings.loading_users': 'Carregando usuários...',
+ 'settings.white_label': 'Marca branca / Branding',
+ 'settings.white_label_desc': 'Personalize a aparência do painel e do player para seus clientes.',
+ 'settings.brand_name': 'Nome da marca',
+ 'settings.logo_url': 'URL do logotipo',
+ 'settings.primary_color': 'Cor primária',
+ 'settings.bg_color': 'Cor de fundo',
+ 'settings.custom_domain': 'Domínio personalizado',
+ 'settings.favicon_url': 'URL do favicon',
+ 'settings.custom_css': 'CSS personalizado (opcional)',
+ 'settings.hide_branding': 'Ocultar marca "ScreenTinker"',
+ 'settings.save_branding': 'Salvar branding',
+ 'settings.preview': 'Pré-visualizar',
+ 'settings.white_label_enterprise_only': 'Branding personalizado disponível no plano Enterprise',
+ 'settings.view_plans': 'Ver planos',
+ 'settings.server_info': 'Informações do servidor',
+ 'settings.server_url': 'URL do servidor',
+ 'settings.api_endpoint': 'Endpoint da API',
+ 'settings.server_url_hint': 'Use esta URL ao configurar o app Android',
+ 'settings.setup_guide': 'Guia de instalação',
+ 'settings.setup_step_1': 'Instale o APK do ScreenTinker na sua TV via sideloading',
+ 'settings.setup_step_2_prefix': 'Abra o app e digite esta URL do servidor:',
+ 'settings.setup_step_3': 'O app exibirá um código de pareamento de 6 dígitos',
+ 'settings.setup_step_4': 'Clique em "Adicionar tela" no painel e digite o código',
+ 'settings.setup_step_5': 'Envie conteúdo na Biblioteca de conteúdo',
+ 'settings.setup_step_6': 'Atribua conteúdo à playlist da tela',
+ 'settings.your_data': 'Seus dados',
+ 'settings.your_data_desc': 'Exporte ou importe seus dispositivos, conteúdo, layouts, agendas e todas as configurações. Use para migrar entre nuvem e auto-hospedado.',
+ 'settings.export_my_data': 'Exportar meus dados',
+ 'settings.include_media_zip': 'Incluir arquivos de mídia (ZIP)',
+ 'settings.import_data': 'Importar dados',
+ 'settings.language': 'Idioma',
+ 'settings.about': 'Sobre',
+ 'settings.about_tagline': 'Sistema de gerenciamento de sinalização digital.',
+ 'settings.third_party_licenses': 'Licenças de terceiros',
+ 'settings.import.reading_file': 'Lendo arquivo...',
+ 'settings.import.zip_detected': 'Exportação ZIP detectada: {name} ({size} MB)
Contém dados + arquivos de mídia.',
+ 'settings.import.confirm': 'Confirmar importação',
+ 'settings.import.invalid_file': 'Arquivo inválido. Deve ser JSON ou ZIP de exportação do ScreenTinker.',
+ 'settings.import.summary_devices': '{n} dispositivos',
+ 'settings.import.summary_content': '{n} itens de conteúdo',
+ 'settings.import.summary_widgets': '{n} widgets',
+ 'settings.import.summary_layouts': '{n} layouts',
+ 'settings.import.summary_schedules': '{n} agendas',
+ 'settings.import.summary_walls': '{n} paredes de vídeo',
+ 'settings.import.summary_kiosk': '{n} páginas de quiosque',
+ 'settings.import.found_summary': 'Encontrado: {summary}.
De: {email} (exportado {date})',
+ 'settings.import.empty_export': 'exportação vazia',
+ 'settings.import.uploading_zip': 'Enviando e importando... Pode demorar para arquivos grandes.',
+ 'settings.import.importing': 'Importando...',
+ 'settings.import.complete': 'Importação concluída: {imported}.',
+ 'settings.import.pairing_codes_title': 'Códigos de pareamento:',
+ 'settings.import.pairing_codes_hint': 'Digite estes códigos em cada dispositivo para revinculá-los. Atribuições e agendas serão preservadas.',
+ 'settings.import.failed': 'Falha na importação',
+ 'settings.import.failed_with_error': 'Falha na importação: {error}',
+ 'settings.import.read_failed': 'Falha ao ler o arquivo: {error}',
+ 'settings.toast.support_token_generated': 'Token de suporte gerado (válido {hours}h)',
+ 'settings.toast.import_success': 'Dados importados com sucesso',
+ 'settings.toast.name_required': 'O nome não pode ficar em branco',
+ 'settings.toast.profile_saved': 'Perfil salvo',
+ 'settings.toast.current_password_required': 'Digite sua senha atual',
+ 'settings.toast.new_password_min_8': 'A nova senha deve ter no mínimo 8 caracteres',
+ 'settings.toast.passwords_dont_match': 'As novas senhas não conferem',
+ 'settings.toast.password_changed': 'Senha alterada',
+ 'settings.toast.branding_saved': 'Branding salvo',
+ 'settings.toast.preview_applied': 'Pré-visualização aplicada (recarregue para redefinir)',
+ 'settings.toast.plan_updated': 'Plano atualizado',
+ 'settings.toast.user_removed': 'Usuário removido',
+ 'settings.user.col_user': 'Usuário',
+ 'settings.user.col_auth': 'Auth',
+ 'settings.user.col_role': 'Função',
+ 'settings.user.col_plan': 'Plano',
+ 'settings.user.col_actions': 'Ações',
+ 'settings.user.remove': 'Remover',
+ 'settings.user.you': 'Você',
+ 'settings.user.confirm': 'Confirmar?',
+ 'settings.user.count_one': '1 usuário registrado',
+ 'settings.user.count_other': '{n} usuários registrados',
};
diff --git a/frontend/js/views/device-detail.js b/frontend/js/views/device-detail.js
index cd0305c..dc9356e 100644
--- a/frontend/js/views/device-detail.js
+++ b/frontend/js/views/device-detail.js
@@ -2,6 +2,7 @@ import { api } from '../api.js';
import { on, off, requestScreenshot, startRemote, stopRemote, sendTouch, sendKey, sendCommand } from '../socket.js';
import { showToast } from '../components/toast.js';
import { esc } from '../utils.js';
+import { t, tn } from '../i18n.js';
let currentDevice = null;
let statusHandler = null;
@@ -33,10 +34,10 @@ export function render(container, deviceId) {
- Back to Displays
+ ${t('device.back')}
- ${device.assignments?.length ? `${device.assignments.length} item(s) in playlist` : 'No content assigned'} + ${device.assignments?.length ? tn('device.playlist_count', device.assignments.length) : t('device.no_content_assigned')}
@@ -164,13 +165,13 @@ async function loadDevice(deviceId, activeTab = null) {${esc(err.message)}
${esc(err.message)}
Add content from your library to this display's playlist.
${t('device.playlist.empty_desc')}
No widgets created yet. Create one
'} + }).join('') || `${t('device.assign.no_widgets')} ${t('device.assign.create_one')}
`}No kiosk pages yet. Create one
'} + `).join('') || `${t('device.assign.no_kiosk')} ${t('device.assign.create_one')}
`}Must be at least 8 characters.
+${t('settings.password_min_8')}
You sign in via ${esc(user.auth_provider || 'SSO')}. Manage your password there.
+${t('settings.sso_note', { provider: esc(user.auth_provider || 'SSO') })}
`}MIT License - all features included.
${t('settings.license_mit')}
Platform admin tools are in the Admin page.
' : ''} + ${isSuperAdmin ? `${t('settings.platform_admin_link')} ${t('nav.admin')} ${t('settings.platform_admin_page_suffix')}
` : ''}Loading users...
${t('settings.loading_users')}
Customize the look of your dashboard and player for your clients.
+${t('settings.white_label_desc')}
Use this URL when setting up the Android app
+${t('settings.server_url_hint')}
${serverUrl}${serverUrl}Export or import your devices, content, layouts, schedules, and all settings. Use this to migrate between cloud and self-hosted instances.
+${t('settings.your_data_desc')}
ScreenTinker v1.4.1
-Digital signage management system.
+${t('settings.about_tagline')}
- Terms of Service + ${t('auth.terms')} · - Privacy Policy + ${t('auth.privacy')} · - Third-Party Licenses + ${t('settings.third_party_licenses')}
| ${d.name} | ${d.pairing_code} |
Custom branding is available on the Enterprise plan
- View Plans +${t('settings.white_label_enterprise_only')}
+ ${t('settings.view_plans')}| User | -Auth | -Role | -Plan | -Actions | +${t('settings.user.col_user')} | +${t('settings.user.col_auth')} | +${t('settings.user.col_role')} | +${t('settings.user.col_plan')} | +${t('settings.user.col_actions')} |
- ${u.id !== currentUser.id ? ` |
`).join('')}
|---|
${users.length} user(s) registered
+${tn('settings.user.count', users.length)}
`; // Plan change handlers @@ -451,7 +448,7 @@ async function loadUsers() { const planId = select.value; try { await api.assignPlan(userId, planId); - showToast('Plan updated', 'success'); + showToast(t('settings.toast.plan_updated'), 'success'); } catch (err) { showToast(err.message, 'error'); loadUsers(); // Revert @@ -466,7 +463,7 @@ async function loadUsers() { if (confirming) { try { await api.deleteUser(btn.dataset.userId); - showToast('User removed', 'success'); + showToast(t('settings.toast.user_removed'), 'success'); loadUsers(); } catch (err) { showToast(err.message, 'error'); @@ -474,12 +471,12 @@ async function loadUsers() { return; } confirming = true; - btn.textContent = 'Confirm?'; + btn.textContent = t('settings.user.confirm'); btn.style.background = 'var(--danger)'; btn.style.color = 'white'; setTimeout(() => { confirming = false; - btn.textContent = 'Remove'; + btn.textContent = t('settings.user.remove'); btn.style.background = ''; btn.style.color = ''; }, 3000);