MrYoshiji
MrYoshiji

Reputation: 54882

Rails multilinguism stored in DataBase (translation by the end-user)

We are having a dilemna about a feature we need to implement. We want to support n languages in our app (0 < n < +infinity).

We decided to go with the solution below:

module TranslatedAttributes

  def use_translated_attributes
    has_many :translated_attributes, as: :owner
    accepts_nested_attributes_for :translated_attributes

    define_method :translate_attribute do |attribute, locale = I18n.locale|
      TranslatedAttribute.where(attribute_name: attribute.to_s, owner_type: self.class.model_name, owner_id: self.id, locale: locale.to_s).first.try(:translation)
    end
  end
end

The User should be able to define X translation(s) for an instance of the Product model for example. Also, depending on the user's locale set in his profile, he will see the translated version of the attribute.

Example:

Product
  id: 12
TranslatedAttribute
  attribute_name: 'name'
  owner_id: 12
  owner_type: 'Product'
  locale: 'en'
  translation: 'Magnificent shiny shoes'
TranslatedAttribute
  attribute_name: 'name'
  owner_id: 12
  owner_type: 'Product'
  locale: 'fr'
  translation: 'Magnifiques chaussures brillantes'

In the view, it would be called like this:

product.translate_attribute(:name)
# or we will eventually define_method for each attribute to be translated
# so we could use the following
# product.name

This works, already tested.

The problem is when we will try to load tons of records, each one needing to query the DB to know the proper translation to display.

My question is: How would you handle a CACHE about this?

Another question I have is: is there another problem you see that I might not see so far? Also, I thought about accepts_nested_attributes_for :translated_attributes to build the translation's form with fields_for. Do you think it is a bad idea to handle it like this?

Thanks you!

Upvotes: 0

Views: 47

Answers (2)

user1003545
user1003545

Reputation: 2118

A first optimization could be to fetch all the translations in the desired locale[1] for a Product instance at the first call to translate_attribute, and cache them in an instance variable.

This way, the number of requests for translations would be reduced to just one by Product instance.

Quick example:

define_method :translate_attribute do |attribute, locale = I18n.locale|
  locale = locale.to_s
  @attributes_translations_cache ||= {}
  @attributes_translations_cache[locale] ||= Hash[
    self.translated_attributes
        .where(locale: locale)
        .map do |translated_attribute|
          [translated_attribute.name, translated_attribute.translation]
        end
  ]
  @attributes_translations_cache[locale][attribute]
end

I think that it should also be possible to join or at least include the translation to the Products in some way, but I haven't given much thought to this idea yet. I'll try to update this answer.

[1] This assumes that you only use a single locale in a given page, but you could also fetch all the locales at the same time, or any combination of locale and attributes.

Upvotes: 1

Ryan Bigg
Ryan Bigg

Reputation: 107708

You could use something such as the globalize3 gem to implement this feature.

Upvotes: 2

Related Questions