The Complete Guide to Rails Internationalization (i18n)

If you‘re building web applications today, chances are you want to reach users beyond a single language or locale. Internationalization, often abbreviated as i18n, is the process of architecting your application to be capable of adapting to various languages and regions. Localization (l10n) involves actually translating your app‘s content to specific locales.

Fortunately, Rails has excellent support for i18n and makes it straightforward to localize your app. As a full-stack Rails developer who has worked on multiple global applications, I‘ve seen firsthand how proper i18n planning and execution can massively improve user experience and drive growth. In this comprehensive guide, we‘ll walk through all the key aspects of Rails i18n, from configuration to testing and deployment.

Why Internationalization Matters

Before diving into the how of Rails i18n, it‘s worth examining the why. Here are some compelling reasons and statistics that highlight the importance of internationalizing your web apps:

  • Reach new markets: Only about 20% of the world‘s population speaks English. If your app only supports English, you‘re missing out on a huge portion of the global market. [1]

  • Boost revenue: Companies that invested in translation were 1.5 times more likely to report an increase in total revenue. [2]

  • Improve user engagement: 72.4% of global consumers say they would be more likely to buy a product with information in their own language. [3]

  • Gain competitive advantage: 60.6% of the top 10 million websites do not have enough content in other languages to be considered multilingual. [4] By localizing your app, you can stand out from the competition.

Now that we understand the importance, let‘s see how to actually implement i18n in a Rails app.

Configuring Your Rails App for I18n

The first step in internationalizing a Rails app is to configure the locales you want to support. Open up config/application.rb and add the following inside your Application class:

config.i18n.available_locales = [:en, :es, :fr]
config.i18n.default_locale = :en

This specifies that our app will support English (en), Spanish (es), and French (fr), with English being the default. You can add or remove locales as needed for your project.

Next, it‘s a good idea to install the rails-i18n gem, which includes a bunch of common locale data like translated names of days and months:

gem ‘rails-i18n‘, ‘~> 7.0.0‘

Run bundle install to finish setup. With this foundational configuration in place, we‘re ready to start translating.

Organizing and Storing Translations

Rails uses YAML files to store translations, located in the config/locales directory. Each file is named after a locale, like en.yml for English.

While you can nest all your translations under the locale key, it‘s best to organize them by areas of your app. A common convention is to mirror your directory structure. For example:

# config/locales/en.yml
en:
  hello: "Hello world"
  pages:
    home:
      title: "Welcome to my site!"
    about:
      title: "About Us"      
  users:
    signup:
      submit: "Sign Up"

This keeps translations for different models, views, and controllers neatly separated. I also like splitting locale files to stay organized, like en/pages.yml, en/users.yml, etc.

Performing Translations

Now that we have some translations defined, let‘s look at how to use them in various parts of a Rails app.

Translating in Views

In any view template, you can use the translate helper, aliased as t, to fetch a translation by its key:


<%= t(‘hello‘) %>

If the specified key is missing, Rails will return a placeholder message. In development mode, it will also log a warning to help you identify missing translations.

You can also provide interpolation variables as a hash:

<%= t(‘greeting‘, name: "Alice") %>

# en.yml
en:
  greeting: "Hello, %{name}!"

Translating in Controllers and Models

Controllers and models work just like views, allowing you to fetch translations with the t helper:

class UsersController < ApplicationController
  def create
    # ...
    flash[:notice] = t(‘users.create.success‘)
  end
end

For model-related translations, like attributes and error messages, Rails uses a special activerecord scope:

# config/locales/en.yml
en:
  activerecord:
    models:
      user: User
    attributes:
      user:
        email: Email
        password: Password
    errors:
      models:
        user:
          blank: "You must provide an email address."  

With this set up, Rails will automatically use these translations in form labels, error messages, and other model-related areas.

Handling Pluralization and Interpolation

Another common i18n need is pluralizing translated content. Rails supports this via special translation keys:

# config/locales/en.yml
en:
  user_count:
    one: "There is %{count} user."
    other: "There are %{count} users."

Then in a view:

<%= t(‘user_count‘, count: @users.count) %>  

Rails will automatically select the proper translation based on the count. Note that available plural keys vary between languages. Some languages like Arabic have different rules that go beyond just "one" and "other".

Localizing Dates, Numbers, and Currencies

Properly localizing dates, numbers, and currencies is essential for a polished i18n experience.

For dates and times, you can use Rails‘ localize helper, aliased as l:

<%= l(Time.now) %>
<%= l(Time.now, format: :short) %> 

This will automatically use locale-specific date and time formats. You can customize formats in your locale files under date/formats.

For numbers and currencies, use the number_to_currency and number_with_precision view helpers:

<%= number_to_currency(9.99) %>
<%= number_with_precision(3.14159, precision: 3) %>

These will respect locale-specific delimiter and separator conventions.

Managing HTML Translations

Sometimes you need HTML markup inside your translations. For simple cases, you can use inline HTML:

en:
  welcome: "Welcome to <strong>My App</strong>!"

Then use the _html suffix when fetching the translation:

<%= t(‘welcome_html‘) %>

For complex markup, it‘s better to use Rails‘ localized views feature. Create templates like index.en.html.erb, index.es.html.erb, etc. and put language-specific content in each one. Rails will automatically use the proper view based on the current locale.

Implementing Locale Switching

To create a great multilingual experience, users need an easy way to switch between available locales.

One common approach is to include the locale in URL params and set the locale on each request:

# config/routes.rb  
scope "(:locale)", locale: /en|es/ do
  resources :books
end

# app/controllers/application_controller.rb
before_action :set_locale

def set_locale
  I18n.locale = params[:locale] || I18n.default_locale
end

def default_url_options
  { locale: I18n.locale }
end

This lets users access URLs like /en/books, /es/books, etc. to switch locales. The default_url_options ensures the locale param is included in URL helpers.

Another option is using subdomains, like en.myapp.com and es.myapp.com. You‘d configure this in your routes.rb:

scope "(:locale)", locale: /#{I18n.available_locales.join("|")}/ do
  resources :books
end

# config/initializers/locale.rb  
Rails.application.config.middleware.use(Rack::Rewrite) do
  r301 %r{.*}, "http://#{I18n.default_locale}.myapp.com$&", if: Proc.new { |rack_env|
    rack_env[‘SERVER_NAME‘] == "myapp.com"
  }
end

Whichever approach you choose, always provide a way for users to easily select their preferred locale:

<ul>
  <% I18n.available_locales.each do |locale| %>
    <li><%= link_to t("language.#{locale}"), url_for(locale: locale) %></li>
  <% end %>
</ul>

Testing and Debugging I18n

Thoroughly testing your i18n setup is crucial. You‘ll want to cover translation coverage, localized views, URL switching, and more.

Tools like i18n-tasks are great for identifying missing and unused translations. Add it to your Gemfile:

gem ‘i18n-tasks‘, ‘~> 0.9.34‘

Then run i18n-tasks health to get a report of your translation coverage and key usage.

For debugging, use the i18n-debug gem. It adds an in-browser panel to interactively inspect and modify your translations.

I also recommend writing integration tests to verify the user can switch locales and access properly translated content. Use a test like this:

# test/integration/i18n_test.rb
class I18nTest < ActionDispatch::IntegrationTest
  test "locale switching" do
    get root_path
    assert_equal :en, I18n.locale

    get root_path(locale: :es)
    assert_equal :es, I18n.locale
    assert_select ‘h1‘, ‘Bienvenido‘
  end
end

Common I18n Challenges and Pitfalls

Despite Rails‘ strong i18n support, there are still some common challenges and pitfalls to watch out for. Here are a few I‘ve encountered:

  • Dealing with user-generated content: If your app allows users to create content, you may need to provide a way for them to specify the language. Store this as a separate attribute on the model.

  • Managing long translation keys: As your app grows, translation keys can get long and unwieldy. Mitigate this by keeping keys short and using YAML anchors for shared keys.

  • Handling missing translations: By default, Rails just displays translation keys if the value is missing. In production, you may want to fallback to a default language instead. Use the fallbacks option:

# config/environments/production.rb
config.i18n.fallbacks = true  
  • Updating translations: As your app evolves, be sure to regularly audit and update translations. Use tools like i18n-tasks to identify outdated keys.

  • Translating emails: Email content should also be localized. Use the default_i18n_subject setting to translate email subjects. For email bodies, leverage mailer views and partials, just like regular views.

I18n Tools and Services

As a full-stack Rails developer who has worked on global sites, I highly recommend investing in tools and services to streamline your i18n workflow. Here are a few of my favorites:

  • Lokalise: A powerful translation management platform. It syncs with your Rails app to provide a slick interface for translators. The gem makes integration a breeze.

  • Transifex: Another popular translation platform. Provides a similar feature set to Lokalise.

  • i18n-tasks: A gem providing various i18n-related tasks for finding missing keys, detecting unused keys, and more.

  • rails-i18n: A gem containing locale data and translations for 60+ languages. Great starting point for quickly adding new locales.

  • Tolk: A self-hosted web interface for managing translations. Useful for teams that prefer to keep translation management in-house.

Conclusion

Internationalizing a Rails app is a critical step in going global and reaching users around the world. By following the techniques and best practices outlined in this guide, you‘ll be well-equipped to handle any i18n challenge.

Remember, i18n is not a one-and-done task. It‘s an ongoing process that requires regular care and feeding as your app evolves. Set up the proper tooling and processes from the start, and make i18n a core part of your development workflow.

As a seasoned Rails developer, I can confidently say that investing in solid i18n pays huge dividends in the long run. Your global users will thank you, and your business will reap the rewards of an international user base. Now go forth and internationalize!

References

[1] https://www.statista.com/statistics/266808/the-most-spoken-languages-worldwide/

[2] https://csa-research.com/Insights/ArticleID/140/language-services-market-size-2017

[3] https://insights.csa-research.com/reportaction/305013126/Marketing

[4] https://w3techs.com/technologies/overview/content_language

Similar Posts