Imagine you want to translate the string "Symfony is great" into French:

1
2
3
4
5
6
7
8
9
10

useSymfony\Component\Translation\Translator;useSymfony\Component\Translation\Loader\ArrayLoader;$translator=newTranslator('fr_FR');$translator->addLoader('array',newArrayLoader());$translator->addResource('array',array('Symfony is great!'=>'J\'aime Symfony!',),'fr_FR');var_dump($translator->trans('Symfony is great!'));

In this example, the message "Symfony is great!" will be translated into
the locale set in the constructor (fr_FR) if the message exists in one of
the message catalogs.

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"). Instead of writing a translation
for every possible iteration of the $name variable, you can replace the
variable with a "placeholder":

The act of creating translation files is an important part of "localization"
(often abbreviated L10n). Translation files consist of a series of
id-translation pairs for the given domain and locale. The source is the identifier
for the individual translation, and can be the message in the main locale (e.g.
"Symfony is great") of your application or a unique identifier (e.g.
symfony.great - see the sidebar below).

Translation files can be created in several different formats, XLIFF being the
recommended format. These files are parsed by one of the loader classes.

This example illustrates the two different philosophies when creating
messages to be translated:

$translator->trans('Symfony is great');$translator->trans('symfony.great');

In the first method, messages are written in the language of the default
locale (English in this case). That message is then used as the "id"
when creating translations.

In the second method, messages are actually "keywords" that convey the
idea of the message. The keyword message is then used as the "id" for
any translations. In this case, translations must be made for the default
locale (i.e. to translate symfony.great to Symfony is great).

The second method is handy because the message key won't need to be changed
in every translation file if you decide that the message should actually
read "Symfony is really great" in the default locale.

The choice of which method to use is entirely up to you, but the "keyword"
format is often recommended.

Additionally, the php and yaml file formats support nested ids to
avoid repeating yourself if you use keywords instead of real text for your
ids:

YAML

1
2
3
4
5
6
7
8

symfony:is:great:Symfony is greatamazing:Symfony is amazinghas:bundles:Symfony has bundlesuser:login:Login

PHP

1
2
3
4
5
6
7
8
9
10
11
12
13
14

array('symfony'=>array('is'=>array('great'=>'Symfony is great','amazing'=>'Symfony is amazing',),'has'=>array('bundles'=>'Symfony has bundles',),),'user'=>array('login'=>'Login',),);

The multiple levels are flattened into single id/translation pairs by
adding a dot (.) between every level, therefore the above examples are
equivalent to the following:

YAML

1
2
3
4

symfony.is.great:Symfony is greatsymfony.is.amazing:Symfony is amazingsymfony.has.bundles:Symfony has bundlesuser.login:Login

PHP

1
2
3
4
5
6

returnarray('symfony.is.great'=>'Symfony is great','symfony.is.amazing'=>'Symfony is amazing','symfony.has.bundles'=>'Symfony has bundles','user.login'=>'Login',);

// the %count% placeholder is assigned to the second argument...$translator->transChoice('There is one apple|There are %count% apples',10);// ...but you can define more placeholders if needed$translator->transChoice('Hurry up %name%! There is one apple left.|There are %count% apples left.',10,// no need to include %count% here; Symfony does that for youarray('%name%'=>$user->getName()));

The second argument (10 in this example) is the number of objects being
described and is used to determine which translation to use and also to populate
the %count% placeholder.

New in version 3.2: Before Symfony 3.2, the placeholder used to select the plural (%count%
in this example) must be included in the third optional argument of the
transChoice() method:

1
2
3
4
5

$translator->transChoice('There is one apple|There are %count% apples',10,array('%count%'=>10));

Starting from Symfony 3.2, when the only placeholder is %count%, you
don't have to pass this third argument.

Based on the given number, the translator chooses the right plural form.
In English, most words have a singular form when there is exactly one object
and a plural form for all other numbers (0, 2, 3...). So, if count is
1, the translator will use the first string (There is one apple)
as the translation. Otherwise it will use There are %count% apples.

Here is the French translation:

1

'Il y a %count% pomme|Il y a %count% pommes'

Even if the string looks similar (it is made of two sub-strings separated by a
pipe), the French rules are different: the first form (no plural) is used when
count is 0 or 1. So, the translator will automatically use the
first string (Il y a %count% pomme) when count is 0 or 1.

Each locale has its own set of rules, with some having as many as six different
plural forms with complex rules behind which numbers map to which plural form.
The rules are quite simple for English and French, but for Russian, you'd
may want a hint to know which rule matches which string. To help translators,
you can optionally "tag" each string:

1
2
3

'one: There is one apple|some: There are %count% apples'
'none_or_one: Il y a %count% pomme|some: Il y a %count% pommes'

The tags are really only hints for translators and don't affect the logic
used to determine which plural form to use. The tags can be any descriptive
string that ends with a colon (:). The tags also do not need to be the
same in the original message as in the translated one.

Tip

As tags are optional, the translator doesn't use them (the translator will
only get a string based on its position in the string).

The easiest way to pluralize a message is to let the Translator use internal
logic to choose which string to use based on a given number. Sometimes, you'll
need more control or want a different translation for specific cases (for
0, or when the count is negative, for example). For such cases, you can
use explicit math intervals:

1

'{0} There are no apples|{1} There is one apple|]1,19] There are %count% apples|[20,Inf[ There are many apples'

The intervals follow the ISO 31-11 notation. The above string specifies
four different intervals: exactly 0, exactly 1, 2-19, and 20
and higher.

You can also mix explicit math rules and standard rules. In this case, if
the count is not matched by a specific interval, the standard rules take
effect after removing the explicit rules:

1

'{0} There are no apples|[20,Inf[ There are many apples|There is one apple|a_few: There are %count% apples'

For example, for 1 apple, the standard rule There is one apple will
be used. For 2-19 apples, the second standard rule There are %count%
apples will be selected.

When translating a message, the Translator uses the specified locale or the
fallback locale if necessary. You can also manually specify the locale to
use for translation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

$translator->trans('Symfony is great',array(),'messages','fr_FR');$translator->transChoice('{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples',10,array(),'messages','fr_FR');

Note

Starting from Symfony 3.2, the third argument of transChoice() is
optional when the only placeholder in use is %count%. In previous
Symfony versions you needed to always define it:

1
2
3
4
5
6
7

$translator->transChoice('{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples',10,array('%count%'=>10),'messages','fr_FR');

In case you want to use the same translation catalogue outside your application
(e.g. use translation on the client side), it's possible to fetch raw translation
messages. Just specify the required locale: