Using I18n in Rails Validations

Posted on

Rails model validations are one of the most expressive and powerful aspects which makes Rails one of my favourite frameworks.

One of the more lesser known aspects is that you can pass in a symbol, and Rails will lookup the appropriate message to return via I18n. You can even pass in interpolations for when you’d like to pass in something specific to your model.

Here is an example:

class User < ApplicationRecord
  # When using the `validates` method, only the translated model name,
  # translated attribute name and the value available for interpolation.
  # en.activerecord.errors.models.user.attributes.avatar.error_key:
  # "You've forgot the %{attribute} in %{model}"
  validates :avatar, presence: { message: :error_key }

  validate :validate_will_always_fail
  validate :validate_will_always_fail_with_interpolations

  def validate_will_always_fail
    # en.activerecord.errors.models.user.attributes.avatar.error_type:
    # "No interpolations here"
    errors.add(:avatar, :error_type)

  def validate_will_always_fail_with_interpolations
    # When using errors.add, you can pass in custom interpolations
    # en.activerecord.errors.models.user.attributes.base.another_error_type:
    # "Another error type: %{value}"
    errors.add(:base, :another_error_type, { value: name })

# So running:
user = 'Gary')
puts user.errors.messages.inspect

# Would output:
# {
#   avatar: ["No interpolations here", "You've forgot the Avatar in User"],
#   base: ["Another error type Gary"]
# }

Keep things standard

Using custom keys to be specific about what type of error occurred is super handy, plus having a “This will be translated in the future” will save you a lot of technical debt going forward. But it’s important to note Rails does ship with a bunch of standard validation messages, where possible you should consider the standard message keys.