Rails: How to use i18n with Rails 4 enums
Solution 1
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 2
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 3
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
To keep the internationalization similar as any other attribute I followed the nested attribute way as you can see here.
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}")
Solution 5
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])
Related videos on Youtube
Chris Beck
Software Architect and Rails developer at www.directra.com
Updated on February 24, 2022Comments
-
Chris Beck about 2 years
Rails 4 Active Record Enums are great, but what is the right pattern for translating with i18n?
-
Chris Beck about 10 yearsi did something similar, but i put it under
{locale}.activerecord.attributes.{model}.{attribute}
and wrote at_enum(model, enum, value)
helper method so the enum translations would be adjacent to the label translation -
Chris Beck almost 9 yearsThis is visually appealing but it breaks the rails convention of
activerecord.attributes.<fieldname>
being thelabel
translation for form helpers -
Stiig over 7 yearsbut how to get translation for single record in this case? Because
.human_attribute_name('genders.male')
don't work -
Code-MonKy over 7 yearsHow about
@user.role
, because that is the main issue. -
tirdadc over 7 yearsHow would you handle using this in a dropdown (ie when not displaying a single value)?
-
matiss about 7 yearsThank you, works like charm in my case!
-
Aliaksandr about 7 yearsI've made lightweight gem for these purposes github.com/shlima/translate_enum
-
Fabian Winkler about 7 yearsThe most straight forward, clean and elegant way.
-
Shiyason over 6 yearsAnyModel.human_attribute_name(:i_dont_exist) => "I dont exist"
-
Repolês over 6 years@tirdadc you can handle a dropdown like this:
<%= f.select :status, User.statuses.keys.collect { |status| [User.human_enum_name(:status, status), status] } %>
. -
Abe Voelker over 6 years+1 good answer. I tweaked it for my use to be a view helper method since I feel this is more of a view concern, and to not pluralize the attribute name: gist.github.com/abevoelker/fed59c2ec908de15acd27965e4725762 Call it in a view like
human_enum_name(@user, :status)
-
danblaker over 6 years@ChrisBeck it appears this follows the convention described in the Rails I18n Guide: guides.rubyonrails.org/…
-
armchairdj about 6 yearsPer Repolês, you could also add another class method to your base model for dropdowns:
self.human_enum_collection(enum_name)
. Code would besend(enum_name.to_s.pluralize).keys.collect { |val| [human_enum_name(enum_name, val), val] }
-
cseelus about 6 yearsWe also use this gem. Has the cleanest approach from all options we evaluated and is well maintained.
-
Ryenski over 3 yearsIn my experience this works without using the
role
key. You can nestcoordinator
andteacher
directly underuser
. -
Hendrik about 3 yearsFML - it is 2021 and this still doesn't properly work with simple_form. But - thanks to your comment I have a good workaround :-)
-
schmijos almost 3 yearsThis is the minimalist solution only using framework tools and therefore the best one in my eyes. Maybe add a test so that you cover all genders in your translations.
-
Jorge Sampayo over 2 yearsThis worked perfectly for me translating enums. There was only one change I needed to do to use it on selects, to put as value the key of the enum and as text the translation, instead of the map in translate_enum_collection:
enum_values.each_with_object({}) do |enum_value, acc| acc[enum_value] = self.translate_enum_name(enum_name, enum_value) end
And then in the view add an invert:User.translate_enum_collection(:status).invert
-
TPR about 2 yearsUsually there is more than one enum name and enum value, does this mean that I have to def
self.human_enum_name2
anddef self.human_enum_name3
and so on? -
TPR about 2 yearsWhat is the genders of
genders.map
? I keep gettingundefined local variable or method `genders'
-
TPR about 2 yearswhat is human_attribute_name?
-
Repolês about 2 years@TPR to use the method
self.human_enum_name
you have to specify the enum name and the enum value. This means only one generic class method will be enough, doesn't matter how many enums your ActiveRecord class has. -
TPR about 2 years@Repolês I see that the code has only one
(enum_name, enum_value)
, if I have more than one enum name and enum value, how should I write this without confusion? -
Repolês about 2 years@TPR here's an example: gist.github.com/repoles/e798a915a0df49e3bcce0b7932478728. Let me know if you have any question.