Laravel has built-in functionality for dealing with translations. You can store arrays for language strings inside
the resources/lang
folders and depending on your lcaole settings Laravel will display the correct language.
To use these translation strings in your blade template you can use the __()
function or the @lang
blade directive.
echo __('messages.welcome');
@lang('messages.welcome')
To find out more about translation strings you can view the documentation here.
The problem you have with using translation strings like this is for each language you need to make sure that you keep all the translation strings in sync. What if you forget to add one for certain languages?
When you use the __()
function Laravel will search for the key in your translation strings and return the value. If
Laravel can't find a matching key then it will just output the key on your page, this means you could
have messages.welcome
appear to your visitors and you may not even know about it.
How do we solve this problem?
In this tutorial we're going to make some changes to how Laravel finds your translation strings and will log any missing translation strings.
The __() Function
Laravel has a helper file helpers.php
which includes the __()
function. When we look at this code we can see exactly
how it works.
if (! function_exists('__')) {
/**
* Translate the given message.
*
* @param string $key
* @param array $replace
* @param string $locale
* @return string|array|null
*/
function __($key, $replace = [], $locale = null)
{
return app('translator')->getFromJson($key, $replace, $locale);
}
}
As you can see it will instantiate a translator class from the app container and then call the getFromJson()
function
to find the translation strings.
What we're going to do is override the translator
class then we can change the behaviour of the getFromJson()
function.
Translator Override Class
First we need to create a new class that will extend the Illuminate\Translation\Translator
class.
If you look inside the getFromJson()
function you'll see the internally this will call the translator get()
method.
This is then used to look up the translation based off the key you send it. Therefore we're going to override
this get()
function in our new JSON Translator class.
Inside our get()
method we need to first call the parent get method.
$translation = parent::get($key, $replace, $locale, $fallback);
If the $translation
variable is the same as the $key
then the translation was not found and we need to log that it
wasn't found.
if ($translation === $key) {
// Log missing translation string
}
<?php
namespace App\Translator;
use Illuminate\Support\Facades\Log;
use Illuminate\Translation\Translator as BaseTranslator;
/**
* Class JsonTranslator
* @package App\Translator
*/
class JsonTranslator extends BaseTranslator
{
/**
* @param string $key
* @param array $replace
* @param null $locale
* @param bool $fallback
*
* @return array|null|string|void
*/
public function get($key, array $replace = [], $locale = null, $fallback = true)
{
$translation = parent::get($key, $replace, $locale, $fallback);
if ($translation === $key) {
Log::warning('Language item could not be found.', [
'language' => $locale ?? config('app.locale'),
'id' => $key,
'url' => config('app.url')
]);
}
return $translation;
}
}
Override the Default Laravel Translator
To override the default Laravel translator we need to add some code to our AppServiceProvider.php
file.
In the register method we can hook into the $this->app
container and extend the translator
key object.
From this we can take the existing translator class and return our new JsonTranslator class passing in the selected locale.
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Translation\Translator;
use App\Translator\JsonTranslator;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
}
/**
* Register any application services.
*
* @return void
*/
public function register()
{
// Override the JSON Translator
$this->app->extend('translator', function (Translator $translator) {
$trans = new JsonTranslator($translator->getLoader(), $translator->getLocale());
$trans->setFallback($translator->getFallback());
return $trans;
});
}
}
Logging
Now when you visit your application and it has missing translation strings you'll notice there is new information in your log file telling you what translation is missing and the locale.
Laravel 10.x or more
In the latest versions of Laravel this feature is not built in, you can achieve the above by using the following code inside you AppServiceProvider.php file.
use Illuminate\Support\Facades\Lang;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Lang::handleMissingKeysUsing(function (string $key, array $replacements, string $locale) {
info("Missing translation key [$key] detected.");
return $key;
});
}