Skip to main content
The TranslatorPlugin automatically discovers and registers translation files from all enabled modules, making module translations available through Laravel’s localization system.

Overview

This plugin scans each module’s lang directory and registers it as a translation namespace. It supports both PHP translation files and JSON translation files.

Source Location

InterNACHI\Modular\Plugins\TranslatorPlugin

Activation

The plugin uses the AfterResolving attribute to activate when the translator is resolved:
#[AfterResolving(Translator::class, parameter: 'translator')]
class TranslatorPlugin extends Plugin
This plugin automatically activates after the translator is resolved from the service container.

How It Works

1. Discovery Phase

The discover() method finds all language directories:
public function discover(FinderFactory $finders): iterable
{
    return $finders
        ->langDirectoryFinder()
        ->withModuleInfo()
        ->values()
        ->map(fn(ModuleFileInfo $dir) => [
            'namespace' => $dir->module()->name,
            'path' => $dir->getRealPath(),
        ]);
}

2. Registration Phase

The handle() method registers each namespace and JSON path:
public function handle(Collection $data): void
{
    $data->each(function(array $row) {
        $this->translator->addNamespace($row['namespace'], $row['path']);
        $this->translator->addJsonPath($row['path']);
    });
}
Each module’s translations are namespaced by the module name, preventing conflicts between modules.

Expected Module Structure

For translations to be discovered, they must be located in:
app-modules/
└── blog/
    └── lang/
        ├── en/
        │   ├── messages.php
        │   └── validation.php
        ├── es/
        │   ├── messages.php
        │   └── validation.php
        ├── en.json
        └── es.json

Translation Types

PHP Translation Files

PHP translation files return associative arrays: lang/en/messages.php:
return [
    'welcome' => 'Welcome to our blog',
    'post_created' => 'Post created successfully',
    'post_updated' => 'Post updated successfully',
    'post_deleted' => 'Post deleted successfully',
];

JSON Translation Files

JSON files provide key-value translations: lang/en.json:
{
    "Welcome to our blog": "Welcome to our blog",
    "Post created successfully": "Post created successfully"
}

Usage

Accessing PHP Translations

Use the module namespace prefix with double colon:
// In controllers or other PHP files
__('blog::messages.welcome');

// With parameters
__('blog::messages.greeting', ['name' => 'John']);

// Using the trans() helper
trans('blog::messages.welcome');

In Blade Templates

{{ __('blog::messages.welcome') }}

{{ __('blog::messages.greeting', ['name' => $user->name]) }}

@lang('blog::messages.welcome')

Accessing JSON Translations

JSON translations don’t require a namespace:
__('Welcome to our blog');
{{ __('Welcome to our blog') }}

Pluralization

Module translations support Laravel’s pluralization features: lang/en/messages.php:
return [
    'posts_count' => '{0} No posts|{1} One post|[2,*] :count posts',
];
Usage:
trans_choice('blog::messages.posts_count', 0); // "No posts"
trans_choice('blog::messages.posts_count', 1); // "One post"
trans_choice('blog::messages.posts_count', 5); // "5 posts"

Nested Translations

Organize translations using nested arrays: lang/en/messages.php:
return [
    'posts' => [
        'create' => 'Create new post',
        'edit' => 'Edit post',
        'delete' => 'Delete post',
    ],
    'comments' => [
        'add' => 'Add comment',
        'edit' => 'Edit comment',
    ],
];
Access with dot notation:
__('blog::messages.posts.create');
__('blog::messages.comments.add');

Locale Fallback

Laravel’s fallback locale configuration applies to module translations. If a key isn’t found in the current locale, Laravel will check the fallback locale.
Configure in config/app.php:
'fallback_locale' => 'en',

Validation Messages

Modules can define custom validation messages: lang/en/validation.php:
return [
    'required' => 'The :attribute field is required.',
    'unique' => 'The :attribute has already been taken.',
    
    'attributes' => [
        'title' => 'post title',
        'content' => 'post content',
    ],
];
Use in form requests:
public function messages()
{
    return [
        'title.required' => __('blog::validation.required', ['attribute' => 'title']),
    ];
}

Publishing Translations

If you want to allow users to customize module translations, you can publish them:
// In a service provider
public function boot()
{
    $this->publishes([
        __DIR__.'/../lang' => $this->app->langPath('vendor/blog'),
    ], 'blog-translations');
}
Users can then publish and customize:
php artisan vendor:publish --tag=blog-translations

Testing

Test translations in your module tests:
public function test_welcome_message_is_translated()
{
    $this->app->setLocale('es');
    
    $message = __('blog::messages.welcome');
    
    $this->assertEquals('Bienvenido a nuestro blog', $message);
}

Dependencies

  • Illuminate\Translation\Translator - Laravel’s translator
  • InterNACHI\Modular\Support\FinderFactory - Directory discovery

Build docs developers (and LLMs) love