La CLI Lingo.dev traduit les fichiers YAML Rails config/locales via un moteur de localisation configuré. Rails intègre l’API i18n en natif : tout le texte traduisible de votre application vit dans des fichiers YAML par langue. Lingo.dev s’intègre à votre pipeline existant sans ajouter de dépendance à l’exécution.
Ce guide vous accompagne pas à pas dans la localisation d’une application Rails, de bout en bout : configuration du CLI, organisation des fichiers YAML par langue, bascule de langue à la volée au moment de la requête et automatisation des traductions en CI.
Dépôt de démonstration
Clonez ou fork lingodotdev/ruby-on-rails-localization-example pour suivre pas à pas. C’est une application Rails pleinement fonctionnelle, avec des fichiers YAML config/locales, un .lingo/config.json versionné et des traductions déjà en place, pour que vous puissiez lire la configuration à côté du résultat qu’elle produit.
Comment fonctionne la localisation dans Rails#
Rails lit les traductions à partir des fichiers YAML situés dans config/locales/. Chaque fichier utilise un code de langue comme clé racine et contient des clés imbriquées qui reflètent les chemins de recherche utilisés par votre code avec I18n.t.
| Couche | Ce qu’elle contient | Fichier d’exemple |
|---|---|---|
| Chaînes d’interface | Boutons, libellés, messages flash | config/locales/en.yml |
| Contenu des e-mails | Objets et corps des ActionMailer | config/locales/mailers.en.yml |
| Erreurs de modèle | Messages de validation et noms d’attributs | config/locales/activerecord.en.yml |
La première clé de chaque fichier YAML Rails est le code de langue lui-même — en:, es:, fr:. Rails indexe les traductions sur cette clé racine, et non sur le nom du fichier : il charge tous les fichiers sous config/locales/ et stocke le contenu de chacun sous la racine qu’il déclare. Résultat : un es.yml toujours enraciné sous en: n’est pas ignoré — il fusionne dans l’espace de noms en. L’espagnol se retrouve alors sans aucune traduction, tandis que les chaînes anglaises sont discrètement écrasées.
Traduire ce format implique donc de réécrire cette clé, pas seulement les valeurs. Le format yaml-root-key fait exactement cela : il parcourt l’arborescence sous la clé racine, traduit uniquement les valeurs de type chaîne, puis écrit le fichier cible avec la langue cible en racine. Les clés imbriquées, les jetons d’interpolation %{name} et les catégories de pluriel CLDR (zero/one/two/few/many/other) relèvent de la structure ; ils sont donc conservés tels quels, tout comme les commentaires et les ancres YAML.
Prérequis#
Créer un moteur de localisation
À chaque exécution du CLI, le contenu est envoyé via un moteur de localisation — la configuration qui détermine quel modèle LLM, glossaire, voix de marque et règles s’appliquent. Créez-en un dans le dashboard Lingo.dev et générez une clé API.
Vérifier Ruby et Rails
Ce guide s’adresse à Rails 7.2 ou version ultérieure, qui nécessite Ruby 3.1 ou version ultérieure. Vérifiez vos versions :
ruby -v
rails -vVérifier Node.js
Le CLI nécessite Node.js 22 ou une version ultérieure :
node -vConfigurer i18n dans Rails
Ce guide part du principe que votre application stocke déjà ses traductions dans config/locales/*.yml. Si vous avez encore des chaînes en dur dans les vues ou les contrôleurs, commencez par les extraire dans des appels t(). Par exemple, remplacez :
<h1>Welcome</h1>par :
<h1><%= t(".welcome") %></h1>puis ajoutez la clé correspondante à config/locales/en.yml. Consultez le guide d’internationalisation de Rails pour la marche à suivre complète.
Organiser les fichiers de traduction#
Rails charge automatiquement chaque fichier *.yml situé sous config/locales/. Gardez la langue source à côté de ses versions traduites afin que le répertoire fasse office de source unique de vérité :
config/locales/
en.yml # Source locale
es.yml # Generated by Lingo.dev
fr.yml
de.ymlUn fichier en.yml typique combine des chaînes simples, des espaces de noms imbriqués, l’interpolation %{name} et la pluralisation :
en:
hello: "Hello"
home:
welcome: "Welcome, %{name}!"
cta: "Get started"
notifications:
unread:
zero: "No unread notifications"
one: "1 unread notification"
other: "%{count} unread notifications"
errors:
messages:
blank: "can't be blank"Configurer la CLI#
Installez le CLI, authentifiez-vous, puis liez le projet à votre moteur :
npm install -g @lingo.dev/cli
lingo login
lingo init
lingo linklingo init et lingo link créent .lingo/config.json avec vos langues, ainsi que le orgId et le engineId de votre moteur. Versionnez-le avec .lingo/lock.json, afin que chaque machine et chaque exécution CI partagent le même état.
Faites pointer une entrée files[] vers le fichier de langue source et définissez explicitement le format :
{
"orgId": "org_...",
"engineId": "eng_...",
"sourceLocale": "en",
"targetLocales": ["es", "fr", "de"],
"files": [{ "pattern": "config/locales/en.yml", "format": "yaml-root-key" }]
}"format": "yaml-root-key" est obligatoire ici, pas facultatif. Un chemin .yml ne permet pas de savoir si sa clé racine correspond à une langue ou à une configuration classique ; le CLI ne cherche donc pas à deviner. Si vous omettez format, le fichier est traité comme un yaml générique : les valeurs sont traduites, mais la racine reste en: — l’échec silencieux décrit plus haut.
Les fichiers cibles sont dérivés du chemin source : config/locales/en.yml produit donc config/locales/es.yml, config/locales/fr.yml et config/locales/de.yml.
Rails charge aussi des fichiers par domaine aux côtés de en.yml — devise.en.yml, mailers.en.yml, activerecord.en.yml. Ajoutez une deuxième entrée avec un glob pour les inclure :
{
"files": [
{ "pattern": "config/locales/en.yml", "format": "yaml-root-key" },
{ "pattern": "config/locales/*.en.yml", "format": "yaml-root-key" }
]
}Les deux motifs ne se chevauchent pas : le premier ne correspond qu’à en.yml, et le second uniquement aux fichiers se terminant par .en.yml. Les fichiers déjà traduits comme es.yml et devise.es.yml ne sont donc jamais repris comme sources. devise.en.yml produit devise.es.yml.
Configurer Rails pour plusieurs langues#
Indiquez à Rails quelles langues sont disponibles et laquelle utiliser par défaut. Dans config/application.rb :
module YourApp
class Application < Rails::Application
config.i18n.available_locales = [:en, :es, :fr, :de]
config.i18n.default_locale = :en
config.i18n.fallbacks = [:en]
end
endDéfinissez la langue de la requête dans ApplicationController à partir d’un paramètre d’URL ou de l’en-tête Accept-Language :
class ApplicationController < ActionController::Base
around_action :switch_locale
private
def switch_locale(&action)
locale = params[:locale] || http_accept_locale || I18n.default_locale
I18n.with_locale(locale, &action)
end
def http_accept_locale
header = request.headers["Accept-Language"].to_s
header.scan(/[a-z]{2}/).find { |l| I18n.available_locales.map(&:to_s).include?(l) }
end
def default_url_options
{ locale: I18n.locale }
end
endAfficher les traductions dans les vues#
Utilisez les helpers t et l dans vos templates ERB. Un point en début de clé la résout par rapport au chemin de la vue courante, ce qui permet de garder les clés de traduction au plus près des templates qui les utilisent :
<h1><%= t(".welcome", name: @user_name) %></h1>
<p><%= t("notifications.unread", count: @unread_count) %></p>
<%= link_to t(".cta"), signup_path, class: "btn-primary" %>Ajoutez un sélecteur de langue à votre layout :
<nav>
<% I18n.available_locales.each do |locale| %>
<%= link_to locale.upcase, url_for(locale: locale) %>
<% end %>
</nav>Traduire en local#
lingo push --waitpush téléverse les fichiers source, les fait passer par votre moteur de localisation, puis réécrit les fichiers traduits. --wait garde la commande bloquante jusqu’à ce que les sorties soient disponibles — un paramètre utile à préciser explicitement, car une prochaine version changera le comportement par défaut : push lancera l’exécution et reviendra immédiatement, à charge pour vous de récupérer les résultats avec lingo pull.
Les exécutions suivantes ne traduisent que le delta : push calcule un hash de la source et le compare à .lingo/lock.json, de sorte que les entrées inchangées ne coûtent rien. La première exécution sur un projet — ou après l’ajout d’une langue — nécessite en revanche l’ensemble du corpus :
lingo push --backfill-missingPour limiter une exécution à un sous-ensemble de fichiers, passez un glob :
lingo push "config/locales/**"Pour récupérer des sorties générées ailleurs — sur une autre machine ou en CI — exécutez lingo pull.
Redémarrez le serveur Rails après la première exécution de traduction pour que les nouveaux fichiers YAML soient bien chargés :
bin/rails serverRendez-vous sur /es pour voir le rendu en espagnol.
Pluriels#
Rails s’appuie sur les catégories plurielles CLDR : zero, one, two, few, many, other. Passez un argument count: à I18n.t et Rails sélectionnera la clé correspondante :
t("notifications.unread", count: 0) # => "No unread notifications"
t("notifications.unread", count: 1) # => "1 unread notification"
t("notifications.unread", count: 12) # => "12 unread notifications"La CLI traduit chaque variante de pluriel sur place. Si votre langue cible nécessite plus de catégories que le one/other de l’anglais, définissez-les dans votre fichier source en.yml.
Automatiser en CI#
La GitHub App Lingo.dev traduit à chaque push et pull request, côté serveur — sans runner et sans clé API stockée dans votre dépôt. Elle résout le moteur à partir du .lingo/config.json versionné ; orgId et engineId doivent donc être présents dans le fichier que vous commitez.
Si vous préférez exécuter le CLI dans votre propre pipeline — GitHub Actions, GitLab CI, Bitbucket Pipelines — consultez les Workflows CI/CD. Fournissez LINGO_API_KEY comme secret et appelez lingo push --wait comme n’importe quelle autre étape de build.
Vérifier avant le déploiement#
Utilisez lingo check comme garde-fou de déploiement pour éviter qu’un contenu non traduit n’arrive en production. La commande renvoie un code de sortie non nul si certaines entrées doivent encore être traduites, et n’écrit rien :
lingo checkAjoutez-le comme étape CI distincte avant la précompilation des assets ou la construction du conteneur :
- name: Verify translations
run: lingo check
- name: Precompile assets
run: bundle exec rails assets:precompile