Aller au contenu
AFUP AFUP Day 2024 Baromètre Planète PHP PUFA
 

Traduire efficacement une application Symfony

Description

La traduction d'une application est une étape primordiale dans un projet international. Mais sa mise en place est souvent douloureuse, et ça le reste tout au long de la vie de l'application. Un nouveau type d'intervenant entre en jeu en plus des développeurs, chefs de projets, designers, voici maintenant les traducteurs. Comment faire travailler toutes ces personnes efficacement, et garder l'intégrité des traductions de l'application ? C'est ce que je vous propose d'aborder dans cette conférence. Nous verrons ensemble quels outils externes utiliser pour faciliter la traduction aussi bien pour les développeurs, que les traducteurs, mais aussi pour les intervenants en qualité qui sont chargés de vérifier tout ce travail. Nous verrons également comment brancher ces outils au sein de Symfony pour bénéficier d'un workflow de traduction cohérent et simple d'utilisation d'un environnement local à la production. À la fin de la conférence vous n'aurez qu'une envie, mettre à jour vos projets avec ces outils pour enfin vous épargner de longues journées de mise à jours de traductions !

Conférence donnée lors du PHP Tour Montpellier 2018, ayant eu lieu les 17 et 18 mai 2018.

Informations complémentaires

Vidéo

Le speaker

Mathieu SANTOSTEFANO

Après un BTS Informatique de Gestion (option Développement d’Applications) et un passage à l’IESA Multimédia, Mathieu se perfectionne sur les technologies PHP et Javascript. Après quelques années de développement avec symfony 1, il rencontre JoliCode via une formation Symfony donnée à l’IESA Multimédia par Benjamin et Joël et, depuis, il explore au quotidien l’univers de PHP, et dispense à son tour la formation Symfony auprès d’étudiants. Mathieu suit assidûment l’évolution des frameworks PHP et Javascript. À l’affût du dernier plugin/outil/librairie/logiciel qui permet d’améliorer la qualité et la robustesse du code, et la productivité. Vous pouvez le retrouver sur Twitter et sur GitHub, ou le croiser lors d’un prochain meetup?!

Transcript

Bonjour. Je m'appelle Mathieu Santotefano, je suis formateur, développeur, je fais du Symfony depuis un moment.

Je travaille chez Joli Code et j'ai découvert hier que la balise <marquee> fonctionne encore (on recrute).

Je suis sur Twitter et Github (@welcomattic) et tous les jours je fais du Symfony, JS, Docker, Elastic Search, et plein d'autres choses super chouettes.

Ce dont je veux vous parler aujourd'hui c'est la traduction dans Symfony.

Ce talk est issu en fait d'un retour d'expérience sur le projet Ouibus, sur lequel on travaille chez Joli Code depuis plus d'un an maintenant.

Ouibus voulait refaire son site au niveau européen, donc déployer six sites en six langues, et afficher plein de données, et il fallait donc traduire tout ça.

La traduction d'une application comporte trois choses : la traduction de l'interface (des moyens d'interaction avec l'utilisateur), la traduction du contenu (des articles, des noms des gares, des horaires, etc.), et la traduction des url (par exemple, "ouibus/trajets" sera "ouibus/trips" en anglais) Aujourd'hui, on va se concentrer sur la traduction de l'interface (on parlera peut-être dans de futurs talks des autres traductions).

Le but est de trouver un workflow pour fluidifier la traduction d'un projet avec les bons outils et les bonnes méthodes pour chaque intervenant.

Avant de parler de tout ça, je fais une parenthèse sur deux sigles qu'on voit souvent quand on recherche des informations sur la traduction : i18n et l10n.

Ce sont deux abréviations pour lesquelles le W3C nous donne des définitions (pour l'anecdote dans i18n, 18 c'est le nombre de lettres qui sont écrites entre le "i" et le "n").

Et l'i18n c'est une étape dans la conception et le développement d'une application qui permet par la suite de l'adapter à un public défini.

Pour les développeurs, ce sont les clés de traduction, les formats numériques, les formats de date, de devise, mais aussi certains symboles, certaines couleurs qui ont des significations dans certains pays...

Et la "l10n" (là encore, 10 étant le nombre de lettres entre le "l" et le "n"), c'est l'adaptation concrète de l'application à un public visé, donc répondre à ses exigences culturelles, linguistiques...

Ça nécessite très souvent des traducteurs ou des natifs du pays, qui connaissent les contraintes particulières, notamment en termes de couleurs ou de style (un site en arabe se lit sur l'écran de droite à gauche).

Il faut prendre en compte toutes ces choses là, ça fait partie de la localization.

Traduire son application : on va voir quand, qui, comment et pourquoi on va le faire.

C'est impératif de le mettre en place le plus tôt possible.

J'ai dû traduire une application où il y avait du texte en dur dans le code : c'est pas drôle d'aller le retrouver ! Cette traduction sera faite par des gens (ressources humaines), en utilisant des outils (ressources techniques), à l'intérieur de Symfony.

Tout ça dans le but de déployer une application dans plusieurs pays, pour des gens qui ne parlent pas tous la même langue et qui n'ont pas tous les mêmes habitudes de consultation d'un site.

Pour ça, on a des standards qui existent : on n'a rien inventé en faisant de la traduction dans le web, on traduit des logiciels depuis beaucoup plus longtemps.

En standards, il y a le ".po" et ".mo" que vous avez peut-être déjà croisé en faisant du Wordpress ou du Drupal (deux formats issus de gettext, une librairie en C, qui permet de faire de l'internationalisation de programme), et il y a le ".xlf" (XLIFF) qui depuis mars dernier est un format reconnu et standard.

L'OASIS (un consortium qui définit un certain nombre de normes) a validé en mars un standard de traduction : web ou pas web, c'est XLIFF (donc ils ont tué .po et .mo).

Mais on a de la chance puisque c'est aussi ce format qui est recommandé dans Symfony.

Pour utiliser ces formats de fichiers il y a des outils : dont poedit (disponible sur Windows, Mac et Linux), et il y a des services en ligne qui proposent d'uploader nos fichiers de traduction, et nous fournissent de belles interfaces pour les manipuler.

Comme je disais, XLIFF est recommandé dans Symfony par l'OASIS mais aussi par Symfony depuis le début, et c'est adapté au format web parce que c'est basé sur xml donc on a l'habitude de manipuler ça.

Enfin, c'est utilisable avec tous les outils de traduction en ligne ou poedit.

Maintenant : quels sont les problèmes qui vont survenir quand on va faire de la traduction ? Quelles sont les difficultés que l'on a pu rencontrer et comment on y a répondu ? Premier problème : un projet web c'est beaucoup d'intervenants.

Des intervenants techniques (développeurs back, front, intégrateurs, etc.), et des intervenants non technique, (traducteurs, chefs de projet, testeurs, rédacteurs, etc.) à qui on va pas donner des fichiers .xlf à éditer ! Il y a le problème de la gestion des messages : il va falloir trouver qui est responsable d'ajouter de nouvelles clés dans l'application, définir une nomenclature pour ces clés (on va pas les nommer n'importe comment, il faut un standard), pour savoir à quel endroit dans l'application correspond telle clé, il va falloir qu'on soit capable de synchroniser l'écriture de code et la traduction d'une fonctionnalité, et ensuite il va falloir gérer les problèmes de déploiement.

Je pense que c'est arrivé à plein d'entre vous si vous avez fait la traduction : un chef de projet ou un rédacteur vous signale une faute. Vous corrigez dans la traduction, mais après, il faut déployer l'appli. Et déployer l'appli pour corriger un "s" sur la homepage, c'est un peu relou ! Il y a le problème de stockage des messages.

Il y a le stockage "propre" : soit dans un Saas qui gère nos traductions pour nous, soit dans les fichiers XLIFF qu'on versionne dans Git, et puis il y a le versionning plus douteux : un .csv qui traîne sur un FTP, qu'on va récupérer, modifier, renvoyer, ou (un truc que j'ai vu dans la vraie vie) un Excel qui se balade en pièce jointe d'un mail.

On télécharge l'Excel, on renvoie le mail derrière avec tout le monde copie : comme ça on a une nouvelle version des traductions ! Très mauvaise idée, faites pas ça ! Côté frontend, on va avoir des problèmes aussi puisqu'il va falloir envoyer nos traductions PHP (donc XLIFF) vers le frontend pour être capable de faire de l'Ajax et d'afficher les choses et les labels dans les bons formats.

Par exemple, quand on a un appel Ajax, il faut afficher le message d'erreur dans la langue de l'utilisateur.

Donc est-ce qu'on met tout dans window.TRANSLATIONS ? Pas trop...

Si vous mettez les traductions des clés de votre back-office en front, ça peut leaker potentiellement quelques infos un peu sensibles.

En plus, il faudrait réinventer la roue : gérer la gestion des paramètres des clés de traduction, les pluriels...

Si vous allez voir la doc de Symfony, les règles de gestion du pluriel en russe c'est assez sympa ! Donc si vous voulez implémenter tout ça : bon courage ! Voilà donc la traduction, vous l'aurez compris c'est pas un truc trivial, il faut beaucoup y réfléchir, analyser le besoin, savoir si on a vraiment besoin de beaucoup de langues ou pas, qui va traduire les choses (est-ce qu'on a des traducteurs ? est-ce qu'on fait appel à Google trad?)...

Il faut analyser tout le besoin, mettre en place un bon workflow et prendre le temps de s'y intéresser.

Maintenant on va voir comment on a réglé tous ces problèmes.

Premier point : on a choisi une solution SaaS, Loco, gratuit pour les projets open source et en dessous de 2000 traductions (payant au-delà mais très peu cher donc ça passe dans un budget de projet).

Ça permet d'uploader ses fichiers XLIFF et d'avoir une jolie interface pour les traducteurs.

On peut "flagger" les traductions comme validées, invalides, à corriger, pas encore faites...

Ensuite, ce SaaS, on va pouvoir l'interroger avec un client d'API.

Pour ça il y a un truc génial : PHP-translation, une organisation sur Github qui fournit des outils pour l'internationalisation de sites web (donc pas forcément que Symfony, PHP en général), créée par Victor et Tobias.

Tobias vous avez sûrement déjà croisé son nom dans des package PHP ou Symfony, il fait énormément d'open source (d'ailleurs merci à lui parce que c'est vraiment génial ce qu'il fait).

PHP-translation ce sont des outils pour l'internationalisation, ça se présente sous quatre outils principaux : une classe "message" qui va représenter un message (une clé de traduction et sa traduction), un "extractor" (qui dans Symfony est basé sur l'extracteur de Symfony mais utilisable aussi en dehors), des "storage adapter" (et c'est là toute la magie du truc car ce sont des classes qui vont nous permettre d'interroger des API de services en ligne de traduction ou les fichiers XLIFF ou les deux en même temps, donc on va pouvoir synchroniser dans notre application Symfony les traductions en local dans nos fichiers et celles qui sont sur les API en ligne des software as a service), et enfin, "translator" (à ne pas confondre avec le translator de Symfony), ici ce sont des classes qui vont permettre de traduire automatiquement des choses.

Vous prenez du texte, vous écrivez la traduction dans votre langue maternelle, vous envoyez dans translator, et translator va vous donner une clé d'API payante chez Google trad ou Bing ou autre, il va vous faire la traduction et vous renvoyer la traduction faite par l'API de Google trad.

Je ne sais pas comment ça marche, j'ai jamais utilisé - reste à voir si la traduction de Google trad est bonne.

Ces outils-là sont framework-agnostic : c'est un package PHP, il ya un bundle Symfony pour faciliter la configuration, un package Laravel pour les mêmes raisons, et ça supporte plusieurs SaaS (comme Transifex, Loco, PhraseApp) et une interface permet d'en implémenter d'autres.

Le bundle Symfony permet de configurer les Storage Adapters facilement à base de variables d'environnement, permet d'accéder à quatre commandes très utiles (extraire les clés de traduction, télécharger les traductions depuis les API, synchroniser les traductions locales et distances et avoir le statut en cours de traduction).

Et trois choses importantes : des outils d'édition de messages.

C'est là qu'on va découvrir que chacun dans le projet (techniques ou non) peut contribuer à la traduction, puisque il y a trois outils dédiés pour chaque personne.

Quelques mots sur la configuration : quelques lignes de code, les langues dans lesquelles on veut traduire, les répertoires dans lesquels on veut extraire et ce qu'on veut pas toucher, le remote storage où on configure la clé d'API pour le SaaS et en avant ! Pour les tests, petite astuce : n'affichez jamais les traductions dans vos pages de test, en environnement de test affichez toujours les clés de traduction : si un traducteur modifie une clé, pour changer le nom de n'importe quel bouton et que votre test passe dessus, si vous affichez la clé, vous aurez toujours la même, si vous affichez la traduction, le traducteur peut casser le test...

Une petite astuce : vous décorez le translator de Symfony.

Dans la méthode trans, si le domaine n'est pas une route (sinon vous perdez l'internationalisation des routes), vous ne renvoyez que l'ID (la clé) : ça vous permet d'afficher que les clé et que le traducteur pète les tests.

Autre astuce en prod, en pré prod, en local : mettez un crontab tous les quarts d'heure, qui va chercher les traductions, comme ça vous êtes toujours à jour vis-à-vis de votre SaaS.

Si les traducteurs sont en train de bosser vous récupérez en quasi temps réel les nouvelles traductions et les mises à jour.

Pour les outils : premier outil à destination des développeurs dans le profiler de Symfony.

On est habitués à avoir un tableau qui nous récapitule toutes les clés traduction utilisées dans la page, eh bien PHP translation nous permet de sélectionner les clés de traductions manquantes (ici le tableau des clés manquantes), de les envoyer dans l'API du SaaS, et en parallèle dans les fichiers XLIFF.

Une fois que c'est envoyé, vous avez un petit bouton "edit" qui apparaît sur le côté pour éditer le message.

Voilà "edit" et "sync", tout se fait en Ajax et tout se synchronise entre les fichiers et les API.

Donc c'est plutôt cool quand nous, développeurs, on a créée de nouvelles features et ajouté de nouvelles clés, on passe par ce tableau, on sélectionne tout, on envoie tout dans le SaaS, on édite dans notre langue maternelle (si on a les maquettes avec les labels faits, sinon on laisse les clés et les traducteurs s'en occuperont).

