Como internacionalizar cadenas con __ y e en PHP en WordPress

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)

  1. Marca todas las cadenas con __(), _e(), _x(), _n(), etc. (marcación consistentemente).
  2. 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.
  3. Un traductor genera el .po para cada idioma (ej. es_ES.po) y compila un .mo (binario) que WordPress carga.
  4. 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



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *