Files
feadulta/docs/guia-traduccion-autoservicio-inma-mixbot.md
T
rafa d2f79ae953 docs: cerrar el bloqueante de traducción — endpoint crear-traduccion ya desplegado
Actualiza la guía de autoservicio de traducción con la llamada real a
fea/v1/crear-traduccion (#225, ya en producción), verificada de extremo a
extremo por Claudix contra prod con un post desechable.
2026-08-31 07:50:52 -04:00

9.6 KiB

Guía de traducción (EN/FR/IT/PT) en autoservicio — para Inma / Mixbot

Para quién es este documento: para Inma y Mixbot, a partir de la conversación del 2026-08-31 en Buzz: además del audio (#222, ver docs/guia-tts-audio-autoservicio-inma-mixbot.md), Rafa confirmó que la traducción se traspasa también. El endpoint para enlazar traducciones (PR #225) se hizo bajo el paraguas del propio #222.

Motores autorizados por Rafa (2026-08-31): un agente local con Haiku desde el propio Claude/Cowork de Inma (no la API de pago de Rafa — es otra cuenta, otro caso), o MiniMax con el mismo token ya compartido para el audio. Gemma queda fuera para vosotros: es un modelo local que corre en la GPU del PC de Rafa, no accesible desde fuera.


0. Crear y enlazar la traducción — ya resuelto

Ya existe un endpoint que crea el post traducido y lo enlaza con Polylang (idioma + grupo de traducción) en una sola llamada — no hace falta tocar nada de bajo nivel:

POST https://www.feadulta.com/wp-json/fea/v1/crear-traduccion
Authorization: Basic <usuario:contraseña_de_aplicación>
Content-Type: multipart/form-data

es_id=<ID del post ES>
lang=en|fr|it|pt
title=<título traducido>
content=<HTML traducido>
excerpt=<opcional>
status=draft|publish   (default draft)
model=<opcional, solo trazabilidad — p.ej. "minimax" o "haiku-local">

Respuesta (201 si crea, 200 si ya existía — es idempotente, repetir la llamada con el mismo es_id/lang nunca duplica):

{"es_id": 56786, "lang": "en", "translation_id": 56787, "created": true,
 "url": "https://www.feadulta.com/en/?p=56787"}

Por detrás replica exactamente lo que hacía scripts/fea_translate_helper.php::create en local: asigna el idioma antes de las categorías, mapea cada categoría del ES a su equivalente traducida (o la deja en español si no existe traducción de esa categoría), preserva el resto del grupo de traducciones si el ES ya tenía otros idiomas, y añade los metas de trazabilidad (traduccion_automatica, traduccion_origen, traduccion_modelo, traduccion_fecha).

Verificado por Claudix el 2026-08-31 con una prueba real de extremo a extremo en producción (post ES desechable → traducción EN creada → confirmado con pll_get_post_translations que el grupo quedó {es: ..., en: ...} — todo borrado después).


1. Las reglas de traducción (igual las use vuestro Claude/Haiku o MiniMax)

Esto es el prompt real que usa el pipeline de Rafa (scripts/translate_post.py), verificado palabra por palabra el 2026-08-31 contra el código — usadlo tal cual, no lo resumáis:

Eres un traductor profesional de textos religiosos cristianos (espiritualidad y teología católica). Traduce del español al {idioma destino}. REGLAS ESTRICTAS:

  1. Conserva EXACTAMENTE el marcado HTML (etiquetas y atributos) y los shortcodes entre [ ] y { }. No los traduzcas ni los reordenes.
  2. NO traduzcas las referencias bíblicas ni sus abreviaturas (p. ej. "Jn 3, 16", "Isaías 5, 1-7", "Mt 5"). Déjalas idénticas.
  3. Conserva los nombres propios de persona y lugar (salvo exónimos establecidos).
  4. Glosario fijo: "Fe Adulta" → "Fe Adulta" (en/fr/it/pt, no se traduce); "EFFA" → "EFFA" (igual, en los 4 idiomas).
  5. Traducción FIEL: no resumas, no añadas, no comentes.

Para el título, además: que quede natural en el idioma destino (no lo dejéis en español salvo que sea un nombre propio o una marca), y si es un modismo, traducid el sentido, no lo calquéis literal.

2. El "learning" del título — dadle el primer párrafo como contexto

Esto es lo que pedía Rafa explícitamente documentar. Aviso de precisión: este cambio existe como commit (4475ebb, rama feat/issue-200-title-context) pero no está fusionado a main — verificado el 2026-08-31 comparando el histórico. O sea: si alguna vez volvéis a usar scripts/translate_post.py tal cual desde main, el título se traduce SIN este contexto. Para vuestro proceso propio (agente Haiku o llamada directa a MiniMax) aplicad la técnica de todas formas, es la que ya dio mejor resultado en pruebas:

  • Traducid primero el cuerpo del artículo, el título al final — necesitáis el cuerpo ya traducido (o al menos leído) para poder darle contexto al título.
  • Al traducir el título, añadid al prompt el primer párrafo del cuerpo en español (recortado a 2000 caracteres), como contexto de significado — no se traduce ni se incluye literal en la salida, solo ayuda al modelo a entender modismos o dobles sentidos del título que sin ese contexto salen mal traducidos.
  • Instrucción exacta añadida al prompt del título: "Si es un modismo, interpreta su sentido según el contexto, no lo calques."

3. Troceo de artículos largos

Si usáis MiniMax para traducir (no Haiku, que admite el artículo entero de una vez gracias a su contexto de 200k), trocead el HTML por párrafos completos (nunca a mitad de una etiqueta) en bloques de ~5.000 caracteres, y unid las traducciones de cada trozo en el mismo orden.

4. QA — checklist antes de dar una traducción por buena

Reformulado en REST puro (el script original, gate_mec2.php, corre dentro de WP y usa funciones de Polylang no disponibles desde fuera — pero las comprobaciones sí son igual de válidas mirando el JSON de cada post por la API):

Para cada origen ES y cada idioma que hayáis traducido, comparad contra el post ES:

  1. Cobertura: los 4 idiomas presentes (o los que toque — Pagola nunca lleva traducción automática, ver §5).
  2. Estado: la traducción en draft hasta que se revise (salvo que el ES ya esté publish y la carta lo pida — ver docs/guia-publicacion-carta-inma.md).
  3. Sin residuos de generación: buscar literalmente en el content devuelto por la API cadenas como </strong>**, </strong></strong>, &lt;&gt;, <<<INI>>>, <<<FIN>>>, |||FIN, o cualquier resto de las marcas del prompt.
  4. Sin alfabetos ajenos: ningún carácter árabe/cirílico/CJK en el resultado (residuo típico de generación que se cuela cuando el modelo "piensa" en otro idioma).
  5. Mismo nº de párrafos que el ES (contar apariciones de <p en el HTML). Un desajuste suele significar que el modelo se comió contenido en el troceado.
  6. Mismo nº de enlaces <a href> que el ES — es el check que más falla y el más importante: en la carta 739, el italiano perdió 15 de 39 enlaces en uno de los trozos troceados mientras EN/FR salieron bien, y nada posterior lo detecta si no lo comprobáis aquí. Si hay menos enlaces en la traducción que en el ES, no la deis por buena.
  7. Referencias bíblicas equivalentes: extraer patrones tipo capítulo,versículo de ambos textos y comparar solo capítulo:versículo, no la abreviatura del libro completa (el inglés cambia la coma por dos puntos y traduce el nombre: Mt 15,21-28 en ES pasa a Matthew 15:21-28 en EN — comparar literal daría un falso positivo de "referencia perdida").
  8. Título distinto del ES (salvo portugués, donde algunos nombres propios/citas coinciden de forma legítima).

Capa editorial adicional si tenéis acceso a un modelo capaz de juzgar fidelidad (vuestro Claude/Haiku local sirve para esto): pedidle que lea origen y traducción y devuelva un veredicto approve / needs_review / rewrite con la cita exacta del problema — descartad cualquier hallazgo cuya cita no aparezca literal en el texto (evita ediciones basadas en alucinaciones del propio revisor).

5. Excepciones que no lleváis por la vía automática

  • Autores que aportan su propia traducción humana (hoy, Pagola: 4 DOCX por carta, adjuntos en el issue de la semana) — nunca generéis fallback automático para ellos aunque falte alguno de los 4 idiomas.
  • Textos litúrgicos enlazados desde la carta (evangelio, lecturas) — confirmar con Rafa si entran en el alcance de "todo lo de la carta" o se dejan fuera; ha habido confusión antes sobre esto (no dar por hecho que "ya deben estar traducidos").

6. Cómo leer la cuota de MiniMax

Con el mismo token que ya tenéis para el audio:

curl -s https://api.minimax.io/v1/token_plan/remains \
  -H "Authorization: Bearer <TOKEN>" -H "Content-Type: application/json"

Del JSON de respuesta:

  • base_resp.status_code debe ser 0 (cualquier otro valor es error de cuenta/plan, ver base_resp.status_msg).
  • model_remains es una lista por modelo. El de texto/traducción es el que tiene model_name == "general"; los de audio son los speech-2.8-* que ya usáis para TTS.
  • De cada entrada: current_interval_remaining_percent (cuánto queda de la ventana de 5h que se resetea sola — end_time es cuándo) y current_weekly_remaining_percent / weekly_end_time (la ventana semanal, más amplia). Restad a 100 para saber "cuánto habéis gastado" si preferís verlo así.

Igual que con el audio: un HTTP 200 no es garantía de cuota — cuando t2a_v2 o el endpoint de traducción devuelvan base_resp.status_code=2056 (plan agotado) o 1039 (límite de ritmo), parad y reintentad más tarde, no machaquéis la API.

7. Qué falta para que esto sea autoservicio de verdad

Con el endpoint del §0 ya no queda ningún bloqueante de infraestructura conocido para crear y enlazar traducciones por REST. Lo que sigue siendo trabajo vuestro, no de infraestructura: generar el texto (motores del principio del documento), aplicar el QA del §4 antes de llamar al endpoint, y decidir cuándo pasáis una traducción de draft a publish (mismo criterio que la carta en español, ver docs/guia-publicacion-carta-inma.md).