Ruby on Rails i18n - Want To Translate Custom Mess

2019-01-21 19:04发布

问题:

I have attributes with special validation where I use the message clause to display a special message just for that validation. Here is one example:

validates :email, presence:   true, length: { maximum: 60 },
                format:     { with: valid_email_regex, message: "is not a valid email address format." },
                uniqueness: { case_sensitive: false } 

I would like to translate the message here but I am not sure how to do it.

I have seen examples where they type something like this: message: t("some_value_here"). I'm not sure about the designation. I tried something like this message: t(:bad_email). I did the following in my yaml file just to try something.

activemodel:
  errors:
    bad_email: "is not a valid email address format."

When I tried to access my Rails application I got the following error:

ActionView::Template::Error (undefined method `t' for #<Class:0x007fefc1b709e0>)

I also tried this in my yaml file:

activemodel:
  errors:
    user:
      bad_email: "is not a valid email address format."

I have been researching this off and on all day long. All I can find is to replace built-in error hashes like blank or empty. Is there a way for me to have custom error hashes and replace them in the model? At this point I cannot get the t to work as coded. My hope is that the problem is how I have my yaml file set up. I have seen varying versions of how to set this up. I am not sure if I should put this under activemodel or activerecord. I assumed activemodel since that is where the custom message is that I want to translate.

Any help would be appreciated. This is the last piece I need to figure out before launching my first translation of the application.

UPDATE 7/29/2013 7:30 pm CDT

bgates gave me a very good start with how to setup my model files to receive the custom message in the YAML file. However I ended up having to do the following setup in my yaml file for the custom messages to be found.

activerecord:
  errors: 
    models: 
      user: 
        attributes: 
          bio: 
            no_links: "cannot contain email addresses or website links (URLs)."
          email: 
            bad_email: "is not a valid email address format."
          username: 
            bad_username: "can only contain numbers and letters.  No special characters or spaces."

回答1:

Use a symbol for the message:

validates :email, presence:   true, length: { maximum: 60 },
            format:     { with: valid_email_regex, message: :bad_email },
            uniqueness: { case_sensitive: false } 

then in the yaml file

[lang]:
  activerecord:
    errors:
      messages:
        bad_email: "just ain't right"

If there's a translation specific to this model, it will override the general one above:

[lang]:
  activerecord:
    errors:
      models:
        model_name: # or namespace/model_name
          attributes:
            email:
              bad_email: "model-specific message for invalid email"

If you write custom validations, add_error(:email, :bad_email) will do the lookup above, but errors[:email] << :bad_email will not.



回答2:

I just walked through all this and found the rails guides for custom validators too hard-coded... I'm posting this here even though it's not exactly what you asked, but the Q title fits perfectly (which is why I read this post for my problem).

Custom validation with i18n message:

validate :something_custom?, if: :some_trigger_condition

def something_custom?
  if some_error_condition
    errors.add(:some_field_key, :some_custom_msg)
  end
end

# en.yml
activerecord: 
  errors:
    models:
      some_model:
        some_custom_msg: "This is i18n controlled. yay!"


回答3:

The solution to this does not need to be custom; The format validator message already maps to the :invalid symbol. You need only set the invalid in translation.

en:
  activerecord:
    errors:
      models:
        some_model:
          attributes:
            email:
              invalid: "FOO"

Reference: http://edgeguides.rubyonrails.org/i18n.html#error-message-interpolation



回答4:

I will give a complete example.

To make your models cleaner, you can create a custom validation helper method in an entirely new directory - app/validations, which will be autoloaded by Rails.

I'll call my file time_validator.rb, located at app/validations/time_validator.rb

My file has the following code, which validates user-entered time - that it is actually time.

# frozen_string_literal: true

class TimeValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    Time.parse(value).strftime('%H:%M')
  rescue ArgumentError 
    record.errors.add(attribute, :invalid_time)
  end
end

You can read more about creating custom ActiveRecord validations here

Our main point of concern is the record.errors line. Note that it's attribute and not :attribute, where attribute is the column in your model.

:invalid_time is the key that retrieves your translated content from your locales file. In my case, this is the en file:

en
  activerecord:
    errors:
      models:
        home:
          attributes:
            check_in_time:
              invalid_time: Please enter valid time
            check_out_time:
              invalid_time: Please enter valid time

Then in the home model:

validates :check_in_time, time: { allow_blank: true }
validates :check_out_time, time: { allow_blank: true }

time automatically gets mapped to the class TimeValidator, and the methods therein get run.

Incase this is violated, ActiveRecord will throw an error right below the column name.

Hope this helps!