be-interactive/laravel-translation-scanner
Scan your Laravel app for translation keys used in Blade, PHP, and JavaScript, compare them against your language files, and quickly spot missing or unused translations to keep every locale in sync.
This is a patch release (5.6.3) on the 5.x line — no breaking changes, no new features announced. If you're already on 5.6.x, composer update be-interactive/laravel-translation-scanner is safe; treat it as a bugfix bump and check the diff link in the release notes if you hit anything odd.
First steps for a fresh install:
composer require --dev be-interactive/laravel-translation-scanner
php artisan translation:scan
php artisan translation:scan --help before scripting anything. This package has evolved its flags across majors, so confirm the exact option names (--path, --translation-file, --json, --sort, filter flags) against the version you actually installed rather than copying snippets from blog posts.php artisan vendor:publish and look for a translation-scanner tag). If it exists, commit it — that's where path exclusions and the source translation file live.The first real use case: point it at your lang/en.json (or lang/en/ directory) and get a list of keys that are used in code but missing from the file and keys that are in the file but never used. That second list is the one that pays for the install — it's dead weight you can usually delete after a quick grep.
Local cleanup loop. Run the scan, take the "unused" list, verify each key with a grep (dynamic keys fool the scanner — see Gotchas), delete in batches, re-run. Do this on a branch, not on main, and let the test suite confirm nothing blows up.
Pre-commit / pre-push guard. Add a Composer script so contributors catch missing keys before review:
"scripts": {
"translations:check": [
"@php artisan translation:scan --no-interaction"
]
}
Wire it into a Husky hook or a composer check aggregate if you already have one.
CI gate. The most valuable pattern is failing the build when a key is used but missing from the source locale. Use the machine-readable output (--json where available) and parse it in a small script rather than grepping the table output — table formatting is cosmetic and changes between releases. Keep the unused-keys side as a warning, not a failure, otherwise the build breaks every time someone stages a feature in two commits.
Locale workflow. Scan against the source locale only (en typically), then propagate keys to other locales via your existing translation pipeline. Running the scanner per-locale mostly produces noise — a key missing from fr is a translation backlog problem, not a code problem.
Pairing with extraction tooling. If you also use a TMS (Crowdin, Lokalise, Phrase) or laravel-lang/* packs, use this scanner as the source-of-truth check on the app side and let the TMS own the downstream locales. Don't let both tools rewrite the same file.
Cleanup cadence. Schedule the scan as a monthly housekeeping task (a scheduled job that opens a ticket, or just a calendar reminder) rather than something you run ad hoc. Translation files only rot slowly, so a periodic sweep beats a heroic one-off cleanup.
Blade and JS components. The scanner is regex-based over your source paths, so make sure the configured paths actually include resources/views and resources/js if you use __() inside Vue/React components — a default config that only covers app/ will silently under-report usage and mark live keys as unused.
__($key), trans('messages.' . $type), and keys built from enums or config values will never be matched. Any key flagged "unused" that comes from a dynamic lookup will look like a deletion candidate — deleting it is a production bug. Always grep before deleting, and consider keeping a documented allowlist for these.trans_choice, | plural forms, and dot-notation nesting in PHP array files don't always round-trip cleanly. Spot-check the output on a file with plurals before trusting a bulk cleanup.vendor/, node_modules/, storage/, bootstrap/cache/, and compiled assets will produce garbage matches and slow the scan down. If you can't configure exclusions in your version, narrow the scanned paths instead.composer.lock, and read the changelog on minors — patch releases like 5.6.3 are safe to take automatically.-v to see which paths were scanned, then reduce to a single file path to confirm the matcher handles your syntax before assuming a parser bug. Nine times out of ten it's a path or exclusion misconfiguration.How can I help you explore Laravel packages today?