feat(i18n): #136 reasignación de categorías traducidas + autores bíblicos por idioma

- trad_cats.php: traduce categorías estructurales (Nuevo/Antiguo Testamento, etc.)
- fix_catnames.php: corrige nombres de términos traducidos
- autores_biblicos.php: crea usuarios NT/AT por idioma y reasigna autor de lecturas
- reasign_cats.php: reasigna a cada post no-ES las categorías del ES traducidas a
  su idioma (crea término espejo si falta). Idempotente/resumible (_cats_reasignadas).
  7977 posts procesados, 0 con categoría en idioma incorrecto.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 15:10:27 -04:00
parent 0bc58bfa96
commit 70831f13f0
4 changed files with 175 additions and 0 deletions
+40
View File
@@ -0,0 +1,40 @@
<?php
$DEF = [
408 => ['en'=>'New Testament','fr'=>'Nouveau Testament','it'=>'Nuovo Testamento','pt'=>'Novo Testamento'],
409 => ['en'=>'Old Testament','fr'=>'Ancien Testament','it'=>'Antico Testamento','pt'=>'Antigo Testamento'],
];
$LOGIN = [408=>'nt', 409=>'at'];
// 1) crear/asegurar usuarios por idioma
$uid_map = []; // [es_author][lang] => uid
foreach ($DEF as $es_author => $names) {
foreach ($names as $L => $dn) {
$login = $LOGIN[$es_author].'-'.$L;
$u = get_user_by('login', $login);
if (!$u) {
$id = wp_insert_user(['user_login'=>$login,'user_pass'=>wp_generate_password(20),
'user_email'=>$login.'@feadulta.local','display_name'=>$dn,'role'=>'author','first_name'=>$dn]);
if (is_wp_error($id)) { echo "ERROR user $login: ".$id->get_error_message()."\n"; continue; }
echo "creado usuario $login (#$id) = $dn\n";
} else { $id = $u->ID; wp_update_user(['ID'=>$id,'display_name'=>$dn]); echo "existe $login (#$id)\n"; }
$uid_map[$es_author][$L] = $id;
}
}
// 2) reasignar autor de las traducciones según el autor del ES original
$reasig = 0;
foreach ([408,409] as $es_author) {
$posts = get_posts(['author'=>$es_author,'post_type'=>'post','post_status'=>['publish','draft'],
'posts_per_page'=>-1,'fields'=>'ids','lang'=>'es','no_found_rows'=>true]);
foreach ($posts as $es_id) {
$tr = pll_get_post_translations($es_id);
foreach (['en','fr','it','pt'] as $L) {
if (empty($tr[$L]) || empty($uid_map[$es_author][$L])) continue;
$p = get_post($tr[$L]);
if ($p && (int)$p->post_author !== (int)$uid_map[$es_author][$L]) {
wp_update_post(['ID'=>$tr[$L],'post_author'=>$uid_map[$es_author][$L]]);
$reasig++;
}
}
}
echo "ES autor $es_author: ".count($posts)." posts ES procesados\n";
}
echo "traducciones reasignadas de autor: $reasig\n";
+26
View File
@@ -0,0 +1,26 @@
<?php
$D = [
410 => ['en'=>'New Testament','fr'=>'Nouveau Testament','it'=>'Nuovo Testamento','pt'=>'Novo Testamento'],
411 => ['en'=>'Old Testament','fr'=>'Ancien Testament','it'=>'Antico Testamento','pt'=>'Antigo Testamento'],
49 => ['en'=>'Advent and Christmas','fr'=>'Avent et Noël','it'=>'Avvento e Natale','pt'=>'Advento e Natal'],
12 => ['en'=>'In Memoriam','fr'=>'In Memoriam','it'=>'In Memoriam','pt'=>'In Memoriam'],
1651 => ['en'=>'News','fr'=>'Actualités','it'=>'Notizie','pt'=>'Notícias'],
61 => ['en'=>'Christian Communities','fr'=>'Communautés chrétiennes','it'=>'Comunità cristiane','pt'=>'Comunidades cristãs'],
23 => ['en'=>'Letters We Receive','fr'=>'Lettres reçues','it'=>'Lettere che riceviamo','pt'=>'Cartas que recebemos'],
39 => ['en'=>'Topics','fr'=>'Thèmes','it'=>'Temi','pt'=>'Temas'],
27 => ['en'=>'Chronological Index','fr'=>'Index chronologique','it'=>'Indice cronologico','pt'=>'Índice cronológico'],
63 => ['en'=>'EFFA','fr'=>'EFFA','it'=>'EFFA','pt'=>'EFFA'],
];
$fixed=0;
foreach ($D as $es=>$names) {
foreach ($names as $L=>$correct) {
$t = pll_get_term($es, $L);
if (!$t) { echo " $es/$L sin término\n"; continue; }
$cur = get_term($t)->name;
if ($cur !== $correct) {
wp_update_term($t, 'category', ['name'=>$correct]);
echo " #$t [$L] \"$cur\" \"$correct\"\n"; $fixed++;
}
}
}
echo "nombres corregidos: $fixed\n";
+70
View File
@@ -0,0 +1,70 @@
<?php
/**
* reasign_cats.php — #136: reasigna las categorías de los posts traducidos (en/fr/it/pt)
* a la versión Polylang de su idioma. Si la categoría no tiene traducción, crea un
* término espejo (mismo nombre) en ese idioma y lo asocia. Idempotente y resumible
* (marca _cats_reasignadas=1).
*
* Ejecutar: docker exec wordpress-web wp eval-file /tmp/reasign_cats.php [LANG] [LIMIT] --allow-root
* sin args: procesa los 4 idiomas. Con LANG (en/fr/it/pt) y LIMIT acota.
*/
$only_lang = (isset($argv[1]) && in_array($argv[1], ['en','fr','it','pt'], true)) ? $argv[1] : null;
$limit = isset($argv[2]) ? (int) $argv[2] : 0;
$mirror_cache = []; // "cat_es|lang" => term_id
function translate_or_mirror($c, $lang, &$cache) {
$key = $c . '|' . $lang;
if (isset($cache[$key])) return $cache[$key];
$t = pll_get_term($c, $lang);
if (!$t) {
$term = get_term($c, 'category');
if (!$term || is_wp_error($term)) { return $cache[$key] = $c; }
$slug = sanitize_title($term->name) . '-' . $lang . '-' . $c;
$res = wp_insert_term($term->name, 'category', ['slug' => $slug]);
if (is_wp_error($res)) {
$ex = $res->get_error_data();
if (!$ex) { return $cache[$key] = $c; }
$t = is_array($ex) ? ($ex['term_id'] ?? 0) : (int) $ex;
} else {
$t = $res['term_id'];
}
if ($t) {
pll_set_term_language($t, $lang);
$tr = pll_get_term_translations($c);
$tr[$lang] = $t;
pll_save_term_translations($tr);
} else {
$t = $c;
}
}
return $cache[$key] = (int) $t;
}
$langs = $only_lang ? [$only_lang] : ['en','fr','it','pt'];
$done = 0; $changed = 0; $mirrors = 0;
$start_mirror_terms = count($mirror_cache);
foreach ($langs as $lang) {
$args = ['lang'=>$lang,'post_type'=>'post','post_status'=>['publish','draft'],
'fields'=>'ids','posts_per_page'=> $limit ?: -1,'no_found_rows'=>true,
'meta_query'=>[['key'=>'_cats_reasignadas','compare'=>'NOT EXISTS']]];
$ids = get_posts($args);
foreach ($ids as $pid) {
// Fuente de verdad = categorías del ES original (las del post traducido
// pueden estar incompletas). Se traducen al idioma del post.
$es = pll_get_post($pid, 'es');
$source = ($es && $es != $pid) ? wp_get_post_categories($es) : wp_get_post_categories($pid);
$new = [];
foreach ($source as $c) {
$clang = pll_get_term_language($c);
$new[] = ($clang === $lang) ? $c : translate_or_mirror($c, $lang, $mirror_cache);
}
$new = array_values(array_unique($new));
$cur = wp_get_post_categories($pid);
sort($cur); $cmp = $new; sort($cmp);
if ($cur !== $cmp && $new) { wp_set_post_categories($pid, $new); $changed++; }
update_post_meta($pid, '_cats_reasignadas', 1);
$done++;
}
}
echo "procesados: $done | con cambios: $changed | términos espejo en caché: " . count($mirror_cache) . "\n";
+39
View File
@@ -0,0 +1,39 @@
<?php
require_once '/var/www/html/wp-load.php';
// Categorías estructurales de contenido → traducciones (es_term_id => [en,fr,it,pt])
$D = [
410 => ['Nuevo Testamento', 'New Testament','Nouveau Testament','Nuovo Testamento','Novo Testamento'],
411 => ['Antiguo Testamento','Old Testament','Ancien Testament','Antico Testamento','Antigo Testamento'],
49 => ['Adviento y Navidad','Advent and Christmas','Avent et Noël','Avvento e Natale','Advento e Natal'],
12 => ['In memoriam','In Memoriam','In Memoriam','In Memoriam','In Memoriam'],
1651 => ['Noticias','News','Actualités','Notizie','Notícias'],
61 => ['Comunidades cristianas','Christian Communities','Communautés chrétiennes','Comunità cristiane','Comunidades cristãs'],
23 => ['Cartas que nos llegan','Letters We Receive','Lettres reçues','Lettere che riceviamo','Cartas que recebemos'],
39 => ['Temas','Topics','Thèmes','Temi','Temas'],
27 => ['Índice cronológico','Chronological Index','Index chronologique','Indice cronologico','Índice cronológico'],
63 => ['EFFA','EFFA','EFFA','EFFA','EFFA'],
];
$LangSlug = ['en'=>'en','fr'=>'fr','it'=>'it','pt'=>'pt'];
$created=0;
foreach ($D as $es_id => $names) {
$es_term = get_term($es_id, 'category');
if (!$es_term || is_wp_error($es_term)) { echo "skip $es_id (no existe)\n"; continue; }
$existing = pll_get_term_translations($es_id);
$group = $existing;
$i = 1;
foreach (['en','fr','it','pt'] as $L) {
$i++;
if (!empty($existing[$L])) { $group[$L]=$existing[$L]; continue; }
$name = $names[$i-1];
$slug = sanitize_title($name).'-'.$L;
$res = wp_insert_term($name, 'category', ['slug'=>$slug]);
if (is_wp_error($res)) { echo " $es_id $L ERROR: ".$res->get_error_message()."\n"; continue; }
$tid = $res['term_id'];
pll_set_term_language($tid, $L);
$group[$L] = $tid;
$created++;
}
pll_save_term_translations($group);
echo "#$es_id ".$names[0]."".implode(",", array_map(function($k,$v){return $k.":".$v;}, array_keys($group),$group))."\n";
}
echo "términos traducidos creados: $created\n";