creative/symfony-db-i18n-bundle
Install the Bundle
composer require creative/symfony-db-i18n-bundle
Add to config/bundles.php:
Creative\DbI18nBundle\DbI18nBundle::class => ['all' => true],
Configure Locales
Define supported locales in config/services.yaml:
parameters:
locales: ['en', 'fr', 'de']
Update Database Schema Run:
php bin/console doctrine:schema:update --force
(or create a custom migration if preferred)
First Translation Usage In Twig templates, set the default domain:
{% trans_default_domain 'db_messages' %}
Then use translations as usual:
{{ 'welcome.message'|trans }}
Create a CRUD Controller/Interface
Use Symfony’s make:crud or manually scaffold a controller for Translation entity (auto-generated by the bundle).
Example route:
# config/routes.yaml
translation:
resource: 'translation'
type: 'simple'
path: '/admin/translations'
Seed Initial Messages
Import existing .po/.yml files into the database:
php bin/console db:i18n:import
Database Structure
The bundle creates a translation table with columns:
domain (e.g., db_messages)locale (e.g., en, fr)message_id (e.g., welcome.message)message (translated string)Integration with Translator
The bundle hooks into Symfony’s TranslatorInterface. Messages from the database take precedence over files.
Example service override (if needed):
# config/services.yaml
services:
Symfony\Contracts\Translation\TranslatorInterface:
alias: 'creative.db_i18n.translator'
Dynamic Locale Switching Use middleware or a listener to set the locale from the request:
// src/EventListener/LocaleListener.php
public function onKernelRequest(GetResponseEvent $event) {
$request = $event->getRequest();
$locale = $request->getPreferredLanguage(['en', 'fr']);
$this->translator->setLocale($locale);
}
Batch Updates For bulk imports/updates, use the console command:
php bin/console db:i18n:import --file=messages.en.yml
Custom Validation
Extend the Translation entity to add validation (e.g., max length):
use Symfony\Component\Validator\Constraints as Assert;
/**
* @Assert\Length(max=255)
*/
private $message;
Translation Events
Listen for translation.pre_save or translation.post_update events to log changes or notify admins:
// src/EventSubscriber/TranslationSubscriber.php
public static function getSubscribedEvents() {
return [
'translation.pre_save' => 'onPreSave',
];
}
Fallback Logic
Configure fallback locales in db_i18n.yaml:
fallback_locales:
fr: en
de: en
API-Friendly Endpoints Expose translations via API for frontend apps:
# config/routes.yaml
api_translations:
path: /api/translations/{locale}
controller: App\Controller\TranslationController::getTranslations
methods: GET
Locale Mismatch Errors
InvalidArgumentException if locales parameter is missing or misconfigured.locales is defined in services.yaml and matches the database entries.Schema Updates
doctrine:schema:update after installation.Caching Headaches
php bin/console cache:clear
Domain Conflicts
message_id but different domain may overlap.auth, validation) and set trans_default_domain per template.Character Encoding
é, ü) may corrupt in the database.utf8mb4:
# config/packages/doctrine.yaml
doctrine:
dbal:
charset: utf8mb4
Check Database Contents Verify translations exist:
SELECT * FROM translation WHERE locale = 'en' AND domain = 'db_messages';
Enable Debug Mode
Temporarily set APP_DEBUG=true in .env to see detailed translation errors.
Log Translator Calls Override the translator to log which messages are being fetched:
public function trans($id, array $parameters = [], $domain = null, $locale = null) {
error_log("Translating: {$id}, Domain: {$domain}, Locale: {$locale}");
return parent::trans($id, $parameters, $domain, $locale);
}
Console Command Debugging
Use --verbose flag for console commands:
php bin/console db:i18n:import --verbose
Custom Storage
Override the default TranslationRepository to add logic (e.g., soft deletes):
// src/Repository/CustomTranslationRepository.php
class CustomTranslationRepository extends TranslationRepository {
public function remove(Translation $translation) {
$translation->setIsDeleted(true);
$this->getEntityManager()->flush();
}
}
Custom Importer
Extend the ImportCommand to support additional file formats (e.g., JSON):
// src/Command/CustomImportCommand.php
class CustomImportCommand extends ImportCommand {
protected function parseFile(string $file): array {
// Custom logic for JSON/other formats
}
}
Translation API
Build a GraphQL or REST API layer on top of the Translation entity for headless CMS use cases.
Webhook Notifications Trigger webhooks on translation updates via event listeners for real-time sync with other services.
How can I help you explore Laravel packages today?