Easy Internationalization for Your Rails App with BDD

John Ivanoff
Share

My company has a web application in US format that we have modified for use by our UK affiliate. However our current need to amend it further and add some functions is leading us down the path of a total re-write. But I believe Rails has saved the day. I am going to show you how easy it is to “internationalize” with Rails. This will be the first in a series with further tests upcoming to create, read, use and delete (CRUD).

We will start by creating a Rails application. I like to use Cucumber and factory_girl for testing, so we won’t need test-unit.

$ rails new international --skip-test-unit
...
$ cd international
international $

Open up the folder that has our new app in it and edit the Gemfile. We need to add the testing Gems to it.

group :test do
  gem 'cucumber-rails', '1.2.1'
  gem 'rspec-rails', '2.8.1'
  gem 'database<em>cleaner', '0.7.1'
 gem 'factory</em>girl', '2.4.0'
end

Save the file. Now run bundler to install the new gems.

international $ bundle install
Fetching source index for http://rubygems.org/
Using rake (0.9.2.2)
Using multi<em>json (1.0.4)
Using activesupport (3.1.3)
Using builder (3.0.0)
Using i18n (0.6.0)
Using activemodel (3.1.3)
Using erubis (2.7.0)
Using rack (1.3.6)
Using rack-cache (1.1)
Using rack-mount (0.8.3)
Using rack-test (0.6.1)
Using hike (1.2.1)
Using tilt (1.3.3)
Using sprockets (2.0.3)
Using actionpack (3.1.3)
Using mime-types (1.17.2)
Using polyglot (0.3.3)
Using treetop (1.4.10)
Using mail (2.3.0)
Using actionmailer (3.1.3)
Using arel (2.2.1)
Using tzinfo (0.3.31)
Using activerecord (3.1.3)
Using activeresource (3.1.3)
Using bundler (1.0.21)
Installing nokogiri (1.5.0) with native extensions
Installing ffi (1.0.11) with native extensions
Installing childprocess (0.3.0)
Installing rubyzip (0.9.5)
Installing selenium-webdriver (2.17.0)
Installing xpath (0.1.4)
Installing capybara (1.1.2)
Using coffee-script-source (1.2.0)
Using execjs (1.2.13)
Using coffee-script (2.2.0)
Using rack-ssl (1.3.2)
Using json (1.6.5)
Using rdoc (3.12)
Using thor (0.14.6)
Using railties (3.1.3)
Using coffee-rails (3.1.1)
Installing diff-lcs (1.1.3)
Installing gherkin (2.7.3) with native extensions
Installing term-ansicolor (1.0.7)
Installing cucumber (1.1.4)
Installing cucumber-rails (1.2.1)
Installing database</em>cleaner (0.7.1)
Installing factory_girl (2.4.0)
Using jquery-rails (1.0.19)
Using rails (3.1.3)
Installing rspec-core (2.8.0)
Installing rspec-expectations (2.8.0)
Installing rspec-mocks (2.8.0)
Installing rspec (2.8.0)
Installing rspec-rails (2.8.1)
Using sass (3.1.12)
Using sass-rails (3.1.5)
Using sqlite3 (1.3.5)
Using uglifier (1.2.2)
Your bundle is complete! Use <code>bundle show [gemname]</code> to see where a bundled gem is installed.

With the new gems loaded we can install Cucumber.

international $ rails generate cucumber:install
create config/cucumber.yml
create script/cucumber
 chmod script/cucumber
create features/step_definitions
create features/support
create features/support/env.rb
 exist lib/tasks
create lib/tasks/cucumber.rake
  gsub config/database.yml
  gsub config/database.yml
 force config/database.yml

In order for cucumber to work, we need to create the database, even though we have no models. The following command will do this:

international $ rake db:migrate db:test:prepare

Let’s see if it’s working. Go ahead and run Cucumber:

international $ cucumber
Using the default profile...
0 scenarios
0 steps
0m0.000s

It works.

Now we can create our first test. Create a new file in the features folder called manage_locations.feature

Feature: Manage locations
  In order to manage locations
  As a user
  I want to create and edit my locations.
  Scenario: List a location.
    Given there is a location named "location 1"
    When I am on the locations page
    Then I should see "location 1"

Save the file and let’s run the test.

international $ cucumber
  Using the default profile...
  Feature: Manage locations
  In order to manage locations
  As a user
  I want to create and edit my locations.
  Scenario: List a location. # features/manage<em>locations.feature:6
 Given there is a location named "location 1" # features/manage</em>locations.feature:7
      Undefined step: "there is a location named "location 1"" (Cucumber::Undefined)
      features/manage<em>locations.feature:7:in <code>Given there is a location named "location 1"'
 When I am on the locations page # features/manage_locations.feature:8
 Undefined step: "I am on the locations page" (Cucumber::Undefined)
 features/manage_locations.feature:8:in</code>When I am on the locations page'
 Then I should see "location 1" # features/manage</em>locations.feature:9
      Undefined step: "I should see "location 1"" (Cucumber::Undefined)
      features/manage_locations.feature:9:in `Then I should see "location 1"'
  1 scenario (1 undefined)
  3 steps (3 undefined)
  0m1.745s
You can implement step definitions for undefined steps with these snippets:
Given /^there is a location named "([^"]<em>)"$/ do |arg1|
 pending # express the regexp above with the code you wish you had
end
When /^I am on the locations page$/ do
 pending # express the regexp above with the code you wish you had
end
Then /^I should see "([^"]</em>)"$/ do |arg1|
 pending # express the regexp above with the code you wish you had
end

Just like Cucumber says, we’ll implement the steps using the template it gave us.

Create a new file in the /features/step_definitions folder called location_steps.rb Copy and paste those snippets into the file we just created.

Given /^there is a location named "([^"]<em>)"$/ do |arg1|
 pending # express the regexp above with the code you wish you had
end
When /^I am on the locations page$/ do
 pending # express the regexp above with the code you wish you had
end
Then /^I should see "([^"]</em>)"$/ do |arg1|
  pending # express the regexp above with the code you wish you had
end

Let’s save the file and rerun the test.

international $ cucumber
  Using the default profile...
  Feature: Manage locations
  In order to manage locations
  As a user
  I want to create and edit my locations.
  Scenario: List a location. # features/manage<em>locations.feature:6
 Given there is a location named "location 1" # features/step</em>definitions/location<em>steps.rb:1
 TODO (Cucumber::Pending)
 ./features/step</em>definitions/location<em>steps.rb:2:in <code>/^there is a location named "([^"]*)"$/'
 features/manage_locations.feature:7:in</code>Given there is a location named "location 1"'
 When I am on the locations page # features/step</em>definitions/location<em>steps.rb:5
 Then I should see "location 1" # features/step</em>definitions/location_steps.rb:9
  1 scenario (1 pending)
  3 steps (2 skipped, 1 pending)
  0m1.249s

As expected it’s not passing, but there’s a bit less noise. We need to create a location model so we can have locations. Using the generator,  create one and, for now, it will just have a name attribute.

international $ rails g model location name:string
invoke active<em>record
create db/migrate/20120118214355</em>create_locations.rb
create   app/models/location.rb

With the model created, time to run the migration and prep the test database.

international $ rake db:migrate db:test:prepare
==  CreateLocations: migrating ================================================
-- create_table(:locations)
   -> 0.0015s
==  CreateLocations: migrated (0.0016s) =======================================

Now we can create a location.

I like to use factory_girl. It makes it easy to create test data. (Learn more) It takes a bit of time to set up, but I think it’s worth it.

In the /features/support folder create a file called factories.rb and add the following.

require 'factory_girl'
FactoryGirl.define do
  factory :location do |f|
    f.name 'test location'
  end
end

This will create a location with the name “test location” in the database for testing purposes. You can always pass in a name to the factory, so it doesn’t have to be “test’ location.” We’ll see an example of that shortly.

In the location_steps.rb file, modify the first step so it looks like this:

Given /^there is a location named "([^"]*)"$/ do |name|
  Factory(:location, :name => name)
end

Save the file and rerun the test

international $ cucumber
  Using the default profile...
  Feature: Manage locations
  In order to manage locations
  As a user
  I want to create and edit my locations.
  Scenario: List a location. # features/manage<em>locations.feature:6
 Given there is a location named "location 1" # features/step</em>definitions/location<em>steps.rb:1
 When I am on the locations page # features/step</em>definitions/location<em>steps.rb:5
 TODO (Cucumber::Pending)
 ./features/step</em>definitions/location<em>steps.rb:6:in <code>/^I am on the locations page$/'
 features/manage_locations.feature:8:in</code>When I am on the locations page'
 Then I should see "location 1" # features/step</em>definitions/location_steps.rb:9
  1 scenario (1 pending)
  3 steps (1 skipped, 1 pending, 1 passed)
  0m1.485s

Excellent… the first step is passing.

Next, we’ll go to the locations page but we’ll need to make a route for that.

Open the routes.rb file in the config folder and add

match 'locations/' => 'locations#index', :as => :locations

That will get us to the locations index page.
We can check the route by typing in the command line:

international $ rake routes
locations /locations(.:format) locations#index

in the location_steps.rb file let’s tell is where to go by adding:

When /^I am on the locations page$/ do
  visit(locations_path)
end

Save the file and rerun the test.

international $ cucumber
  Using the default profile...
  Feature: Manage locations
  In order to manage locations
  As a user
  I want to create and edit my locations.
  Scenario: List a location. # features/manage<em>locations.feature:6
 Given there is a location named "location 1" # features/step</em>definitions/location<em>steps.rb:1
 When I am on the locations page # features/step</em>definitions/location<em>steps.rb:5
uninitialized constant LocationController (ActionController::RoutingError)
 ./features/step</em>definitions/location<em>steps.rb:6:in <code>/^I am on the locations page$/'
 features/manage_locations.feature:8:in</code>When I am on the locations page'
 Then I should see "location 1" # features/step</em>definitions/location<em>steps.rb:9
 Failing Scenarios:
 cucumber features/manage</em>locations.feature:6 # Scenario: List a location.
  1 scenario (1 failed)
  3 steps (1 failed, 1 skipped, 1 passed)
  0m1.501s

It tells us there is no LocationsController.
OK, create one and just worry about the index page, for now.

international $ rails g controller locations index
 create app/controllers/locations<em>controller.rb
 route get "locations/index"
 invoke erb
 create app/views/locations
 create app/views/locations/index.html.erb
 invoke helper
 create app/helpers/locations</em>helper.rb
 invoke assets
 invoke   coffee
 create     app/assets/javascripts/locations.js.coffee
 invoke   scss
 create     app/assets/stylesheets/locations.css.scss

Let’s go in the routes file (config/routes.rb) and delete the route that was created when we genereated the controller.

Remove this line.

get "locations/index"

Save the file,. Now that we have a controller, let’s rerun the test.

international $ cucumber
  Using the default profile...
  Feature: Manage locations
  In order to manage locations
  As a user
  I want to create and edit my locations.
  Scenario: List a location. # features/manage<em>locations.feature:6
 Given there is a location named "location 1" # features/step</em>definitions/location<em>steps.rb:1
 When I am on the locations page # features/step</em>definitions/location<em>steps.rb:5
 Then I should see "location 1" # features/step</em>definitions/location<em>steps.rb:9
 TODO (Cucumber::Pending)
 ./features/step</em>definitions/location_steps.rb:10:in <code>/^I should see "([^"]*)"$/'
 features/manage_locations.feature:9:in</code>Then I should see "location 1"'
  1 scenario (1 pending)
  3 steps (1 pending, 2 passed)
  0m1.858s

Perfect. We just have to display the names of the locations. In /app/views/locations folder open the index.html.erb file. We’ll add this code that will loop through the names:

<ul>
  <% @locations.each do |location| %>
    <li>
      <%= location.name %>
    </li>
  <% end %>
</ul>

Let’s see what happens if we run the test now.

international $ cucumber
  Using the default profile...
  Feature: Manage locations
  In order to manage locations
  As a user
  I want to create and edit my locations.
  Scenario: List a location. # features/manage<em>locations.feature:6
 Given there is a location named "location 1" # features/step</em>definitions/location<em>steps.rb:1
 When I am on the locations page # features/step</em>definitions/location<em>steps.rb:5
 You have a nil object when you didn't expect it!
 You might have expected an instance of Array.
 The error occurred while evaluating nil.each (ActionView::Template::Error)
 ......
 features/manage</em>locations.feature:8:in `When I am on the locations page'
    Then I should see "location 1" # features/step<em>definitions/location</em>steps.rb:9
