Back to Course |
Multi-Language Laravel 11: All You Need to Know

Translating Validation Messages

In Laravel, majority of validation error messages are good enough:

However, if you change the label, the validation message does not match the label:

Let's see how we can fix this.


Label and Field Name Mismatch - Translated Form Attributes

As the application grows, you'll have cases where the field name and label do not match. For example, I have a field called Order Date on the UI but the form has an ordered_at name:

Blade form

<div class="mt-4">
<x-input-label for="ordered_at" :value="__('Order date')"/>
 
<x-text-input id="ordered_at" class="block mt-1 w-full" type="date" name="ordered_at"
:value="old('ordered_at')"/>
 
<x-input-error :messages="$errors->get('ordered_at')" class="mt-2"/>
</div>

This will result in the following validation message:

The ordered at field is required.

It is not that user-friendly as our name is Order date. Let's fix this by adding the attributes method to our Form Request class:

Form Request Class

use Illuminate\Foundation\Http\FormRequest;
 
class StoreOrderRequest extends FormRequest
{
public function rules(): array
{
return [
'ordered_at' => ['required', 'date'],
// ...
];
}
 
// ...
 
public function attributes(): array
{
return [
'ordered_at' => 'order date',
];
}
}

With this, we've told that once we have a field named ordered_at - its visual representation should be order date. This will result in the following validation message:

The order date field is required.

To go even further, we can use a translation here:

Form Request Class

// ...
public function attributes(): array
{
return [
'ordered_at' => __('Order date'),
];
}

This will result in: The Order date field is required. - matching the label.


Validating Array of Fields

This one is tricky. You might see something like The field.0 is required. which is not very user-friendly. Let's see how we can fix this.

We will have a form with multiple fields:

And the following validation rules:

Form Request Class

class StoreOrderRequest extends FormRequest
{
public function rules(): array
{
return [
'user_id' => ['required', 'integer'],
'ordered_at' => ['required', 'date'],
'complete' => ['required'],
'products' => ['required', 'array'],
'products.*.name' => ['required', 'string'],
'products.*.quantity' => ['required', 'integer'],
];
}
}

We try to validate the products array and make sure that we have name and quantity fields. If we try to submit this form without any products - we'll see the following validation message:

And it doesn't look nice. We can fix this by adding the attributes method to our request class, with a * sign:

Form Request Class

class StoreOrderRequest extends FormRequest
{
public function rules(): array
{
return [
'user_id' => ['required', 'integer'],
'ordered_at' => ['required', 'date'],
'complete' => ['required'],
'products' => ['required', 'array'],
'products.*.name' => ['required', 'string'],
'products.*.quantity' => ['required', 'integer'],
];
}
 
public function attributes(): array
{
return [
'products.*.name' => __('product name'),
'products.*.quantity' => __('product quantity')
];
}
}

As you can see, we've set the following:

  • products.*.name - product name
  • products.*.quantity - product quantity

And now submitting the form without any products will result in the following validation message:

Much better! But there's more we can do. We can use the index of the array to make the message even more user-friendly. Let's see how:

Form Request Class

public function attributes(): array
{
return [
'products.*.name' => __('product :index name'),
'products.*.quantity' => __('product :index quantity')
];
}

Note that we've used :index in our text. This is a special placeholder that will be replaced with the index of the array. So if we have 3 products - we'll see the following validation message:

While it's not perfect in our scenario - it's a great example of how flexible this can be. To make sure that we don't display 0 as the index (not everyone is a developer and counts from 0!) we could actually use :position instead of :index:

Form Request Class

public function attributes(): array
{
return [
'products.*.name' => __('product :position name'),
'products.*.quantity' => __('product :position quantity')
];
}

Which will result in the following validation message:

This is way nicer, especially if you are displaying messages at the top of the form!


Using Custom Validation Messages

Another great feature of Laravel is the ability to use custom validation messages. This is especially useful when you have a lot of fields, and you want to have a custom message for each field. Let's see how we can do this.

We will use the messages method to define our custom messages:

Form Request Class

class StoreOrderRequest extends FormRequest
{
public function rules(): array
{
return [
'user_id' => ['required', 'integer'],
'ordered_at' => ['required', 'date'],
'complete' => ['required'],
'products' => ['required', 'array'],
'products.*.name' => ['required', 'string'],
'products.*.quantity' => ['required', 'integer'],
];
}
 
public function messages()
{
return [
'products.*.name.required' => __('Product :position is required'),
'products.*.quantity.required' => __('Quantity is required'),
];
}
}

Once we've defined our custom messages - we can submit the form without any products, and we'll see the following validation message:

Why did that happen? Let's dive a little deeper at the messages method:

  • Our key is the field name that we want to validate + the validation rule that we want to validate. In our case, we want to validate the products.*.name field with the required rule.
  • Our value is the message that we want to display. In our case, we want to display the Product :position is required message.

You don't have to use :position placeholder here. You can use any text that you want. By default, the message would look like this:

The :attribute field is required.

Let's add another example to our messages method - changing the message for the products.*.quantity field:

Request Class

public function messages()
{
return [
'products.*.name.required' => __('Product :position is required'),
'products.*.quantity.required' => __('Quantity is required'),
'products.*.quantity.integer' => __('Quantity has to be a number'),
];
}

Now if we submit the form with a quantity that is not a number - we'll see the following validation message:


Repository: https://github.com/laraveldaily/laravel11-localization-course/tree/lesson/complex-forms-validation