Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Common Laravel Package

php-translation/common

Shared contracts and utilities for the PHP Translation ecosystem. Provides common interfaces, models, and helpers used across translation bundles to keep integrations consistent and reduce duplication, making it easier to build and maintain translation features in PHP apps.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require php-translation/common
    

    Ensure your project uses PHP 8.2+ and Symfony 6.4+ (or 7.x).

  2. Basic Usage: Load translations from a standard .json or .php file:

    use PhpTranslation\Common\Loader\JsonFileLoader;
    use PhpTranslation\Common\MessageCatalogue;
    
    $loader = new JsonFileLoader();
    $catalogue = $loader->load('path/to/translations.json', 'en');
    $message = $catalogue->get('key.name');
    
  3. First Use Case: Replace Laravel’s default trans() helper for a specific module (e.g., API responses):

    // In AppServiceProvider
    $this->app->bind(\PhpTranslation\Common\TranslatorInterface::class, function () {
        $loader = new JsonFileLoader();
        $catalogue = $loader->load(resource_path('lang/en.json'), 'en');
        return new \PhpTranslation\Common\Translator($catalogue);
    });
    
    // Usage in routes/controllers
    $translator = app(\PhpTranslation\Common\TranslatorInterface::class);
    return $translator->trans('validation.required');
    

Where to Look First

  • Documentation: Focus on:
    • MessageCatalogue (core interface for translations).
    • Loader interfaces (JsonFileLoader, PhpFileLoader).
    • Translator (for translating messages with placeholders).
  • Examples: Check the tests directory for integration patterns.
  • Symfony Integration: Review SymfonyBridge for Laravel-Symfony interop.

Implementation Patterns

Core Workflows

1. Translation Loading

  • Static Files:
    $loader = new JsonFileLoader();
    $catalogue = $loader->load('lang/en.json', 'en');
    
  • Database/External API: Extend LoaderInterface:
    class DatabaseLoader implements LoaderInterface {
        public function load(string $path, string $locale): MessageCatalogue {
            $data = DB::table('translations')->where('locale', $locale)->get();
            return new MessageCatalogue($data->toArray());
        }
    }
    

2. Dynamic Language Switching

Use middleware to set the locale:

// app/Http/Middleware/SetLocale.php
public function handle(Request $request, Closure $next) {
    $locale = $request->header('Accept-Language') ?? config('app.fallback_locale');
    app()->setLocale($locale);
    return $next($request);
}

Bind a locale-aware translator:

$this->app->singleton(TranslatorInterface::class, function () {
    $loader = new JsonFileLoader();
    $locale = app()->getLocale();
    return new Translator($loader->load("lang/{$locale}.json", $locale));
});

3. Pluralization and Gender Rules

Leverage Symfony’s Intl integration:

use PhpTranslation\Common\Pluralization\Pluralizer;
use Symfony\Component\Intl\Intl;

$pluralizer = new Pluralizer(Intl::getLocaleBundle());
$pluralized = $pluralizer->pluralize('message.key', 5, ['%count%' => 5]);

4. Validation Messages

Replace Laravel’s default validator messages:

$validator = Validator::make($data, [
    'email' => 'required|email',
], [], [], [
    'email.required' => trans('validation.custom.email_required'),
]);

Use the package’s Translator to load custom messages:

$catalogue = $loader->load('lang/validation.json', 'en');
$translator = new Translator($catalogue);
$validator->setCustomMessages($translator->getAll());

Integration Tips

  • Laravel Service Container: Bind interfaces to concrete implementations:

    $this->app->bind(LoaderInterface::class, JsonFileLoader::class);
    $this->app->bind(TranslatorInterface::class, function ($app) {
        $loader = $app->make(LoaderInterface::class);
        return new Translator($loader->load('lang/' . app()->getLocale() . '.json', app()->getLocale()));
    });
    
  • Caching: Cache MessageCatalogue instances:

    $catalogue = Cache::remember("translations_{$locale}", now()->addHours(1), function () use ($loader, $locale) {
        return $loader->load("lang/{$locale}.json", $locale);
    });
    
  • Fallback Locales: Implement a FallbackCatalogue decorator:

    class FallbackCatalogue implements MessageCatalogueInterface {
        public function __construct(
            private MessageCatalogueInterface $primary,
            private MessageCatalogueInterface $fallback
        ) {}
    
        public function get(string $id, array $parameters = []): string {
            return $this->primary->has($id)
                ? $this->primary->get($id, $parameters)
                : $this->fallback->get($id, $parameters);
        }
    }
    
  • Testing: Mock MessageCatalogue in tests:

    $catalogue = $this->createMock(MessageCatalogueInterface::class);
    $catalogue->method('get')->willReturn('Mocked translation');
    $translator = new Translator($catalogue);
    $this->assertEquals('Mocked translation', $translator->trans('test.key'));
    

Gotchas and Tips

Pitfalls

  1. Locale Mismatches:

    • Issue: Passing en_US as locale but loading en.json files.
    • Fix: Normalize locales (e.g., use en for en_US):
      $locale = strtok($locale, '_'); // 'en_US' -> 'en'
      
  2. Circular References in Translations:

    • Issue: Translations referencing each other (e.g., key1 uses key2, which uses key1).
    • Fix: Use a DepthLimitException handler or implement a cycle detector in custom loaders.
  3. Symfony Version Conflicts:

    • Issue: Using Symfony 6 components with the package (which targets Symfony 7).
    • Fix: Pin Symfony dependencies to ^6.4 or upgrade to Symfony 7:
      composer require symfony/intl:^7.0
      
  4. Placeholder Syntax:

    • Issue: Laravel’s {0} placeholders vs. Symfony’s %var%.
    • Fix: Use the package’s Translator with Symfony-style placeholders:
      $translator->trans('message.key', ['%var%' => 'value']);
      
  5. File Permissions:

    • Issue: JsonFileLoader failing silently on unreadable files.
    • Fix: Add error handling:
      try {
          $catalogue = $loader->load('unreadable.json', 'en');
      } catch (FileNotFoundException $e) {
          Log::error("Translation file missing: {$e->getMessage()}");
          $catalogue = $fallbackLoader->load('en.json', 'en');
      }
      

Debugging Tips

  • Enable Debug Mode:

    $translator = new Translator($catalogue, Translator::DEBUG_MODE);
    

    This logs missing keys and untranslated messages.

  • Inspect Catalogue Contents:

    dump($catalogue->getAll()); // View all loaded translations
    
  • Check Loader Paths: Use absolute paths for testing:

    $loader->load(__DIR__ . '/../lang/en.json', 'en');
    

Extension Points

  1. Custom Loaders: Implement LoaderInterface for databases, APIs, or S3:

    class S3Loader implements LoaderInterface {
        public function load(string $path, string $locale): MessageCatalogue {
            $data = S3::getObject($path)->get('Body')->toArray();
            return new MessageCatalogue($data);
        }
    }
    
  2. MessageCatalogue Decorators: Add logic before/after translation:

    class LoggingCatalogue implements MessageCatalogueInterface {
        public function __construct(private MessageCatalogueInterface $catalogue) {}
    
        public function get(string $id, array $parameters = []): string {
            Log::debug("Translating: {$id}", $parameters);
            return $this->catalogue->get($id, $parameters);
        }
    }
    
  3. Pluralization Rules: Override default rules for

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor