Dave Sag
Dave Sag

Reputation: 13486

How ought I store localised values in a DRY and database efficient way - Ruby on Rails / i18n

What is the recommended approach to maintaining multi-language values within an ActiveRecord model.

I am looking into upgrading our database schema and Object Models to allow for widespread internationalisation of many of the values, and I am weighing up various ways to do this.

The standard rails-i18n system is largely silent on this, although it offers powerful tools for internationalising field and model names, in addition to the text within views.

The R18n gem allows you to overload your database with columns that store the localised strings, and which present the correct value depending on the locale. This presents a couple of problems.

Say we are talking about a model Sport — database table sports. We need to be able to search for Sport.where(name: 'soccer') even though in the UK they call it 'football', so the query becomes scope :with_name ->(n){ where("name_en_GB = ? OR name_en_AU = ?", n, n) }.

If we want to add another locale we need to both update the schema, and update any such queries on that schema. A rather brittle solution.

Another solution I've seen is to maintain a separate SportLocale model and associated sport_locales table, that holds the name and locale.

Assuming

class Sport < ActiveRecord::Base
  has_many :locales
end

class SportLocale < ActiveRecord::Base
  belongs_to :sport
end

Then to find the right sport you'd do something like

class Sport < ActiveRecord::Base
  has_many :locales, class_name: "SportLocale"
  self.with_name(n)
    SportLocale.where(name: n, locale: I18n.locale).first.try(:sport)
  end
end

This is fine if Sport is your only localised model but when you start adding all the other models it becomes a bit crazy, with each of them needing an associated *Locale model. Not the DRYest of solutions either.

I'd like a solution that allows

class Sport < ActiveRecord::Base
  include Localised
  localised_field :name
end

and magically Sport.where(name: 'football') will find the right sport.

Is there any such system out there already, or would I have to build it myself? How are other projects dealing with this sort of problem?

Upvotes: 4

Views: 132

Answers (3)

Sven Fuchs
Sven Fuchs

Reputation: 296

The i18n Ruby gem (which is used by Rails) is mostly useful for static translations, i.e. translations which can be considered part of the code (just like you'd consider static, hardcoded strings in your views part of the code). These are, most of the time, stored in static YAML files, so they'd change at deploy time.

I don't know about the current version of R18n. When I once looked at it years ago it provided an alternative way to do similar things (with a different syntax and slightly different features).

Translating model data is a different task. Globalize is maybe the most widely used solution for it, but there are lots of other solutions. Unless you're feeling adventurous I'd recommend to use a mature solution, translating data (and integrating with ActiveRecord) can be hairy when it comes to more complicated usecases.

Upvotes: 3

ferrisoxide
ferrisoxide

Reputation: 167

If I understand the problem you are trying to solve, you may get something out of the Globalize gem.

It hides away the translation tables you need, but it can provide a standard for mapping of 'soccer' [en-au] to 'football' [en-uk] that you might be able to interrogate for the translated attribute values you're looking for.

Upvotes: 1

Fer
Fer

Reputation: 3347

Why don't you just store the I18n localization key associated to your sport and search for it?

To display the name to the user, just use that key against the user's current locale.

Upvotes: 0

Related Questions