Installation:
composer require jms/translation-bundle
Enable the bundle in config/bundles.php:
return [
// ...
JMS\TranslationBundle\JMSTranslationBundle::class => ['all' => true],
];
Configuration:
Add to config/packages/jms_translation.yaml:
jms_translation:
configs:
app:
dirs: ["%kernel.project_dir%/translations"]
output_dir: "%kernel.project_dir%/translations"
excluded_namespaces: []
excluded_dirs: [cache, logs]
file_template: "%%locale%%_%%domain%%.xliff"
ignored_domains: []
extractors: [twig, validation, form]
First Use Case: Extract translations from Twig templates:
php bin/console translation:extract
This generates .xliff files in your translations/ directory.
Twig Templates:
Automatically extracts trans and transchoice calls in Twig:
{{ 'welcome.message'|trans({'%name%': user.name}) }}
Run:
php bin/console translation:extract twig
Validation Messages: Extracts Symfony validator constraints:
use Symfony\Component\Validator\Constraints as Assert;
/**
* @Assert\NotBlank(message="user.email.not_blank")
*/
private $email;
Run:
php bin/console translation:extract validation
Forms: Extracts form labels, placeholders, and constraints:
$builder->add('email', EmailType::class, [
'label' => 'user.email.label',
'constraints' => [new NotBlank(['message' => 'user.email.not_blank'])]
]);
Run:
php bin/console translation:extract form
Web UI:
Access the translation editor at /_translations (configured in routing.yaml):
jms_translation:
routing:
prefix: /_translations
ICU Format: Dump translations to ICU format (useful for JavaScript/i18n libraries):
php bin/console translation:dump --format=icu
Dynamic Forms:
Use translation_domain option to specify the domain for form translations:
$builder->add('email', EmailType::class, [
'translation_domain' => 'validation'
]);
Choice Lists:
Translate choices in ChoiceType:
$builder->add('gender', ChoiceType::class, [
'choices' => [
'male' => 'user.gender.male',
'female' => 'user.gender.female',
],
'translation_domain' => 'forms',
]);
Extend Extractors:
Create a custom extractor by implementing JMS\TranslationBundle\Extractor\ExtractorInterface:
namespace App\Extractor;
use JMS\TranslationBundle\Extractor\ExtractorInterface;
class CustomExtractor implements ExtractorInterface {
public function extract(array $config, $source) {
// Custom logic to extract translations
return ['custom.key' => 'Custom Translation'];
}
}
Register in config/packages/jms_translation.yaml:
jms_translation:
configs:
app:
extractors: [custom]
Register Extractor: Use a compiler pass to add your extractor:
namespace App\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
class AddCustomExtractorPass implements CompilerPassInterface {
public function process(ContainerBuilder $container) {
$definition = $container->findDefinition('jms_translation.extractor');
$definition->addMethodCall('addExtractor', [new Reference('app.custom_extractor')]);
}
}
simplexml_load_file() expects parameter 1 to be a valid path:
Ensure .xliff files are generated in the correct directory. Verify output_dir in jms_translation.yaml.
strtolower() expects parameter 1 to be string:
Update to v1.5.1+ to fix strict type issues. If stuck on an older version, cast objects to strings manually in your templates:
{{ ('welcome.message' ~ { '%name%': user.name|string })|trans }}
The format "yml" does not exist:
Use yaml instead of yml in configuration. The bundle supports xliff, yaml, and icu formats.
Missing Twig Nodes:
If trans calls aren’t extracted, ensure your Twig templates are compiled. Run:
php bin/console cache:clear
Excluded Directories:
The excluded_dirs option in jms_translation.yaml does not support glob patterns. Use absolute paths:
excluded_dirs: ["%kernel.project_dir%/var/cache"]
Translation Domains:
If translations aren’t found, verify the translation_domain is set in Twig:
{{ 'welcome.message'|trans({}, 'messages') }}
Or in forms:
$builder->add('field', TextType::class, ['translation_domain' => 'forms']);
Web UI Access:
The default route /_translations may conflict with other routes. Customize the prefix:
jms_translation:
routing:
prefix: /admin/translations
Memory Limits: Large projects may hit memory limits during extraction. Increase PHP memory:
php -d memory_limit=-1 bin/console translation:extract
Or split extraction by domain:
php bin/console translation:extract twig --domain=messages
Caching: Disable caching during development to see real-time changes:
# config/packages/framework.yaml
framework:
twig:
cache: false
Custom XLIFF Headers:
Override the XLIFF template in Resources/views/JMSTranslationBundle/base.html.twig to add custom metadata.
Post-Extraction Hooks: Use Symfony events to modify extracted translations:
namespace App\EventListener;
use JMS\TranslationBundle\Event\ExtractionEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class TranslationExtractorSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return [
'jms_translation.extraction' => 'onExtraction',
];
}
public function onExtraction(ExtractionEvent $event) {
$translations = $event->getTranslations();
$translations['custom.key'] = 'Overridden Value';
$event->setTranslations($translations);
}
}
Validation Constraint Extraction:
To exclude specific constraints from extraction, use the translation option:
use Symfony\Component\Validator\Constraints as Assert;
/**
* @Assert\NotBlank(translation="user.email.not_blank")
* @Assert\Email(translation="user.email.invalid")
*/
private $email;
Only translation-annotated constraints will be extracted.
Log Extracted Messages: Enable debug logging to see what’s being extracted:
# config/packages/monolog.yaml
monolog:
handlers:
main:
level: debug
channels: ["translation"]
Then run:
php bin/console translation:extract --debug
Compare XLIFF Files:
Use diff to compare before/after extraction:
diff <(php bin/console translation:dump --format=xliff) translations/en.xliff
Test Extraction:
Use the translation:test command to validate extracted messages:
php bin/console translation:test
How can I help you explore Laravel packages today?