Problemas de traducción Polylang/WPML: por qué se mezclan los idiomas y cómo solucionarlo

¿Te fue útil?

Tu página de inicio está en inglés. Tu menú está en francés. Tu página de producto está a medias en español porque un widget decidió improvisar. Eso no es “multilingüe”; es una mezcladora de contenido.

Cuando Polylang o WPML “mezclan idiomas”, rara vez es aleatorio. Es un modo de fallo predecible: contexto de idioma incorrecto, IDs de objetos erróneos, claves de caché equivocadas o una migración que silenciosamente dejó relaciones de traducción en la cuneta. Tratémoslo como un incidente: reproducir, medir, aislar y aplicar soluciones que sobrevivan a la próxima actualización del plugin.

Qué es (y qué no es) la “mezcla de idiomas”

La gente describe muchos errores diferentes como “WPML está mezclando idiomas” o “Polylang está roto”. Bajo el capó hay algunas clases distintas de problemas:

  • Errores de contexto: WordPress renderiza la página en el Idioma A, pero un widget/consulta se ejecuta en el Idioma B porque no heredó el idioma actual o lo sobrescribió.
  • Errores de relaciones: El sitio tiene traducciones, pero los enlaces entre originales y traducciones faltan o son incorrectos. Entonces el plugin no puede encontrar el contraparte correcto y recurre a un fallback.
  • Errores de taxonomía/menús: Las entradas están traducidas, pero las categorías/etiquetas/menús no están traducidos o no se asignaron correctamente. La interfaz muestra páginas “correctas”, mientras que la navegación jala “lo que exista”.
  • Errores de caché: Tu caché almacena HTML del Idioma A y lo sirve al Idioma B porque la clave de caché no varía por idioma, cookie, URL o cabecera.
  • Errores de rewrite/canonical: Las URLs resuelven, pero la detección de idioma elige el idioma equivocado, o los prefijos de idioma en las redirecciones de reescritura son inconsistentes.
  • Errores de traducción de cadenas: Cadenas del tema, títulos de widgets o etiquetas ACF se obtienen del dominio equivocado o de la tabla de idioma incorrecta.

También: a veces no es el plugin de traducción. Un tema con consultas personalizadas “inteligentes” y IDs hardcodeados puede producir caos perfecto incluso en un sitio monolingüe. Añade multilingüe y pasa de “raro” a “incidente”.

Una verdad operacional: la salida con idiomas mezclados suele ser determinista. Si puedes reproducirlo con la misma URL + mismas cookies + mismo estado de caché, puedes arreglarlo. Si no puedes reproducirlo, a menudo es variación de caché o una carrera entre múltiples caches.

Cómo WPML y Polylang almacenan el idioma y las traducciones

WPML: grupos de traducción, códigos de idioma y “element IDs”

WPML normalmente rastrea las relaciones de traducción en una tabla que agrupa “elementos” (entradas, términos, a veces cadenas) en grupos de traducción. Cada elemento tiene un código de idioma y un ID de grupo de traducción. Si ese mapeo está mal o incompleto, WPML tiene que adivinar. Adivinar es como obtienes entradas en inglés con categorías en alemán y un selector de idioma que no lleva a ninguna parte.

Polylang: taxonomía de idiomas + post meta + relaciones

Polylang usa una taxonomía de idioma (términos que representan idiomas) y almacena relaciones entre posts traducidos. Es simple en concepto, pero hereda todos los bordes afilados habituales de WordPress: caché de términos, caché de objetos y consultas personalizadas que no respetan taxonomías.

Por qué ambos pueden mezclar idiomas aunque “la configuración parezca correcta”

Porque la página de ajustes es solo el plano de control. El plano de datos es tu base de datos, capas de caché, reglas de reescritura y lo que tu tema y plugins hagan en las consultas. En producción, el error suele ser uno de:

  • Enlaces de traducción faltantes (integridad de datos)
  • Idioma detectado incorrectamente (enrutamiento)
  • Clave de caché sin idioma (caché)
  • Consulta personalizada que ignora filtros de idioma (lógica de aplicación)

