CLI Lingo.dev переводит Rails YAML-файлы config/locales через настроенный движок локализации. В Rails i18n API встроен из коробки, поэтому весь переводимый текст приложения хранится в отдельных YAML-файлах для каждой локали. Lingo.dev легко встраивается в существующий пайплайн и не добавляет зависимостей во время выполнения.
В этом руководстве разберём локализацию Rails-приложения от начала до конца: настроим CLI, организуем YAML-файлы по локалям, включим переключение локалей на уровне запроса и автоматизируем переводы через GitHub Actions.
Демо-репозиторий
Клонируйте или форкните lingodotdev/ruby-on-rails-localization-example, чтобы повторять всё по ходу. В репозитории есть рабочее Rails-приложение с YAML-файлами config/locales, конфигурация CLI Lingo.dev и рабочий процесс GitHub Actions.
Как устроена локализация в Rails#
Rails читает переводы из YAML-файлов в каталоге config/locales/. В корне каждого файла находится ключ с кодом локали, а внутри — вложенные ключи, повторяющие пути поиска, которые ваш код использует через I18n.t.
| Слой | Что здесь хранится | Пример файла |
|---|---|---|
| Строки интерфейса | Кнопки, метки, flash-сообщения | config/locales/en.yml |
| Тексты писем | Темы и содержимое для ActionMailer | config/locales/mailers.en.yml |
| Ошибки моделей | Сообщения валидации и названия атрибутов | config/locales/activerecord.en.yml |
Первый ключ в каждом Rails YAML-файле — это код локали: en:, es:, fr:. Bucket yaml-root-key в CLI понимает такую структуру: перед отправкой контента в ваш движок локализации он убирает префикс локали, а затем создаёт параллельный файл, где корневым ключом становится код целевой локали. Вложенные ключи, токены интерполяции %{name} и категории множественного числа CLDR (zero/one/two/few/many/other) при этом сохраняются.
Что понадобится#
Создайте движок локализации
При каждом запуске CLI контент проходит через движок локализации — конфигурацию, которая определяет, какая модель LLM, glossary, тональность бренда и instructions будут использоваться. Создайте его в Lingo.dev dashboard и сгенерируйте API key.
Проверьте Ruby и Rails
Это руководство рассчитано на Rails 7.2 и выше, а для него нужен Ruby 3.1 или новее. Проверьте свои версии:
ruby -v
rails -vПроверьте Node.js
Для CLI нужен Node.js 18 или новее:
node -vНастройте i18n в Rails
Предполагается, что ваше приложение уже хранит переводы в config/locales/*.yml. Если в представлениях или контроллерах ещё остались захардкоженные строки, сначала вынесите их в вызовы t(). Например, замените:
<h1>Welcome</h1>на:
<h1><%= t(".welcome") %></h1>затем добавьте соответствующий ключ в config/locales/en.yml. Полный процесс миграции описан в руководстве Rails по интернационализации.
Организуйте файлы переводов#
Rails автоматически загружает каждый файл *.yml из каталога config/locales/. Храните исходную локаль рядом с переведёнными версиями, чтобы каталог оставался единым источником истины:
config/locales/
en.yml # Source locale
es.yml # Generated by Lingo.dev
fr.yml
de.ymlТипичный файл en.yml сочетает обычные строки, вложенные пространства имён, интерполяцию %{name} и формы множественного числа:
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"Настройте CLI#
Создайте файл i18n.json в корне проекта. Объявите bucket yaml-root-key, который указывает на файлы локалей:
{
"$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"]
}
}
}Плейсхолдер [locale] разворачивается в каждый настроенный код локали. С source: "en" CLI читает config/locales/en.yml и записывает переведённые файлы в config/locales/es.yml, config/locales/fr.yml и config/locales/de.yml.
Rails спокойно загружает mailers.en.yml, pages.en.yml и другие файлы рядом с en.yml. Добавьте дополнительные шаблоны в массив include вашего bucket yaml-root-key:
{
"buckets": {
"yaml-root-key": {
"include": [
"config/locales/[locale].yml",
"config/locales/mailers.[locale].yml",
"config/locales/pages.[locale].yml"
]
}
}
}Настройте Rails для нескольких локалей#
Укажите Rails, какие локали доступны и какая должна использоваться по умолчанию. В 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
endВыбирайте локаль запроса в ApplicationController по параметру URL или заголовку 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
endВыводите переводы в представлениях#
Используйте хелперы t и l в ERB-шаблонах. Точка в начале ключа вычисляется относительно текущего пути представления, поэтому ключи переводов остаются рядом с шаблонами, где они используются:
<h1><%= t(".welcome", name: @user_name) %></h1>
<p><%= t("notifications.unread", count: @unread_count) %></p>
<%= link_to t(".cta"), signup_path, class: "btn-primary" %>Добавьте в layout переключатель локали:
<nav>
<% I18n.available_locales.each do |locale| %>
<%= link_to locale.upcase, url_for(locale: locale) %>
<% end %>
</nav>Переводите локально#
Укажите API key и запустите CLI:
export LINGO_API_KEY="your-api-key"
npx lingo.dev@latest runCLI читает все файлы, которые подходят под шаблоны вашего bucket, находит непереведённые записи с помощью lockfile, отправляет дельту в ваш движок локализации и записывает результат в файл для каждой целевой локали. Корневой ключ локали, вложенные пространства имён, токены интерполяции в формате %{name} и категории множественного числа сохраняются — меняется только переводимый текст.
Чтобы перевести только одну конкретную локаль во время разработки:
npx lingo.dev@latest run --target-locale esПосле первого запуска перевода перезапустите сервер Rails, чтобы он подхватил новые YAML-файлы:
bin/rails serverОткройте /es, чтобы посмотреть испанский вариант.
Множественное число#
Rails использует категории множественного числа CLDR — zero, one, two, few, many, other. Передайте аргумент count: в I18n.t, и Rails сам выберет подходящий ключ:
t("notifications.unread", count: 0) # => "No unread notifications"
t("notifications.unread", count: 1) # => "1 unread notification"
t("notifications.unread", count: 12) # => "12 unread notifications"CLI переводит каждый вариант множественного числа прямо на месте. Если целевой локали нужно больше категорий, чем английские one/other, добавьте их в исходный en.yml.
Автоматизируйте через GitHub Actions#
Добавьте файл рабочего процесса в .github/workflows/translate.yml, чтобы переводы запускались при каждом push:
Переводы коммитятся напрямую в main — без лишнего трения, идеально для небольших команд:
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 }}Сохраните API key как LINGODOTDEV_API_KEY в разделе Settings > Secrets and variables > Actions вашего GitHub-репозитория.
Проверяйте перед деплоем#
Используйте флаг --frozen как стоп-сигнал перед деплоем, чтобы в production не уходил непереведённый контент. Если какие-то записи требуют перевода, CLI завершится с ненулевым статусом:
npx lingo.dev@latest run --frozenДобавьте это как отдельный шаг CI перед предварительной компиляцией ассетов или сборкой контейнера:
- name: Verify translations
run: npx lingo.dev@latest run --frozen
- name: Precompile assets
run: bundle exec rails assets:precompile