DEV: Correctly pluralize error messages (#26469)

This commit is contained in:
Gerhard Schlager 2024-04-04 15:02:09 +02:00 committed by GitHub
parent 79cf7c0935
commit 82c62fe44f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 88 additions and 42 deletions

View File

@ -88,7 +88,7 @@ class UsernameValidator
return unless errors.empty? return unless errors.empty?
if username_grapheme_clusters.size < User.username_length.begin if username_grapheme_clusters.size < User.username_length.begin
self.errors << I18n.t(:"user.username.short", min: User.username_length.begin) self.errors << I18n.t(:"user.username.short", count: User.username_length.begin)
end end
end end
@ -96,7 +96,7 @@ class UsernameValidator
return unless errors.empty? return unless errors.empty?
if username_grapheme_clusters.size > User.username_length.end if username_grapheme_clusters.size > User.username_length.end
self.errors << I18n.t(:"user.username.long", max: User.username_length.end) self.errors << I18n.t(:"user.username.long", count: User.username_length.end)
elsif username.length > MAX_CHARS elsif username.length > MAX_CHARS
self.errors << I18n.t(:"user.username.too_long") self.errors << I18n.t(:"user.username.too_long")
end end

View File

@ -134,8 +134,12 @@ en:
number_value_not_valid_min: "Value must be larger than or equal to %{min}." number_value_not_valid_min: "Value must be larger than or equal to %{min}."
number_value_not_valid_max: "Value must be smaller than or equal to %{max}." number_value_not_valid_max: "Value must be smaller than or equal to %{max}."
string_value_not_valid_min_max: "Value must be between %{min} and %{max} characters long." string_value_not_valid_min_max: "Value must be between %{min} and %{max} characters long."
string_value_not_valid_min: "Value must be at least %{min} characters long." string_value_not_valid_min:
string_value_not_valid_max: "Value must be at most %{max} characters long." one: "Value must be at least %{count} character long."
other: "Value must be at least %{count} characters long."
string_value_not_valid_max:
one: "Value must be at most %{count} character long."
other: "Value must be at most %{count} characters long."
objects: objects:
humanize_required: "The property at JSON Pointer '%{property_json_pointer}' must be present." humanize_required: "The property at JSON Pointer '%{property_json_pointer}' must be present."
required: "must be present" required: "must be present"
@ -159,10 +163,18 @@ en:
humanize_not_valid_categories_value: "The property at JSON Pointer '%{property_json_pointer}' must be an array of valid category ids." humanize_not_valid_categories_value: "The property at JSON Pointer '%{property_json_pointer}' must be an array of valid category ids."
not_valid_categories_value: "must be an array of valid category ids" not_valid_categories_value: "must be an array of valid category ids"
humanize_categories_value_not_valid_min: "The property at JSON Pointer '%{property_json_pointer}' must have at least %{min} category ids." humanize_categories_value_not_valid_min:
categories_value_not_valid_min: "must have at least %{min} category ids" one: "The property at JSON Pointer '%{property_json_pointer}' must have at least %{count} category id."
humanize_categories_value_not_valid_max: "The property at JSON Pointer '%{property_json_pointer}' must have at most %{max} category ids." other: "The property at JSON Pointer '%{property_json_pointer}' must have at least %{count} category ids."
categories_value_not_valid_max: "must have at most %{max} category ids" categories_value_not_valid_min:
one: "must have at least %{count} category id"
other: "must have at least %{count} category ids"
humanize_categories_value_not_valid_max:
one: "The property at JSON Pointer '%{property_json_pointer}' must have at most %{count} category id."
other: "The property at JSON Pointer '%{property_json_pointer}' must have at most %{count} category ids."
categories_value_not_valid_max:
one: "must have at most %{count} category id"
other: "must have at most %{count} category ids"
humanize_not_valid_topic_value: "The property at JSON Pointer '%{property_json_pointer}' must be a valid topic id." humanize_not_valid_topic_value: "The property at JSON Pointer '%{property_json_pointer}' must be a valid topic id."
not_valid_topic_value: "must be a valid topic id" not_valid_topic_value: "must be a valid topic id"
@ -172,25 +184,49 @@ en:
humanize_not_valid_groups_value: "The property at JSON Pointer '%{property_json_pointer}' must be an array of valid group ids." humanize_not_valid_groups_value: "The property at JSON Pointer '%{property_json_pointer}' must be an array of valid group ids."
not_valid_groups_value: "must be an array of valid group ids" not_valid_groups_value: "must be an array of valid group ids"
humanize_groups_value_not_valid_min: "The property at JSON Pointer '%{property_json_pointer}' must have at least %{min} group ids." humanize_groups_value_not_valid_min:
groups_value_not_valid_min: "must have at least %{min} group ids" one: "The property at JSON Pointer '%{property_json_pointer}' must have at least %{count} group id."
humanize_groups_value_not_valid_max: "The property at JSON Pointer '%{property_json_pointer}' must have at most %{max} group ids." other: "The property at JSON Pointer '%{property_json_pointer}' must have at least %{count} group ids."
groups_value_not_valid_max: "must have at most %{max} group ids" groups_value_not_valid_min:
one: "must have at least %{count} group id"
other: "must have at least %{count} group ids"
humanize_groups_value_not_valid_max:
one: "The property at JSON Pointer '%{property_json_pointer}' must have at most %{count} group id."
other: "The property at JSON Pointer '%{property_json_pointer}' must have at most %{count} group ids."
groups_value_not_valid_max:
one: "must have at most %{count} group id"
other: "must have at most %{count} group ids"
humanize_not_valid_tags_value: "The property at JSON Pointer '%{property_json_pointer}' must be an array of valid tag names." humanize_not_valid_tags_value: "The property at JSON Pointer '%{property_json_pointer}' must be an array of valid tag names."
not_valid_tags_value: "must be an array of valid tag names" not_valid_tags_value: "must be an array of valid tag names"
humanize_tags_value_not_valid_min: "The property at JSON Pointer '%{property_json_pointer}' must have at least %{min} tag names." humanize_tags_value_not_valid_min:
tags_value_not_valid_min: "must have at least %{min} tag names" one: "The property at JSON Pointer '%{property_json_pointer}' must have at least %{count} tag name."
humanize_tags_value_not_valid_max: "The property at JSON Pointer '%{property_json_pointer}' must have at most %{max} tag names." other: "The property at JSON Pointer '%{property_json_pointer}' must have at least %{count} tag names."
tags_value_not_valid_max: "must have at most %{max} tag names" tags_value_not_valid_min:
one: "must have at least %{count} tag name"
other: "must have at least %{count} tag names"
humanize_tags_value_not_valid_max:
one: "The property at JSON Pointer '%{property_json_pointer}' must have at most %{count} tag name."
other: "The property at JSON Pointer '%{property_json_pointer}' must have at most %{count} tag names."
tags_value_not_valid_max:
one: "must have at most %{count} tag name"
other: "must have at most %{count} tag names"
humanize_not_valid_upload_value: "The property at JSON Pointer '%{property_json_pointer}' must be a valid upload id." humanize_not_valid_upload_value: "The property at JSON Pointer '%{property_json_pointer}' must be a valid upload id."
not_valid_upload_value: "must be a valid upload id" not_valid_upload_value: "must be a valid upload id"
humanize_string_value_not_valid_min: "The property at JSON Pointer '%{property_json_pointer}' must be at least %{min} characters long." humanize_string_value_not_valid_min:
string_value_not_valid_min: "must be at least %{min} characters long" one: "The property at JSON Pointer '%{property_json_pointer}' must be at least %{count} character long."
humanize_string_value_not_valid_max: "The property at JSON Pointer '%{property_json_pointer}' must be at most %{max} characters long." other: "The property at JSON Pointer '%{property_json_pointer}' must be at least %{count} characters long."
string_value_not_valid_max: "must be at most %{max} characters long" string_value_not_valid_min:
one: "must be at least %{count} character long"
other: "must be at least %{count} characters long"
humanize_string_value_not_valid_max:
one: "The property at JSON Pointer '%{property_json_pointer}' must be at most %{count} character long."
other: "The property at JSON Pointer '%{property_json_pointer}' must be at most %{count} characters long."
string_value_not_valid_max:
one: "must be at most %{count} character long"
other: "must be at most %{count} characters long"
humanize_number_value_not_valid_min: "The property at JSON Pointer '%{property_json_pointer}' must be larger than or equal to %{min}." humanize_number_value_not_valid_min: "The property at JSON Pointer '%{property_json_pointer}' must be larger than or equal to %{min}."
number_value_not_valid_min: "must be larger than or equal to %{min}" number_value_not_valid_min: "must be larger than or equal to %{min}"
@ -240,7 +276,9 @@ en:
format: ! "%{attribute} %{message}" format: ! "%{attribute} %{message}"
format_with_full_message: "<b>%{attribute}</b>: %{message}" format_with_full_message: "<b>%{attribute}</b>: %{message}"
messages: messages:
too_long_validation: "is limited to %{max} characters; you entered %{length}." too_long_validation:
one: "is limited to %{count} character; you entered %{length}."
other: "is limited to %{count} characters; you entered %{length}."
invalid_boolean: "Invalid boolean." invalid_boolean: "Invalid boolean."
taken: "has already been taken" taken: "has already been taken"
accepted: must be accepted accepted: must be accepted
@ -2604,8 +2642,12 @@ en:
must_include_latest: "Top menu must include the 'latest' tab." must_include_latest: "Top menu must include the 'latest' tab."
invalid_string: "Invalid value." invalid_string: "Invalid value."
invalid_string_min_max: "Must be between %{min} and %{max} characters." invalid_string_min_max: "Must be between %{min} and %{max} characters."
invalid_string_min: "Must be at least %{min} characters." invalid_string_min:
invalid_string_max: "Must be no more than %{max} characters." one: "Must be at least %{count} character."
other: "Must be at least %{count} characters."
invalid_string_max:
one: "Must be no more than %{count} character."
other: "Must be no more than %{count} characters."
invalid_json: "Invalid JSON." invalid_json: "Invalid JSON."
invalid_reply_by_email_address: "Value must contain '%{reply_key}' and be different from the notification email." invalid_reply_by_email_address: "Value must contain '%{reply_key}' and be different from the notification email."
invalid_alternative_reply_by_email_addresses: "All values must contain '%{reply_key}' and be different from the notification email." invalid_alternative_reply_by_email_addresses: "All values must contain '%{reply_key}' and be different from the notification email."
@ -2897,8 +2939,12 @@ en:
new_user_typed_too_fast: "New user typed too fast" new_user_typed_too_fast: "New user typed too fast"
content_matches_auto_silence_regex: "Content matches auto silence regex" content_matches_auto_silence_regex: "Content matches auto silence regex"
username: username:
short: "must be at least %{min} characters" short:
long: "must be no more than %{max} characters" one: "must be at least %{count} character"
other: "must be at least %{count} characters"
long:
one: "must be no more than %{count} character"
other: "must be no more than %{count} characters"
too_long: "is too long" too_long: "is too long"
characters: "must only include numbers, letters, dashes, dots, and underscores" characters: "must only include numbers, letters, dashes, dots, and underscores"
unique: "must be unique" unique: "must be unique"

View File

@ -169,22 +169,22 @@ class ThemeSettingsObjectValidator
end end
if (min = validations&.dig(:min)) && value.length < min if (min = validations&.dig(:min)) && value.length < min
add_error(property_name, :"#{type}_value_not_valid_min", min:) add_error(property_name, :"#{type}_value_not_valid_min", count: min)
return false return false
end end
if (max = validations&.dig(:max)) && value.length > max if (max = validations&.dig(:max)) && value.length > max
add_error(property_name, :"#{type}_value_not_valid_max", max:) add_error(property_name, :"#{type}_value_not_valid_max", count: max)
return false return false
end end
when "string" when "string"
if (min = validations&.dig(:min_length)) && value.length < min if (min = validations&.dig(:min_length)) && value.length < min
add_error(property_name, :string_value_not_valid_min, min:) add_error(property_name, :string_value_not_valid_min, count: min)
return false return false
end end
if (max = validations&.dig(:max_length)) && value.length > max if (max = validations&.dig(:max_length)) && value.length > max
add_error(property_name, :string_value_not_valid_max, max:) add_error(property_name, :string_value_not_valid_max, count: max)
return false return false
end end

View File

@ -41,9 +41,9 @@ class StringSettingValidator
if @opts[:min] && @opts[:max] if @opts[:min] && @opts[:max]
I18n.t("site_settings.errors.invalid_string_min_max", min: @opts[:min], max: @opts[:max]) I18n.t("site_settings.errors.invalid_string_min_max", min: @opts[:min], max: @opts[:max])
elsif @opts[:min] elsif @opts[:min]
I18n.t("site_settings.errors.invalid_string_min", min: @opts[:min]) I18n.t("site_settings.errors.invalid_string_min", count: @opts[:min])
else else
I18n.t("site_settings.errors.invalid_string_max", max: @opts[:max]) I18n.t("site_settings.errors.invalid_string_max", count: @opts[:max])
end end
elsif @json_fail elsif @json_fail
I18n.t("site_settings.errors.invalid_json") I18n.t("site_settings.errors.invalid_json")

View File

@ -8,7 +8,7 @@ class StrippedLengthValidator < ActiveModel::EachValidator
record.errors.add attribute, record.errors.add attribute,
I18n.t( I18n.t(
"errors.messages.too_long_validation", "errors.messages.too_long_validation",
max: range.end, count: range.end,
length: value.length, length: value.length,
) )
else else

View File

@ -777,7 +777,7 @@ RSpec.describe ThemeSettingsObjectValidator do
expect(errors.keys).to eq(["/tags_property"]) expect(errors.keys).to eq(["/tags_property"])
expect(errors["/tags_property"].full_messages).to contain_exactly( expect(errors["/tags_property"].full_messages).to contain_exactly(
"must have at least 1 tag names", "must have at least 1 tag name",
) )
errors = errors =
@ -912,7 +912,7 @@ RSpec.describe ThemeSettingsObjectValidator do
expect(errors.keys).to eq(["/group_property"]) expect(errors.keys).to eq(["/group_property"])
expect(errors["/group_property"].full_messages).to contain_exactly( expect(errors["/group_property"].full_messages).to contain_exactly(
"must have at least 1 group ids", "must have at least 1 group id",
) )
errors = errors =
@ -1154,7 +1154,7 @@ RSpec.describe ThemeSettingsObjectValidator do
expect(errors.keys).to eq(["/category_property"]) expect(errors.keys).to eq(["/category_property"])
expect(errors["/category_property"].full_messages).to contain_exactly( expect(errors["/category_property"].full_messages).to contain_exactly(
"must have at least 1 category ids", "must have at least 1 category id",
) )
end end

View File

@ -29,7 +29,7 @@ RSpec.describe ThemeSettingsValidator do
) )
expect(errors).to contain_exactly( expect(errors).to contain_exactly(
"The property at JSON Pointer '/0/name' must be at most 1 characters long.", "The property at JSON Pointer '/0/name' must be at most 1 character long.",
) )
end end
end end

