Rails: 如何在 Rails 4枚举中使用 i18n

Rails4活动记录枚举很棒,但是用 i18n 翻译的正确模式是什么呢?

41141 次浏览

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}"

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

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

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

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])

Try the enum_help gem. From its description:

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

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}")

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"

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"

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

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

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

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) %>

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

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é"
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

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

Model Order:

 enum order_type: {normal: false, security: true}.freeze, _default: :normal

OrdersController

    @order_type = Order.order_types.except(:security) unless secure_present # optional code
@order_type.transform_keys! { |key| I18n.t("orders.new.order_types.#{key}")}

Locales (Ukrainian)

uk:
orders:
new:
order_types:
normal: 'Звичайна'
security: 'Безпечна'