Funzione per visualizzare un elenco dei post più lunghi con suggerimenti per dividerli

Seleccionar idioma
Funzione per visualizzare un elenco dei post più lunghi con suggerimenti per dividerli

Se qualche giorno fa abbiamo visto come suddividere i post lunghi di WordPress in pagine senza influire sulla SEO per migliorare i tempi di caricamento, oggi aggiungeremo una funzione supplementare che vi permetterà di trovare e classificare facilmente i post lunghi in base al tipo di contenuto, alla lunghezza e alla lingua.

Quando le voci sono poche e lunghe, è facile conoscerle quasi a memoria. Diventa più complicato quando ce ne sono molte e hanno anche le rispettive traduzioni in altre lingue. Questo codice ha lo scopo di velocizzare il processo di selezione quando si dividono i post (con il blocco "Page Break" o "Page Break" di Gutenberg) e di farvi sapere rapidamente quali dovete ancora dividere e se le vostre attuali suddivisioni sono coerenti.

Cosa fa esattamente il codice?

Il seguente codice aggiunto al functions.php del vostro template o tema figlio genererà uno shortcode, utilizzando il quale su qualsiasi pagina o post verrà visualizzato un elenco paginato (10 voci per pagina) con tutti i post che hanno più di 1500 parole, compresi quelli tradotti in altre lingue. Funziona sia con Polylang che senza Polylang.

Questi valori possono essere modificati in // 2. Configurazione (basta non aumentare troppo il numero totale di pagine da visualizzare se non si vuole che la query rallenti la risposta). L'idea è quella di utilizzare lo shortcode su pagine in bozza o private, in modo da non rischiare che il numero di visitatori influisca sulle prestazioni e consumi la CPU del server, se si dispone di risorse limitate).

Questo codice consente di vedere non solo quante interruzioni ha un post (o se non ne ha affatto), ma anche come le parole sono distribuite tra le diverse sezioni, mostrando quante parole contiene ogni interruzione, il che può essere molto utile per valutare se la paginazione attuale è equilibrata.

Rilevamento automatico dei tipi di contenuto

È stato aggiunto il rilevamento automatico per 4 tipi di contenuto, dove "pausa" significa un'interruzione ogni x parole. Il calcolo si basa sulla densità delle informazioni e il limite massimo è configurabile.

Tecnica (700 parole/intervallo): Tutorial, codice

Narrativa (1200 parole/intervallo): storie, narrazioni

Giornalistico (900 parole/intervallo): notizie, articoli

Generale (1000 parole/intervallo): Contenuto standard

Criteri di selezione

Per i criteri del codice ho cercato suggerimenti e riferimenti. Alcuni parametri li ho adattati al mio blog, ma questo è sempre elastico e può essere messo a punto per ogni sito; è sempre possibile aggiungere altri segnali per rendere il rilevamento più preciso. Prendetelo come punto di partenza ed esaminate quali elementi utilizzate più spesso nei vostri articoli in base alla loro tipologia o aggiungete i vostri marchi che il codice può identificare e alcuni dei tag che utilizzate più spesso.

I criteri attuali per lo screening sono i seguenti:

A. Contenuto tecnico (⚙️ 700 parole/intervallo)

  • Categorie: tutorial, guida, tecnica, coding, programmazione
  • Tags: codice, software, tecnologia, sviluppo
  • Struttura:
    • Blocchi di codice (``` o <pre> )
    • Formule matematiche (più $)
    • Più di 2 frammenti di codice identificati

B. Contenuto narrativo (📖 1200 parole/intervallo)

  • Categorie: racconto, storia, narrativa, letteratura
  • Tag: racconto, romanzo, narrativa, poesia
  • Struttura:
    • Dialoghi (righe che iniziano con - o ")
    • Numerazione dei capitoli (1, 2.)
    • Più di 3 dialoghi identificati

C. Contenuto giornalistico (📰 900 parole/intervallo)

  • Categorie: notizie, attualità, relazione, articolo
  • Tag: stampa, intervista, opinione
  • Struttura:
    • Sottotitoli frequenti (###, ####)
    • Note dell'editore (*Nota:)
    • Più di 3 sottotitoli identificati

D. Contenuto generale (✍️ 1000 parole/intervallo)

  • Si applica quando non coincide con i criteri di cui sopra.
  • Valore predefinito per i messaggi standard

Il codice valuta gli elementi nel seguente ordine:

  1. Categorie postali
  2. Tag dei post
  3. Struttura del contenuto
  4. Assegnare "Generale" se non c'è corrispondenza

Il numero massimo di interruzioni suggerito è di 5 per ogni post.

Aspetto

Il CSS è incluso nel codice per comodità e restituisce questo aspetto alle schede. Anche se le informazioni visualizzate sono piuttosto esplicite, ecco le note per ogni elemento.

Si può modificare per collegare il titolo alla pagina di modifica, se lo si ritiene più comodo per un accesso rapido, o apportare qualsiasi altro miglioramento.

Funzione per visualizzare un elenco dei post più lunghi con suggerimenti per dividerli 1

Dopo il codice è possibile scaricare un'altra funzione o un plugin personalizzato per visualizzare un'utile calcolatrice di paragrafi per URL.

Codice

// ======================================================================
//Muestra una lista paginada de post largos (ordenados de mayor a menor número de palabras) con sugerencias para dividirlos en páginas según tipo de contenido 
// Shortcode para análisis de Page Breaks en posts largos. Cortesía de /jrmora.com
// ======================================================================

function analizador_pagebreaks_completo() {
    // 1. Protección contra ejecución en admin/AJAX
    if (is_admin() || wp_doing_ajax() || (defined('REST_REQUEST') && REST_REQUEST)) {
        return '';
    }

    // 2. Configuración (convertida a constante para mejor performance)
    define('PB_UMBRAL_PALABRAS', 1500);
    define('PB_POSTS_POR_PAGINA', 10);
    $mostrar_todos_idiomas = true;

    // 3. Función optimizada para detección de tipo de contenido
    function detectar_tipo_contenido($post_id) {
        static $cache = [];
        if (isset($cache[$post_id])) {
            return $cache[$post_id];
        }

        $post = get_post($post_id);
        $content = strip_tags($post->post_content);
        $result = ['tipo' => 'general', 'color' => '#f5f5f5', 'icono' => '✍️'];
        
        // Verificar categorías
        $categorias = wp_get_post_categories($post_id, ['fields' => 'slugs']);
        $cats_lower = array_map('strtolower', $categorias);
        
        if (array_intersect($cats_lower, ['tutorial', 'guia', 'tecnico', 'code', 'programacion'])) {
            $result = ['tipo' => 'técnico', 'color' => '#e3f2fd', 'icono' => '⚙️'];
        } elseif (array_intersect($cats_lower, ['relato', 'historia', 'narrativa', 'literatura'])) {
            $result = ['tipo' => 'narrativo', 'color' => '#f3e5f5', 'icono' => '📖'];
        } elseif (array_intersect($cats_lower, ['noticia', 'actualidad', 'reportaje', 'articulo'])) {
            $result = ['tipo' => 'periodístico', 'color' => '#e8f5e9', 'icono' => '📰'];
        } else {
            // Verificar etiquetas solo si no se determinó por categorías
            $etiquetas = wp_get_post_tags($post_id, ['fields' => 'slugs']);
            $tags_lower = array_map('strtolower', $etiquetas);
            
            if (array_intersect($tags_lower, ['codigo', 'software', 'tecnologia', 'dev'])) {
                $result = ['tipo' => 'técnico', 'color' => '#e3f2fd', 'icono' => '⚙️'];
            } elseif (array_intersect($tags_lower, ['cuento', 'novela', 'ficcion', 'poesia'])) {
                $result = ['tipo' => 'narrativo', 'color' => '#f3e5f5', 'icono' => '📖'];
            } else {
                // Verificar estructura solo como último recurso
                if (preg_match_all('/```|<pre>|<code>/i', $content) > 2) {
                    $result = ['tipo' => 'técnico', 'color' => '#e3f2fd', 'icono' => '⚙️'];
                } elseif (preg_match_all('/\n—|\n"|\n\d+\. /i', $content) > 3) {
                    $result = ['tipo' => 'narrativo', 'color' => '#f3e5f5', 'icono' => '📖'];
                } elseif (preg_match_all('/\n### |\n#### |\*Nota:/i', $content) > 3) {
                    $result = ['tipo' => 'periodístico', 'color' => '#e8f5e9', 'icono' => '📰'];
                }
            }
        }

        $cache[$post_id] = $result;
        return $result;
    }

    // 4. Cálculo de breaks por tipo (optimizado con array estático)
    function calcular_breaks_por_tipo($palabras, $tipo) {
        static $config = [
            'técnico' => 700,
            'narrativo' => 1200,
            'periodístico' => 900,
            'general' => 1000
        ];
        
        $breaks = max(1, floor($palabras / ($config[$tipo] ?? 1000)) - 1);
        return min($breaks, 5);
    }

    // 5. Función optimizada para calcular distribución de palabras
    function calcular_distribucion_palabras($content) {
        static $cache = [];
        $cache_key = md5($content);
        
        if (isset($cache[$cache_key])) {
            return $cache[$cache_key];
        }
        
        $secciones = explode('<!--nextpage-->', $content);
        $distribucion = [];
        
        foreach ($secciones as $seccion) {
            $distribucion[] = str_word_count(strip_tags($seccion));
        }
        
        $cache[$cache_key] = $distribucion;
        return $distribucion;
    }

    // 6. Función optimizada para obtener IDs de posts
    function obtener_ids_posts_a_procesar($mostrar_todos_idiomas) {
        static $cache_key = 'pb_post_ids_cache';
        $cached = get_transient($cache_key);
        
        if ($cached !== false) {
            return $cached;
        }
        
        $args = [
            'post_type' => 'post',
            'posts_per_page' => -1,
            'fields' => 'ids',
            'post_status' => 'publish',
            'no_found_rows' => true,
            'update_post_term_cache' => false,
            'update_post_meta_cache' => false
        ];

        if ($mostrar_todos_idiomas && function_exists('pll_get_post_translations')) {
            $query = new WP_Query($args);
            $all_post_ids = [];
            
            foreach ($query->posts as $post_id) {
                $translations = pll_get_post_translations($post_id);
                $all_post_ids = array_merge($all_post_ids, array_values($translations));
            }
            
            $result = array_unique($all_post_ids);
        } else {
            $query = new WP_Query($args);
            $result = $query->posts;
        }
        
        set_transient($cache_key, $result, HOUR_IN_SECONDS);
        return $result;
    }

    // 7. Función optimizada para procesar posts
    function procesar_post_con_tipo($post_id, $umbral, &$posts) {
        static $post_cache = [];
        
        if (isset($post_cache[$post_id])) {
            if ($post_cache[$post_id]['palabras'] >= $umbral) {
                $posts[] = $post_cache[$post_id];
            }
            return;
        }
        
        $post = get_post($post_id);
        if (!$post) return;

        $content = $post->post_content;
        $word_count = str_word_count(strip_tags($content));
        
        if ($word_count >= $umbral) {
            $tipo = detectar_tipo_contenido($post_id);
            $idioma = function_exists('pll_get_post_language') ? (pll_get_post_language($post_id) ?: 'es') : 'es';
            
            $post_data = [
                'ID' => $post_id,
                'titulo' => $post->post_title,
                'palabras' => $word_count,
                'contenido' => $content,
                'enlace' => get_permalink($post_id),
                'tiene_break' => (strpos($content, '<!--nextpage-->') !== false),
                'breaks_actuales' => substr_count($content, '<!--nextpage-->'),
                'idioma' => $idioma,
                'tipo' => $tipo
            ];
            
            $posts[] = $post_data;
            $post_cache[$post_id] = $post_data;
        }
    }

    // 8. Obtener y procesar posts
    $posts_procesados = [];
    $post_ids = obtener_ids_posts_a_procesar($mostrar_todos_idiomas);

    foreach ($post_ids as $post_id) {
        procesar_post_con_tipo($post_id, PB_UMBRAL_PALABRAS, $posts_procesados);
    }

    // 9. Ordenar por palabras (mayor a menor)
    usort($posts_procesados, function($a, $b) {
        return $b['palabras'] - $a['palabras'];
    });

    // 10. Paginación manual
    $paged = max(1, get_query_var('paged'));
    $total_posts = count($posts_procesados);
    $total_paginas = ceil($total_posts / PB_POSTS_POR_PAGINA);
    $offset = ($paged - 1) * PB_POSTS_POR_PAGINA;
    $posts_paginados = array_slice($posts_procesados, $offset, PB_POSTS_POR_PAGINA);

    // CSS (incluido al final para mejor renderizado)
    $output = '<style>.pb-ultimate-container{max-width:800px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif}.pb-ultimate-item{background:#fff;border-radius:8px;padding:20px;margin-bottom:20px;box-shadow:0 1px 3px rgba(0,0,0,0.08);border:1px solid #e0e0e0}.pb-ultimate-title{font-size:1.25rem;margin:0 0 10px 0;line-height:1.4}.pb-ultimate-meta{display:flex;flex-wrap:wrap;gap:12px;margin-bottom:12px;font-size:0.85rem}.pb-ultimate-meta-item{display:inline-flex;align-items:center;gap:4px}.pb-ultimate-type{padding:2px 10px;border-radius:10px;font-size:0.75rem;font-weight:500}.pb-ultimate-suggestion{background:#f5f5f5;border-left:3px solid #64b5f6;padding:10px 15px;margin-top:15px;border-radius:0 4px 4px 0;font-size:0.9rem}.pb-ultimate-pagination{display:flex;justify-content:center;margin:30px 0;flex-wrap:wrap;gap:8px}.pb-ultimate-page{padding:8px 16px;border-radius:4px;text-decoration:none}.pb-ultimate-page-number{border:1px solid #e0e0e0;color:#1976d2}.pb-ultimate-page-number:hover{background:#e3f2fd}.pb-ultimate-page-current{background:#1976d2;color:white;border:1px solid #1976d2}.pb-ultimate-wordcount{font-weight:600;color:#212121}.pb-ultimate-language{background:#e3f2fd;color:#1565c0;padding:2px 10px;border-radius:10px;font-size:0.75rem}.pb-ultimate-warning{color:#d32f2f;font-weight:500}.pb-ultimate-distribution{margin-left:5px;font-size:0.95em;font-weight:600;color:#444;background:#f8f8f8;padding:2px 6px;border-radius:4px;border-left:2px solid #64b5f6}.pb-ultimate-distribution-separator{color:#999;font-weight:normal;margin:0 3px}</style>';

    $output .= '<div class="pb-ultimate-container">';

    if (!empty($posts_procesados)) {
        foreach ($posts_paginados as $post) {
            $breaks_sugeridos = calcular_breaks_por_tipo($post['palabras'], $post['tipo']['tipo']);
            $palabras_por_seccion = ceil($post['palabras'] / ($breaks_sugeridos + 1));
            
            $output .= '<div class="pb-ultimate-item">';
            $output .= '<h3 class="pb-ultimate-title"><a href="' . esc_url($post['enlace']) . '">' . esc_html($post['titulo']) . '</a></h3>';
            
            $output .= '<div class="pb-ultimate-meta">';
            $output .= '<span class="pb-ultimate-meta-item pb-ultimate-wordcount">📝 ' . number_format($post['palabras']) . ' palabras</span>';
            $output .= '<span class="pb-ultimate-meta-item pb-ultimate-type" style="background:' . $post['tipo']['color'] . '">';
            $output .= $post['tipo']['icono'] . ' ' . ucfirst($post['tipo']['tipo']);
            $output .= '</span>';
            $output .= '<span class="pb-ultimate-meta-item pb-ultimate-language">' . strtoupper($post['idioma']) . '</span>';
            
            if ($post['tiene_break']) {
                $distribucion = calcular_distribucion_palabras($post['contenido']);
                $distribucion_text = '';
                
                if (!empty($distribucion)) {
                    $distribucion_text = '<span class="pb-ultimate-distribution">(';
                    $distribucion_parts = [];
                    foreach ($distribucion as $index => $palabras) {
                        $distribucion_parts[] = ($index + 1) . 'º: ' . number_format($palabras);
                    }
                    $distribucion_text .= implode('<span class="pb-ultimate-distribution-separator"> · </span>', $distribucion_parts) . ')</span>';
                }
                
                $output .= '<span class="pb-ultimate-meta-item">✅ ' . $post['breaks_actuales'] . ' breaks ';
                $output .= $distribucion_text;
                $output .= '</span>';
            } else {
                $output .= '<span class="pb-ultimate-meta-item pb-ultimate-warning">⚠️ SIN PAGINAR</span>';
            }
            
            $output .= '</div>';
            
            $output .= '<div class="pb-ultimate-suggestion">';
            $output .= '<strong>Sugerencia:</strong> ' . $breaks_sugeridos . ' breaks (1 cada ~' . number_format($palabras_por_seccion) . ' palabras)';
            $output .= '</div>';
            
            $output .= '</div>';
        }

        // Paginación
        if ($total_paginas > 1) {
            $output .= '<div class="pb-ultimate-pagination">';
            $output .= paginate_links([
                'base' => str_replace(999999999, '%#%', esc_url(get_pagenum_link(999999999))),
                'format' => '?paged=%#%',
                'current' => $paged,
                'total' => $total_paginas,
                'prev_text' => __('&laquo;'),
                'next_text' => __('&raquo;'),
                'mid_size' => 1
            ]);
            $output .= '</div>';
        }
    } else {
        $output .= '<div class="pb-ultimate-item">No se encontraron posts que requieran paginación.</div>';
    }

    return $output . '</div>';
}