Datos interesantes y contexto histórico (por qué es difícil)

  1. WordPress no nació multilingüe. Durante años, multilingüe significó “instalar sitios separados” o “hackearlo con plugins”. El core aún asume una localización por petición.
  2. WPML popularizó los “grupos de traducción” temprano. Es una abstracción funcional, pero requiere mapeo consistente entre tipos de post, términos y meta.
  3. Polylang se apoyó en las taxonomías de WordPress. Ingenioso: reutiliza mecanismos nativos de términos, pero también hereda los problemas de caché de términos.
  4. La caché de objetos cambió el juego. Redis/Memcached hicieron WordPress rápido, y luego hicieron el multilingüe extraño porque muchos temas/plugins nunca variaron las claves de caché por idioma.
  5. WooCommerce lo complicó más. Los productos no son solo posts; son posts más variaciones, atributos y tablas de búsqueda. La descoordinación de idioma puede derivar en precios equivocados, no solo texto incorrecto.
  6. “Idioma en la URL” se volvió norma SEO. Prefijos (/fr/), subdominios y parámetros tienen comportamientos distintos de caché y reescritura. Algunos son más fáciles de razonar; ninguno es gratis.
  7. La traducción de cadenas es un segundo sistema. La traducción de contenido de posts y la traducción de cadenas de UI usan almacenamiento y rutas de búsqueda distintas, por eso es común “arreglar posts” mientras headers y widgets siguen mal.
  8. Reglas canonical y hreflang evolucionaron. Los motores de búsqueda se volvieron más estrictos con el marcado de idioma/región. Una actualización del plugin que “arregla SEO” puede romper supuestos de enrutamiento y exponer bugs latentes.
  9. Los constructores modernos (Elementor, WPBakery) añaden otra capa. Almacenan contenido en blobs serializados; los plugins de traducción se integran, pero los casos límite sobre widgets globales y plantillas siguen siendo comunes.

Guía rápida de diagnóstico

Cuando los idiomas se mezclan, no empieces por cambiar ajustes al azar. Comienza como un SRE: identifica la capa que miente.

Primero: demuestra si es caché

  • Desactiva la caché de página temporalmente (o evítala) y vuelve a probar la misma URL en dos idiomas.
  • Comprueba si el HTML difiere cuando varías la cookie de idioma, el prefijo de URL de idioma o la cabecera Accept-Language.
  • Si cambia al evitar caché: tus cachés están mal keyeados o no se purgan por idioma.

Segundo: valida la detección de idioma y el enrutamiento

  • Confirma que el plugin piensa que la petición actual es el Idioma A.
  • Verifica que los permalinks/reglas de reescritura sean consistentes y no duplicados por múltiples plugins.
  • Asegúrate de no tener ambos WPML y Polylang activos (sí, hay gente que lo hace).

Tercero: comprueba relaciones de traducción y traducción de taxonomías

  • Elige un post roto. Confirma que su traducción existe y está enlazada.
  • Revisa categorías/etiquetas/menús buscando equivalentes traducidos.
  • Verifica que las consultas del tema sean conscientes del idioma y no tengan IDs hardcodeados.

Cuarto: busca “optimizaciones” que “ayudan”

  • Drop-ins de caché de objetos (Redis), plugins de caché persistente, CDN de página completa y caché de fragmentos.
  • Cachés a nivel de tema (transients, opciones que almacenan HTML renderizado, cachés de plantillas de page builder).

Regla de decisión: si la misma URL sirve distintos idiomas sin cambio de URL/cookie/cabecera, casi siempre es contaminación de caché o fragmentos compartidos.

Tareas prácticas: comandos, salidas, decisiones (12+)

Estas tareas asumen que tienes acceso a shell y WP-CLI. Si no lo tienes, aún puedes aplicar la lógica desde el panel de hosting—solo con más clics y menos verdad.

Task 1: Confirmar qué plugin multilingüe está activo (y que haya solo uno)

cr0x@server:~$ wp plugin list --status=active
+-----------------------+--------+-----------+---------+
| name                  | status | update    | version |
+-----------------------+--------+-----------+---------+
| wpml-multilingual-cms | active | none      | 4.6.11  |
| wpml-string-translation | active | available | 3.2.7 |
| redis-cache           | active | none      | 2.5.4   |
+-----------------------+--------+-----------+---------+

Qué significa la salida: WPML está activo, junto con Redis object cache. No hay Polylang. Bien.

Decisión: Si ves WPML y Polylang activos, detente. Desactiva uno antes de depurar. Dos proveedores de contexto de idioma equivalen a caos.

Task 2: Verificar que WP-CLI puede ver la URL del sitio y el entorno

