* Add workspace webhooks and drop the unused automation webhook node. Give workspaces HMAC-signed outgoing webhooks for the post lifecycle, with retry, auto-pause, replay, and live logs, and keep HTTP Request as the only outbound automation node. * Tighten webhook controller and validation after review. Drop the redundant workspace redirects, prune logs without counting, and validate events/status with Rule::enum. * Move leftover webhook UI copy behind i18n. HTTP status phrases, delete-cancel, and validation attribute names were still English literals. * Build the webhook-paused email through Maizzle. The hand-written Blade skipped the shared layout, header, and footer used by the other mail templates. * Cover real webhook dispatch paths and restyle the webhook pages. * Ask for the shared delete keyword when confirming a webhook delete. The endpoint URL is a poor confirm string; posts and assets already use the common "delete" keyword. * Fix webhook review blockers so CI can go green. Drop leftover French automation keys, stop mutating Inertia log props, and show delivered_at instead of created_at. * Close the remaining webhook review gaps. Keep Echo log updates across infinite scroll, align the channel with the policy, persist log ids across retries, and fail unknown automation nodes without throwing. * Stop webhook delivery after disable and record last sent only on success. Queued jobs now skip paused or disabled endpoints unless the user replays, and changing the URL re-pings it first. * Limit webhooks to owners and admins, and encrypt signing secrets. Members can no longer create or inspect outgoing integrations, and secrets stay encrypted at rest. * Cover webhook secret hiding, skip-ping, and failed-delivery edges. * Send the full post on webhooks after labels and platforms are saved. * Fix webhook payloads for integer media ids and type webhook status. * Split the webhook show page into focused components. * Reset live webhook logs when switching endpoints. * Keep the newest webhook logs at the top after live merges. * Cast media item ids to string without the extra scalar check. * Add post.unscheduled webhooks and put the log id on the envelope. Unscheduling is now a first-class event, and receivers can send the delivery id back so we can find the matching log. * Translate webhook event names in the UI. * Make the webhook show page full-width and stop stacking flash toasts. * Translate remaining webhook UI copy in every locale. * Sign webhook pings and drop author email from the payload. * Send signed webhook tests after create instead of pinging on save. Create and update only block private URLs so the receiver can copy the secret first. The show page then sends a signed webhook.test with an object data envelope. * Polish webhook test UX and always mint the dispatch log id in the job. Keep send-test in the actions menu (its own group) and drop the leftover constructor param so retries reuse the serialized id instead of a caller-supplied one.
402 lines
18 KiB
PHP
402 lines
18 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
return [
|
|
'mobile_notice' => 'L\'editor delle automazioni funziona meglio su uno schermo più grande. Aprilo su un desktop per creare il tuo flusso di lavoro.',
|
|
'title' => 'Automazioni',
|
|
'default_name' => 'Nuova automazione',
|
|
|
|
'actions' => [
|
|
'new' => 'Nuova automazione',
|
|
'edit' => 'Modifica',
|
|
'save' => 'Salva',
|
|
'activate' => 'Attiva',
|
|
'pause' => 'Metti in pausa',
|
|
'delete' => 'Elimina',
|
|
'retry' => 'Riprova',
|
|
'guide' => 'Scopri come funziona',
|
|
],
|
|
|
|
'tabs' => [
|
|
'build' => 'Crea',
|
|
'variables' => 'Variabili',
|
|
'test' => 'Test',
|
|
],
|
|
|
|
'nav' => [
|
|
'workflow' => 'Flusso di lavoro',
|
|
'invocations' => 'Esecuzioni',
|
|
'metrics' => 'Metriche',
|
|
'settings' => 'Impostazioni',
|
|
],
|
|
|
|
'settings' => [
|
|
'general' => 'Generale',
|
|
'general_description' => 'Rinomina questa automazione.',
|
|
'name_label' => 'Nome',
|
|
'name_saved' => 'Automazione rinominata.',
|
|
'status_title' => 'Stato',
|
|
'status_description' => 'Attiva per avviarla, o metti in pausa per fermarla.',
|
|
'activated_at' => 'Attivata il :date',
|
|
'paused_at' => 'Messa in pausa il :date',
|
|
'created_at' => 'Creata il :date',
|
|
'danger_title' => 'Zona pericolosa',
|
|
'danger_description' => 'Azioni irreversibili.',
|
|
'delete_title' => 'Elimina questa automazione',
|
|
'delete_description' => 'Rimuove definitivamente l\'automazione e la sua cronologia di esecuzioni.',
|
|
],
|
|
|
|
'status_run' => [
|
|
'pending' => 'In sospeso',
|
|
'running' => 'In esecuzione',
|
|
'waiting' => 'In attesa',
|
|
'completed' => 'Completata',
|
|
'failed' => 'Non riuscita',
|
|
'cancelled' => 'Annullata',
|
|
],
|
|
|
|
'node_type' => [
|
|
'trigger' => 'Trigger',
|
|
'generate' => 'Genera contenuto',
|
|
'delay' => 'Ritardo',
|
|
'condition' => 'Condizione',
|
|
'publish' => 'Pubblica',
|
|
'end' => 'Fine',
|
|
'fetch_rss' => 'Recupera RSS',
|
|
'http_request' => 'Richiesta HTTP',
|
|
],
|
|
|
|
'invocations' => [
|
|
'empty' => 'Ancora nessuna esecuzione.',
|
|
'refresh' => 'Aggiorna',
|
|
'search_placeholder' => 'Cerca per ID esecuzione…',
|
|
'copied' => 'ID esecuzione copiato.',
|
|
'loading' => 'Caricamento passaggi…',
|
|
'no_steps' => 'Nessun passaggio registrato.',
|
|
'load_error' => 'Impossibile caricare i passaggi.',
|
|
'steps' => '{0}Nessun passaggio|{1}:count passaggio|[2,*]:count passaggi',
|
|
'filter' => [
|
|
'all' => 'Tutti gli stati',
|
|
],
|
|
'columns' => [
|
|
'timestamp' => 'Data e ora',
|
|
'run' => 'Esecuzione',
|
|
'status' => 'Stato',
|
|
'message' => 'Ultimo messaggio',
|
|
'duration' => 'Durata',
|
|
],
|
|
'summary' => [
|
|
'completed' => 'Flusso di lavoro completato',
|
|
'failed' => 'Flusso di lavoro non riuscito',
|
|
'running' => 'Flusso di lavoro in esecuzione',
|
|
'cancelled' => 'Flusso di lavoro annullato',
|
|
'pending' => 'Flusso di lavoro in sospeso',
|
|
],
|
|
],
|
|
|
|
'metrics' => [
|
|
'overview' => 'Panoramica',
|
|
'runs_over_time' => 'Esecuzioni nel tempo',
|
|
'posts_by_platform' => 'Post per piattaforma',
|
|
'no_posts' => 'Nessun post pubblicato in questo periodo.',
|
|
'cards' => [
|
|
'runs' => 'Esecuzioni totali',
|
|
'completed' => 'Completate',
|
|
'failed' => 'Non riuscite',
|
|
'in_progress' => 'In corso',
|
|
'success_rate' => 'Tasso di successo',
|
|
'avg_duration' => 'Durata media',
|
|
'posts_created' => 'Post creati',
|
|
],
|
|
'legend' => [
|
|
'started' => 'Avviate',
|
|
'completed' => 'Completate',
|
|
'failed' => 'Non riuscite',
|
|
],
|
|
],
|
|
|
|
'categories' => [
|
|
'sources' => 'Fonti',
|
|
'content' => 'Contenuto',
|
|
'flow' => 'Flusso',
|
|
'output' => 'Output',
|
|
],
|
|
|
|
'variables' => [
|
|
'title' => 'Variabili del flusso di lavoro',
|
|
'hint' => 'Valori riutilizzabili richiamabili ovunque con {{ variables.KEY }}. Memorizzati in forma cifrata.',
|
|
'empty' => 'Ancora nessuna variabile.',
|
|
'key' => 'Chiave',
|
|
'value' => 'Valore',
|
|
'key_placeholder' => 'API_KEY',
|
|
'value_placeholder' => 'Valore',
|
|
'add' => 'Nuova variabile',
|
|
],
|
|
|
|
'expr' => [
|
|
'trigger_event' => 'Nome dell\'evento trigger',
|
|
'trigger_fired_at' => 'Quando il trigger è scattato',
|
|
'trigger_post_id' => 'ID del post che ha attivato',
|
|
'trigger_post_content' => 'Contenuto del post che ha attivato',
|
|
'trigger_post_status' => 'Stato del post che ha attivato',
|
|
'trigger_post_scheduled_at' => 'Quando il post è programmato',
|
|
'trigger_post_published_at' => 'Quando il post è stato pubblicato',
|
|
'fetched_title' => 'Titolo dell\'elemento recuperato',
|
|
'fetched_link' => 'Link dell\'elemento recuperato',
|
|
'fetched_date' => 'Data di pubblicazione dell\'elemento recuperato',
|
|
'fetched_content' => 'Contenuto completo dell\'elemento recuperato',
|
|
'fetched_description' => 'Riassunto dell\'elemento recuperato',
|
|
'fetched_author' => 'Autore dell\'elemento recuperato',
|
|
'fetched_image' => 'URL immagine dell\'elemento recuperato',
|
|
'fetched_categories' => 'Categorie dell\'elemento recuperato',
|
|
'fetched_enclosure' => 'Media dell\'elemento recuperato (audio/video/file)',
|
|
'fetched_pubdate' => 'Data di pubblicazione dell\'elemento recuperato',
|
|
'fetched_http' => 'Elemento HTTP recuperato (aggiungi un campo)',
|
|
'generated_content' => 'Contenuto del post generato dall\'IA',
|
|
'generated_post_url' => 'URL del post generato dall\'IA',
|
|
'variable' => 'Variabile del flusso di lavoro',
|
|
'now' => 'Data e ora correnti',
|
|
],
|
|
|
|
'test' => [
|
|
'description' => 'Esegue l\'automazione dall\'inizio alla fine usando un payload di trigger sintetizzato. Utile per convalidare ogni nodo senza aspettare la pianificazione o il feed reali.',
|
|
'starting' => 'Avvio dell\'esecuzione di test…',
|
|
'in_progress' => 'In corso',
|
|
'completed' => 'Completata',
|
|
'failed' => 'Non riuscita',
|
|
'waiting' => 'In attesa',
|
|
'close' => 'Chiudi',
|
|
'no_node_runs' => 'In attesa dell\'avvio del primo nodo…',
|
|
'node_input' => 'Input',
|
|
'node_output' => 'Output',
|
|
'node_error' => 'Errore',
|
|
'no_new_items' => 'Nessun nuovo elemento — nessun nodo a valle è stato eseguito.',
|
|
'error_starting' => 'Impossibile avviare l\'esecuzione di test.',
|
|
'with_real_data' => 'Con dati reali',
|
|
'run' => 'Esegui test',
|
|
'idle_hint' => 'Premi Esegui test per eseguire l\'automazione dall\'inizio alla fine.',
|
|
'real_data_hint' => 'Questo test pubblicherà i post, farà avanzare i watermark di polling e attiverà effetti collaterali esterni.',
|
|
'dry_badge' => 'Simulazione',
|
|
],
|
|
|
|
'status' => [
|
|
'draft' => 'Bozza',
|
|
'active' => 'Attiva',
|
|
'paused' => 'In pausa',
|
|
],
|
|
|
|
'index' => [
|
|
'empty_title' => 'Ancora nessuna automazione',
|
|
'empty_description' => 'Crea la tua prima automazione per iniziare a pubblicare in automatico.',
|
|
'columns' => [
|
|
'name' => 'Nome',
|
|
'status' => 'Stato',
|
|
'created' => 'Creata',
|
|
],
|
|
],
|
|
|
|
'form' => [
|
|
'activate_error_fallback' => 'Impossibile attivare l\'automazione.',
|
|
'pause_error_fallback' => 'Impossibile mettere in pausa l\'automazione.',
|
|
'save_error_fallback' => 'Impossibile salvare l\'automazione.',
|
|
'save_success' => 'Automazione salvata.',
|
|
'empty_canvas_title' => 'Inizia a creare la tua automazione',
|
|
'empty_canvas_description' => 'Trascina un nodo dal pannello di sinistra per iniziare.',
|
|
'name_placeholder' => 'Automazione senza titolo',
|
|
],
|
|
|
|
'nodes' => [
|
|
'trigger' => 'Trigger',
|
|
'generate' => 'Genera',
|
|
'delay' => 'Ritardo',
|
|
'condition' => 'Condizione',
|
|
'publish' => 'Pubblica',
|
|
'end' => 'Fine',
|
|
'end_summary' => 'Interrompe l\'automazione qui',
|
|
'fetch_rss' => 'Recupera RSS',
|
|
'http_request' => 'Richiesta HTTP',
|
|
'handles' => [
|
|
'items' => 'ha elementi',
|
|
'no_items' => 'nessun elemento',
|
|
],
|
|
],
|
|
|
|
'config' => [
|
|
'select_placeholder' => 'Seleziona…',
|
|
'invalid_json' => 'Questo non è ancora un JSON valido.',
|
|
'expand_editor' => 'Espandi editor',
|
|
'minimize_editor' => 'Riduci',
|
|
|
|
'trigger' => [
|
|
'type' => 'Tipo di trigger',
|
|
'types' => [
|
|
'schedule' => 'Pianificazione',
|
|
'post_published' => 'Quando un post viene pubblicato',
|
|
'post_scheduled' => 'Quando un post viene programmato',
|
|
],
|
|
'post_published_hint' => 'Viene eseguito ogni volta che un post di questo workspace viene pubblicato. Il post pubblicato diventa disponibile in {{ trigger.post }} per i nodi a valle.',
|
|
'post_scheduled_hint' => 'Viene eseguito ogni volta che un post di questo workspace viene programmato. Il post programmato è disponibile in {{ trigger.post }}.',
|
|
|
|
'schedule' => [
|
|
'field' => 'Intervallo del trigger',
|
|
'fields' => [
|
|
'minutes' => 'Minuti',
|
|
'hours' => 'Ore',
|
|
'days' => 'Giorni',
|
|
'weeks' => 'Settimane',
|
|
'months' => 'Mesi',
|
|
],
|
|
'minutes_interval' => 'Minuti tra un trigger e l\'altro',
|
|
'hours_interval' => 'Ore tra un trigger e l\'altro',
|
|
'days_interval' => 'Giorni tra un trigger e l\'altro',
|
|
'hour' => 'Attiva all\'ora',
|
|
'minute' => 'Attiva al minuto',
|
|
'weekdays' => 'Attiva nei giorni della settimana',
|
|
'day_of_month' => 'Giorno del mese',
|
|
'weekday_names' => [
|
|
'sun' => 'Dom',
|
|
'mon' => 'Lun',
|
|
'tue' => 'Mar',
|
|
'wed' => 'Mer',
|
|
'thu' => 'Gio',
|
|
'fri' => 'Ven',
|
|
'sat' => 'Sab',
|
|
],
|
|
'summary' => [
|
|
'every_n_minutes' => 'Viene eseguito ogni minuto|Viene eseguito ogni :count minuti',
|
|
'every_n_hours' => 'Viene eseguito ogni ora al minuto :minute|Viene eseguito ogni :count ore al minuto :minute',
|
|
'every_n_days' => 'Viene eseguito ogni giorno alle :time|Viene eseguito ogni :count giorni alle :time',
|
|
'weekly' => 'Viene eseguito ogni :days alle :time',
|
|
'monthly' => 'Viene eseguito il giorno :day di ogni mese alle :time',
|
|
],
|
|
],
|
|
],
|
|
'generate' => [
|
|
'social_accounts' => 'Account social',
|
|
'social_accounts_empty' => 'Nessun account social collegato. Collegane uno prima.',
|
|
'target_slide_count' => 'Slide da generare',
|
|
'prompt_template' => 'Modello di prompt',
|
|
'prompt_template_hint' => 'Digita {{ per inserire dati dai passaggi precedenti.',
|
|
'image_count' => 'Immagini da generare',
|
|
'image_count_hint' => '0 = post di solo testo (nessuna immagine). 1 = immagine singola. 2+ = carosello.',
|
|
'use_brand_voice' => 'Usa il tono del brand',
|
|
'use_brand_voice_hint' => 'Applica la descrizione e il tono del tuo brand. Disattiva per una curatela fedele di fonti di terze parti (notizie, RSS).',
|
|
'use_brand_visuals' => 'Usa la grafica del brand',
|
|
'use_brand_visuals_hint' => 'Orienta le immagini IA con i colori e l\'identità del tuo brand. Disattiva per immagini neutre guidate solo dall\'argomento del post.',
|
|
'style' => 'Stile',
|
|
'account_summary' => ':count account · :format|:count account · :format',
|
|
'formats' => [
|
|
'single' => 'singola',
|
|
'carousel' => 'carosello',
|
|
],
|
|
],
|
|
'delay' => [
|
|
'duration' => 'Durata',
|
|
'unit' => 'Unità',
|
|
'units' => [
|
|
'minutes' => 'Minuti',
|
|
'hours' => 'Ore',
|
|
'days' => 'Giorni',
|
|
],
|
|
],
|
|
'condition' => [
|
|
'field' => 'Campo',
|
|
'operator' => 'Operatore',
|
|
'operators' => [
|
|
'contains' => 'contiene',
|
|
'not_contains' => 'non contiene',
|
|
'equals' => 'uguale a',
|
|
'not_equals' => 'diverso da',
|
|
'matches' => 'corrisponde (regex)',
|
|
'greater_than' => 'maggiore di',
|
|
'less_than' => 'minore di',
|
|
],
|
|
'value' => 'Valore',
|
|
],
|
|
'publish' => [
|
|
'mode' => 'Modalità',
|
|
'modes' => [
|
|
'now' => 'Pubblica ora',
|
|
'scheduled' => 'Programma',
|
|
'draft' => 'Salva come bozza',
|
|
],
|
|
'scheduled_offset' => 'Scostamento dal trigger (minuti)',
|
|
'offset_summary' => ':mode · +:offset min',
|
|
],
|
|
'end' => [
|
|
'reason' => 'Motivo (facoltativo)',
|
|
'reason_placeholder' => 'es. Escluso dalla condizione',
|
|
],
|
|
'fetch_rss' => [
|
|
'feed_url' => 'URL del feed',
|
|
'feed_url_hint' => 'Alla prima esecuzione, il watermark viene impostato su "adesso" così gli elementi storici non inondano i nodi a valle. Le esecuzioni successive vedono solo gli elementi più recenti del polling precedente.',
|
|
'inspect' => 'Ispeziona feed',
|
|
'inspecting' => 'Ispezione in corso…',
|
|
'inspect_hint' => 'Recupera un campione per scoprire i campi disponibili da usare nei nodi a valle.',
|
|
'inspect_error' => 'Impossibile leggere questo feed. Controlla l\'URL e riprova.',
|
|
'discovered_fields' => 'Campi disponibili',
|
|
'discovered_empty' => 'Nessun campo trovato nell\'ultimo elemento.',
|
|
],
|
|
'http_request' => [
|
|
'url' => 'URL',
|
|
'method' => 'Metodo',
|
|
'auth_type' => 'Autenticazione',
|
|
'auth' => [
|
|
'none' => 'Nessuna (pubblica)',
|
|
'bearer' => 'Token Bearer',
|
|
'basic' => 'Autenticazione di base',
|
|
'api_key' => 'Header con chiave API',
|
|
],
|
|
'bearer_token' => 'Token Bearer',
|
|
'basic_username' => 'Nome utente',
|
|
'basic_password' => 'Password',
|
|
'api_key_header' => 'Nome dell\'header',
|
|
'api_key_value' => 'Chiave API',
|
|
'body_template' => 'Modello del corpo (JSON)',
|
|
'headers' => 'Header',
|
|
'header_name' => 'Nome dell\'header',
|
|
'header_value' => 'Valore',
|
|
'add_header' => 'Aggiungi header',
|
|
'polling_section' => 'Elenco e deduplicazione (facoltativo)',
|
|
'polling_hint' => 'Quando la risposta è un elenco, ogni elemento esegue il flusso di lavoro separatamente. Un singolo oggetto viene eseguito una volta.',
|
|
'items_path' => 'Percorso degli elementi',
|
|
'items_path_hint' => 'Lascia vuoto se la risposta è già un array. Usa un percorso con punti (es. data.items) per un array annidato, oppure * per un oggetto con chiave per id.',
|
|
'item_key_path' => 'Percorso della chiave elemento',
|
|
'item_key_path_hint' => 'Percorso JSON a un id univoco (es. id). Gli elementi già visti vengono saltati, così un feed senza date inoltra comunque solo le voci nuove.',
|
|
'item_date_path' => 'Percorso della data elemento',
|
|
'item_date_path_hint' => 'Percorso JSON al timestamp dell\'elemento (es. published_at). Preferito rispetto al percorso della chiave quando disponibile. Il primo polling registra la baseline e non inoltra nulla, così un feed esistente non inonda mai il primo giorno.',
|
|
],
|
|
],
|
|
|
|
'delete' => [
|
|
'title' => 'Elimina automazione',
|
|
'description' => 'Vuoi davvero eliminare questa automazione? Verranno rimossi anche tutte le esecuzioni e gli elementi trigger. Questa azione non può essere annullata.',
|
|
'confirm' => 'Elimina',
|
|
'cancel' => 'Annulla',
|
|
],
|
|
|
|
'flash' => [
|
|
'deleted' => 'Automazione eliminata con successo!',
|
|
],
|
|
|
|
'errors' => [
|
|
'no_active_social_accounts' => 'Nessun account social attivo configurato per questa automazione.',
|
|
'must_have_one_trigger' => 'L\'automazione deve avere esattamente un nodo trigger.',
|
|
'trigger_must_be_connected' => 'Il nodo trigger deve essere collegato ad almeno un nodo.',
|
|
'graph_contains_cycle' => 'Il grafo dell\'automazione contiene un ciclo.',
|
|
'only_failed_can_retry' => 'Solo le esecuzioni non riuscite possono essere ritentate.',
|
|
'no_generated_post' => 'Nessun post generato trovato nell\'esecuzione.',
|
|
'url_not_allowed' => 'L\'URL della richiesta punta a un indirizzo privato o irraggiungibile ed è stato bloccato.',
|
|
'node_no_longer_exists' => 'Il nodo :node_id non esiste più nell\'automazione.',
|
|
'no_trigger_connection' => 'Nessun nodo collegato al nodo Trigger.',
|
|
'fetch_rss_missing_url' => 'Al nodo Recupera RSS manca un URL del feed.',
|
|
'fetch_rss_request_failed' => 'La richiesta al feed RSS non è riuscita.',
|
|
'fetch_rss_malformed' => 'Il feed RSS è malformato.',
|
|
'http_missing_url' => 'Al nodo richiesta HTTP manca un URL.',
|
|
'http_request_exception' => 'La richiesta HTTP ha generato un\'eccezione.',
|
|
'http_request_failed' => 'La richiesta HTTP non è riuscita.',
|
|
'http_items_path_not_array' => 'Il percorso degli elementi non ha restituito un elenco.',
|
|
'generate_image_format_required' => 'La generazione AI crea solo immagini. Scegli un formato immagine (non video).',
|
|
],
|
|
];
|