\n"); exit(1); } $LANGS = ['en', 'fr', 'it', 'pt']; function resolve_post_from_href($href) { if (preg_match('~[?&]p=(\d+)~', $href, $m)) { return (int)$m[1]; } $path = preg_replace('~^https?://[^/]+~i', '', $href); $path = preg_replace('~[?#].*$~', '', $path); $path = preg_replace('~^/fea~', '', $path); $path = preg_replace('~^/(en|fr|it|pt|es)(/|$)~', '/', $path); $segs = array_values(array_filter(explode('/', $path), 'strlen')); if (count($segs) !== 1) { return 0; // categorías, home, rutas multi-segmento: no aplica } $p = get_page_by_path($segs[0], OBJECT, ['post', 'page']); return $p ? $p->ID : 0; } function href_lang_prefix($href) { $path = preg_replace('~^https?://[^/]+~i', '', $href); $path = preg_replace('~^/fea~', '', $path); if (preg_match('~^/(en|fr|it|pt)(/|$)~', $path, $m)) { return $m[1]; } return null; } $translations = pll_get_post_translations($CARTA); if (empty($translations)) { fwrite(STDERR, "El post $CARTA no tiene grupo de traducciones Polylang.\n"); exit(1); } echo "Verificando enlaces de la carta ES=$CARTA\n"; echo "Traducciones: " . json_encode($translations) . "\n\n"; $total_sin_traducir = 0; $total_mismatch = 0; $total_rotos = 0; foreach ($LANGS as $lang) { if (empty($translations[$lang])) { echo "[$lang] SIN TRADUCCIÓN DE LA CARTA (no existe post en este idioma)\n\n"; continue; } $pid = $translations[$lang]; $post = get_post($pid); if (!$post) { echo "[$lang] post $pid no encontrado\n\n"; continue; } // Captura cualquier href absoluto http(s) — funciona tanto en local (dominio // Tailscale) como en prod (www.feadulta.com); resolve_post_from_href() ya // descarta lo que no resuelva a un post interno (externos, mailto, etc.). preg_match_all('~href="(https?://[^"]+)"~i', $post->post_content, $m); $hrefs = array_values(array_unique($m[1])); $issues = []; foreach ($hrefs as $href) { $target_id = resolve_post_from_href($href); if (!$target_id) { continue; // home, categorías, rutas no resolubles a un post concreto } $target_lang = pll_get_post_language($target_id) ?: 'es'; $prefix = href_lang_prefix($href); if ($target_lang === $lang && $prefix === $lang) { continue; // todo correcto } // Averiguar el ES de origen del contenido apuntado, para buscar la traducción correcta if ($target_lang === 'es') { $es_id = $target_id; } else { $tr = pll_get_post_translations($target_id); $es_id = $tr['es'] ?? null; } $correct_id = $es_id ? pll_get_post($es_id, $lang) : null; if ($correct_id && $correct_id != $target_id) { $correct_url = get_permalink($correct_id); $issues[] = [ 'tipo' => 'MISMATCH', 'detalle' => "href=\"$href\" resuelve a post $target_id (lang=$target_lang) — correcto: $correct_url (post $correct_id)", ]; $total_mismatch++; } elseif ($correct_id && $correct_id == $target_id && $prefix !== $lang) { // resuelve al post correcto pero con URL/slug distinta a la actual (permalink cambió) $correct_url = get_permalink($correct_id); $issues[] = [ 'tipo' => 'MISMATCH', 'detalle' => "href=\"$href\" no coincide con el permalink actual: $correct_url (post $correct_id)", ]; $total_mismatch++; } elseif (!$correct_id && $target_lang !== $lang) { $issues[] = [ 'tipo' => 'SIN_TRADUCIR', 'detalle' => "href=\"$href\" -> post $target_id (lang=$target_lang) sin traducción en '$lang'", ]; $total_sin_traducir++; } } echo "[$lang] post $pid — " . count($hrefs) . " enlaces internos, " . count($issues) . " con problemas\n"; foreach ($issues as $issue) { echo " {$issue['tipo']}: {$issue['detalle']}\n"; } echo "\n"; } echo "RESUMEN: $total_sin_traducir sin traducir, $total_mismatch mismatch de idioma/slug\n"; exit(($total_sin_traducir + $total_mismatch + $total_rotos) > 0 ? 2 : 0);