cr0x@server:~$ wp option get siteurl
https://example.com

Qué significa: Estás operando en el sitio esperado (no en una BD de staging obsoleta).

Decisión: Si la URL es incorrecta, arréglala primero. Ejecutar WP-CLI apuntando a la BD equivocada convierte “depurar” en “pérdida de datos”.

Task 3: Comprobar la estructura de permalinks (capa de rewrite)

cr0x@server:~$ wp option get permalink_structure
/%category%/%postname%/

Qué significa: Permalinks bonitos habilitados. Los plugins de idioma engancharán aquí.

Decisión: Si los permalinks están vacíos o son inconsistentes entre entornos, vacía las regras después de confirmar ajustes de idioma.

Task 4: Vaciar reglas de reescritura de forma segura

cr0x@server:~$ wp rewrite flush --hard
Success: Rewrite rules flushed.

Qué significa: Regeneró las reglas de reescritura en la base de datos y en .htaccess (si se usa).

Decisión: Si las URLs de idioma daban 404 o redireccionaban mal, prueba ahora. Si no cambia nada, el enrutamiento no es el problema principal.

Task 5: Identificar si hay caché de página completa (cabeceras)

cr0x@server:~$ curl -sI https://example.com/fr/ | egrep -i 'cache|vary|set-cookie|x-cache|cf-cache|age'
cache-control: max-age=600
vary: Accept-Encoding
x-cache: HIT
age: 534

Qué significa: Hay una caché (x-cache: HIT) y no varía por cookie de idioma ni por la URL más allá de /fr/ en sí.

Decisión: Si usas detección de idioma por cookie y la caché no varía por cookie, servirás el idioma equivocado. Arregla la caché antes de tocar ajustes de WPML/Polylang.

Task 6: Comparar la misma página en dos idiomas y buscar claves de caché idénticas

cr0x@server:~$ curl -sI https://example.com/ | egrep -i 'x-cache|age|cache-control'
x-cache: HIT
age: 410

cr0x@server:~$ curl -sI https://example.com/fr/ | egrep -i 'x-cache|age|cache-control'
x-cache: HIT
age: 410

Qué significa: Age idéntica sugiere que la caché podría estar colapsando variantes (depende del stack, pero es sospechoso).

Decisión: Si ves comportamiento de caché sospechosamente idéntico entre idiomas, evita la caché y verifica el contenido. Luego arregla el keying de la caché.

Task 7: Evitar la caché y probar la corrección de idioma (origen verdadero)

cr0x@server:~$ curl -sH 'Cache-Control: no-cache' https://example.com/fr/ | grep -i '

Qué significa: El origen renderiza correctamente en francés (al menos el atributo HTML lang).

Decisión: Si el origen es correcto pero la caché está mal, deja de depurar datos de WPML/Polylang. Arregla la capa de caché primero.

Task 8: Verificar que la caché de objetos esté habilitada (el saboteador silencioso)

cr0x@server:~$ wp redis status
Status: Connected
Client: PhpRedis (v5.3.7)
Database: 0
Prefix: wp_cache:

Qué significa: Caché de objetos persistente activada. Excelente para velocidad; peligrosa si el código no varía por contexto de idioma.

Decisión: Si los problemas multilingües aparecieron “tras habilitar Redis”, sospecha de resultados de consultas cachéadas o fragmentos sin claves por idioma.

Task 9: Limpiar la caché de objetos para ver si el error desaparece

cr0x@server:~$ wp cache flush
Success: The cache was flushed.

Qué significa: Cualquier objeto/consulta/fragmento en caché se eliminó.

Decisión: Si el problema desaparece tras el flush y vuelve después, tienes un problema de keying de caché. No programes flushs horarios y llámalo “arreglado”.

Task 10: Inspeccionar ajustes de idioma de WPML desde la base de datos (chequeo de salud)

cr0x@server:~$ wp option get icl_sitepress_settings --format=json | head
{"icl_lang_sel_type":"dropdown","language_negotiation_type":1,"urls":{"directory_for_default_language":0}}

Qué significa: El tipo de negociación de WPML 1 comúnmente indica “idioma por directorio”. Los ajustes existen y son legibles.

Decisión: Si la negociación es “cookie” o “parameter”, asegúrate de que cada capa de caché varíe en consecuencia. Si es “directory”, confirma que las reescrituras y redirecciones canónicas lo preserven.

Task 11: Comprobar si un post tiene traducciones enlazadas (WPML)

cr0x@server:~$ wp db query "SELECT element_id, element_type, trid, language_code, source_language_code FROM wp_icl_translations WHERE element_id=123;"
+------------+--------------+------+---------------+----------------------+
| element_id | element_type | trid | language_code | source_language_code |
+------------+--------------+------+---------------+----------------------+
| 123        | post_post    | 9012 | en            | NULL                 |
+------------+--------------+------+---------------+----------------------+

Qué significa: El post 123 existe en inglés, grupo de traducción 9012. Pero aún no vemos su hermano francés.

Decisión: Consulta por el trid. Si no existen otras filas, las traducciones no están enlazadas (o no existen). El selector de idioma puede recurrir al fallback equivocado.

Task 12: Encontrar todas las traducciones de ese grupo (WPML)

cr0x@server:~$ wp db query "SELECT element_id, language_code FROM wp_icl_translations WHERE trid=9012;"
+------------+---------------+
| element_id | language_code |
+------------+---------------+
| 123        | en            |
| 456        | fr            |
+------------+---------------+

Qué significa: La traducción existe (post francés ID 456) y está enlazada.

Decisión: Si la página francesa aún muestra fragmentos en inglés, lo más probable es caché, consultas del tema o taxonomías/cadenas no traducidas, en lugar de falta de enlace de traducción.

Task 13: Confirmar que los términos de taxonomía están traducidos y enlazados (términos WPML)

cr0x@server:~$ wp db query "SELECT element_id, element_type, trid, language_code FROM wp_icl_translations WHERE element_type LIKE 'tax_%' AND element_id=77;"
+------------+---------------------+------+---------------+
| element_id | element_type        | trid | language_code |
+------------+---------------------+------+---------------+
| 77         | tax_category        | 3001 | en            |
+------------+---------------------+------+---------------+

Qué significa: El término de categoría 77 solo está registrado para inglés en el mapeo de traducciones de WPML.

Decisión: Si menús/categorías mezclan idiomas, la falta de traducción de términos es sospechosa principal. Añade/adjunta términos traducidos o habilita traducción de términos para esa taxonomía.

Task 14: Detectar una consulta personalizada de tema que ignora filtros de idioma

cr0x@server:~$ wp eval 'global $wp_query; echo "lang=".(defined("ICL_LANGUAGE_CODE")?ICL_LANGUAGE_CODE:"none")."\n";'
lang=fr

Qué significa: El contexto de WPML es francés en este entorno de petición (útil cuando se ejecuta en contexto web; en CLI depende del bootstrap).

Decisión: Si WPML dice “fr” pero tu widget muestra posts en inglés, el widget probablemente ejecuta una consulta que evita los filtros de WPML (por ejemplo, suppress_filters=true) o usa SQL directo.

Task 15: Encontrar patrones de consulta sospechosos en tu código (suppress_filters)

cr0x@server:~$ grep -R --line-number "suppress_filters" /var/www/html/wp-content | head
/var/www/html/wp-content/themes/acme/functions.php:812:  'suppress_filters' => true,
/var/www/html/wp-content/plugins/acme-widgets/widget-latest.php:55: $q = new WP_Query(['suppress_filters'=>true,'post_type'=>'post']);

Qué significa: El código deshabilita explícitamente filtros. Los hooks de WPML/Polylang son filtros. Ese es tu arma humeante.

Decisión: Elimina suppress_filters, o añade restricciones de idioma explícitas mediante las APIs del plugin, o usa los helpers de contexto de idioma para acotar consultas. Si necesitas suppress_filters por rendimiento, debes gestionar manualmente el scope de idioma y las claves de caché.

Task 16: Verificar que cron no esté “útilmente” regenerando cachés en el idioma equivocado

cr0x@server:~$ wp cron event list | egrep -i 'wpml|polylang|cache|preload' | head
wpml_cache_clear         2025-12-27 03:10:00 +0000
rocket_preload_cache     2025-12-27 03:15:00 +0000

Qué significa: Existe un preloader de caché. Si solo precarga las URLs del idioma por defecto, puede sobreescribir entradas de caché compartidas.

Decisión: Haz que el preloader sea consciente del idioma (precargar variantes de URL por idioma) o arregla las claves de caché para que las variantes no puedan sobrescribirse entre sí.