Deuxième outil : une WEBUI accessible via une url (à sécuriser dans votre application pour éviter que tout le monde y accède) et vous avez le statut de toutes les langues et de tous les domaines de traduction.

Et ça se présente sous cette forme, vous avez aussi des text area (petite astuce : il n'y a pas de bouton valider, quand vous perdez le focus du champ ça envoie une requête Ajax).

Et dernier outil : un EditInPlace. Vous avez une clé, vous cliquez sur le petit bouton crayon, et vous pouvez éditer votre clé, comme par magie dans HTML, vous validez, et ça envoie une requête Ajax, ça synchronise vos API, le XLIFF, etc. et tout le monde est content.

Attention : mettez ça derrière un rôle admin pour éviter que tous vos users y aient accès, sinon ils vont modifier tous vos labels et ça va être n'importe quoi...

L'avantage c'est que les testeurs, les chefs de projets ou les rédacteurs peuvent directement corriger des clés de traduction, sans avoir à passer par tous les outils de traduction, directement depuis de frontend donc c'est pratique.

Pour la nomenclature des clés, PHP translation nous fournit un tableau avec des conseils, je vous recommande vivement de les suivre. Nous on les a appliqués, pas tout à fait à la lettre, parce qu'on a beaucoup utilisé le dernier joker où on met vraiment le chemin vers lequel on veut pointer dans l'interface, mais pour tout le reste (formulaires, labels, errors, etc.), ça évite d'avoir 70 occurrences de la traduction "envoyer" ou "oui", et ça on économise de la place, du temps et c'est génial.

Vous l'aurez compris, Saas + PHP-translation, c'est le bonheur pour tout ce qui est traduction, on règle les problèmes dont on a parlé au début, on a une convention nomenclature, on a réglé le problème du stockage puisqu'on stocke en remote et on copie en local dans les fichiers.

Pour l'édition de messages, chacun a son outil : tout le monde peut travailler en autonomie.

On est indépendant des développeurs et du chef de projet qui demande de corriger un "s" et déployer l'appli puisqu'on a un crontab qui télécharge régulièrement et met à jour les traductions sans déployer le code pour corriger une faute.

Schéma récap : première étape, les développeurs ajoutent des clés, c'est synchronisé entre l'API des SaaS et le .xlf ; deuxième étape, les traducteurs traduisent via l'interface graphique des SaaS, et ensuite on a un cron infini qui tourne tous les quarts d'heure dans tous les environnements pour télécharger depuis les API et éditer les XLIFF pour les mettre à jour.

Bon c'est bien tout ça mais il reste quelques petits problèmes...

Pour le front, on n'a pas vraiment eu de besoins mais il existe BazingaJsTranslationBundle, qui je pense est une bonne alternative (il faudrait le tester).

Dans le bundle de PHP-Translation, il y a un problème de gestion du cache : quand on télécharge de nouveaux domaines de traduction qui n'ont pas été créés, le cache est mal reconstruit.

Et il y avait le problème d'internationalisation des routes, qui depuis Symfony 4.1.0-BETA est mergé et released, et Symfony 4.1 sort bientôt donc on va pouvoir supprimer un bundle pour la traduction des url.

Pour aller plus loin, si vous vous servez de tout ça, je vous encourage vivement à ajouter des Storage Adapters, si vous en avez besoin, si vous utilisez d'autres services en ligne qui sont pas supportés, ou si vous avez du stockage de clés de traduction un peu exotique (redis, doctrine ou autre).

C'est hyper facile, il y a une interface et trois méthodes à implémenter (ça prend une heure avec les tests) Contribuer à améliorer les outils d'édition : par exemple, perdre le focus d'un champ pour valider un formulaire, c'est pas génial donc il y a un peu de boulot là dessus.

Et battle-tester au maximum des configurations différentes, parce que la traduction dépend du projet (si vous avez beaucoup de langues ou pas, énormément traductions ou très peu, etc.).

Utilisez ça et ça fera avancer le projet. Maintenant, à vous de jouer ! Vous devez traduire vos sites, même si vous n'avez qu'une langue.

Ce sera toujours plus facile d'avoir un endroit centralisé avec les labels de toute votre interface dans un fichier, plutôt que d'aller vous amuser à ouvrir toutes vos vues à chaque fois.

Choisissez un SaaS : composer req php-translation/symfony-bundle ou Laravel, ou le package standalone si vous êtes sur autre chose.

Utilisez ou créez votre Storage Adapter, expliquez à votre équipe le workflow : réunissez tout le monde pour expliquer comment va fonctionner la traduction à partir de maintenant.

Formez vos équipes aux outils, montrez-leur comment ça marche, ce à quoi ils ont accès, comment ils peuvent faire, les effets que ça donne, qu'ils peuvent corriger en prod (avec parcimonie !), et les gens sont tous assez intelligents pour comprendre comment ça marche.

Il faut pas avoir bac + 30 et trois doctorats pour faire fonctionner ça, c'est simple et ça simplifie la vie à tout le monde.

Je vous remercie. Et merci à l'AFUP pour organisation du PHP Tour.

Merci à toi. Des questions ? Bonjour, merci : on a deux façons de modifier les traductions, soit directement dans l'appli en mode dev, soit via le mode SaaS. Certaines solutions SaaS ont aussi un workflow interne de validation.

Comment se passent d'éventuels conflits quand on modifie quelque chose "in app", le contexte de qui l'a modifié pour rejoindre le workflow de validation dans les outils, est-ce que ça se passe et si oui comment ? Il y a une petite couche de sécurité dans le bundle. Admettons qu'on ait modifié les .xlf en local, on a rajouté des nouvelles clés, donc le fichier est modifié et les traducteurs ont fait des modifs aussi de leur côté.

Au moment où on va merger, on va d'abord synchroniser (donc envoyer les nouvelles clés manquantes), et ensuite on va récupérer le tout. Ce sera mergé du côté du SaaS et s'il y a des erreurs de ce côté, qu'il détecte des conflits, ça va être plus facile de les merger côté interface du SaaS, où il y aura des alertes (un peu comme dans Github), on valide tout ça et ensuite on récupère, et la petite sécurité c'est que le bundle télécharge d'abord tout ça, vérifie qu'il y a des données dans ce qu'il a téléchargé, vérifie qu'il y a tout ce qui a besoin d'être dans le fichier avant d'écrire sur le disque.

Donc s'il y a un problème, que ça répond un code d'erreur bizarre sur l'API, il ne va pas écrire sur le disque, il va laisser les .xlf présents et il va pas perdre des traductions. Jamais.

Tout d'abord merci. J'ai un peu de mal avec le SaaS, notamment parce que s'il tombe, tu es un peu embêté sur ton site. Même s'il y a du cache, je suppose, est-ce que tu même s'il ya du cash je suppose mais est ce que tu as regardé les solutions de type Loco mais en opensource, en self-hosted éventuellement ? J'en ai pas vu. Je sais que blablacar avait essayé de le faire à l'époque.

Évidemment on s'est demandé ce qui se passait si le SaaS tombait, est-ce qu'on perdait nos traductions, etc.

En fait on s'est rendu compte que s'il tombait ce n'était pas si grave que ça parce qu'au pire, les traductions sont pas synchronisés pendant quelques minutes, heures voire une journée, mais en même temps tu modifies pas des traductions tous les quatre matins.

Si tu rajoutes des features, tu retardes un peu leur déploiement si possible, jusqu'au moment où tu récupères ton SaaS.

Au pire, les fichiers XLIFF sont toujours là et, au besoin, tu les synchronises entre les différents environnements, tu les synchroniser avec git, tu les déploies avec ton service de développement (git ou autre) et tu te retrouves avec les bonnes traductions en prod.

Il faut que le SaaS soit la source de tes données de traduction et il faut avoir la copie en local avec les XLIFF.

En théorie, on n'aurait pas besoin de versionner les XLIFF, mais c'est mieux pour être sûrs de les avoir quelque part, et d'avoir un backup au sein de ton workflow avec git, qui évite d'être dépendant à 100% du SaaS.

Merci pour le talk. J'ai deux questions.

Premièrement, tu proposes de désactiver le traducteur en environnement de test pour afficher uniquement la clé de traduction, mais on sait que souvent, les clés de traductions ont besoin de certaines variables, comment est-ce que tu fais pour vérifier que ces variables sont bien passées aux clés de traduction en test ? Deuxièmement, si on utilise une plateforme de traduction, est-ce vraiment aux développeurs de créer les clés de traduction, ou est-ce que ça devrait être fait en fait en amont du développement ? Pour la première question, j'ai présenté un translater relativement simple, dans celui qu'on a implémenté on récupère la liste des paramètres et on la concatène au bout de la clé de traduction avec un certain pattern, ce qui permet de tester aussi que les paramètres sont bien passés, et leur valeur.

Pour récupérer un autre translator, vous vous décorer un translator de base, vous rajoutez les paramètres, vous pouvez même faire une chaîne JSON avec la clé en premier en premier index et le reste.

Deuxième question : est-ce que c'est toujours aux développeurs d'ajouter les clés de traduction ? Ça pourrait être fait en amont, il faut juste être capable de récupérer la clé par défaut.

Je pense que c'est possible, nous on s'est donné la responsabilité d'ajouter les clés, mais ça peut être fait au niveau de la conception, au niveau du maquettage peut-être.

Du moment qu'il y a une nomenclature de nommage des clés et que tout le monde s'y retrouve, pourquoi pas.

Après les développeurs récupèrent la liste des nouvelles clés ajoutés pour les mettre au bon endroit.

Merci Mathieu.

De rien et bon appétit !