diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index e813629855..d583d48b54 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -62,6 +62,11 @@ module ActiveModel CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict] MESSAGE_OPTIONS = [:message] + class << self + attr_accessor :i18n_full_message # :nodoc: + end + self.i18n_full_message = false + attr_reader :messages, :details # Pass in the instance of the object that is using the errors object. @@ -375,7 +380,7 @@ module ActiveModel def full_message(attribute, message) return message if attribute == :base - if @base.class.respond_to?(:i18n_scope) + if self.class.i18n_full_message && @base.class.respond_to?(:i18n_scope) parts = attribute.to_s.split(".") attribute_name = parts.pop namespace = parts.join("/") unless parts.empty? diff --git a/activemodel/lib/active_model/railtie.rb b/activemodel/lib/active_model/railtie.rb index a9cdabba00..0ed70bd473 100644 --- a/activemodel/lib/active_model/railtie.rb +++ b/activemodel/lib/active_model/railtie.rb @@ -7,8 +7,14 @@ module ActiveModel class Railtie < Rails::Railtie # :nodoc: config.eager_load_namespaces << ActiveModel + config.active_model = ActiveSupport::OrderedOptions.new + initializer "active_model.secure_password" do ActiveModel::SecurePassword.min_cost = Rails.env.test? end + + initializer "active_model.i18n_full_message" do + ActiveModel::Errors.i18n_full_message = config.active_model.delete(:i18n_full_message) || false + end end end diff --git a/activemodel/test/cases/railtie_test.rb b/activemodel/test/cases/railtie_test.rb index ff5022e960..ab60285e2a 100644 --- a/activemodel/test/cases/railtie_test.rb +++ b/activemodel/test/cases/railtie_test.rb @@ -31,4 +31,24 @@ class RailtieTest < ActiveModel::TestCase assert_equal true, ActiveModel::SecurePassword.min_cost end + + test "i18n full message defaults to false" do + @app.initialize! + + assert_equal false, ActiveModel::Errors.i18n_full_message + end + + test "i18n full message can be disabled" do + @app.config.active_model.i18n_full_message = false + @app.initialize! + + assert_equal false, ActiveModel::Errors.i18n_full_message + end + + test "i18n full message can be enabled" do + @app.config.active_model.i18n_full_message = true + @app.initialize! + + assert_equal true, ActiveModel::Errors.i18n_full_message + end end diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb index ea462d9e07..1c62e750de 100644 --- a/activemodel/test/cases/validations/i18n_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_validation_test.rb @@ -12,6 +12,9 @@ class I18nValidationTest < ActiveModel::TestCase I18n.load_path.clear I18n.backend = I18n::Backend::Simple.new I18n.backend.store_translations("en", errors: { messages: { custom: nil } }) + + @original_i18n_full_message = ActiveModel::Errors.i18n_full_message + ActiveModel::Errors.i18n_full_message = true end def teardown @@ -19,6 +22,7 @@ class I18nValidationTest < ActiveModel::TestCase I18n.load_path.replace @old_load_path I18n.backend = @old_backend I18n.backend.reload! + ActiveModel::Errors.i18n_full_message = @original_i18n_full_message end def test_full_message_encoding @@ -42,7 +46,20 @@ class I18nValidationTest < ActiveModel::TestCase assert_equal ["Field Name empty"], @person.errors.full_messages end + def test_errors_full_messages_doesnt_use_attribute_format_without_config + ActiveModel::Errors.i18n_full_message = false + + I18n.backend.store_translations("en", activemodel: { + errors: { models: { person: { attributes: { name: { format: "%{message}" } } } } } }) + + person = Person.new + assert_equal "Name cannot be blank", person.errors.full_message(:name, "cannot be blank") + assert_equal "Name test cannot be blank", person.errors.full_message(:name_test, "cannot be blank") + end + def test_errors_full_messages_uses_attribute_format + ActiveModel::Errors.i18n_full_message = true + I18n.backend.store_translations("en", activemodel: { errors: { models: { person: { attributes: { name: { format: "%{message}" } } } } } }) @@ -52,6 +69,8 @@ class I18nValidationTest < ActiveModel::TestCase end def test_errors_full_messages_uses_model_format + ActiveModel::Errors.i18n_full_message = true + I18n.backend.store_translations("en", activemodel: { errors: { models: { person: { format: "%{message}" } } } }) @@ -61,6 +80,8 @@ class I18nValidationTest < ActiveModel::TestCase end def test_errors_full_messages_uses_deeply_nested_model_attributes_format + ActiveModel::Errors.i18n_full_message = true + I18n.backend.store_translations("en", activemodel: { errors: { models: { 'person/contacts/addresses': { attributes: { street: { format: "%{message}" } } } } } }) @@ -70,6 +91,8 @@ class I18nValidationTest < ActiveModel::TestCase end def test_errors_full_messages_uses_deeply_nested_model_model_format + ActiveModel::Errors.i18n_full_message = true + I18n.backend.store_translations("en", activemodel: { errors: { models: { 'person/contacts/addresses': { format: "%{message}" } } } }) diff --git a/guides/source/configuring.md b/guides/source/configuring.md index 7d5ca4b8a7..49c5b0406f 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -305,6 +305,10 @@ All these configuration options are delegated to the `I18n` library. config.i18n.fallbacks.map = { az: :tr, da: [:de, :en] } ``` +### Configuring Active Model + +* `config.active_model.i18n_full_message` is a boolean value which controls whether the `full_message` error format can be overridden at the attribute or model level in the locale files + ### Configuring Active Record `config.active_record` includes a variety of configuration options: