laravel-lang/attributes
Laravel Lang: Attributes adds PHP attribute helpers for Laravel Lang packages, simplifying localization-related metadata and tooling. Includes documentation, tests, and easy Composer installation for Laravel projects.
Installation:
composer require laravel-lang/attributes --dev
Add the service provider to config/app.php:
LaravelLang\Attributes\AttributesServiceProvider::class,
First Use Case: Define a translation attribute on a model or DTO:
use LaravelLang\Attributes\Label;
class UserRequest extends FormRequest {
#[Label('First Name')]
public string $first_name;
}
Access the translated label in Blade:
<x-input label="{{ $request->first_name->label }}" />
Or in validation messages:
$this->validate([
'first_name' => 'required|string',
], [
'first_name.required' => 'The :attribute is required.', // Automatically replaced with "First Name"
]);
Translation Files:
Ensure translations exist in resources/lang/{locale}/attributes.php:
return [
'first_name' => 'First Name',
];
config/attributes.php: Default configuration (e.g., fallback locales, attribute classes).vendor/laravel-lang/attributes/src/: Source code for custom attribute extensions.Use attributes on Laravel models, FormRequests, or DTOs to define translations:
use LaravelLang\Attributes\{Label, Placeholder};
class ProfileRequest extends FormRequest {
#[Label('Full Name')]
#[Placeholder('John Doe')]
public string $name;
#[Label('Date of Birth')]
public string $dob;
}
Access in Blade:
<input
type="text"
name="name"
placeholder="{{ $request->name->placeholder }}"
aria-label="{{ $request->name->label }}"
>
Leverage attributes in validation messages:
$this->validate([
'name' => 'required|string|max:255',
'dob' => 'required|date',
], [
'name.required' => 'The :attribute is required.', // Uses "Full Name"
'dob.date' => 'The :attribute must be a valid date.', // Uses "Date of Birth"
]);
For dynamic fields (e.g., API-generated forms), resolve attributes at runtime:
use LaravelLang\Attributes\AttributeResolver;
$resolver = new AttributeResolver();
$label = $resolver->resolveLabel($request, 'email'); // Returns translated label
Extend the package with new attributes (e.g., [Tooltip]):
namespace App\Attributes;
use LaravelLang\Attributes\Attribute;
#[Attribute('tooltip')]
class Tooltip extends \LaravelLang\Attributes\Attribute {
public function __construct(public string $text) {}
}
Register the custom attribute in config/attributes.php:
'attributes' => [
\App\Attributes\Tooltip::class,
],
Configure fallbacks in config/attributes.php:
'fallback_locale' => 'en',
'fallback_to_attribute_name' => true, // Show "first_name" if translation missing
Blade Components: Create reusable components for form fields:
<x-form.input :field="$request->first_name" />
// In a Blade component
public function render() {
return <<<'blade'
<input
type="text"
name="{{\$field->name}}"
placeholder="{{\$field->placeholder}}"
aria-label="{{\$field->label}}"
>
blade;
}
API Responses: Use attributes in API validation error responses:
$this->validate([...]);
return response()->json([
'errors' => $this->errors()->messages(),
], 422);
The :attribute placeholder will auto-resolve to translated labels.
Testing: Mock attributes in PHPUnit:
$request = new ProfileRequest();
$request->shouldReceive('getAttributeLabels')
->andReturn(['name' => 'Full Name']);
Localization Workflow:
php artisan lang:publish to copy translation files.resources/lang/{locale}/attributes.php.php artisan lang:test.Attribute Reflection Overhead:
public function getAttributeLabels(): array {
return $this->attributeLabels ??= (new AttributeResolver())->resolveAll($this);
}
Translation Key Conflicts:
first_name) may clash with existing __('first_name') calls.attributes.php:
return [
'user.first_name' => 'First Name',
];
Then reference in attributes:
#[Label('user.first_name')]
Blade Caching:
@once directives or disable caching for dynamic forms:
@once
<x-form.input :field="$dynamicField" />
@endonce
Fallback Locale Mismatch:
en instead of en_US).app.locale and attributes.fallback_locale consistently.Custom Attribute Registration:
config/attributes.php.attributes array includes your custom class:
'attributes' => [
\LaravelLang\Attributes\Label::class,
\App\Attributes\Tooltip::class,
],
dd((new \LaravelLang\Attributes\AttributeResolver())->resolveAll($request));
php artisan lang:test to ensure all attribute keys are translated.config/attributes.php:
'debug' => true, // Logs missing translations to Laravel log
Custom Attribute Resolvers: Override the default resolver for complex logic:
use LaravelLang\Attributes\AttributeResolverInterface;
class CustomResolver implements AttributeResolverInterface {
public function resolveLabel(object $object, string $property): string {
// Custom logic here
}
}
Register in config/attributes.php:
'resolver' => \App\Services\CustomResolver::class,
Dynamic Attribute Loading: Load attributes from a database or API:
#[Attribute('dynamic_label', [
'source' => 'database',
'table' => 'form_fields',
'column' => 'label'
])]
class DynamicLabel extends \LaravelLang\Attributes\Attribute {
// ...
}
Translation Providers: Extend the package’s translation provider to support custom sources (e.g., JSON files):
use LaravelLang\Attributes\TranslationProvider;
class CustomTranslationProvider extends TranslationProvider {
public function loadTranslations(): array {
return array_merge(
parent::loadTranslations(),
json_decode(file_get_contents('custom/attributes.json'), true)
);
}
}
Bind in a service provider:
$this->app->bind(
\LaravelLang\Attributes\TranslationProvider::class,
\App\Providers\CustomTranslationProvider::class
);
Use in API Resources: Attach translated labels to API responses:
public function toArray($request) {
return [
'data' => [
'name' => $this->name,
'label' => $this->getAttributeLabel('name'),
],
];
}
Combine with Laravel Breeze/Jetstream: Override default form components to use attributes:
<x-jet-input
type="text
How can I help you explore Laravel packages today?