diff --git a/frontend/js/i18n/de.js b/frontend/js/i18n/de.js index 5e98fe6..d401a30 100644 --- a/frontend/js/i18n/de.js +++ b/frontend/js/i18n/de.js @@ -549,4 +549,80 @@ export default { 'widget.dir.remove_logo': 'Entfernen', 'widget.dir.no_bg_images': 'Keine Hintergrundbilder ausgewählt', 'widget.dir.remove_bg': 'Entfernen', + + // Designer + 'designer.title': 'Inhaltsdesigner', + 'designer.subtitle': 'Erstellen Sie dynamischen Anzeigeinhalt', + 'designer.help_tip': 'Erstellen Sie individuelle Anzeigen mit Live-Elementen: Uhren, Wetter, RSS-Ticker, Countdowns, QR-Codes. Als Widget veröffentlichen oder als PNG exportieren.', + 'designer.load_design': 'Design laden', + 'designer.export_png': 'PNG exportieren', + 'designer.publish': 'In Bibliothek veröffentlichen', + 'designer.preview_hint': 'Klicken Sie auf Elemente zur Auswahl. Ziehen zum Verschieben. Live-Vorschau aktualisiert in Echtzeit.', + 'designer.add_element': 'Element hinzufügen', + 'designer.background': 'Hintergrund', + 'designer.bg_image': 'Bild', + 'designer.properties': 'Eigenschaften', + 'designer.layers': 'Ebenen', + 'designer.no_elements': 'Noch keine Elemente', + 'designer.save_design_file': 'Design-Datei speichern', + 'designer.qr_label': 'QR-CODE', + 'designer.loading_news': 'Nachrichten werden geladen...', + 'designer.no_items': 'Keine Einträge', + 'designer.feed_unavailable': 'Feed nicht verfügbar', + 'designer.countdown_now': 'JETZT!', + 'designer.widget_name': 'Design {date}', + 'designer.el.text': 'Text', + 'designer.el.heading': 'Überschrift', + 'designer.el.image': 'Bild', + 'designer.el.video': 'Video', + 'designer.el.clock': 'Uhr', + 'designer.el.date': 'Datum', + 'designer.el.weather': 'Wetter', + 'designer.el.ticker': 'Ticker', + 'designer.el.shape': 'Form', + 'designer.el.qr': 'QR-Code', + 'designer.el.countdown': 'Countdown', + 'designer.el.webpage': 'Webseite', + 'designer.bg.black': 'Schwarz', + 'designer.bg.dark_blue': 'Dunkelblau', + 'designer.bg.dark_gradient': 'Dunkler Verlauf', + 'designer.bg.blue_gradient': 'Blauer Verlauf', + 'designer.bg.sunset': 'Sonnenuntergang', + 'designer.bg.ocean': 'Ozean', + 'designer.bg.forest': 'Wald', + 'designer.bg.dark_red': 'Dunkelrot', + 'designer.bg.white': 'Weiß', + 'designer.default.text': 'Ihr Text hier', + 'designer.default.heading': 'ÜBERSCHRIFT', + 'designer.default.coming_soon': 'Demnächst', + 'designer.prompt.video_url': 'Video-URL (MP4):', + 'designer.prompt.weather_location': 'Stadt, Land:', + 'designer.prompt.rss_url': 'RSS-Feed-URL:', + 'designer.prompt.qr_url': 'QR-Code-URL:', + 'designer.prompt.countdown_date': 'Zieldatum (JJJJ-MM-TT):', + 'designer.prompt.webpage_url': 'Webseiten-URL:', + 'designer.prop.text': 'Text', + 'designer.prop.size': 'Größe', + 'designer.prop.font': 'Schriftart', + 'designer.prop.color': 'Farbe', + 'designer.prop.bold': 'Fett', + 'designer.prop.shadow': 'Schatten', + 'designer.prop.format': 'Format', + 'designer.prop.show_seconds': 'Sekunden anzeigen', + 'designer.prop.muted': 'Stumm', + 'designer.prop.loop': 'Schleife', + 'designer.prop.opacity': 'Deckkraft', + 'designer.prop.shape': 'Form', + 'designer.prop.location': 'Standort', + 'designer.prop.feed_url': 'Feed-URL', + 'designer.prop.speed': 'Geschwindigkeit (Sekunden)', + 'designer.prop.text_color': 'Textfarbe', + 'designer.prop.bg_color': 'Hintergrundfarbe', + 'designer.prop.target_date': 'Zieldatum', + 'designer.prop.label': 'Beschriftung', + 'designer.toast.published': 'Als Widget veröffentlicht! Weisen Sie es einer Layout-Zone zu.', + 'designer.toast.publish_failed': 'Veröffentlichung fehlgeschlagen', + 'designer.toast.export_failed': 'Export fehlgeschlagen: {error}', + 'designer.toast.loaded': 'Design geladen', + 'designer.toast.invalid_file': 'Ungültige Design-Datei', }; diff --git a/frontend/js/i18n/en.js b/frontend/js/i18n/en.js index 8a1ff86..1839253 100644 --- a/frontend/js/i18n/en.js +++ b/frontend/js/i18n/en.js @@ -580,4 +580,85 @@ export default { 'widget.dir.remove_logo': 'Remove', 'widget.dir.no_bg_images': 'No background images selected', 'widget.dir.remove_bg': 'Remove', + + // Designer + 'designer.title': 'Content Designer', + 'designer.subtitle': 'Create dynamic signage content', + 'designer.help_tip': 'Create custom signage with live elements: clocks, weather, RSS tickers, countdowns, QR codes. Publish as a widget or export as PNG.', + 'designer.load_design': 'Load Design', + 'designer.export_png': 'Export PNG', + 'designer.publish': 'Publish to Library', + 'designer.preview_hint': 'Click elements to select. Drag to reposition. Live preview updates in real-time.', + 'designer.add_element': 'Add Element', + 'designer.background': 'Background', + 'designer.bg_image': 'Image', + 'designer.properties': 'Properties', + 'designer.layers': 'Layers', + 'designer.no_elements': 'No elements yet', + 'designer.save_design_file': 'Save Design File', + 'designer.qr_label': 'QR CODE', + 'designer.loading_news': 'Loading news...', + 'designer.no_items': 'No items', + 'designer.feed_unavailable': 'Feed unavailable', + 'designer.countdown_now': 'NOW!', + 'designer.widget_name': 'Design {date}', + // Element buttons + 'designer.el.text': 'Text', + 'designer.el.heading': 'Heading', + 'designer.el.image': 'Image', + 'designer.el.video': 'Video', + 'designer.el.clock': 'Clock', + 'designer.el.date': 'Date', + 'designer.el.weather': 'Weather', + 'designer.el.ticker': 'Ticker', + 'designer.el.shape': 'Shape', + 'designer.el.qr': 'QR Code', + 'designer.el.countdown': 'Countdown', + 'designer.el.webpage': 'Webpage', + // Backgrounds + 'designer.bg.black': 'Black', + 'designer.bg.dark_blue': 'Dark Blue', + 'designer.bg.dark_gradient': 'Dark Gradient', + 'designer.bg.blue_gradient': 'Blue Gradient', + 'designer.bg.sunset': 'Sunset', + 'designer.bg.ocean': 'Ocean', + 'designer.bg.forest': 'Forest', + 'designer.bg.dark_red': 'Dark Red', + 'designer.bg.white': 'White', + // Defaults / prompts + 'designer.default.text': 'Your text here', + 'designer.default.heading': 'HEADING', + 'designer.default.coming_soon': 'Coming Soon', + 'designer.prompt.video_url': 'Video URL (MP4):', + 'designer.prompt.weather_location': 'City, State:', + 'designer.prompt.rss_url': 'RSS Feed URL:', + 'designer.prompt.qr_url': 'QR Code URL:', + 'designer.prompt.countdown_date': 'Target date (YYYY-MM-DD):', + 'designer.prompt.webpage_url': 'Webpage URL:', + // Properties + 'designer.prop.text': 'Text', + 'designer.prop.size': 'Size', + 'designer.prop.font': 'Font', + 'designer.prop.color': 'Color', + 'designer.prop.bold': 'Bold', + 'designer.prop.shadow': 'Shadow', + 'designer.prop.format': 'Format', + 'designer.prop.show_seconds': 'Show seconds', + 'designer.prop.muted': 'Muted', + 'designer.prop.loop': 'Loop', + 'designer.prop.opacity': 'Opacity', + 'designer.prop.shape': 'Shape', + 'designer.prop.location': 'Location', + 'designer.prop.feed_url': 'Feed URL', + 'designer.prop.speed': 'Speed (seconds)', + 'designer.prop.text_color': 'Text Color', + 'designer.prop.bg_color': 'BG Color', + 'designer.prop.target_date': 'Target Date', + 'designer.prop.label': 'Label', + // Toasts + 'designer.toast.published': 'Published as widget! Assign it to a layout zone.', + 'designer.toast.publish_failed': 'Publish failed', + 'designer.toast.export_failed': 'Export failed: {error}', + 'designer.toast.loaded': 'Design loaded', + 'designer.toast.invalid_file': 'Invalid design file', }; diff --git a/frontend/js/i18n/es.js b/frontend/js/i18n/es.js index 6d4f295..f085d54 100644 --- a/frontend/js/i18n/es.js +++ b/frontend/js/i18n/es.js @@ -548,4 +548,80 @@ export default { 'widget.dir.remove_logo': 'Quitar', 'widget.dir.no_bg_images': 'No se han seleccionado imágenes de fondo', 'widget.dir.remove_bg': 'Quitar', + + // Designer + 'designer.title': 'Diseñador de contenido', + 'designer.subtitle': 'Crea contenido dinámico para señalización', + 'designer.help_tip': 'Crea señalización personalizada con elementos en vivo: relojes, clima, tickers RSS, cuentas regresivas, códigos QR. Publica como widget o exporta como PNG.', + 'designer.load_design': 'Cargar diseño', + 'designer.export_png': 'Exportar PNG', + 'designer.publish': 'Publicar en biblioteca', + 'designer.preview_hint': 'Haz clic en los elementos para seleccionar. Arrastra para reposicionar. La vista previa se actualiza en tiempo real.', + 'designer.add_element': 'Agregar elemento', + 'designer.background': 'Fondo', + 'designer.bg_image': 'Imagen', + 'designer.properties': 'Propiedades', + 'designer.layers': 'Capas', + 'designer.no_elements': 'Aún no hay elementos', + 'designer.save_design_file': 'Guardar archivo de diseño', + 'designer.qr_label': 'CÓDIGO QR', + 'designer.loading_news': 'Cargando noticias...', + 'designer.no_items': 'Sin elementos', + 'designer.feed_unavailable': 'Feed no disponible', + 'designer.countdown_now': '¡AHORA!', + 'designer.widget_name': 'Diseño {date}', + 'designer.el.text': 'Texto', + 'designer.el.heading': 'Título', + 'designer.el.image': 'Imagen', + 'designer.el.video': 'Video', + 'designer.el.clock': 'Reloj', + 'designer.el.date': 'Fecha', + 'designer.el.weather': 'Clima', + 'designer.el.ticker': 'Ticker', + 'designer.el.shape': 'Forma', + 'designer.el.qr': 'Código QR', + 'designer.el.countdown': 'Cuenta regresiva', + 'designer.el.webpage': 'Página web', + 'designer.bg.black': 'Negro', + 'designer.bg.dark_blue': 'Azul oscuro', + 'designer.bg.dark_gradient': 'Gradiente oscuro', + 'designer.bg.blue_gradient': 'Gradiente azul', + 'designer.bg.sunset': 'Atardecer', + 'designer.bg.ocean': 'Océano', + 'designer.bg.forest': 'Bosque', + 'designer.bg.dark_red': 'Rojo oscuro', + 'designer.bg.white': 'Blanco', + 'designer.default.text': 'Tu texto aquí', + 'designer.default.heading': 'TÍTULO', + 'designer.default.coming_soon': 'Próximamente', + 'designer.prompt.video_url': 'URL del video (MP4):', + 'designer.prompt.weather_location': 'Ciudad, Estado:', + 'designer.prompt.rss_url': 'URL del feed RSS:', + 'designer.prompt.qr_url': 'URL del código QR:', + 'designer.prompt.countdown_date': 'Fecha objetivo (AAAA-MM-DD):', + 'designer.prompt.webpage_url': 'URL de la página web:', + 'designer.prop.text': 'Texto', + 'designer.prop.size': 'Tamaño', + 'designer.prop.font': 'Fuente', + 'designer.prop.color': 'Color', + 'designer.prop.bold': 'Negrita', + 'designer.prop.shadow': 'Sombra', + 'designer.prop.format': 'Formato', + 'designer.prop.show_seconds': 'Mostrar segundos', + 'designer.prop.muted': 'Silenciado', + 'designer.prop.loop': 'Bucle', + 'designer.prop.opacity': 'Opacidad', + 'designer.prop.shape': 'Forma', + 'designer.prop.location': 'Ubicación', + 'designer.prop.feed_url': 'URL del feed', + 'designer.prop.speed': 'Velocidad (segundos)', + 'designer.prop.text_color': 'Color del texto', + 'designer.prop.bg_color': 'Color de fondo', + 'designer.prop.target_date': 'Fecha objetivo', + 'designer.prop.label': 'Etiqueta', + 'designer.toast.published': '¡Publicado como widget! Asígnalo a una zona de diseño.', + 'designer.toast.publish_failed': 'Falló la publicación', + 'designer.toast.export_failed': 'Falló la exportación: {error}', + 'designer.toast.loaded': 'Diseño cargado', + 'designer.toast.invalid_file': 'Archivo de diseño no válido', }; diff --git a/frontend/js/i18n/fr.js b/frontend/js/i18n/fr.js index 06bbfc9..38d039b 100644 --- a/frontend/js/i18n/fr.js +++ b/frontend/js/i18n/fr.js @@ -549,4 +549,80 @@ export default { 'widget.dir.remove_logo': 'Retirer', 'widget.dir.no_bg_images': 'Aucune image de fond sélectionnée', 'widget.dir.remove_bg': 'Retirer', + + // Designer + 'designer.title': 'Concepteur de contenu', + 'designer.subtitle': 'Créez du contenu d\'affichage dynamique', + 'designer.help_tip': 'Créez de l\'affichage personnalisé avec des éléments en direct : horloges, météo, tickers RSS, comptes à rebours, codes QR. Publiez comme widget ou exportez en PNG.', + 'designer.load_design': 'Charger un design', + 'designer.export_png': 'Exporter PNG', + 'designer.publish': 'Publier dans la bibliothèque', + 'designer.preview_hint': 'Cliquez sur les éléments pour les sélectionner. Glissez pour les repositionner. L\'aperçu se met à jour en temps réel.', + 'designer.add_element': 'Ajouter un élément', + 'designer.background': 'Fond', + 'designer.bg_image': 'Image', + 'designer.properties': 'Propriétés', + 'designer.layers': 'Calques', + 'designer.no_elements': 'Pas encore d\'éléments', + 'designer.save_design_file': 'Enregistrer le fichier de design', + 'designer.qr_label': 'CODE QR', + 'designer.loading_news': 'Chargement des actualités...', + 'designer.no_items': 'Aucun élément', + 'designer.feed_unavailable': 'Flux indisponible', + 'designer.countdown_now': 'MAINTENANT !', + 'designer.widget_name': 'Design {date}', + 'designer.el.text': 'Texte', + 'designer.el.heading': 'Titre', + 'designer.el.image': 'Image', + 'designer.el.video': 'Vidéo', + 'designer.el.clock': 'Horloge', + 'designer.el.date': 'Date', + 'designer.el.weather': 'Météo', + 'designer.el.ticker': 'Bandeau', + 'designer.el.shape': 'Forme', + 'designer.el.qr': 'Code QR', + 'designer.el.countdown': 'Compte à rebours', + 'designer.el.webpage': 'Page web', + 'designer.bg.black': 'Noir', + 'designer.bg.dark_blue': 'Bleu sombre', + 'designer.bg.dark_gradient': 'Dégradé sombre', + 'designer.bg.blue_gradient': 'Dégradé bleu', + 'designer.bg.sunset': 'Coucher de soleil', + 'designer.bg.ocean': 'Océan', + 'designer.bg.forest': 'Forêt', + 'designer.bg.dark_red': 'Rouge sombre', + 'designer.bg.white': 'Blanc', + 'designer.default.text': 'Votre texte ici', + 'designer.default.heading': 'TITRE', + 'designer.default.coming_soon': 'Bientôt', + 'designer.prompt.video_url': 'URL de la vidéo (MP4) :', + 'designer.prompt.weather_location': 'Ville, Région :', + 'designer.prompt.rss_url': 'URL du flux RSS :', + 'designer.prompt.qr_url': 'URL du code QR :', + 'designer.prompt.countdown_date': 'Date cible (AAAA-MM-JJ) :', + 'designer.prompt.webpage_url': 'URL de la page web :', + 'designer.prop.text': 'Texte', + 'designer.prop.size': 'Taille', + 'designer.prop.font': 'Police', + 'designer.prop.color': 'Couleur', + 'designer.prop.bold': 'Gras', + 'designer.prop.shadow': 'Ombre', + 'designer.prop.format': 'Format', + 'designer.prop.show_seconds': 'Afficher les secondes', + 'designer.prop.muted': 'Muet', + 'designer.prop.loop': 'Boucle', + 'designer.prop.opacity': 'Opacité', + 'designer.prop.shape': 'Forme', + 'designer.prop.location': 'Emplacement', + 'designer.prop.feed_url': 'URL du flux', + 'designer.prop.speed': 'Vitesse (secondes)', + 'designer.prop.text_color': 'Couleur du texte', + 'designer.prop.bg_color': 'Couleur de fond', + 'designer.prop.target_date': 'Date cible', + 'designer.prop.label': 'Étiquette', + 'designer.toast.published': 'Publié comme widget ! Attribuez-le à une zone de mise en page.', + 'designer.toast.publish_failed': 'Échec de la publication', + 'designer.toast.export_failed': 'Échec de l\'export : {error}', + 'designer.toast.loaded': 'Design chargé', + 'designer.toast.invalid_file': 'Fichier de design invalide', }; diff --git a/frontend/js/i18n/pt.js b/frontend/js/i18n/pt.js index 5b5d078..8171d55 100644 --- a/frontend/js/i18n/pt.js +++ b/frontend/js/i18n/pt.js @@ -549,4 +549,80 @@ export default { 'widget.dir.remove_logo': 'Remover', 'widget.dir.no_bg_images': 'Nenhuma imagem de fundo selecionada', 'widget.dir.remove_bg': 'Remover', + + // Designer + 'designer.title': 'Designer de conteúdo', + 'designer.subtitle': 'Crie conteúdo dinâmico de sinalização', + 'designer.help_tip': 'Crie sinalização personalizada com elementos ao vivo: relógios, clima, tickers RSS, contagens regressivas, códigos QR. Publique como widget ou exporte como PNG.', + 'designer.load_design': 'Carregar design', + 'designer.export_png': 'Exportar PNG', + 'designer.publish': 'Publicar na biblioteca', + 'designer.preview_hint': 'Clique em elementos para selecionar. Arraste para reposicionar. Pré-visualização atualiza em tempo real.', + 'designer.add_element': 'Adicionar elemento', + 'designer.background': 'Fundo', + 'designer.bg_image': 'Imagem', + 'designer.properties': 'Propriedades', + 'designer.layers': 'Camadas', + 'designer.no_elements': 'Sem elementos ainda', + 'designer.save_design_file': 'Salvar arquivo de design', + 'designer.qr_label': 'CÓDIGO QR', + 'designer.loading_news': 'Carregando notícias...', + 'designer.no_items': 'Sem itens', + 'designer.feed_unavailable': 'Feed indisponível', + 'designer.countdown_now': 'AGORA!', + 'designer.widget_name': 'Design {date}', + 'designer.el.text': 'Texto', + 'designer.el.heading': 'Título', + 'designer.el.image': 'Imagem', + 'designer.el.video': 'Vídeo', + 'designer.el.clock': 'Relógio', + 'designer.el.date': 'Data', + 'designer.el.weather': 'Clima', + 'designer.el.ticker': 'Ticker', + 'designer.el.shape': 'Forma', + 'designer.el.qr': 'Código QR', + 'designer.el.countdown': 'Contagem regressiva', + 'designer.el.webpage': 'Página web', + 'designer.bg.black': 'Preto', + 'designer.bg.dark_blue': 'Azul escuro', + 'designer.bg.dark_gradient': 'Gradiente escuro', + 'designer.bg.blue_gradient': 'Gradiente azul', + 'designer.bg.sunset': 'Pôr do sol', + 'designer.bg.ocean': 'Oceano', + 'designer.bg.forest': 'Floresta', + 'designer.bg.dark_red': 'Vermelho escuro', + 'designer.bg.white': 'Branco', + 'designer.default.text': 'Seu texto aqui', + 'designer.default.heading': 'TÍTULO', + 'designer.default.coming_soon': 'Em breve', + 'designer.prompt.video_url': 'URL do vídeo (MP4):', + 'designer.prompt.weather_location': 'Cidade, Estado:', + 'designer.prompt.rss_url': 'URL do feed RSS:', + 'designer.prompt.qr_url': 'URL do código QR:', + 'designer.prompt.countdown_date': 'Data alvo (AAAA-MM-DD):', + 'designer.prompt.webpage_url': 'URL da página web:', + 'designer.prop.text': 'Texto', + 'designer.prop.size': 'Tamanho', + 'designer.prop.font': 'Fonte', + 'designer.prop.color': 'Cor', + 'designer.prop.bold': 'Negrito', + 'designer.prop.shadow': 'Sombra', + 'designer.prop.format': 'Formato', + 'designer.prop.show_seconds': 'Mostrar segundos', + 'designer.prop.muted': 'Mudo', + 'designer.prop.loop': 'Loop', + 'designer.prop.opacity': 'Opacidade', + 'designer.prop.shape': 'Forma', + 'designer.prop.location': 'Local', + 'designer.prop.feed_url': 'URL do feed', + 'designer.prop.speed': 'Velocidade (segundos)', + 'designer.prop.text_color': 'Cor do texto', + 'designer.prop.bg_color': 'Cor de fundo', + 'designer.prop.target_date': 'Data alvo', + 'designer.prop.label': 'Rótulo', + 'designer.toast.published': 'Publicado como widget! Atribua a uma zona de layout.', + 'designer.toast.publish_failed': 'Falha ao publicar', + 'designer.toast.export_failed': 'Falha ao exportar: {error}', + 'designer.toast.loaded': 'Design carregado', + 'designer.toast.invalid_file': 'Arquivo de design inválido', }; diff --git a/frontend/js/views/designer.js b/frontend/js/views/designer.js index 72b2ade..50988d5 100644 --- a/frontend/js/views/designer.js +++ b/frontend/js/views/designer.js @@ -1,16 +1,19 @@ import { api } from '../api.js'; import { showToast } from '../components/toast.js'; +import { t } from '../i18n.js'; +// Background swatches: ids resolve to translated names; values are the actual +// CSS to apply. const BACKGROUNDS = [ - { name: 'Black', value: '#000000' }, - { name: 'Dark Blue', value: '#0f172a' }, - { name: 'Dark Gradient', value: 'linear-gradient(135deg, #0c0c0c 0%, #1a1a2e 50%, #16213e 100%)' }, - { name: 'Blue Gradient', value: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' }, - { name: 'Sunset', value: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)' }, - { name: 'Ocean', value: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)' }, - { name: 'Forest', value: 'linear-gradient(135deg, #134e5e 0%, #71b280 100%)' }, - { name: 'Dark Red', value: 'linear-gradient(135deg, #200122 0%, #6f0000 100%)' }, - { name: 'White', value: '#FFFFFF' }, + { id: 'black', value: '#000000' }, + { id: 'dark_blue', value: '#0f172a' }, + { id: 'dark_gradient', value: 'linear-gradient(135deg, #0c0c0c 0%, #1a1a2e 50%, #16213e 100%)' }, + { id: 'blue_gradient', value: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' }, + { id: 'sunset', value: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)' }, + { id: 'ocean', value: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)' }, + { id: 'forest', value: 'linear-gradient(135deg, #134e5e 0%, #71b280 100%)' }, + { id: 'dark_red', value: 'linear-gradient(135deg, #200122 0%, #6f0000 100%)' }, + { id: 'white', value: '#FFFFFF' }, ]; const FONTS = ['Arial', 'Helvetica', 'Georgia', 'Impact', 'Verdana', 'Trebuchet MS', 'Courier New', 'Times New Roman']; @@ -30,11 +33,11 @@ export function render(container) { container.innerHTML = `
@@ -43,51 +46,51 @@ export function render(container) {
-

Click elements to select. Drag to reposition. Live preview updates in real-time.

+

${t('designer.preview_hint')}

-

Add Element

+

${t('designer.add_element')}

- - - - - - - - - - - - + + + + + + + + + + + +
-

Background

+

${t('designer.background')}

- ${BACKGROUNDS.map(b => `
`).join('')} + ${BACKGROUNDS.map(b => `
`).join('')}
- +
-

Layers

+

${t('designer.layers')}

@@ -108,8 +111,8 @@ export function render(container) { }; // Add element handlers - document.getElementById('addText').onclick = () => addElement({ type: 'text', x: 10, y: 60, text: 'Your text here', fontSize: 24, fontFamily: 'Arial', color: '#FFFFFF', bold: false, shadow: false }); - document.getElementById('addHeading').onclick = () => addElement({ type: 'text', x: 5, y: 5, text: 'HEADING', fontSize: 64, fontFamily: 'Impact', color: '#FFFFFF', bold: true, shadow: true }); + document.getElementById('addText').onclick = () => addElement({ type: 'text', x: 10, y: 60, text: t('designer.default.text'), fontSize: 24, fontFamily: 'Arial', color: '#FFFFFF', bold: false, shadow: false }); + document.getElementById('addHeading').onclick = () => addElement({ type: 'text', x: 5, y: 5, text: t('designer.default.heading'), fontSize: 64, fontFamily: 'Impact', color: '#FFFFFF', bold: true, shadow: true }); document.getElementById('addImage').onclick = () => { const input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*'; input.onchange = () => { @@ -120,30 +123,30 @@ export function render(container) { input.click(); }; document.getElementById('addVideo').onclick = () => { - const url = prompt('Video URL (MP4):'); + const url = prompt(t('designer.prompt.video_url')); if (url) addElement({ type: 'video', x: 5, y: 5, width: 50, height: 50, src: url, muted: true, loop: true }); }; document.getElementById('addClock').onclick = () => addElement({ type: 'clock', x: 60, y: 5, fontSize: 48, fontFamily: 'Arial', color: '#FFFFFF', format: '12h', showSeconds: true, shadow: true }); document.getElementById('addDate').onclick = () => addElement({ type: 'date', x: 60, y: 20, fontSize: 24, fontFamily: 'Arial', color: '#FFFFFF', shadow: false }); document.getElementById('addWeather').onclick = () => { - const location = prompt('City, State:', 'Milwaukee, WI'); + const location = prompt(t('designer.prompt.weather_location'), 'Milwaukee, WI'); if (location) addElement({ type: 'weather', x: 5, y: 70, fontSize: 36, color: '#FFFFFF', location, units: 'imperial' }); }; document.getElementById('addTicker').onclick = () => { - const url = prompt('RSS Feed URL:', 'https://feeds.bbci.co.uk/news/rss.xml'); + const url = prompt(t('designer.prompt.rss_url'), 'https://feeds.bbci.co.uk/news/rss.xml'); if (url) addElement({ type: 'ticker', x: 0, y: 90, width: 100, height: 10, feedUrl: url, speed: 30, fontSize: 20, color: '#FFFFFF', bgColor: 'rgba(0,0,0,0.7)' }); }; document.getElementById('addShape').onclick = () => addElement({ type: 'shape', x: 20, y: 20, width: 30, height: 20, color: '#3b82f6', opacity: 0.7, radius: 8, shape: 'rect' }); document.getElementById('addQR').onclick = () => { - const data = prompt('QR Code URL:', 'https://example.com'); + const data = prompt(t('designer.prompt.qr_url'), 'https://example.com'); if (data) addElement({ type: 'qr', x: 80, y: 70, size: 15, data, fgColor: '#FFFFFF', bgColor: '#000000' }); }; document.getElementById('addCountdown').onclick = () => { - const target = prompt('Target date (YYYY-MM-DD):', '2026-04-01'); - if (target) addElement({ type: 'countdown', x: 20, y: 40, fontSize: 48, color: '#FFFFFF', targetDate: target, label: 'Coming Soon' }); + const target = prompt(t('designer.prompt.countdown_date'), '2026-04-01'); + if (target) addElement({ type: 'countdown', x: 20, y: 40, fontSize: 48, color: '#FFFFFF', targetDate: target, label: t('designer.default.coming_soon') }); }; document.getElementById('addWebpage').onclick = () => { - const url = prompt('Webpage URL:'); + const url = prompt(t('designer.prompt.webpage_url')); if (url) addElement({ type: 'webpage', x: 5, y: 5, width: 40, height: 40, url }); }; @@ -159,10 +162,10 @@ export function render(container) { const res = await fetch('/api/widgets', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${localStorage.getItem('token')}` }, - body: JSON.stringify({ widget_type: 'text', name: `Design ${new Date().toLocaleDateString()}`, config: { html: generateInnerHTML(), css: '', background: bgValue } }) + body: JSON.stringify({ widget_type: 'text', name: t('designer.widget_name', { date: new Date().toLocaleDateString() }), config: { html: generateInnerHTML(), css: '', background: bgValue } }) }); - if (res.ok) showToast('Published as widget! Assign it to a layout zone.', 'success'); - else showToast('Publish failed', 'error'); + if (res.ok) showToast(t('designer.toast.published'), 'success'); + else showToast(t('designer.toast.publish_failed'), 'error'); } catch (err) { showToast(err.message, 'error'); } }; @@ -207,7 +210,7 @@ export function render(container) { } const link = document.createElement('a'); link.download = 'signage-design.png'; link.href = canvas.toDataURL('image/png'); link.click(); - } catch (err) { showToast('Export failed: ' + err.message, 'error'); } + } catch (err) { showToast(t('designer.toast.export_failed', { error: err.message }), 'error'); } }; // Load saved design @@ -222,8 +225,8 @@ export function render(container) { bgValue = data.bgValue || '#000'; bgImageDataUrl = data.bgImageDataUrl || null; redraw(); - showToast('Design loaded', 'success'); - } catch { showToast('Invalid design file', 'error'); } + showToast(t('designer.toast.loaded'), 'success'); + } catch { showToast(t('designer.toast.invalid_file'), 'error'); } }; reader.readAsText(input.files[0]); }; @@ -315,16 +318,16 @@ function redraw() { html += `
`; break; case 'weather': - html += `
⛅ Loading...
`; + html += `
⛅ ${t('common.loading')}
`; break; case 'ticker': html += `
-
Loading news...
+
${t('designer.loading_news')}
`; break; case 'qr': html += `
-
QR CODE
+
${t('designer.qr_label')}
${el.data?.slice(0, 25)}
`; break; @@ -378,7 +381,7 @@ function updateDynamic() { if (cdEl && el.targetDate) { const update = () => { const diff = new Date(el.targetDate) - new Date(); - if (diff <= 0) { cdEl.textContent = 'NOW!'; return; } + if (diff <= 0) { cdEl.textContent = t('designer.countdown_now'); return; } const days = Math.floor(diff / 86400000); const hours = Math.floor((diff % 86400000) / 3600000); const mins = Math.floor((diff % 3600000) / 60000); @@ -397,15 +400,15 @@ function updateDynamic() { const temp = el.units === 'metric' ? cur.temp_C + '°C' : cur.temp_F + '°F'; wEl.textContent = `${temp} ${cur.weatherDesc?.[0]?.value || ''}`; } - }).catch(() => { wEl.textContent = '⛅ ' + el.location; }); + }).catch(() => { wEl.textContent = '⛅ ' + el.location; }); } } if (el.type === 'ticker') { const tEl = document.getElementById(`ticker_${i}`); if (tEl && el.feedUrl) { fetch(`https://api.rss2json.com/v1/api.json?rss_url=${encodeURIComponent(el.feedUrl)}`).then(r => r.json()).then(d => { - tEl.textContent = (d.items || []).map(item => item.title).join(' • ') || 'No items'; - }).catch(() => { tEl.textContent = 'Feed unavailable'; }); + tEl.textContent = (d.items || []).map(item => item.title).join(' • ') || t('designer.no_items'); + }).catch(() => { tEl.textContent = t('designer.feed_unavailable'); }); } } }); @@ -426,42 +429,42 @@ function updateProps() { `; if (el.type === 'text') { - html += `
-
${el.fontSize}px
-
-
- - `; + html += `
+
${el.fontSize}px
+
+
+ + `; } else if (el.type === 'clock') { - html += `
-
-
- `; + html += `
+
+
+ `; } else if (el.type === 'image' || el.type === 'video' || el.type === 'webpage') { html += `
`; - if (el.type === 'video') html += ` - `; + if (el.type === 'video') html += ` + `; } else if (el.type === 'shape') { html += `
-
-
-
`; +
+
+
`; } else if (el.type === 'weather') { - html += `
-
-
`; + html += `
+
+
`; } else if (el.type === 'ticker') { - html += `
-
-
-
`; + html += `
+
+
+
`; } else if (el.type === 'countdown') { - html += `
-
-
-
`; + html += `
+
+
+
`; } // Save design button @@ -470,7 +473,7 @@ function updateProps() { a.download = 'design.json'; a.href = 'data:application/json,' + encodeURIComponent(JSON.stringify({elements: ${JSON.stringify(elements)}, bgValue: '${bgValue}'})); a.click(); - })()">Save Design File`; + })()">${t('designer.save_design_file')}`; fields.innerHTML = html; @@ -498,7 +501,7 @@ function updateLayers() { ${typeIcons[el.type] || '?'} ${el.text || el.type} - `).join('') || '

No elements yet

'; + `).join('') || `

${t('designer.no_elements')}

`; list.querySelectorAll('[data-layer]').forEach(el => { el.onclick = () => { selectedIdx = parseInt(el.dataset.layer); redraw(); };