Ruby on Rails Translating ActiveRecord model attributes

Example

globalize gem is a great solution to add translations to your ActiveRecord models. You can install it adding this to your Gemfile:

gem 'globalize', '~> 5.0.0'

If you're using Rails 5 you will also need to add activemodel-serializers-xml

gem 'activemodel-serializers-xml'

Model translations allow you to translate your models' attribute values, for example:

class Post < ActiveRecord::Base
  translates :title, :text
end

I18n.locale = :en
post.title # => Globalize rocks!

I18n.locale = :he
post.title # => גלובאלייז2 שולט!

After you defined your model attributes that need to be translated you have to create a translation table, through a migration. globalize provides create_translation_table! and drop_translation_table!.

For this migration you need to use up and down, and not change. Also, in order to run this migration successfully, you have to define the translated attributes in your model first, like shown above. A proper migration for the previous Post model is this:

class CreatePostsTranslationTable < ActiveRecord::Migration
  def up
    Post.create_translation_table! title: :string, text: :text
  end

  def down
    Post.drop_translation_table!
  end
end

You may also pass options for specific options, like:

class CreatePostsTranslationTable < ActiveRecord::Migration
  def up
    Post.create_translation_table! title: :string,
      text: { type: :text, null: false, default: "Default text" }
  end

  def down
    Post.drop_translation_table!
  end
end

In case you already have any existing data in your needing translation columns, you can easily migrate it to the translations table, by adjusting your migration:

class CreatePostsTranslationTable < ActiveRecord::Migration
  def up
    Post.create_translation_table!({
      title: :string,
      text: :text
    }, {
      migrate_data: true
    })
      
  end

  def down
    Post.drop_translation_table! migrate_data: true
  end
end

Make sure you drop the translated columns from the parent table after all your data is safely migrated. To automatically remove the translated columns from the parent table after the data migration, add the option remove_source_columns to the migration:

class CreatePostsTranslationTable < ActiveRecord::Migration
  def up
    Post.create_translation_table!({
      title: :string,
      text: :text
    }, {
      migrate_data: true,
      remove_source_columns: true
    })
      
  end

  def down
    Post.drop_translation_table! migrate_data: true
  end
end

You may also add new fields to a previously created translations table:

class Post < ActiveRecord::Base
  # Remember to add your attribute here too.
  translates :title, :text, :author
end

class AddAuthorToPost < ActiveRecord::Migration
  def up
    Post.add_translation_fields! author: :text
  end

  def down
    remove_column :post_translations, :author
  end
end