opensoul.org

Abusing Rails I18N to Set Page Titles

I got tired of @page_title variables being sprinkled around in controllers and views on Speaker Deck, so I made good use of Rails’ I18n API to define them all in one spot.

My config/locales/en.yml now looks something like this:

en:
  title:
    default: "Speaker Deck - Share Presentations without the Mess"
    account:
      new: "Sign Up"
      show: "My Account"
    categories:
      show: "%{category}"
    likes:
      index: "Presentations Liked by %{user}"
    passwords:
      index: "Password Reset"
    talks:
      index: "All Presentations"
      edit: "Edit %{talk}"
      new: "New Presentation"
      show: "%{talk}"
    users:
      show: "Talks by %{user}"

To make this work, add a #page_title helper method, which looks up the page title translation using the controller and action name.

module TitleHelper
  def page_title
    t page_title_translation_key,
      page_title_context.merge(:default => :"title.default")
  end

  def page_title_translation_key
    :"title.#{controller_name}.#{action_name}"
  end

  def page_title_context
    controller.view_assigns.symbolize_keys
  end
end

The helper also passes all of the instance variables assigned in the controller, so you can use any of those variables in your page title. The only caveat is that we have to define #to_s in order for our model objects to show up properly in the title:

class User < ActiveRecord::Base
  def to_s
    username
  end
end

Now in our layout, we can use our new helper:

<title><%= page_title %></title>

I’m really happy with how this little hack worked out. This almost seems like something that should be in Rails core. Maybe I’ll have to submit a pull request…

rails and speakerdeck November 05, 2012

7 Comments

  1. Pablo Orellana Pablo Orellana November 5, 2012

    Great idea, I will definitely give it a try. Thanks!

  2. Luis Lavena Luis Lavena November 5, 2012

    Hello Brandon, I use a similar approach because, like you, I got tired of doing it :-P

    A small note on page_title_translation_key, the key doesn’t need to be a symbol to work, you can use a simple string.

    Saying this because you’re converting all these strings into symbols that will not get GC’ed.

    As your application grows, more symbols will be allocated.

    I know, premature optimization is the root of all evils, yada yada yada :-D

  3. Brandon Keepers Brandon Keepers November 5, 2012

    Luis: Good point. Fortunately it will be limited to the number of unique actions that the app has. Speaker Deck is a relatively small app, but it could be a problem with a ridiculously large app.

  4. Justin Hileman Justin Hileman November 6, 2012

    If you swap out controller_name for controller_path.gsub('/', '.') in #page_title_translation_key you can even make it work when you have duplicate controller names, for example a sessions controller for both users and admins

  5. eric eric November 12, 2012

    Very nice approach, I see only one big problem: how do you manage those actions that render other views?
    For example new and update, if there are errors they’d likely render ‘new’ / ‘edit’, instead of redirect to that page. So you would have to add 2 times every title for those pages (and duplication is bad, especially when you have to have to translate them!)
    How have you solved this problem?

  6. Brandon Keepers Brandon Keepers November 12, 2012

    eric: That’s a good point, and not something that we are handling. It might make sense to have the helper alias some action names (create/update) to related actions (new/edit).

  7. eric eric November 13, 2012

    yeah, I came up with this:
    def page_title_translation_key
    aliases = {’create’ => ‘new’, ‘update’ => ’edit’}
    “page_titles.#{controller_name}.#{aliases[action_name] || action_name}”
    end

    but it happens that some actions (especially single resources, like dashboard, invite, or other) has like a show (eg /invite) which post to create… if you’re going to pull a request for the rails core take care in consideration this too…

Post a Comment

Comments use textile. Anonymous comments will be deleted.

I am Brandon Keepers. I build Internet things, usually with Ruby or JavaScript. I work at GitHub and live in Holland, MI.

Popular Posts