Rails: How to use i18n with Rails 4 enums

Ruby on-Rails-4Enums

Ruby on-Rails-4 Problem Overview


Rails 4 Active Record Enums are great, but what is the right pattern for translating with i18n?

Ruby on-Rails-4 Solutions


Solution 1 - Ruby on-Rails-4

Starting from Rails 5, all models will inherit from ApplicationRecord.

class User < ApplicationRecord
  enum status: [:active, :pending, :archived]
end

I use this superclass to implement a generic solution for translating enums:

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  def self.human_enum_name(enum_name, enum_value)
    I18n.t("activerecord.attributes.#{model_name.i18n_key}.#{enum_name.to_s.pluralize}.#{enum_value}")
  end
end

Then I add the translations in my .yml file:

en:
  activerecord:
    attributes:
      user:
        statuses:
          active: "Active"
          pending: "Pending"
          archived: "Archived"

Finally, to get the translation I use:

User.human_enum_name(:status, :pending)
=> "Pending"

Solution 2 - Ruby on-Rails-4

I didn't find any specific pattern either, so I simply added:

en:
  user_status:
    active:   Active
    pending:  Pending...
    archived: Archived

to an arbitrary .yml file. Then in my views:

I18n.t :"user_status.#{user.status}"

Solution 3 - Ruby on-Rails-4

Here is a view:

select_tag :gender, options_for_select(Profile.gender_attributes_for_select)

Here is a model (you can move this code into a helper or a decorator actually)

class Profile < ActiveRecord::Base
  enum gender: {male: 1, female: 2, trans: 3}

  # @return [Array<Array>]
  def self.gender_attributes_for_select
    genders.map do |gender, _|
      [I18n.t("activerecord.attributes.#{model_name.i18n_key}.genders.#{gender}"), gender]
    end
  end
end

And here is locale file:

en:
  activerecord:
    attributes:
      profile:
        genders:
          male: Male
          female: Female
          trans: Trans

Solution 4 - Ruby on-Rails-4

To keep the internationalization similar as any other attribute I followed the nested attribute way as you can see [here][1].

If you have a class User:

class User < ActiveRecord::Base
  enum role: [ :teacher, :coordinator ]
end

And a yml like this:

pt-BR:
  activerecord:
    attributes:
      user/role: # You need to nest the values under model_name/attribute_name
        coordinator: Coordenador
        teacher: Professor

You can use:

User.human_attribute_name("role.#{@user.role}")

[1]: http://guides.rubyonrails.org/i18n.html#translations-for-active-record-models "here"

Solution 5 - Ruby on-Rails-4

Model:

enum stage: { starting: 1, course: 2, ending: 3 }

def self.i18n_stages(hash = {})
  stages.keys.each { |key| hash[I18n.t("checkpoint_stages.#{key}")] = key }
  hash
end

Locale:

checkpoint_stages:
    starting: Saída
    course: Percurso
    ending: Chegada

And on the view (.slim):

= f.input_field :stage, collection: Checkpoint.i18n_stages, as: :radio_buttons

Solution 6 - Ruby on-Rails-4

Elaborating on user3647358's answer, you can accomplish that very closely to what you're used to when translating attributes names.

Locale file:

en:
  activerecord:
    attributes:
      profile:
        genders:
          male: Male
          female: Female
          trans: Trans

Translate by calling I18n#t:

profile = Profile.first
I18n.t(profile.gender, scope: [:activerecord, :attributes, :profile, :genders])

Solution 7 - Ruby on-Rails-4

Combining the answers from Repolês and Aliaksandr, for Rails 5, we can build 2 methods that allow you to translate a single value or a collection of values from an enum attribute.

Set up the translations in your .yml file:

en:
  activerecord:
    attributes:
      user:
        statuses:
          active: "Active"
          pending: "Pending"
          archived: "Archived"

In the ApplicationRecord class, from which all models inherit, we define a method that handles translations for a single value and another one that handles arrays by calling it:

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  def self.translate_enum_name(enum_name, enum_value)
    I18n.t("activerecord.attributes.#{model_name.i18n_key}.#{enum_name.to_s.pluralize}.#{enum_value}")
  end

  def self.translate_enum_collection(enum_name)
    enum_values = self.send(enum_name.to_s.pluralize).keys
    enum_values.map do |enum_value|
      self.translate_enum_name enum_name, enum_value
    end
  end
end 

In our views, we can then translate single values:

<p>User Status: <%= User.translate_enum_name :status, @user.status %></p>

Or the entire collection of enum values:

<%= f.select(:status, User.translate_enum_collection :status) %>

Solution 8 - Ruby on-Rails-4

Try using TranslateEnum gem for these purposes

class Post < ActiveRecord::Base
  enum status: { published: 0, archive: 1 }
  translate_enum :status
end


Post.translated_status(:published)
Post.translated_statuses

@post = Post.new(status: :published)
@post.translated_status 

Solution 9 - Ruby on-Rails-4

I've created a gem for this.

http://rubygems.org/gems/translated_attribute_value

Add to your gemfile:

gem 'translated_attribute_value'

If you have a status field for user:

pt-BR:
  activerecord:
    attributes:
      user:
        status_translation:
          value1: 'Translation for value1'
          value2: 'Translation for value2'

And in your view you can call like this:

user.status_translated

It works with active record, mongoid or any other class with getter/setters:

https://github.com/viniciusoyama/translated_attribute_value

Solution 10 - Ruby on-Rails-4

The model:

class User < ActiveRecord::Base
  enum role: [:master, :apprentice]
end

The locale file:

en:
  activerecord:
    attributes:
      user:
        master: Master
        apprentice: Apprentice

Usage:

User.human_attribute_name(:master) # => Master
User.human_attribute_name(:apprentice) # => Apprentice

Solution 11 - Ruby on-Rails-4

Try the enum_help gem. From its description:

> Help ActiveRecord::Enum feature to work fine with I18n and simple_form.

Solution 12 - Ruby on-Rails-4

Heres a t_enum helper method that I use.

<%= t_enum(@user, :status) %>

enum_helper.rb:

module EnumHelper

  def t_enum(inst, enum)
    value = inst.send(enum);
    t_enum_class(inst.class, enum, value)
  end

  def t_enum_class(klass, enum, value)
    unless value.blank?
      I18n.t("activerecord.enums.#{klass.to_s.demodulize.underscore}.#{enum}.#{value}")
    end
  end

end

user.rb:

class User < ActiveRecord::Base
  enum status: [:active, :pending, :archived]
end 

en.yml:

en:
  activerecord:
    enums:
      user:
        status:
          active:   "Active"
          pending:  "Pending..."
          archived: "Archived"

Solution 13 - Ruby on-Rails-4

Yet another way, I find it a bit more convenient using a concern in models

Concern :

module EnumTranslation
  extend ActiveSupport::Concern

  def t_enum(enum)
    I18n.t "activerecord.attributes.#{self.class.name.underscore}.enums.#{enum}.#{self.send(enum)}"
  end
end

YML:

fr:
    activerecord:
      attributes:
        campaign:
          title: Titre
          short_description: Description courte
          enums:
            status:
              failed: "Echec"

View :

<% @campaigns.each do |c| %>
  <%= c.t_enum("status") %>
<% end %>

Don't forget to add concern in your model :

class Campaign < ActiveRecord::Base
  include EnumTranslation

  enum status: [:designed, :created, :active, :failed, :success]
end

Solution 14 - Ruby on-Rails-4

I prefer a simple helper in application_helper

  def translate_enum(object, enum_name)
    I18n.t("activerecord.attributes.#{object.model_name.i18n_key}.#{enum_name.to_s.pluralize}.#{object.send(enum_name)}")
  end

