Para la traducción e internacionalización de cadenas de texto WordPress utiliza la biblioteca y herramientas gettext. Todo texto que sea pasado a través de una de las funciones descritas en está listo para ser traducido.

Generalmente las traducciones son realizadas a través de archivos POT (Portable Object Template), lo que implica que si no nos gusta una traducción, o simplemente queremos mostrar un texto diferente, tenemos que editar estos archivos, con el riesgo de perder los cambios en cada actualización.

La solución: el filtro gettext. Cada traducción realizada a través de una función gettext se pasa por este filtro, lo que nos permite cambiarla por nuestro propio texto, incluso las traducciones del core. Por ejemplo, en lugar de que se traduzca «Post» como «Entrada» podemos hacer que se traduzca como «Noticia»:

add_filter( 'gettext', 'cyb_filter_gettext', 10, 3 );
function cyb_filter_gettext( $translated, $original, $domain ) {
    if ( $translated == "Entrada" ) {
        $translated = "Noticia";
    }
    return $translated;
}

El ejemplo anterior es muy básico. Este filtro nos permite hacer muchas más cosas. Por ejemplo, en lugar de hacer una traducción forzosa como antes, podemos hacer que una cadena se traduzca utilizando un text domain diferente:

add_filter( 'gettext', 'cyb_filter_gettext', 10, 3 );
function cyb_filter_gettext( $translated, $original, $domain ) {
    if ( $original == "Post" ) {
        $translated = __( 'Post', 'text-domain' );
    }
    return $translated;
 }

También podemos manejar la traducción de varias cadenas en un mismo filtro. Por ejemplo:

add_filter( 'gettext', 'cyb_filter_gettext', 10, 3 );
function cyb_filter_gettext( $translated, $original, $domain ) {
    // make the changes to the text
    switch( $original ) {

        case 'Author':
          $translated = __( 'Journalist', 'text-domain' );
        break;

        case 'Comment':
          $translated = __( 'Thought', 'text-domain' );
        break;
     }

     return $translated;

}

Sobreescribir traducciones con contexto

Si el string original cuya traducción queremos sobreescribir utiliza traducción en contexto, el filtro gettext no funcionará. Hay que utilizar el filtro gettext_with_context.

Las traducciones sin contexto utilizan las funciones __() y _e() y las traducciones con contexto utilizan las funciones _x() y _ex(), equivalentes a __() y _e() pero con un parámetro adicional que indica el contexto en el que se está utilizando la palabra a traducir, muy útil para dar instrucciones a los traductores cuándo el significado de una palabra o frase puede ser ambiguo según el contexto.

Por ejemplo:

// Sin contexto
__( 'Post', 'text-domain' );
// Con contexto, Post como noun (sustantivo)
_x( 'Post', 'noun', 'text-domain' );
// Con contexto, Post como verb (verbo)
_x( 'Post', 'verb', 'text-domain' );

Para sobreesribir las traducciones con contexto se utiliza el filtro gettext_with_context:

add_filter( 'gettext_with_context', 'cyb_filter_gettext_with_context', 10, 4 );
function cyb_filter_gettext_with_context( $translated, $original, $context, $domain ) {
    if ( $original == "Post" && $context == 'noun' ) ) {
        $translated = "Noticia";
    }
    return $translated;
}

Sobreescribir traducciones singular/plural

Las cadenas que contienen información en singular y en plural se traducen con las funciones gettext _n() y _nx(). Por ejemplo:

$count = get_comments_number();
$translated = sprintf( _n( '%s comment', '%s comments', $count, 'text-domain' ), number_format_i18n( $count ) );

De forma análoga a los filtros disponibles para __() y _x(), para sobreesribir traducciones con plural tenemos disponibles los filtros ngettext y ngettext_with_context.

Sobreescribir archivos .mo

Si se van a sobreescribir muchas cadenas puede que sea interesante cargar un archivo .mo personalizado utilizando el filtro load_textdomain_mofile.

Por ejemplo, podríamos crear una carpeta dentro del directorio de idiomas (predeterminado wp-content/laguages/) donde guardar una copia de los archivos .mo modificados (ejemplo no probado):

add_filter( 'load_textdomain_mofile', 'cyb_filter_load_textdomain_mofile', 10, 2 );
function cyb_filter_load_textdomain_mofile( $mofile, $domain ) {
    if ( $domain == 'some-textdomain' ) {
        $mofile = WP_LANG_DIR . '/overrides/' . basename( $mofile );
    }
    return $mofile;
}