add_shortcode('analizador_pagebreaks_ultimate', 'analizador_pagebreaks_completo');

Extra, calcolatrice/analizzatore di paragrafi

Come strumento complementare, qui avete il codice scaricabile di un calcolatore di paragrafi che sto testando e che mostra i paragrafi che si possono dividere per post o pagina (fino a 5 suddivisioni) inserendo l'URL di qualsiasi post del vostro blog. Deve ancora essere perfezionato per regolare alcuni contatori.

Il margine di errore nel conteggio delle parole è compreso tra l'1-3%.

Può essere utilizzato come plugin o aggiungendo il codice al functions.php del proprio modello. È consigliabile non utilizzarlo su siti in produzione senza averlo prima testato in uno staging e prenderlo come codice nei test da usare e buttare via.

Scarica 📦calculador_parrafos.zip

Importante, questo è un codice sperimentale temporaneo senza debug. Non sono responsabile di eventuali bug nel vostro ambiente. Non utilizzatelo su siti di produzione. Non ho intenzione di aggiornarlo perché smetterò di usarlo quando avrò diviso tutti i miei lunghi post.

Si presenta così.

Funzione per visualizzare un elenco dei post più lunghi con suggerimenti per dividerli 2

E così mostra i risultati:

Funzione per visualizzare un elenco dei post più lunghi con suggerimenti per dividerli 3

