Redmine Plugins #3 : supporter plusieurs langues
Redmine supporte à ce jour 37 langues. Si vous souhaitez diffuser votre plugin, c’est une bonne idée de respecter les mêmes conventions que le core, pour en faciliter les traductions, voire proposer plusieurs traductions de votre plugin directement. C’est ce que je fais par exemple pour mon plugin “Datacenter” que je livre en anglais et en français (voir la page de wiki française).
Pour cela, Redmine utilise l’internationalisation de Rails. Chaque mot ou groupe de mot qui doit être traduit est associé à une clé unique. Chaque langue
a son fichier YAML dans le dossier config/locales/
, et dans ce fichier on indique que telle clé correspond à telle
chaine de caractères. Par exemple, plutôt que d’écrire “Mon super plugin” directement dans vos vus et helpers, vous allez lui associer une clé de votre
choix, mettons text_my_super_plugin
.
Dans la vue, vous pourrez utiliser le helper l()
(un L minuscule) de cette façon :
<%= l(:text_my_super_plugin) %>
Ensuite vous devrez associer cette clé à sa valeur pour chaque langue. Pour le français, le fichier config/locales/fr.yml
de votre plugin
ressemblera à ça :
fr: text_my_super_plugin: Mon super plugin
Et vous pouvez traduire votre appli en anglais, en ajoutant un fichier config/locales/en.yml
contenant :
en: text_my_super_plugin: My great plugin
Pour un texte accentué ou comportant des caractères spéciaux, il suffira de mettre la chaine entre quotes pour éviter toute confusion lors de l’analyse du fichier. Attention à ce que votre fichier reste bien en UTF8 tout de même.
Si la traduction n’existe pas (fichier de langue manquant ou clé inexistante dans la langue de l’utilisateur), Redmine affichera une erreur. C’est la que
le helper l_or_humanize
peut être utile :
<%= l_or_humanize(:super_plugin) %>
Si la clé existe, elle sera remplacée par sa traduction. Si non, Rails tentera d’en faire une chaine pour humain (remplacement des underscores par des
espaces, majuscule à la première lettre, etc.). En l’occurrence Super plugin
.
Pour les affichages de dates, heures, temps ou intervalles de temps, il existe des helpers beaucoup plus évolués que ceux présentés ci-dessus. Ils sont
définis dans lib/redmine/i18n.rb
. En voici une liste, ainsi que comment les tester dans une console Rails :
% ruby script/console Loading production environment (Rails 2.3.5) >> include Redmine::I18n => Object >> set_language_if_valid('fr') => :fr >> l_hours(5) => "5.00 heures" >> format_date(Time.now) => "26/04/2010" >> format_time(Time.now) => "26/04/2010 19:55" >> day_name(1) => "lundi" >> month_name(3) => "mars"
A des fins de test, le helper ll()
permet de préciser d’abord la locale avant la clé et ainsi de tester une clé dans une locale particulière
:
>> ll("fi", :field_mail) => "Sähköposti"
Dernière chose, il est possible d’utiliser des variables dans vos fichiers de langue. Ils seront interpolés lors du rendu de la vue. Si vous n’avez qu’une
variable à mettre, vous pouvez utiliser le nom “value” et passer la valeur dans votre vue directement en 2e argument de votre l()
. Si vous avez
2 variables ou plus, il faut leur donner un nom et passer un hash en 2e argument de l()
dans votre vue. Evidemment ces valeurs peuvent
elles-même faire appel à vos traductions pour éviter de dupliquer des traductions.
Un exemple vaut mieux qu’un long discours. Avec ce fichier de langue :
fr: label_draft_saved_time: "Brouillon sauvegardé à {{value}}" label_draft_pending: "Brouillon en attente, sauvegardé il y a {{time}} : {{restore}} ou {{delete}}" label_draft_restore: "restaurer" label_draft_delete: "supprimer"
Je peux faire appel à ceci dans mes vues (les valeurs de temps sont bidon) :
<%= l(:label_draft_saved_time, format_time(Time.now)) %> <%= l(:label_draft_pending, {:time => format_time(Time.now), :restore => l(:label_draft_restore), :delete => l(:label_draft_delete)}) %>
J’essaierai de documenter tout ça en anglais dans le wiki Redmine un de ces 4.