our-edu/laravel-translation-client

Laravel client package for consuming centralized translation service

Maintainers

Package info

github.com/our-edu/laravel-translation-client

pkg:composer/our-edu/laravel-translation-client

Transparency log

Statistics

Installs: 5 387

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.5.0 2026-08-19 11:20 UTC

README

Latest Version License

A Laravel package for consuming centralized translation services. This package replaces Laravel's file-based translation loader with an API-based loader that fetches translations from a remote translation service.

Features

  • API-based translations - Fetch translations from a centralized service
  • Write translations - Push translations back to the service
  • Multi-level caching - Memory, Laravel cache, and service-side caching
  • Automatic updates - Translations update without redeployment
  • Multi-tenant support - Per-tenant translation isolation
  • Zero code changes - Works with existing trans() and __() calls
  • High performance - Sub-millisecond translation lookups
  • Graceful degradation - Falls back to stale cache on API failures
  • Import from files - Migrate existing Laravel translations

Requirements

  • PHP 8.1 or higher
  • Laravel 10.x or 11.x

Installation

1. Install via Composer

composer require ouredu/laravel-translation-client

2. Publish Configuration

php artisan vendor:publish --provider="OurEdu\TranslationClient\TranslationServiceProvider"

This will create config/translation-client.php.

3. Configure Environment

Add the following to your .env file:

TRANSLATION_SERVICE_URL=https://your-translation-service.com
TRANSLATION_PRELOAD=true
TRANSLATION_CLIENT=backend

4. Configure Available Locales

In config/translation-client.php, add:

'available_locales' => ['en', 'ar', 'fr'],

Configuration

All configuration options are in config/translation-client.php:

return [
    // Translation service base URL
    'service_url' => env('TRANSLATION_SERVICE_URL', 'http://localhost'),
    
    // Cache TTL in seconds
    'manifest_ttl' => env('TRANSLATION_MANIFEST_TTL', 300), // 5 minutes
    'bundle_ttl' => env('TRANSLATION_BUNDLE_TTL', 3600), // 1 hour
    
    // Client type: backend, frontend, mobile
    'client' => env('TRANSLATION_CLIENT', 'backend'),
    
    // HTTP timeout in seconds
    'http_timeout' => env('TRANSLATION_HTTP_TIMEOUT', 10),
    
    // Use stale cache on API failure
    'fallback_on_error' => env('TRANSLATION_FALLBACK_ON_ERROR', true),
    
    // Cache store (null = default)
    'cache_store' => env('TRANSLATION_CACHE_STORE'),
    
    // Logging
    'logging' => [
        'enabled' => env('TRANSLATION_LOGGING', false),
        'channel' => env('TRANSLATION_LOG_CHANNEL', 'stack'),
    ],
    
     /*
    |--------------------------------------------------------------------------
    | Available Locales
    |--------------------------------------------------------------------------
    |
    | The list of available locales to sync translations for
    |
    */
    'available_locales' => [
        'ar',
        'en',
    ],
];

Usage

Basic Usage

Once installed, use Laravel's translation functions as normal:

// In controllers
__('messages.welcome')
trans('validation.required')

// With replacements
trans('messages.hello', ['name' => 'Ahmed'])

Commands

Clear Cache

Clear translation caches:

# Clear all translation caches
php artisan translations:clear-cache

# Clear specific locale
php artisan translations:clear-cache --locale=ar

Import Translations

Import translations from Laravel lang files to the service:

# Import all locales
php artisan translations:import

# Import specific locale
php artisan translations:import --locale=ar

# Import from custom path
php artisan translations:import --path=/path/to/lang

Keeping Translations Fresh

Nothing needs scheduling. Translations are fetched on first use and cached, and each cached bundle carries the version it was built from. That version is checked against the service's manifest on every read, so a bundle cannot go stale indefinitely.

There is a lag, though, and it is worth knowing. The manifest is itself cached for manifest_ttl — 300 seconds by default. Until that entry expires the version comparison is against a stale manifest, so it agrees with the cached bundle and serves it. An edit in the service therefore appears within manifest_ttl, not on the very next request.

Measured against a real consuming app: a value created in the service was still absent immediately afterwards, and present once the manifest cache was cleared.

