如何解决如何在移动性中使用 locale 或 fallthrough 访问器来验证翻译?
如何在创建或更新时使用访问器来验证具有区域设置或失败访问器和移动性 gem 的模型?
使用移动宝石: v1.0.5
c = Category.new(name_en: 'Cat. 1',name_de: 'Kat. 1')
c.valid?
# => false
c.errors
# Should output the individual validation errors for every single locale accessor.
示例:
- 姓名(en):已被占用。
- 姓名 (de):有效。
=> #<ActiveModel::Errors:0x00007fb75c488ee0 ...
@messages={:name_en=>["has already been taken"]},@details={:name_en=>[{:error=>:taken,:value=>"Cat. 1"}]}
解决方法
最后我编写了一个名为 ValidatesAccessors
的插件。欢迎反馈。
配置移动性
# config/initializers/mobility.rb
Mobility.configure do
# PLUGINS
plugins do
# ...
# One of both must be set.
fallthrough_accessors
locale_accessors
# Add ValidatesAccessors plugin
validates_accessors
end
end
添加移动插件代码
# lib/mobility/plugins/validates_accessors.rb
module Mobility
# Mobility plugins.
module Plugins
# Adds accessor validation so you can easily validate accessor attributes like +name_en+.
module ValidatesAccessors
extend Plugin
default true
initialize_hook do |*names|
if options[:validates_accessors] &&
!options[:fallthrough_accessors] &&
!options[:locale_accessors]
warn 'The Validates Accessors plugin depends on Fallthrough Accessors or Locale '\
'Accessors being enabled,but both options are falsey'
else
options[:validates_accessors] = {} unless options[:validates_accessors].is_a?(Hash)
options[:validates_accessors].reverse_merge!(locales: Mobility.available_locales)
names.each do |name|
define_accessors_locales(name,options[:validates_accessors])
end
include Mobility::Validations::Concern
end
end
private
def define_accessors_locales(name,options)
module_eval <<~RUBY,__FILE__,__LINE__ + 1
def mobility_accessors_locales_#{name}
#{options[:locales].map(&:to_sym)}
end
RUBY
end
end
register_plugin(:validates_accessors,ValidatesAccessors)
end
end
添加移动模型关注代码
# lib/mobility/validations/concern.rb
module Mobility
module Validations
# Mobility validations concern module.
module Concern
extend ActiveSupport::Concern
private
# This validation will perform a validation round against each mobility locales and add
# errors for mobility attributes names.
def validates_mobility_attributes
# Only validates mobility attributes from the admin locale.
return unless Mobility.locale.to_sym == I18n.locale.to_sym
mobility_errors = mobility_errors_for_attributes(self.class.mobility_attributes)
# Add translated attributes errors back to the object.
mobility_errors.each do |attribute,attribute_errors|
attribute_errors[:messages].zip(attribute_errors[:details]).each do |attribute_error|
errors.add(attribute,attribute_error.second.delete(:error),message: attribute_error.first,**attribute_error.second)
end
end
end
# Return all translated attributes with errors for the given locales,including their error
# messages.
def mobility_errors_for_attributes(attribute_names)
{}.tap do |mobility_errors|
locales = mobility_accessors_locales(attribute_names)
additional_locales = locales - [I18n.locale.to_sym]
# Track errors for current locale.
if locales.include?(I18n.locale.to_sym)
mobility_errors.merge!(mobility_errors_for_locale(attribute_names,I18n.locale.to_sym))
end
# Validates the given object against each locale except the current one and track their
# errors.
additional_locales.each do |locale|
Mobility.with_locale(locale) do
if invalid?
mobility_errors.merge!(mobility_errors_for_locale(attribute_names,locale))
end
end
end
end
end
# Return all translated attributes with errors for the given locale,including their error
# messages.
def mobility_errors_for_locale(attribute_names,locale)
{}.tap do |mobility_errors|
attribute_names.each do |attribute|
next unless mobility_accessors_locales_for(attribute).include?(locale) &&
(messages = errors.messages.delete(attribute.to_sym)).present?
mobility_errors["#{attribute}_#{locale.to_s.underscore}".to_sym] = {
messages: messages,details: errors.details.delete(attribute.to_sym)
}
end
end
end
# Define which locales to validate against all translated attribute.
def mobility_accessors_locales(attribute_names)
locales = []
attribute_names.each { |attribute| locales |= mobility_accessors_locales_for(attribute) }
locales
end
# Define which locales to validate against for a single translated attribute.
def mobility_accessors_locales_for(attribute)
locales = if try("mobility_accessors_locales_#{attribute}").respond_to?(:call)
try("mobility_accessors_locales_#{attribute}").call(self)
else
try("mobility_accessors_locales_#{attribute}")
end
locales || []
end
end
end
end
使用插件
class Category < ApplicationRecord
extend Mobility
translates :name # without config.
translates :name,validates_accessors: { locales: %i[en de fr] } # specifying locales to validate.
translates :description,validates_accessors: { locales: %i[en de] } # using another attribute.
translates :name,:description,validates_accessors: { locales: %i[en de fr] } # same config for both attributes.
# Add here your translated attributes validations.
validates :name,presence: true,uniqueness: true,length: { maximum: 5 }
# After validations add mobility ValidatesAccessors validation.
validate :validates_mobility_attributes
# ...
end
示例:
- 姓名(en):已被占用。
- 姓名 (de):有效。
=> #<ActiveModel::Errors:0x00007fb75c488ee0 ...
@messages={:name_en=>["has already been taken"]},@details={:name_en=>[{:error=>:taken,:value=>"Cat. 1"}]}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。