Chiste #1: Un sitio multilingüe sin caché consciente del idioma es como una recepcionista bilingüe que contesta cada llamada en el último idioma que aprendió.

Los grandes modos de fallo: por qué se mezclan los idiomas

1) La clave de caché no incluye el idioma (caché de página completa)

Esta es la causa raíz más común en la vida real porque aparece como “aleatorio”. No es aleatorio; es el idioma que calentó la caché primero.

Desencadenantes típicos:

  • Cambiar de “idioma en URL” a “idioma por cookie” sin actualizar la configuración de caché.
  • Añadir un CDN o proxy inverso que ignora cookies o quita parámetros de consulta.
  • Usar un plugin de caché optimizado para sitios de un solo idioma.

Solución: variar la caché por idioma. En la práctica eso significa: URL distinta por idioma (preferible), o incluir la cookie de idioma en la clave de caché, o evitar la caché cuando haya cookie de idioma presente.

2) Caché de objetos y cachés de fragmentos sin scoping por idioma

Las cachés de página no son los únicos culpables. Una caché de objetos persistente puede servir resultados de consultas en caché entre idiomas si la clave no incorpora el idioma. WPML/Polylang intentan integrarse, pero no pueden arreglar transients personalizados o cachés de tema que escribiste en 2017 y olvidaste.

Dónde ocurre:

  • Transients que almacenan HTML renderizado para widgets
  • Opciones de tema que guardan “HTML de menú en caché”
  • Helpers personalizados “get_posts_cached()”

Solución: incluir el código de idioma en cada clave de transient. Si no puedes, elimina la optimización y sigue adelante con tu vida.

3) Consultas personalizadas que deshabilitan filtros

Si ves suppress_filters=true, asume que lo multilingüe está roto hasta demostrar lo contrario. WPML y Polylang dependen de filtros para ajustar consultas.

Solución: quitar suppress_filters, o añadir restricciones de idioma explícitas usando las APIs del plugin, o usar sus helpers de contexto de idioma para acotar las consultas.

4) Traducciones de taxonomías faltantes o mal enlazadas

Así es como obtienes una página en francés que lista categorías en inglés, o un selector de idioma que mantiene el mismo slug de categoría pero cambia el idioma de la página. Los términos tienen su propio mapeo de traducción. Si el mapeo no está, los plugins recurren al fallback.

Solución: traducir términos, asignar términos traducidos correctos a posts traducidos y asegurar que los menús se construyan por idioma.

5) La configuración de menús y widgets no es específica por idioma

Los menús son básicamente configuración. En un sitio multilingüe, la configuración también necesita acotarse. WPML puede sincronizar menús; Polylang puede asignar menús por idioma. Pero si tienes un menú con enlaces hardcodeados, has construido una máquina mezcladora de contenido.

Solución: separar menús por idioma o usar las herramientas de sincronización soportadas por el plugin, y evitar enlaces absolutos hardcodeados al idioma por defecto.

6) La configuración de “idioma por directorio” del idioma por defecto causa confusión canónica

Una configuración clásica de WPML es “directorio para el idioma por defecto: off”, es decir, el idioma por defecto vive en / mientras los demás están en /fr/, /de/, etc. Esto funciona, pero aumenta las probabilidades de que cachés y redirecciones canónicas aplasten rutas y detecten mal el idioma.

Solución: si puedes permitírtelo, pon el idioma por defecto también en un directorio. Es operativamente aburrido, que es lo mejor que puedes decir sobre diseño de URLs.

7) Contenido mezclado dentro de un mismo post (flujo editorial)

A veces el plugin está bien y el problema es editorial: traductores copian/pegan bloques, constructors reutilizan plantillas globales, o alguien editó la versión equivocada porque el selector de idioma de la barra de administración mintió.

Solución: asegura el flujo de trabajo: roles, capacidades y una canalización de traducción explícita (incluso si es “siempre duplicar y luego traducir, nunca editar el original”).

Errores comunes: síntoma → causa raíz → solución

1) Síntoma: La página de inicio a veces cambia de idioma sin cambiar la URL

Causa raíz: caché de página completa que no varía por cookie o cabecera de idioma.

Solución: usa negociación por URL (idioma en URL), o configura la caché para variar por cookie de idioma; purga la caché después de cambiar la negociación.

