Contenu principal
wpi18n

Aide-mémoire sur l’internationalisation et WordPress

Je ne vous cacherais pas que le but de cet article est purement égoïste puisqu’il va me servir de pense-bête. Il s’agit de regrouper quelques informations concernant l’utilisation de _n() et _x() principalement, au moment de passer à la rédaction du fichier .po. Je veux dire que le codex de WordPress explique bien comment se servir de ces fonctions mais concernant le fichier .po, là il y a un manque hélas (c’est pas un reproche hein, juste un constat).
La première fois que j’ai voulu les utiliser, je pense qu’il m’a fallu 2 heures de recherche sur le net pour trouver les bonnes réponses, sur un blog perdu aux confins de l’Univers des pages de résultats de Google. La deuxième fois, une bonne demie-heure pour retrouver le fichier de ma « première fois ».

__() et _e()

Tout d’abord, quelques rapides rappels sur deux fonctions que vous avez certainement déjà employées.

1234

// Stocker dans une variable
$texte = __('Challenge accepted!', 'stinson');
// Ou imprimer directement
_e('Legendary', 'stinson');

Le premier paramètre est le texte à traduire, le second est le domaine. Le domaine est une chaine de caractères spécifique à un thème ou un plugin. Lorsque le domaine n’est pas précisé, il est alors égal à « default », ce qui est le domaine par défaut de WordPress. « default » est à utiliser au maximum lorsque vous voulez traduire des chaines comme « Post » par exemple, qui existent déjà dans WordPress.

Au niveau du fichier .po :

12

msgid "Challenge accepted!"
msgstr "Défi accepté !"

Dans poedit on utilise toujours des double quotes (« ) pour délimiter nos chaines de caractères, sinon on a droit à de belles erreurs.

Jusque là c’est très simple :)

Charger les fichiers d’internationalisation

Thème :

1

load_theme_textdomain('stinson', TEMPLATEPATH . '/languages/');

Plugin :

1

load_plugin_textdomain( 'stinson', false, basename( dirname( __FILE__ ) ) ) . '/languages/' );

Pour load_plugin_textdomain() le deuxième paramètre doit être à false ou à null car il a été déclaré « deprecated » depuis WordPress 2.7.

Nommage des fichiers

Thème : fr_FR.po
Plugin : stinson-fr_FR.po

_x() et _ex()

D’abord, évacuons le côté le plus simple de cette partie. La différence entre _x() et _ex() est la même qu’entre __() et _e() : _ex(); est équivalent à echo _x();.

Passons aux choses sérieuses.

Utilisation :

1

$texte = _x("Texte à traduire", 'contexte', 'domaine');

_x() permet de préciser un contexte pour notre internationalisation. A quoi cela sert-il ?
Selon la situation on peut avoir besoin de traduire un mot de deux façon différentes. Un exemple simple, « Comment » en anglais, peut se traduire en français par « Commentaire » ou « Commenter ». Dans ce cas là, le contexte nous est utile. Pour simplifier on pourrait dire que le contexte correspond à un emplacement dans un thème/plugin (nom de colonne, pied de page, etc), ou un « état » (nom, verbe, pluriel, féminin, etc).

Par exemple, récemment j’ai voulu créer un custom post type « Actualités ». Bien sûr, comme je suis totalement bilingue français/bullshit, je l’ai traduit par « News » très facilement. Jusque là, je ne me mouille pas trop. Étape suivante, au singulier ça donne quoi ? Ben « News » bien sûr (voix intérieure qui crie : « Oh, crap! »).
Résultat :

12

$singular = _x("News", 'post type singular name', 'stinson');
$plural   = _x("News", 'post type general name', 'stinson');

Ouais, je sais, c’est balo.

Le fichier .po : (oui, il faut pas oublier que c’était l’objectif de l’article au début)

123456789

#: Singulier
msgctxt "post type singular name"
msgid "News"
msgstr "Actualité"

#: Pluriel
msgctxt "post type general name"
msgid "News"
msgstr "Actualités"

Nous avions msgid (comprendre msg id : identifiant, chaine à traduire) et msgstr (comprendre msg str : string, chaine traduite). Maintenant nous avons aussi msgctxt pour préciser le contexte (comprendre msg ctxt : contexte).

_n()

Celle-ci permet de retourner la forme au singulier ou au pluriel, selon une quantité.
A noter que la forme _en() n’existe pas.

12

$quantite = 4;
$texte = _n( "Texte au singulier", "Texte au pluriel", $quantite, 'domaine' );

