Getting started
Usage
How it works
Once you installed the plugin into your panel, it will automatically start generating translation keys for all the resources/pages/clusters/widgets where you extended the base class or implemented the HasTranslations interface.
When starting to use the package, the generated translation keys really explain themselves because of their consistency and because the key is generated based on the location of the text. However, below is an introduction with several examples for people just getting started to use the plugin, or to get an idea beforehand how it would work.
Translating forms
Consider the example that you have an EditUser page under a UserResource. The form defined in the UserResource ->form() method has a component as follows:
Forms\Components\Select::make('role'),
The plugin will now look at various aspects to determine a consistent translation key. In this case, it will notice that the form component is placed on a resource page App\Filament\Resources\UserResource\Pages\EditUser, the $operation is edit and that generally the form for that page is defined under the UserResource, so it will compile the following translation key: filament/resources/user-resource.form.components.role.label.
If the translation key is present in your language files it will get displayed, and if not, you will see the raw translation key, giving you a visual indication that you are missing this translation key. On production, the plugin will fall back to the default Filament behaviour, which is to make an English headline out of the component name. This will only happen for required translations, so if you are not having an optional helperText() or a hint() translation defined for a certain field, it will of course not display a raw translation key or force you to add it. However, if you add a translation key filament/resources/user-resource.form.components.role.helper_text, the plugin will automatically pick up the translation. The same applies for pretty much all other methods you can think of, from things like ->addActionLabel() to ->hint(), ->prefix(), ->suffix(), ->tooltip(), ->validationAttribute() and ->loadingMessage().
This is very convenient, because adding any translation comes down to just adding a new key in your translation file only and no other files need to be opened or touched.
The lang/{locale}/filament/resources/user-resource.php translation could now look like this:
return [
'form' => [
'components' => [
'role' => [
'label' => 'Role', // Required
'helper_text' => 'The role of the user', // Optional, will be recognized once you add it.
'loading_message' => 'Loading available roles from server', // Optional, will be recognized once you add it.
'prefix' => 'Role ', // Optional, will be recognized once you add it.
'suffix' => ' for user', // Optional, will be recognized once you add it.
// Etc...
],
],
],
];
Complex forms
The plugin can understand infinitely nested and complex structures, including those with Builders/Repeaters, nested actions, affix actions, et cetera. Consider the following example which is a good indication of how "clean" your Filament files will become without all the boilerplate code only for translations:
use RalphJSmit\Filament\AutoTranslator\AutoTranslator;
use RalphJSmit\Filament\AutoTranslator\Enums\ActionGroup;
Schemas\Components\Section::make('authorization') // The value passed here is used as a key in the translation file...
->schema([
Forms\Components\Select::make('role')
->required(),
Forms\Components\Actions::make([
Actions\Action::make('reset_password')
->schema([
Forms\Components\TextInput::make('password')
->required(),
// ...
])
->action(function (Actions\Action $action, array $data, User $record) {
// Perform action...
Notification::make()
->title(AutoTranslator::translateActionText($action, ActionGroup::Notifications, 'success.title', ['userName' => $record->name]))
->body(AutoTranslator::translateActionText($action, ActionGroup::Notifications, 'success.body'))
->success()
->send();
})
]),
]),
]),
Which corresponds to the following translation file in filament/resources/user-resource.php:
return [
'form' => [
'components' => [
'authorization' => [
'heading' => 'Section Heading',
'description' => 'Section Description Paragraph.',
'schema' => [
'role' => [
'label' => 'Role',
],
'actions' => [
'reset_password' => [
'schema' => [
'components' => [
'password' => [
'label' => 'New password',
],
// ...
],
],
'notifications' => [
'success' => [
'title' => 'Reset password for :userName',
'body' => 'Password successfully reset',
],
],
],
],
],
],
],
],
];
As you can see, the translation keys are generated very consistently and in a clear format that corresponds to their place in the Filament panel.
Manually request a translation key
You can also easily request a translation key manually using the AutoTranslator::translate*Text() methods, where you pass in the component you want to get a sub-key for and it will automatically compile and construct the right translation key:
use RalphJSmit\Filament\AutoTranslator\Enums\SchemaGroup;
Forms\Components\Select::make('role')
->options(fn (Forms\Components\Select $component) => [
'admin' => AutoTranslator::translateSchemaText($component, SchemaGroup::Components, 'options.admin.label'),
'user' => AutoTranslator::translateSchemaText($component, SchemaGroup::Components, 'options.user.label'),
]),
There are methods like these for translateSchemaText(), translateActionText() and translateTableText().
Each of these functions accept the schema/action/table component that you want to retrieve the translation key for, plus a "top-level" group:
SchemaGroup::Components: use it whenever you are interacting with something that originates from inside a form or infolist schema. This corresponds to e.g. theform.{components}orinfolist.{components}key in your resource.ActionGroup::Schema/ActionGroup::Notifications: useSchemawhen you work with something originating from an action schema, andNotificationswhen you work with something originating from a notification sent by an action. This corresponds to e.g. the{action_name}.{schema/notifications}key.TableGroup::Actions/TableGroup::BulkActions/TableGroup::Columns/TableGroup::Filters/TableGroup::Summarizers: useActionswhen you work with something originating from a table action,BulkActionswhen you work with something originating from a table bulk action,Columnswhen you work with something originating from a table column, andFilterswhen you work with something originating from a table filter. This corresponds to e.g. thetable.{actions/bulk_actions/columns/filters/summarizers}key.
Translating tables
Now, consider the following table on the UserResource:
$table
->columns([
Tables\Columns\TextColumn::make('name'),
Tables\Columns\TextColumn::make('email'),
])
->actions([
Tables\Actions\Action::make('reset_password')
->schema([
Forms\Components\TextInput::make('password')
->required(),
// ...
])
->action(function (Actions\Action $action, array $data, User $record) {
// Perform action...
Notification::make()
->title(AutoTranslator::translateTableText($action, ActionGroup::Notifications, 'success.title', ['userName' => $record->name]))
->body(AutoTranslator::translateTableText($action, ActionGroup::Notifications, 'success.body'))
->success()
->send();
})
])
->filters([
Tables\Filters\SelectFilter::make('role')
->options(fn (Tables\Filters\SelectFilter $filter) => [
'admin' => AutoTranslator::translateTableText($filter, TableGroup::Filters, 'options.admin.label'),
'user' => AutoTranslator::translateTableText($filter, TableGroup::Filters, 'options.user.label'),
]),
])
This corresponds to the following translation file:
return [
'table' => [
'columns' => [
'name' => [
'label' => 'Name', // Required
],
'email' => [
'label' => 'E-mail', // Required
'prefix' => 'E-mail: ', // Optional
],
],
'actions' => [
'reset_password' => [
'schema' => [
'components' => [
'password' => [
'label' => 'New password',
],
// ...
],
],
'notifications' => [
'success' => [
'title' => 'Reset password for :userName',
'body' => 'Password successfully reset',
],
],
],
],
'filters' => [
'role' => [
'label' => 'Role',
'options' => [
// Custom translation key requested manually
'admin' => 'Admin',
'user' => 'User',
],
],
],
'empty_state_heading' => 'No users found',
],
];
Translating actions
The package will also generate translation keys for actions, even if they are infinitely nested. Consider the following header action on the App\Filament\Resources\UserResource\Pages\EditUser page:
protected function getHeaderActions(): array
{
return [
Actions\Action::make('first_action')
->schema([
// ..
])
->extraModalFooterActions([
Actions\Action::make('second_action')
->schema([
//
]),
// ...
]),
];
}
The translation keys are generated as follows in the filament/resources/user-resource.php file:
return [
'pages' => [
'edit-user' => [
'navigation-label' => 'Navigation label',
'title' => 'Page title',
'subheading' => 'This page allows you to edit a user and perform several actions.',
'actions' => [
'first_action' => [
'label' => 'First action',
'schema' => [
'components' => [
// ..
],
],
'extra_modal_footer_actions' => [
'second_action' => [
'label' => 'Second action',
'schema' => [
'components' => [
// ..
],
],
],
// ...
]
],
],
],
],
];
As you can see, I hope you have gotten a good impression of how this package will have an automated translation key for pretty much anything you can imagine, from the important ones like $field->label() to the tiniest ones as $table->emptyStateDescription() or infinitely nested actions within actions. The goal is to remove at least 99% of __() usage in your application, so if you are running into an issue with a translation key, let me know at [email protected].
Translation namespace
The default namespace for a translation is generated based on the FQCN of the class where it originates. Shortly speaking, a FQCN App\Filament\Resources\UserResource will get a translation key of filament/resources/user-resource.php, whereas an FQCN App\Filament\Admin\Resources\UserResource will get a translation key of filament/admin/resources/user-resource.php.
In case that you want to override the namespace, you can override it using the $plugin->translationGroups() method:
$plugin
->translationGroups([
'App\\Filament\\Admin' => 'filament/admin-panel', // Re-codes all files under `App\Filament\Admin` to the translation key `filament/admin-panel/...`.
])
Override translation key
Sometimes you need to override the default translation key for a certain component or action. You can pass a custom relative translation key or absolute translation key:
TextInput::make('input')
->translationKey('alternative_name'), // Preserves full namespace such as `filament/resources/user-resource.form.fields.alternative_name.*`
If you would like to replace the entire translation key, you can also pass true as the second parameter to take the key as the top-level translation:
class ReusableAction extends Action
{
protected function setUp(): void
{
parent::setUp();
// Absolute without looking at the relative location of the action.
$this->translationKey('filament/actions/reusable-action', true);
}
}
The method is inserted via a Macro, so you might not see it auto-completed.