
🚀 Get the Package
GitHub Repository:
👉 EG-Mohamed/laravel-missing-translations
Install with Composer:
composer require eg-mohamed/laravel-missing-translations --dev
Managing translations in Laravel projects can become painful as the application grows.
At the beginning, it feels simple:
__('Save')
__('Cancel')
__('Dashboard')
But after weeks or months of development, translation strings start spreading across controllers, Blade views, Livewire components, Filament resources, table columns, actions, forms, and custom pages.
Sooner or later, you face the same problem:
Some text exists in the code, but the translation key does not exist in your JSON language file.
That means untranslated labels, missing UI text, inconsistent language files, and manual cleanup work before every release.
eg-mohamed/laravel-missing-translations solves this problem by scanning your Laravel project, detecting translation strings, and appending missing keys automatically to your JSON locale files.
What Is Laravel Missing Translations?
Laravel Missing Translations is a Laravel development tool that scans your project files for translation function calls and user-facing Filament labels, then compares the detected keys against your JSON language files.
For example, if your code contains:
__('Create Account')
But your lang/en.json file does not contain:
{
"Create Account": ""
}
The package can automatically append it.
Instead of manually searching your codebase for missing translation strings, you can run one Artisan command and let the package detect them for you.
Why This Package Exists
Laravel already provides a clean translation system, but large projects often face a few practical issues.
You may add a new button:
__('Export Report')
Then forget to add it to lang/en.json.
You may update a Filament resource:
TextInput::make('email')
->label('Email Address')
->placeholder('Enter your email');
But those plain strings may not exist in your translation file.
You may remove old UI text from the application, but the old keys stay forever in your JSON file.
Over time, this creates messy translation files with:
- Missing keys
- Unused keys
- Duplicated manual work
- Untranslated UI labels
- Hard-to-review localization changes
- Poor release confidence in multilingual projects
This package helps make translation maintenance part of your normal development workflow.
Installation
Install the package as a development dependency:
composer require eg-mohamed/laravel-missing-translations --dev
This is usually best installed with --dev because the package is mainly used during development, review, or CI checks.
Then publish the configuration file:
php artisan vendor:publish --tag="missing-translations-config"
This will publish the config file:
config/laravel-missing-translations.php
Basic Usage
To scan your project and append missing keys to lang/en.json, run:
php artisan missing-translations en
The package will scan the configured paths, detect translation keys, compare them with the existing JSON locale file, and append any missing keys with an empty value.
Example result:
{
"Dashboard": "Dashboard",
"Create Account": "",
"Export Report": ""
}
Existing translations are not overwritten.
That is important because the package is designed to safely add missing keys without destroying existing work.
Using the Default Application Locale
If you do not pass a locale, the command falls back to your Laravel application locale:
php artisan missing-translations
Internally, this uses:
config('app.locale')
So if your application locale is en, the package will target:
lang/en.json
Preview Missing Translations Without Writing Files
Before modifying your language files, you can preview the missing keys using --dry-run:
php artisan missing-translations en --dry-run
This shows the missing keys without changing your files.
This is useful when you want to review the output first.
Example output:
Keys scanned: 42 | Existing: 38 | Missing: 4
Dry run mode: no changes written.
A dry run is a good habit before running the command on a large project or before committing translation changes.
Scan All Existing Locale Files
If your project has multiple JSON locale files, you can scan all of them at once:
php artisan missing-translations --all
For example, if your project has:
lang/en.json
lang/ar.json
lang/fr.json
The package can process all of them in one command.
This is helpful for multilingual applications where every supported locale should contain the same translation keys.
Remove Unused Translation Keys
Translation files often become polluted with old keys that are no longer used anywhere in the project.
You can remove unused keys with:
php artisan missing-translations en --remove-unused
This finds keys that exist in lang/en.json but are no longer referenced in the scanned files.
For safer review, combine it with --dry-run:
php artisan missing-translations en --remove-unused --dry-run
This lets you preview unused keys before actually removing them.
This is especially useful before major releases, UI rewrites, or after removing old features.
JSON Output for CI Pipelines
The package also supports JSON output:
php artisan missing-translations en --json
Example output:
{
"locale": "en",
"existing_count": 38,
"missing_count": 4,
"missing_keys": [
"New Key One",
"New Key Two"
]
}
When used with --remove-unused, the output can also include unused keys:
{
"locale": "en",
"existing_count": 38,
"missing_count": 4,
"missing_keys": [
"New Key One"
],
"unused_count": 2,
"unused_keys": [
"Old Key",
"Stale Key"
]
}
This makes it easier to use the package in CI pipelines.
For example, you can fail a build if missing translations are detected.
Example GitHub Actions Workflow
You can run the command during CI to detect missing translation keys before code is merged.
name: Check Missing Translations
on:
pull_request:
push:
branches:
- main
jobs:
translations:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
- name: Install dependencies
run: composer install --no-interaction --prefer-dist
- name: Check missing translations
run: php artisan missing-translations en --dry-run --json
Depending on your workflow, you can parse the JSON output and fail the pipeline if missing keys are found.
Supported Translation Calls
The package can detect common Laravel translation usage, including:
__('Dashboard');
trans('Dashboard');
trans_choice('One user|Many users', $count);
Lang::get('Dashboard');
Lang::has('Dashboard');
Lang::choice('One user|Many users', $count);
It also supports Blade translation directives:
@lang('Dashboard')
@choice('One user|Many users', $count)
This covers most normal Laravel translation usage across controllers, Blade views, components, services, and application classes.
Filament Support
One of the strongest features of this package is Filament support.
In many Filament projects, user-facing text is not always wrapped in __().
For example:
TextInput::make('email')
->label('Email Address')
->placeholder('Enter your email')
->helperText('Never shared');
These are real UI strings, but they are easy to miss when managing translation files manually.
Laravel Missing Translations can detect these strings automatically when Filament scanning is enabled.
Filament Field Example
use Filament\Forms\Components\TextInput;
TextInput::make('email')
->label('Email Address')
->placeholder('Enter your email')
->helperText('Never shared');
The package can detect:
Email Address
Enter your email
Never shared
Then it can append them to your JSON locale file:
{
"Email Address": "",
"Enter your email": "",
"Never shared": ""
}
Filament Table Column Example
use Filament\Tables\Columns\TextColumn;
TextColumn::make('status')
->label('Current Status')
->tooltip('Last updated today');
Detected strings:
Current Status
Last updated today
This is useful because table columns often contain labels, tooltips, descriptions, badges, and action text that should be translatable.
Filament Action Example
use Filament\Actions\Action;
Action::make('approve')
->label('Approve')
->modalHeading('Confirm Approval')
->modalDescription('Are you sure you want to approve this item?')
->modalSubmitActionLabel('Yes, approve');
Detected strings:
Approve
Confirm Approval
Are you sure you want to approve this item?
Yes, approve
This makes the package helpful for admin panels, dashboards, CRMs, SaaS back offices, and internal systems built with Filament.
Detecting Structural Filament Components
The package can also detect display labels from structural Filament components like:
Section::make('Personal Information');
Tab::make('Account Settings');
Fieldset::make('Billing Details');
Detected strings:
Personal Information
Account Settings
Billing Details
The package is careful not to treat normal field names as labels.
For example:
TextInput::make('email');
This should not be treated as a translation string.
But this should be:
Section::make('User Details');
Because it looks like a user-facing label.
Auto-Generated Filament Labels
Filament can automatically generate labels from field names.
For example:
TextColumn::make('scheduled_time');
Filament may display it as:
Scheduled time
The package can capture this generated label too.
Another example:
TextInput::make('firstName');
Detected label:
First name
This is a small but important feature because many Filament resources rely on auto-generated labels instead of explicit ->label() calls.
Avoiding Double Counting
If you already wrap a Filament label with Laravel’s translation helper, the package does not need to count it twice.
Example:
TextInput::make('email')
->label(__('Email Address'));
In this case, the key is detected by the normal __() scanner.
The Filament scanner should not add another duplicate entry for the same string.
Configuration
After publishing the config file, you can customize how the scanner behaves.
Example:
<?php
return [
'paths' => [
app_path(),
resource_path('views'),
],
'extensions' => [
'php',
'blade.php',
],
'exclude_paths' => [],
'sort_keys' => true,
'exclude_dot_keys' => false,
'include_functions' => [
'__',
'trans',
'trans_choice',
'@lang',
'@choice',
'Lang::get',
'Lang::has',
'Lang::choice',
],
'exclude_patterns' => [],
'ignore_package_keys' => true,
'json_flags' => JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE,
'filament' => [
'enabled' => true,
'methods' => [
'label',
'placeholder',
'helperText',
'hint',
'hintTooltip',
'description',
'heading',
'subheading',
'title',
'modalHeading',
'modalDescription',
'modalSubmitActionLabel',
'modalCancelActionLabel',
'emptyStateHeading',
'emptyStateDescription',
'tooltip',
'prefix',
'suffix',
'navigationLabel',
'navigationGroup',
'pluralLabel',
'singularLabel',
'breadcrumb',
],
'static_methods' => [
'Tab',
'Section',
'Fieldset',
'Group',
'Step',
],
],
];
Customizing Scan Paths
By default, the package scans common Laravel paths like:
app_path()
resource_path('views')
You can add more paths if your project has custom modules, packages, or domain folders.
Example:
'paths' => [
app_path(),
resource_path('views'),
base_path('modules'),
base_path('packages'),
],
This is useful for modular Laravel applications.
Excluding Paths
You may want to exclude generated files, cached files, vendor-like folders, or specific modules.
Example:
'exclude_paths' => [
base_path('vendor'),
base_path('node_modules'),
base_path('storage'),
base_path('bootstrap/cache'),
],
This keeps the scan focused and avoids unnecessary noise.
Excluding Dot Keys
Laravel projects often use PHP array-based translation files with dotted keys:
__('auth.failed')
This key usually belongs to:
lang/en/auth.php
Not:
lang/en.json
If you only want to manage JSON translation strings, you may choose to exclude dotted keys:
'exclude_dot_keys' => true,
That way, keys like this are ignored:
__('auth.failed');
But normal JSON strings are still detected:
__('Create Account');
Ignoring Package Translation Keys
Package translation keys often use namespace syntax:
__('filament-shield::resource.role')
These usually belong to the package’s own translation files, not your application JSON file.
You can ignore package keys with:
'ignore_package_keys' => true,
This prevents your JSON files from being filled with package-specific keys that you probably do not want to manage manually.
Excluding Keys by Pattern
You can exclude keys using regex patterns.
Example:
'exclude_patterns' => [
'/^debug\./',
'/^internal\./',
],
This is useful if your project has internal keys, logs, or development-only strings that should not be added to the JSON translation files.
Sorting Keys
You can keep JSON translation files sorted alphabetically:
'sort_keys' => true,
This makes pull requests cleaner and easier to review.
Without sorting, new keys may be appended at the end.
With sorting, the file stays organized.
JSON Encoding Flags
The package allows customizing JSON encoding flags:
'json_flags' => JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE,
This is especially useful for Arabic and other non-Latin languages.
For example, with JSON_UNESCAPED_UNICODE, Arabic text stays readable:
{
"Dashboard": "Ù„ÙˆØØ© التØÙƒÙ…"
}
Instead of being escaped into Unicode sequences.
Real Example
Imagine this Laravel Blade file:
<h1>{{ __('Dashboard') }}</h1>
<a href="/reports">
{{ __('View Reports') }}
</a>
<button>
{{ __('Export Report') }}
</button>
And this Filament form:
TextInput::make('name')
->label('Full Name')
->placeholder('Enter full name');
Select::make('status')
->label('Current Status')
->options([
'active' => __('Active'),
'inactive' => __('Inactive'),
]);
If your lang/en.json only contains:
{
"Dashboard": "Dashboard",
"Active": "Active"
}
Running:
php artisan missing-translations en
Can append missing keys like:
{
"Active": "Active",
"Current Status": "",
"Dashboard": "Dashboard",
"Enter full name": "",
"Export Report": "",
"Full Name": "",
"Inactive": "",
"View Reports": ""
}
This gives you a clean list of strings that still need translation.
Suggested Workflow
A practical workflow would be:
php artisan missing-translations en --dry-run
Review the missing keys.
Then run:
php artisan missing-translations en
Translate the new empty values.
Then optionally check unused keys:
php artisan missing-translations en --remove-unused --dry-run
If the unused keys are safe to remove:
php artisan missing-translations en --remove-unused
This workflow gives you more control and avoids accidental cleanup mistakes.
When Should You Use This Package?
This package is useful when you are building:
- Multilingual Laravel applications
- SaaS dashboards
- Filament admin panels
- Internal CRM systems
- Client portals
- E-commerce back offices
- Government or enterprise systems with multiple languages
- Applications where translation completeness matters before deployment
It is especially useful when the project has many UI screens and many developers contributing code.
What This Package Is Not
This package is not a full translation management platform.
It does not replace tools where translators can edit translations through a dashboard.
It does not automatically translate your content.
It does not guarantee perfect detection of every dynamic runtime key.
For example:
__("statuses.{$status}")
Dynamic keys like this are difficult for static scanners because the final key is only known at runtime.
The package is best used for static translation strings and common Laravel or Filament UI text.
Best Practices
1. Use Dry Run Before Writing
php artisan missing-translations en --dry-run
Always preview changes first on large projects.
2. Commit Translation Changes Separately
Try to keep translation updates in a separate commit.
Good commit example:
Add missing translation keys
This makes code review easier.
3. Avoid Dynamic Translation Keys When Possible
Instead of:
__("status.{$status}");
Prefer clearer mappings:
$statusLabels = [
'active' => __('Active'),
'inactive' => __('Inactive'),
'pending' => __('Pending'),
];
return $statusLabels[$status] ?? $status;
This makes translation detection easier and improves readability.
4. Use JSON Files for User-Facing Strings
For simple UI strings, JSON translations are very convenient:
__('Create Account')
__('Delete User')
__('Export Report')
For structured validation, auth, or domain files, PHP translation arrays may still be better:
__('auth.failed')
__('validation.required')
5. Review Unused Keys Carefully
Before removing unused keys, always run:
php artisan missing-translations en --remove-unused --dry-run
Some keys may be used dynamically and may not be detected by static scanning.
Why Filament Developers Will Like It
Filament applications usually contain a lot of user-facing labels in resources, relation managers, widgets, tables, forms, filters, and actions.
Example:
TextInput::make('email')
->label('Email Address');
TextColumn::make('created_at')
->label('Created At');
Action::make('ban')
->label('Ban User')
->modalHeading('Confirm Ban');
Without a tool like this, you may forget to add these strings to your JSON translation files.
With Laravel Missing Translations, you can scan and collect them automatically.
That is a big time saver for Filament-heavy projects.
How It Works Internally
At a high level, the package works like this:
- It scans configured project paths.
- It looks for files with configured extensions.
- It extracts translation keys from supported Laravel functions.
- It extracts user-facing Filament strings when Filament scanning is enabled.
- It compares detected keys against your JSON locale file.
- It appends missing keys with empty values.
- It keeps existing translations untouched.
- It avoids duplicate keys.
- It can remove unused keys when requested.
- It writes files safely using file locking.
This makes the command safe to run repeatedly.
Example Before and After
Before running the command:
{
"Dashboard": "Dashboard"
}
Your code:
__('Dashboard');
__('Create User');
__('Delete User');
Run:
php artisan missing-translations en
After:
{
"Create User": "",
"Dashboard": "Dashboard",
"Delete User": ""
}
The existing value for Dashboard is preserved.
The missing keys are added with empty values.
Final Thoughts
Laravel Missing Translations is a practical package for Laravel developers who want cleaner, safer, and easier translation maintenance.
Instead of manually hunting for missing strings, you can scan your project and update your JSON locale files with one command.
The package becomes even more useful in Filament projects, where many labels, placeholders, headings, tooltips, and action names may exist as plain strings inside resources and components.
If your Laravel project supports multiple languages, this package can save time, reduce missed translations, and make localization part of your normal development workflow.
Repository:
https://github.com/EG-Mohamed/laravel-missing-translations