Contents
Introducción
Este artículo explica con todo lujo de detalles cómo internacionalizar cadenas en WordPress usando las funciones principales de traducción en PHP: __() y _e(). Veremos qué hacen, cuándo usar cada una, cómo declarar el dominio de texto, cómo cargar las traducciones en temas y plugins, ejemplos prácticos, buenas prácticas (seguridad/escapado y placeholders), y cómo generar los ficheros .pot/.po/.mo para que un traductor pueda trabajar.
Concepto básico
WordPress utiliza el sistema gettext para gestionar traducciones. Las funciones __() y _e() marcan cadenas para traducción y buscan la versión traducida en los ficheros de idioma cargados para el «text domain» correspondiente.
Diferencia rápida
Función | Qué hace |
---|---|
__() | Devuelve la cadena traducida. Útil cuando necesitas manipularla, concatenarla o usarla en printf. |
_e() | Imprime (echo) la cadena traducida directamente. Útil en templates donde sólo quieres mostrar el texto. |
Firmas y parámetros
- __(text, domain) — recibe la cadena original en inglés (o idioma fuente) y el text domain devuelve la traducción.
- _e(text, domain) — mismo comportamiento semántico pero imprime la cadena traducida.
Ejemplo de firma real: __(Hello World, mi-dominio)
Ejemplos básicos
En un template (tema)
lt?php _e(Leer más, mi-tema) ?gt
Usando __() para manipular o pasar a printf
lt?php title = __(Título del artículo, mi-tema) echo lth2gt . esc_html( title ) . lt/h2gt // Con placeholders printf( __(Publicado por %s el %s, mi-tema), esc_html( author ), esc_html( date ) ) ?gt
Cargar el text domain
Para que WordPress encuentre las traducciones debes cargar el dominio desde el tema o plugin. La forma recomendada varía si es tema o plugin.
En un tema (ejemplo típico)
function mi_tema_setup() { load_theme_textdomain( mi-tema, get_template_directory() . /languages ) // otras inicializaciones... } add_action( after_setup_theme, mi_tema_setup )
En un plugin (ejemplo típico)
function mi_plugin_load_textdomain() { load_plugin_textdomain( mi-plugin, false, dirname( plugin_basename( __FILE__ ) ) . /languages ) } add_action( plugins_loaded, mi_plugin_load_textdomain )
Cabecera de plugin y text domain
En el header de tu plugin, añade la cabecera Text Domain y (en plugins distribuidos) la cabecera Domain Path si usas un subdirectorio. Ejemplo:
/ Plugin Name: Mi Plugin Plugin URI: https://example.com Description: Un plugin de ejemplo. Version: 1.0 Author: Autor Text Domain: mi-plugin Domain Path: /languages /
Generar ficheros de traducción (.pot, .po, .mo)
- Marca todas las cadenas con __(), _e(), _x(), _n(), etc. (marcación consistentemente).
- Usa herramientas para escanear las cadenas y generar un .pot:
- WP-CLI: wp i18n make-pot . languages/mi-tema.pot
- Poedit: Escanear el proyecto y crear .po/.mo
- Plugins como Loco Translate para generar y gestionar traducciones en el propio WP.
- Un traductor genera el .po para cada idioma (ej. es_ES.po) y compila un .mo (binario) que WordPress carga.
- Coloca los ficheros en el directorio correcto: para temas suele ser /wp-content/themes/mi-tema/languages, para plugins /wp-content/plugins/mi-plugin/languages o en global /wp-content/languages/plugins/.
Buenas prácticas y matices importantes
- No concatenes cadenas traducibles. Evita hacer algo así: __(Hello, domain) . . __(World, domain) porque el orden y la gramática varían entre idiomas. Usa placeholders y sprintf/printf:
printf( __(Hola %s, tienes %d mensajes, mi-dominio), esc_html( nombre ), intval( count ) )
- Escapado: Siempre escapa salidas para evitar XSS. Si vas a mostrar HTML seguro, usa funciones como esc_html__(), esc_attr__(), esc_html_e(), esc_attr_e() o aplica esc_html() a la cadena devuelta por __(). Ejemplo:
// imprimir con escape para HTML echo esc_html( __(Mi texto traducible, mi-tema) ) // imprimir atributo echo esc_attr( __(Valor del atributo, mi-tema) )
- Contexto: Si la misma palabra tiene distintos significados, usa _x() o _ex() para dar contexto al traductor.
- Plural: Para plurales usa _n() (no confundir con __/_e). Ejemplo:
echo sprintf( _n(%s comentario, %s comentarios, n, mi-dominio), number_format_i18n( n ) )
- Text domain coherente: Usa siempre el mismo text domain dentro de tu plugin/tema y refléjalo en la cabecera y en la carga del dominio.
- Evita traducir cadenas dinámicas sin marcar: Si partes de una cadena vienen de base de datos o usuario y forman parte de una frase, mejor crear la cadena completa con placeholders y marcarla para traducción.
Ejemplos reales: tema y plugin
Ejemplo completo en un template de tema
lt!-- header.php --gt lt?php // Cargar fuera del template ejemplo dentro functions.php // load_theme_textdomain(mi-tema, get_template_directory() . /languages) ?gt ltnavgt ltulgt ltligtlta href=lt?php echo esc_url( home_url(/) ) ?gtgtlt?php _e(Inicio, mi-tema) ?gtlt/agtlt/ligt ltligtlta href=lt?php echo esc_url( get_permalink( get_option( page_for_posts ) ) ) ?gtgtlt?php _e(Blog, mi-tema) ?gtlt/agtlt/ligt lt/ulgt lt/navgt
Ejemplo completo en un plugin
/ Plugin Name: Saludo Text Domain: saludo-plugin Domain Path: /languages / function saludo_plugin_load_textdomain() { load_plugin_textdomain( saludo-plugin, false, dirname( plugin_basename( __FILE__ ) ) . /languages ) } add_action( plugins_loaded, saludo_plugin_load_textdomain ) function saludo_shortcode( atts ) { atts = shortcode_atts( array( nombre =gt invitado, ), atts, saludo ) // Uso de __() con escape format = __(Hola %s, bienvenido a nuestro sitio., saludo-plugin) return sprintf( esc_html( format ), esc_html( atts[nombre] ) ) } add_shortcode( saludo, saludo_shortcode )
Errores comunes y cómo evitarlos
- No cargar el text domain o cargarlo en un hook tardío: asegúrate de llamar a load_theme_textdomain en after_setup_theme y load_plugin_textdomain en plugins_loaded.
- Usar un text domain distinto al declarado en la cabecera del plugin: esto impide que las traducciones se asocien correctamente.
- Olvidar el escapado: siempre sanear el contenido antes de imprimirlo.
- Concatenar cadenas traducibles en lugar de usar placeholders.
Resumen práctico
- __() devuelve la cadena traducida usar cuando necesitas manipular el texto.
- _e() imprime la cadena traducida uso directo en templates.
- Cargar text domain con load_theme_textdomain o load_plugin_textdomain.
- Generar .pot/.po/.mo y colocar los archivos en /languages adecuados.
- Seguir buenas prácticas: placeholders, escape, no concatenar cadenas traducibles, y usar contexto/plurales cuando corresponda.
Recursos útiles
- Documentación de internacionalización en Themes (WordPress.org)
- Documentación de internacionalización en Plugins (WordPress.org)
- load_plugin_textdomain() — referencia
- load_theme_textdomain() — referencia
|
Acepto donaciones de BAT's mediante el navegador Brave 🙂 |