__(): ¿Qué pasa si tengo que pasar una variable?
En la documentación de la función de traducción __( $text, $domain )
, se indica que debes poner la cadena directamente en lugar de $text, que no puedes hacer algo ingenioso como __( $my_text, 'text-domain' );
.
Sin embargo, estoy escribiendo un método que recibe una cadena y necesita pasarla a __(
... )
de alguna manera. ¿Hay alguna forma, por ejemplo, a través de printf
o sprintf
que pueda resolver esto?
Algo como...
function print_description( $text ) {
// Imprime un párrafo con el texto traducido
echo '<p>' . __( printf( '%s', $text ), 'text-domain' ) . '</p>';
}

Para traducir una cadena en WordPress, como en la mayoría de CMS que usan PHP, hay que envolver la cadena en una función de traducción GetText (como __(), _e(), _n(), _x()... etc).
La cadena también debe incluirse en un archivo PO (archivos Portable Object de GetText, el estándar de la industria para sitios web multilingües en PHP - https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html). Este archivo contiene pares de cadenas, una es la cadena en el idioma original y la otra es su traducción en el idioma de destino.
Para construir estos archivos PO para cada idioma (por ejemplo, my-text-domain-es_ES.po para español, my-text-domain-fr_FR.po para francés... etc.) en los que necesitamos traducir el plugin o tema, el traductor usará un archivo POT (plantilla PO) que contiene todas las cadenas a traducir en el idioma original y añadirá una cadena traducida para cada una. Los archivos PO se compilan a archivos MO binarios, que pueden procesarse mucho más rápido.
En tiempo de ejecución, las cadenas a traducir se obtienen a través de los archivos MO y se reemplazan por su traducción.
Los archivos POT generalmente se generan usando herramientas especiales que analizan los archivos de código fuente y extraen las cadenas traducibles.
Si escribimos código como este:
$translated_text = __( $text, 'my_text_domain');
Cuando el código es analizado por las herramientas especiales, el valor de la cadena a traducir ($text) aún no está definido y por lo tanto no está presente en el código. Como resultado, las herramientas de búsqueda automática no incluirán esta cadena en el archivo POT y por lo tanto no se incluirá en los archivos PO ni MO.
En tiempo de ejecución, cuando el valor de la cadena $text presumiblemente ya estará definido, no habrá correspondencia para este valor en los archivos de traducción y la traducción no será posible.
Sin embargo, si el conjunto de valores posibles para esa variable es limitado y los conocemos, tenemos dos opciones para superar este problema:
Opción 1: Editar manualmente el archivo POT para añadir las entradas con los valores posibles de $text. Esta opción es simple y fácil de implementar. Cualquier editor de código y conocimiento mínimo del formato POT será suficiente. Pero tiene un inconveniente. Cada vez que usemos las herramientas de búsqueda automática para actualizar las traducciones después de modificar nuestro código, las modificaciones que hayamos hecho se perderán y tendremos que incluirlas manualmente de nuevo.
Opción 2: Incluir en nuestro código todos los valores posibles de $text envueltos por funciones de traducción. Veamos un ejemplo. Supongamos que $text puede tomar los valores: apple, orange, banana, peach y pear. Necesitaríamos escribir el siguiente código:
// esta variable se usa solo para incluir como parámetros en
// funciones de traducción todos los valores posibles de $text
$locale = __('apple', 'my_text_domain') .
__('orange', 'my_text_domain') .
__('banana', 'my_text_domain') .
__('peach', 'my_text_domain') .
__('pear', 'my_text_domain');
$translated_text = __( $text, 'my_text_domain');
Esta opción también es fácil de implementar y tiene la ventaja de que no se perderá cuando usemos las herramientas de búsqueda automática para actualizar las traducciones.
Si tenemos, en nuestro tema o plugin, varias variables que queremos que sean traducidas y tienen un conjunto limitado de valores posibles conocidos, podríamos incluirlas todas en un archivo separado que debe estar en la carpeta raíz o una subcarpeta (como 'includes' o 'assets') del tema o plugin como se muestra a continuación:
<?php
//valores de las variables a traducir
$locale_var1 = __('text-of-var1_val1', 'my_text_domain') .
__('text-of-var1_val2', 'my_text_domain') .
__('text-of-var1_val3', 'my_text_domain') .
.......
__('text-of-var1_valn', 'my_text_domain');
$locale_var2 = __('text-of-var2_val1', 'my_text_domain') .
__('text-of-var2_val2', 'my_text_domain') .
.......
__('text-of-var2_valn', 'my_text_domain');
.............
$locale_varn = __('text-of-varn_val1', 'my_text_domain') .
__('text-of-varn_val2', 'my_text_domain') .
.......
__('text-of-varn_valn', 'my_text_domain');
Este es un enfoque limpio y mantenible que mantiene la definición de estas cadenas en un archivo separado que puede modificarse sin afectar otros archivos de código.

