Translations
Edit this pageWarning: You are browsing the documentation for Symfony 4.2, which is no longer maintained.
Read the updated version of this page for Symfony 6.3 (the current stable version).
Translations
The term "internationalization" (often abbreviated i18n) refers to the process of abstracting strings and other locale-specific pieces out of your application into a layer where they can be translated and converted based on the user's locale (i.e. language and country). For text, this means wrapping each with a function capable of translating the text (or "message") into the language of the user:
1 2 3 4 5 6
// text will *always* print out in English
echo 'Hello World';
// text can be translated into the end-user's language or
// default to English
echo $translator->trans('Hello World');
Note
The term locale refers roughly to the user's language and country. It
can be any string that your application uses to manage translations and
other format differences (e.g. currency format). The ISO 639-1
language code, an underscore (_
), then the ISO 3166-1 alpha-2
country code (e.g. fr_FR
for French/France) is recommended.
In this article, you'll learn how to use the Translation component in the Symfony Framework. You can read the Translation component documentation to learn even more. Overall, the process has several steps:
- Enable and configure Symfony's translation service;
- Abstract strings (i.e. "messages") by wrapping them in calls to the
Translator
("Translations"); - Create translation resources/files for each supported locale that translate each message in the application;
- Determine, set and manage the user's locale for the request and optionally on the user's entire session.
Installation
First, run this command to install the translator before using it:
1
$ composer require symfony/translation
Configuration
The previous command creates an initial config file where you can define the default locale of the app and the fallback locales that will be used if Symfony can't find some translation:
1 2 3 4 5 6
# config/packages/translation.yaml
framework:
default_locale: 'en'
translator:
fallbacks: ['en']
# ...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
<!-- config/packages/translation.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:framework="http://symfony.com/schema/dic/symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services
https://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony
https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
<framework:config default-locale="en">
<framework:translator>
<framework:fallback>en</framework:fallback>
<!-- ... -->
</framework:translator>
</framework:config>
</container>
1 2 3 4 5 6
// config/packages/translation.php
$container->loadFromExtension('framework', [
'default_locale' => 'en',
'translator' => ['fallbacks' => ['en']],
// ...
]);
The locale used in translations is the one stored on the request. This is
typically set via a _locale
attribute on your routes (see How to Work with the User's Locale).
Basic Translation
Translation of text is done through the translator
service
(Translator). To translate a block
of text (called a message), use the
trans() method. Suppose,
for example, that you're translating a simple message from inside a controller:
1 2 3 4 5 6 7 8 9
// ...
use Symfony\Contracts\Translation\TranslatorInterface;
public function index(TranslatorInterface $translator)
{
$translated = $translator->trans('Symfony is great');
// ...
}
When this code is executed, Symfony will attempt to translate the message
"Symfony is great" based on the locale
of the user. For this to work,
you need to tell Symfony how to translate the message via a "translation
resource", which is usually a file that contains a collection of translations
for a given locale. This "dictionary" of translations can be created in several
different formats, XLIFF being the recommended format:
1 2 3 4 5 6 7 8 9 10 11 12
<!-- translations/messages.fr.xlf -->
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="symfony_is_great">
<source>Symfony is great</source>
<target>J'aime Symfony</target>
</trans-unit>
</body>
</file>
</xliff>
1 2
# translations/messages.fr.yaml
Symfony is great: J'aime Symfony
1 2 3 4
// translations/messages.fr.php
return [
'Symfony is great' => "J'aime Symfony",
];
For information on where these files should be located, see Translations.
Now, if the language of the user's locale is French (e.g. fr_FR
or fr_BE
),
the message will be translated into J'aime Symfony
. You can also translate
the message inside your `templates <Translations in Templates>`.
The Translation Process
To actually translate the message, Symfony uses the following process when
using the trans()
method:
- The
locale
of the current user, which is stored on the request is determined; - A catalog (e.g. big collection) of translated messages is loaded from translation
resources defined for the
locale
(e.g.fr_FR
). Messages from the fallback locale are also loaded and added to the catalog if they don't already exist. The end result is a large "dictionary" of translations. This catalog is cached in production to minimize performance impact. - If the message is located in the catalog, the translation is returned. If not, the translator returns the original message.
Message Format
Sometimes, a message containing a variable needs to be translated:
1 2
// ...
$translated = $translator->trans('Hello '.$name);
However, creating a translation for this string is impossible since the translator will try to look up the message including the variable portions (e.g. "Hello Ryan" or "Hello Fabien").
Another complication is when you have translations that may or may not be plural, based on some variable:
1 2
There is one apple.
There are 5 apples.
To manage these situations, Symfony follows the ICU MessageFormat syntax by using PHP's MessageFormatter class. Read more about this in How to Translate Messages using the ICU MessageFormat.
4.2
Support for ICU MessageFormat was introduced in Symfony 4.2. Prior to this, pluralization was managed by the transChoice() method.
Translations in Templates
Most of the time, translation occurs in templates. Symfony provides native support for both Twig and PHP templates:
1
<h1>{% trans %}Symfony is great!{% endtrans %}</h1>
Read Using Translation in Templates for more information about the Twig tags and filters for translation.
Extracting Translation Contents and Updating Catalogs Automatically
The most time-consuming tasks when translating an application is to extract all
the template contents to be translated and to keep all the translation files in
sync. Symfony includes a command called translation:update
that helps you
with these tasks:
1 2 3 4 5
# updates the French translation file with the missing strings found in templates/
$ php bin/console translation:update --dump-messages --force fr
# updates the English translation file with the missing strings found in AppBundle
$ php bin/console translation:update --dump-messages --force en AppBundle
Note
If you want to see the missing translation strings without actually updating
the translation files, remove the --force
option from the command above.
Tip
If you need to extract translation strings from other sources, such as controllers, forms and flash messages, consider using the more advanced third-party TranslationBundle.
Translation Resource/File Names and Locations
Symfony looks for message files (i.e. translations) in the following default locations:
- the
translations/
directory (at the root of the project); - the
src/Resources/<bundle name>/translations/
directory; - the
Resources/translations/
directory inside of any bundle.
4.2
Using the src/Resources/<bundle name>/translations/
directory to store
translations was deprecated in Symfony 4.2. Use instead the directory
defined in the default_path
option (which is translations/
by default).
The locations are listed here with the highest priority first. That is, you can override the translation messages of a bundle in any of the top two directories.
The override mechanism works at a key level: only the overridden keys need to be listed in a higher priority message file. When a key is not found in a message file, the translator will automatically fall back to the lower priority message files.
The filename of the translation files is also important: each message file
must be named according to the following path: domain.locale.loader
:
- domain: An optional way to organize messages into groups (e.g.
admin
,navigation
or the defaultmessages
) - see The Translation Component; - locale: The locale that the translations are for (e.g.
en_GB
,en
, etc); - loader: How Symfony should load and parse the file (e.g.
xlf
,php
,yaml
, etc).
The loader can be the name of any registered loader. By default, Symfony provides many loaders, including:
xlf
: XLIFF file;php
: PHP file;yaml
: YAML file.
The choice of which loader to use is entirely up to you and is a matter of
taste. The recommended option is to use xlf
for translations.
For more options, see The Translation Component.
Note
You can add other directories with the paths option in the configuration:
1 2 3 4 5
# config/packages/translation.yaml
framework:
translator:
paths:
- '%kernel.project_dir%/custom/path/to/translations'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
<!-- config/packages/translation.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:framework="http://symfony.com/schema/dic/symfony"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-Instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
https://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony
https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"
>
<framework:config>
<framework:translator>
<framework:path>%kernel.project_dir%/custom/path/to/translations</framework:path>
</framework:translator>
</framework:config>
</container>
1 2 3 4 5 6 7 8
// config/packages/translation.php
$container->loadFromExtension('framework', [
'translator' => [
'paths' => [
'%kernel.project_dir%/custom/path/to/translations',
],
],
]);
Note
You can also store translations in a database, or any other storage by providing a custom class implementing the LoaderInterface interface. See the Built-in Symfony Service Tags tag for more information.
Caution
Each time you create a new translation resource (or install a bundle that includes a translation resource), be sure to clear your cache so that Symfony can discover the new translation resources:
1
$ php bin/console cache:clear
Handling the User's Locale
Translating happens based on the user's locale. Read How to Work with the User's Locale to learn more about how to handle it.
Fallback Translation Locales
Imagine that the user's locale is fr_FR
and that you're translating the
key Symfony is great
. To find the French translation, Symfony actually
checks translation resources for several locales:
- First, Symfony looks for the translation in a
fr_FR
translation resource (e.g.messages.fr_FR.xlf
); - If it wasn't found, Symfony looks for the translation in a
fr
translation resource (e.g.messages.fr.xlf
); - If the translation still isn't found, Symfony uses the
fallbacks
configuration parameter, which defaults toen
(see Configuration).
Note
When Symfony can't find a translation in the given locale, it will add the missing translation to the log file. For details, see Framework Configuration Reference (FrameworkBundle).
Translating Database Content
The translation of database content should be handled by Doctrine through the Translatable Extension or the Translatable Behavior (PHP 5.4+). For more information, see the documentation for these libraries.
Debugging Translations
When you work with many translation messages in different languages, it can be hard to keep track which translations are missing and which are not used anymore. Read How to Find Missing or Unused Translation Messages to find out how to identify these messages.
Summary
With the Symfony Translation component, creating an internationalized application no longer needs to be a painful process and boils down to these steps:
- Abstract messages in your application by wrapping each in the trans() method;
- Translate each message into multiple locales by creating translation message files. Symfony discovers and processes each file because its name follows a specific convention;
- Manage the user's locale, which is stored on the request, but can also be set on the user's session.