Hay una categoría de fallos de WordPress especialmente cara de diagnosticar: los que no producen ningún error. El código es válido, no hay excepciones, el registro está limpio, y sin embargo el dato no aparece por ninguna parte. Estos tres son de los más frecuentes, y comparten la misma forma: una función del núcleo descarta lo que se le da porque no cumple una condición que no está a la vista.

1. El icono que se esfuma: esc_url() y las direcciones data:
Situación típica: se añade un icono de marca al menú del panel. Un SVG pequeño, incrustado en la propia dirección como data:image/svg+xml;base64,…, para que no genere ni una petición extra al servidor. El CSS está bien, el elemento está en el HTML, y el icono no se ve.
La causa es que la dirección pasa por esc_url(), que es lo que uno hace por reflejo con cualquier URL. Pero esa función solo deja pasar los protocolos que WordPress considera admitidos, y data: no está entre ellos. El resultado no es un error: es una cadena vacía.
// El icono desaparece, sin ningún aviso:
'background:url(' . esc_url( $icono ) . ')'
// Si la cadena se genera en el propio código a partir de un SVG fijo,
// no hay nada que sanear:
'background:url("' . $icono . '")'
La regla general que deja este caso: escapar no es gratis. Cada función de escape tiene una lista de lo que considera aceptable, y lo que no encaja se descarta en silencio. Si un dato desaparece, el primer sitio donde mirar es qué función lo tocó por última vez.
2. La traducción que nunca carga
Otro caso frecuente: un plugin preparado para varios idiomas, con las cadenas envueltas, la plantilla generada y las traducciones compiladas a .mo, todo dentro de la carpeta languages/ del plugin. Se cambia el idioma del sitio y la interfaz sigue enteramente en el idioma original.
Los archivos están donde tienen que estar y con el nombre correcto. Lo que falta es una línea:
load_plugin_textdomain(
'mi-plugin', false, dirname( plugin_basename( __FILE__ ) ) . '/languages'
);
Sin ella, WordPress solo mira en su carpeta de idiomas (wp-content/languages/plugins/), nunca dentro del plugin. Y no lo dice: no hay aviso, no hay entrada en el registro, no hay nada. La interfaz se queda como estaba, como si las traducciones no existieran.
3. La directiva que se imprime a medias
Al revisar el SEO técnico de un sitio es habitual encontrar la etiqueta de robots así:
<meta name="robots" content="max-image-preview:large, max-snippet, index, follow">
La directiva max-snippet está, pero sin valor. Y sin valor no significa nada: los buscadores la ignoran y aplican su límite por defecto, con lo que el fragmento que muestran en los resultados se queda corto.
El código parece correcto:
$robots['max-snippet'] = -1; // «sin límite»
El detalle está en cómo WordPress recorre ese array. La función wp_robots() añade :valor solo si el valor es una cadena de texto. Con cualquier otra cosa que se evalúe como verdadera —un entero, un true— imprime la directiva sola. La corrección es de un carácter:
$robots['max-snippet'] = '-1'; // entre comillas
El patrón que comparten los tres
Los tres fallos son distintos, pero se diagnostican igual, y el error más caro es el mismo en los tres casos: dar por bueno el proceso en vez de mirar el resultado.
«El código se ejecuta sin excepciones» no significa que haga lo que se espera. «El archivo está en su carpeta» no significa que se cargue. «La función se llamó» no significa que su salida llegara a la página.
Estos tres son los amables, porque el sitio sigue en pie mientras se buscan. El mismo descuido en un gancho de activación no perdona igual: ahí el fallo deja el sitio entero devolviendo error 500, panel incluido.
Cómo comprobar cada uno
- ¿Un icono, una imagen o un enlace no aparece? Hay que buscar su dirección en el HTML servido. Si el atributo está vacío, alguna función de escape se la comió.
- ¿Una traducción no se aplica? No mirar el archivo: cargar el dominio a mano y pedir una frase concreta con
__(). Si vuelve el original, no está cargando. - ¿Una etiqueta del
<head>sale rara? Descargar la página concurly leerla. Lo que se ve en el navegador ya ha pasado por el DOM y puede no ser lo que envió el servidor.
En los tres casos el trabajo de diagnóstico es el mismo: comparar lo que se escribió con lo que de verdad salió. Cuando no coinciden y no hay error, la respuesta casi siempre es una función del núcleo aplicando una regla que no se conocía.
De los tres, el de la etiqueta de robots es el que más caro sale a largo plazo, porque nadie lo nota: el sitio se ve perfecto y el buscador está recibiendo instrucciones a medias durante meses. Comprobar la cabecera que sirve el servidor es de lo primero que se mira en una revisión de posicionamiento SEO.
Preguntas frecuentes
¿Por qué un icono SVG incrustado no aparece y no da ningún error?
Casi seguro está pasando por esc_url(). Esa función solo admite los protocolos de wp_allowed_protocols(), y data: no está en la lista, así que devuelve una cadena vacía sin avisar. Si la dirección se genera en el propio código a partir de un SVG fijo, no hay nada que sanear: se imprime directamente. Si viene de fuera, hay que validar su formato en lugar de escaparla.
¿Por qué un plugin no usa los archivos .mo que vienen dentro de él?
Porque falta load_plugin_textdomain(). WordPress solo busca las traducciones de un plugin en wp-content/languages/plugins/; los archivos que se entregan dentro del plugin se ignoran salvo que se le indique de forma explícita. No aparece ningún aviso: la interfaz simplemente se queda en el idioma original. Conviene engancharlo a init, no antes, porque desde WordPress 6.7 cargar un dominio demasiado pronto genera una advertencia.
¿Cómo se comprueba de verdad que una traducción carga?
No mirando si el archivo existe, sino pidiéndole una frase concreta. Se carga el dominio a mano con load_textdomain() apuntando al .mo, se llama a __() con una cadena que se sabe traducida, y se imprime el resultado. Si devuelve el original, no carga — da igual lo que pese el archivo o lo bien que se vea en Poedit.
¿Por qué la meta robots imprime «max-snippet» sin el valor?
Porque se le pasó un número entero. La función wp_robots() solo escribe directiva:valor cuando el valor es una cadena de texto; con cualquier otra cosa que se evalúe como verdadera imprime la directiva sola, que no limita nada. Hay que usar '-1' entre comillas, no -1.
¿Hay forma de encontrar estos fallos antes de que los vea el cliente?
La única que funciona es comprobar el resultado servido, no el código. Se pide la página ya renderizada y se busca dentro lo que debería estar: la etiqueta con su valor, el icono con su dirección, la frase en el idioma que toca. Una prueba que solo comprueba que el código se ejecutó sin excepciones pasará en verde con la pantalla rota.