2) Síntoma: Los menús muestran elementos en el idioma equivocado, pero el contenido de la página es correcto

Causa raíz: menú asignado globalmente, no por idioma; o HTML de menú cacheado sin clave por idioma.

Solución: asignar menús por idioma (feature de WPML/Polylang), y eliminar cualquier caché de fragmento de menú o añadir el idioma a su clave.

3) Síntoma: Un archivo de categoría muestra posts de múltiples idiomas

Causa raíz: consulta personalizada usa suppress_filters; mapeo de traducción de taxonomía faltante; o la opción “mostrar todos los idiomas” está activada para la taxonomía.

Solución: quitar suppress_filters; traducir y enlazar términos; asegurar que la taxonomía esté marcada como “traducible” y filtrada por el idioma actual.

4) Síntoma: El selector de idioma apunta a la página de inicio para algunas páginas

Causa raíz: falta la relación de traducción para ese tipo de contenido (post/term/product), o la traducción existe pero no está enlazada en el grupo de traducción.

Solución: reconstruir enlaces de traducción (duplicar y conectar correctamente), ejecutar las herramientas de “troubleshooting” del plugin y verificar el mapeo en BD para los items afectados.

5) Síntoma: El carrito/checkout de WooCommerce muestra cadenas en idiomas mezclados

Causa raíz: cadenas provienen de dominios de tema/plugin no registradas para traducción, o fragmentos cacheados (mini-cart) almacenados sin clave de idioma.

Solución: registrar cadenas correctamente, limpiar cachés y asegurar que los fragmentos de carrito varíen por cookie/URL de idioma.

6) Síntoma: El idioma de edición en admin es “equivocado”, llevando a editar la traducción incorrecta

Causa raíz: confusión entre idioma del perfil de usuario, idioma del sitio y ajustes de idioma del admin de WPML; editores usan edición rápida sin comprobar el idioma.

Solución: estandarizar comportamiento del admin, formar a editores para comprobar indicadores de idioma y restringir quién puede editar originales.

7) Síntoma: Las etiquetas hreflang son incorrectas o faltan intermitentemente

Causa raíz: head cacheado compartido entre idiomas; o redirección canonical que quita el directorio de idioma; o ajustes de permalinks inconsistentes.

Solución: arreglar keying de caché para fragmentos del head; validar comportamiento canónico; asegurar formato URL consistente para todos los idiomas.

8) Síntoma: Tras una migración, las traducciones existen pero están “desenlazadas”

Causa raíz: la migración/export parcial no preservó tablas del plugin o el import reescribió IDs de post rompiendo referencias de grupos de traducción; o la sincronización staging→prod omitió tablas de traducción.

Solución: repetir la migración incluyendo tablas WPML/Polylang; evitar exportaciones parciales; verificar que el recuento de filas en tablas de traducción coincida con la fuente antes del corte.

Chiste #2: “Copiamos la base de datos y vemos qué pasa” es el equivalente multilingüe de “la reinicio y vemos” — a veces funciona, pero no es una estrategia.

Tres mini-historias corporativas desde el terreno

Mini-historia 1: El incidente causado por una suposición equivocada

Tenían un setup ordenado: WPML, tres idiomas, un plugin de caché y un CDN. Marketing se quejaba de que visitantes franceses veían titulares en inglés “a veces”. Ingeniería hizo lo clásico: culpar a WPML y abrir un ticket con el proveedor.

La suposición equivocada fue simple: “el idioma lo determina la URL, así que cachear es seguro”. En realidad, el idioma por defecto se servía en /, y el sitio usaba una cookie para recordar el idioma seleccionado por algunos usuarios. El CDN ignoraba cookies por diseño (¡rendimiento!) y cachéó / como un único objeto.

Eso significaba que el idioma que primero golpeó / tras una purga “ganaba” la caché. Los usuarios franceses que seleccionaban francés desencadenaban HTML en francés en /, el CDN lo almacenaba y ahora los usuarios ingleses recibían francés hasta la siguiente purga. El incidente fue intermitente, irritante y completamente determinista.

La solución no fue una opción de WPML. Cambiaron la negociación de idioma a basada en directorios para todos los idiomas incluyendo el por defecto (así que inglés pasó a /en/), actualizaron redirecciones, purgaron cachés y el problema se detuvo. También añadieron una comprobación sintética que fetch-eaba cada homepage por idioma y verificaba que el <html lang> coincidiera. Aburrido, efectivo.

