I18n Dynamic Ruby Translations with Lambdas

Sebastian Suttner
Cedarcode
Published in
3 min readMay 24, 2019

YAML files are the number one place to define translations with I18n. But, did you know that ruby (rb) files can be another option? Even a more powerful one…

I found out about this while working on a project that uses it, and when I tried to find more about it online I realized it was not a straightforward thing to search for. I hope that with this post, developers can find this more easily and leverage it sooner in their codebase if needed. Please note that this post is based on i18n v1.6.0–I didn’t try earlier versions.

How to store your translations

As stated in the Rails Guides, translations can be stored in both ruby and YAML file formats. Please note that this is also true for any ruby application using i18n. Just make sure all the translation files are part of the I18n.load_path.

# config/locales/en.rb{
en: {
foo: 'bar'
}
}

Which is equivalent to:

# config/locales/en.ymlen:
foo: 'bar'

If both translation files are loaded, the union of the two would make up the set of translations our application will use.

These are still static translation examples we are talking about. Let’s take a look at a more complex scenario next.

Add logic to translations

We know we can pass variables into translations, obviously supported by translations stored in YAML files. But, what if we need something more complex that cannot be handled by interpolation? Then, the only option left is to embed ruby logic into our translations. This is where ruby translations come in handy.

Translations stored in ruby files support the use of lambdas. Documentation about this can be found in the code of i18n itself.

# config/locales/en.rb{
en: {
salutation: lambda do |key, options|
if options[:formal]
"Welcome #{options[:name]}."
else
"Hi #{options[:name]}!"
end
end
}
}

Actually, the string returned by lambda will go through string interpolation too.

# config/locales/en.rb{
en: {
salutation: lambda do |key, options|
if options[:formal]
"Welcome %{name}."
else
"Hi %{name}!"
end
end
}
}

Both of the examples above would behave in the following way:

I18n.t(:salutation, formal: true, name: 'Foo') => 'Welcome Foo.'
I18n.t(:salutation, formal: false, name: 'Foo') => 'Hi Foo!'

Powerful! Isn’t it? A lot of complex translations can be solved with this technique. A quick glance at the use of lambdas for pluralization in Rails can be found in the Rails Guides as well.

A small caveat that the i18n gem recommends taking into account:

It is recommended to use/implement lambdas in an “idempotent” way. E.g. when a cache layer is put in front of I18n.translate it will generate a cache key from the argument values passed to #translate. Therefor your lambdas should always return the same translations/values per unique combination of argument values.

Conclusion

Both YAML and ruby translation files are great to use on a project. While ruby files are more powerful, YAML files are more dry and maintainable.

I’ve seen them work together perfectly. They can even team up and interact with each other, letting developers unleash their full potential.

Last, but not least, check out the following example!

# config/locales/en.rb{
en: {
salutation: lambda do |key, options|
if options[:formal]
I18n.t('salutations.formal', name: options[:name])
else
I18n.t('salutations.informal', name: options[:name])
end
end
}
}
# config/locales/en.ymlen:
salutations:
formal: 'Welcome %{name}.'
informal: 'Hi %{name}!'

--

--