diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index e9a61daab234307b05a8f80c8d466efea1c94493..99f47f2cbeddf9e099f3b1dc941d6f411a8df530 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -266,8 +266,8 @@ def generate_message(attribute, type = :invalid, options = {}) type = options.delete(:message) if options[:message].is_a?(Symbol) defaults = @base.class.lookup_ancestors.map do |klass| - [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.underscore}.attributes.#{attribute}.#{type}", - :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.underscore}.#{type}" ] + [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}", + :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ] end defaults << options.delete(:message) diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb index 61e1632088ff99d815e6b401bf6fdb02605b0eaf..0706df4b5907b64fe1d1a7eaed1a64c8bd2f264b 100644 --- a/activemodel/lib/active_model/naming.rb +++ b/activemodel/lib/active_model/naming.rb @@ -2,7 +2,7 @@ module ActiveModel class Name < String - attr_reader :singular, :plural, :element, :collection, :partial_path, :route_key, :param_key + attr_reader :singular, :plural, :element, :collection, :partial_path, :route_key, :param_key, :i18n_key alias_method :cache_key, :collection def initialize(klass, namespace = nil) @@ -18,6 +18,7 @@ def initialize(klass, namespace = nil) @partial_path = "#{@collection}/#{@element}".freeze @param_key = (namespace ? _singularize(@unnamespaced) : @singular).freeze @route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural).freeze + @i18n_key = _singularize(self, '.').to_sym end # Transform the model name into a more humane format, using I18n. By default, @@ -31,7 +32,7 @@ def human(options={}) @klass.respond_to?(:i18n_scope) defaults = @klass.lookup_ancestors.map do |klass| - klass.model_name.underscore.to_sym + klass.model_name.i18n_key end defaults << options.delete(:default) if options[:default] @@ -42,9 +43,10 @@ def human(options={}) end private - def _singularize(str) - ActiveSupport::Inflector.underscore(str).tr('/', '_') - end + + def _singularize(string, replacement='_') + ActiveSupport::Inflector.underscore(string).tr('/', replacement) + end end # == Active Model Naming @@ -60,6 +62,9 @@ def _singularize(str) # BookCover.model_name # => "BookCover" # BookCover.model_name.human # => "Book cover" # + # BookCover.model_name.i18n_key # => "book_cover" + # BookModule::BookCover.model_name.i18n_key # => "book_module.book_cover" + # # Providing the functionality that ActiveModel::Naming provides in your object # is required to pass the Active Model Lint test. So either extending the provided # method below, or rolling your own is required.. diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb index dbb76244e48828bb5eca92176427d6910d87a40d..920a133159c5a783d10f2a5a10e8aaa8802e5f48 100644 --- a/activemodel/lib/active_model/translation.rb +++ b/activemodel/lib/active_model/translation.rb @@ -44,7 +44,7 @@ def lookup_ancestors # Specify +options+ with additional translating options. def human_attribute_name(attribute, options = {}) defaults = lookup_ancestors.map do |klass| - :"#{self.i18n_scope}.attributes.#{klass.model_name.underscore}.#{attribute}" + :"#{self.i18n_scope}.attributes.#{klass.model_name.i18n_key}.#{attribute}" end defaults << :"attributes.#{attribute}" diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb index e9f0e430fe9c93aeb81fe803e35206069faf4e40..5cb7bff4e76e6eedc155d3d5f0148b5354324d86 100644 --- a/activemodel/test/cases/validations/i18n_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_validation_test.rb @@ -55,6 +55,14 @@ def test_errors_full_messages_translates_human_attribute_name_for_model_attribut assert_equal ["Person's name not found"], @person.errors.full_messages end + def test_errors_full_messages_translates_human_attribute_name_for_model_in_module_attributes + I18n.backend.store_translations('en', :activemodel => {:attributes => {:person_module => {:person => {:name => "Person in Module's name"}}}}) + person = PersonModule::Person.new + person.errors.add(:name, 'not found') + PersonModule::Person.expects(:human_attribute_name).with(:name, :default => 'Name').returns("Person in Module's name") + assert_equal ["Person in Module's name not found"], person.errors.full_messages + end + def test_errors_full_messages_uses_format I18n.backend.store_translations('en', :errors => {:format => "Field %{attribute} %{message}"}) @person.errors.add('name', 'empty') @@ -363,4 +371,15 @@ def test_validates_with_message_string assert_equal ["I am a custom error"], @person.errors[:title] end + def test_model_with_module_i18n_scope + I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person_module => {:person => {:blank => 'generic blank'}}}}} + PersonModule::Person.validates_presence_of :title + person = PersonModule::Person.new + person.valid? + assert_equal ['generic blank'], person.errors[:title] + + I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person_module => {:person => {:attributes => {:title => {:blank => 'title cannot be blank'}}}}}}} + person.valid? + assert_equal ['title cannot be blank'], person.errors[:title] + end end diff --git a/activemodel/test/models/person.rb b/activemodel/test/models/person.rb index cf16a38618c839bff67610593344cade39a0857c..eb84f7a27d428533f8da66f2c6b3c77258858c8f 100644 --- a/activemodel/test/models/person.rb +++ b/activemodel/test/models/person.rb @@ -11,3 +11,8 @@ def condition_is_true class Child < Person end + +module PersonModule + class Person < ::Person + end +end