avoo/serializer-translation-bundle
Install the Package
Add to composer.json:
{
"require": {
"avoo/serializer-translation-bundle": "^0.1"
}
}
Run composer update.
Register the Bundle
Add to config/bundles.php (Symfony 4+):
return [
// ...
Avoo\SerializerTranslationBundle\AvooSerializerTranslationBundle::class => ['all' => true],
];
For Symfony 3.x, add to AppKernel.php:
new Avoo\SerializerTranslationBundle\AvooSerializerTranslationBundle(),
Configure JMS Serializer
Ensure jms/serializer-bundle is installed and configured in config/packages/jms_serializer.yaml:
jms_serializer:
metadata:
directories:
FOSUserBundle: ../../vendor/friendsofsymfony/user-bundle/Resources/config/serializer
App: %kernel.project_dir%/config/serializer
First Use Case: Translate a Property
Annotate a DTO or entity property with @Serializer\SerializedName and @Serializer\Translation:
use JMS\Serializer\Annotation as Serializer;
class ProductDto
{
/**
* @Serializer\SerializedName("name")
* @Serializer\Translation("name", "product.name")
*/
public $name;
}
Ensure translations exist in translations/messages.en.yaml:
product:
name: "Product Name"
Serialize with Translation
$serializer = $container->get('jms_serializer');
$productDto = new ProductDto();
$productDto->name = 'product_name_key'; // Key, not value
$serialized = $serializer->serialize($productDto, 'json');
// Output: {"name":"Product Name"}
Use a translation service to fetch translations dynamically:
use Symfony\Contracts\Translation\TranslatorInterface;
class ProductDto
{
/**
* @Serializer\SerializedName("description")
* @Serializer\Translation("description", service="translator", method="trans")
*/
public $descriptionKey;
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
public function getDescriptionKey()
{
return $this->descriptionKey;
}
}
Configure in config/packages/jms_serializer.yaml:
jms_serializer:
handlers:
Avoo\SerializerTranslation\TranslationHandler:
service: translator
method: trans
Pass translation context (e.g., gender, pluralization):
/**
* @Serializer\Translation("greeting", "greeting.hello", context={"gender": "male"})
*/
public $gender;
Translations file:
greeting:
hello:
male: "Hello Sir"
female: "Hello Madam"
Translate properties in nested objects:
class AddressDto
{
/**
* @Serializer\Translation("street", "address.street")
*/
public $streetKey;
}
class UserDto
{
/**
* @Serializer\SerializedName("address")
*/
public $address;
}
Serializer will automatically translate streetKey in the nested AddressDto.
Use a custom handler for conditional logic:
use Avoo\SerializerTranslation\TranslationHandlerInterface;
class ConditionalTranslationHandler implements TranslationHandlerInterface
{
public function translate($value, array $context = [])
{
if ($value === 'special') {
return 'Special Value';
}
return $value;
}
}
Register in services.yaml:
services:
App\Serializer\ConditionalTranslationHandler:
tags: [jms_serializer.subscribing_handler]
Translate API responses globally by extending the serializer context:
$context = [
'translation_locale' => 'en',
'translation_domain' => 'api',
];
$serializer->serialize($data, 'json', $context);
Leverage Symfony’s Translator component for consistency:
# config/packages/translation.yaml
framework:
translator:
paths: ['%kernel.project_dir%/translations']
defaults:
locale: '%locale%'
Extend functionality by creating a custom translation provider:
use Avoo\SerializerTranslation\TranslationProviderInterface;
class DatabaseTranslationProvider implements TranslationProviderInterface
{
public function translate($key, array $context = [])
{
// Fetch from DB
return $this->db->fetchTranslation($key, $context);
}
}
Register in services.yaml:
services:
App\Serializer\DatabaseTranslationProvider:
tags: [avoo.serializer_translation.provider]
Mock translations in tests:
$translator = $this->createMock(TranslatorInterface::class);
$translator->method('trans')->willReturn('Mocked Translation');
$handler = new TranslationHandler($translator);
$this->assertEquals('Mocked Translation', $handler->translate('key'));
"product.name"), not values (e.g., "Product Name").$serialized output matches expected translations.SerializerTranslationException if translation files are missing or misconfigured.translations/messages.{locale}.yaml exists.framework.translation.paths in config/packages/translation.yaml.php bin/console debug:translation to list available translations.tags: [jms_serializer.subscribing_handler] with priority:
tags:
- { name: jms_serializer.subscribing_handler, priority: 255 }
$serializer->serialize($data, 'json', ['locale' => 'fr']);
@Serializer\MaxDepth to limit serialization depth:
/**
* @Serializer\MaxDepth(3)
*/
class UserDto { ... }
Configure JMS Serializer to log metadata:
jms_serializer:
metadata:
debug: true
Check logs for generated metadata (e.g., app/log/dev.log).
Temporarily log handler calls:
class TranslationHandler
{
public function translate($value, array $context = [])
{
error_log("Translating: {$value}, Context: " . json_encode($context));
return $this->translator->trans($value, $context);
}
}
Use a regex to validate translation keys (e.g., ^[a-z]+\.[a-z]+$):
if (!preg_match('/^[a-z]+\.[a-z]+$/', $key)) {
throw new \InvalidArgumentException("Invalid translation key: {$key}");
}
Implement TranslationProviderInterface for non-Symfony translation sources:
class ApiTranslationProvider implements TranslationProviderInterface
{
public function translate($key, array $context = [])
{
return $this->httpClient->request('GET', "/api/translate?key={$key}")->getContent();
}
}
Register with:
tags: [avoo.serializer_translation.provider]
Modify context
How can I help you explore Laravel packages today?