Posts tagged "i18n"

As we already know, in Rails 3 all dependencies will be bundled. This means you will be able to use latest I18n version which includes several improvements by itself.

Besides that, a couple things changed on Rails 3 I18n, some features were added and others were deprecated. This post is a quick walkthrough it:

1) Error messages defaults

With the addition of ActiveModel, it’s easy to add I18n behavior to any object by simply including ActiveModel::Translation and ActiveModel::Validations. As side effect, if we followed the same translation pattern as in Rails 2.3, each ORM would have to specify its default error messages at #{ORM.name}.errors.messages. This means that if you are using two ORMs in your app, you would have to translate the same messages twice.

To handle this situation, error messages now has errors.attributes and errors.messages as fallbacks in case a message cannot be found for an specific ORM. Notice that this also allows you to customize a message for an specific attribute used by models in different ORMs. So if you have several models with title attribute, you can customize the blank message for them all by simply placing it at errors.attributes.title.blank.

2) Attributes default

In the same line as above, you can now specify default attributes across different models and ORMs as well. For example, if you are exposing created_at and updated_at in your views, until Rails 2.3 you needed to specify the same translation for each model. In Rails 3, you can simply do:

en:
  attributes:
    created_at: "Created at"
    updated_at: "Updated at"

3) f.submit

f.submit in forms now can be called without arguments and it will inspect the form object to set the proper label. Imagine the following form:

<% form_for @post do |f| %>
  <%= f.submit %>
<% end %>

If @post is a new record, it will use “Create Post” as submit button label, otherwise, it uses “Update Post”. You can customize those labels with I18n under the following keys:

en:
  helpers:
    submit:
      create: "Create a {{model}}"
      update: "Confirm changes to {{model}}"

Nonetheless, it also searches for keys under the object name key as well:

en:
  helpers:
    submit:
      post:
        create: "Add {{model}}"

4) Labels with I18n

Labels were also updated to use I18n. Imagine the following form:

<% form_for @post do |f| %>
  <%= f.label :title %>
  <%= f.text_field :title %>
  <%= f.submit %>
<% end %>

It will search for a label value at helpers.label.post.title, falling back to the value returned by Post.human_attribute_name(:title). This is useful in case you want more personalized labels instead of just “Title”.

5) Deprecation

Besides those improvements, I18n has two deprecations: the first is the key support.select should now be helpers.select and the other is that AV error messages are now agnostic, so activerecord.errors.template should now be errors.template.

That’s all, enjoy!

A new I18n gem just got released and it comes with two new backends extensions: Fast and InterpolationCompiler.

First, what is a backend?

I18n.t, I18n.translate, I18n.l and I18n.localize methods are actually just wrappers to I18n.backend, which is actually who does all the heavy lifting. This means that you can change your backend to other stuff, as long as it respects the required API.

By default, I18n comes with the Simple backend, but others are available. For example, I18n has an ActiveRecord, which stores translations in the database. This is useful in cases someone needs to change translations through a web interface. To use it, you just need to do:

  I18n.backend = I18n::Backend::ActiveRecord

There are a couple other backends, like a backend which implements fallbacks, so if something cannot be found in a specified language, like german (:de), it can fallback to english (:en). You can check the whole list, but for this while, we are going to focus on the two new backends extensions.

Fast

Fast, means fast. And oh, boy, this one is fast. This extension flattens translations to speed up the look up. For example, the following hash { :a => { :b => { :c => :foo } } }, gets flattened to { :"a.b.c" => "foo" }, so instead of recursively looking into hashes, it looks just once. The obvious expense is that whenever you are storing translations, you need to flatten the translation hash, and it takes more time than usual.

In order to measure different backend implementations, I pushed some benchmark setup to the I18n repository. The current setup measures the storage time, the time it takes to translate a key (the depth of the key means how many nested hashes it takes), the time to translate a key falling back to the default key and the time translate a key (at depth 5) and interpolate. The results comparing the Simple backend without and with Fast extension are showed below:

Simple vs. Fast

In other words, a simple lookup using the Fast extension is 3 to 4 times faster than the Simple one. Besides, configuring your application to use it is very simple:

  I18n::Backend::Simple.send :include, I18n::Backend::Fast

Nice!

Interpolation compiler

The InterpolationCompiler is a backend extension which extracts all required interpolation keys from a string, leaving just the minimum required to runtime. Imagine the following string: "This is a custom blank message for {{model}}: {{attribute}}". This extension annotates the string so it knows that it needs to interpolate both model and attribute, and points exactly where the interpolation should happen. We can compare the Simple backend without and with the InterpolationCompiler below:

Simple vs. Interpol

The InterpolationCompiler just changes the time taken when we have interpolation keys, without affecting to much the other translations. You can add it to your app as easy as the Fast one:

  I18n::Backend::Simple.send :include, I18n::Backend::InterpolationCompiler

Run, I18n, run!

But the best is still coming! Fast and InterpolationCompiler can actually be used together, to achieve unseen performance in I18n. The benchmark speaks for itself:

Simple vs. FastInterpol

While we speed up the performance in around four times in simple lookups, Fast and InterpolationCompiler improvements get combined whenever we need to use interpolation, becoming around six times faster!

As said previously, both extensions increase the time taken to store translations as side-effect. Such can be viewed below:

Store

The yaml hash used in for the benchmark, is relatively slow, but it shows how the time taken to store translations grows with such extensions. But remember, you are constantly storing translations only in development (before each request is processed). In production, the translations are stored at startup and that is it.

Using within Rails

You should be able to use such features today in Rails 2.3.5 and it will also be possible in Rails 3. You just need to install the I18n gem and configure it in your environment.

Why care?

All the times shown are in miliseconds. In other words, why care? If you are building a simple application, using just one language, those improvements likely won’t change anything. But in an application which relies on I18n, during a request/response lifecycle, I18n is invoked many times: error messages for models, flash messages, page titles, e-mail subjects, page content, date and time localization, pluralization rules and even in many of ActionView helpers. So in such cases, it’s worth to give such extensions a try.

Running benchmarks on your own

If you want to run benchmarks on your own, it’s quite simple. You just need to do:

git clone git://github.com/svenfuchs/i18n.git
cd i18n
ruby benchmark/run.rb

Credits

The possibility to have backends and such extensions is due to Sven Fuchs, which leading the I18n group quite well.

Many of the backends were added by I18n community, and the Fast and InterpolationCompiler were created by thedarkone.

Guys, I owe you a beer! ;)

Enjoy!