Translate your ActiveRecord attributes without stress or bloat

Nicolas Blanco
Aug 24, 2017 · 3 min read

If you want your Rails application to be multi-lingual, you’ll surely have to translate your records. For example, let say you want your products names and descriptions to be available in different locales. Like everything in technology, there are lots of ways to achieve this.

One well-known way in the Rails eco-system is to use some library like Globalize. The problem with these kind of libraries is that they usually add a lot of complexity to your project. Developers who had to update big projects using Globalize may remember the pain… Those librairies sometimes add a lot of callbacks, virtual attributes and scopes to your models and debugging can become extremely complex. They usually rely on external tables or columns and modify the behaviour of ActiveRecord to perform “joins” when retrieving records.

Those librairies were usually written way before the latest improvements in RDBMS like PostgreSQL or MySQL, which now provide native JSON fields. So if you can use a recent version of PostgreSQL or MySQL, you’ll see in this example how easy it is to provide translated attributes without the use of any external library. By using native JSON fields, it’s also quite easy to query your records using a simple where clause without performing joins on external tables…

Let say we want our Product model to have translated name and description. To do this, we simply create two JSON fields named name_translations and description_translations. Appending _translations to the column names is simply a personal convention, you may name your fields as you like of course…

That’s it! Now the strategy is to have a hash inside those columns where the different locales are the keys.

Because we have set the default value of the column to be an empty hash, we don’t have to check fornil first when accessing the translated field and we can safely write code like product.name_translations[locale].

Let’s see how we can design the form where the user can input the different locales for our name_translations field… Inside our form, let’s use the fields_for helper…

As you may see in this example, the trick is simply to encapsulate the hash inside an OpenStruct. By doing that, the Rails helpers can read the value of the hash like if it was a Ruby object…

This is almost finished. Now, we just need to let Strong Parameters authorize the hash and its keys…

In the controller, this may be done like this…

We may even use the native JSON operators to query for a particular record…

As you see, it’s clearly easy to store translated fields and query them without any external help and at the same time keep the codebase quite simple… We did not need to add a single line into our models for this basic implementation to work!

Don’t hesitate to read the JSON documentation of PostgreSQL and MySQL to have more information about how to query this type :

Nicolas Blanco

Written by

Like developing and designing great web apps.