Lower TRANSLATION_MANIFEST_TTL if you need edits to land faster — it costs one small request per locale per interval — or run translations:clear-cache to force it immediately.

Middleware for Locale Detection

Add to app/Http/Kernel.php:

protected $middlewareGroups = [
    'web' => [
        // ...
        \OurEdu\TranslationClient\Middleware\SetLocaleFromRequest::class,
    ],
];

This middleware detects locale from:

  1. Query parameter (?locale=ar)
  2. Header (X-Locale: ar)
  3. Accept-Language header
  4. Authenticated user preference

Using the Facade

Reading Translations

use OurEdu\TranslationClient\Facades\Translation;

// Check version
$manifest = Translation::checkVersion('ar');
// Returns: ['version' => 42, 'etag' => '...', 'updated_at' => '...']

// Fetch specific groups
$translations = Translation::fetchBundle('ar', ['messages', 'validation']);

// Load all translations
$all = Translation::loadTranslations('ar');

// Clear cache
Translation::clearCache('ar');

Writing Translations

// Push single translation
Translation::pushTranslation(
    locale: 'ar',
    group: 'messages',
    key: 'welcome',
    value: 'مرحبا'
);

// Push multiple translations
Translation::pushTranslations([
    [
        'locale' => 'ar',
        'group' => 'messages',
        'key' => 'hello',
        'value' => 'مرحباً',
        'client' => 'backend',
    ],
]);

// Import from Laravel lang files
Translation::importFromFiles('ar', lang_path());

Programmatic Access

use OurEdu\TranslationClient\Services\TranslationClient;

class MyService
{
    public function __construct(
        private TranslationClient $translationClient
    ) {}
    
    public function getTranslations()
    {
        return $this->translationClient->fetchBundle('ar', ['messages']);
    }
}

How It Works

Architecture

Laravel App → trans() → ApiTranslationLoader → TranslationClient → Translation Service API
                                ↓
                         Memory Cache (in-memory)
                                ↓
                         Laravel Cache (Redis/File)
                                ↓
                         Translation Service (Redis + DB)

Caching Strategy

  1. Memory Cache: Loaded translations stored in PHP memory
  2. Laravel Cache: Persistent cache with configurable TTL
  3. Version Checking: Manifest API checked every 5 minutes
  4. Automatic Invalidation: Cache refreshed when version changes

Performance

  • Cached Translation: ~1-5ms (from memory)
  • Laravel Cache Hit: ~5-10ms
  • API Call (first time): ~50-200ms
  • Manifest Check: ~10-50ms

Multi-Tenant Support

For multi-tenant applications:

// Set tenant dynamically based on request
config(['translation-client.tenant_id' => $currentTenant->id]);

// Or extend the TranslationClient
class MultiTenantTranslationClient extends TranslationClient
{
    public function __construct()
    {
        parent::__construct();
        $this->tenantUuid = auth()->user()?->tenant_uuid;
    }
}

Error Handling

The package handles errors gracefully:

  • API Unavailable: Uses stale cache if available
  • Network Timeout: Falls back to cached data
  • Invalid Response: Logs error and returns empty array
  • Missing Translations: Falls back to key name (Laravel default)

Enable logging for debugging:

TRANSLATION_LOGGING=true
TRANSLATION_LOG_CHANNEL=stack

Testing

When testing, you may want to disable API calls:

// In tests/TestCase.php
protected function setUp(): void
{
    parent::setUp();
    
    // Mock the translation client
    $this->mock(TranslationClient::class, function ($mock) {
        $mock->shouldReceive('loadTranslations')
            ->andReturn(['messages.welcome' => 'Welcome']);
    });
}

Troubleshooting

Translations not loading

  1. Check service URL is correct: php artisan config:cache
  2. Verify API is accessible: curl https://your-service.com/translation/manifest?locale=en
  3. Enable logging: TRANSLATION_LOGGING=true
  4. Check logs: tail -f storage/logs/laravel.log

Cache not clearing

php artisan cache:clear
php artisan translations:clear-cache
php artisan config:cache

Performance issues

  1. Use Redis for caching instead of file cache
  2. Increase cache TTL if translations rarely change

License

This package is open-sourced software licensed under the MIT license.

Support

For issues and questions, please use the GitHub issue tracker.