Mini-historia 2: La optimización que salió mal

Un equipo de producto quería páginas de categoría más rápidas. Alguien añadió una “mejora de rendimiento”: cachear el widget “Top Products” renderizado en un transient por 30 minutos porque era una consulta pesada. Gran idea en un sitio de un solo idioma.

En ese sitio, los productos se traducían por idioma, y precios/disponibilidad variaban por mercado. El HTML cacheado del widget se guardó bajo una clave como top_products_widget sin idioma. Así que la primera petición que calentó esa caché (a menudo inglés, por personal interno) determinó lo que todos verían los siguientes 30 minutos.

Se puso peor: el widget incluía enlaces a productos en el idioma que calentó la caché. Así que páginas de categoría en francés mostraban nombres de producto en inglés, URLs en inglés y a veces formato de moneda en inglés. No catastrófico, pero erosionó la confianza y aumentó contactos a soporte. El equipo intentó “vaciar caché al publicar”, lo que solo lo hizo más inestable.

La solución final fue: eliminar el cacheo de HTML y en su lugar cachear los IDs subyacentes por idioma (y por categoría), luego renderizar títulos/URLs correctos por idioma en tiempo de petición. Consumió algo más de CPU pero restauró la corrección. En sistemas en producción, la corrección es rendimiento. Los usuarios no compran productos que no entienden.

Mini-historia 3: La práctica aburrida pero correcta que salvó el día

Otra compañía ejecutaba Polylang con WooCommerce y Redis. Ya habían sufrido antes, así que hicieron algo dolorosamente poco sexy: cada release tenía una lista de comprobación de humo multilingüe. Al principio no automatizada; solo disciplina.

La checklist incluía: abrir la homepage, una página de producto, un archivo de categoría, el carrito y checkout en cada idioma; verificar idioma del menú; verificar formato de moneda y locale; verificar enlaces del selector de idioma; verificar presencia de hreflang. También tenían una regla: si añades caché, debes documentar la clave de caché y qué la hace variar.

Un día un desarrollador actualizó un tema que introdujo una nueva opción de “caché del header global”. Mejoró el Time To First Byte y dejó los dashboards bonitos. Las pruebas de humo detectaron que el idioma del header estaba mal en idiomas no por defecto justo después del despliegue.

Hicieron rollback en minutos, luego redeploy con el caché del header deshabilitado hasta parchearlo para que variara por idioma. Sin drama, sin impacto relevante para clientes, sin ticket de “bug misterioso”. La práctica aburrida hizo exactamente lo que promete: salvó el día.

Listas de verificación / plan paso a paso

Plan paso a paso para arreglar salida con idiomas mezclados (orden sensato)

  1. Elige una URL reproducible que muestre idioma mezclado. Escríbela. No persigas diez bugs a la vez.
  2. Evita caché de página completa y compara la salida. Si evitar la caché lo arregla, estás en terreno de caché.
  3. Vacía la caché de objetos y vuelve a probar. Si eso lo arregla temporalmente, tienes caché de objetos o de fragmentos sin scoping por idioma.
  4. Confirma el método de negociación de idioma (directorio/subdominio vs cookie vs parámetro). Alinealo con tu estrategia de caché.
  5. Audita consultas personalizadas procurando suppress_filters, SQL directo y IDs hardcodeados.
  6. Valida el enlace de traducciones para el contenido afectado: posts y términos.
  7. Valida menús por idioma y asegura que el tema renderice la ubicación de menú correcta según idioma.
  8. Revisa dominios de traducción de cadenas para tema y plugins; asegura que las cadenas correctas existan en cada idioma.
  9. Purge las cachés en el orden correcto: CDN/proxy inverso → plugin de caché de página → caché de objetos → caché del navegador.
  10. Añade monitorización: un script simple que fetch-ea páginas clave por idioma y afirma marcadores de idioma.

Checklist operacional: antes de cambiar ajustes

  • Backup de la base de datos (incluyendo tablas WPML/Polylang) y de wp-content.
  • Anotar el tipo de negociación de idioma y estrategia de URL actual.
  • Listar todas las capas de caché: navegador, CDN, proxy inverso, plugin de caché de página, caché de objetos.
  • Confirmar que el entorno de staging puede reproducir el problema.

