I18n Best Practices

8 min read


Rails I18n best practices

Rails framework give us the localization features via rails-i18n gem. While the Rails guides describe the technical details on how to use it, it doesn’t prescribe any guidelines on how to name and sort our localization strings. Here’s our take on how a project’s I18n strings should be sorted out.


Store the user preferences; they are liketly not to change.

Google gets it wrong: switches currency to THai hwen I’m in Thailand. Not good.

For new users: auto-detect the language

That’s easy to do from the browser.

Ruby on Rails checklist below

Quote our strings

As we know, the YAML format is liberal and quotation marks aren’t needed, the consistency is always welcome. It also makes it less ambiguous for non-developers who may edit the content of the YAML file.

			header_page: "All events"

Group by controller and action

Place our page’s string under their respective controller and action names. This allow to we take advantage of Rails’s shortcuts.

# looks up "events.index.header_page", if we're on the events#index view

# We will use it with the lazy lookup


More detail: Lazy lookup (guides.rubyonrails.org)

Group strings for partial

The same concept should apply for the partials. We can also use the t(".back_to_home") shortcut inside partial views.

			back_to_home: "Back to home"

Avoid over nesting

Prefer to nest with just one level. Overly-complicates nesting can seem very arbitrary and confusing.

# Bad
				delivered: "Delivered"
				read: "Read"
# Good
		delivered: "Delivered"
		read: "Read"

**Familiarize ourself with defaults**

The default locale file is available under rails-i18n. We can config in application.rb file:

config.i18n.default_locale = :en

Don’t forget config the available locales

In the application.rb we can config:

config.i18n.available_locales = %i[en vi]

Don’t forget set up fallbacks for missing translations

When using Rails I18n, it is important to set up fallbacks for missing translations because they provide a way to handle cases where the translation key does not exist in the current locale. This ensures that the application will still be able to display something meaningful instead of an error message or blank space.

To set up fallbacks, we must config in the application.rb file:

config.i18n.fallbacks = [:en]

This would mean that if a translation was missing from the English locale, then the Vietnamese locale would be checked next. If no translation was found there either, then the default value (The original text) would be displayed.

Keep our translation keys consistent

Keeping translation keys consistent helps to ensure that all translations are up-to-date and accurate. This is because when a key changes, the corresponding translation must also be updated in order for it to remain valid. Additionally, having consistent keys makes it easier to find and update existing translations as well as add new ones.

To keep our translation keys consistent, we should use a naming convention such as using underscores or hyphens to separate words, avoiding special characters, and keeping the names short but descriptive. We should also avoid reusing the same key for different translations, as this can lead to confusion and errors.

Finally, make sure to document any changes made to the keys so that everyone involved in the project knows what has been changed.

**Use pluralization rules in our translations**

Using pluralization rules helps to ensure that our translations are accurate and consistent. It allows we to specify different forms of a word depending on the number of items being referred to.

To use pluralization rules in Rails I18n, we need to define them in the locale file.

The syntax looks like this: key_name: { one: 'singular', other: 'plural' }.

Once defined, we can access these forms using the t helper method with the count option set to the appropriate value.

For example, <%= t('key_name', count: @items.size) %> will output either “singular” or “plural” depending on the number of items.

Use localized view to reduce the length of the YAML file

We need to create views named index.LANG_CODE.html.erb, where LANG_CODE corresponds to one of the supported languages.

For example, if we have English locale and Vietnamese locale, we will create 2 views is index.en.html.erb and index.vi.html.erb.

Inside just place content for English and Vietnamese version of the site, and Rails will automatically pick the proper view based on the currently set locale.

Time and Date

Don’t use .strftime

Use Rails I18n to format timestamps. Define our time formats in time.formats. Listed below are the default time formats available in Rails.

l(Time.now, format: :short)
			default: "%a, %d %b %Y %H:%M:%S %z"
			short: "%d %b %H:%M"

The above rule will same with dates

The I18n.l method also supports dates. Also, when creating new formats, add comments on what they should look like to help our teammates along.

l(Date.today, format: :long)
      default: '%Y-%m-%d' # 2023-02-18
      long: '%B %d, %Y' # Feb 18, 2023
      short: '%b %d' # Feb 18


Use ActiveRecord errors

The Rails method full_messages looks up in predefined in our locale file. Take advantage of them. See the cheatsheet for more information.

event = Event.create
=> ["Title can't be blank", "Start time can't be blank", "End time can't be blank"]

More detail: Translations for the error messages for helper (guides.rubyonrails.org)

Use form label

There are also conventions on where f.label will look up labels.

<%= form_with(model: participation, class: "contents") do |form| %>
	<%= form.label :event_id %>
<% end %>
	    event_id: "Event"


We can use a great gem called translate_enum to readable value of enum in Rails.

More detail: Reference to translate_enum.

Till Carlos

I'm Till, a senior developer who started a software company. I explain software concepts for people in supervising / leading roles.