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.
Architecture fit. lost-in-translation is a development-time static analysis utility, not a runtime dependency. It scans Blade templates for translation calls (__(), @lang, trans()) and reports keys that have no corresponding entry in the application's language files. That places it cleanly in the same architectural tier as linters, Pint, and PHPStan: invoked by developers and CI, never loaded in production, no service provider overhead on request paths, no database or queue surface. For a Laravel codebase that relies on Blade-rendered UI and file-based translations, this fills a real gap — Laravel itself ships no missing-key detector, and the failure mode it addresses (a raw key rendering in front of a user) is a silent, low-severity but high-embarrassment defect.
Integration feasibility. Mechanically simple: a require --dev install, a publishable config in the likely case, and an Artisan command or Composer script invocation. The effort is dominated by evaluating output quality, not by wiring. Because it is MIT and touches only static files, a trial can be run and reverted with a single composer remove — the blast radius of a failed evaluation is effectively zero.
Technical risk. The risk profile is asymmetric: low technical risk, high supply-chain/adoption risk.
| Risk | Assessment |
|---|---|
| Maturity / adoption | 9 stars, 0 dependents, single-maintainer project. No evidence of production usage at scale. |
| Maintenance continuity | Bus factor of 1. A release dated 2026-09-22 is either forward-dated metadata or a data-quality artifact — verify before relying on release cadence as a health signal. |
| Detection accuracy | Translation extraction from Blade is heuristic. Dynamic keys (@lang($key), concatenated keys), trans_choice pluralization, JSON-based translations, namespaced package keys (vendor::file.key), and @php blocks are the classic false-positive/false-negative sources. |
| False confidence | A clean run does not prove coverage; it proves the tool's parser did not find gaps in the patterns it understands. |
| Security | Static file reads in dev/CI only. Negligible, but it should still not be granted production credentials or run against production checkouts. |
Key questions.
trans_choice, @lang with parameters, JSON translation files, and namespaced keys?composer.json, and is it tested in CI across those versions?Stack fit. Requires Laravel (Blade-based views), Composer 2, and a PHP version matching the package constraint — confirm against the application's runtime, since a Laravel 10/11 app on PHP 8.1 versus 8.3 changes the answer. Fits naturally alongside an existing quality gate: Pint for style, PHPStan/Larastan for types, this for translation integrity. It is additive and orthogonal; no conflict with localisation packages such as laravel-lang, spatie/laravel-translation-loader, or translation-management UIs is expected, though overlapping responsibilities should be clarified so teams do not run two competing sources of truth.
Migration path. None required — this is greenfield tooling, not a replacement. The one migration-adjacent consideration: if translations currently live in a database or an external TMS rather than lang/ files, the tool's model of the world will not match yours, and the value drops sharply. Confirm the source of truth before committing.
Compatibility. Three concrete checks before adoption: (a) Laravel/PHP constraint satisfiability against the app's composer.lock; (b) whether the command is registered via auto-discovery and what its signature is; (c) whether it respects lang/ path configuration and non-standard locale directories. Where the app uses a single-locale source with machine-translated targets, confirm it compares against the correct reference locale rather than flagging every target locale as incomplete.
Sequencing.
Maintenance. Low ongoing effort but non-zero ownership. Pin an exact version, commit the lockfile, and accept that upstream fixes may not arrive. The mitigating factor is size: a package this small is realistically forkable and patchable in-house under MIT if the maintainer disappears. Budget a nominal half-day per year for that contingency, and designate an owner so the CI job does not silently rot.
Support. Community-only, no SLA, thin documentation likely. Internal support burden falls on whoever owns the CI pipeline; expect questions of the form "why is this key flagged" and "how do I suppress this." A short internal README with the suppression convention and the reference-locale definition prevents most of that traffic.
Scaling. Two dimensions. Repository size: a full Blade walk across a large monolith or multi-tenant app with thousands of views may take tens of seconds to minutes; if it exceeds tolerable CI time, scope it to changed files or run it nightly rather than per-PR. Locale count: cost scales with the number of locales compared, and a 20-locale app produces a much noisier report than a 2-locale app — consider limiting the gate to the source locale plus the top few targets. Parallelisation is unlikely to be built in.
Failure modes.
Ramp-up. Very low for engineers: one install command, one command invocation, a paragraph in the contributing guide. Realistic estimate is under a day for the initial spike and roughly a person-week of calendar time to reach a trusted, non-blocking CI state. The learning curve is not in the tool but in understanding its detection boundaries — that knowledge should be written down rather than held by the person who ran the spike.
Recommendation. Adopt as an advisory, dev-only tool with a measured precision gate before any blocking enforcement. Do not make it load-bearing for release quality until it has demonstrated accuracy on your own templates; the low adoption signal is a reason to validate, not a reason to dismiss, given the negligible integration cost and clean removal path.
How can I help you explore Laravel packages today?