Failing Scenarios:
cucumber features/manage_locations.feature:6 # Scenario: List a location.
1 scenario (1 failed)
3 steps (1 failed, 1 skipped, 1 passed)
0m1.599s

Nil object? We didn’t get anything from the database. Let’s fix that by opening up the locations_controller.rb file in /app/controllers Change the index method:

def index
  @locations = Location.all

  respond_to do |format|
    format.html # index.html.erb
    format.json { render json: @locations }
  end
end

In the location_step.rb we need to change the following:

Then /^I should see "([^"]*)"$/ do |arg1|
  pending # express the regexp above with the code you wish you had
end

to:

Then /^(?:|I )should see "([^"]*)"$/ do |text|
  if page.respond_to? :should
    page.should have_content(text)
  else
    assert page.has_content?(text)
  end
end

Save the files and rerun the test.

international $ cucumber
  Using the default profile...
  Feature: Manage locations
  In order to manage locations
  As a user
  I want to create and edit my locations.
  Scenario: List a location. # features/manage<em>locations.feature:6
 Given there is a location named "location 1" # features/step</em>definitions/location<em>steps.rb:1
 When I am on the locations page # features/step</em>definitions/location<em>steps.rb:5
 Then I should see "location 1" # features/step</em>definitions/location_steps.rb:9
  1 scenario (1 passed)
  3 steps (3 passed)
  0m1.679s

