Script Genèric de Vinculació WPML

Script PHP per vincular traduccions de posts/CPT importats a WordPress amb WPML.

Què fa

Vincula automàticament posts o custom post types que han estat importats amb camps meta de traducció, connectant-los correctament a WPML perquè apareguin les banderes i el selector d’idiomes funcioni.

Requisits previs

IMPORTANT: Abans d’importar els posts, assegura’t d’incloure aquests dos custom fields:

  1. wpmltranslationid – ID que agrupa les traduccions del mateix contingut
    • Exemple: Post CA id=100, Post ES id=200, Post EN id=300 → tots amb wpmltranslationid = "1"
  2. wpmllanguagecode – Codi d’idioma de cada post
    • Valors: caesen (o els idiomes que tinguis configurats)

Exemple d’importació CSV:

id,title,wpmltranslationid,wpmllanguagecode
100,"Títol en català",1,ca
200,"Título en español",1,es
300,"Title in english",1,en

Configuració

Edita les variables al principi del fitxer:

$CONFIG = [
    'security_key' => 'mmkt2024',              // Canvia-ho per seguretat
    'post_type' => 'post',                     // 'post', 'page', 'product', etc.
    'translation_id_meta' => 'wpmltranslationid',
    'language_code_meta' => 'wpmllanguagecode',
    'languages' => ['ca', 'es', 'en'],         // Idiomes del teu site
    'original_lang' => 'ca',                   // Idioma original/principal
    'require_complete_groups' => true,         // true = només vincular grups amb tots els idiomes
    'allow_partial_groups' => false,           // true = vincular també grups sense idioma original
];

Ús

  1. Puja el fitxer wpml-link-generic.php a l’arrel del WordPress
  2. Accedeix via navegador:
   https://elseuteu.com/wpml-link-generic.php?key=mmkt2024
  1. Segueix els passos:
    • Verificació: Veure estadístiques i exemples
    • Previsualització: Revisar què es vincularà
    • Execució: Fer la vinculació
  2. Elimina el fitxer després d’usar-lo

Advertències

  • ⚠️ Fes backup de la base de dades abans d’executar
  • ⚠️ Revisa la previsualització abans d’executar
  • ⚠️ Els grups sense l’idioma original (CA per defecte) no es processaran
  • ⚠️ Si un grup ja està vinculat correctament, s’ignora
  • ⚠️ Aquest script només funciona si els posts ja tenen els custom fields wpmltranslationid i wpmllanguagecode

Casos d’ús

Importació estàndard (3 idiomes complets)

'require_complete_groups' => true  // Només vincular CA+ES+EN

Importació parcial (acceptar grups incomplets)

'require_complete_groups' => false  // Vincular fins i tot si falta algun idioma
'allow_partial_groups' => true      // Vincular grups sense idioma original

Altres post types

'post_type' => 'product'  // Per WooCommerce
'post_type' => 'page'     // Per pàgines

Noms de camps personalitzats

'translation_id_meta' => 'custom_trans_id',
'language_code_meta' => 'custom_lang_code',

Troubleshooting

“No s’han trobat posts” → Comprova que els meta fields wpmltranslationid i wpmllanguagecode existeixen als posts

“Grups sense CA” → Assegura’t que cada grup té un post amb wpmllanguagecode = ca

“Ja vinculat” → Els posts ja estan correctament vinculats a WPML, no cal fer res

<?php
/**
 * Script genèric de vinculació WPML
 * Author: Milimetric Marketing
 * Plugin URI: https://milimetricmkt.com
 * 
 * CONFIGURACIÓ (canvia aquests valors segons la teva importació):
 */