View File

@ -47,7 +47,7 @@ RSpec.describe UsernameValidator do
expect_invalid( expect_invalid(
*usernames, *usernames,
error_message: I18n.t(:"user.username.short", min: min_username_length), error_message: I18n.t(:"user.username.short", count: min_username_length),
) )
end end
@ -62,7 +62,7 @@ RSpec.describe UsernameValidator do
expect_invalid( expect_invalid(
"a" * (max_username_length + 1), "a" * (max_username_length + 1),
error_message: I18n.t(:"user.username.long", max: max_username_length), error_message: I18n.t(:"user.username.long", count: max_username_length),
failure_reason: "Should be invalid as username length > #{max_username_length}", failure_reason: "Should be invalid as username length > #{max_username_length}",
) )
end end
@ -154,7 +154,7 @@ RSpec.describe UsernameValidator do
expect_invalid( expect_invalid(
*usernames, *usernames,
error_message: I18n.t(:"user.username.short", min: min_username_length), error_message: I18n.t(:"user.username.short", count: min_username_length),
) )
end end
@ -170,7 +170,7 @@ RSpec.describe UsernameValidator do
expect_invalid( expect_invalid(
"ם" * (max_username_length + 1), "ם" * (max_username_length + 1),
"äl" * (max_username_length + 1), "äl" * (max_username_length + 1),
error_message: I18n.t(:"user.username.long", max: max_username_length), error_message: I18n.t(:"user.username.long", count: max_username_length),
failure_reason: "Should be invalid as username length are > #{max_username_length}", failure_reason: "Should be invalid as username length are > #{max_username_length}",
) )
end end

View File

@ -1739,7 +1739,7 @@ RSpec.describe UsersController do
body = response.parsed_body body = response.parsed_body
expect(body["errors"].first).to include( expect(body["errors"].first).to include(
I18n.t("user.username.short", min: User.username_length.begin), I18n.t("user.username.short", count: User.username_length.begin),
) )
expect(user.reload.username).to eq(old_username) expect(user.reload.username).to eq(old_username)
@ -1893,7 +1893,7 @@ RSpec.describe UsersController do
it 'should return the "too long" message' do it 'should return the "too long" message' do
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(response.parsed_body["errors"]).to include( expect(response.parsed_body["errors"]).to include(
I18n.t(:"user.username.long", max: SiteSetting.max_username_length), I18n.t(:"user.username.long", count: SiteSetting.max_username_length),
) )
end end
end end