Okay, all green! The first test is passing!
Who needs a break?

¿Habla español?

Let’s get into the internationalization. We need to make an i18n module. In the /config/initializers/ folder create a new file called i18n.rb
In this file we’ll add

#encoding: utf-8

I18n.default_locale = :en

LANGUAGES = [
  ['English', 'en'],
  ["Español".html_safe, 'es']
]

This sets the default language to English and creates a list of languages that we will support along with their locales.
The locale will be specified in the URL,  telling us which language to use.  We’ll scope this in the routes and add a root URL. Don’t forget to remove /public/index.html!

Open the routes.rb file and add these lines:

scope '(:locale)' do
  match 'locations/' => 'locations#index', :as => :locations
  root :to => 'locations#index'
end

We have nested our routes inside the scope of :locale, and since it’s in parentheses, it’s optional.

http://localhost:3000/es will use the default locale, English. http://localhost:3000/en and http://localhost:3000/es will use the same controller and method, but will use the specified locale in the URL

Now we need to set the locale based on the param in the URL, if it’s provided.

We’ll do this in the Application Controller with a before filter.Open the application_colbtroller.rb file in the /app/controllers folder.

Add this line:

class ApplicationController < ActionController::Base
  before_filter :set</em>i18n<em>locale_from_params
  protect_from_forgery

protected
  def set_i18n_locale_from_params
    if params[:locale]
      if I18n.available_locales.include?(params[:locale].to_sym)
        I18n.locale = params[:locale]
      else
        flash.now[:notice] = "#{params[:locale]} translation not available"
        logger.error
        flash.now[:notice]
      end
    end
  end
  def default_url_options
    { locale: I18n.locale }
  end
end

So here it is checking for a locale parameter. If there is one, it checks to see if that locale is in our list of languages back in the /config/initializers/i18n.rb file. If it is in the list, we set the locale to the param. If it is not in the list, we will show a message saying that locale is not available.

The default_url_options sets the default locale when one is not provided. Since we’ve made a lot of changes at this point, let’s save all of the files and rerun the test.

international $ cucumber

Using the default profile...
Feature: Manage locations
In order to manage locations
As a user
I want to create and edit my locations.

Scenario: List a location. # features/manage<em>locations.feature:6
Given there is a location named "location 1" # features/step</em>definitions/location<em>steps.rb:1
When I am on the locations page # features/step</em>definitions/location<em>steps.rb:5
Then I should see "location 1" # features/step</em>definitions/location_steps.rb:9

1 scenario (1 passed)
3 steps (3 passed)
0m1.583s

Still all green. Whew.
Let’s get this i18n cooking.
On our locations index page, add a “Locations” heading

Open the managing_locations.feature file and add

Scenario: List a location.
Given there is a location named "location 1"
When I am on the locations page
Then I should see "Locations"
And I should see "location 1"

Save the files and rerun the test.

international $ cucumber
  Using the default profile...
  Feature: Manage locations
  In order to manage locations
  As a user
  I want to create and edit my locations.
  Scenario: List a location. # features/manage<em>locations.feature:6
 Given there is a location named "location 1" # features/step</em>definitions/location<em>steps.rb:1
 When I am on the locations page # features/step</em>definitions/location<em>steps.rb:5
 Then I should see "Locations" # features/step</em>definitions/location<em>steps.rb:9
 expected there to be content "Locations" in "Internationalnnnn location 1n n" (RSpec::Expectations::ExpectationNotMetError)
 ./features/step</em>definitions/location<em>steps.rb:11:in <code>/^(?:|I )should see "([^"]*)"$/'
 features/manage_locations.feature:9:in</code>Then I should see "Locations"'
 And I should see "location 1" # features/step</em>definitions/location<em>steps.rb:9
 Failing Scenarios:
 cucumber features/manage</em>locations.feature:6 # Scenario: List a location.
  1 scenario (1 failed)
  4 steps (1 failed, 1 skipped, 2 passed)
  0m1.593s

Fails as expected.

Open the index.html.ern file in /app/views/locations and add

<h1>Locations</h1>

Save the file and rerun the test.

international $ cucumber
  Using the default profile...
  Feature: Manage locations
  In order to manage locations
  As a user
  I want to create and edit my locations.
  Scenario: List a location. # features/manage<em>locations.feature:6
 Given there is a location named "location 1" # features/step</em>definitions/location<em>steps.rb:1
 When I am on the locations page # features/step</em>definitions/location<em>steps.rb:5
 Then I should see "Locations" # features/step</em>definitions/location<em>steps.rb:9
 And I should see "location 1" # features/step</em>definitions/location_steps.rb:9
  1 scenario (1 passed)
  4 steps (4 passed)
  0m1.604s

We’re back to green.

Now copy the same test but instead of “Location” in the heading we should see “Locaciones”
Change your managing_locations.feature to:

Feature: Manage locations
  In order to manage locations
  As a user
  I want to create and edit my locations.
  Scenario: List a location.
    Given there is a location named "location 1"
    When I am on the locations page
    Then I should see "Locations"
    And I should see "location 1"
  Scenario: List a location.
    Given there is a location named "location 1"
    When I am on the locations page
    Then I should see "Locaciones"
    And I should see "location 1"

Save the file and rerun the test

international $ cucumber
  Using the default profile...
  Feature: Manage locations
  In order to manage locations
  As a user
  I want to create and edit my locations.
  Scenario: List a location. # features/manage<em>locations.feature:6
 Given there is a location named "location 1" # features/step</em>definitions/location<em>steps.rb:1
 When I am on the locations page # features/step</em>definitions/location<em>steps.rb:5
 Then I should see "Locations" # features/step</em>definitions/location<em>steps.rb:9
 And I should see "location 1" # features/step</em>definitions/location<em>steps.rb:9
 Scenario: List a location. # features/manage</em>locations.feature:12
    Given there is a location named "location 1" # features/step<em>definitions/location</em>steps.rb:1
    When I am on the locations page # features/step<em>definitions/location</em>steps.rb:5
    Then I should see "Locaciones" # features/step<em>definitions/location</em>steps.rb:9
     expected there to be content "Locaciones" in "InternationalnnLocationsnnn location 1n n" (RSpec::Expectations::ExpectationNotMetError)
     ./features/step<em>definitions/location</em>steps.rb:11:in <code>/^(?:|I )should see "([^"]*)"$/'
features/manage_locations.feature:15:in</code>Then I should see "Locaciones"'
    And I should see "location 1" # features/step<em>definitions/location</em>steps.rb:9
  Failing Scenarios:
  cucumber features/manage_locations.feature:12 # Scenario: List a location.
  2 scenarios (1 failed, 1 passed)
  8 steps (1 failed, 1 skipped, 6 passed)
  0m1.643s

It failed where we expected.
Here’s where all the coding we did earlier pays off. It’s time to translate. For the words we need to translate, we will call I18n.translate which has an alias I18n.t. There is also a helper provided named t.
The parameter to the translate function is a unique dot-qualified name. We can choose any name you like, but if we use the t helper function provided, names that start with a dot will first be expanded using the name of the template. Let’s do that.

In the index.html.erb, change as follows

<h1>Locations</h1>

to

<h1><%= t('.title_html') %></h1>

Save the file.
We need to create a en.yml file in /config/locales folder.

en:

locations:
    index:
      title_html:    "Locations

We also need to create a es.yml file in /config/locales folder

es:

locations:
    index:
      title_html:    "Locaciones"

These files hold the translations. Also notice the structure of the YAML resembles the file structure? (/locations/index). This will be handy down the road.
Save the files and rerun the test…again.