Le principe est simple : selon la valeur de $quantite, la forme au singulier ou au pluriel sera retournée.
Comme me le rappelle BoiteAWeb dans les commentaires, _n() renvoie un résultat différent pour 0 selon la langue. On dit « 0 comments » et « 0 commentaire », et bien je ne sais pas où s’opère la magie mais c’est bien le singulier qui est renvoyé pour le français.

Le fichier .po :

1234

msgid "Singular"
msgid_plural "Plural"
msgstr[0] "Singulier"
msgstr[1] "Pluriel"

Les nouveaux « mots clés » à retenir sont donc msgid_plural pour le pluriel du texte d’origine, msgstr[0] pour le texte traduit au singulier et msgstr[1] pour le texte traduit au pluriel.
Pour info, certaines langues ont plusieurs formes plurielles, comme le russe apparemment, je suppose donc qu’on pourrait trouver msgstr[2], etc.

Allez, on prend une bonne inspiration pour la partie suivante car on mélange tout.

_nx()

Vous l’aurez compris, il s’agit l’un mélange entre _n() et _x(), on va donc retrouver la même mécanique, et les mêmes mots clés dans le fichier .po (il n’y a pas de _enx() non plus).

12

$quantite = 4;
$texte = _nx( "Texte au singulier", "Texte au pluriel", $quantite, 'contexte', 'domaine' );

Le fichier .po :

12345

msgctxt "context"
msgid "Singular"
msgid_plural "Plural"
msgstr[0] "Singulier"
msgstr[1] "Pluriel"

Bon, au fait, ça donne quoi avec mon CPT « News » ?

12

$quantite = 4;
$texte = _n( "News", "News", $quantite, 'stinson' );

Voix interne : Oh crap! J’ai besoin du contexte ! Bon… Deuxième essai.

12

$quantite = 4;
$texte = _nx( "News", "News", $quantite, 'post type general name', 'stinson' );

Voix interne : Oh crap! Les contextes sont différents selon la forme singulière ou plurielle. Bon… va falloir bidouiller dans le .po >_<
Avant :

123456789

#: Singulier
msgctxt "post type singular name"
msgid "News"
msgstr "Actualité"

#: Pluriel
msgctxt "post type general name"
msgid "News"
msgstr "Actualités"

Après :

010203040506070809101112131415

#: Singulier et pluriel selon quantité
msgid "News"
msgid_plural "News"
msgstr[0] "Actualité"
msgstr[1] "Actualités"

#: Singulier avec contexte
msgctxt "post type singular name"
msgid "News"
msgstr "Actualité"

#: Pluriel avec contexte
msgctxt "post type general name"
msgid "News"
msgstr "Actualités"

Ouais, c’est un peu tordu parce qu’il faut déclarer deux fois les choses mais ça marche (si vous avez une meilleure solution je suis preneur).
Finalement je n’ai même pas utilisé _nx() dans cet exemple :/

Bonus : internationaliser une date

Pour cela il y a date_i18n() :

1

echo date_i18n( $format, $timestampUnix, $gmt );

$format est la chaine de caractères qui indique dans quel format la date doit être affichée (jour de la semaine, mois, etc).
$timestampUnix est le timestamp (horodatage) généralement généré avec mktime(). Si laissé vide, on aura la date actuelle.
$gmt indique le fuseau horaire. Si laissé vide, on aura le fuseau réglé dans WordPress (!).
L’avantage de cette fonction est double, puisque d’une part elle va traduire automatiquement les jours et les mois, mais aussi elle nous renverra la date et l’heure du fuseau réglé dans WordPress, là où date() nous renverrais date et heure du serveur.
Utilisation :

010203040506070809101112131415

// Aujourd'hui
echo date_i18n('l j F Y');	// Mercredi 4 juillet 2012

// Demain
$demain = mktime(0,0,0,date("m"),date("d")+1,date("Y"));
echo date_i18n('l j F Y', $demain);	// Jeudi 5 juillet 2012

// Date d'un post
global $post;
if ( !empty( $post ) ) {
	echo date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) );
}

// Date et heure selon les formats réglés dans WordPress
echo date_i18n( sprintf('%1$s @ %2$s', get_option( 'date_format' ), get_option(' time_format' )) );

Je crois que j’ai rien oublié de ce que je ne voulais pas oublier. J’aurais quand même dû me faire un pense-bête pour ne rien oublier dans ce pense-bête au cas où…

[update] Depuis WP 3.4 on peut traduire le nom des templates de page dans le dropdown. Pour cela il faut ajouter 2 lignes dans l’entête du fichier style.css du thème : le domaine de la traduction et le chemin relatif vers le fichier .mo (et traduire le nom des templates dans le fichier .mo bien sûr). Ceci fonctionne aussi pour les plugins, afin de traduire leur description.

12

Text Domain: stinson
Domain Path: /languages

See ya!