Checklist operacional: después de aplicar una solución

  • Probar el flujo de usuario deslogueado (la mayoría de cachés aplican aquí).
  • Probar el flujo de editor logueado (cookies distintas, comportamiento de caché distinto).
  • Verificar enlaces del selector de idioma para al menos 5 páginas aleatorias, no solo la homepage.
  • Comprobar archivos de taxonomía y resultados de búsqueda (las consultas personalizadas se esconden allí).
  • Verificar que hreflang y tags canonical coincidan con las URLs reales.

Preguntas frecuentes

1) ¿Por qué el sitio mezcla idiomas solo “a veces”?

Porque las cachés no “a veces” varían por idioma; varían por lo que incluya la clave de caché. Si el idioma no está en la clave, el idioma que calentó la caché más recientemente gana hasta la expiración.

2) ¿Es siempre mejor el idioma en la URL que el idioma por cookie?

Operativamente, sí. Las URLs hacen que el caché, la depuración y el SEO sean más deterministas. La detección por cookie puede funcionar, pero cada capa de caché debe variar por esa cookie, y muchas no lo harán a menos que las forces.

3) Mi contenido principal está correcto, pero los widgets están en el idioma equivocado. ¿Por qué?

Los widgets suelen ejecutar consultas separadas y pueden usar fragmentos cacheados. Si esas consultas deshabilitan filtros o usan transients sin clave por idioma, la salida del widget puede desviarse del idioma de la página.

4) ¿Puede la caché de objetos Redis causar mezcla de idiomas?

Sí, indirectamente. Redis no es el problema; almacena fielmente las claves que le pida tu código. Si tu tema/plugin cachea “últimos posts” sin incluir idioma en la clave, Redis servirá a escala el idioma equivocado.

5) ¿Por qué el selector de idioma envía algunas páginas a la página de inicio?

Normalmente es por falta de enlace de traducción (la traducción existe pero no está conectada) o la traducción no existe. El selector no puede inventar un destino, así que recurre a una página segura.

6) Tras migrar, las traducciones aparecen duplicadas o desenlazadas. ¿Qué pasó?

Los exports parciales de BD suelen omitir tablas del plugin o reescriben IDs de post de forma que rompen referencias de grupos de traducción. Las migraciones deben incluir las tablas del plugin y preservar IDs o reconciliarlos correctamente.

7) ¿Cómo sé si es traducción de taxonomía versus traducción de post?

Si el contenido principal es correcto pero las categorías/etiquetas, migas y menús están mal, sospecha de la traducción de taxonomía. Si el cuerpo del post está mal o los destinos del switcher son incorrectos, sospecha del enlace de traducción de posts o del enrutamiento.

8) ¿Debería “arreglarlo” desactivando cachés permanentemente?

No. Eso no es una solución; es programar un incidente de rendimiento. Haz que las cachés sean conscientes del idioma y purga correctamente. Mantén la velocidad y la corrección.

9) Usamos un page builder. ¿Cambia algo?

Añade más caché y más lugares donde almacenar plantillas globales. Muchos problemas de mezcla aparecen por widgets/plantillas globales reutilizadas entre idiomas sin variantes conscientes del idioma.

10) ¿Cuál es el cambio duradero más rápido si estoy atascado?

Migrar a idioma-en-URL para todos los idiomas (incluido el por defecto), luego purgar todas las cachés y volver a guardar permalinks. Reduce los grados de libertad que causan mezcla.

Una cita de fiabilidad que vale la pena conservar

“La esperanza no es una estrategia.” — atribuido a la cultura de operaciones (idea parafraseada)

Conclusión: siguientes pasos que funcionan

Si solo haces dos cosas, haz estas: haz el idioma explícito en la URL, y haz que cada capa de caché varíe por idioma. La mayoría de incidentes de “idioma mezclado” desaparecen cuando el enrutamiento y la caché dejan de improvisar.

Luego haz el trabajo menos emocionante: auditar suppress_filters, eliminar transients agnósticos al idioma, traducir y enlazar taxonomías, y añadir un monitor ligero que compruebe páginas clave por idioma tras cada despliegue. WordPress multilingüe puede ser estable. Simplemente no se vuelve estable por accidente.

← Anterior
Guerras de copias de seguridad MySQL vs MariaDB en un VPS: mysqldump frente a copias físicas
Siguiente →
Los parches me robaron los FPS: mito, realidad y la aburrida verdad

Deja un comentario