Then in my YML file :

fr:
  activerecord:
    attributes:
      my_model:
        my_enum_plural:
          pending:  "En cours"
          accepted: "Accepté"
          refused:  "Refusé"

Solution 15 - Ruby on-Rails-4

You can simply add a helper:

def my_something_list
  modes = 'activerecord.attributes.mymodel.my_somethings'
  I18n.t(modes).map {|k, v| [v, k]}
end

and set it up as usually:

en:
  activerecord:
    attributes:
      mymodel:
        my_somethings:
           my_enum_value: "My enum Value!"

then use it with your select: my_something_list

Solution 16 - Ruby on-Rails-4

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true

  def self.enum(definitions)
    defind_i18n_text(definitions) if definitions.delete(:_human)
    super(definitions)
  end

  def self.defind_i18n_text(definitions)
    scope = i18n_scope
    definitions.each do |name, values|
      next if name.to_s.start_with?('_')
      define_singleton_method("human_#{name.to_s.tableize}") do
        p values
        values.map { |key, _value| [key, I18n.t("#{scope}.enums.#{model_name.i18n_key}.#{name}.#{key}")] }.to_h
      end

      define_method("human_#{name}") do
        I18n.t("#{scope}.enums.#{model_name.i18n_key}.#{name}.#{send(name)}")
      end
    end
  end
end


en:
  activerecord:
    enums:
      mymodel:
        my_somethings:
           my_enum_value: "My enum Value!"

enum status: [:unread, :down], _human: true

Solution 17 - Ruby on-Rails-4

Here is the simplest solution I have found.

Model file 'house.rb':

enum status: { unavailable: 0, available: 1 }

In the view, a simple_form select:

<%= simple_form_for(@house) do |f| %>
...
<%= f.input :status,
        collection: House.statuses.keys.map { |s| [t("house_enum_status_#{s}"), s] }
...
<% end %>

The collection creates an array with [key, value] expected for the select, with the correct translation.

And here is both locales yml files used:

'fr.yml'

house_enum_status_unavailable: "Indisponible"
house_enum_status_available: "Disponible"

'en.yml'

house_enum_status_unavailable: "Not available"
house_enum_status_available: "Available"

Here is the result in French

Here is the result in English

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChris BeckView Question on Stackoverflow
Solution 1 - Ruby on-Rails-4RepolêsView Answer on Stackoverflow
Solution 2 - Ruby on-Rails-4pierreaView Answer on Stackoverflow
Solution 3 - Ruby on-Rails-4AliaksandrView Answer on Stackoverflow
Solution 4 - Ruby on-Rails-4DoguitaView Answer on Stackoverflow
Solution 5 - Ruby on-Rails-4Oswaldo FerreiraView Answer on Stackoverflow
Solution 6 - Ruby on-Rails-4Rafael ValverdeView Answer on Stackoverflow
Solution 7 - Ruby on-Rails-4ne5tView Answer on Stackoverflow
Solution 8 - Ruby on-Rails-4AliaksandrView Answer on Stackoverflow
Solution 9 - Ruby on-Rails-4Vinícius OyamaView Answer on Stackoverflow
Solution 10 - Ruby on-Rails-4Dmitry ShvetsovView Answer on Stackoverflow
Solution 11 - Ruby on-Rails-4KukuninView Answer on Stackoverflow
Solution 12 - Ruby on-Rails-4cwestonView Answer on Stackoverflow
Solution 13 - Ruby on-Rails-4Geoffrey HView Answer on Stackoverflow
Solution 14 - Ruby on-Rails-4SnakeView Answer on Stackoverflow
Solution 15 - Ruby on-Rails-4ftshtwView Answer on Stackoverflow
Solution 16 - Ruby on-Rails-4Kevin.XinView Answer on Stackoverflow
Solution 17 - Ruby on-Rails-4FranfranView Answer on Stackoverflow