Skip to content

Translating Validation Messages

Validation rules still stay on the fields themselves, for example via options.rules. The text of validation error messages is controlled through the system translations.

This is especially useful for rules like required or required_if, where the default messages are often too technical.

There are three levels for custom validation messages:

  1. global for the entire system language
  2. module-wide for all fields of one module
  3. field-specific for one concrete rule on one concrete field

Resolution always goes from the most specific entry to the most general one.

When Brezel resolves an error message for a field and a rule, it checks in this order:

  1. modules.<module>.validation.fields.<field>.<rule>
  2. modules.<module>.validation.rules.<rule>
  3. validation.<rule>
  4. Laravel default behavior, for example resources/lang/<locale>/validation.php

Example for offers.send_end_of_day_method with the required_if rule:

  1. modules.offers.validation.fields.send_end_of_day_method.required_if
  2. modules.offers.validation.rules.required_if
  3. validation.required_if
  4. Laravel default message

This level is useful when a rule should be phrased similarly across the whole system language.

{
"validation": {
"required": "Please fill in “:attribute”.",
"required_if": "The field “:attribute” is required once “:other” is enabled."
}
}

These texts act as the language-wide default for the whole system as long as no more specific definition exists.

This level is useful when one module needs its own wording that should apply to multiple fields.

{
"modules": {
"offers": {
"validation": {
"rules": {
"required": "In the offers module, “:attribute” must be filled in.",
"required_if": "The field “:attribute” is required once “:other” is enabled."
}
}
}
}
}

These texts apply to all fields in the offers module if there is no more specific field-level message.

This level is useful when a single field needs especially good or domain-specific wording.

{
"modules": {
"offers": {
"validation": {
"fields": {
"send_end_of_day_method": {
"required_if": "Please choose a delivery method once “:other” is enabled."
}
}
}
}
}
}

This entry overrides both the module-wide and the global message for exactly this combination of field and rule.

Brezel uses the field translations from the module for :attribute and related placeholders.

Example:

{
"modules": {
"offers": {
"fields": {
"send_end_of_day": "Zusammenfassung um 18:00",
"send_end_of_day_method": "Weg",
"send_end_of_day_recipient": "Empfänger"
}
}
}
}

Because of this, a generic message like

The field “:attribute” is required once “:other” is enabled.

becomes, at runtime, something like:

The field “Weg” is required once “Zusammenfassung um 18:00” is enabled.
{
"buttons": {},
"validation": {
"required": "Please fill in “:attribute”.",
"required_if": "Global: The field “:attribute” is required once “:other” is enabled."
},
"modules": {
"offers": {
"fields": {
"send_end_of_day": "Zusammenfassung um 18:00",
"send_end_of_day_method": "Weg",
"send_end_of_day_recipient": "Empfänger"
},
"validation": {
"rules": {
"required_if": "Module-wide: The field “:attribute” is required once “:other” is enabled."
},
"fields": {
"send_end_of_day_method": {
"required_if": "Please choose a delivery method once “:other” is enabled."
}
}
}
}
}
}

Result:

  1. send_end_of_day_method.required_if uses the field-specific message.
  2. other required_if rules inside the offers module use the module-wide message.
  3. required_if in other modules uses the global message.
  4. if none of those exist, Laravel falls back to its previous default message.

In practice, this structure works well:

  1. validation.<rule> for good language-wide defaults
  2. modules.<module>.validation.rules.<rule> for module-specific wording
  3. modules.<module>.validation.fields.<field>.<rule> only where truly custom wording is needed

This keeps translations consistent without overloading individual module definitions with lots of message text.