From 0743901e48dda54c9770d8bfdd598565b6f0f2be Mon Sep 17 00:00:00 2001 From: ScreenTinker Date: Wed, 29 Apr 2026 19:52:31 -0500 Subject: [PATCH] i18n batch 2a: wire widgets.js (~107 keys) - All widget types (clock/weather/rss/text/webpage/social/directory-board) with localized names + descriptions - Full Directory Board editor (categories, entries, logo, backgrounds) - Content picker overlay - Confirms, toasts, empty states - 532 keys total, 100% parity across en/es/fr/de/pt Designer.js follows in batch 2b. Co-Authored-By: Claude Opus 4.7 --- frontend/js/i18n/de.js | 109 +++++++++++++++++ frontend/js/i18n/en.js | 113 +++++++++++++++++ frontend/js/i18n/es.js | 109 +++++++++++++++++ frontend/js/i18n/fr.js | 109 +++++++++++++++++ frontend/js/i18n/pt.js | 109 +++++++++++++++++ frontend/js/views/widgets.js | 228 ++++++++++++++++++----------------- 6 files changed, 669 insertions(+), 108 deletions(-) diff --git a/frontend/js/i18n/de.js b/frontend/js/i18n/de.js index 0a578f3..5e98fe6 100644 --- a/frontend/js/i18n/de.js +++ b/frontend/js/i18n/de.js @@ -440,4 +440,113 @@ export default { 'settings.user.confirm': 'Bestätigen?', 'settings.user.count_one': '1 Benutzer registriert', 'settings.user.count_other': '{n} Benutzer registriert', + + // Widgets + 'widget.title': 'Widgets', + 'widget.subtitle': 'Fügen Sie Ihren Layouts dynamische Inhalte hinzu', + 'widget.help_tip': 'Dynamische Inhaltselemente: Live-Uhren, Wetter, RSS-Ticker, Text, Webseiten und Social-Feeds. Erstellen Sie ein Widget und weisen Sie es einer Geräte-Playlist zu.', + 'widget.new_widget': 'Neues Widget', + 'widget.configure': 'Widget konfigurieren', + 'widget.preview': 'Vorschau', + 'widget.preview_title': 'Vorschau', + 'widget.close': 'Schließen', + 'widget.edit_x': '{type} bearbeiten', + 'widget.new_x': 'Neues {type}', + 'widget.empty_title': 'Noch keine Widgets', + 'widget.empty_desc': 'Erstellen Sie ein Widget, um Ihren Layouts dynamische Inhalte hinzuzufügen.', + 'widget.this_widget': 'dieses Widget', + 'widget.confirm_delete': '„{name}" löschen? Dies kann nicht rückgängig gemacht werden.', + 'widget.toast.saved': 'Widget gespeichert', + 'widget.toast.deleted': 'Widget gelöscht', + 'widget.toast.preview_failed': 'Vorschau fehlgeschlagen', + 'widget.type.clock.name': 'Uhr', + 'widget.type.clock.desc': 'Digitaluhr mit Datum', + 'widget.type.weather.name': 'Wetter', + 'widget.type.weather.desc': 'Aktuelle Wetterbedingungen', + 'widget.type.rss.name': 'Nachrichten-Ticker', + 'widget.type.rss.desc': 'Scrollender RSS-Feed', + 'widget.type.text.name': 'Text/HTML', + 'widget.type.text.desc': 'Benutzerdefinierter Text oder HTML-Inhalt', + 'widget.type.webpage.name': 'Webseite', + 'widget.type.webpage.desc': 'Webseite einbetten', + 'widget.type.social.name': 'Social Feed', + 'widget.type.social.desc': 'Social-Media-Feed', + 'widget.type.directory_board.name': 'Verzeichnistafel', + 'widget.type.directory_board.desc': 'Scrollendes Mieter-/Raumverzeichnis für Lobbys', + 'widget.field.name': 'Widget-Name', + 'widget.field.format': 'Format', + 'widget.field.format_12h': '12 Stunden', + 'widget.field.format_24h': '24 Stunden', + 'widget.field.timezone': 'Zeitzone', + 'widget.field.font_size': 'Schriftgröße', + 'widget.field.font_size_px': 'Schriftgröße (px)', + 'widget.field.color': 'Farbe', + 'widget.field.background': 'Hintergrund', + 'widget.field.location': 'Standort', + 'widget.field.location_placeholder': 'Stadt, Land', + 'widget.field.units': 'Einheiten', + 'widget.field.units_imperial': 'Imperial (°F)', + 'widget.field.units_metric': 'Metrisch (°C)', + 'widget.field.feed_url': 'Feed-URL', + 'widget.field.scroll_speed_seconds': 'Scroll-Geschwindigkeit (Sekunden)', + 'widget.field.max_items': 'Max. Elemente', + 'widget.field.html_content': 'HTML-Inhalt', + 'widget.field.css_optional': 'CSS (optional)', + 'widget.field.url': 'URL', + 'widget.field.zoom_pct': 'Zoom (%)', + 'widget.field.refresh_interval': 'Aktualisierungsintervall (Sekunden, 0 = nie)', + 'widget.field.platform': 'Plattform', + 'widget.field.platform_twitter': 'Twitter/X', + 'widget.field.platform_instagram': 'Instagram', + 'widget.field.query': 'Abfrage', + 'widget.field.query_placeholder': '@handle oder #hashtag', + 'widget.picker.default_title': 'Bild auswählen', + 'widget.picker.select_logo': 'Logo auswählen', + 'widget.picker.select_bg_images': 'Hintergrundbilder auswählen', + 'widget.picker.search': 'Bilder suchen...', + 'widget.picker.no_matches': 'Keine Treffer.', + 'widget.picker.no_images': 'Keine Bilder in Ihrer Bibliothek. Laden Sie zuerst Bilder aus der Inhaltsbibliothek hoch.', + 'widget.picker.selected_count': '{n} ausgewählt', + 'widget.dir.title_label': 'Titel', + 'widget.dir.title_placeholder': 'Lincoln Lager', + 'widget.dir.logo_label': 'Logo (optional)', + 'widget.dir.footer_text_label': 'Fußzeile', + 'widget.dir.footer_placeholder': 'Mietanfragen: Kontakt...', + 'widget.dir.bg_images_label': 'Hintergrundbilder (optional)', + 'widget.dir.bg_images_hint': 'Bilder wechseln alle 15 Sekunden bei 30 % Deckkraft. Mehrere für Rotation hinzufügen.', + 'widget.dir.add_bg_image': '+ Hintergrundbild hinzufügen', + 'widget.dir.theme': 'Thema', + 'widget.dir.theme_dark': 'Dunkel', + 'widget.dir.theme_light': 'Hell', + 'widget.dir.scroll_speed': 'Scroll-Geschwindigkeit', + 'widget.dir.speed_slow': 'Langsam', + 'widget.dir.speed_medium': 'Mittel', + 'widget.dir.speed_fast': 'Schnell', + 'widget.dir.columns': 'Spalten', + 'widget.dir.columns_auto': 'Auto', + 'widget.dir.categories': 'Kategorien', + 'widget.dir.add_category': '+ Kategorie hinzufügen', + 'widget.dir.add_entry': '+ Eintrag hinzufügen', + 'widget.dir.empty_categories': 'Fügen Sie Ihre erste Etage oder Abteilung hinzu, um zu beginnen', + 'widget.dir.no_entries': 'Noch keine Einträge', + 'widget.dir.entry': 'Eintrag', + 'widget.dir.entries': 'Einträge', + 'widget.dir.collapse': 'Einklappen', + 'widget.dir.expand': 'Aufklappen', + 'widget.dir.move_up': 'Nach oben', + 'widget.dir.move_down': 'Nach unten', + 'widget.dir.delete_category': 'Kategorie löschen', + 'widget.dir.delete_entry': 'Eintrag löschen', + 'widget.dir.unnamed': '(unbenannt)', + 'widget.dir.confirm_delete_category': 'Kategorie „{name}" und alle Einträge löschen?', + 'widget.dir.category_name_placeholder': 'z. B. Erste Etage', + 'widget.dir.entry_id_placeholder': '101', + 'widget.dir.entry_name_placeholder': 'Mietername', + 'widget.dir.entry_subtitle_placeholder': 'Details (optional)', + 'widget.dir.available': 'Verfügbar', + 'widget.dir.change': 'Ändern', + 'widget.dir.choose_logo': 'Logo wählen', + 'widget.dir.remove_logo': 'Entfernen', + 'widget.dir.no_bg_images': 'Keine Hintergrundbilder ausgewählt', + 'widget.dir.remove_bg': 'Entfernen', }; diff --git a/frontend/js/i18n/en.js b/frontend/js/i18n/en.js index dc73d72..8a1ff86 100644 --- a/frontend/js/i18n/en.js +++ b/frontend/js/i18n/en.js @@ -467,4 +467,117 @@ export default { 'settings.user.confirm': 'Confirm?', 'settings.user.count_one': '1 user registered', 'settings.user.count_other': '{n} users registered', + + // Widgets + 'widget.title': 'Widgets', + 'widget.subtitle': 'Add dynamic content to your layouts', + 'widget.help_tip': 'Dynamic content elements: live clocks, weather, RSS tickers, text, webpages, and social feeds. Create a widget then assign it to a device playlist.', + 'widget.new_widget': 'New Widget', + 'widget.configure': 'Configure Widget', + 'widget.preview': 'Preview', + 'widget.preview_title': 'Preview', + 'widget.close': 'Close', + 'widget.edit_x': 'Edit {type}', + 'widget.new_x': 'New {type}', + 'widget.empty_title': 'No widgets yet', + 'widget.empty_desc': 'Create a widget to add dynamic content to your layouts.', + 'widget.this_widget': 'this widget', + 'widget.confirm_delete': 'Delete "{name}"? This cannot be undone.', + 'widget.toast.saved': 'Widget saved', + 'widget.toast.deleted': 'Widget deleted', + 'widget.toast.preview_failed': 'Preview failed', + // Widget types + 'widget.type.clock.name': 'Clock', + 'widget.type.clock.desc': 'Digital clock with date', + 'widget.type.weather.name': 'Weather', + 'widget.type.weather.desc': 'Current weather conditions', + 'widget.type.rss.name': 'News Ticker', + 'widget.type.rss.desc': 'Scrolling RSS feed', + 'widget.type.text.name': 'Text/HTML', + 'widget.type.text.desc': 'Custom text or HTML content', + 'widget.type.webpage.name': 'Webpage', + 'widget.type.webpage.desc': 'Embed a webpage', + 'widget.type.social.name': 'Social Feed', + 'widget.type.social.desc': 'Social media feed', + 'widget.type.directory_board.name': 'Directory Board', + 'widget.type.directory_board.desc': 'Scrolling tenant/room directory for lobbies', + // Widget config form fields + 'widget.field.name': 'Widget Name', + 'widget.field.format': 'Format', + 'widget.field.format_12h': '12 Hour', + 'widget.field.format_24h': '24 Hour', + 'widget.field.timezone': 'Timezone', + 'widget.field.font_size': 'Font Size', + 'widget.field.font_size_px': 'Font Size (px)', + 'widget.field.color': 'Color', + 'widget.field.background': 'Background', + 'widget.field.location': 'Location', + 'widget.field.location_placeholder': 'City, State', + 'widget.field.units': 'Units', + 'widget.field.units_imperial': 'Imperial (°F)', + 'widget.field.units_metric': 'Metric (°C)', + 'widget.field.feed_url': 'Feed URL', + 'widget.field.scroll_speed_seconds': 'Scroll Speed (seconds)', + 'widget.field.max_items': 'Max Items', + 'widget.field.html_content': 'HTML Content', + 'widget.field.css_optional': 'CSS (optional)', + 'widget.field.url': 'URL', + 'widget.field.zoom_pct': 'Zoom (%)', + 'widget.field.refresh_interval': 'Refresh Interval (seconds, 0 = never)', + 'widget.field.platform': 'Platform', + 'widget.field.platform_twitter': 'Twitter/X', + 'widget.field.platform_instagram': 'Instagram', + 'widget.field.query': 'Query', + 'widget.field.query_placeholder': '@handle or #hashtag', + // Content picker + 'widget.picker.default_title': 'Select Image', + 'widget.picker.select_logo': 'Select Logo', + 'widget.picker.select_bg_images': 'Select Background Images', + 'widget.picker.search': 'Search images...', + 'widget.picker.no_matches': 'No matches.', + 'widget.picker.no_images': 'No images in your content library. Upload images first from Content Library.', + 'widget.picker.selected_count': '{n} selected', + // Directory Board + 'widget.dir.title_label': 'Title', + 'widget.dir.title_placeholder': 'Lincoln Warehouse', + 'widget.dir.logo_label': 'Logo (optional)', + 'widget.dir.footer_text_label': 'Footer Text', + 'widget.dir.footer_placeholder': 'For Leasing Inquiries: Contact...', + 'widget.dir.bg_images_label': 'Background Images (optional)', + 'widget.dir.bg_images_hint': 'Images crossfade every 15 seconds at 30% opacity. Add multiple for rotation.', + 'widget.dir.add_bg_image': '+ Add Background Image', + 'widget.dir.theme': 'Theme', + 'widget.dir.theme_dark': 'Dark', + 'widget.dir.theme_light': 'Light', + 'widget.dir.scroll_speed': 'Scroll Speed', + 'widget.dir.speed_slow': 'Slow', + 'widget.dir.speed_medium': 'Medium', + 'widget.dir.speed_fast': 'Fast', + 'widget.dir.columns': 'Columns', + 'widget.dir.columns_auto': 'Auto', + 'widget.dir.categories': 'Categories', + 'widget.dir.add_category': '+ Add Category', + 'widget.dir.add_entry': '+ Add Entry', + 'widget.dir.empty_categories': 'Add your first floor or department to get started', + 'widget.dir.no_entries': 'No entries yet', + 'widget.dir.entry': 'entry', + 'widget.dir.entries': 'entries', + 'widget.dir.collapse': 'Collapse', + 'widget.dir.expand': 'Expand', + 'widget.dir.move_up': 'Move up', + 'widget.dir.move_down': 'Move down', + 'widget.dir.delete_category': 'Delete category', + 'widget.dir.delete_entry': 'Delete entry', + 'widget.dir.unnamed': '(unnamed)', + 'widget.dir.confirm_delete_category': 'Delete category "{name}" and all its entries?', + 'widget.dir.category_name_placeholder': 'e.g. First Floor', + 'widget.dir.entry_id_placeholder': '101', + 'widget.dir.entry_name_placeholder': 'Tenant name', + 'widget.dir.entry_subtitle_placeholder': 'Details (optional)', + 'widget.dir.available': 'Available', + 'widget.dir.change': 'Change', + 'widget.dir.choose_logo': 'Choose Logo', + 'widget.dir.remove_logo': 'Remove', + 'widget.dir.no_bg_images': 'No background images selected', + 'widget.dir.remove_bg': 'Remove', }; diff --git a/frontend/js/i18n/es.js b/frontend/js/i18n/es.js index 827007e..6d4f295 100644 --- a/frontend/js/i18n/es.js +++ b/frontend/js/i18n/es.js @@ -439,4 +439,113 @@ export default { 'settings.user.confirm': '¿Confirmar?', 'settings.user.count_one': '1 usuario registrado', 'settings.user.count_other': '{n} usuarios registrados', + + // Widgets + 'widget.title': 'Widgets', + 'widget.subtitle': 'Agrega contenido dinámico a tus diseños', + 'widget.help_tip': 'Elementos de contenido dinámico: relojes en vivo, clima, tickers RSS, texto, páginas web y feeds sociales. Crea un widget y asígnalo a la lista de un dispositivo.', + 'widget.new_widget': 'Nuevo widget', + 'widget.configure': 'Configurar widget', + 'widget.preview': 'Previsualizar', + 'widget.preview_title': 'Previsualización', + 'widget.close': 'Cerrar', + 'widget.edit_x': 'Editar {type}', + 'widget.new_x': 'Nuevo {type}', + 'widget.empty_title': 'Aún no hay widgets', + 'widget.empty_desc': 'Crea un widget para agregar contenido dinámico a tus diseños.', + 'widget.this_widget': 'este widget', + 'widget.confirm_delete': '¿Eliminar "{name}"? Esto no se puede deshacer.', + 'widget.toast.saved': 'Widget guardado', + 'widget.toast.deleted': 'Widget eliminado', + 'widget.toast.preview_failed': 'Falló la previsualización', + 'widget.type.clock.name': 'Reloj', + 'widget.type.clock.desc': 'Reloj digital con fecha', + 'widget.type.weather.name': 'Clima', + 'widget.type.weather.desc': 'Condiciones meteorológicas actuales', + 'widget.type.rss.name': 'Ticker de noticias', + 'widget.type.rss.desc': 'Feed RSS con desplazamiento', + 'widget.type.text.name': 'Texto/HTML', + 'widget.type.text.desc': 'Texto o HTML personalizado', + 'widget.type.webpage.name': 'Página web', + 'widget.type.webpage.desc': 'Incrustar una página web', + 'widget.type.social.name': 'Feed social', + 'widget.type.social.desc': 'Feed de redes sociales', + 'widget.type.directory_board.name': 'Directorio', + 'widget.type.directory_board.desc': 'Directorio de inquilinos/salas con desplazamiento para vestíbulos', + 'widget.field.name': 'Nombre del widget', + 'widget.field.format': 'Formato', + 'widget.field.format_12h': '12 horas', + 'widget.field.format_24h': '24 horas', + 'widget.field.timezone': 'Zona horaria', + 'widget.field.font_size': 'Tamaño de fuente', + 'widget.field.font_size_px': 'Tamaño de fuente (px)', + 'widget.field.color': 'Color', + 'widget.field.background': 'Fondo', + 'widget.field.location': 'Ubicación', + 'widget.field.location_placeholder': 'Ciudad, Estado', + 'widget.field.units': 'Unidades', + 'widget.field.units_imperial': 'Imperial (°F)', + 'widget.field.units_metric': 'Métrico (°C)', + 'widget.field.feed_url': 'URL del feed', + 'widget.field.scroll_speed_seconds': 'Velocidad de desplazamiento (segundos)', + 'widget.field.max_items': 'Máx. elementos', + 'widget.field.html_content': 'Contenido HTML', + 'widget.field.css_optional': 'CSS (opcional)', + 'widget.field.url': 'URL', + 'widget.field.zoom_pct': 'Zoom (%)', + 'widget.field.refresh_interval': 'Intervalo de actualización (segundos, 0 = nunca)', + 'widget.field.platform': 'Plataforma', + 'widget.field.platform_twitter': 'Twitter/X', + 'widget.field.platform_instagram': 'Instagram', + 'widget.field.query': 'Consulta', + 'widget.field.query_placeholder': '@usuario o #hashtag', + 'widget.picker.default_title': 'Seleccionar imagen', + 'widget.picker.select_logo': 'Seleccionar logotipo', + 'widget.picker.select_bg_images': 'Seleccionar imágenes de fondo', + 'widget.picker.search': 'Buscar imágenes...', + 'widget.picker.no_matches': 'Sin coincidencias.', + 'widget.picker.no_images': 'No hay imágenes en tu biblioteca. Sube imágenes primero desde la Biblioteca de contenido.', + 'widget.picker.selected_count': '{n} seleccionadas', + 'widget.dir.title_label': 'Título', + 'widget.dir.title_placeholder': 'Almacén Lincoln', + 'widget.dir.logo_label': 'Logotipo (opcional)', + 'widget.dir.footer_text_label': 'Texto del pie', + 'widget.dir.footer_placeholder': 'Consultas de arrendamiento: Contacto...', + 'widget.dir.bg_images_label': 'Imágenes de fondo (opcional)', + 'widget.dir.bg_images_hint': 'Las imágenes se alternan cada 15 segundos al 30% de opacidad. Agrega varias para rotación.', + 'widget.dir.add_bg_image': '+ Agregar imagen de fondo', + 'widget.dir.theme': 'Tema', + 'widget.dir.theme_dark': 'Oscuro', + 'widget.dir.theme_light': 'Claro', + 'widget.dir.scroll_speed': 'Velocidad de desplazamiento', + 'widget.dir.speed_slow': 'Lenta', + 'widget.dir.speed_medium': 'Media', + 'widget.dir.speed_fast': 'Rápida', + 'widget.dir.columns': 'Columnas', + 'widget.dir.columns_auto': 'Auto', + 'widget.dir.categories': 'Categorías', + 'widget.dir.add_category': '+ Agregar categoría', + 'widget.dir.add_entry': '+ Agregar entrada', + 'widget.dir.empty_categories': 'Agrega tu primer piso o departamento para empezar', + 'widget.dir.no_entries': 'Aún no hay entradas', + 'widget.dir.entry': 'entrada', + 'widget.dir.entries': 'entradas', + 'widget.dir.collapse': 'Contraer', + 'widget.dir.expand': 'Expandir', + 'widget.dir.move_up': 'Subir', + 'widget.dir.move_down': 'Bajar', + 'widget.dir.delete_category': 'Eliminar categoría', + 'widget.dir.delete_entry': 'Eliminar entrada', + 'widget.dir.unnamed': '(sin nombre)', + 'widget.dir.confirm_delete_category': '¿Eliminar la categoría "{name}" y todas sus entradas?', + 'widget.dir.category_name_placeholder': 'p. ej. Primer piso', + 'widget.dir.entry_id_placeholder': '101', + 'widget.dir.entry_name_placeholder': 'Nombre del inquilino', + 'widget.dir.entry_subtitle_placeholder': 'Detalles (opcional)', + 'widget.dir.available': 'Disponible', + 'widget.dir.change': 'Cambiar', + 'widget.dir.choose_logo': 'Elegir logotipo', + 'widget.dir.remove_logo': 'Quitar', + 'widget.dir.no_bg_images': 'No se han seleccionado imágenes de fondo', + 'widget.dir.remove_bg': 'Quitar', }; diff --git a/frontend/js/i18n/fr.js b/frontend/js/i18n/fr.js index 556a2ac..06bbfc9 100644 --- a/frontend/js/i18n/fr.js +++ b/frontend/js/i18n/fr.js @@ -440,4 +440,113 @@ export default { 'settings.user.confirm': 'Confirmer ?', 'settings.user.count_one': '1 utilisateur inscrit', 'settings.user.count_other': '{n} utilisateurs inscrits', + + // Widgets + 'widget.title': 'Widgets', + 'widget.subtitle': 'Ajoutez du contenu dynamique à vos mises en page', + 'widget.help_tip': 'Éléments de contenu dynamique : horloges, météo, tickers RSS, texte, pages web et flux sociaux. Créez un widget puis attribuez-le à la liste d\'un appareil.', + 'widget.new_widget': 'Nouveau widget', + 'widget.configure': 'Configurer le widget', + 'widget.preview': 'Aperçu', + 'widget.preview_title': 'Aperçu', + 'widget.close': 'Fermer', + 'widget.edit_x': 'Modifier {type}', + 'widget.new_x': 'Nouveau {type}', + 'widget.empty_title': 'Aucun widget pour le moment', + 'widget.empty_desc': 'Créez un widget pour ajouter du contenu dynamique à vos mises en page.', + 'widget.this_widget': 'ce widget', + 'widget.confirm_delete': 'Supprimer « {name} » ? Cette action est irréversible.', + 'widget.toast.saved': 'Widget enregistré', + 'widget.toast.deleted': 'Widget supprimé', + 'widget.toast.preview_failed': 'Échec de l\'aperçu', + 'widget.type.clock.name': 'Horloge', + 'widget.type.clock.desc': 'Horloge numérique avec date', + 'widget.type.weather.name': 'Météo', + 'widget.type.weather.desc': 'Conditions météo actuelles', + 'widget.type.rss.name': 'Bandeau d\'actualités', + 'widget.type.rss.desc': 'Flux RSS défilant', + 'widget.type.text.name': 'Texte/HTML', + 'widget.type.text.desc': 'Texte ou contenu HTML personnalisé', + 'widget.type.webpage.name': 'Page web', + 'widget.type.webpage.desc': 'Intégrer une page web', + 'widget.type.social.name': 'Flux social', + 'widget.type.social.desc': 'Flux de réseaux sociaux', + 'widget.type.directory_board.name': 'Annuaire', + 'widget.type.directory_board.desc': 'Annuaire défilant des locataires/salles pour halls', + 'widget.field.name': 'Nom du widget', + 'widget.field.format': 'Format', + 'widget.field.format_12h': '12 heures', + 'widget.field.format_24h': '24 heures', + 'widget.field.timezone': 'Fuseau horaire', + 'widget.field.font_size': 'Taille de police', + 'widget.field.font_size_px': 'Taille de police (px)', + 'widget.field.color': 'Couleur', + 'widget.field.background': 'Fond', + 'widget.field.location': 'Emplacement', + 'widget.field.location_placeholder': 'Ville, Région', + 'widget.field.units': 'Unités', + 'widget.field.units_imperial': 'Impérial (°F)', + 'widget.field.units_metric': 'Métrique (°C)', + 'widget.field.feed_url': 'URL du flux', + 'widget.field.scroll_speed_seconds': 'Vitesse de défilement (secondes)', + 'widget.field.max_items': 'Éléments max', + 'widget.field.html_content': 'Contenu HTML', + 'widget.field.css_optional': 'CSS (facultatif)', + 'widget.field.url': 'URL', + 'widget.field.zoom_pct': 'Zoom (%)', + 'widget.field.refresh_interval': 'Intervalle d\'actualisation (secondes, 0 = jamais)', + 'widget.field.platform': 'Plateforme', + 'widget.field.platform_twitter': 'Twitter/X', + 'widget.field.platform_instagram': 'Instagram', + 'widget.field.query': 'Requête', + 'widget.field.query_placeholder': '@compte ou #hashtag', + 'widget.picker.default_title': 'Sélectionner une image', + 'widget.picker.select_logo': 'Sélectionner un logo', + 'widget.picker.select_bg_images': 'Sélectionner des images de fond', + 'widget.picker.search': 'Rechercher des images...', + 'widget.picker.no_matches': 'Aucun résultat.', + 'widget.picker.no_images': 'Aucune image dans votre bibliothèque. Téléversez d\'abord des images depuis la Bibliothèque.', + 'widget.picker.selected_count': '{n} sélectionnées', + 'widget.dir.title_label': 'Titre', + 'widget.dir.title_placeholder': 'Entrepôt Lincoln', + 'widget.dir.logo_label': 'Logo (facultatif)', + 'widget.dir.footer_text_label': 'Texte de pied', + 'widget.dir.footer_placeholder': 'Demandes de location : Contact...', + 'widget.dir.bg_images_label': 'Images de fond (facultatif)', + 'widget.dir.bg_images_hint': 'Les images alternent toutes les 15 secondes à 30 % d\'opacité. Ajoutez-en plusieurs pour la rotation.', + 'widget.dir.add_bg_image': '+ Ajouter une image de fond', + 'widget.dir.theme': 'Thème', + 'widget.dir.theme_dark': 'Sombre', + 'widget.dir.theme_light': 'Clair', + 'widget.dir.scroll_speed': 'Vitesse de défilement', + 'widget.dir.speed_slow': 'Lente', + 'widget.dir.speed_medium': 'Moyenne', + 'widget.dir.speed_fast': 'Rapide', + 'widget.dir.columns': 'Colonnes', + 'widget.dir.columns_auto': 'Auto', + 'widget.dir.categories': 'Catégories', + 'widget.dir.add_category': '+ Ajouter une catégorie', + 'widget.dir.add_entry': '+ Ajouter une entrée', + 'widget.dir.empty_categories': 'Ajoutez votre premier étage ou département pour commencer', + 'widget.dir.no_entries': 'Pas encore d\'entrées', + 'widget.dir.entry': 'entrée', + 'widget.dir.entries': 'entrées', + 'widget.dir.collapse': 'Réduire', + 'widget.dir.expand': 'Développer', + 'widget.dir.move_up': 'Monter', + 'widget.dir.move_down': 'Descendre', + 'widget.dir.delete_category': 'Supprimer la catégorie', + 'widget.dir.delete_entry': 'Supprimer l\'entrée', + 'widget.dir.unnamed': '(sans nom)', + 'widget.dir.confirm_delete_category': 'Supprimer la catégorie « {name} » et toutes ses entrées ?', + 'widget.dir.category_name_placeholder': 'ex. Premier étage', + 'widget.dir.entry_id_placeholder': '101', + 'widget.dir.entry_name_placeholder': 'Nom du locataire', + 'widget.dir.entry_subtitle_placeholder': 'Détails (facultatif)', + 'widget.dir.available': 'Disponible', + 'widget.dir.change': 'Changer', + 'widget.dir.choose_logo': 'Choisir un logo', + 'widget.dir.remove_logo': 'Retirer', + 'widget.dir.no_bg_images': 'Aucune image de fond sélectionnée', + 'widget.dir.remove_bg': 'Retirer', }; diff --git a/frontend/js/i18n/pt.js b/frontend/js/i18n/pt.js index 623d612..5b5d078 100644 --- a/frontend/js/i18n/pt.js +++ b/frontend/js/i18n/pt.js @@ -440,4 +440,113 @@ export default { 'settings.user.confirm': 'Confirmar?', 'settings.user.count_one': '1 usuário registrado', 'settings.user.count_other': '{n} usuários registrados', + + // Widgets + 'widget.title': 'Widgets', + 'widget.subtitle': 'Adicione conteúdo dinâmico aos seus layouts', + 'widget.help_tip': 'Elementos de conteúdo dinâmico: relógios ao vivo, clima, tickers RSS, texto, páginas web e feeds sociais. Crie um widget e atribua à playlist de um dispositivo.', + 'widget.new_widget': 'Novo widget', + 'widget.configure': 'Configurar widget', + 'widget.preview': 'Pré-visualizar', + 'widget.preview_title': 'Pré-visualização', + 'widget.close': 'Fechar', + 'widget.edit_x': 'Editar {type}', + 'widget.new_x': 'Novo {type}', + 'widget.empty_title': 'Nenhum widget ainda', + 'widget.empty_desc': 'Crie um widget para adicionar conteúdo dinâmico aos seus layouts.', + 'widget.this_widget': 'este widget', + 'widget.confirm_delete': 'Excluir "{name}"? Isso não pode ser desfeito.', + 'widget.toast.saved': 'Widget salvo', + 'widget.toast.deleted': 'Widget excluído', + 'widget.toast.preview_failed': 'Falha na pré-visualização', + 'widget.type.clock.name': 'Relógio', + 'widget.type.clock.desc': 'Relógio digital com data', + 'widget.type.weather.name': 'Clima', + 'widget.type.weather.desc': 'Condições climáticas atuais', + 'widget.type.rss.name': 'Ticker de notícias', + 'widget.type.rss.desc': 'Feed RSS com rolagem', + 'widget.type.text.name': 'Texto/HTML', + 'widget.type.text.desc': 'Texto ou conteúdo HTML personalizado', + 'widget.type.webpage.name': 'Página web', + 'widget.type.webpage.desc': 'Incorporar uma página web', + 'widget.type.social.name': 'Feed social', + 'widget.type.social.desc': 'Feed de redes sociais', + 'widget.type.directory_board.name': 'Diretório', + 'widget.type.directory_board.desc': 'Diretório rolante de inquilinos/salas para lobbies', + 'widget.field.name': 'Nome do widget', + 'widget.field.format': 'Formato', + 'widget.field.format_12h': '12 horas', + 'widget.field.format_24h': '24 horas', + 'widget.field.timezone': 'Fuso horário', + 'widget.field.font_size': 'Tamanho da fonte', + 'widget.field.font_size_px': 'Tamanho da fonte (px)', + 'widget.field.color': 'Cor', + 'widget.field.background': 'Fundo', + 'widget.field.location': 'Local', + 'widget.field.location_placeholder': 'Cidade, Estado', + 'widget.field.units': 'Unidades', + 'widget.field.units_imperial': 'Imperial (°F)', + 'widget.field.units_metric': 'Métrico (°C)', + 'widget.field.feed_url': 'URL do feed', + 'widget.field.scroll_speed_seconds': 'Velocidade de rolagem (segundos)', + 'widget.field.max_items': 'Máx. itens', + 'widget.field.html_content': 'Conteúdo HTML', + 'widget.field.css_optional': 'CSS (opcional)', + 'widget.field.url': 'URL', + 'widget.field.zoom_pct': 'Zoom (%)', + 'widget.field.refresh_interval': 'Intervalo de atualização (segundos, 0 = nunca)', + 'widget.field.platform': 'Plataforma', + 'widget.field.platform_twitter': 'Twitter/X', + 'widget.field.platform_instagram': 'Instagram', + 'widget.field.query': 'Consulta', + 'widget.field.query_placeholder': '@usuario ou #hashtag', + 'widget.picker.default_title': 'Selecionar imagem', + 'widget.picker.select_logo': 'Selecionar logotipo', + 'widget.picker.select_bg_images': 'Selecionar imagens de fundo', + 'widget.picker.search': 'Buscar imagens...', + 'widget.picker.no_matches': 'Sem correspondências.', + 'widget.picker.no_images': 'Sem imagens na biblioteca. Envie imagens primeiro pela Biblioteca de conteúdo.', + 'widget.picker.selected_count': '{n} selecionadas', + 'widget.dir.title_label': 'Título', + 'widget.dir.title_placeholder': 'Armazém Lincoln', + 'widget.dir.logo_label': 'Logotipo (opcional)', + 'widget.dir.footer_text_label': 'Texto do rodapé', + 'widget.dir.footer_placeholder': 'Consultas de locação: Contato...', + 'widget.dir.bg_images_label': 'Imagens de fundo (opcional)', + 'widget.dir.bg_images_hint': 'As imagens alternam a cada 15 segundos com 30% de opacidade. Adicione várias para rotação.', + 'widget.dir.add_bg_image': '+ Adicionar imagem de fundo', + 'widget.dir.theme': 'Tema', + 'widget.dir.theme_dark': 'Escuro', + 'widget.dir.theme_light': 'Claro', + 'widget.dir.scroll_speed': 'Velocidade de rolagem', + 'widget.dir.speed_slow': 'Lenta', + 'widget.dir.speed_medium': 'Média', + 'widget.dir.speed_fast': 'Rápida', + 'widget.dir.columns': 'Colunas', + 'widget.dir.columns_auto': 'Auto', + 'widget.dir.categories': 'Categorias', + 'widget.dir.add_category': '+ Adicionar categoria', + 'widget.dir.add_entry': '+ Adicionar entrada', + 'widget.dir.empty_categories': 'Adicione seu primeiro andar ou departamento para começar', + 'widget.dir.no_entries': 'Sem entradas ainda', + 'widget.dir.entry': 'entrada', + 'widget.dir.entries': 'entradas', + 'widget.dir.collapse': 'Recolher', + 'widget.dir.expand': 'Expandir', + 'widget.dir.move_up': 'Mover para cima', + 'widget.dir.move_down': 'Mover para baixo', + 'widget.dir.delete_category': 'Excluir categoria', + 'widget.dir.delete_entry': 'Excluir entrada', + 'widget.dir.unnamed': '(sem nome)', + 'widget.dir.confirm_delete_category': 'Excluir a categoria "{name}" e todas as entradas?', + 'widget.dir.category_name_placeholder': 'ex. Primeiro andar', + 'widget.dir.entry_id_placeholder': '101', + 'widget.dir.entry_name_placeholder': 'Nome do inquilino', + 'widget.dir.entry_subtitle_placeholder': 'Detalhes (opcional)', + 'widget.dir.available': 'Disponível', + 'widget.dir.change': 'Alterar', + 'widget.dir.choose_logo': 'Escolher logotipo', + 'widget.dir.remove_logo': 'Remover', + 'widget.dir.no_bg_images': 'Nenhuma imagem de fundo selecionada', + 'widget.dir.remove_bg': 'Remover', }; diff --git a/frontend/js/views/widgets.js b/frontend/js/views/widgets.js index c4796c7..c7e17d2 100644 --- a/frontend/js/views/widgets.js +++ b/frontend/js/views/widgets.js @@ -1,35 +1,41 @@ import { showToast } from '../components/toast.js'; +import { t } from '../i18n.js'; const API = (url, opts = {}) => fetch('/api' + url, { headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${localStorage.getItem('token')}`, ...opts.headers }, ...opts }).then(r => r.json()); -const WIDGET_TYPES = [ - { id: 'clock', name: 'Clock', icon: '🕓', desc: 'Digital clock with date' }, - { id: 'weather', name: 'Weather', icon: '⛅', desc: 'Current weather conditions' }, - { id: 'rss', name: 'News Ticker', icon: '📰', desc: 'Scrolling RSS feed' }, - { id: 'text', name: 'Text/HTML', icon: '📝', desc: 'Custom text or HTML content' }, - { id: 'webpage', name: 'Webpage', icon: '🌐', desc: 'Embed a webpage' }, - { id: 'social', name: 'Social Feed', icon: '💬', desc: 'Social media feed' }, - { id: 'directory-board', name: 'Directory Board', icon: '🏢', desc: 'Scrolling tenant/room directory for lobbies' }, -]; +// Widget type ids only — name + desc are looked up via t() so they switch +// language with the rest of the UI. +const WIDGET_TYPES = ['clock', 'weather', 'rss', 'text', 'webpage', 'social', 'directory-board']; +const WIDGET_ICONS = { + clock: '🕓', + weather: '⛅', + rss: '📰', + text: '📝', + webpage: '🌐', + social: '💬', + 'directory-board': '🏢', +}; +const widgetTypeName = (id) => t(`widget.type.${id.replace(/-/g, '_')}.name`); +const widgetTypeDesc = (id) => t(`widget.type.${id.replace(/-/g, '_')}.desc`); function escAttr(s) { return String(s == null ? '' : s).replace(/&/g, '&').replace(/"/g, '"').replace(//g, '>'); } -function openContentPicker({ multiple = false, title = 'Select Image' } = {}) { +function openContentPicker({ multiple = false, title } = {}) { return new Promise(async (resolve) => { const overlay = document.createElement('div'); overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.6);display:flex;align-items:center;justify-content:center;z-index:10000;padding:16px'; overlay.innerHTML = `
-

${title}

- +

${title || t('widget.picker.default_title')}

+
- - ${multiple ? '' : ''} + + ${multiple ? `` : ''}
`; @@ -43,7 +49,7 @@ function openContentPicker({ multiple = false, title = 'Select Image' } = {}) { const resolveUrl = (item) => item.remote_url || `/api/content/${item.id}/file`; const updateCount = () => { const el = overlay.querySelector('#cpSelCount'); - if (el && multiple) el.textContent = `${selected.size} selected`; + if (el && multiple) el.textContent = t('widget.picker.selected_count', { n: selected.size }); }; function renderList() { @@ -51,7 +57,7 @@ function openContentPicker({ multiple = false, title = 'Select Image' } = {}) { const filtered = items.filter(i => (i.filename || '').toLowerCase().includes(q)); const list = overlay.querySelector('#cpList'); if (!filtered.length) { - list.innerHTML = `
${items.length ? 'No matches.' : 'No images in your content library. Upload images first from Content Library.'}
`; + list.innerHTML = `
${items.length ? t('widget.picker.no_matches') : t('widget.picker.no_images')}
`; return; } list.innerHTML = `
${ @@ -105,8 +111,8 @@ function showPreviewModal(html) { overlay.innerHTML = `
- Preview - + ${t('widget.preview_title')} +
`; @@ -128,19 +134,19 @@ function showPreviewModal(html) { export async function render(container) { container.innerHTML = `