更新 1:
如果您不需要担心 i18n 和应用程序文本的翻译,我发现 februaryInk 的答案非常接近正确。如果您将has_many :child_model 置于您的所有验证中,验证将以正确的顺序显示。但是,full_messages 似乎没有使用语言环境文件翻译模型或属性名称,所以如果您需要翻译错误消息(我这样做),我的回答似乎仍然是一个不错的解决方案。
更新 2:
在发布第一个更新后刚刚意识到,我可以通过删除使用更新 1 中的发现进行排序的部分来大量简化生成 messages 列表的代码,而只保留进行翻译的部分。所以这是我的新解决方案,它结合了我的更新 1 和我的原始解决方案。对于此更新的解决方案,有关 config/locales/xx.yml 和 config/application.rb 文件的所有其他信息仍与原始解决方案相同。
app/models/parent_model.rb
...
validates :name, # validations hash
validates :description, # validations hash
validates :others, # validations hash
has_many :child_models
accepts_nested_attributes_for :child_models
...
app/models/child_model.rb
...
validates :nested_1, # validations hash
validates :nested_2, # validations hash
...
app/helpers/application_helper.rb
messages = resource.errors.messages.keys.map {|value| error_message_attribute(resource, value) + I18n.t('space') + resource.errors.messages[value].first}.map { |msg| content_tag(:li, msg) }.join
private
def error_message_attribute(resource, symbol)
if symbol.to_s.split(".").length > 1
model_name, attribute_name = symbol.to_s.split(".")
model_class = model_name.singularize.camelize.constantize
model_class.model_name.human + I18n.t('space') + model_class.human_attribute_name(attribute_name).downcase
else
resource.class.human_attribute_name(symbol)
end
end
更新结束
我在application_helper.rb 中对我的error_messages 函数进行了一些更改,现在一切都按我想要的方式工作:主表单验证错误在顶部,嵌套表单验证错误在这些错误的下面,错误的顺序除了将嵌套表单错误移到主表单错误下之外,不会改变。
我的解决方案是更改error_messages 中的messages = 行,如下所示,并添加一个私有辅助方法。 (这可能应该分成几部分,以便于阅读和理解,但我在控制台中构建它以获得我想要的,然后直接从那里粘贴)。
app/helpers/application_helper.rb
messages = Hash[resource.errors.messages.keys.map.with_index(1) { |attribute, index| [attribute, [index, attribute.match(/\./) ? 1 : 0]] }].sort_by {|attribute, data| [data[1], data[0]]}.collect { |attributes| attributes[0]}.map {|value| error_message_attribute_name(resource, value) + I18n.t('space') + resource.errors.messages[value].first}.map { |msg| content_tag(:li, msg) }.join
private
def error_message_attribute_name(resource, symbol)
if symbol.to_s.split(".").length > 1
model_name, attribute_name = symbol.to_s.split(".")
model_class = model_name.singularize.camelize.constantize
model_class.model_name.human + I18n.t('space') + model_class.human_attribute_name(attribute_name).downcase
else
resource.class.human_attribute_name(symbol)
end
end
此解决方案也适用于其他其他语言环境,因为我使用 I18n 来获取所有名称。您还必须添加以下内容:
config/locales/en.yml
en:
space: " "
这样模型和属性名称将在单词之间有或没有空格的语言中正确处理(我需要支持的第一个语言环境是中文,它在单词之间没有空格)。如果你确实需要支持中文,例如,你会使用这个:
config/locales/zh.yml
zh:
space: ""
如果您不必支持这种情况,I18n.t('space') 的所有实例都可以替换为" "。模型和属性名称也可以翻译为,但同样,如果您不需要支持英语以外的语言环境,则无需执行任何操作(尽管您可以使用 en.yml 文件更改模型名称或显示的属性)。
例如,使用en.yml 更改使用常见作者/书籍示例显示的名称:
config/locales/en.yml
en:
activerecord:
models:
author: "writer"
book: "manuscript"
attributes:
author:
name: "non de plume"
book:
name: "title"
published: "year"
在此示例中,如果没有对 en.yml 进行上述添加,则默认值为:
- 名称不能为空。
- 书名不能为空。
- 出版的图书不能为空白。
但在en.yml 中添加上述内容后,它将是:
- Nom de plume 不能为空。
- 稿件标题不能为空。
- 稿件年份不能为空。
当然,如果您有一个带有适当翻译的zh.yml 文件,那么您在其中的任何内容都会显示出来。
如果确实需要支持多语言环境,别忘了在config/application.rb中添加以下内容(这部分只是表面测试,可能需要一些额外的配置):
config/application.rb
config.i18n.available_locales = [:zh, :en]
config.i18n.default_locale = :en