Gracias por eso @Juan Guerrero. No estoy seguro de por qué esta respuesta no recibió algunos pulgares arriba - creo que tu idea de un archivo de recursos para conjuntos predecibles de cadenas es muy buena - como dices, es un enfoque "limpio y mantenible". Lo estaré usando.

No
Las herramientas que ayudan a generar una traducción no pueden analizar tu código y decidir cuáles son las cadenas que necesitan traducción cuando las cadenas pasadas a las rutinas de traducción son totalmente dinámicas.
En tu ejemplo, la forma correcta de codificar esta función es:
function print_description( $text ) {
echo '<p>' . $text . '</p>';
}
y llamarla así:
print_description(__('descripción específica','text_domain'));

No, porque no se puede traducir texto cuando no se sabe realmente qué es ese texto.
La traducción funciona, esencialmente, mediante un gran arreglo. Tomas tu código, encuentras todas las cadenas en él, luego construyes una gran lista de cadenas. Un traductor las traduce a otro idioma. Luego las diversas funciones de traducción hacen una búsqueda en el gran arreglo y devuelven la cadena traducida.
Si usas una variable para contener la cadena de texto, entonces no hay forma de saber qué es de antemano y colocarla en la lista inicial de cadenas para traducir.
Las variables no pueden usarse en funciones de traducción, porque eso en realidad no tiene sentido. No se puede traducir una variable.

Por alguna razón, a mí sí me funciona pasar variables a _e():
$string = 'ciao';
_e($string, 'textdomain'); // usa tu propio textdomain..
// mostrará 'ciao' o 'hello', según el idioma actual
Obviamente, debes crear tu textdomain antes, idealmente en yourtheme/functions.php:
add_action( 'after_setup_theme', 'theme_setup' );
function theme_setup() {
load_theme_textdomain( 'yourtextdomain', TEMPLATEPATH . '/languages' );
// [...] otro código de inicialización
}
Si traduzco la palabra 'ciao' en el archivo .po, se traducirá correctamente en el frontend. Usa Poedit o similar para compilar el .mo. Tanto .po como .mo deben ubicarse en /wp-content/yourtheme/languages (de acuerdo al código anterior).
#. Test en_EN.po
msgid "ciao"
msgstr "hello"
No estoy seguro de la razón por la que funciona, pero lo hace, mostrará 'ciao' en italiano y 'hello' en inglés.

Esto solo funciona porque estás añadiendo la traducción manualmente. Si usas una herramienta que busca cadenas en tu código para traducir, fallará en encontrar la cadena correcta.

Sí, por supuesto, pero creo que ese no es el problema de la pregunta original

Como mencioné aquí http://www.westofwonder.com/2013/12/whats-with-all-the-double-underscores-everywhere/ (por favor disculpen el tema, he hecho mejoras pero aún no las he subido), una posible solución para esto es crear una función para el texto:
function yourtheme_esteeseltexto() {
return __( 'Este es el texto.', 'yourthemetextdomain' );
}
Feo en sí mismo, pero para una cadena de traducción larga o de uso frecuente, la compensación puede valer la pena por la legibilidad del código.
(¿Está permitido? ¿Enlazar a mi blog como respuesta directa a una pregunta? Si no es así, me disculpo y no lo volveré a hacer.)
