Laravel Log Missing Translation Strings

Paulund - Apr 23 - - Dev Community

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')
Enter fullscreen mode Exit fullscreen mode

To find out more about translation strings you can view the documentation here.

Localization

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode
<?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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
    });
}
Enter fullscreen mode Exit fullscreen mode
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .