stevegrunwell/lost-in-translation
Installation:
composer require stevegrunwell/lost-in-translation
This replaces Laravel’s default TranslationServiceProvider with an enhanced version.
First Use Case:
Run your application in a non-production environment (APP_DEBUG=true). Attempt to load a missing translation (e.g., __('non.existent.key')). The package will:
LostInTranslation\MissingTranslationException (if APP_DEBUG is true).storage/logs/lost-in-translation.log.Where to Look First:
storage/logs/lost-in-translation.log) for missing keys.config/translation.php for package-specific settings (e.g., lost_in_translation section).Detect Missing Translations:
__(), trans(), or Lang::get() as usual. The package intercepts calls to missing keys.// This will trigger a log entry or exception
$message = __('some.missing.key');
Log Analysis:
storage/logs/lost-in-translation.log to identify gaps in translations.grep -oP 'missing key: \K[^ ]+' storage/logs/lost-in-translation.log | sort | uniq
Integration with CI/CD:
# Example: Fail if >10 missing keys
grep -c "missing key:" storage/logs/lost-in-translation.log | awk '{if ($1 > 10) exit 1}'
Dynamic Translation Loading:
// Pseudocode: Fetch missing keys from log and create a new language file
$missingKeys = collect(file('storage/logs/lost-in-translation.log'))
->map(fn ($line) => preg_match('/missing key: (.+)/', $line, $matches) ? $matches[1] : null)
->filter()
->unique();
$missingKeys->each(fn ($key) => Lang::set('en.' . $key, 'default value'));
Environment-Specific Behavior:
lost_in_translation.throw_exceptions to false in config/translation.php:
'lost_in_translation' => [
'throw_exceptions' => env('APP_ENV') !== 'production',
'log_missing' => true,
],
Performance Overhead:
storage/logs/lost-in-translation.log. Disable logging in production:
'lost_in_translation' => [
'log_missing' => env('APP_ENV') !== 'production',
],
False Positives:
__('user.message', ['key' => $dynamic])) may log as missing. Use fallback logic:
__('user.message', [], 'default fallback');
Exception Handling:
MissingTranslationException is thrown only in debug mode. In production, missing translations silently return the key itself (Laravel’s default behavior). Avoid relying on exceptions for user-facing errors.Log Rotation:
lost-in-translation.log) isn’t rotated by default. Configure Laravel’s log rotation or use a symlink to a temporary file for testing.Namespace Conflicts:
TranslationServiceProvider, ensure the package’s provider is loaded after your custom provider to avoid conflicts.Clear Logs Safely:
echo "" > storage/logs/lost-in-translation.log
Test Locally:
resources/lang directory or editing config/app.php to point to a non-existent path:
'lang' => 'nonexistent',
Custom Exceptions:
MissingTranslationException to add context (e.g., line numbers):
use LostInTranslation\MissingTranslationException;
throw new MissingTranslationException(
"Missing key: {$key}",
$this->getTraceAsString() // Include stack trace
);
Ignore Specific Keys:
// In a service provider, override the missing key handler
Translation::setMissingCallback(function ($key) {
if (str_contains($key, 'placeholder.')) return $key;
// Default behavior
});
CI/CD Integration:
# Example: Generate a markdown report
echo "# Missing Translations" > missing-translations.md
grep -oP 'missing key: \K[^ ]+' storage/logs/lost-in-translation.log | sort | uniq >> missing-translations.md
Custom Logging:
Monolog\Logger to the lost-in-translation tag in Laravel’s service container:
$this->app->bind('lost-in-translation.logger', function () {
return Log::channel('custom');
});
Slack/Email Alerts:
// In a service provider
Translation::setMissingCallback(function ($key) {
if (app()->environment('production')) {
Notification::route('slack', config('services.slack.webhook'))
->notify(new MissingTranslationAlert($key));
}
});
Database Backing:
// Migration
Schema::create('missing_translations', function (Blueprint $table) {
$table->id();
$table->string('key');
$table->string('locale')->default('en');
$table->timestamps();
});
// Observer
event(new MissingTranslationDetected($key, $locale));
Dynamic Fallbacks:
// Pseudocode: Scrape Blade templates for __() calls
$missingKeys = collect(file(base_path('resources/views/**/*.blade.php')))
->map(fn ($content) => preg_match_all('/__\('([^)]+)'\)/', $content, $matches))
->flatten()
->unique();
How can I help you explore Laravel packages today?