gettext/translator
Lightweight PHP translation layer for gettext/gettext. Use Translator to load PHP array translations without the native gettext extension, or GettextTranslator to leverage the extension with the same API. Includes global helper functions for template-friendly __().
composer require gettext/translator
gettext extension):
use Gettext\Translator;
$t = new Translator();
gettext if available):
use Gettext\GettextTranslator;
$t = new GettextTranslator();
.php arrays (generated by Gettext\Extractors\PhpArray):
$t->loadTranslations('locales/es/messages.php');
.mo files (native gettext format):
$t->loadDomain('messages', 'path/to/locales');
use Gettext\TranslatorFunctions;
TranslatorFunctions::register($t);
Now use __('key') in Blade like Laravel’s trans() helper.vendor/bin/php-gettext-extract --format=php --output=locales/es/messages.php resources/views/*.blade.php
.php file to add translations:
return [
'welcome' => '¡Bienvenido!',
'items' => [
'one' => '1 artículo',
'other' => '{0} artículos',
],
];
<h1><?= __('welcome') ?></h1>
<p><?= __('items', 5) ?></p> <!-- Pluralization -->
Register the translator as a Laravel service provider to avoid manual instantiation:
// app/Providers/GettextServiceProvider.php
namespace App\Providers;
use Gettext\Translator;
use Gettext\TranslatorFunctions;
use Illuminate\Support\ServiceProvider;
class GettextServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('gettext.translator', function () {
$t = new Translator();
$t->loadTranslations(config('gettext.locales.*.*.php'));
return $t;
});
$this->app->booting(function () {
TranslatorFunctions::register(app('gettext.translator'));
});
}
}
Register in config/app.php:
'providers' => [
// ...
App\Providers\GettextServiceProvider::class,
],
Bind the translator to the current locale (e.g., from app() or middleware):
// app/Http/Middleware/SetLocale.php
public function handle($request, Closure $next)
{
$locale = $request->header('Accept-Language') ?? config('app.locale');
app('gettext.translator')->setLanguage($locale);
return $next($request);
}
Load different domains for different parts of the app (e.g., validation, auth):
$t = new GettextTranslator();
$t->loadDomain('validation', 'locales/'.app()->getLocale().'/LC_MESSAGES');
$t->loadDomain('auth', 'locales/'.app()->getLocale().'/LC_MESSAGES');
Extend Blade to support context-aware translations (e.g., gettext('key', 'context')):
// app/Providers/BladeServiceProvider.php
Blade::directive('gettext', function ($expression) {
return "<?php echo app('gettext.translator')->gettext({$expression}); ?>";
});
Usage in Blade:
<p>@gettext('welcome')</p>
Combine with Laravel’s fallback mechanism:
$t = new Translator();
$t->loadTranslations('locales/es/messages.php');
$t->loadTranslations('locales/en/messages.php', 'fallback'); // Fallback locale
Automate extraction from Blade/PHP files:
# Extract strings from Blade templates
vendor/bin/php-gettext-extract --format=php --output=locales/es/messages.php resources/views/*.blade.php
# Extract strings from PHP classes
vendor/bin/php-gettext-extract --format=php --output=locales/es/messages.php app/Http/Controllers/*.php
Mock the translator in tests:
$t = new Translator();
$t->loadTranslations([
'welcome' => 'Test Translation',
]);
$this->app->instance('gettext.translator', $t);
File Paths in .mo Files:
GettextTranslator expects .mo files in the standard locale/LC_MESSAGES/domain.mo structure.$t->loadDomain('messages', base_path('locales'));
Pluralization Mismatches:
gettext standards..php translation files:
return [
'items' => [
'one' => '1 artículo',
'other' => '{0} artículos',
],
'plural_forms' => 'nplurals=2; plural=(n != 1);', // Spanish example
];
Global Function Overrides:
TranslatorFunctions globally may conflict with Laravel’s __() helper.TranslatorFunctions::register($t, '__gettext');
// Now use `__gettext('key')` instead of `__('key')`.
Caching .mo Files:
.mo files are compiled binaries. Changes require recompilation:
msgfmt locales/es/LC_MESSAGES/messages.po -o locales/es/LC_MESSAGES/messages.mo
// app/Console/Commands/CompileGettext.php
public function handle()
{
$locales = ['es', 'fr'];
foreach ($locales as $locale) {
$this->compileLocale($locale);
}
}
protected function compileLocale($locale)
{
$poFiles = glob(resource_path("lang/{$locale}/*.po"));
foreach ($poFiles as $poFile) {
$moPath = str_replace('.po', '.mo', $poFile);
$this->call('msgfmt', [
'input' => $poFile,
'output' => $moPath,
]);
}
}
Blade Caching Issues:
php artisan view:clear
Locale Detection:
GettextTranslator uses environment variables (LANG, LC_ALL) by default.AppServiceProvider:
$t = new GettextTranslator();
$t->setLanguage(app()->getLocale());
Check Loaded Translations:
$t->gettext('nonexistent_key'); // Returns the key if not found
$t->gettext('nonexistent_key', true); // Returns `null` if not found (2nd param = strict)
Log Missing Translations:
$t->setMissingHandler(function ($key, $locale) {
Log::warning("Missing translation: {$key} in {$locale}");
});
Validate .po/.mo Files:
msgattrib locales/es/LC_MESSAGES/messages.po # Check for errors
Custom Extractors:
Gettext\Extractors\ExtractorInterface to support custom file formats (e.g., JSON):
class JsonExtractor implements ExtractorInterface
{
public function extract($filePath)
{
$json = json_decode(file_get_contents($filePath), true);
return $this->convertToPhpArray($json);
}
}
Domain-Specific Loaders:
resources/lang structure:
class LaravelDomainLoader
{
public function load($domain, $locale)
{
$path = resource_path("
How can I help you explore Laravel packages today?