Lingo.dev CLI는 설정된 로컬라이제이션 엔진을 통해 Rails config/locales YAML 파일을 번역합니다. Rails에는 i18n API가 기본으로 내장되어 있어, 앱에서 번역 가능한 텍스트는 로캘별 YAML 파일에 저장됩니다. Lingo.dev는 런타임 의존성을 추가하지 않고도 기존 파이프라인에 자연스럽게 녹아듭니다.
이 가이드는 Rails 앱 로컬라이제이션 전 과정을 단계별로 안내합니다. CLI 설정, 로캘별 YAML 파일 구성, 요청 시점의 로캘 전환, GitHub Actions를 활용한 번역 자동화까지 모두 다룹니다.
데모 리포지토리
같이 따라 해보려면 lingodotdev/ruby-on-rails-localization-example를 클론하거나 포크하세요. 이 리포지토리에는 config/locales YAML 파일, Lingo.dev CLI 설정, GitHub Actions 워크플로가 포함된 동작하는 Rails 앱이 들어 있습니다.
Rails 로컬라이제이션 작동 방식#
Rails는 config/locales/ 아래에 있는 YAML 파일에서 번역을 읽어옵니다. 각 파일은 루트에 로캘 코드가 키로 들어가며, 코드에서 I18n.t로 참조하는 조회 경로와 동일한 구조의 중첩 키를 포함합니다.
| 레이어 | 포함되는 내용 | 예시 파일 |
|---|---|---|
| UI 문자열 | 버튼, 레이블, 플래시 메시지 | config/locales/en.yml |
| 메일러 문구 | ActionMailer의 제목과 본문 | config/locales/mailers.en.yml |
| 모델 오류 | 유효성 검사 메시지와 속성 이름 | config/locales/activerecord.en.yml |
모든 Rails YAML 파일의 첫 번째 키는 로캘 코드 자체입니다. 즉, en:, es:, fr:처럼 시작합니다. CLI의 yaml-root-key 버킷은 이 구조를 이해합니다. 콘텐츠를 로컬라이제이션 엔진으로 보내기 전에 로캘 접두사를 제거하고, 대상 로캘 코드를 새 루트 키로 하는 병렬 파일을 생성합니다. 중첩 키, %{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 -vNode.js 확인
CLI를 사용하려면 Node.js 18 이상이 필요합니다:
node -vRails i18n 설정
이 가이드는 앱이 이미 번역 문자열을 config/locales/*.yml에 저장하고 있다고 가정합니다. 뷰나 컨트롤러에 하드코딩된 문자열이 있다면 먼저 t() 호출로 추출하세요. 예를 들어, 다음 코드를:
<h1>Welcome</h1>다음처럼 바꿉니다:
<h1><%= t(".welcome") %></h1>그런 다음 해당 키를 config/locales/en.yml에 추가하세요. 전체 마이그레이션 절차는 Rails internationalization guide를 참고하세요.
번역 파일 구성하기#
Rails는 config/locales/ 아래의 모든 *.yml 파일을 자동으로 로드합니다. 소스 로캘 파일을 번역된 파일들과 나란히 두면, 이 디렉터리가 단일 진실 공급원 역할을 하게 됩니다:
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 파일을 만드세요. 로캘 파일을 가리키는 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는 en.yml와 함께 mailers.en.yml, pages.en.yml 같은 파일도 문제없이 로드합니다. yaml-root-key 버킷의 include 배열에 패턴을 추가하세요:
{
"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
endURL 파라미터 또는 Accept-Language 헤더를 기준으로 ApplicationController에서 요청 로캘을 선택하세요:
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는 버킷 패턴과 일치하는 모든 파일을 읽고, lockfile을 기준으로 아직 번역되지 않은 항목을 찾아낸 뒤, 그 차이분만 로컬라이제이션 엔진으로 번역해 각 대상 로캘 파일에 기록합니다. 로캘 루트 키, 중첩 네임스페이스, %{name} 스타일 보간 토큰, 복수형 범주는 유지되며, 번역 가능한 텍스트만 바뀝니다.
개발 중 특정 로캘만 대상으로 하려면:
npx lingo.dev@latest run --target-locale es새 YAML 파일이 로드되도록 첫 번역 실행 후 Rails 서버를 다시 시작하세요:
bin/rails server스페인어 결과를 확인하려면 /es로 이동하세요.
복수형#
Rails는 CLDR plural categories를 사용합니다. 즉, 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는 각 복수형 변형을 해당 위치에서 그대로 번역합니다. 대상 로캘에 영어의 one/other보다 더 많은 범주가 필요하다면, 소스 en.yml에 미리 정의해두세요.
GitHub Actions로 자동화하기#
푸시할 때마다 번역이 실행되도록 .github/workflows/translate.yml에 워크플로 파일을 추가하세요:
번역 결과를 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는 0이 아닌 상태 코드로 종료됩니다:
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