
Global business
Software localization
There's a bit of know-how required if you're looking to translate Symfony 3 apps, but luckily for you, we've got you covered.
So, let's talk about how to use the Translation component of Symfony to localize your application. We'll also get into some best practices for managing your translations.
🔗 Resource » Make sure to stop by our complete tutorial on Symfony internationalization to see some practical and more advanced examples of i18n.
The first thing you have to do is to enable and configure the Translator service.
This service is for handling translations and can be configured in your app/config/config.yml
file.
If you did this by following the recommended method and created your app using the "Symfony Installer," you will already find the translator entry there.
Set the fallbacks:
to the locales Symfony should use. If it can not find the translations in the users' locale then comment it in:
… framework: translator: { fallbacks: [en] } …
Normally you should choose the language that the majority of your visitors understand or that you are using as a basis for your translation process as fallback locale. In most cases, it will be EN.
You can find more information on how Symfony handles missing translations here.
Now, after enabling the Translator service, you can use the trans()
method of the Translator
to translate your text blocks (called "messages"). A simple example controller translating the String "Hello World"
will look as follows:
<?php namespace AppBundle\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; class DefaultController extends Controller { /** * @Route("/", name="homepage") */ public function indexAction(Request $request) { // translate "Hello World" using trans() method Translator $translated = $this->get('translator')->trans('Hello World'); return new Response($translated); } }
Symfony will detect the locale of use from the request and will translate the message into this locale. But how does Symfony find the translation for the user locales?
This is where the locale or translation files come in.
Symfony supports many file formats that allow you to load translations into the Translator
Some examples of these are XLIFF, YAML and PHP Arrays.
Therefore, it's highly recommended that you use XLIFF because it's widely used, has great tool support for translators, and it's XML based.
The translation files for your app should be placed under ./app/Resources/translations/
and follow the file name pattern domain.locale.file_format
.
The domain part can be used to separate and structure your translation files. The default domain is messages
.
As an example; to provide translations for the German locale in the default messages scope you have to create ./app/Resources/translations/messages.de.xlf
.
So the example XLIFF file for our example will look like the following:
<?xml version="1.0" encoding="UTF-8"?> <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2"> <file original="global" datatype="plaintext" source-language="en" target-language="de"> <body> <trans-unit id="hello_world"> <source xml:lang="en">Hello World</source> <target xml:lang="de">Hallo Welt</target> </trans-unit> </body> </file> </xliff>
By default, the Translator will look for the text to translate in the <source>
tag and translate it to the text within the <target >
tag.
Managing your translations this way is really not a very good practice. Instead, you should use unique keys like index.hello_message
to access your translations.
Most other Frameworks supporting XLIFF use id
attributes to map translation keys. Symfony doesn't work this way, but you can use the resname
attribute for defining translation keys.
By following this approach the XLIFF file will need to be changed as the following:
<?xml version="1.0" encoding="UTF-8"?> <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2"> <file original="global" datatype="plaintext" source-language="en" target-language="de"> <body> <trans-unit id="hello_world" resname="index.hello_world"> <source xml:lang="en">Hello World</source> <target xml:lang="de">Hallo Welt</target> </trans-unit> </body> </file> </xliff>
For more information about the XLIFF resname
you can take a look at the XLIFF Specification.
Now the example controller code can be changed using the key name to identify the translation:
public function indexAction(Request $request) { // translate "Hello World" using trans() method Translator $translated = $this->get('translator')->trans('index.hello_world'); return new Response($translated); }
Symfony only requires the keys to be unique in their domain but I highly recommend using unique keys over all domains. This can be forced by using the domain as a prefix. For example, the key index.hello_world
inside your messages domain should be changed to messages.index.hello_world
if you are using multiple message domains. This approach makes your code more readable and helps you to avoid conflicts while using third-party software like Phrase to manage your translations.
As for the naming of translation keys, you feel free to check out our Lessons Learned: Naming and Managing Rails I18n Keys, where we talk about Rails I18n, but the same process can be adapted for Symfony localization without too much of a hassle.
You'll be often going to find yourself in situations where your messages are dependent on certain variables.
This is where placeholders come in.
Lets assume that you want to display a greeting message based on a $name
variable. Symfony allows you to specify a placeholder array as argument for trans()
. Here you can define replacements:
public function indexAction(Request $request) { // translate "Hello $name" using trans() method Translator $name = "Paul"; $translated = $this->get('translator') ->trans('greeting', array('%{name}' => $name)); return new Response($translated); }
Now your translation will have to be changed using the placeholder:
<trans-unit id="greeting" resname="greeting"> <source xml:lang="en">Hello %{name}</source> <target xml:lang="de">Hallo %{name}</target> </trans-unit>
You can use anything you like as your placeholder, however, I'd highly recommend that you decide on one pattern and stick to it.
I think that wrapping a placeholder with %{placeholder}
is the solution most frameworks use, so I'd strongly recommend using this pattern and not the default one shown in Symfony documentation.
Most of your messages are placed in the view templates of your application. Symfony provides an easy way to use the Translator in Twig views by using the trans
or transchoice
tag. Instead, let's change our "greeting" example and use a Twig template and a trans tag. The Twig template could look something like the following:
{% extends 'base.html.twig' %} {% block body %} <h1>{% trans with {'%{name}': name} %}greeting{% endtrans %}</h1> {% endblock %}
Render the template in the controller:
public function indexAction(Request $request) { // translate "Hello $name" using trans() method Translator $name = "Paul"; return $this->render( 'default/index.html.twig', array('name' => $name) ); }
Aside from the trans()
method the Translator
provides transChoice
to handle pluralization. You can find more information on Pluralisation in the Symfony documentation.
Furthermore, if you want to take localization one step further, check out Phrase. We help you manage your translation process online through the cloud. You can even order professional translations with the click of a button.
Also, you can use Phrase Clients pull
and push
command to simply upload and download your translation files to be shared with your translators or hired professional translations.
Providing context information to your translators will have a high impact on the quality of your resulting translations.
So, using Phrase will help you to provide more content information during the translation process.
You can also use comments for keys in the Translation Center.
And lastly, the Phrase in-context editor is a great way to translate your app while serving it. We provide step-by-step documentation on how to set it for your Symfony apps.
Last updated on October 23, 2022.