Write a post

Enjoy this post? Give Bjorn Linder a like if it's helpful.

Localization with Rails: Supporting Websites with Multiple Languages Using i18n & yml

Published Jan 20, 2017Last updated Feb 02, 2017
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:

Discover and read more posts from Bjorn Linder
get started
Enjoy this post?

Leave a like and comment for Bjorn

Be the first to share your opinion

Subscribe to our weekly newsletter