[en, fr, it, pt]. Claves normalizadas con trim. */ function fea_menu_map(): array { static $m = null; if ($m !== null) return $m; $m = [ // ── Menú principal (header) ── 'PORTADA' => ['Home', 'Accueil', 'Home', 'Início'], 'Quiénes somos' => ['About us', 'À propos', 'Chi siamo', 'Quem somos'], 'Colaboradores' => ['Contributors', 'Collaborateurs', 'Collaboratori', 'Colaboradores'], 'Este portal' => ['This portal', 'Ce portail', 'Questo portale', 'Este portal'], 'Para poner al día la Fe' => ['Bringing faith up to date', 'Mettre la foi à jour', 'Aggiornare la fede', 'Atualizar a fé'], 'Cartas' => ['Letters', 'Lettres', 'Lettere', 'Cartas'], 'Esta semana' => ['This week', 'Cette semaine', 'Questa settimana', 'Esta semana'], 'Semana pasada' => ['Last week', 'Semaine dernière', 'Settimana scorsa', 'Semana passada'], 'Otras semanas' => ['Other weeks', 'Autres semaines', 'Altre settimane', 'Outras semanas'], 'Acceso a webs anteriores:' => ['Previous websites:', 'Anciens sites :', 'Siti precedenti:', 'Sites anteriores:'], 'Web V1 — FrontPage (2006-2012)' => ['Site V1 — FrontPage (2006-2012)', 'Site V1 — FrontPage (2006-2012)', 'Sito V1 — FrontPage (2006-2012)', 'Site V1 — FrontPage (2006-2012)'], 'Web V2 — Joomla (2012-2026)' => ['Site V2 — Joomla (2012-2026)', 'Site V2 — Joomla (2012-2026)', 'Sito V2 — Joomla (2012-2026)', 'Site V2 — Joomla (2012-2026)'], 'Nueva política de datos' => ['New data policy', 'Nouvelle politique de données', 'Nuova politica sui dati', 'Nova política de dados'], 'Contactar' => ['Contact', 'Contact', 'Contatti', 'Contactar'], 'Para contactar con nosotros' => ['Contact us', 'Nous contacter', 'Per contattarci', 'Para contactar-nos'], 'Para recibir la carta de novedades' => ['Subscribe to the newsletter', 'Recevoir la newsletter', 'Ricevere la newsletter', 'Receber a newsletter'], 'Para inscribirse en la Escuela' => ['Enrol in the School', 'S\'inscrire à l\'École', 'Iscriversi alla Scuola', 'Inscrever-se na Escola'], '🎓 Escuela' => ['🎓 School', '🎓 École', '🎓 Scuola', '🎓 Escola'], '📚 Librería 🛒' => ['📚 Bookshop 🛒', '📚 Librairie 🛒', '📚 Libreria 🛒', '📚 Livraria 🛒'], 'Buscar' => ['Search', 'Rechercher', 'Cerca', 'Pesquisar'], // ── Menús del pie ── 'Cartas que nos llegan' => ['Letters we receive', 'Lettres que nous recevons', 'Lettere che riceviamo', 'Cartas que recebemos'], 'Tablón de anuncios' => ['Notice board', 'Tableau d\'annonces', 'Bacheca', 'Mural de avisos'], 'Asociación FeAdulta' => ['FeAdulta Association', 'Association FeAdulta', 'Associazione FeAdulta', 'Associação FeAdulta'], 'La suma de todos' => ['The sum of all', 'La somme de tous', 'La somma di tutti', 'A soma de todos'], 'Comunidades cristianas' => ['Christian communities', 'Communautés chrétiennes', 'Comunità cristiane', 'Comunidades cristãs'], 'El Evangelio de cada día' => ['The daily Gospel', 'L\'Évangile de chaque jour', 'Il Vangelo di ogni giorno', 'O Evangelho de cada dia'], 'Índice cronológico' => ['Chronological index', 'Index chronologique', 'Indice cronologico', 'Índice cronológico'], 'Índice cronológico' => ['Chronological index', 'Index chronologique', 'Indice cronologico', 'Índice cronológico'], 'Evangelios y comentarios' => ['Gospels and commentaries', 'Évangiles et commentaires', 'Vangeli e commenti', 'Evangelhos e comentários'], 'Oraciones eucarísticas' => ['Eucharistic prayers', 'Prières eucharistiques', 'Preghiere eucaristiche', 'Orações eucarísticas'], 'A modo de salmos' => ['In the manner of psalms', 'À la manière de psaumes', 'A mo\' di salmi', 'À maneira de salmos'], 'Preces y oraciones varias' => ['Prayers and various orations', 'Prières et oraisons diverses', 'Preci e orazioni varie', 'Preces e orações várias'], 'Primeras lecturas' => ['First readings', 'Premières lectures', 'Prime letture', 'Primeiras leituras'], 'Autores' => ['Authors', 'Auteurs', 'Autori', 'Autores'], 'Temas' => ['Topics', 'Thèmes', 'Temi', 'Temas'], 'Multimedia' => ['Multimedia', 'Multimédia', 'Multimedia', 'Multimédia'], 'Índice de pensamientos' => ['Index of reflections', 'Index des pensées', 'Indice dei pensieri', 'Índice de pensamentos'], 'Cantoral' => ['Hymnal', 'Recueil de chants', 'Canzoniere', 'Cancioneiro'], 'Películas' => ['Films', 'Films', 'Film', 'Filmes'], 'Reseñas de libros' => ['Book reviews', 'Critiques de livres', 'Recensioni di libri', 'Resenhas de livros'], 'In memoriam' => ['In memoriam', 'In memoriam', 'In memoriam', 'In memoriam'], ]; return $m; } /** Índice de columna por idioma. */ function fea_menu_lang_col(string $lang): int { return ['en' => 0, 'fr' => 1, 'it' => 2, 'pt' => 3][$lang] ?? -1; } function fea_menu_tr(string $label, int $col): ?string { $map = fea_menu_map(); $key = trim($label); if (isset($map[$key][$col]) && $map[$key][$col] !== '') return $map[$key][$col]; return null; } /** * Remapea una URL ES al destino traducido si existe (post/página/categoría). * Devuelve la URL original si no hay traducción o no se resuelve. */ function fea_menu_localize_url(string $url, string $lang): string { if ($url === '' || preg_match('~^(mailto:|tel:|javascript:|#)~i', $url)) return $url; // Solo enlaces internos de ESTE sitio (no Librería, no webs anteriores externas). $host = parse_url($url, PHP_URL_HOST); if ($host) { $site_host = parse_url(home_url('/'), PHP_URL_HOST); if ($host !== $site_host) return $url; // externo } $path = trim((string) parse_url($url, PHP_URL_PATH), '/'); // quitar subcarpeta local (fea) y prefijo de idioma (es/en/…), con o sin barra final $path = preg_replace('#^fea(/|$)#', '', $path); $path = preg_replace('#^(es|en|fr|it|pt)(/|$)#', '', $path); if ($path === '') { // raíz del sitio → portada del idioma return function_exists('pll_home_url') ? pll_home_url($lang) : $url; } // categoría: category/. El slug del menú es ES; buscamos SIN filtro de // idioma de Polylang (lang => '') porque el render va en otro idioma. if (preg_match('#^category/([^/]+)/?$#', $path, $mm)) { $terms = get_terms(['taxonomy' => 'category', 'slug' => $mm[1], 'hide_empty' => false, 'number' => 1, 'lang' => '']); $term = (!is_wp_error($terms) && $terms) ? $terms[0] : null; if ($term && function_exists('pll_get_term')) { $tr = pll_get_term($term->term_id, $lang); if ($tr) { $link = get_category_link($tr); if ($link && !is_wp_error($link)) return $link; } } return $url; } // página/post por último segmento (lang => '' para no filtrar por idioma actual) $slug = basename($path); $q = get_posts(['name' => $slug, 'post_type' => ['post', 'page'], 'numberposts' => 1, 'post_status' => 'publish', 'lang' => '']); $page = $q ? $q[0] : null; if ($page && function_exists('pll_get_post')) { $tr = pll_get_post($page->ID, $lang); if ($tr) { $link = get_permalink($tr); if ($link) return $link; } } return $url; } add_filter('render_block', function ($content, $block) { if (empty($block['blockName'])) return $content; if ($block['blockName'] !== 'core/navigation-link' && $block['blockName'] !== 'core/navigation-submenu') return $content; if (!function_exists('pll_current_language')) return $content; $lang = pll_current_language(); $col = fea_menu_lang_col((string) $lang); if ($col < 0) return $content; // es o desconocido → sin tocar $label = isset($block['attrs']['label']) ? (string) $block['attrs']['label'] : ''; $url = isset($block['attrs']['url']) ? (string) $block['attrs']['url'] : ''; // 1) etiqueta if ($label !== '') { $tr = fea_menu_tr($label, $col); if ($tr !== null && $tr !== $label) { $content = str_replace('>' . esc_html($label) . '<', '>' . esc_html($tr) . '<', $content); } } // 2) URL — localizar el href realmente renderizado (robusto frente a discrepancias attr/HTML) $content = preg_replace_callback('/href=("|\')([^"\']*)\1/i', function ($m) use ($lang) { $href = html_entity_decode($m[2], ENT_QUOTES); $loc = fea_menu_localize_url($href, (string) $lang); return 'href=' . $m[1] . esc_url($loc) . $m[1]; }, $content, 1); return $content; }, 10, 2);