mlocati/concrete5-translation-library
A PHP library for managing Concrete CMS (concrete5) translations: extract translatable strings from core and package source code, build and update translation dictionaries, and generate the .mo files needed to localize sites and add-ons.
This is not a Laravel package — it is the standalone localization library extracted from Concrete CMS. There is no service provider, facade, config file, or Artisan command. You add it with composer require concrete5-community/translation-library and drive it through its own singleton:
use Concrete\Core\Localization\Translation;
$translator = Translation::getInstance();
$translator->getTranslator()->translate('Some key');
Look at these first, in this order:
Translation::getInstance() — the global entry point and the thing you will wrap.TranslatorAdapterInterface — the seam where you can plug your own translator implementation.CoreTranslationFileLoader, GettextTranslationFileLoader, Zend/Symfony variants) — these decide which on-disk format you must author.The realistic first use case is not greenfield Laravel i18n — use __()/@lang for that. You reach for this package when you are porting or co-hosting a Concrete CMS site (or a theme/addon's translation files) inside a Laravel app and need its dictionary formats and locale resolution to keep working unchanged.
1.11.0 specifically: the minimum PHP is now 7.2, up from 5.3. That is fine for any Laravel you'd run today (Laravel 6+ already requires 7.2), but if you pin platform.php in composer.json or run a legacy PHP 7.0/7.1 job in CI, the upgrade will fail resolution. Check your CI matrix and any config.platform override before bumping.
Wrap the singleton in the container. Never let Translation::getInstance() leak into application code. Bind a small adapter in a service provider so locale and adapter state are explicit and testable:
$this->app->singleton(TranslationService::class, function ($app) {
$translation = Translation::getInstance();
$translation->setAdapter(new CoreTranslatorAdapter($translation));
return new TranslationService($translation);
});
Adapter + loader is the composition point. The adapter decides how lookup happens; the loader decides where strings come from. For dictionaries already shipped as PHP arrays (the Concrete default), use the core adapter with the core file loader. For .mo catalogs, use the Gettext path — but see the gotchas about ext-gettext and system locales.
Bridge to Laravel's translator rather than replacing it. The cleanest integration is a custom TranslatorAdapterInterface implementation that delegates to Illuminate\Translation\Translator. That way your legacy Concrete keys resolve through the same loader chain, placeholder replacement, and fallback locale as the rest of the app, and you keep one source of truth for the active locale.
Locale switching belongs in middleware. Set the locale once per request from the same source your Laravel middleware uses (App::setLocale() plus the library's locale setter), so the two systems cannot drift.
Twig (new in 1.11.0). If you are porting a Concrete theme that renders Twig, the optional integration lets those templates call the same translation API as Blade. Install Twig explicitly — it is an optional, not a hard, dependency:
composer require twig/twig
Treat this as a migration aid, not a reason to introduce Twig into a Blade app.
Cache aggressively. File loaders hit disk. Load the dictionary once per locale into a repository or Laravel cache keyed by locale + file mtime; translation lookups should never touch the filesystem in a request loop.
Translation::getInstance() is a process-wide singleton holding the active adapter and locale. Under Octane, Swoole, or a queue worker, a locale set by one request/job bleeds into the next. Reset the locale in middleware terminate() or a job finally block, or bind a scoped instance.config.platform.php. Conversely, if you were blocked on this library because of the 5.3 floor, 1.11.0 unblocks you — but re-audit the code you assumed was PHP-5-era before adopting patterns from older docs.twig/twig in your root composer.json can resolve to a major version the bridge was not tested against. Pin it.ext-gettext must be installed and the target locales generated on the host (locale -a). Container images routinely lack them, producing silently untranslated output rather than errors. Prefer PHP-array dictionaries where you control the deployment.config/translation.php to publish, no Facade, no lang:publish. All configuration is imperative PHP. Budget for a thin wrapper class and document it, or the next developer will call the singleton directly.{{ }} and the library's placeholder conventions are unrelated. If your dictionaries use Concrete-style placeholders, translate first and pass the result through — do not expect Blade's escaping or :attribute replacement to apply.TranslatorAdapterInterface is the only sane integration seam. Subclassing or monkey-patching the singleton will break on minor upgrades.How can I help you explore Laravel packages today?