international $ cucumber
  Using the default profile...
  Feature: Manage locations
  In order to manage locations
  As a user
  I want to create and edit my locations.
  Scenario: List a location. # features/manage<em>locations.feature:6
 Given there is a location named "location 1" # features/step</em>definitions/location<em>steps.rb:1
 When I am on the locations page # features/step</em>definitions/location<em>steps.rb:5
 Then I should see "Locations" # features/step</em>definitions/location<em>steps.rb:9
 And I should see "location 1" # features/step</em>definitions/location<em>steps.rb:9
 Scenario: List a location. # features/manage</em>locations.feature:12
    Given there is a location named "location 1" # features/step<em>definitions/location</em>steps.rb:1
    When I am on the locations page # features/step<em>definitions/location</em>steps.rb:5
    Then I should see "Locaciones" # features/step<em>definitions/location</em>steps.rb:9
     expected there to be content "Locaciones" in "InternationalnnLocationsnnn location 1n n" (RSpec::Expectations::ExpectationNotMetError)
     ./features/step<em>definitions/location</em>steps.rb:11:in <code>/^(?:|I )should see "([^"]*)"$/'
 features/manage_locations.feature:15:in</code>Then I should see "Locaciones"'
    And I should see "location 1" # features/step<em>definitions/location</em>steps.rb:9
  Failing Scenarios:
  cucumber features/manage_locations.feature:12 # Scenario: List a location.
  2 scenarios (1 failed, 1 passed)
  8 steps (1 failed, 1 skipped, 6 passed)
  0m1.643s

WHAT? Red?
It seems that in the second test we never said to use es as the locale so it is defaulting to en.

Let’s fix that as follows:

Scenario: List a location.
  Given there is a location named "location 1"
  And I am on the es site
  When I am on the locations page
  Then I should see "Locaciones"
  And I should see "location 1"

Save the files and rerun the test.

international $ cucumber
  Using the default profile...
  Feature: Manage locations
  In order to manage locations
  As a user
  I want to create and edit my locations.
  Scenario: List a location. # features/manage<em>locations.feature:6
 Given there is a location named "location 1" # features/step</em>definitions/location<em>steps.rb:1
 When I am on the locations page # features/step</em>definitions/location<em>steps.rb:5
 Then I should see "Locations" # features/step</em>definitions/location<em>steps.rb:9
 And I should see "location 1" # features/step</em>definitions/location<em>steps.rb:9
 Scenario: List a location. # features/manage</em>locations.feature:12
    Given there is a location named "location 1" # features/step<em>definitions/location</em>steps.rb:1
    And I am on the es site # features/manage<em>locations.feature:14
 Undefined step: "I am on the es site" (Cucumber::Undefined)
 features/manage</em>locations.feature:14:in `And I am on the es site'
    When I am on the locations page # features/step<em>definitions/location</em>steps.rb:5
    Then I should see "Locaciones" # features/step<em>definitions/location</em>steps.rb:9
    And I should see "location 1" # features/step<em>definitions/location</em>steps.rb:9
  2 scenarios (1 undefined, 1 passed)
  9 steps (3 skipped, 1 undefined, 5 passed)
  0m1.723s
  You can implement step definitions for undefined steps with these snippets:
  Given /^I am on the es site$/ do
    pending # express the regexp above with the code you wish you had
  end

Alright, then! It tells us what to do.
Open the location_steps.rb and add this to it.

Given /^I am on the (.+) site$/ do |language|
  I18n.locale = language
end

Save the file and rerun the test.

international $ cucumber
  Using the default profile...
  Feature: Manage locations
  In order to manage locations
  As a user
  I want to create and edit my locations.
  Scenario: List a location. # features/manage<em>locations.feature:6
 Given there is a location named "location 1" # features/step</em>definitions/location<em>steps.rb:1
 When I am on the locations page # features/step</em>definitions/location<em>steps.rb:5
 Then I should see "Locations" # features/step</em>definitions/location<em>steps.rb:9
 And I should see "location 1" # features/step</em>definitions/location<em>steps.rb:9
 Scenario: List a location. # features/manage</em>locations.feature:12
    Given there is a location named "location 1" # features/step<em>definitions/location</em>steps.rb:1
    And I am on the es site # features/step<em>definitions/location</em>steps.rb:17
    When I am on the locations page # features/step<em>definitions/location</em>steps.rb:5
    Then I should see "Locaciones" # features/step<em>definitions/location</em>steps.rb:9
    And I should see "location 1" # features/step<em>definitions/location</em>steps.rb:9
  2 scenarios (2 passed)
  9 steps (9 passed)
  0m1.636s

SCORE! You now have a very simple multilingual site. I hope you found useful for using BDD to drive internationalization of your Rails app.

CSS Master, 3rd Edition