
며칠 전에 SEO에 영향을 주지 않고 긴 워드프레스 글을 페이지로 분할하는 방법을 살펴보았다면, 오늘은 콘텐츠 유형, 길이 및 언어에 따라 긴 글을 쉽게 찾고 분류할 수 있는 기능을 추가합니다.
긴 항목이 몇 개만 있으면 거의 외우기 쉽습니다. 항목이 많고 다른 언어로 번역된 항목이 있는 경우에는 더 복잡해집니다. 이 코드는 글을 분할할 때(구텐베르크 "페이지 나누기" 또는 "페이지 나누기" 블록 사용) 선택 프로세스의 속도를 높이고 어떤 글을 분할해야 하는지, 현재 분할이 일관성이 있는지 빠르게 알려주는 데 목적이 있습니다.
이 코드는 정확히 어떤 기능을 하나요?
템플릿 또는 하위 테마의 functions.php에 다음 코드를 추가하면 단축 코드가 생성되며, 페이지 또는 글에서 이 단축 코드를 사용하면 다른 언어로 번역된 글을 포함하여 단어가 1500개 이상인 모든 글이 페이지별로 나열된 목록(페이지당 10개 항목)을 표시합니다. 이 쇼트코드는 폴리랑과 폴리랑 없이도 작동합니다.
이 값은 // 2. 설정에서 변경할 수 있습니다(쿼리로 인해 응답 속도가 느려지는 것을 원하지 않는다면 표시할 총 페이지 수를 너무 많이 늘리지 마세요). 초안 페이지나 비공개 페이지에 단축 코드를 사용하면 방문자가 몰려 성능에 영향을 미치고 리소스가 제한된 경우 서버의 CPU를 소모하는 위험을 피할 수 있습니다.)
이 코드를 사용하면 글에 몇 개의 나누기가 있는지(또는 전혀 없는지)뿐만 아니라 각 나누기에 포함된 단어 수를 표시하여 단어가 여러 섹션에 어떻게 분포되어 있는지 확인할 수 있으므로 현재 페이지 매김이 균형을 이루고 있는지 평가하는 데 매우 유용할 수 있습니다.
콘텐츠 유형 자동 감지
4가지 콘텐츠 유형에 대한 자동 감지 기능이 추가되며, 여기서 '나누기'는 x단어마다 나누기를 의미합니다. 계산은 정보 밀도를 기반으로 하며 최대 한도는 구성할 수 있습니다.
기술 (700단어/단락): 튜토리얼, 코드
내러티브 (1200단어/단락): 스토리, 내러티브
저널리즘 (900단어/단락): 뉴스, 기사
일반 (1000단어/휴식): 표준 콘텐츠
선택 기준
코드 기준에 대해서는 팁과 참고 자료를 검색해 보았습니다. 일부 매개 변수는 블로그에 맞게 조정했지만 이는 항상 탄력적이며 각 사이트에 맞게 미세 조정할 수 있으므로 언제든지 더 많은 신호를 추가하여 감지를 더 정확하게 만들 수 있습니다. 이를 출발점으로 삼아 글에서 유형에 따라 가장 자주 사용하는 요소를 조사하거나 코드가 식별할 수 있는 자체 브랜드와 가장 자주 사용하는 태그를 추가하세요.
현재 심사 기준은 다음과 같습니다:
A. 기술 콘텐츠 (⚙️ 700단어/단락)
- 카테고리: 튜토리얼, 가이드, 기술, 코딩, 프로그래밍
- 태그: 코드, 소프트웨어, 기술, 개발
- 구조
:폴리랑 플레이스홀더는 수정하지 않습니다.
B. 내러티브 콘텐츠 (📖 1200단어/단락)
- 카테고리: 단편소설, 역사, 서사, 문학
- 태그: 단편, 소설, 소설, 시, 시조
- 구조
:폴리랑 플레이스홀더는 수정하지 않습니다.
C. 저널리즘 콘텐츠 (📰 900단어/단락)
- 카테고리: 뉴스, 시사, 리포트, 기사
- Tags: 언론, 인터뷰, 의견
- 구조
:폴리랑 플레이스홀더는 수정하지 않습니다.
D. 일반 콘텐츠 (✍️ 1000단어/단락)
- 위의 기준과 일치하지 않을 때 적용됩니다.
- 표준 글의 기본값
코드는 다음 순서로 항목을 평가합니다:
- 게시물 카테고리
- 게시물 태그
- 콘텐츠의 구조
- 일치하는 항목이 없는 경우 '일반'을 지정합니다.
권장되는 최대 휴식 시간은 글당 5개입니다.
측면
CSS는 편의를 위해 코드에 포함되어 있으며 이 모양을 탭에 반환합니다. 표시되는 정보는 매우 명확합니다.
빠른 액세스가 더 편리하다고 생각되면 제목을 편집 페이지로 연결하도록 수정하거나 다른 개선 사항을 적용할 수 있습니다.
코드
// ======================================================================
//Displays a paginated list of long posts (ordered from most to least number of words) with suggestions for splitting them into pages according to content type
// Shortcode for Page Breaks analysis on long posts. Courtesy of /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
$config = [
'umbral_palabras' => 1500,
'posts_por_pagina' => 10,
'mostrar_todos_idiomas' => true
];
// 3. Detección de tipo de contenido
function detectar_tipo_contenido($post_id) {
$post = get_post($post_id);
$content = strip_tags($post->post_content);
// Por categorías
$categorias = wp_get_post_categories($post_id, ['fields' => 'slugs']);
if (array_intersect($categorias, ['tutorial', 'guia', 'tecnico', 'code', 'programacion'])) {
return ['tipo' => 'técnico', 'color' => '#e3f2fd', 'icono' => '⚙️'];
} elseif (array_intersect($categorias, ['relato', 'historia', 'narrativa', 'literatura'])) {
return ['tipo' => 'narrativo', 'color' => '#f3e5f5', 'icono' => '📖'];
} elseif (array_intersect($categorias, ['noticia', 'actualidad', 'reportaje', 'articulo'])) {
return ['tipo' => 'periodístico', 'color' => '#e8f5e9', 'icono' => '📰'];
}
// Por etiquetas
$etiquetas = wp_get_post_tags($post_id, ['fields' => 'slugs']);
if (array_intersect($etiquetas, ['codigo', 'software', 'tecnologia', 'dev'])) {
return ['tipo' => 'técnico', 'color' => '#e3f2fd', 'icono' => '⚙️'];
} elseif (array_intersect($etiquetas, ['cuento', 'novela', 'ficcion', 'poesia'])) {
return ['tipo' => 'narrativo', 'color' => '#f3e5f5', 'icono' => '📖'];
}
// Por estructura
if (preg_match_all('/```|<pre>|<code>/i', $content) > 2) {
return ['tipo' => 'técnico', 'color' => '#e3f2fd', 'icono' => '⚙️'];
} elseif (preg_match_all('/\n—|\n"|\n\d+\. /i', $content) > 3) {
return ['tipo' => 'narrativo', 'color' => '#f3e5f5', 'icono' => '📖'];
} elseif (preg_match_all('/\n### |\n#### |\*Nota:/i', $content) > 3) {
return ['tipo' => 'periodístico', 'color' => '#e8f5e9', 'icono' => '📰'];
}
return ['tipo' => 'general', 'color' => '#f5f5f5', 'icono' => '✍️'];
}
// 4. Cálculo de breaks por tipo
function calcular_breaks_por_tipo($palabras, $tipo) {
$config = [
'técnico' => 700,
'narrativo' => 1200,
'periodístico' => 900,
'general' => 1000
];
$breaks = max(1, floor($palabras / ($config[$tipo] ?? 1000)) - 1);
return min($breaks, 5); // Máximo 5 breaks
}
// 5. Función para calcular distribución de palabras entre breaks
function calcular_distribucion_palabras($content) {
$total_palabras = str_word_count(strip_tags($content));
$breaks = substr_count($content, '<!--nextpage-->');
if ($breaks === 0) {
return [];
}
// Dividir el contenido por los breaks
$secciones = explode('<!--nextpage-->', $content);
$distribucion = [];
foreach ($secciones as $seccion) {
$palabras_seccion = str_word_count(strip_tags($seccion));
$distribucion[] = $palabras_seccion;
}
return $distribucion;
}
// 6. Función para obtener todos los IDs de posts a procesar
function obtener_ids_posts_a_procesar($mostrar_todos_idiomas) {
$args = [
'post_type' => 'post',
'posts_per_page' => -1,
'fields' => 'ids',
'post_status' => 'publish'
];
// Si Polylang está activo y queremos mostrar todos los idiomas
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));
}
return array_unique($all_post_ids);
}
// Caso normal (sin Polylang o no mostrar todos los idiomas)
$query = new WP_Query($args);
return $query->posts;
}
// 7. Obtener y procesar posts
$posts_procesados = [];
$post_ids = obtener_ids_posts_a_procesar($config['mostrar_todos_idiomas']);
foreach ($post_ids as $post_id) {
procesar_post_con_tipo($post_id, $config['umbral_palabras'], $posts_procesados);
}
// 8. Ordenar por palabras (mayor a menor)
usort($posts_procesados, function($a, $b) {
return $b['palabras'] - $a['palabras'];
});
// 9. Paginación manual
$paged = max(1, get_query_var('paged'));
$total_posts = count($posts_procesados);
$total_paginas = ceil($total_posts / $config['posts_por_pagina']);
$offset = ($paged - 1) * $config['posts_por_pagina'];
$posts_paginados = array_slice($posts_procesados, $offset, $config['posts_por_pagina']);
// 10. CSS con mejoras visuales
$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; /* Aumentado de 0.8em a 0.95em */
font-weight: 600; /* Negrita añadida */
color: #444; /* Color más oscuro para mejor contraste */
background: #f8f8f8; /* Fondo sutil */
padding: 2px 6px;
border-radius: 4px;
border-left: 2px solid #64b5f6; /* Borde izquierdo azul */
}
.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' => __('«'),
'next_text' => __('»'),
'mid_size' => 1
]);
$output .= '</div>';
}
} else {
$output .= '<div class="pb-ultimate-item">No se encontraron posts que requieran paginación.</div>';
}
return $output . '</div>';
}
// Función auxiliar: Procesar post con tipo
function procesar_post_con_tipo($post_id, $umbral, &$posts) {
$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 = 'es'; // Valor por defecto
// Detectar idioma si Polylang está activo
if (function_exists('pll_get_post_language')) {
$idioma = pll_get_post_language($post_id) ?: 'es';
}
$posts[] = [
'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
];
}
}
add_shortcode('analizador_pagebreaks_ultimate', 'analizador_pagebreaks_completo');