🔹 FUNZIONI PRINCIPALI:

  1. Analizza gli articoli e suggerisce i punti ottimali per la suddivisione dei contenuti
  2. Escludere automaticamente gli elementi non rilevanti (barre laterali, piè di pagina, ecc.).
  3. Fornisce metriche dettagliate sulla struttura dei contenuti

🔹 CARATTERISTICHE PRINCIPALI:

  • Shortcode:
[calculador_parrafos]
  • Interfaccia intuitiva con AJAX
  • Rilevamento semantico di paragrafi validi
  • Esclusione di 10 tipi di articoli indesiderati
  • Sistema di analisi con DOMDocument + XPath
  • Metodo alternativo per i contenuti non contrassegnati
  • Statistiche complete (parole, lunghezza, distribuzione)

🔹 DATI CHE FORNISCE:
✔ Numero totale di paragrafi validi.
✔ Divisione suggerita con:

  • Posizione numerica e percentuale
  • Estratto del paragrafo
  • Numero di parole e caratteri
  • Raccomandazione di posizione
    Diagnosi tecnica completa:
  • Tempi di esecuzione
  • Utilizzo della memoria
  • Articoli esclusi
  • Versioni del sistema

🔹 USO TIPICO:

  1. Inserire lo shortcode nella pagina/post
  2. Inserire l'URL dell'articolo da analizzare
  3. Selezionare il numero di divisioni desiderato
  4. Esaminare i suggerimenti e i dati tecnici

🔹 VANTAGGI:
✅ Precisione nell'individuazione dei paragrafi reali.
✅ Esclusione intelligente dei contenuti irrilevanti.
✅ Interfaccia professionale con una visualizzazione dettagliata
✅ Raccomandazioni basate sulla struttura semantica
✅ Compatibile con la maggior parte dei temi di WordPress

🔹 NOTE:

  • Richiede jQuery
  • Ottimale per contenuti narrativi e strutturati
  • Include un sistema di gestione degli errori
  • Tempo di analisi tipico: 500-1500 ms

Articoli correlati

Este blog se aloja en LucusHost

LucusHost, el mejor hosting