anomaly/translator-module
Translator module for the Anomaly CMS that adds a control panel for managing and editing language files and translation strings. Browse locales, update keys, and keep multilingual content in sync without editing source files by hand.
The Translator module is a Streams Platform (PyroCMS 3) addon, not a standalone Laravel package. It gives you an admin UI for walking through every resources/lang file in your installed addons and system, and writing translations per locale. Treat it as an internal tool for a PyroCMS site — not something you'd bolt onto a vanilla Laravel app.
Prerequisites
anomaly/streams-platform).composer.json constraints — check these before anything else, since the repo is barely maintained and older releases pin old Laravel versions.Install
composer require anomaly/translator-module
php artisan addon:install anomaly/translator-module
Where to look first, in this order
src/TranslatorModuleServiceProvider.php — see what it binds, what it registers, and whether it swaps the translator loader.config/translator.php — locale list, storage driver, cache settings.migrations/ — find the translations table; you'll query it constantly when debugging.src/Http/Controller/Admin/ — the actual UI endpoints; tells you what the screens do.resources/lang/en/ — the module's own strings, a good sample of the expected file layout.First useful run
Enable a second locale (typically in config/streams.php under locales, plus config/translator.php), log into /admin, open Translations, pick the addon you care about, choose the target locale, and translate the addon.php, field.php, and button.php files first — those are the strings users see on every screen and give the fastest visible win.
Sanity-check the result on the front end:
{{ trans('anomaly.module.pages::addon.name') }}
If that returns your new text without a redeploy, the module is wired up correctly.
File-first, database-second. The module reads your addon's resources/lang/{locale}/*.php files to build the key list, but stores entered translations in the database. Keep the files as the source of truth for which keys exist, and use the module's export path to write translated values back into resources/lang/{locale}/ before committing. This is the only way to keep staging, production, and your repo in agreement.
Key namespacing matters. Use the full addon namespace in calls so the module can attribute a key to the right addon:
// good – the module can map this to the addon
trans('anomaly.module.pages::addon.name');
// avoid – ambiguous, shows up as "system" or gets skipped
trans('addon.name');
Workflow per release
resources/lang/en/.php artisan list translator for the exact command name in your version) so the new keys appear in the UI.git diff resources/lang/ — review before committing.Programmatic seeding. When you need to ship translations without a UI pass (e.g. a fresh environment), write to the translations table directly via the module's model/repository, or better, seed from resources/lang files with a small artisan command so you only ever maintain one format.
Placeholder discipline. The UI is a text field; it will happily let a translator delete :name or :count. Add a validation rule or a post-export grep in CI:
grep -L ':count' resources/lang/de/**/*.php # flag suspects
Better: compare placeholder sets between en and the target locale in a test.
Fallback configuration. Set the platform's fallback locale so missing keys resolve to English rather than printing the raw key. The translator UI shows keys with no value, which is helpful — but only if fallback is configured so the live site never displays anomaly.module.pages::addon.name.
Translatable content vs UI strings. This module is for addon and system strings. For user-entered content (pages, posts, product descriptions), use the Streams/Pages multilingual features or a dedicated translatable-fields setup — don't try to route content through the translator.
Adoption is essentially zero. One star, no dependents, minimal commit activity. Pin the exact version in composer.json, and expect to read the source rather than documentation. If a bug blocks you, you will be patching it — fork it or vendor it with a composer patch (cweagans/composer-patches).
Caches will lie to you. After any translation change, clear in this order:
php artisan cache:clear
php artisan view:clear
php artisan translator:clear # if present in your version
php artisan streams:compile # rebuild Streams addon cache
Skipping streams:compile is the most common cause of "the addon disappeared" or "my new lang file isn't listed."
The DB is the runtime source. If you translate in the UI and then overwrite resources/lang files from git, your translations can be silently shadowed or lost depending on load order. Decide once — either DB-authoritative (import files, export rarely) or file-authoritative (export every time, treat DB as scratch) — and document it in the repo.
File permissions. Export writing to resources/lang fails quietly on read-only production filesystems. Run exports locally or in CI, never on prod.
Namespace collisions. Two addons using the same short key in an un-namespaced file will overwrite each other in the translations table. Always qualify with the addon slug.
Pluralization and trans_choice. The | separator format is not modeled by the UI — pluralized strings round-trip poorly. Keep plural forms in files, or hand-edit the exported file and never re-import it.
Long strings. Field types are basic; descriptions and multi-paragraph help text are painful to edit in the grid. Prefer flat keys and short sentences for anything you plan to translate.
Don't edit vendor/. Override the module's views through the standard Laravel vendor publish path, and its lang files through your app's own resources/lang/vendor/translator/.
Debugging checklist when a translation won't render
php artisan route:list | grep -i translat — confirm the admin routes are registered.config/app.php locale and the platform's locales config both include the target locale.app('translator')->getLoader() — in some versions the module decorates it, and a second registration elsewhere can override it.Extension points worth knowing. The module's service provider is the natural seam: bind a custom repository or decorate the translation loader there if you need to change where translations come from (e.g. a remote translation service). Its admin controllers and views are overridable, which is usually faster than forking.
Bottom line. Useful if you're already on PyroCMS 3 and need to hand a translation UI to non-developers. Not worth adopting into a new project — the maintenance burden outweighs what is essentially a table-driven admin CRUD over lang files, and you can rebuild the useful 20% (list keys, edit strings, export PHP arrays) in a day with a Filament or Nova resource.
How can I help you explore Laravel packages today?