La CLI de Lingo.dev traduce archivos YAML de Rails config/locales mediante un motor de localización configurado. Rails trae la API de i18n integrada de forma nativa: el texto traducible de tu app vive en archivos YAML por idioma. Lingo.dev encaja en tu pipeline actual sin añadir dependencias en tiempo de ejecución.
Esta guía te acompaña paso a paso para localizar una app de Rails de punta a punta: configurar la CLI, organizar archivos YAML por idioma, cambiar el idioma en tiempo de solicitud y automatizar traducciones con GitHub Actions.
Repositorio de demo
Clona o haz fork de lingodotdev/ruby-on-rails-localization-example para seguir el recorrido. El repositorio incluye una app de Rails funcional con archivos YAML config/locales, una configuración de la CLI de Lingo.dev y un flujo de trabajo de GitHub Actions.
Cómo funciona la localización en Rails#
Rails lee las traducciones desde los archivos YAML en config/locales/. Cada archivo usa un código de idioma como clave raíz y contiene claves anidadas que reflejan las rutas de búsqueda que tu código usa con I18n.t.
| Capa | Qué contiene | Archivo de ejemplo |
|---|---|---|
| Textos de UI | Botones, etiquetas, mensajes flash | config/locales/en.yml |
| Copys de correo | Asuntos y cuerpos para ActionMailer | config/locales/mailers.en.yml |
| Errores del modelo | Mensajes de validación y nombres de atributos | config/locales/activerecord.en.yml |
La primera clave de cada archivo YAML de Rails es el propio código de idioma: en:, es:, fr:. El bucket yaml-root-key de la CLI entiende esta estructura: elimina el prefijo del idioma antes de enviar el contenido a tu motor de localización y luego escribe un archivo paralelo con el código del idioma de destino como nueva clave raíz. Se conservan las claves anidadas, los tokens de interpolación %{name} y las categorías de plural de CLDR (zero/one/two/few/many/other).
Requisitos previos#
Crea un motor de localización
Cada ejecución de la CLI envía el contenido a través de un motor de localización: la configuración que define qué modelo LLM, glosario, voz de marca e instrucciones se aplican. Crea uno en el dashboard de Lingo.dev y genera una API key.
Verifica Ruby y Rails
Esta guía está pensada para Rails 7.2 o superior, que requiere Ruby 3.1 o superior. Revisa tus versiones:
ruby -v
rails -vVerifica Node.js
La CLI requiere Node.js 18 o superior:
node -vConfigura i18n en Rails
Esta guía asume que tu app ya guarda las traducciones en config/locales/*.yml. Si tienes cadenas hardcodeadas en vistas o controladores, primero extráelas a llamadas de t(). Por ejemplo, reemplaza:
<h1>Welcome</h1>por:
<h1><%= t(".welcome") %></h1>y luego agrega la clave correspondiente en config/locales/en.yml. Consulta la guía de internacionalización de Rails para ver todos los pasos de migración.
Organiza los archivos de traducción#
Rails carga automáticamente cada archivo *.yml dentro de config/locales/. Mantén el idioma de origen junto a sus versiones traducidas para que el directorio funcione como fuente única de verdad:
config/locales/
en.yml # Source locale
es.yml # Generated by Lingo.dev
fr.yml
de.ymlUn archivo en.yml típico combina cadenas simples, espacios de nombres anidados, interpolación %{name} y pluralización:
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"Configura la CLI#
Crea un archivo i18n.json en la raíz de tu proyecto. Declara un bucket yaml-root-key que apunte a los archivos de idioma:
{
"$schema": "https://lingo.dev/schema/i18n.json",
"version": "1.15",
"locale": {
"source": "en",
"targets": ["es", "fr", "de"]
},
"buckets": {
"yaml-root-key": {
"include": ["config/locales/[locale].yml"]
}
}
}El placeholder [locale] se resuelve en cada código de idioma configurado. Con source: "en", la CLI lee config/locales/en.yml y escribe los archivos traducidos en config/locales/es.yml, config/locales/fr.yml y config/locales/de.yml.
Rails carga sin problema mailers.en.yml, pages.en.yml y así sucesivamente junto con en.yml. Agrega patrones adicionales al arreglo include de tu bucket yaml-root-key:
{
"buckets": {
"yaml-root-key": {
"include": [
"config/locales/[locale].yml",
"config/locales/mailers.[locale].yml",
"config/locales/pages.[locale].yml"
]
}
}
}Configura Rails para varios idiomas#
Indícale a Rails qué idiomas están disponibles y cuál debe usar por defecto. En 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
endSelecciona el idioma de la solicitud en ApplicationController a partir de un parámetro en la URL o del encabezado 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
endRenderiza traducciones en las vistas#
Usa los helpers t y l en las plantillas ERB. Un punto al inicio de la clave se resuelve en función de la ruta de la vista actual, lo que mantiene las claves de traducción junto a las plantillas que las usan:
<h1><%= t(".welcome", name: @user_name) %></h1>
<p><%= t("notifications.unread", count: @unread_count) %></p>
<%= link_to t(".cta"), signup_path, class: "btn-primary" %>Agrega un selector de idioma a tu layout:
<nav>
<% I18n.available_locales.each do |locale| %>
<%= link_to locale.upcase, url_for(locale: locale) %>
<% end %>
</nav>Traduce localmente#
Configura tu API key y ejecuta la CLI:
export LINGO_API_KEY="your-api-key"
npx lingo.dev@latest runLa CLI lee cada archivo que coincide con los patrones de tu bucket, identifica las entradas sin traducir con el lockfile, traduce el delta a través de tu motor de localización y escribe los resultados en cada archivo del idioma de destino. Se conservan la clave raíz del idioma, los espacios de nombres anidados, los tokens de interpolación estilo %{name} y las categorías de plural: solo cambia el texto traducible.
Para apuntar a un idioma específico durante el desarrollo:
npx lingo.dev@latest run --target-locale esReinicia el servidor de Rails después de la primera ejecución de traducción para que se carguen los nuevos archivos YAML:
bin/rails serverVisita /es para ver la versión en español.
Plurales#
Rails usa categorías de plural de CLDR: zero, one, two, few, many, other. Pasa un argumento count: a I18n.t y Rails elegirá la clave correspondiente:
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 traduce cada variante plural en su lugar. Si tu idioma de destino necesita más categorías que one/other en inglés, defínelas en tu archivo fuente en.yml.
Automatiza con GitHub Actions#
Agrega un archivo de flujo de trabajo en .github/workflows/translate.yml para traducir en cada push:
Las traducciones se confirman directamente en main: cero fricción, ideal para equipos pequeños:
name: Translate
on:
push:
branches: [main]
permissions:
contents: write
jobs:
translate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Lingo.dev
uses: lingodotdev/lingo.dev@main
with:
api-key: ${{ secrets.LINGODOTDEV_API_KEY }}Guarda tu API key como LINGODOTDEV_API_KEY en Settings > Secrets and variables > Actions de tu repositorio de GitHub.
Verifica antes de desplegar#
Usa la bandera --frozen como gate de despliegue para asegurarte de que no llegue contenido sin traducir a producción. La CLI termina con un estado distinto de cero si alguna entrada necesita traducción:
npx lingo.dev@latest run --frozenAgrega esto como un paso de CI independiente antes de la precompilación de assets o de la construcción del contenedor:
- name: Verify translations
run: npx lingo.dev@latest run --frozen
- name: Precompile assets
run: bundle exec rails assets:precompile