Lingo.dev CLI 可通过已配置的 本地化引擎 翻译 Rails 的 config/locales YAML 文件。Rails 原生内置 i18n API——应用中所有可翻译文本都存放在按 locale 划分的 YAML 文件里。Lingo.dev 能无缝接入你现有的流程,无需额外引入运行时依赖。
本指南将带你完整走通 Rails 应用本地化流程:配置 CLI、组织按 locale 划分的 YAML 文件、在请求时切换 locale,以及用 GitHub Actions 自动化翻译。
演示仓库
克隆或 fork lingodotdev/ruby-on-rails-localization-example,即可跟着一起操作。这个仓库包含一个可运行的 Rails 应用、config/locales YAML 文件、Lingo.dev CLI 配置以及 GitHub Actions 工作流。
Rails 本地化如何运作#
Rails 会从 config/locales/ 下的 YAML 文件中读取翻译。每个文件都以 locale code 作为根键,内部则包含嵌套键,对应你在代码里通过 I18n.t 使用的查找路径。
| 层级 | 存放内容 | 示例文件 |
|---|---|---|
| UI 文案 | 按钮、标签、flash 消息 | config/locales/en.yml |
| 邮件文案 | ActionMailer 的主题和正文 | config/locales/mailers.en.yml |
| 模型错误 | 校验消息和属性名 | config/locales/activerecord.en.yml |
每个 Rails YAML 文件的第一个键都是 locale code 本身——en:、es:、fr:。CLI 的 yaml-root-key bucket 能识别这种结构:它会先去掉 locale 前缀,再把内容发送到本地化引擎;随后以目标 locale code 作为新的根键,写出对应的平行文件。嵌套键、%{name} 插值标记,以及 CLDR 复数类别(zero/one/two/few/many/other)都会被完整保留。
前提条件#
创建本地化引擎
确认 Ruby 和 Rails 版本
本指南适用于 Rails 7.2 及以上版本,而它要求 Ruby 3.1 及以上。先检查你的版本:
ruby -v
rails -v确认 Node.js 版本
CLI 需要 Node.js 18 或更高版本:
node -v设置 Rails i18n
本指南默认你的应用已经将翻译存放在 config/locales/*.yml 中。如果你的视图或控制器里还有硬编码字符串,请先将它们提取为 t() 调用。比如,把下面这段替换成:
<h1>Welcome</h1>替换为:
<h1><%= t(".welcome") %></h1>然后把对应的键添加到 config/locales/en.yml 中。完整迁移步骤可参考 Rails 的 internationalization guide。
组织翻译文件#
Rails 会自动加载 config/locales/ 下所有匹配的 *.yml 文件。建议将源 locale 和各个目标语言文件放在同一目录中,让这个目录成为唯一可信来源:
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 文件,并声明一个指向 locale 文件的 yaml-root-key bucket:
{
"$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] 占位符会解析为每个已配置的 locale code。使用 source: "en" 时,CLI 会读取 config/locales/en.yml,并把翻译后的文件写入 config/locales/es.yml、config/locales/fr.yml 和 config/locales/de.yml。
Rails 可以与 en.yml 一起正常加载 mailers.en.yml、pages.en.yml 等文件。只需把更多模式添加到 yaml-root-key bucket 的 include 数组中:
{
"buckets": {
"yaml-root-key": {
"include": [
"config/locales/[locale].yml",
"config/locales/mailers.[locale].yml",
"config/locales/pages.[locale].yml"
]
}
}
}为多个 locale 配置 Rails#
告诉 Rails 哪些 locale 可用,以及默认使用哪个。在 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 请求头选择当前请求的 locale:
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在视图中渲染翻译#
在 ERB 模板中使用 t 和 l 辅助方法。键名前面的点会相对于当前视图路径解析,让翻译键与使用它们的模板就近放置:
<h1><%= t(".welcome", name: @user_name) %></h1>
<p><%= t("notifications.unread", count: @unread_count) %></p>
<%= link_to t(".cta"), signup_path, class: "btn-primary" %>在布局中添加一个语言切换器:
<nav>
<% I18n.available_locales.each do |locale| %>
<%= link_to locale.upcase, url_for(locale: locale) %>
<% end %>
</nav>在本地执行翻译#
设置好 API 密钥后,运行 CLI:
export LINGO_API_KEY="your-api-key"
npx lingo.dev@latest runCLI 会读取所有匹配 bucket 模式的文件,借助 lockfile 识别尚未翻译的条目,通过本地化引擎翻译增量内容,并将结果写入各个目标 locale 文件。locale 根键、嵌套命名空间、%{name} 风格的插值标记以及复数类别都会被保留——变化的只有可翻译文本。
开发过程中,如果只想处理某个特定 locale:
npx lingo.dev@latest run --target-locale es首次运行翻译后,请重启 Rails 服务器,以便加载新的 YAML 文件:
bin/rails server访问 /es 查看西班牙语效果。
复数形式#
Rails 使用 CLDR 复数类别——zero、one、two、few、many、other。给 I18n.t 传入 count: 参数后,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 会在原位翻译每一种复数变体。如果目标 locale 需要比英语的 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 }}在 GitHub 仓库的 Settings > Secrets and variables > Actions 中,将你的 API 密钥保存为 LINGODOTDEV_API_KEY。
部署前验证#
使用 --frozen 标志作为部署门禁,确保不会把未翻译内容发布到生产环境。如果仍有条目待翻译,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