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!
Commentaires
Commentaire de Julio Potier @ BoiteAWeb.
Merci pour ces infos toujours utiles et merci à toi d’avoir perdu 2h + 30mn à notre place.
A propos du pluriel, il me semble que la valeur « 0 » va renvoyer le pluriel non ? En anglais on dit « 0 comments » alors qu’en français on dit « 0 commentaire ». As tu testé ?
Aussi j’aimerai dire aux développeurs qui utilisent date_i18n ou tout autre formatage de date de ne pas afficher arbitrairement leur format de date mais d’utiliser celui du site !!
get_option( ‘date_format’ ); et get_option(‘ time_format’ ); sont là pour ça. N’oubliez pas qu’entre les langues, nous n’affichons pas de la même façon les dates, il serait dommage dans votre thème/plugin d’avoir forcé l’affichage style « français ».
Bisou
Commentaire de Greg.
Ouay j’ai testé, c’est ça que j’ai oublié dans l’article. Et bien j’ai été surpris, je croyais que c’était le pluriel qui aller sortir mais non, c’est bien le singulier qui sort avec 0.
Je vais ajouter les deux get_option dans les exemples.
Merci :)
Commentaire de Rahe.
Bien détaillé par contre tu peux utiliser la librairie utilisée sur le repository pour générer ton fichier po avec le _n et _x etc.. Il faut aller voir dans http://codex.wordpress.org/I18n_for_WordPress_Developers dans la section ‘Marking strings in themes and plugins’ ;)
Commentaire de Greg.
Dans ce chapitre on parle plutôt d’ajouter automatiquement le domaine aux appels gettext lorsqu’on ne l’a pas précisé (il génère aussi un .po ?).
En revanche c’est dans « Generating a POT file » où l’on parle de… générer automatiquement un fichier .pot, ce que fait déjà poedit, à part qu’il se foire pour _n et _x par exemple ^^. La manip ne semble pas bien aisée aussi puisqu’il faut passer par SVN :/
Merci :)
Commentaire de Rahe.
Oui mais leur classe fonctionne pour faire le POT et donc tu peux gérer les _n et _x d’où le :
If your plugin is not in the repository, you can checkout the wordpress-i18n tools directory from SVN (see Using Subversion to learn about SVN) and then run the makepot.php script like this:
Mais je sais qu’il y a un service qui le fait, en fait tu donnes le zip de ton thème/plugin et hop il te donne le pot ;), malheureusement je ne me souviens plus du nom :/
Commentaire de Greg.
icanlocalize ?
Commentaire de Rahe.
Oui je crois bien ^^
Commentaire de Julio Potier @ BoiteAWeb.
First !
https://twitter.com/BoiteAWeb/statuses/220435987023671296 10h36 !
Commentaire de Greg.
Petite mise à jour concernant la traduction du nom des templates de page.
Commentaire de FxB.
Super,
tu peux rajouter les commentaires pour les traducteurs avec
12
Pour le makepot de WordPress rien de plus simple qu’une ligne de commande
Commentaire de FxB.
Et pis j’avais oublié tu peux faire le fou avec
Escaping output
esc_html__(), esc_html_e(), esc_html_x()
Escaping attributes
esc_attr__(), esc_attr_e(), and esc_attr_x()
histoire d’être safe avec l’affichage des pages