If you’ve ever watched a TV show like “Ramsay’s Kitchen Nightmares” or similar then you’re probably aware of how important it is to label your food when you store it in a freezer. You might be wondering how this relates to software development? Just like food, forms also have labels.

In the past months I’ve worked on several multilingual projects and one of the recurring themes has been how to structure the translations for labels used in our form but while also keeping them consistent within our API endpoints.

I’m not here to promote the perfect solution and you should definitely decide on a convention with the team you’re working with. However, there is one particular part to it that is already solved by the Laravel framework and — you’ve guessed it right — it’s about form labels.

The problem

In a lot of codebases you see the following pattern occur when it comes to structuring translation files.

// feature_a.php
return [
  'title' => 'My Feature',
  'create' => [
        'title' => 'Create resource',
        'fields' => [
            'scheduled_for' => [
                'label' => 'Due date',
            ],
        ],
    ],
]

Which would then be applied in a form like this:

<form>
    <label for="scheduled_for">{{ __('feature_a.create.fields.scheduled_for.label') }}</label>
    <input name="scheduled_for" id="scheduled_for" type="date" />
</form>

It’s a good solution — it is already structured so the next developers can follow the same pattern. However, a problem pops up when you introduce request validation.

$request->validate([
    'scheduled_for' => ['required', 'date_format:Y-m-d', 'after_or_equal:today''],
]);

When we submit this form, Laravel will try to create a human friendly version of the attribute name to use in the error message. This results in one of the following error messages:

The scheduled for field is required.
The scheduled for does not match the format Y-m-d.
The scheduled for must be a date after or equal to today.

This can appear confusing to our users because our label states “Due date”, so they might wonder which field this error relates to.

The solution

The framework actually comes with a solution for this problem already. It comes down to adding your attribute name to the validation.attributes array:

return [
'attributes' => [
'scheduled_for' => 'Due date'
],
];

This will result in the validator using this translation instead and as a result, users will be seeing more relevant error messages such as:

The Due date field is required.
The Due date does not match the format Y-m-d.
The Due date must be a date after or equal to today.

We can also make use of this key in our HTML template to avoid duplication:

<form>
    <label for="scheduled_for">{{ __('validation.attributes.scheduled_for') }}</label>
    <input name="scheduled_for" id="scheduled_for" type="date" />
</form>

It’s the small, lesser-known features like this that make Laravel one of my favorite frameworks to work with. Combining this with other goodies such as specifying translations for values or specifying messages for certain attribute/rule combinations can really help keep your translations under control.