Hey everyone! Today, I want to share a quick tip about Laravel's Lang::handleMissingKeysUsing
method. If you haven't heard of it, no worries – it's a neat feature that gets triggered when a translation key is missing.
The catch? Well, it tends to fire every time validation error messages are generated. Let's take a peek at the method responsible for this:
/**
* Get the validation message for an attribute and rule.
*
* @param string $attribute
* @param string $rule
* @return string
*/
protected function getMessage($attribute, $rule)
{
$attributeWithPlaceholders = $attribute;
$attribute = $this->replacePlaceholderInString($attribute);
$inlineMessage = $this->getInlineMessage($attribute, $rule);
// First we will retrieve the custom message for the validation rule if one
// exists. If a custom validation message is being used we'll return the
// custom message, otherwise we'll keep searching for a valid message.
if (! is_null($inlineMessage)) {
return $inlineMessage;
}
$lowerRule = Str::snake($rule);
$customKey = "validation.custom.{$attribute}.{$lowerRule}";
$customMessage = $this->getCustomMessageFromTranslator(
in_array($rule, $this->sizeRules)
? [$customKey.".{$this->getAttributeType($attribute)}", $customKey]
: $customKey
);
// First we check for a custom defined validation message for the attribute
// and rule. This allows the developer to specify specific messages for
// only some attributes and rules that need to get specially formed.
if ($customMessage !== $customKey) {
return $customMessage;
}
// If the rule being validated is a "size" rule, we will need to gather the
// specific error message for the type of attribute being validated such
// as a number, file or string which all have different message types.
elseif (in_array($rule, $this->sizeRules)) {
return $this->getSizeMessage($attributeWithPlaceholders, $rule);
}
// Finally, if no developer specified messages have been set, and no other
// special messages apply for this rule, we will just pull the default
// messages out of the translator service for this validation rule.
$key = "validation.{$lowerRule}";
if ($key !== ($value = $this->translator->get($key))) {
return $value;
}
return $this->getFromLocalArray(
$attribute, $lowerRule, $this->fallbackMessages
) ?: $key;
}
Alright, that's a pretty lengthy method. In a nutshell, it checks if a custom validation rule exists. If not, it looks for a general rule, and if all else fails, it falls back to a default message. The problem? Every time a translation is missing, Lang::handleMissingKeysUsing gets activated – not exactly what we want, right?
Below is my solution:
if (Str::of($key)->startsWith('validation.custom.') ||
Str::of($key)->startsWith('validation.values.')) {
return;
}
And here's the complete AppServiceProvider class, the place where my Lang::handleMissingKeysUsing
is located:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
Lang::handleMissingKeysUsing(function (string $key, array $replacements, string $locale) {
if (! App::environment(['production', 'staging'])) {
return;
}
if (Str::of($key)->startsWith('validation.custom.') ||
Str::of($key)->startsWith('validation.values.')) {
return;
}
// be notified of missing translation
return $key;
});
}
}
That's all there is to it. Cheers!