// === CONFIGURACIÓ EDITABLE ===
$CONFIG = [
    // Clau de seguretat (canvia-la!)
    'security_key' => 'mmkt2024',
    
    // Post type a vincular (post, page, product, etc.)
    'post_type' => 'post',
    
    // Meta keys dels camps importats
    'translation_id_meta' => 'wpmltranslationid',  // Camp que agrupa les traduccions
    'language_code_meta' => 'wpmllanguagecode',     // Camp amb l'idioma (ca, es, en...)
    
    // Configuració d'idiomes
    'languages' => ['ca', 'es', 'en'],  // Idiomes disponibles
    'original_lang' => 'ca',            // Idioma original
    
    // Opcions
    'require_complete_groups' => true,   // true = només vincular grups amb tots els idiomes
    'allow_partial_groups' => false,     // true = vincular també grups sense idioma original
];
// === FI CONFIGURACIÓ ===
// Seguretat
if (!isset($_GET['key']) || $_GET['key'] !== $CONFIG['security_key']) {
    die('Accés denegat');
}
require_once('wp-load.php');
define('WP_USE_THEMES', false);
$mode = $_GET['mode'] ?? 'check';  // check | link | execute
?>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Vinculació WPML - <?php echo strtoupper($CONFIG['post_type']); ?></title>
    <style>
        body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; padding: 20px; background: #f5f5f5; }
        .container { background: white; padding: 30px; border-radius: 8px; max-width: 1400px; margin: 0 auto; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
        h2, h3 { color: #2c3e50; border-bottom: 3px solid #3498db; padding-bottom: 10px; }
        .nav { margin: 20px 0; padding: 15px; background: #ecf0f1; border-radius: 5px; }
        .nav a { display: inline-block; padding: 10px 20px; margin: 0 5px; background: #3498db; color: white; text-decoration: none; border-radius: 5px; }
        .nav a:hover { background: #2980b9; }
        .nav a.active { background: #2c3e50; }
        .box { padding: 20px; margin: 20px 0; border-radius: 5px; border-left: 5px solid; }
        .success { background: #d4edda; border-color: #28a745; color: #155724; }
        .warning { background: #fff3cd; border-color: #ffc107; color: #856404; }
        .error { background: #f8d7da; border-color: #dc3545; color: #721c24; }
        .info { background: #d1ecf1; border-color: #17a2b8; color: #0c5460; }
        table { width: 100%; border-collapse: collapse; margin: 20px 0; font-size: 13px; }
        th, td { padding: 12px 8px; text-align: left; border: 1px solid #ddd; }
        th { background: #34495e; color: white; font-weight: 600; position: sticky; top: 0; }
        tr:nth-child(even) { background: #f8f9fa; }
        tr:hover { background: #e9ecef; }
        .group { margin: 15px 0; padding: 15px; background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 5px; }
        .badge { display: inline-block; padding: 4px 8px; border-radius: 3px; font-size: 11px; font-weight: bold; margin: 0 3px; }
        .badge-success { background: #28a745; color: white; }
        .badge-warning { background: #ffc107; color: #000; }
        .badge-danger { background: #dc3545; color: white; }
        .badge-info { background: #17a2b8; color: white; }
        .btn { display: inline-block; padding: 12px 24px; margin: 10px 5px; border-radius: 5px; text-decoration: none; font-weight: 600; cursor: pointer; border: none; }
        .btn-primary { background: #3498db; color: white; }
        .btn-primary:hover { background: #2980b9; }
        .btn-success { background: #28a745; color: white; }
        .btn-success:hover { background: #218838; }
        .btn-danger { background: #dc3545; color: white; }
        .btn-danger:hover { background: #c82333; }
        .config-box { background: #e8f4f8; padding: 15px; margin: 20px 0; border-radius: 5px; font-family: monospace; font-size: 12px; }
        .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin: 20px 0; }
        .stat-card { background: white; padding: 20px; border-radius: 5px; border: 2px solid #e0e0e0; text-align: center; }
        .stat-value { font-size: 32px; font-weight: bold; color: #2c3e50; }
        .stat-label { color: #7f8c8d; margin-top: 5px; }
    </style>
</head>
<body>
<div class="container">
    <h2>🔗 Vinculació WPML - <?php echo strtoupper($CONFIG['post_type']); ?></h2>
    
    <div class="nav">
        <a href="?key=<?php echo $CONFIG['security_key']; ?>&mode=check" class="<?php echo $mode == 'check' ? 'active' : ''; ?>">📋 Verificació</a>
        <a href="?key=<?php echo $CONFIG['security_key']; ?>&mode=link" class="<?php echo $mode == 'link' ? 'active' : ''; ?>">🔗 Previsualització</a>
    </div>
    
    <?php
    global $wpdb;
    
    // Mostrar configuració actual
    echo '<div class="config-box">';
    echo '<strong>⚙️ Configuració actual:</strong><br>';
    echo "Post Type: <strong>{$CONFIG['post_type']}</strong><br>";
    echo "Meta Translation ID: <strong>{$CONFIG['translation_id_meta']}</strong><br>";
    echo "Meta Language Code: <strong>{$CONFIG['language_code_meta']}</strong><br>";
    echo "Idiomes: <strong>" . implode(', ', $CONFIG['languages']) . "</strong><br>";
    echo "Idioma original: <strong>{$CONFIG['original_lang']}</strong><br>";
    echo "Només grups complets: <strong>" . ($CONFIG['require_complete_groups'] ? 'Sí' : 'No') . "</strong><br>";
    echo '</div>';
    
    // Obtenir posts
    $posts_data = $wpdb->get_results($wpdb->prepare("
        SELECT 
            p.ID,
            p.post_title,
            p.post_type,
            pm.meta_value as translation_id,
            pml.meta_value as language_code,
            t.trid as current_trid,
            t.language_code as wpml_lang,
            t.source_language_code
        FROM {$wpdb->posts} p
        INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = %s
        LEFT JOIN {$wpdb->postmeta} pml ON p.ID = pml.post_id AND pml.meta_key = %s
        INNER JOIN {$wpdb->prefix}icl_translations t 
            ON p.ID = t.element_id 
            AND t.element_type = %s
        WHERE p.post_status = 'publish'
        AND p.post_type = %s
        ORDER BY pm.meta_value, pml.meta_value
    ", 
        $CONFIG['translation_id_meta'],
        $CONFIG['language_code_meta'],
        'post_' . $CONFIG['post_type'],
        $CONFIG['post_type']
    ));
    
    if (empty($posts_data)) {
        echo '<div class="box error"><strong>❌ No s\'han trobat posts amb els meta fields especificats.</strong><br>';
        echo 'Comprova que els camps <code>' . $CONFIG['translation_id_meta'] . '</code> i <code>' . $CONFIG['language_code_meta'] . '</code> existeixen.</div>';
        echo '</div></body></html>';
        exit;
    }
    
    // Agrupar per translation_id
    $groups = [];
    foreach ($posts_data as $post) {
        $groups[$post->translation_id][] = $post;
    }
    
    // Analitzar grups
    $stats = [
        'total_posts' => count($posts_data),
        'total_groups' => count($groups),
        'complete_groups' => 0,
        'incomplete_groups' => 0,
        'with_original' => 0,
        'without_original' => 0,
        'needs_linking' => 0,
        'already_linked' => 0
    ];
    
    $processable_groups = [];
    $examples_complete = [];
    $examples_incomplete = [];
    
    foreach ($groups as $tid => $posts) {
        $langs = array_map(fn($p) => strtolower($p->language_code ?? ''), $posts);
        $has_original = in_array($CONFIG['original_lang'], $langs);
        $is_complete = count($posts) == count($CONFIG['languages']) && 
                       count(array_intersect($langs, $CONFIG['languages'])) == count($CONFIG['languages']);
        
        if ($has_original) $stats['with_original']++;
        else $stats['without_original']++;
        
        if ($is_complete) {
            $stats['complete_groups']++;
            if (count($examples_complete) < 3) {
                $examples_complete[] = ['tid' => $tid, 'posts' => $posts];
            }
        } else {
            $stats['incomplete_groups']++;
            if (count($examples_incomplete) < 3) {
                $examples_incomplete[] = ['tid' => $tid, 'posts' => $posts];
            }
        }
        
        // Comprovar si necessita vinculació
        $trids = array_unique(array_map(fn($p) => $p->current_trid, $posts));
        $needs_linking = count($trids) > 1;
        
        if ($needs_linking) {
            $stats['needs_linking']++;
        } else {
            $stats['already_linked']++;
        }
        
        // Determinar si es pot processar
        $can_process = false;
        if ($CONFIG['require_complete_groups']) {
            $can_process = $is_complete && $has_original && $needs_linking;
        } else {
            $can_process = ($has_original || $CONFIG['allow_partial_groups']) && count($posts) >= 2 && $needs_linking;
        }
        
        if ($can_process) {
            $processable_groups[$tid] = $posts;
        }
    }
    
    // === MODE VERIFICACIÓ ===
    if ($mode == 'check') {
        echo '<h3>📊 Estadístiques</h3>';
        
        echo '<div class="stats-grid">';
        echo '<div class="stat-card"><div class="stat-value">' . $stats['total_posts'] . '</div><div class="stat-label">Posts totals</div></div>';
        echo '<div class="stat-card"><div class="stat-value">' . $stats['total_groups'] . '</div><div class="stat-label">Grups de traducció</div></div>';
        echo '<div class="stat-card"><div class="stat-value" style="color:#28a745">' . $stats['complete_groups'] . '</div><div class="stat-label">Grups complets</div></div>';
        echo '<div class="stat-card"><div class="stat-value" style="color:#ffc107">' . $stats['incomplete_groups'] . '</div><div class="stat-label">Grups incomplets</div></div>';
        echo '<div class="stat-card"><div class="stat-value" style="color:#17a2b8">' . $stats['with_original'] . '</div><div class="stat-label">Amb ' . strtoupper($CONFIG['original_lang']) . '</div></div>';
        echo '<div class="stat-card"><div class="stat-value" style="color:#dc3545">' . count($processable_groups) . '</div><div class="stat-label">Per vincular</div></div>';
        echo '</div>';
        
        // Exemples de grups complets
        if (!empty($examples_complete)) {
            echo '<h3>✅ Exemples de grups COMPLETS</h3>';
            foreach ($examples_complete as $ex) {
                $trids = array_unique(array_map(fn($p) => $p->current_trid, $ex['posts']));
                $needs = count($trids) > 1;
                
                echo '<div class="group">';
                echo '<strong>Translation ID: ' . $ex['tid'] . '</strong> ';
                echo $needs ? '<span class="badge badge-warning">Necessita vinculació</span>' : '<span class="badge badge-success">Ja vinculat</span>';
                echo '<table><tr><th>ID</th><th>Idioma</th><th>TRID</th><th>Source</th><th>Títol</th></tr>';
                
                usort($ex['posts'], function($a, $b) use ($CONFIG) {
                    $order = array_flip($CONFIG['languages']);
                    return ($order[strtolower($a->language_code)] ?? 99) - ($order[strtolower($b->language_code)] ?? 99);
                });
                
                foreach ($ex['posts'] as $p) {
                    echo '<tr>';
                    echo '<td>' . $p->ID . '</td>';
                    echo '<td><strong>' . strtoupper($p->language_code) . '</strong></td>';
                    echo '<td>' . $p->current_trid . '</td>';
                    echo '<td>' . ($p->source_language_code ?: 'NULL') . '</td>';
                    echo '<td>' . substr($p->post_title, 0, 50) . '</td>';
                    echo '</tr>';
                }
                echo '</table></div>';
            }
        }
        
        // Exemples de grups incomplets
        if (!empty($examples_incomplete)) {
            echo '<h3>⚠️ Exemples de grups INCOMPLETS</h3>';
            foreach ($examples_incomplete as $ex) {
                $langs = array_map(fn($p) => strtolower($p->language_code), $ex['posts']);
                $missing = array_diff($CONFIG['languages'], $langs);
                
                echo '<div class="group">';
                echo '<strong>Translation ID: ' . $ex['tid'] . '</strong> ';
                echo '<span class="badge badge-warning">Falten: ' . implode(', ', $missing) . '</span>';
                echo '<table><tr><th>ID</th><th>Idioma</th><th>TRID</th><th>Títol</th></tr>';
                
                foreach ($ex['posts'] as $p) {
                    echo '<tr>';
                    echo '<td>' . $p->ID . '</td>';
                    echo '<td><strong>' . strtoupper($p->language_code) . '</strong></td>';
                    echo '<td>' . $p->current_trid . '</td>';
                    echo '<td>' . substr($p->post_title, 0, 50) . '</td>';
                    echo '</tr>';
                }
                echo '</table></div>';
            }
        }
        
        // Conclusió
        echo '<div class="box info">';
        echo '<h3>📝 Resum</h3>';
        if (count($processable_groups) > 0) {
            echo "<p><strong style='color:#28a745'>✅ Hi ha " . count($processable_groups) . " grups preparats per vincular.</strong></p>";
            echo '<a href="?key=' . $CONFIG['security_key'] . '&mode=link" class="btn btn-primary">➡️ Anar a previsualització</a>';
        } else {
            if ($stats['needs_linking'] == 0) {
                echo '<p><strong style="color:#28a745">✅ Tots els grups ja estan correctament vinculats!</strong></p>';
            } else {
                echo '<p><strong style="color:#dc3545">⚠️ No hi ha grups que compleixin els requisits per vincular.</strong></p>';
                if ($CONFIG['require_complete_groups']) {
                    echo '<p>Intenta desactivar <code>require_complete_groups</code> si vols vincular grups incomplets.</p>';
                }
            }
        }
        echo '</div>';
    }
    
    // === MODE PREVISUALITZACIÓ ===
    elseif ($mode == 'link') {
        if (empty($processable_groups)) {
            echo '<div class="box error"><strong>❌ No hi ha grups per vincular.</strong></div>';
            echo '<a href="?key=' . $CONFIG['security_key'] . '&mode=check" class="btn btn-primary">◀️ Tornar a verificació</a>';
            echo '</div></body></html>';
            exit;
        }
        
        echo '<div class="box warning">';
        echo '<h3>⚠️ MODE PREVISUALITZACIÓ</h3>';
        echo '<p>Es vincularan <strong>' . count($processable_groups) . ' grups</strong> amb un total de <strong>' . array_sum(array_map('count', $processable_groups)) . ' posts</strong>.</p>';
        echo '</div>';
        
        echo '<h3>Primers 10 grups que es vincularan:</h3>';
        
        $count = 0;
        foreach ($processable_groups as $tid => $posts) {
            if ($count++ >= 10) break;
            
            // Trobar post original
            $original_post = null;
            foreach ($posts as $p) {
                if (strtolower($p->language_code) == $CONFIG['original_lang']) {
                    $original_post = $p;
                    break;
                }
            }
            
            if (!$original_post && !$CONFIG['require_complete_groups']) {
                $original_post = $posts[0];
            }
            
            $new_trid = min(array_map(fn($p) => $p->current_trid, $posts));
            
            echo '<div class="group">';
            echo "<strong>Translation ID: {$tid}</strong> → Nou TRID: <strong>{$new_trid}</strong><br>";
            echo '<table><tr><th>ID</th><th>Idioma</th><th>TRID actual</th><th>Nou TRID</th><th>Source Lang</th><th>Acció</th></tr>';
            
            usort($posts, function($a, $b) use ($CONFIG) {
                $order = array_flip($CONFIG['languages']);
                return ($order[strtolower($a->language_code)] ?? 99) - ($order[strtolower($b->language_code)] ?? 99);
            });
            
            foreach ($posts as $p) {
                $is_original = ($original_post && $p->ID == $original_post->ID);
                $new_source = $is_original ? 'NULL' : $CONFIG['original_lang'];
                $action = $is_original ? '<span class="badge badge-success">ORIGINAL</span>' : '<span class="badge badge-info">Traducció</span>';
                
                echo '<tr>';
                echo '<td>' . $p->ID . '</td>';
                echo '<td><strong>' . strtoupper($p->language_code) . '</strong></td>';
                echo '<td>' . $p->current_trid . '</td>';
                echo '<td><strong>' . $new_trid . '</strong></td>';
                echo '<td>' . $new_source . '</td>';
                echo '<td>' . $action . '</td>';
                echo '</tr>';
            }
            
            echo '</table></div>';
        }
        
        if (count($processable_groups) > 10) {
            echo '<p><em>... i ' . (count($processable_groups) - 10) . ' grups més</em></p>';
        }
        
        echo '<div class="box warning">';
        echo '<h3>⚠️ IMPORTANT abans d\'executar:</h3>';
        echo '<ol>';
        echo '<li><strong>Fes un backup de la base de dades</strong></li>';
        echo '<li>Revisa que els exemples són correctes</li>';
        echo '<li>Assegura\'t que l\'idioma original és el correcte</li>';
        echo '</ol>';
        echo '<a href="?key=' . $CONFIG['security_key'] . '&mode=execute" class="btn btn-danger">▶️ EXECUTAR VINCULACIÓ</a> ';
        echo '<a href="?key=' . $CONFIG['security_key'] . '&mode=check" class="btn btn-primary">◀️ Tornar</a>';
        echo '</div>';
    }
    
    // === MODE EXECUCIÓ ===
    elseif ($mode == 'execute') {
        if (empty($processable_groups)) {
            echo '<div class="box error"><strong>❌ No hi ha grups per vincular.</strong></div>';
            echo '</div></body></html>';
            exit;
        }
        
        echo '<div class="box info"><h3>🔄 Executant vinculació...</h3></div>';
        
        $updated = 0;
        $errors = 0;
        $log = [];
        
        foreach ($processable_groups as $tid => $posts) {
            // Trobar post original
            $original_post = null;
            foreach ($posts as $p) {
                if (strtolower($p->language_code) == $CONFIG['original_lang']) {
                    $original_post = $p;
                    break;
                }
            }
            
            if (!$original_post && !$CONFIG['require_complete_groups']) {
                $original_post = $posts[0];
            }
            
            if (!$original_post) {
                $log[] = "⚠️ Grup {$tid}: No s'ha trobat post original, saltat";
                continue;
            }
            
            $new_trid = min(array_map(fn($p) => $p->current_trid, $posts));
            
            foreach ($posts as $p) {
                $is_original = ($p->ID == $original_post->ID);
                
                $result = $wpdb->update(
                    $wpdb->prefix . 'icl_translations',
                    [
                        'trid' => $new_trid,
                        'source_language_code' => $is_original ? null : $CONFIG['original_lang']
                    ],
                    [
                        'element_id' => $p->ID,
                        'element_type' => 'post_' . $CONFIG['post_type']
                    ]
                );
                
                if ($result !== false) {
                    $updated++;
                    $log[] = "✓ Post {$p->ID} [{$p->language_code}] → TRID {$new_trid}";
                } else {
                    $errors++;
                    $log[] = "✗ ERROR: Post {$p->ID} [{$p->language_code}]";
                }
            }
        }
        
        echo '<div class="box success">';
        echo '<h3>✅ Vinculació completada!</h3>';
        echo "<p>Posts actualitzats: <strong>{$updated}</strong></p>";
        echo "<p>Grups vinculats: <strong>" . count($processable_groups) . "</strong></p>";
        if ($errors > 0) {
            echo "<p style='color:#dc3545'>Errors: <strong>{$errors}</strong></p>";
        }
        echo '</div>';
        
        if (!empty($log)) {
            echo '<div class="box info">';
            echo '<h3>📝 Log (primers 30):</h3>';
            echo '<pre style="max-height:400px; overflow-y:auto; font-size:11px; background:#2c3e50; color:#ecf0f1; padding:15px; border-radius:5px;">';
            foreach (array_slice($log, 0, 30) as $line) {
                echo $line . "\n";
            }
            if (count($log) > 30) {
                echo "... i " . (count($log) - 30) . " operacions més\n";
            }
            echo '</pre>';
            echo '</div>';
        }
        
        echo '<div class="box info">';
        echo '<h3>📋 Següents passos:</h3>';
        echo '<ol>';
        echo '<li>Comprova al backend que els posts estan vinculats</li>';
        echo '<li>Prova el selector d\'idiomes al frontend</li>';
        echo '<li>Si tot funciona, <strong>elimina aquest fitxer</strong></li>';
        echo '</ol>';
        echo '<a href="' . admin_url('edit.php?post_type=' . $CONFIG['post_type']) . '" class="btn btn-success" target="_blank">📝 Anar al backend</a> ';
        echo '<a href="?key=' . $CONFIG['security_key'] . '&mode=check" class="btn btn-primary">◀️ Nova verificació</a>';
        echo '</div>';
        
        // Netejar cache
        if (function_exists('icl_cache_clear')) {
            icl_cache_clear();
        }
    }
    
    ?>
    
    <hr>
    <p style="text-align:center; color:#7f8c8d; font-size:12px;">
        <em>Script genèric de vinculació WPML - Milimetric Marketing - https://milimetricmkt.com</em>
    </p>
</div>
</body>
</html>

CCS pel menu de WPMl a kadence (menu amb subrallat)

/* wpml */
.mobile-menu-container .wpml-ls-item a {
    justify-content: center;
}
.primary-menu-container .wpml-ls-first-item {
    position: relative;
    padding-left: 12px;
    margin-left: 12px;
}
.primary-menu-container .wpml-ls-first-item::before {
    content: '';
    position: absolute;
    top: 50%;
    left: 0;
    transform: translateY(-50%);
    width: 2px;
    height: 20px;
    background-color: var(--global-palette3);
}
.header-navigation[class*="header-navigation-style-underline"] .header-menu-container.primary-menu-container>ul>li.wpml-ls-current-language>a {
    color:  var(--global-palette2);
}
.header-navigation[class*="header-navigation-style-underline"] .header-menu-container.primary-menu-container>ul>li.wpml-ls-current-language>a:after {
    transform: scale(1, 1) translate(50%, 0);
}

Kadence, canviar el titols de archive i tax

El més importat és el tema de afegir prioritat al filtre, amb 100 es suficient, de moment.

/**
 * Canviar títols d'arxiu a Kadence
 * IMPORTANT: Prioritat alta (100) necessària perquè Kadence processa després
 */
function change_archive_titles( $title ) {
    if ( is_post_type_archive( 'exposicio' ) ) {
        return __( 'Les exposicions', 'kadence' );
    }
    if ( is_tax() && get_queried_object_id() == 4 ) {
        $term_name = get_term( 4, 'estat' )->name;
        return sprintf(__('Les exposicions %s','kadence'), $term_name);
    }
    return $title;
}
add_filter( 'get_the_archive_title', 'change_archive_titles', 100 );

Traduïr titols de Archive a kadence

// WPML translate CPT 'immoble' archive title on es
function wpml_translate_cpt_archive_title($title)
{
    if (apply_filters( 'wpml_current_language', null ) == 'es' && is_post_type_archive('immoble')) {
        $title = __('Inmuebles', 'kadence-child');
    }
    return $title;
}
add_filter('get_the_archive_title', 'wpml_translate_cpt_archive_title', 100);

CSS per al Switcher de WPML

Integració a la capçalera de kadence theme

/* wpml */
.header-html .wpml-ls-legacy-list-horizontal.wpml-ls-statics-footer {
    margin-bottom: 0;
}
.wpml-ls-item {
    position: relative;
    
}
.wpml-ls-item .wpml-ls-link {
    padding-left: calc(2em / 2);
    padding-right: calc(2em / 2);
    padding-top: 0.6em;
    padding-bottom: 0.6em;
}
.wpml-ls-item.wpml-ls-current-language:after {
    content: '';
    width: 100%;
    position: absolute;
    bottom: 0px;
    height: 2px;
    right: 50%;
    background: var(--global-palette1);
    -webkit-transform: scale(0, 0) translate(-50%, 0);
    transform: scale(0, 0) translate(-50%, 0);
    transition: color .0s ease-in-out, -webkit-transform .2s ease-in-out;
    transition: transform .2s ease-in-out, color .0s ease-in-out;
    transition: transform .2s ease-in-out, color .0s ease-in-out, -webkit-transform .2s ease-in-out;
    width: calc(100% - 2em);
    transform: scale(1, 1) translate(50%, 0);
}
.transparent-header .wpml-ls-item.wpml-ls-current-language:after {
   background: var(--global-palette9); 
}
.wpml-ls-item.wpml-ls-current-language .wpml-ls-link {
    color: var(--global-palette1);
}
/* Si posem el shortcode dins del menú mobile, sinó esborrar o no actua */
#mobile-drawer .wpml-ls-item.wpml-ls-current-language:after,
#mobile-drawer .mobile-html .wpml-ls-item.wpml-ls-current-language .wpml-ls-link {
    color: var(--global-palette9);
}

Kadence css “fixings”

/* Transitions */
@view-transition {
    navigation: auto;  
}
/*  Kadence css “fixings” */
body {
    text-wrap: pretty;
}
/* header mobile */
.popup-drawer .drawer-content.content-align-center {
    overflow-y: auto;
    max-width: 90vh;
}
.mobile-navigation {
    max-height: 100%;
}
.kt-btn-width-type-auto {
    width: auto;
}
.kb-adv-form-message.kb-adv-form-warning {
    padding: 0.1em 1em;
}
.new-row-diferent-height .kt-inner-column-height-full:not(.kt-row-layout-row) {
    grid-auto-rows: unset;
}
@media (max-width: 1024px) {
    #block-8 figure {
        margin: 0 auto;
    }
    .kt-inner-column-height-full.kt-tab-layout-equal, .kt-inner-column-height-full:not(.kt-tab-layout-inherit):not(.kt-tab-layout-row) {
        grid-auto-rows: unset;
    }
}

One page links per Kadence

function highlightAnchorLinks() {
  // Get the menu elements
  const primaryMenu = document.getElementById('primary-menu');
  const mobileMenu = document.getElementById('mobile-menu');
  // Get all anchor links within the primary menu
  const primaryAnchorLinks = primaryMenu.querySelectorAll('li > a[href*="#"]');
  // Get all anchor links within the mobile menu
  const mobileAnchorLinks = mobileMenu.querySelectorAll('li > a[href*="#"]');
  // Function to extract the target ID from the href attribute
  const extractTargetId = (href) => {
    const hashIndex = href.indexOf('#');
    return hashIndex !== -1 ? href.substring(hashIndex + 1) : null;
  };
  // Create an intersection observer instance for the primary menu
  const primaryObserver = new IntersectionObserver(entries => {
    entries.forEach(entry => {
      const targetId = entry.target.getAttribute('id');
      const listItem = primaryMenu.querySelector(`li > a[href$="#${targetId}"]`).parentNode;
      if (entry.isIntersecting && entry.intersectionRatio >= 0.5) {
        const activeListItem = primaryMenu.querySelector('.current-menu-item');
        if (activeListItem) {
          activeListItem.classList.remove('current-menu-item', 'current_page_item');
        }
        listItem.classList.add('current-menu-item', 'current_page_item');
      } else {
        listItem.classList.remove('current-menu-item', 'current_page_item');
      }
    });
  }, { threshold: [0.5] });
  // Create an intersection observer instance for the mobile menu
  const mobileObserver = new IntersectionObserver(entries => {
    entries.forEach(entry => {
      const targetId = entry.target.getAttribute('id');
      const listItem = mobileMenu.querySelector(`li > a[href$="#${targetId}"]`).parentNode;
      if (entry.isIntersecting && entry.intersectionRatio >= 0.5) {
        const activeListItem = mobileMenu.querySelector('.current-menu-item');
        if (activeListItem) {
          activeListItem.classList.remove('current-menu-item', 'current_page_item');
        }
        listItem.classList.add('current-menu-item', 'current_page_item');
      } else {
        listItem.classList.remove('current-menu-item', 'current_page_item');
      }
    });
  }, { threshold: [0.5] });
  // Observe each section for the primary menu
  primaryAnchorLinks.forEach(anchorLink => {
    const targetId = extractTargetId(anchorLink.getAttribute('href'));
    const section = document.getElementById(targetId);
    if (section) {
      primaryObserver.observe(section);
    }
  });
  // Observe each section for the mobile menu
  mobileAnchorLinks.forEach(anchorLink => {
    const targetId = extractTargetId(anchorLink.getAttribute('href'));
    const section = document.getElementById(targetId);
    if (section) {
      mobileObserver.observe(section);
    }
  });
}
// Call the function when the DOM is fully loaded
document.addEventListener('DOMContentLoaded', highlightAnchorLinks);
Wiki MilimetricMKT
Resum de la privadesa

Aquest lloc web utilitza galetes per tal de proporcionar-vos la millor experiència d’usuari possible. La informació de les galetes s’emmagatzema al navegador i realitza funcions com ara reconèixer-vos quan torneu a la pàgina web i ajuda a l'equip a comprendre quines seccions del lloc web us semblen més interessants i útils.