Modérateur
Bonjour, bonsoir,

Je souhaiterais rendre des pages php traduisibles en utilisant gettext. Ça fonctionne sans problème sauf que lorsqu'on ajoute une traduction, on est obligé de redémarrer le serveur afin que les modifications dans le fichier .mo soient prises en compte. Pour info voici le code testé :
<?php
$lang = "fr";
$domain = "mondomaine";
putenv("LC_ALL=$lang");
setlocale(LC_ALL, $lang);
bindtextdomain($domain, "lang");
textdomain($domain);
?>
<!doctype html>
<html>
<head>
	<meta charset="UTF-8">
	<title>Test localisation</title>
	
</head>
<body>
    <h1>Test localisation</h1>
    <p><?php echo _("This string is localized using getetext") ?></p>
	
	<p><?php echo _("I hope it works!!!") ?></p>
    
    <p><?php echo _("Adding some other string...") ?></p>
	
</body>
</html>

Le troisième paragraphe a été traduit dans un deuxième temps (en éditant le fichier .po adéquat) et est resté en anglais. Après redémarrage du serveur (en local), la chaîne s'est bien affichée en français.
Le problème vient du fait que gettext met les fichiers .mo en cache pour accélérer l'accès.
J'ai trouvé des astuces pour contourner ce problème mais elles ne devraient pas être appliquées en production : Voir par exemple le deuxième commentaire de la doc sur gettext.

Ma question donc : connaissez-vous une bibliothèque php (restant simple d'utilisation) permettant d'utiliser gettext tout en s'affranchissant de redémarrer le serveur ?

J'ai beau chercher, je ne trouve rien hormis des framework du type zend qui me semble être beaucoup trop développé pour que je l'utilise juste pour de la localisation (et même si je l'utilisais pour cela, je ne suis pas sûr de savoir comment faire...).

Merci d'avance pour votre aide.
Modifié par jojaba (11 Mar 2015 - 10:28)
GetText est dispo en version PHP depuis à peu près 2 siècles. T'as pas pensé à chercher sur ton pote Google avant de poser ta question ?
Modérateur
a écrit :
GetText est dispo en version PHP depuis à peu près 2 siècles. T'as pas pensé à chercher sur ton pote Google avant de poser ta question ?

Ben si, justement !
As-tu bien lu ce que j'ai écrit ?
Gettext fonctionne très bien et depuis pas mal de temps effectivement et est uen extension php de manière automatique sur tous les serveurs apaches, ce qui explique pourquoi beaucoup de framework l'utilisent encore. Le problème que je soulève ci-dessus est que si l'on veut modifier une traduction, la seule manière de mettre à jour sur le site est de redémarrer apache, ce qui ne pourra pas se faire pour ma part.
Après une nuit de sommeil, je me demande si je ne vais pas plutôt opter pour une solution xml. L'avantage étant que la modification des traductions pourra se faire directement dans le fichier xml ou par l'intermédiaire de fonctions qui sont capables d'analyser un fichier xml (telle que simple_xml). Des retours d'expérience là-dessus ?
Modifié par jojaba (08 Mar 2015 - 10:51)
Bonjour,

Pour ma part, je suis d'avis que ce qui marche le mieux est encore ce qui est le plus simple: des fichiers ini...

Sur un de mes sites, j'ai ça par exemple :


function parse_ini_file_2 ($filename) {
$t = array();
foreach (@file($filename, FILE_SKIP_EMPTY_LINES | FILE_IGNORE_NEW_LINES) as $row) {
if (preg_match('%^\s*(.*?)\s*=\s*"?(.*)"?\s*$%', $row, $match)) {
$t[$match[1]] = trim($match[2]);
}}
return $t;
}


ET puis après :


$t = parse_ini_file_2("lang/$lang/module.txt");

echo <<<EOS
<p>{$t['This message should be written in the correct language']}</p>
EOS;


Avec un contenu de module.txt du genre

This message should be written in the correct language=Ce message devrait être écrit dans la bonne langue


Les avantages d'après moi sont:
- C'est extrêmement léger
- Les modifications sont prises en compte instantanément
- ON peut facilement répartir les différentes parties/modules du site dans des fichiers séparés
- ON n'a pas besoin d'installer 18 programmes spéciaux pour éditer les fichiers, et c'est, il me semble, facile à lire

XML pour moi ça transforme les avantages 1 et 4 en inconvénients sans contrepartie vraiment intéressante.
jojaba a écrit :

Ben si, justement !
As-tu bien lu ce que j'ai écrit ?


Non, j'ai pas lu, je répond juste avec des phrases aléatoires comme ça pour le fun. Smiley smile

Balgue à part, je disais juste qu'il existe des versions écrites en PHP de GetText, plutôt que l'extension écrite en C que tu utilises et qu'avec ces bibliothèques tu n'auras pas le problème de relancer le serveur à chaque mise à jour de tes fichiers de traductions.

Par rapport à ton autre question concernant le xml pour stocker tes trad, comme Quentin je préfère le format ini (ou yaml) qui t'éviteras des eventuels problèmes d'échappement et le xml ne t'apportera rien à part une lourdeur inutile en plus.

Autrement il existe un framework moderne lancé il y a quelques temps par Mozilla qui s'appelle l20n (http://l20n.org/) et qui permet de gérer la partie traduction d'un site/webapp. Je n'ai pas encore eu le temps de le tester sur un projet mais j'ai eu de bons retours d'experience dessus.
Modérateur
Que pensez-vous d'une solution .json (on me l'a soufflé juste à l'instant) ?
Ouais c'est pas mal, tu as l'avantage d'avoir des fichiers compatibles JS et PHP et ça reste facile à écrire, mais moins que des fichiers ini. Personellement j'utilise json pour mes fichiers de configuration.

A toi de voir ce qui te convient le mieux.
Modérateur
tempif a écrit :

Balgue à part, je disais juste qu'il existe des versions écrites en PHP de GetText, plutôt que l'extension écrite en C que tu utilises et qu'avec ces bibliothèques tu n'auras pas le problème de relancer le serveur à chaque mise à jour de tes fichiers de traductions.
Justement, je ne trouve pas ces versions php, tu as un lien ou un nom à me proposer ? J'ai trouvé le système de localisation du zend framework (je pense qu'on peut juste utiliser le dossier /l18n/ du famework afin de mettre une localisation de fichiers)...
a écrit :

Autrement il existe un framework moderne lancé il y a quelques temps par Mozilla qui s'appelle l20n (http://l20n.org/) et qui permet de gérer la partie traduction d'un site/webapp. Je n'ai pas encore eu le temps de le tester sur un projet mais j'ai eu de bons retours d'experience dessus.

Ça semble être une solution JavaScript, le traitement côté client ne m'intéresse pas.

Je vais probablement opter pour le json qui me semble suffisamment simple et efficace pour ce que je veux faire. J'essaierai de revenir ici pour faire le bilan, si j'ai bien opté pour cette solution.
a écrit :
Que pensez-vous d'une solution .json (on me l'a soufflé juste à l'instant) ?


Je pense aussi que ça se situe entre ini et XML, question légèreté, simplicité d'édition et traitement/parsing.

Perso je préfère balancer mon tableau associatif contenant les traductions à json_encode si j'en ai besoin en js et garder mes ini côté serveur, mais après effectivement c'est chacun fait comme il le sent.
Modérateur
Bon ben, comme c'était a première fois que j'utilisais json, je me suis un peu amusé avec...
Voilà où j'en suis pour l'instant :
json (test.json) :
{
    "title": 
    {
        "basis":"Testing the json translation",
        "trans":"Test traduction avec json"
    },
    "First paragraph on top":
    {
        "basis":"First paragraph to test the accentuated characters an others...",
        "trans":"Premier paragraphe du site avec caractères accentués et autres : garçon, €uro, l'apostrophe, les \"guillemets\""
    },
    "paragraph_2":
    {
        "basis":"This paragraph has not been tranlated",
        "trans":""
    },
    "Paragraph 3":
    {
        "basis":"This one is with some html tags...",
        "trans":"Troisième paragraphe du site. sans rien de spécial, juste quelques balises html.<br />Retour à la ligne puis <strong>mis en gras</strong> et en <em>italique</em>."
    }
}


php + html:
<?php
// choix de la langue (définie dans la chaîne get ici)
$lang = (isset($_GET['lang'])) ? $_GET['lang'] : "fr";
// On récupère le contenu du json de traduction (ici test.json)
$lang_json = file_get_contents('lang/'.$lang.'/test.json');
// On rend l'objet utilisable par php...
$texte = json_decode($lang_json);
// Fonction pour récupérer les chaînes de manière optimisée
function trad($key) {
    global $texte;
    if ($texte->{$key}->{'trans'} != '')
        // Si la chaine correspondant à la clé est traduite
        echo $texte->{$key}->{'trans'};
    else
        echo $texte->{$key}->{'basis'};
}
?>
<!doctype html>
<html>

<head>
	<meta charset="UTF-8">
	<title><?php trad("title") ?></title>
</head>

<body>
    <h1><?php trad("title") ?></h1>
    <p><?php trad("First paragraph on top") ?></p>
	
	<p><?php trad("paragraph_2") ?></p>
    
    <p><?php trad('Paragraph 3') ?></p>
    
    <h2>Le tableau entier</h2>
    
    <p><?php var_dump($texte) ?></p>
	
</body>

</html>

Ce qui m'a étonné, c'est que les clés pouvaient comporter des espaces (voir les premier et troisième paragraphes). Cela m'a fait penser que la clé aurait pu être la langue par défaut (l'anglais), mais j'ai peur que la longueur de ces clés soit limitée à un certain nombre de caractères et lors de l'ajout d'une traduction, les erreurs de clés (typo ou mauvis copier/coller) risquent d'arriver beaucoup plus facilement si le texte est long...
J'ai mis en place une fonction permettant de gérer le fait de ne pas avoir de traduction des chaînes d'origine (ici en anglais), de plus, si je veux faire traduire par une tierce personne, c'est plus pratique ainsi, puisqu'elle aura toujours le texte d'origine sous les yeux... Pour la mise à jour des traductions, un simple comparateur de fichiers fera l'affaire.
Votre avis ?

