From 6c1240867ce06051b6799f966278eaf9dda1e103 Mon Sep 17 00:00:00 2001 From: Azmi Muwahid <92735014+azmi2409@users.noreply.github.com> Date: Thu, 8 Oct 2026 14:50:42 +0700 Subject: [PATCH] Fix #5864: Keep original casing of multi-word authentication keys in flash messages Since #5822, human_attribute_name values are downcased on the first letter for the failure flash message. For multi-word title-cased names like "Company Code" this produces "company Code", which reads like a typo. Only apply downcase_first to single-word names ("Email" -> "email"), preserving the original casing of multi-word names ("Company Code"). --- lib/devise/failure_app.rb | 9 ++++++++- test/failure_app_test.rb | 9 +++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/devise/failure_app.rb b/lib/devise/failure_app.rb index 70cf6d2f31..6b64894e2e 100644 --- a/lib/devise/failure_app.rb +++ b/lib/devise/failure_app.rb @@ -114,7 +114,14 @@ def i18n_message(default = nil) human_keys = (auth_keys.respond_to?(:keys) ? auth_keys.keys : auth_keys).map { |key| # TODO: Remove the fallback and just use `downcase_first` once we drop support for Rails 7.0. human_key = scope_class.human_attribute_name(key) - human_key.respond_to?(:downcase_first) ? human_key.downcase_first : human_key[0].downcase + human_key[1..] + # Only downcase single-word names ("Email" -> "email"). Downcasing just + # the first letter of multi-word title-cased names ("Company Code" -> + # "company Code") reads like a typo, so those keep their original casing. + if human_key.match?(/\s/) + human_key + else + human_key.respond_to?(:downcase_first) ? human_key.downcase_first : human_key[0].downcase + human_key[1..] + end } options[:authentication_keys] = human_keys.join(I18n.t(:"support.array.words_connector")) options = i18n_options(options) diff --git a/test/failure_app_test.rb b/test/failure_app_test.rb index c9e4a56ce1..99d8842785 100644 --- a/test/failure_app_test.rb +++ b/test/failure_app_test.rb @@ -200,6 +200,15 @@ def call_failure(env_params = {}) assert_equal 'Invalid email or password.', @request.flash[:alert] end + test 'keeps original casing of multi-word authentication_keys in the flash message' do + store_translations :en, activerecord: { attributes: { user: { company_cd: 'Company Code' } } } do + swap Devise, authentication_keys: [:company_cd] do + call_failure('warden' => OpenStruct.new(message: :invalid)) + assert_equal 'Invalid Company Code or password.', @request.flash[:alert] + end + end + end + test 'humanizes the flash message' do call_failure('warden' => OpenStruct.new(message: :invalid)) assert_equal @request.flash[:alert], @request.flash[:alert].humanize