coding-socks/lost-in-translation
Lost in Translation is a Laravel package that helps you manage translation files with confidence. Detect missing or unused keys, keep language files tidy, and streamline your localization workflow so no string gets lost between languages.
Install as a dev dependency. This is a static-analysis tool, not a runtime dependency, so keep it out of production builds:
composer require --dev coding-socks/lost-in-translation
Confirm auto-discovery and find the command. The package should register a service provider via Laravel package discovery. Run:
php artisan list | grep -i translation
The exact command namespace is the first thing to verify in vendor/coding-socks/lost-in-translation/src/ or the README, since small packages sometimes rename commands between releases. Look for the console command class first — that tells you the available options (--path, --locale, --format, etc.).
Publish the config if a config file ships with it:
php artisan vendor:publish --tag=lost-in-translation-config
Check config/lost-in-translation.php (or similarly named) for path excludes, translation function names, and locale settings.
Run a baseline scan before changing anything. Commit the current state, then run the scan against resources/views and lang/. Save the output — that becomes your backlog and your baseline for measuring progress.
First meaningful use case: run the scan on a single feature branch's Blade directory, add the reported keys to lang/en/..., and re-run until clean. That tight loop is the entire value proposition of the package.
Local development loop. Wire the scan into your day-to-day workflow rather than treating it as a one-off audit. A typical loop is:
php artisan <translation-command> --path=resources/views
# add missing keys
php artisan <translation-command> --path=resources/views
Run it before opening a PR on any branch that touches Blade templates.
CI gate. Add the command to your pipeline so missing keys fail the build. Make sure the command exits non-zero when it finds missing strings; if it doesn't, wrap it:
- run: php artisan <translation-command> --path=resources/views
If the package only reports and always exits 0, capture stdout and grep for a known failure marker, or write a small Artisan wrapper command that calls the scanner and returns 1 when the result set is non-empty.
Composer script for convenience. Expose it as a project script so nobody has to remember the command name:
"scripts": {
"translations:check": [
"@php artisan <translation-command> --path=resources/views"
]
}
Then composer translations:check works in local hooks and CI alike.
Scope scans deliberately. Point the scanner at resources/views (and app/ if you use translation calls in PHP) rather than the project root. Exclude vendor/, node_modules/, storage/, and generated view caches. Most false positives come from scanning too broadly.
Pair with a lang-file linter. This package finds keys used but not defined. Pair it with a check in the opposite direction (defined but unused) so you can prune dead translation entries. Two commands, one cleanup pass.
Multi-locale strategy. Decide whether the tool checks against a single source locale (en) or every locale. Checking against en only keeps CI fast and avoids failing on partially translated locales. If the package supports per-locale comparison, run the strict check on the source locale in CI and the full check as a scheduled report.
Handle dynamic keys explicitly. Keys built at runtime — __("messages.{$type}.title"), @lang($key), keys from config or database — cannot be resolved statically. Maintain an allowlist/ignore file (config or inline comment, depending on what the package supports) so these don't create permanent noise. Document the convention in the repo so contributors know to add entries when they introduce dynamic keys.
Blade component and slot awareness. If the scanner supports Blade components, verify it catches @lang, {{ __('...') }}, @choice, and trans_choice. If it only catches __(), standardize the team on that helper in new code, or add the missing patterns via config if the package exposes them.
False positives from concatenation. __('messages.' . $key) and interpolated keys will be reported as missing even when the underlying strings exist. This is the single biggest source of noise. Budget time for an ignore list on first adoption.
JSON vs. PHP lang files. Laravel supports both lang/en.json (key-as-string) and lang/en/*.php (nested keys). Confirm which formats the scanner resolves. A common failure mode is keys that work at runtime via JSON but are reported missing because the tool only reads PHP arrays — or vice versa.
Vendor and namespaced keys. __('vendor::file.key') and package-provided translations may be flagged if the scanner only looks at your application's lang/ directory. Exclude these or verify the tool resolves vendor namespaces before trusting the report.
Pluralization variants. Keys with | separators and trans_choice calls can produce odd results. Check that {0}, {1}, and range variants are treated as a single key, not multiple missing strings.
Config caching. After editing the package config, run php artisan config:clear (and config:cache in production-like environments). Stale cached config causes path excludes and locale settings to silently not apply.
Exit codes matter more than output. Before wiring this into CI, test the failure path locally: introduce a deliberately missing key, run the command, and check echo $?. If it returns 0, your CI gate is a no-op.
Low adoption means verify compatibility yourself. With a small dependent count, assume less battle-testing across Laravel versions. Pin the version in composer.json, check the package's composer.json for its supported illuminate/* range, and run the scan against a representative branch before merging into your main pipeline. The release metadata is worth a sanity check against the repo's tags.
Performance on large repos. On a monolith with thousands of Blade files, scan time can grow noticeably. Scope to changed directories in local use (--path=resources/views/checkout) and reserve full scans for CI or a scheduled job.
Read the source before extending. If you need custom behavior — a new translation function pattern, a custom formatter, a different exit-code policy — check whether the package exposes extension points (config-driven parser patterns, custom reporters) before forking. For a package this small, the source is quick to read and is the most reliable documentation.
How can I help you explore Laravel packages today?