Je ne sais pas trop combien je vais définir de fichiers de traduction json. A priori, je vais avoir un inc.header.php et un inc.footer.php inclus dans tous les fichiers du site. Je comptais donc créer 3 fichiers json dans chaque dossier de traduction :
* header.json
* footer.json
* content_xxxxx.json (pour le contenu principal de chaque fichier, il y en aura 4 ou 5)
Ou serait-il plus intéressant de placer tout dans un seul et même fichier ?
Votre avis ?
Modifié par jojaba (09 Mar 2015 - 19:36)
Tu es parti sur une structure de données vachement compliquée pour tes trads. Tu devrait rester sur une structure clé/valeur a un seul niveau :

article_1_fr

title: "le titre"
meta_description: "bla bla bla"
heading: "le titre h1"
content: "<p>some text</p><p>some other text</p>"


article_1_en

title: "the title"
meta_description: "bla bla bla"
heading: "the article heading"
content: "" // vide, pas encore traduit en anglais.


Edit : balise code manquante.
Modifié par tempif (09 Mar 2015 - 20:48)
Modérateur
tempif a écrit :
Tu es parti sur une structure de données vachement compliquée pour tes trads.

Je voulais garder dans les traduction les chaînes de rférences (l'anglais). J'avais d'abord commencé par faire comme tu proposes et j'ai ajouté les chaînes "basis" pour toujors avoir sous les yeux la version "en" qui sert de base aux traducteurs.
La strucutre sous laquelle tu stockes tes données n'a pas a être calquée sur ce que tu affiches ou à la façon dont tu t'en sers au final, autrement ton système devient extrèmement rigide et inutilement complexe.
Modifié par tempif (09 Mar 2015 - 20:47)
Modérateur
tempif a écrit :
La strucutre sous laquelle tu stockes tes données n'a pas a être calquée sur ce que tu affiches ou à la façon dont tu t'en sers au final, autrement ton système devient extrèmement rigide et inutilement complexe.
Tu as raison, il faut structurer la base de données le plus simplement possible afin que l'on puisse la réutiliser sans souci (pour un autre langage ou pour une autre utilisation), mais dans mon cas, je ne vois pas à quoi cela pourra servir autrement que pour la traduction, on peut donc se permettre une certaine rigidité, non ? En outre, ce que tu dis signifie-t-il que le fichier de traduction unique serait la meilleure solution (un seul fichier à analyser, même si certaines occurrences ne seront pas utiles) ?
Modifié par jojaba (10 Mar 2015 - 07:16)
Pour 1 vs plusieurs fichiers, c'est surtout une question de taille et de feeling.
A partir d'une certaine taille, je pense qu'il vaut effectivement la peine de séparer en plusieurs fichiers. Ca facilite la maintenance, le travail à plusieurs, on charge moins de données inutiles....
Par contre si ça reste un site de taille modeste, tout mettre dans un seul fichier peut être plus pratique que d'avoir 12 fichiers avec seulement 3 ou 4 phrases à chaque fois.

Pas de science exacte ici, à chacun ses préférences et son organisation; c'est assez personnel.
Pour ma part mon projet perso qui est en 7 langues a 3000 messages par langue donc séparation indispensable, en une quaranttaine de modules distincts.



Maintenant, pour ce qui est d'avoir les textes anglais comme clé ou plus généralement des clés longues avec espaces et accents, c'est une mauvaise idée.
C'est pratique parce que tu n'as pas d'indirection, tu as directement le texte anglais de départ dans le code php. Mais tu risques d'avoir d'autres problèmes :

1 - Tu modifies le texte anglais et paf, la traduction ne fonctionne plus parce que tu as oublié de changer la clé dans le fichier
2 - Tant que tu restes bilingue ça va encore, mais par contre tu vas faire quoi quand tu ajouteras l'allemand ou l'espagnol ? Tu vas répéter plusieurs fois le texte anglais qui te sert de clé. ET voilà, tu as le problème en 1° en 3 ou 4 fois pire maintenant.

Sauf erreur, il me semble que gettext prend les textes anglais comme clé. Comme tu l'auras compris, pour moi ça me paraît être un sérieux problème de conception.


Même garder la base anglaise dans le fichier .json, je pense que ce n'est pas une bonne idée. Le jour où tu ajouteras une troisième langue, soit tu vas copier plusieurs fois le fichier de départ avec la base (un par langue) et donc le jour où tu modifies l'anglais ça va être galère de mettre à jour toutes les bases, soit tu seras obligé de mettre base+fr+es+de+... tout dans le même fichier, et du coup ça va être galère parce que tous les traducteurs vont devoir modifier le même fichier. Si tout le monde gère GIT/SVN/HG/etc. ça peut passer, mais même, bof; avec 6 ou 7 langues c'est garanti que ça finit en bordel.

Alors, oui, bon, le prix à payer c'est que le code php est moins lisible, tu n'as plus immédiatement les textes sous les yeux, il faut aller regarder les clés dans l'autre fichier pour voir quoi s'affiche où. D'où l'intérêt après de les choisir intelligemment pour s'y retrouver.

Plus spécifiquement par rapport à JSON, il me semble que tu es censé encoder en \uXXXX tous les caractères spéciaux non ASCII. Pas cool pour la relecture !

Tiens, question, gettext est-il capable de gérer les chaînes avec token du genre "Vous avez %1 messages non lus" ? Quand ja'vais regardé j'en avais pas l'impression. Pour ce genre de cas, une petite fonction n'est pas compliquée à faire, ou sinon on peut utiliser sprintf jusqu'à un certain point.
Modifié par QuentinC (10 Mar 2015 - 13:00)
Modérateur
QuentinC a écrit :
Pour 1 vs plusieurs fichiers, c'est surtout une question de taille et de feeling.
Ce sera donc un fichier unique je pense.
QuentinC a écrit :
Maintenant, pour ce qui est d'avoir les textes anglais comme clé ou plus généralement des clés longues avec espaces et accents, c'est une mauvaise idée.
D'accord avec toi, j'avais abandonné l'idée des clés qui étaient la version de base (anglaise donc) du texte à traduire.
QuentinC a écrit :
Sauf erreur, il me semble que gettext prend les textes anglais comme clé. Comme tu l'auras compris, pour moi ça me paraît être un sérieux problème de conception.
Je ne pourrai pas te répondre sur ce point-là, en revanche, pour avoir l'habitude de faire de la traduction de plugins WordPress (qui utilise gettext), aucun souci lorsque la version de base (anglaise) a été modifié dans le fichiers php. En effet, en utilisant poedit (éditeur de fichier po qui génèrent les mo pour la traduction), tu peux mettre à jour automatiquement les clés modifiées. Le logiciel scanne le dossier dans lequel se trouvent les fichiers localisés, met à jour les clés et propose ensuite de modifier la traduction correspondant à la clé qui a été modifiée et qui est considérée comme "approximative". C'est vraiment bien fait ! Il détecte également les traductions obsolètes (les clés ne sont pas retrouvées dans les fichiers sources). On peut faire ça également en ligne de commande (sans poedit).
QuentinC a écrit :
Même garder la base anglaise dans le fichier .json, je pense que ce n'est pas une bonne idée. Le jour où tu ajouteras une troisième langue, soit tu vas copier plusieurs fois le fichier de départ avec la base (un par langue) et donc le jour où tu modifies l'anglais ça va être galère de mettre à jour toutes les bases, soit tu seras obligé de mettre base+fr+es+de+... tout dans le même fichier, et du coup ça va être galère parce que tous les traducteurs vont devoir modifier le même fichier. Si tout le monde gère GIT/SVN/HG/etc. ça peut passer, mais même, bof; avec 6 ou 7 langues c'est garanti que ça finit en bordel.
OK pour ça. On va revenir à la version proposée par tempif. Ça m'apprendra de vouloir trop bien faire Smiley langue (pour finalement faire mal !)
QuentinC a écrit :
Plus spécifiquement par rapport à JSON, il me semble que tu es censé encoder en \uXXXX tous les caractères spéciaux non ASCII. Pas cool pour la relecture !
Euh, tout est encodé en utf-8, ça ne devrait donc pas poser de problème...
QuentinC a écrit :
Tiens, question, gettext est-il capable de gérer les chaînes avec token du genre "Vous avez %1 messages non lus" ? Quand ja'vais regardé j'en avais pas l'impression. Pour ce genre de cas, une petite fonction n'est pas compliquée à faire, ou sinon on peut utiliser sprintf jusqu'à un certain point.
Oui. Exemple trouvé dans un plugins WordPress au hasard :
#: ../includes/gce-feed-cpt.php:75 ../includes/gce-feed-cpt.php:76
#, php-format
msgid "%4$s updated. %1$sView %2$s%3$s"
msgstr "%4$s mis à jour. %1$sVoir %2$s%3$s"

Modifié par jojaba (10 Mar 2015 - 19:13)