Working with Symfony and Doctrine entities translations

As you know there is no built-in feature in Doctrine2 for translating entities. If you need entities translation you can choose one of several already existing bundles or create the new one. As there is no „the right” way of working with Doctrine2 translations I just want to share my experience.

DoctrineExtensions: translatable
I don’t really like Doctrine2 Translatable extension as:

  • the idea of storing each translatable property as different record in DBis not a good solution to me;
  • quite complex logic when we want to display form to edit translations for all languages that we support in application;
  • changing default locale causes DB data changes;
  • maybe this is only my opinion by this is not really intuitive for me.

I’ve used this solution only in one project and it’s not something that I remember well.
You can try it but I’m not recommending it. Official documentation can be found here.

KnpLabs\DoctrineBehaviors: translatable

This is my favorite entities translation implementation for Doctrine2 – I couldn’t find better solution so far. Even for project that I was migrating schema from old symfony 1.0 where there were used Propel translations with Active Record models like Category and CategoryI18n – almost everything can be configured and even if there is something that can’t be configured we can just override it.

Note: KNPLabs translatable behaviour is using PHP traits. If you don’t know it already please read the documentation.

In this example I will create simple Post entity for storing translations in three languages (you can use as many languages as you need). I’m also using Symfony3 framework with its installer to setup new project. We will also need DB connection (mysql, sqlite or any other you like).

Install KnpLabsBehaviors bundle and configure it according to documentation.

Next step is to generate our Post and PostTranslation entities for storing translations. Post will be our main entity and PostTranslation will be used for storing translations:

Post entity with properties:

  • Post->status
  • Post->created_at

and PostTranslation entity with properties:

  • PostTranslation->title
  • PostTranslation->content

Note: if you want to generate PostTranslation entity using Doctrine2 commmand you would have to remove PostTranslation->id property after generation as Knp is adding it automatically generating also proper relations for translations.

When entities are ready we should add new methods to be able to work with translations. We don’t have to that manually, just to import and use proper traits from Knp bundle:

  • to translated entity add import of: use Knp\DoctrineBehaviors\Model\Translatable\Translatable;
  • to entity which will be storing translation we should add: use Knp\DoctrineBehaviors\Model\Translatable\Translation;

Then we need to update DB schema:

and we can start working with translations:

Translation interface is simple and intuitive. In similar scenario we would have to add proper relation and persist translation entity. Thanks to Knp this is handled by calling Post::mergeNewTranslations method.

For me it is also very useful to be able to call direct method on translated entity based on current locale:

Thanks to this method we can simply call:

Knp is shipped with TranslatableSubscriber which is also responsible for setting current locale on translated entities. We can change that locale by calling setCurrentLocale on translated entity (Post in our case).

Note: if you need Doctrine2 entities translations to be managed from Symfony forms, this implementation works great with A2lix\TranslationFormBundle bundle. I described this integration in dedicated blog post.