fchris82/jms-translation-bundle-fork
A fork of the JMS Translation Bundle for Symfony, offering tools to extract, manage, and update translation messages. It integrates with Symfony’s translation workflow to help keep locale files organized and up to date.
This is a fork of the original JMSTranslationBundle (JMS\TranslationBundle namespace). It provides three things: extractors that scan your PHP/Twig sources for translation calls, a web UI for editing translation files side by side, and console commands to extract and audit keys. It does not replace Symfony's runtime translator — it's a workflow tool around your existing messages.* files.
Minimal setup:
composer require --dev fchris82/jms-translation-bundle-fork
Register the bundle (JMSTranslationBundle in AppKernel/bundles.php), then add a config block. One named config per project or per bundle is the normal pattern:
# config.yml
jms_translation:
configs:
app:
dirs: ["%kernel.root_dir%/../src", "%kernel.root_dir%/../templates"]
ignored_dirs: ["%kernel.root_dir%/../vendor", "%kernel.root_dir%/../var"]
output_dir: "%kernel.root_dir%/../translations"
enabled_extractors: [php, twig]
output_format: xliff
Expose the editor UI under a protected prefix and add a firewall rule:
# routing.yml
jms_translation:
resource: "@JMSTranslationBundle/Resources/config/routing.xml"
prefix: /_trans
# security.yml
access_control:
- { path: ^/_trans, roles: ROLE_TRANSLATOR }
First use case: on a legacy app, point dirs at your source tree and run a dry run to see everything the extractors find before touching files:
bin/console translation:extract en --config=app --dry-run
bin/console translation:extract en --config=app # writes skeletons
Then open /_trans, filter to untranslated messages, and edit inline. Saving writes directly back to the files in output_dir.
Extract → review diff → commit. Treat extraction output as generated code. Run translation:extract locally after adding trans() calls, review the diff (new keys, removed keys), and commit it with the feature. In CI, run with --dry-run and fail the build if the output would change — that keeps the translation files in sync with the code without relying on developers to remember.
One config per bundle/output directory. output_dir is per-config, so if you keep Resources/translations inside bundles, define a config for each and loop in a script:
for c in app admin_bundle api_bundle; do
bin/console translation:extract en --config=$c --no-backup
done
Curate extraction with annotations. For PHP, the extractors only pick up literal keys. Use @Ignore on methods/classes with dynamic keys, and @Desc/@Meaning to carry context into the generated file (the UI shows it to translators). This is how you keep translation:find-unused output actionable:
/** @JMS\TranslationBundle\Annotation\Ignore */
public function labelFor($type) { return $this->translator->trans('type.'.$type); }
Custom extractors for non-PHP/Twig sources. If you have JS or other files with keys, implement the bundle's ExtractorInterface (or a file extractor) and register it as a service tagged jms_translation.extractor. This is the main extension point for wiring pre-existing key catalogs into the same UI/diff workflow.
Web UI as the translator handoff. The practical workflow is: developer extracts and commits the English source strings, translator opens /_trans on staging, filters "untranslated", edits, saves. Because saving writes files, run it against a checkout where the translator's changes can be committed (or use the remote storage option to keep them in a DB and out of git).
Remote storage. The config supports a remote_storage block (e.g. MySQL) so edits go to a table rather than the filesystem. Useful in teams where translation files shouldn't be hand-edited, but it requires provisioning the table and is an extra moving part — most small teams should stick to file-backed output.
Runtime is unchanged. The bundle doesn't need to be enabled in prod for translation lookup; Symfony's normal translator reads the files you generated. You generally only need the bundle (and its routes) in dev/staging.
trans($var), concatenated keys ('foo.'.$bar), and keys built in loops are never extracted. Annotate them with @Ignore and maintain them by hand, or find-unused will keep flagging them as dead.trans('key', [], 'domain') with a string literal domain is attributed correctly; variable domains fall into the default domain and produce misleading output.--no-backup avoids .bak files littering the tree; add *.bak to .gitignore if you don't use it./_trans aggressively. The UI writes to disk, and it writes wherever output_dir and dirs point. Never expose it publicly, and keep dirs narrow — pointing at vendor/ makes extraction slow and pollutes output with third-party keys.output_dir. A brand-new locale needs an initial extract (translation:extract fr --config=app) or an empty file before it appears.translation:find-unused is a hint, not a verdict. Dynamic keys, keys used only in YAML/DB-driven content, and keys consumed by external systems show up as unused. Expect false positives.ContainerAwareCommand, %kernel.root_dir%). Pin the version in composer.json, check constraints against your Symfony/PHP version before adopting, and treat it as unmaintained — don't build long-term process around it without a migration path to Symfony's own debug:translation/lint:xliff tooling or a maintained extraction tool.How can I help you explore Laravel packages today?