Localization with Rails: Supporting Websites with Multiple Languages Using i18n & yml
Sometimes you'll want a website to support multiple languages, which we call localzation. When I first started using i18N with Rails, I found myself going to StackOverflow frequently. Here you'll find some of the lessons I learned while creating the equestrian tours website, Wanderreitten, with Rails and i18n localization. The Wanderritt application itself is fairly simple — it has no database or models and only has three controllers (including ApplicationController). All the texts on the website are either in English or German and the texts are also stored in the yml. I created the website to replace an existing site for the client and copied much of the original content into the yml with very few modifications.
Setup
module Wanderreiten
class Application < Rails::Application
# All translations from config/locales/*.rb,yml are auto loaded.
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}')]
# set default locale to German (if english is your default you can simply leave this line out)
config.i18n.default_locale = :de
config.i18n.enforce_available_locales = true
end
end
Rails.application.routes.draw do
scope '(:locale)', locale: /en|de/ do
root 'homes#index'
get 'pictures' => 'homes#pictures'
get 'impressum' => 'homes#impressum'
get 'termine' => 'termine#index'
get 'termine/:termin' => 'termine#show', as: 'termin'
end
end
yml Structure
Even though our default locale is German, we can easily switch the locales to English by prepending ‘/en’ to page paths. By default, our views will look for yml content in the file ‘locales/de.yml’, and locate the locale files the matches the views. We can store translations for homes#index either in locales/de.yml or in locales/homes/de.yml. In my opinion, it makes sense for the yml file structure to match the application layout. The official guide gives you a nice picture of how you could organize your files. In either case, we may use the translations in the same way:
homes:
index:
title: Wanderritte Brandenburg- und Mecklenburg Vorpommern
horses: Die Pferde
company:
manager: Geschäftsführer
address: Sitz der Gesellschaft
In homes/index.html we render the text for horses with<%= t(‘.horses’) %>
. The period signifies we’re using the lazy lookup for views; we can refer to this text from other locations with the full i18n path: <%= t(‘homes.index.horses’) %>
Shared Yml Translations
I set up some custom logic for accessing tour details in the show pages (below). The reason I did this is that this content is shared across the index and shows pages for the tours (termine). There are multiple ways to organize shared translations; you could do whatever seems fit for your application.
class TermineController < ApplicationController
def show
@url = params[:termine]
@termin = I18n.t(‘termine’)[:rides][@url.to_sym]
end
end
This allows me to access the shared yml content for the rides in termine#show (for example Short Trail Ride) easily. @termin[:title]
gives me a simple title for a ride instead of using the equivalent long form <%= t("termine.rides.#{params[:termin]}.title") %>
Locale Navigation
Switching locales:
<% if I18n.locale == :de %>
<%= link_to url_for(locale: :en) do %>
<%= image_tag('eng-icon.gif', width: '16', height: '11', alt: 'English') %> English
<% end %>
<% else %>
<%= link_to url_for(locale: :de) do %>
<%= image_tag('de-icon.gif', width: '16', height: '11', alt: 'English') %> Deutsch
<% end %>
<% end %>
Remember to add in the set_locale filter (below).
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :set_locale
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
def default_url_options(options = {})
{ locale: I18n.locale }
end
end
I had all the other localization and routing set up but realized that the localization still wasn't working properly. A user would go to the English version of the website and expect all the links to work in English. However, all of the links would still point to the default locale (without /en). Here's the solution: 'default_url_options' adds the locale to routes;. I discovered this method on Ruby Snippets. An alternative option for automatically creating correct links is the gem 'routing-filter'.
That's it for today. I hope this tutorial provided you with some useful insights on working with i18n. I look forward to reading your comments and questions. If you have any more suggestions for further reading, I'd be happy to add them to the resources listed below. Thanks for reading!
Other Resources:
- Localizing Time in Traditional Rails apps with Moment.js
- gem 'route_translator' is useful if you want the page paths to be localized as well. For this application, we would have the /tours path for the English version rather than the /en/termine.
- i18n Official Guide
- Ask for help on Ruby and i18n
It’s great that support is available in multiple languages. This is important for me, especially since the issue of translations accompanies me everywhere. I visit here whenever I need expert help. Detailed reviews help you avoid wasting time on inappropriate options and choose a reliable and high-quality source for translation.