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

Translation Contracts Laravel Package

symfony/translation-contracts

Symfony Translation Contracts provides lightweight interfaces and abstractions for translation in PHP, extracted from Symfony components. Use it to build interoperable, battle‑tested translation integrations while staying framework-agnostic and compatible with Symfony implementations.

View on GitHub
Deep Wiki
Context7
## Getting Started
### Minimal Setup
1. **Install the package** (already included in Laravel via `symfony/translation`):
   ```bash
   composer require symfony/translation-contracts

(Note: Laravel 9+ already bundles this via symfony/translation, so no explicit install is needed unless extending functionality.)

  1. Locate core interfaces in vendor/symfony/translation-contracts/src/Translation/:

    • TranslatorInterface: Core translation contract (e.g., trans(), getLocale()).
    • TranslatableInterface: For deferred translation (e.g., TranslatableMessage).
    • MessageCatalogueInterface: For locale/message domain management.
  2. First use case: Extend Laravel’s built-in translator by implementing a custom adapter. Example:

    use Symfony\Contracts\Translation\TranslatorInterface;
    
    // Bind a custom translator to Laravel's container
    $app->bind(TranslatorInterface::class, function ($app) {
        return new CustomTranslator($app['translator']); // Wrap Laravel's translator
    });
    

Implementation Patterns

1. Leveraging Laravel’s Translator with Contracts

  • Default Workflow: Laravel’s trans() helper already implements TranslatorInterface. Use it directly:
    $translator = app(TranslatorInterface::class);
    $message = $translator->trans('validation.required', ['attribute' => 'email']);
    
  • Domain-Specific Translation: Pass a domain parameter (e.g., validation, auth) to isolate message catalogs:
    $translator->trans('welcome', [], 'auth');
    

2. Deferred Translation with TranslatableInterface

  • Use Case: Delay translation until rendering (e.g., in Blade or API responses).
  • Implementation:
    use Symfony\Contracts\Translation\TranslatableMessage;
    
    $message = new TranslatableMessage('validation.required', ['attribute' => 'email']);
    // Later, translate when locale is known:
    $translated = $translator->trans($message);
    
  • Laravel Integration: Store TranslatableMessage in view data or Livewire properties.

3. Custom Translator Adapters

  • Example: Build a translator for a third-party service (e.g., AWS Translate):
    use Symfony\Contracts\Translation\TranslatorInterface;
    
    class AwsTranslator implements TranslatorInterface {
        public function trans(string $id, array $parameters = [], string $domain = null, string $locale = null): string {
            return aws_translate($id, $parameters, $locale);
        }
        // ... other required methods
    }
    
  • Register in Laravel:
    $app->bind(TranslatorInterface::class, function () {
        return new AwsTranslator(config('services.aws'));
    });
    

4. Locale and Fallback Logic

  • Dynamic Locale Switching: Use Laravel’s App::setLocale() or middleware:
    $translator->setLocale(request()->header('Accept-Language') ?? 'en');
    
  • Fallback Chain: Implement TranslatorTrait for built-in fallback support:
    use Symfony\Component\Translation\TranslatorTrait;
    
    class FallbackTranslator implements TranslatorInterface {
        use TranslatorTrait;
    
        protected function doTrans($id, array $parameters = [], $domain = null, $locale = null) {
            // Custom logic (e.g., check DB, then fallback to Laravel's translator)
        }
    }
    

5. Testing with Mocks

  • Unit Tests: Mock TranslatorInterface to isolate logic:
    $mockTranslator = $this->createMock(TranslatorInterface::class);
    $mockTranslator->method('trans')
        ->with('welcome', ['name' => 'John'], 'auth', 'en')
        ->willReturn('Welcome, John!');
    
    $this->app->instance(TranslatorInterface::class, $mockTranslator);
    
  • Test Edge Cases: Verify locale/domain handling and parameter substitution.

Gotchas and Tips

⚠️ Pitfalls

  1. No Concrete Implementation:

    • This package defines only interfaces. To translate, you need:
      • Laravel’s built-in translator (trans() helper), or
      • A third-party implementation (e.g., symfony/translation, google/cloud-translate).
    • Fix: Ensure your TranslatorInterface binding points to a real translator.
  2. Parameter Syntax Mismatches:

    • Laravel’s trans() uses :placeholder syntax, but symfony/translation supports ICU MessageFormat (e.g., {count, plural, one{...} other{...}}).
    • Tip: Use trans() for Laravel-native strings; ICU syntax for Symfony-compatible messages.
  3. Missing Pluralization Helpers:

    • The contract lacks transChoice() (e.g., for "1 item" vs. "2 items"). Laravel provides this via trans_choice().
    • Workaround: Implement a wrapper:
      function transChoice($id, $number, array $parameters = [], $domain = null) {
          return app(TranslatorInterface::class)->trans(
              $id . ($number == 1 ? '' : '_plural'),
              $parameters,
              $domain
          );
      }
      
  4. Locale Not Persisted:

    • Setting the locale on the translator (setLocale()) is not the same as Laravel’s App::setLocale().
    • Tip: Use middleware to sync both:
      public function handle(Request $request, Closure $next) {
          $locale = $request->header('X-Locale') ?? 'en';
          app()->setLocale($locale);
          app(TranslatorInterface::class)->setLocale($locale);
          return $next($request);
      }
      
  5. Domain Isolation Issues:

    • If your custom translator ignores the domain parameter, translations may pull from the wrong catalog.
    • Debug: Log the domain in trans() calls to verify it’s being passed correctly.

🛠️ Pro Tips

  1. Extend TranslatorTrait for Boilerplate:

    • Reduces code duplication for custom translators:
      use Symfony\Component\Translation\TranslatorTrait;
      
      class CustomTranslator implements TranslatorInterface {
          use TranslatorTrait;
      
          protected function doTrans($id, array $parameters = [], $domain = null, $locale = null) {
              // Your custom logic here
          }
      }
      
  2. Use TranslatableMessage for API Responses:

    • Store TranslatableMessage objects in JSON:API or GraphQL responses to defer translation until the client’s locale is known.
  3. Cache Translator Instances:

    • Laravel’s service container caches singletons. For performance, bind your translator as a singleton:
      $app->singleton(TranslatorInterface::class, function () {
          return new AwsTranslator();
      });
      
  4. Validate Locale Formats:

    • Ensure locales match ICU standards (e.g., en_US vs. en-US). Use Locale::getPrimaryLanguage() to normalize:
      use Symfony\Component\Translation\Locale;
      $normalized = Locale::getPrimaryLanguage($locale);
      
  5. Debugging Silent Failures:

    • Add a trans() wrapper to log unresolved messages:
      $translator = app(TranslatorInterface::class);
      $translator->trans = function ($id, $params = [], $domain = null, $locale = null) use ($translator) {
          $result = $translator->trans($id, $params, $domain, $locale);
          if (strpos($result, '{{') !== false) { // Untranslated placeholder
              Log::warning("Untranslated ID: {$id} (Locale: {$locale}, Domain: {$domain})");
          }
          return $result;
      };
      
  6. Laravel-Specific Quirks:

    • View Composer: Use TranslatableMessage in view composers to pass deferred translations to Blade:
      View::composer('*', function ($view) {
          $view->with('welcomeMessage', new TranslatableMessage('welcome'));
      });
      
    • Livewire: Translate messages in component properties:
      public $translatableMessage;
      
      public function mount() {
          $this->translatableMessage = new TranslatableMessage('validation.required');
      }
      
      public function render() {
          return view('livewire.component', [
              'message' => app(TranslatorInterface::class)->trans($this->translatableMessage)
          ]);
      }
      

---
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.
codraw/entity-migrator
codraw/doctrine-extra
codraw/aws-tool-kit
codraw/validator
codraw/workflow
codraw/open-api
codraw/cron-job
codraw/process
codraw/log
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony