Your Guide to Symfony 4 Internationalization Beyond the Basics

Symfony is a very popular PHP framework which also consists of reusable components for cases when we want to use only some services only. In terms of i18n there are a lot of options such as the Translation Component for handling translation messages, the Intl component for displaying locale aware information and localized Routing configurations for handling URL paths to translated pages. We are going to leverage in full the provided abstractions with some advanced examples of those components in action.

There is an excellent introduction on how to translate Symfony 3 applications like a boss where you can learn how to use the Translation Component In this article, we are going to step up even further and utilize to the max this library and see some practical and advanced examples of i18n.

For the purposes of this tutorial, we are using PHP 7 and the latest Symfony 4. All the code examples are available on GitHub.

Localized Routing

The latest version of Symfony offers among other improvements, the ability to add unique paths per locale. That way we can show a different URL pathname for each language. If you couple that with annotation specific configuration we can have our routes map to each locale without much effort.

Let’s see how we can do that.

First, expose some locale parameters in the services.yaml file.

File: phrase-app-symfony4/phrase-app/config/services.yaml

Here app_locales is a list of supported locales for our app. We also used bind to wire them as parameters in our controllers.

Next, add the annotation configuration to prefix our routes with locale information:

File: phrase-app-symfony4/phrase-app/config/routes/annotations.yaml

With that, all our routes need to have a _locale prefix, even for the index route.

We can also customize further each route in the controller.

File: phrase-app-symfony4/phrase-app/src/Controller/DefaultController.php

File: phrase-app-symfony4/phrase-app/templates/index.html.twig

With those configuration values, we can visit the following routes for the Greek locale:

  • /el/giasas
  • /el/hello

and have the same page translated into Greek.

Twig extensions

If you are building web apps using Twig templates then you will benefit from having to include the Twig extensions library as it’s bundled with some useful tools that enhance the existing functionality. For example, the i18n extension adds Gettext support and if you are working with.PO  and.MO  files then it’s really handy.

To install them just use composer first

And make sure you enable them in the twig_extensions.yaml

File: phrase-app-symfony4/phrase-app/config/packages/twig_extensions.yaml

The i18nExtension adds Gettext support and you may find useful our guide to the Gettext Tools.

The IntlExtension has a very useful filter for printing localizable date string from timestamps or DateTime instances, localizable numbers and localizable currencies. Let’s see an example below:

Define a new controller:

File: phrase-app-symfony4/phrase-app/src/Controller/ExampleController.php

and add the following template:

File: phrase-app-symfony4/phrase-app/templates/twig.html.twig

Then navigate to a few supported locale routes and see the results. I’ve included some examples for Greek, German and French.

Basic Intl Component

If you want to store and operate on data using a locale-independent binary representation and operations then your best bet is to use the Intl Component.
This is an extension that adds locale-specific tools for special cases.

To install it invoke the following command:

Some useful use cases for this library are:

  • Writing or reading locale-specific information to files: You can use the, for example, to write PHP resources in a locale-specific format for later use or for archiving.
  • Retrieving locale information: You have a variety of methods to retrieve the list of languages, scripts, locales, country names, currencies or currency symbols for use. Those prove very handy as you won’t have to hardcopy anything in your code.

Let’s see some examples below:

Printing the list of locales based on the current locale:

Printing the list of currencies based on the current locale:

Printing the list of currencies based on the current locale:

Database Translations

In most cases, you might be storing your content in a database using Docturine ORM. Typically if you want to have your content translated to different locales then you want to have the content stored in a separate column or table where you can reference.

For example, if you have an Entity called Product which has the following fields :


and you want to give translations for the Description field you can add a few fields such as Description_el or Description_de to store the Greek and German translations. 

This solution is suited only if you plan only to support a couple of locales. A more scalable approach is to include a separate table for the model that represent its Translations, such as in that case we would need the ProductTranslation where we can refer to each Product Id with a list of translations. In order to retrieve the model with the relevant translations, you will need to do a LEFT OUTER JOIN on the Product and ProductTranslation entities.

You can choose to carry out your own repository manager for that or we can use a bundle that does that for us. There is a well-known Bundle called DocturineBehaviors from KnpLabs that add a few interesting traits and one of them is the translatable trait. 

Let’s see how we can use that.

First, require the library and add it to the list of loaded bundles.

File: phrase-app-symfony4/phrase-app/config/bundles.php

Create a new entity either from the generator or by hand called Product and add some fields.

File: phrase-app-symfony4/phrase-app/src/Entity/Product.php

Now create the ProductTranslation entity that will have all the fields that we want to provide translations.

File: phrase-app-symfony4/phrase-app/src/Entity/ProductTranslation.php

Create a ProductRepository class as a base manager.

File: phrase-app-symfony4/phrase-app/src/Repository/ProductRepository.php

Now if you haven’t set up the database you can do it now.

and then

The last command will create all the necessary migrations for the table schemas.

Now run the migrations to apply them in the database.

If you have the database explorer on you can see that there are 2 tables created one for the Product and one for the ProductTranslation:

Now in order to populate the database, you can use the database explore itself or use the Product Entity itself.

Now if you can retrieve the Product Entity using the repository manager and pass the current locale in the translate method to retrieve the correct translations for the field:

Integrating Phrase In-Context Editor

If you want to take your internationalization process to the next level, then I suggest you look at the Phrase platform and specifically the In-Context Editor they offer.
It will give you better insights on how the strings appear in your software and also you can manage your translations easier and will help you keep track of missing translations.

Let’s see how can we integrate this into our Symfony App.

I’m using the guidelines from the setup page

Start by creating a new configuration file:

Copy the configuration from the dev package to the newly created translation folder.

Make sure to update the bundles to include the new environment.

File: phrase-app-symfony4/phrase-app/config/bundles.php

Create the PhraseTranslator class as per instructions

File: phrase-app-symfony4/phrase-app/src/AppBundle/PhraseTranslator.php

Add a process method to the Kernel Class to use the PhraseTanslator on this environment

File: phrase-app-symfony4/phrase-app/src/Kernel.php

Then on your templates include the javascript loader as per instructions

File: phrase-app-symfony4/phrase-app/templates/base.html.twig

Navigate to to get a trial version.

Once you set your account up, you can create a project and navigate to Project Setting to find your projectId key.

Now change the environment and restart the server

When you navigate to the page you will see the login modal again and once you are authenticated you will see the translated strings change to include edit buttons next to them. The In-Context editor panel will show also.

From there you can manage your translations easier.


In this article, we have seen how to translate Symfony applications using well-known techniques. We’ve also seen how can we integrate PhraseApp’s In-Context Editor in our workflow. If you have any other questions left, don’t hesitate to post a comment or drop me a line. Thank you for reading and see you again next time!

Your Guide to Symfony 4 Internationalization Beyond the Basics
4.6 (92.8%) 25 votes
Theo Phrase Content Team