
Rails intègre d'emblée un framework d'internationalisation. Il ne vous en faut pas beaucoup plus pour vous lancer : l'API I18n fait partie intégrante de la pile et gère efficacement les fonctions de base : traduction des chaînes statiques, mise en forme des dates et des nombres, et gestion du pluriel selon les paramètres régionaux.
Mais « intégré » ne signifie pas « complet ». Dès que vos besoins s'étendent aux URL localisées, aux traductions JavaScript ou SEO multilingue, vous sortez du cadre de ce que Rails offre en natif. Ces lacunes nécessitent des choix architecturaux mûrement réfléchis.
Passons en revue ces deux niveaux : ce que gère l'API I18n de Rails, et les aspects que vous devrez développer vous-même – ou déléguer.
L'API I18n met à votre disposition deux méthodes principales : I18n.t pour la traduction des chaînes de caractères et I18n.l pour la localisation des dates et des nombres. Ces deux méthodes lisent les fichiers YAML ou Ruby situés dans le répertoire config/locales/, et Rails les charge automatiquement au démarrage.
Dès l'installation, cela couvre les chaînes d'interface utilisateur statiques, le formatage des dates et des nombres, ainsi que les messages de validation ActiveRecord. Pour la plupart des projets impliquant une seule langue source et une seule langue cible, cela suffit pour la mise en production.
Les lacunes apparaissent rapidement dès qu'on va plus loin.
Rails I18n ne se prononce pas sur la structure des URL : pas de routes localisées, pas de détection des sous-domaines, ni de préfixes de chemin. Il ne dispose d'aucun mécanisme permettant de traduire le contenu stocké dans la base de données, comme les noms de produits ou les articles de blog. Les fichiers JavaScript se situent entièrement en dehors du pipeline. Quant SEO multilingue, les éléments tels que les balises hreflang, les slugs traduits et les plans de site spécifiques à chaque langue nécessitent un travail distinct dont le framework ne s'occupe pas.
Il faut savoir où se trouve la limite avant de commencer à construire.
Vous pouvez commencer par le gem rails-i18n.
Rails ne fournit que des données de localisation en anglais ; par conséquent, sans celles-ci, la fonction `I18n.l(Date.today)` ne fonctionne pas dans les locales non anglophones, et les chaînes de caractères au niveau du framework, telles que les messages de validation d'Active Record, restent en anglais quelle que soit la locale actuelle.
# Gemfile
gem "rails-i18n"Configurez ensuite les paramètres par défaut de votre application :
# config/application.rb
config.i18n.default_locale = :en
config.i18n.available_locales = [:en, :fr]
config.i18n.enforce_available_locales = true
La valeur « enforce_available_locales = true » génère une erreur si votre application tente de définir une locale ne figurant pas dans cette liste. Sans cette configuration, une faute de frappe ou un paramètre d'URL mal formé peut entraîner, sans avertissement, la définition d'une locale non prise en charge en production.
Bien que Rails moderne analyse un seul niveau de profondeur, il lui arrive souvent de ne pas détecter les dossiers profondément imbriqués (comme config/locales/views/products/). L'ajout de cette ligne garantit que tous les sous-répertoires seront pris en compte à mesure que votre application se développe :
config.i18n.load_path += Dir[Rails.root.join("config", "locales", "**", « *.{rb,yml} »)]Cela vous permet de classer les fichiers de traduction par domaine :
config/locales/
fr/
models.yml
views.yml
mailers.yml
fr/
models.yml
views.yml
mailers.yml⚠️ YAML interprète les valeurs telles que « true » et « false » comme des booléens. Si vous souhaitez les utiliser sous forme de texte littéral, mettez-les explicitement entre guillemets. Les versions récentes de Ruby/YAML traitent désormais « yes » et « no » comme des chaînes de caractères, mais il reste prudent de les mettre entre guillemets pour des raisons de compatibilité :
fr:
réponses :
affirmatif : « oui »
négatif: « non »Cela permet de détecter un type de bug qui n'apparaît que lorsqu'une traduction renvoie « true » au lieu de « oui » et que votre vue n'affiche rien, ou pire encore, la chaîne « true ».
Dans les vues, t et l sont les deux fonctions d'aide que vous utiliserez sans cesse.
La fonction `l` localise les dates, les heures et les nombres en fonction des paramètres régionaux actuels. Une fois `rails-i18n` installé, les définitions de format pour plus de 100 paramètres régionaux sont fournies. Sans cet extension, l'appel de `l(Date.today)` dans un environnement non anglophone renverra généralement une chaîne « traduction manquante » ou une date non formatée, car Rails ne dispose pas des modèles de format localisés nécessaires.
<%= l(Date.today) %>
<%= l(Date.today, format: :long) %>t traduit les chaînes par clé. La forme complète est t("products.index.title"), mais dans les vues, vous pouvez utiliser la syntaxe abrégée de recherche différée :
<%= t(".title") %>Le point initial indique à Rails de résoudre la clé par rapport au chemin d'accès de la vue actuelle. Dans app/views/products/index.html.erb, t(".title") se transforme automatiquement en t("products.index.title"). Cela permet de garder des clés courtes et d'appliquer une convention de nommage cohérente sans effort supplémentaire.
For interpolation, use %{variable} in your YAML and pass the value as a keyword argument:
en:
welcome: "Hello, %{name}"<%= t(".welcome", name: current_user.name) %>Si une traduction contient du code HTML que vous jugez fiable, ajoutez « _html » à la fin du nom de la clé. Rails le marque automatiquement comme sûr, sans qu'il soit nécessaire d'appeler la méthode `html_safe`.
en:
notice_html: "Please <strong>confirm</strong> your email."La recherche différée ne fonctionne que lorsque Rails peut déduire le chemin d'accès à la vue, ce qui signifie qu'elle ne fonctionne pas dans les tâches en arrière-plan ni dans les contrôleurs API. Dans ces cas-là, utilisez systématiquement la clé complète.
Avant de commencer à écrire du code de détection de la locale, choisissez une stratégie. Il existe cinq approches principales, et celle qui vous convient dépendra de la structure de votre application et de vos besoins en matière de référencement.
Le préfixe de chemin, tel que /fr/products, est le choix le plus courant pour les applications Rails destinées au grand public. Il permet d'indiquer explicitement la langue dans chaque URL, ce qui permet aux moteurs de recherche de les indexer séparément pour chaque langue.
Les stratégies basées sur les sous-domaines et les TLD sont efficaces lorsque l'on souhaite renforcer l'identité régionale, mais elles impliquent une charge supplémentaire en termes de configuration DNS et de certificats SSL.
Les paramètres d'URL, tels que /products?locale=fr, conviennent parfaitement aux outils internes pour lesquels le référencement naturel n'a pas d'importance.
Les préférences enregistrées dans la base de données fonctionnent pour les applications authentifiées lorsque les paramètres régionaux suivent l'utilisateur, et non l'URL.
Quelle que soit la stratégie que vous choisissiez, il existe une erreur de mise en œuvre qui peut entraîner des bugs discrets en production : l'utilisation directe de `I18n.locale =`.
# Don't do this
before_action { I18n.locale = params[:locale] }I18n.locale = écrit dans Thread.current.
Si vous utilisez un serveur web Puma, celui-ci réutilise les threads d'une requête à l'autre. Si une requête ne définit pas explicitement les paramètres régionaux, elle hérite de ceux laissés par la requête précédente.
En développement local en mode monothread, ce problème ne se manifeste jamais. En production, il provoque des réponses dans une langue incorrecte, de manière intermittente, qui sont difficiles à reproduire et encore plus difficiles à tracer.
Utilisez plutôt I18n.with_locale à l'intérieur d'une action « around_action » :
around_action :switch_localed
ef switch_locale(&action)
locale = params[:locale] || I18n.default_locale
I18n.with_locale(locale, &action)
endwith_locale rétablit les paramètres régionaux précédents à la fin du bloc, quelle que soit la manière dont la requête se termine.
Définissez vos routes sous /:locale et remplacez la valeur de default_url_options afin que Rails ajoute automatiquement la locale actuelle au début de chaque aide-URL :
# config/routes.rb
scope "/:locale" do
resources :products
root "home#index"
end
# app/controllers/application_controller.rb
around_action :switch_locale
def switch_locale(&action)
locale = params[:locale]
# Fallback to default if the param is missing or unsupported
valid_locale = I18n.available_locales.map(&:to_s).include?(locale) ? locale : I18n.default_locale
I18n.with_locale(valid_locale, &action)
end
def default_url_options
{ locale: I18n.locale }
end
Lorsque l'option `default_url_options` est définie, le chemin `products_path` s'affiche automatiquement sous la forme ` /fr/products` lorsque la locale actuelle est :fr.
Si vous souhaitez que la locale par défaut omette le préfixe, rendez le segment facultatif en lui attribuant la portée « (:locale) ». Cela permet d'afficher /products pour l'anglais et /fr/products pour le français, mais crée une ambiguïté. Un chemin tel que /about pourrait correspondre soit à une locale manquante, soit à un contrôleur nommé about, selon l'ordre de vos routes.
Pour la détection des sous-domaines, lisez la valeur de `request.subdomains.first` et vérifiez-la par rapport à `available_locales` avant de définir quoi que ce soit :
def extraire_la_langue_à_partir_du_sous-domaine
sous-domaine = request.subdomains.first
return nil si subdomain.blank? || subdomain == "www"
sous-domaine si I18n.available_locales.map(&:to_s).include?(sous-domaine)
end
La vérification « www » empêche qu'il soit traité comme une locale. Sur localhost, request.subdomains renvoie un tableau vide ; la détection des sous-domaines échoue donc en mode développement, à moins d'utiliser un serveur Pow ou d'ajouter des entrées au fichier /etc/hosts.
Il y a un élément à configurer séparément : la variable `default_url_options` définie dans `ApplicationController` ne se répercute pas sur les envois d'e-mails ni sur les tâches en arrière-plan. Définissez-la explicitement pour ces contextes :
Rails.application.routes.default_url_options = { host: "example.com", locale: :en }Les routes localisées vous fournissent la structure des URL, mais les balises hreflang, les slugs traduits et les plans de site multilingues restent entièrement à gérer manuellement une fois cette configuration effectuée.
L'intégration du proxy inverse Weglot gère automatiquement cette couche, en générant des balises hreflang et des URL spécifiques à chaque langue sans configuration supplémentaire.
La formation du pluriel en anglais est simple. Une forme pour le singulier, une autre pour tous les autres cas.
en:
messages:
one: "%{count} message"
other: "%{count} messages"
La plupart des langues ne sont pas si simples que ça.
En bulgare, par exemple, certains mots au masculin ont un pluriel régulier et un « pluriel de dénombrement » particulier. Ainsi, ден (jour) devient normalement дни (plusieurs jours), mais два дена (deux jours) lorsqu’on les compte.
bg:
cities:
one: "%{count} ден"
few: "%{count} дена"
many: "%{count} дни"
Sans rails-i18n, Rails n'a pas connaissance de ces règles. Vos traductions en bulgare généreraient des erreurs ou reviendraient à l'autre forme pour chaque nombre.
Gem intègre une logique de pluralisation pour plus de 100 langues, ce qui permet un affichage correct en bulgare, en russe, en arabe, en polonais et dans toute autre langue dont les règles de plural ne sont pas celles de l'anglais.
Les solutions de secours constituent un cas à part et sont désactivées par défaut. Sans elles, toute clé de traduction manquante entraîne l'affichage d'un message « traduction manquante » en production. C'est le genre de problème qui se glisse dans la version finale et apparaît sur les captures d'écran.
Activez les solutions de secours et définissez une destination par défaut dans le fichier config/application.rb:
config.i18n.fallbacks = [I18n.default_locale]Lorsque l'option `fallbacks = true` est activée, une clé manquante dans la locale actuelle est remplacée par celle de la locale par défaut. Pour les variantes régionales, vous pouvez définir des chaînes explicites :
config.i18n.fallbacks = { "fr-CA": :fr, "en-GB": :en }Pensez à le configurer dès le début. Il est plus difficile d'intégrer a posteriori un comportement de secours dans une application dont certaines parties sont déjà traduites et en production que de le mettre en place dès le départ.
JavaScript n'a pas accès au pipeline I18n de Rails.
Il n'existe ni aide-mémoire « t », ni chargeur YAML, ni passerelle intégrée entre vos fichiers de traduction et vos contrôleurs Stimulus. Vous devez transmettre les traductions explicitement depuis le côté serveur.
L'approche la plus simple avec Stimulus consiste à utiliser l'API des valeurs. Définissez la traduction en tant que valeur dans le contrôleur, définissez-la dans le code HTML et récupérez-la en JavaScript :
# app/views/products/index.html.erb
<div data-controller="notification"
data-notification-message-value="<%= t('.success_message') %>">
La valeur est générée côté serveur à l'aide de l'assistant t standard, elle respecte donc automatiquement les paramètres régionaux actuels. Côté JavaScript, déclarez-la comme une valeur statique et récupérez-la via l'API values :
// app/javascript/controllers/notification_controller.js
export default class extends Controller {
static values = { message: String }
show() {
alert(this.messageValue)
}
}
Chaque traduction est explicite et limitée au contrôleur qui en a besoin. Rien ne se propage dans l'espace de noms global.
Si un contrôleur a besoin de plusieurs traductions à la fois, il est plus clair de les regrouper sous forme d'attribut de données JSON plutôt que d'ajouter des définitions de valeur individuelles pour chaque chaîne :
<div data-controller="cart"
data-cart-i18n-value="<%= { add: t('.add'), remove: t('.remove') }.to_json %>">
Du côté JavaScript, déclarez i18n comme une valeur de type Object et accédez directement aux clés individuelles :
static values = { i18n: Object }
add() {
console.log(this.i18nValue.add)
}
Pour les applications où JavaScript doit pouvoir accéder à l'ensemble des données de localisation dans de nombreux contrôleurs, le gem i18n-js exporte vos fichiers YAML vers un objet JavaScript que vous pouvez interroger sous la forme I18n.t("clé").
Cela fonctionne, mais cela ajoute une étape de compilation et envoie l'intégralité de votre ensemble de traductions au client. Utilisez cette méthode lorsque les deux premières approches deviennent répétitives, et non par défaut.
Lorsque vous utilisez des Turbo Frames, si l'URL source d'un Turbo Frame ne comporte pas le préfixe de locale, la requête échouera généralement avec une erreur de routage (404), car elle ne correspondra pas au modèle de votre scope « /:locale ». Utilisez toujours des aides de chemin tenant compte de la locale pour les sources de frames.
Il ne s'agit pas d'un bug de Turbo. Il s'agit plutôt d'une erreur de routage, et l'action `around_action` de la section de détection de la locale permet de l'éviter tant que chaque URL du cadre utilise un assistant de chemin tenant compte de la locale.
Le proxy inverse Weglot adopte une approche totalement différente. Il traduit le DOM généré une fois la page chargée, ce qui permet de prendre en charge le contenu généré par Stimulus et les chaînes insérées dynamiquement sans nécessiter aucune configuration particulière.
Trois catégories échappent totalement à la gestion de Rails I18n :
Weglot traite tous ces aspects au niveau de la couche de sortie plutôt qu'au niveau du code :
Cela dit, il y a quelques limites qu'il convient de connaître avant de l'évaluer :
Toute implémentation de l'internationalisation (i18n) dans Rails revient à prendre les mêmes décisions, dans un ordre à peu près identique.
Commencez par définir votre stratégie d'URL avant d'écrire le moindre code de détection de la langue. Les segments de chemin d'accès conviennent à la plupart des applications grand public. Les sous-domaines sont utiles lorsque l'identité régionale est importante. Les préférences enregistrées dans la base de données conviennent aux applications nécessitant une authentification, où la langue suit l'utilisateur plutôt que l'URL.
Deuxièmement, déterminez comment JavaScript récupère les traductions. L'API Stimulus values couvre la plupart des cas. Les attributs JSON sont utiles lorsqu'un contrôleur a besoin de plusieurs chaînes de caractères à la fois.
Troisièmement, déterminez ce que vous souhaitez développer manuellement. Rails gère bien les chaînes de caractères statiques. La couche SEO, le flux de travail de traduction et le contenu de la base de données nécessitent tous des décisions distinctes. Vous pouvez développer chacun de ces éléments vous-même, ou confier la couche de sortie à un outil tel que Weglot consacrer votre temps de développement à l'application elle-même.
Ignorez complètement la couche de sortie. Testez Weglot pendant 14 jours et bénéficiez SEO multilingue, d'URL traduites et de la détection automatique du contenu sans toucher à votre code d'internationalisation.
La meilleure façon de comprendre la puissance de Weglot de le tester par vous-même. Essayez-le gratuitement et sans engagement.
Un site web de démonstration est disponible dans votre tableau de bord si vous n'êtes pas encore prêt à connecter votre site web.