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

92
18个回答

92

从Rails 5开始,所有的模型都将继承自ApplicationRecord

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

我使用这个超类来实现一个通用的枚举翻译解决方案:

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

然后我在我的.yml文件中添加翻译:

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

最终,我使用以下方法进行翻译:

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

3
在下拉菜单中使用时,您会如何处理它(例如,在不显示单个值时)? - tirdadc
7
@tirdadc,您可以像这样处理一个下拉菜单:<%= f.select :status, User.statuses.keys.collect { |status| [User.human_enum_name(:status, status), status] } %> - Repolês
6
+1 好的回答。我对其进行了微调,使其成为一个视图辅助方法,因为我觉得这更属于视图方面,并且不要将属性名称变为复数形式:https://gist.github.com/abevoelker/fed59c2ec908de15acd27965e4725762 在视图中使用它,如 human_enum_name(@user, :status) - Abe Voelker
1
根据Repolês的建议,您还可以为基本模型添加另一个用于下拉列表的类方法:self.human_enum_collection(enum_name)。代码如下:send(enum_name.to_s.pluralize).keys.collect { |val| [human_enum_name(enum_name, val), val] } - armchairdj
1
@TPR 这是一个例子:https://gist.github.com/repoles/e798a915a0df49e3bcce0b7932478728。如果您有任何问题,请告诉我。 - Repolês
显示剩余3条评论

57

我也没有发现任何特定的模式,所以我只是添加了:

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

将一个任意的 .yml 文件添加进来。然后在我的视图中:

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

5
我之前做了类似的事情,但我将其放在{locale}.activerecord.attributes.{model}.{attribute}下,并编写了一个t_enum(model, enum, value)辅助方法,使枚举翻译与标签翻译相邻。 - Chris Beck

35
为了保持国际化与其他属性类似,我采用了嵌套属性方式,您可以在这里看到。如果你有一个类User:
class User < ActiveRecord::Base
  enum role: [ :teacher, :coordinator ]
end

而且有一个像这样的yml

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

你可以使用:

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

2
这很吸引人,但它打破了 Rails 约定,即 activerecord.attributes.<fieldname> 应该作为表单帮助器的 label 翻译。 - Chris Beck
7
@ChrisBeck,看起来这个遵循了Rails I18n指南中描述的约定:http://guides.rubyonrails.org/i18n.html#translations-for-active-record-models - danblaker
1
根据我的经验,这可以在不使用“role”键的情况下工作。您可以直接将“coordinator”和“teacher”嵌套在“user”下面。 - Ryenski
什么是human_attribute_name? - TPR

33

这是一个视图:

select_tag :gender, options_for_select(Profile.gender_attributes_for_select)

这里有一个模型(实际上,您可以将此代码移到帮助程序或装饰器中)

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

以下是本地化文件:

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

1
但在这种情况下如何获取单个记录的翻译呢?因为“.human_attribute_name('genders.male')”无法起作用。 - Stiig
谢谢,这对我的情况非常好用!(http://stackoverflow.com/q/42185293/6873497) - matiss
我已经为这些目的制作了一个轻量级的宝石 https://github.com/shlima/translate_enum - Aliaksandr
FML - 真是够了,都2021年了,这个还不能与simple_form正常工作。但是,多亏了你的评论,我有了一个好的解决方法 :-) - Hendrik
genders.map的性别是什么?我一直得到undefined local variable or method `genders' - TPR

8

对于user3647358的回答,你可以通过类似翻译属性名称的方式来实现。

本地化文件:

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

通过调用I18n#t进行翻译:

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

这是最简洁的解决方案,仅使用框架工具,因此在我看来是最好的。也许可以添加一个测试,以便在翻译中涵盖所有性别。 - schmijos

7

结合RepolêsAliaksandr的答案,对于Rails 5,我们可以构建两种方法,允许您从枚举属性中翻译单个值或一组值。

在您的.yml文件中设置翻译:

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

ApplicationRecord类中,所有模型都继承自该类,我们定义了一个处理单个值的方法和另一个通过调用它来处理数组的方法来处理翻译。
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 

在我们的视角中,我们可以翻译单个值:
<p>User Status: <%= User.translate_enum_name :status, @user.status %></p>

或者是枚举值的整个集合:

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

这对我翻译枚举非常完美。我只需要做一个更改就可以在选择器上使用它,将键作为值,将翻译作为文本,而不是在translate_enum_collection中使用映射:enum_values.each_with_object({}) do |enum_value, acc| acc[enum_value] = self.translate_enum_name(enum_name, enum_value) end然后在视图中添加一个invert:User.translate_enum_collection(:status).invert - Jorge Sampayo

7

模型:

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

本地化:

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

在视图文件中 (.slim):


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

4
我已经为此创建了一个宝石。

http://rubygems.org/gems/translated_attribute_value

在你的Gemfile中添加:

gem 'translated_attribute_value'

如果您有一个用户状态字段:
pt-BR:
  activerecord:
    attributes:
      user:
        status_translation:
          value1: 'Translation for value1'
          value2: 'Translation for value2'

在你的视图中,你可以这样调用:

user.status_translated

它可以与Active Record、Mongoid或任何具有getter/setter的类一起使用:

https://github.com/viniciusoyama/translated_attribute_value


4

尝试使用TranslateEnum gem来实现这些目的。

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 

1
我们也使用这个宝石。在我们评估的所有选项中,它具有最干净的方法并且得到了良好的维护。 - cseelus

3

这个模型:

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

本地化文件:

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

使用方法:

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

@user.role 怎么样,因为这是主要问题。 - Code-MonKy
最直接、干净、优雅的方式。 - Fabian Winkler
7
AnyModel.human_attribute_name(:i_dont_exist) => "I dont exist" 任何模型.human_attribute_name(:i_dont_exist) => "我不存在" - Shiyason

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接