Building a Laravel Translation Package – Handling Missing Translation Keys

Published on by

Building a Laravel Translation Package – Handling Missing Translation Keys image

In the last instalment of this series, we talked about building the frontend translation management tool. In this article, we are going to move away from the frontend and follow the process of building another backend feature.

One of the most frustrating things about translation management in a Laravel application is forgetting to add translations to the corresponding language file. This has the undesirable result of either the translation key or the default language being rendered on the page rather than the translation for the current language.

To mitigate this issue, the Laravel Translation package provides a way to scan a project for translations that don’t exist in the translation files for all languages supported by the application.

We achieve the scanning part of this process by recursively looking through all files of the configured directories for instances of any of the Laravel translation retrieval methods (e.g. __(), trans()). These methods are captured using regular expressions and the translation strings extracted. These strings can be compared against the existing translations to see whether or not they already exist or need to be created.

Configuration

In the package configuration, there are two keys defined to make the scanner more flexible and efficient.

'translation_methods' => ['trans', '__']

The translation_methods option allows the user to provide an array of translation retrieval methods which the scanner uses to look for keys.

'scan_paths' => [app_path(), resource_path()]

The scan_paths key allows the user to define an array of directories for the scanner to look through when searching for translation keys.

Of course, it’s possible to use base_path() here to search through the whole project, but defining only the directories where you expect to find translations will add significant speed improvements to the scanning process.

Implementation

The scanning functionality is handled by a single class. This class accepts an instance of Laravel’s Illuminate\Filesystem\Filesystem, which it uses to traverse directories and interact with files, along with the array of translation methods and scan paths outlined above.

The class contains a single method findTranslations, which is responsible for carrying out the task. It utilises the following regular expression which is influenced by a combination of those found in Barry vd. Heuvel’s Laravel Translation Manager package and Mohamed Said’s Laravel Language Manager package.

$matchingPattern =
'[^\w]'. // Must not start with any alphanum or _
'(?<!->)'. // Must not start with ->
'('.implode('|', $this->translationMethods).')'. // Must start with one of the functions
"\(". // Match opening parentheses
"[\'\"]". // Match " or '
'('. // Start a new group to match:
'.+'. // Must start with group
')'. // Close group
"[\'\"]". // Closing quote
"[\),]"; // Close parentheses or new parameter

The method recursively iterates over all of the files in the provided directories using the regular expression to find instances of the provided translation retrieval methods, returning an array of all of the matches.

On each match, an additional check is performed to determine whether the match is a group translation (php array style) or single translation (JSON style). This is done by simply checking whether or not the match contains a period. If so, everything before the period is the file and everything after is the key (e.g. you could find the translation for validation.accepted by getting the accepted key from the validation.php file).

In the end, we have an array which looks similar to the following:

[
'single' => [
'Welcome' => 'Welcome'
],
'group' => [
'validation' => [
'accepted' => 'The :attribute must be accepted.',
...
],
],
];

Of course, doing this will give us every translation found in the configured paths, so how do we go about determining those which are missing? It’s simple really, we now have all the tagged translations in the app in an array and we can use our file driver created earlier in the series to get all the translations in the language files in an array format.

Not only that, but the format of the two arrays will be the same, so all we need to do is diff the two. We can conclude that anything in the array of translations gathered from scanning the app which doesn’t appear in the language file translations needs to be added to the relevant language file.

To do this, we simply iterate over the missing translations and utilise the methods already built in the file driver to add them.

$missingTranslations = $this->findMissingTranslations($language);
 
if (isset($missingTranslations['single'])) {
foreach ($missingTranslations['single'] as $key => $value) {
$this->addSingleTranslation($language, $key);
}
}
 
if (isset($missingTranslations['group'])) {
foreach ($missingTranslations['group'] as $group => $keys) {
foreach ($keys as $key => $value) {
$this->addGroupTranslation($language, "{$group}.{$key}");
}
}
}

This article brings us to the end of the file-based functionality of the translation package. Next time, we will utilise the groundwork we have in place to start our database driver. See you next time, and as usual, if you have any questions, please feel free to contact me on Twitter.

Joe Dixon photo

Founder and CTO of ubisend. Proud Father to two tiny heroes, Husband, developer, occasional globetrotter.

Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.

image
Paragraph

Manage your Laravel app as if it was a CMS – edit any text on any page or in any email without touching Blade or language files.

Visit Paragraph
Laravel Forge logo

Laravel Forge

Easily create and manage your servers and deploy your Laravel applications in seconds.

Laravel Forge
Tinkerwell logo

Tinkerwell

The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

Tinkerwell
No Compromises logo

No Compromises

Joel and Aaron, the two seasoned devs from the No Compromises podcast, are now available to hire for your Laravel project. ⬧ Flat rate of $7500/mo. ⬧ No lengthy sales process. ⬧ No contracts. ⬧ 100% money back guarantee.

No Compromises
Kirschbaum logo

Kirschbaum

Providing innovation and stability to ensure your web application succeeds.

Kirschbaum
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
Bacancy logo

Bacancy

Supercharge your project with a seasoned Laravel developer with 4-6 years of experience for just $2500/month. Get 160 hours of dedicated expertise & a risk-free 15-day trial. Schedule a call now!

Bacancy
LoadForge logo

LoadForge

Easy, affordable load testing and stress tests for websites, APIs and databases.

LoadForge
Paragraph logo

Paragraph

Manage your Laravel app as if it was a CMS – edit any text on any page or in any email without touching Blade or language files.

Paragraph
Lucky Media logo

Lucky Media

Bespoke software solutions built for your business. We ♥ Laravel

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar: Laravel E-Commerce

E-Commerce for Laravel. An open-source package that brings the power of modern headless e-commerce functionality to Laravel.

Lunar: Laravel E-Commerce
DocuWriter.ai logo

DocuWriter.ai

Save hours of manually writing Code Documentation, Comments & DocBlocks, Test suites and Refactoring.

DocuWriter.ai
Rector logo

Rector

Your partner for seamless Laravel upgrades, cutting costs, and accelerating innovation for successful companies

Rector

The latest

View all →
Launch your Startup Fast with LaraFast image

Launch your Startup Fast with LaraFast

Read article
Embed Livewire Components on Any Website image

Embed Livewire Components on Any Website

Read article
Statamic announces next Flat Camp retreat (EU edition) image

Statamic announces next Flat Camp retreat (EU edition)

Read article
Laravel Herd releases v1.5.0 with new services. No more Docker, DBNGIN, or even homebrew! image

Laravel Herd releases v1.5.0 with new services. No more Docker, DBNGIN, or even homebrew!

Read article
Resources for Getting Up To Speed with Laravel 11 image

Resources for Getting Up To Speed with Laravel 11

Read article
Laravel 11 streamlined configuration files image

Laravel 11 streamlined configuration files

Read article