Set up Rails i18n and localization for your app. Organize translation files, switch between languages, and automate translations with AI.
To show how Rails internationalization works in practice, we built a small Ruby on Rails demo application. The app displays the current server time with a refresh button and includes a simple language switcher.
You can apply these concepts to your existing Rails application, or create a new Rails app with rails new YourAppName and build along with us.
For the full source code, see GitHub: Rails i18n Demo App
Configuring Rails Internationalization (i18n)
Rails internationalization requires three configuration steps: setting available locales, adding locale to your URLs, and making Rails load the correct locale for each request. You’ll also want to install the rails-i18n gem, which provides locale data like translated month names, pluralization rules, and default Rails error messages.
Set Available Locales
First, in config/application.rb, tell Rails which languages the app should support and set a default locale:
config.i18n.default_locale = :en
config.i18n.available_locales = [:en, :es, :de]This configuration tells Rails:
- English (
:en) is the default language - The app supports English, Spanish, and German
- Users can switch between these three languages
You can add any language codes you need.
Add Locale to URLs
Next, add the locale to the URL so each language has its own path, like /en/time or /es/time:
# config/routes.rb
scope "/:locale" do
get '/time', to: 'home#index', as: :time_display
endThis creates locale-specific URLs for your Rails application. Users can access different language versions by changing the locale parameter in the URL.
Configure Locale Switching
Finally, in app/controllers/application_controller.rb, make sure Rails loads the right locale from the URL and includes it in all links:
class ApplicationController < ActionController::Base
around_action :switch_locale
def switch_locale(&action)
locale = params[:locale] || I18n.default_locale
I18n.with_locale(locale, &action)
end
def default_url_options
{ locale: I18n.locale }
end
endThis code:
- Reads the locale from the URL parameter
- Falls back to the default locale if no parameter is present
- Ensures all generated URLs include the current locale
Install the rails-i18n Gem
The rails-i18n gem provides locale data for multiple languages, including translated month names, pluralization rules, and default Rails error messages. This saves you from translating these standard strings yourself.
Add it to your Gemfile:
gem 'rails-i18n'
Then run:
bundle install
Rails is now fully configured for internationalization.
Adding a Language Switcher
Now that your Rails app supports multiple locales, your global users need a way to switch between languages. Rails makes this simple because the default_url_options method in ApplicationController automatically includes the current locale in all generated URLs.
This means you can create a language switcher that only updates the locale parameter while keeping the user on the same page.
Create the Language Switcher UI
Add the language switcher to your layout file. In our demo app, the switcher is added in app/views/layouts/application.html.erb:
<nav>
<%= link_to "English", url_for(locale: :en) %> |
<%= link_to "Español", url_for(locale: :es) %> |
<%= link_to "Deutsch", url_for(locale: :de) %>
</nav>Each link uses url_for(locale: :locale_code) to generate a URL with the specified locale. When a user clicks a link:
- The
switch_localemethod in ApplicationController detects the change - Rails renders the page in the new language
Because you defined default_url_options in ApplicationController, Rails automatically includes the locale in all generated links. This means the locale persists as users navigate through your app. You don’t need to manually add locale parameters to every link.
Style Your Language Switcher
You can style the language switcher in app/assets/stylesheets/application.css to match your design. The demo app includes basic styling to make the switcher visible and user-friendly.
Setting Up Translation Keys in Rails Views
Before you can translate your app, all user-facing text needs to live in Rails i18n files instead of being hard-coded. Rails provides the t helper method (short for translate) to look up and display translated strings.
Replace Hard-Coded Text with Translation Keys
Rails uses the built-in I18n.t (or simply t) helper to look up strings from your translation files.
Correct (using translation keys):
<h1><%= t(:hello) %></h1>
<p><%= t(:current_time, time: @time) %></p>
<button id="click-me"><%= t(:refresh) %></button>
Incorrect (hard-coded text):
<h1>Hello</h1>
<p>Current time: <%= @time %></p>
<button>Refresh</button>Hard-coded text won’t appear in your YAML file, which means it can’t be translated later. Always use translation keys for any user-facing text.
Using Interpolation in Translations
Notice the time: @time parameter in the example above? This is called interpolation. It lets you insert dynamic values into your translations. The translation string in your YAML file uses %{time} as a placeholder:
current_time: "Current time: %{time}"When you call the t helper with time: @time, Rails replaces %{time} with the actual value. You can use any variable names you want – just make sure they match between the YAML file and your view.
Lazy Lookup for Cleaner Code
When your translation keys are organized to match your view folder structure, Rails lets you use a shortcut called lazy lookup. Instead of writing the full key path, use a leading dot:
<!-- Instead of this: -->
<%= t('home.index.hello') %>
<!-- You can write this: -->
<%= t('.hello') %>
Rails looks at which view file you’re in (home/index.html.erb) and automatically adds home.index. in front of your key. This keeps your code cleaner and makes it easier to move views around. If you rename or relocate a view, the lazy lookup paths update automatically.
Creating and Organizing Translation Files
Now that you’ve added translation keys to your views, you need to define those keys in a YAML file. Rails stores these definitions in the config/locales/ directory.
Add Keys to Your English File
Rails translation files use a simple key-value structure. Open config/locales/en.yml and add the keys you referenced in your views:
# config/locales/en.yml
en:
hello: "Hello"
current_time: "Current time: %{time}"
refresh: "Refresh"
In this Rails i18n structure:
enis the language code (English)hello,current_time, andrefreshare translation keys- The strings on the right are the actual text
%{time}is a placeholder for dynamic content (interpolation)
This is your source file. It contains all the text in your default language. Later in this tutorial, you’ll use PTC to automatically generate Spanish, German, and other language versions.
Organizing Keys with Nesting
To use lazy lookup (the .hello shortcut from the previous section), organize your keys to match your view folder structure:
en:
home:
index:
hello: "Hello"
current_time: "Current time: %{time}"
refresh: "Refresh"
This groups all translations for app/views/home/index.html.erb under home.index. Now you can use t('.hello') in that view instead of t('home.index.hello').
As your Rails application grows, you can also create namespaces for shared translations (like shared.navigation for menus) to keep things organized, but this is optional.
Add JavaScript Strings to YAML
Your Rails views are now internationalized, but what about JavaScript? Rails doesn’t automatically extract text from JavaScript files. If your application has client-side text like alerts, tooltips, or confirmation messages, you need to add them to your English translation file.
For example, to translate a confirmation popup, add the string to config/locales/en.yml:
# config/locales/en.yml
en:
confirm: "Are you sure?"At this point, your en.yml file contains all the text that appears in your app – both in Rails views and in JavaScript.
When you use PTC to translate your application (covered in the next section), these JavaScript strings will be translated along with everything else.
Translating Rails Apps with PTC
Now, it’s time to get translations for all your application’s text stored in the YAML file. Instead of manually translating hundreds of strings, you can use Private Translation Cloud (PTC).
PTC uses AI to automatically translate your Rails YAML files while preserving their structure and keys. You can start with the free trial, which lets you translate 20,000 words into any two languages, no credit card required. After that, pay only for what you translate (no subscription fees).
Once you upload and translate your first file, you can move to a continuous localization workflow:
- GitHub, GitLab, or Bitbucket Integration – Connect your repository. PTC finds your YAML files, translates them, and sends back a merge request with the new language files.
- API/CLI – Integrate into your CI/CD pipeline using the PTC API. View a sample Rails repository with GitHub Actions.
For this guide, we used Git integration for a fully automated workflow. PTC connected to our repository, created translated versions like es.yml and de.yml, and opened a pull request with the new files in the same folder.
Once you merge the pull request, Rails automatically loads the translations. When users switch languages, your app displays the correct text.
For a complete setup walkthrough, see our Getting Started Guide.
Converting Translations for JavaScript
Now that you have translated YAML files from PTC, you need to make those translations available to JavaScript. JavaScript can’t read YAML files directly – it needs JSON files.
Installing i18n-js
The i18n-js gem converts your YAML translation files into JSON format that browsers can read.
Add the gem to your Gemfile:
gem 'i18n-js'
Then run:
bundle install
After installation, initialize the gem:
i18n init
This generates a configuration file. Update it to export translations to public/locales.json:
# config/i18n.rb
require "i18n-js"
I18n::JS.config do |config|
config.export_i18n_js = false
config.translations_path = "public/locales.json"
end
Exporting Translations to JSON
Run the export command to generate the JSON file:
i18n export
This reads all your YAML files (en.yml, es.yml, de.yml) and creates public/locales.json with all translations in a format JavaScript can use.
Loading Translations in JavaScript
Rails 7+ includes Importmap for managing JavaScript dependencies without bundlers.
Pin the i18n-js library in config/importmap.rb:
# config/importmap.rb
pin "i18n-js", to: "https://esm.sh/i18n-js@latest/dist/import/index.js"
pin "load_locale", to: "load_locale.js"
Create a loader function in app/javascript/load_locale.js:
// app/javascript/load_locale.js
export async function loadLocale() {
const response = await fetch('/locales.json');
const data = await response.json();
return data;
}
This function fetches the JSON file containing all your translations.
Using Translations in JavaScript
Pass the current locale to JavaScript by adding it to the <body> tag in your layout:
<!-- app/views/layouts/application.html.erb -->
<body data-locale="<%= I18n.locale %>">Then import i18n-js and use translations in your JavaScript:
// app/javascript/application.js
import { I18n } from "i18n-js"
import { loadLocale } from "./load_locale"
document.addEventListener('turbo:load', async () => {
// Load all translations
const translations = await loadLocale()
const i18n = new I18n(translations)
// Set the locale from the body tag
i18n.locale = document.body.dataset['locale']
// Use translations in your JavaScript
if (confirm(i18n.t('confirm'))) {
// User clicked OK
}
})
The i18n.t() method works like Rails’ t helper. When users switch languages, JavaScript automatically uses the correct translations from the data-locale attribute.
Other Rails i18n Features
This guide covered the core Rails internationalization workflow: setting up i18n, organizing translations, and automating the translation process. Rails i18n includes additional features you might need as your application grows.
How do I localize dates and times?
Rails provides the l helper (short for localize) to format dates and times according to each locale’s conventions:
<%= l Time.now, format: :long %>The rails-i18n gem you installed provides default date and time formats for many languages, including translated month names and locale-specific formatting. You can also define custom formats in your YAML files.
How do I localize numbers and currency?
Rails includes helpers for formatting numbers and currency according to locale:
<%= number_to_currency(100, locale: :es) %> <!-- €100.00 -->
<%= number_with_delimiter(1000000) %> <!-- 1,000,000 -->
These helpers respect locale-specific conventions for decimal separators, thousand delimiters, and currency symbols.
Can I create different view files for each language?
Yes. For pages with significantly different content per locale, you can create separate view files:
app/views/pages/
about.html.erb <!-- Default -->
about.es.html.erb <!-- Spanish version -->
about.de.html.erb <!-- German version -->
Rails automatically renders the appropriate view based on the current locale.
Ready to translate your Rails app?
Start your free 30-day trial with PTC and get 20,000 words translated into two languages—no credit card required.
