Rails, SEO and I18n: The Basics

Ah, yes. Adding SEO to your Rails app. Nothing glamorous about it but we can all agree it’s critical.

This is a step-by-step how-to of one way to handle the set-up and maintenance of SEO related tags using an I18n YAML file that can be edited separate from the Rails source.

I’m a big fan of the I18n Rails Internationalization gem. By pulling the displayed text outside of the code itself, once the initial set-up is in place, you’ve just empowered a copywriter or editor or translator or the person down in marketing to make changes without needing a programmer to modify code.

This is the perfect place to store the text we’ll need for the SEO related tags like <title> and the meta description. After that, a couple methods to retrieve and display the text in the tags and presto you have the SEO basics covered.

(Think about how happy you’re about to make the folks that have to beg for a programmer’s time to make small text updates. Not to mention the programmer!)


Starting Point

To illustrate this feature I’ve created a very bare bones Rails app that you can find on Github called jehughes/rails-basic-seo-example. There are three controllers: reports (index, edit, create), dashboard (index) and help (index) and a message view that’s rendered from the dashboard controller. The edit view of the reports has a robots noindex tag for illustration. You can see the app in action here. The example is a Rails 4 / Ruby 2.0 app.


Store the Strings of Tag Text

To store the text for the SEO tags, create the file config/en.header_text.yml

At the top of the file there are several required entries and you’ll want to set up the defaults.

Here’s the complete file for our example application.

The file format and requirements will be explained as we go along.


Retrieve the I18n Text

Create a new helper file app/helpers/set_seo_tags_helper.rb and add the following methods.

The method get_header_text centralizes the call to t(), making sure there are defaults in place with a graceful failure. (Refactored 9/4/2013: merge scope into page_name)

The method get_view_text is a shortcut for when the “lazy” look-up can be used and no other options are needed.

For more about this “lazy” look-up feature, read section 4.1.4 of Rails I18n Internationalization.


Adding the SEO Tags to the Rails Application

Now that we can store and retrieve the text, we need to add the SEO related tags to the Rails views. We’re creating an extension of the feature outlined by Ryan Bates in RailsCast #30: Pretty Page Title and the episode comments.

One extension is taking advantage of the I18n “lazy” look-up to use entries in the YAML file rather than requiring content_for in every view. In an ideal world every page would have the SEO related tags set but we’re setting up layers of defaults so, if not, they will at least fail gracefully.

Add the following to app/helpers/set_seo_tags_helper.rb :

Here’s the complete set_seo_tags_helper.rb for the example app.

Add the following to the <head> section of app/views/layouts/application.index.erb (or whatever layout you’re working with) :


Connecting the Pieces

Okay, we have the text centralized somewhere it’s easy to maintain, a way to retrieve the desired text and a mechanism for displaying this text in various SEO related tags.

The final step is to set-up the views. The following are illustrations on how to do this using the example Rails app.

Use “lazy” look-up

The help page in the example app illustrates using the “lazy” look-up built into the yield_or_default() calls in the layout header. The <H1> tag is the only one that would need to be set in the view.

In config/locale/en.header_text.yml (outside of the header_text scope):

Then in app/views/help/index.html.erb:

The meta_keywords and robots tags will pick up the last resort default empty string.

The views for the reports index and new report pages have the same set-up.

Use “lazy” look-up and defaults

Since the dashboard is our root page, other than the <H1> tag, we can just use the header_text defaults.

In config/locale/en.header_text.yml (outside of the header_text scope):

Then in app/views/dashboard/index.html.erb, this one line.

Use content_for instead of “lazy” look-up

Sometimes it’s not possible to use the “lazy” look-up. It this case we need to set content_for for each tag. This is done using the seo_tag() method we added to app/helpers/set_seo_tags_helper.rb.

An illustration of this is the message view in our example app. This view is rendered from the dashboard controller rather than a corresponding message index controller action.

In this case, add the following to the config/locale/en.header_text.yml inside the header_text scope:

In app/messages/index.html.erb, make the following calls to seo_tag():

Use content_for to pass through a local variable

There will be situations when you need to add a local variable to the tags. In the example app,the report title is added to the edit and show views for reports.

Here are these sections of the config/locale/en.header_text.yml file (outside of the header_text scope):

With these lines at the top of the app/views/reports/show.html.erb file:

Notice the @report.title being passed in for the %{add_in} variable in the header_text file.

The top of app/views/reports/edit.html.erb is similar with the addition of a robots noindex tag:


Reality Check

As much as this is hands-free, i.e., programmer free, the en.header_text.yml YAML file will need to be verified when it’s returned from the editor or whoever is doing the updates. YAMLlint.com is straight forward (even if it does remove my comments and spacing).

One caution. Don’t forget to check the updated YAML file for extended characters and other data sometimes added by a word processors. (I’m looking at you Microsoft and your “special” quote marks. Can’t tell you how many times the word “don’t” has tripped me up.)


Final Comments

This integrates the basic SEO related tags but can easily be extended for tags like rel=canonical or rel=author. I’ve also found myself putting repeating blocks of calls to seo_tag into a layout partial.

Update 8/30/2013: A simple test has been added to the sample application to check for the default keys in en.header_text.yml

Update 9/24/2013: Moved the new helper methods out of app/helpers/application_helper.rb to a newly created helper file app/helpers/set_seo_tags_helper.rb.

I’d love to hear some feedback on this feature. Problems? Improvements? Suggested extensions? Helpful?