Skip to content
  • About
    • What is Symfony?
    • Community
    • News
    • Contributing
    • Support
  • Documentation
    • Symfony Docs
    • Symfony Book
    • Screencasts
    • Symfony Bundles
    • Symfony Cloud
    • Training
  • Services
    • SensioLabs Professional services to help you with Symfony
    • Platform.sh for Symfony Best platform to deploy Symfony apps
    • SymfonyInsight Automatic quality checks for your apps
    • Symfony Certification Prove your knowledge and boost your career
    • Blackfire Profile and monitor performance of your apps
  • Other
  • Blog
  • Download
sponsored by SensioLabs
  1. Home
  2. Documentation
  3. Translations
  • Documentation
  • Book
  • Reference
  • Bundles
  • Cloud

Table of Contents

  • Configuration
  • Basic Translation
    • The Translation Process
  • Message Placeholders
  • Pluralization
  • Translations in Templates
    • Twig Templates
    • PHP Templates
  • Translation Resource/File Names and Locations
  • Fallback Translation Locales
  • Handling the User's Locale
  • Translating Database Content
  • Debugging Translations
  • Summary
  • Learn more

Translations

Edit this page

Warning: You are browsing the documentation for Symfony 3.2, which is no longer maintained.

Read the updated version of this page for Symfony 6.2 (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
7
8
// text will *always* print out in English
dump('Hello World');
die();

// text can be translated into the end-user's language or
// default to English
dump($translator->trans('Hello World'));
die();

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:

  1. Enable and configure Symfony's translation service;
  2. Abstract strings (i.e. "messages") by wrapping them in calls to the Translator ("Translations");
  3. Create translation resources/files for each supported locale that translate each message in the application;
  4. Determine, set and manage the user's locale for the request and optionally on the user's entire session.

Configuration

Translations are handled by a translator service that uses the user's locale to lookup and return translated messages. Before using it, enable the translator in your configuration:

  • YAML
  • XML
  • PHP
1
2
3
# app/config/config.yml
framework:
    translator: { fallbacks: [en] }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- app/config/config.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
        http://symfony.com/schema/dic/services/services-1.0.xsd
        http://symfony.com/schema/dic/symfony
        http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">

    <framework:config>
        <framework:translator>
            <framework:fallback>en</framework:fallback>
        </framework:translator>
    </framework:config>
</container>
1
2
3
4
// app/config/config.php
$container->loadFromExtension('framework', array(
    'translator' => array('fallbacks' => array('en')),
));

See Translations for details on the fallbacks key and what Symfony does when it doesn't find a translation.

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\Component\HttpFoundation\Response;

public function indexAction()
{
    $translated = $this->get('translator')->trans('Symfony is great');

    return new Response($translated);
}

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:

  • XML
  • YAML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
<!-- 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
# messages.fr.yml
Symfony is great: J'aime Symfony
1
2
3
4
// messages.fr.php
return array(
    '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.

The Translation Process

To actually translate the message, Symfony uses a simple process:

  • 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.
  • If the message is located in the catalog, the translation is returned. If not, the translator returns the original message.

When using the trans() method, Symfony looks for the exact string inside the appropriate message catalog and returns it (if it exists).

Message Placeholders

Sometimes, a message containing a variable needs to be translated:

1
2
3
4
5
6
7
8
use Symfony\Component\HttpFoundation\Response;

public function indexAction($name)
{
    $translated = $this->get('translator')->trans('Hello '.$name);

    return new Response($translated);
}

However, creating a translation for this string is impossible since the translator will try to look up the exact message, including the variable portions (e.g. "Hello Ryan" or "Hello Fabien").

For details on how to handle this situation, see Using the Translator in the components documentation. For how to do this in templates, see Translations.

Pluralization

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 handle this, use the transChoice() method or the transchoice tag/filter in your template.

For much more information, see Using the Translator in the Translation component documentation.

Translations in Templates

Most of the time, translation occurs in templates. Symfony provides native support for both Twig and PHP templates.

Twig Templates

Symfony provides specialized Twig tags (trans and transchoice) to help with message translation of static blocks of text:

1
2
3
4
5
{% trans %}Hello %name%{% endtrans %}

{% transchoice count %}
    {0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples
{% endtranschoice %}

The transchoice tag automatically gets the %count% variable from the current context and passes it to the translator. This mechanism only works when you use a placeholder following the %var% pattern.

Caution

The %var% notation of placeholders is required when translating in Twig templates using the tag.

Tip

If you need to use the percent character (%) in a string, escape it by doubling it: {% trans %}Percent: %percent%%%{% endtrans %}

You can also specify the message domain and pass some additional variables:

1
2
3
4
5
6
7
{% trans with {'%name%': 'Fabien'} from "app" %}Hello %name%{% endtrans %}

{% trans with {'%name%': 'Fabien'} from "app" into "fr" %}Hello %name%{% endtrans %}

{% transchoice count with {'%name%': 'Fabien'} from "app" %}
    {0} %name%, there are no apples|{1} %name%, there is one apple|]1,Inf[ %name%, there are %count% apples
{% endtranschoice %}

The trans and transchoice filters can be used to translate variable texts and complex expressions:

1
2
3
4
5
6
7
{{ message|trans }}

{{ message|transchoice(5) }}

{{ message|trans({'%name%': 'Fabien'}, "app") }}

{{ message|transchoice(5, {'%name%': 'Fabien'}, 'app') }}

Tip

Using the translation tags or filters have the same effect, but with one subtle difference: automatic output escaping is only applied to translations using a filter. In other words, if you need to be sure that your translated message is not output escaped, you must apply the raw filter after the translation filter:

1
2
3
4
5
6
7
8
9
10
{# text translated between tags is never escaped #}
{% trans %}
    <h3>foo</h3>
{% endtrans %}

{% set message = '<h3>foo</h3>' %}

{# strings and variables translated via a filter are escaped by default #}
{{ message|trans|raw }}
{{ '<h3>bar</h3>'|trans|raw }}

Tip

You can set the translation domain for an entire Twig template with a single tag:

1
{% trans_default_domain "app" %}

Note that this only influences the current template, not any "included" template (in order to avoid side effects).

PHP Templates

The translator service is accessible in PHP templates through the translator helper:

1
2
3
4
5
6
7
<?php echo $view['translator']->trans('Symfony is great') ?>

<?php echo $view['translator']->transChoice(
    '{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
    10,
    array('%count%' => 10)
) ?>

Translation Resource/File Names and Locations

Symfony looks for message files (i.e. translations) in the following default locations:

  • the app/Resources/translations directory;
  • the app/Resources/<bundle name>/translations directory;
  • the Resources/translations/ directory inside of any bundle.

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 2 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 default messages) - 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, yml, etc).

The loader can be the name of any registered loader. By default, Symfony provides many loaders, including:

  • xlf: XLIFF file;
  • php: PHP file;
  • yml: 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:

  • YAML
  • XML
  • PHP
1
2
3
4
5
# app/config/config.yml
framework:
    translator:
        paths:
            - '%kernel.root_dir%/../translations'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- app/config/config.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
        http://symfony.com/schema/dic/services/services-1.0.xsd
        http://symfony.com/schema/dic/symfony
        http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"
>

    <framework:config>
        <framework:translator>
            <framework:path>%kernel.root_dir%/../translations</framework:path>
        </framework:translator>
    </framework:config>
</container>
1
2
3
4
5
6
7
8
// app/config/config.php
$container->loadFromExtension('framework', array(
    'translator' => array(
        'paths' => array(
            '%kernel.root_dir%/../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 The Dependency Injection 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

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:

  1. First, Symfony looks for the translation in a fr_FR translation resource (e.g. messages.fr_FR.xlf);
  2. If it wasn't found, Symfony looks for the translation in a fr translation resource (e.g. messages.fr.xlf);
  3. If the translation still isn't found, Symfony uses the fallbacks configuration parameter, which defaults to en (see Configuration).

Note

When Symfony doesn't find a translation in the given locale, it will add the missing translation to the log file. For details, see FrameworkBundle Configuration ("framework").

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.

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 just a few basic steps:

  • Abstract messages in your application by wrapping each in either the trans() or transChoice() methods (learn about this in Using the Translator);
  • 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.

Learn more

  • How to Find Missing or Unused Translation Messages
  • How to Find Errors in Translation Files
  • How to Work with the User's Locale
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version
    We stand with Ukraine.
    Version:
    Check Code Performance in Dev, Test, Staging & Production

    Check Code Performance in Dev, Test, Staging & Production

    Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).

    Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).

    Symfony footer

    ↓ Our footer now uses the colors of the Ukrainian flag because Symfony stands with the people of Ukraine.

    Avatar of Pedro Piedade, a Symfony contributor

    Thanks Pedro Piedade for being a Symfony contributor

    1 commit • 15 lines changed

    View all contributors that help us make Symfony

    Become a Symfony contributor

    Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

    Learn how to contribute

    Symfony™ is a trademark of Symfony SAS. All rights reserved.

    • What is Symfony?

      • Symfony at a Glance
      • Symfony Components
      • Case Studies
      • Symfony Releases
      • Security Policy
      • Logo & Screenshots
      • Trademark & Licenses
      • symfony1 Legacy
    • Learn Symfony

      • Symfony Docs
      • Symfony Book
      • Reference
      • Bundles
      • Best Practices
      • Training
      • eLearning Platform
      • Certification
    • Screencasts

      • Learn Symfony
      • Learn PHP
      • Learn JavaScript
      • Learn Drupal
      • Learn RESTful APIs
    • Community

      • SymfonyConnect
      • Support
      • How to be Involved
      • Code of Conduct
      • Events & Meetups
      • Projects using Symfony
      • Downloads Stats
      • Contributors
      • Backers
    • Blog

      • Events & Meetups
      • A week of symfony
      • Case studies
      • Cloud
      • Community
      • Conferences
      • Diversity
      • Documentation
      • Living on the edge
      • Releases
      • Security Advisories
      • SymfonyInsight
      • Twig
      • SensioLabs
    • Services

      • SensioLabs services
      • Train developers
      • Manage your project quality
      • Improve your project performance
      • Host Symfony projects

      Deployed on

    Follow Symfony

    Search by Algolia