|
| 1 | +Translations |
| 2 | +============ |
| 3 | + |
| 4 | +The :namespace:`Symfony\\Component\\Translation` Component provides a way to |
| 5 | +internationalize all the messages in your application. |
| 6 | + |
| 7 | +A *message* can be any string you want to internationalize. Messages are |
| 8 | +categorized by locale and domain. |
| 9 | + |
| 10 | +A *domain* allows you to better organized your messages in a given locale (it |
| 11 | +can be any string; by default, all messages are stored under the ``messages`` |
| 12 | +domain). |
| 13 | + |
| 14 | +A *locale* can be any string, but we recommended to use the ISO639-1 language |
| 15 | +code, followed by a underscore (``_``), followed by the ISO3166 country code |
| 16 | +(use ``fr_FR`` for French/France for instance). |
| 17 | + |
| 18 | +Configuration |
| 19 | +------------- |
| 20 | + |
| 21 | +Before using the translator features, enable it in your configuration: |
| 22 | + |
| 23 | +.. configuration-block:: |
| 24 | + |
| 25 | + .. code-block:: yaml |
| 26 | +
|
| 27 | + # app/config/config.yml |
| 28 | + app.config: |
| 29 | + translator: { fallback: en } |
| 30 | +
|
| 31 | + .. code-block:: xml |
| 32 | +
|
| 33 | + <!-- app/config/config.xml --> |
| 34 | + <app:config> |
| 35 | + <app:translator fallback="en" /> |
| 36 | + </app:config> |
| 37 | +
|
| 38 | + .. code-block:: php |
| 39 | +
|
| 40 | + // app/config/config.php |
| 41 | + $container->loadFromExtension('app', 'config', array( |
| 42 | + 'translator' => array('fallback' => 'en'), |
| 43 | + )); |
| 44 | +
|
| 45 | +The ``fallback`` attribute defines the fallback locale when a translation does |
| 46 | +not exist in the user locale. |
| 47 | + |
| 48 | +.. tip:: |
| 49 | + When a translation does not exist for a locale, the translator tries to |
| 50 | + find the translation for the language (``fr`` when the locale is ``fr_FR`` |
| 51 | + for instance); if it also fails, it looks for a translation for the |
| 52 | + fallback locale. |
| 53 | + |
| 54 | +The locale used in translations is the one stored in the user session. |
| 55 | + |
| 56 | +Translations |
| 57 | +------------ |
| 58 | + |
| 59 | +Translations are available through the ``translator`` service |
| 60 | +(:class:`Symfony\\Component\\Translation\\Translator`). Use the |
| 61 | +:method:`Symfony\\Component\\Translation\\Translator::trans` method to |
| 62 | +translate a message:: |
| 63 | + |
| 64 | + $t = $this['translator']->trans('Symfony2 is great!'); |
| 65 | + |
| 66 | +If you have placeholders in strings, pass their values as the second |
| 67 | +argument:: |
| 68 | + |
| 69 | + $t = $this['translator']->trans('Symfony2 is {{ what }}!', array('{{ what }}' => 'great')); |
| 70 | + |
| 71 | +.. note:: |
| 72 | + The placeholders can have any form, but using the ``{{ var }}`` notation |
| 73 | + allows the message to be used in Twig templates. |
| 74 | + |
| 75 | +By default, the translator looks for messages in the default ``messages`` |
| 76 | +domain. Override it via the third argument:: |
| 77 | + |
| 78 | + $t = $this['translator']->trans('Symfony2 is great!', array(), 'applications'); |
| 79 | + |
| 80 | +Catalogues |
| 81 | +---------- |
| 82 | + |
| 83 | +Translations are stored on the filesystem and discovered by Symfony2, thanks |
| 84 | +to some conventions. |
| 85 | + |
| 86 | +Store translations for messages found in a bundle under the |
| 87 | +``Resources/translations/`` directory; and override them under the |
| 88 | +``app/translations/`` directory. |
| 89 | + |
| 90 | +Each message file must be named according to the following pattern: |
| 91 | +``domain.locale.loader`` (the domain name, followed by a dot (``.``), followed |
| 92 | +by the locale name, followed by a dot (``.``), followed by the loader name.) |
| 93 | + |
| 94 | +The loader can be the name of any registered loader. By default, Symfony2 |
| 95 | +provides the following loaders: |
| 96 | + |
| 97 | +* ``php``: PHP file; |
| 98 | +* ``xliff``: XLIFF file; |
| 99 | +* ``yaml``: YAML file. |
| 100 | + |
| 101 | +Each file consists of pairs of id/translation strings for the given domain and |
| 102 | +locale. The id can be the message in the main locale of your application of a |
| 103 | +unique identifier: |
| 104 | + |
| 105 | +.. configuration-block:: |
| 106 | + |
| 107 | + .. code-block:: xml |
| 108 | +
|
| 109 | + <?xml version="1.0"?> |
| 110 | + <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2"> |
| 111 | + <file source-language="en" datatype="plaintext" original="file.ext"> |
| 112 | + <body> |
| 113 | + <trans-unit id="1"> |
| 114 | + <source>Symfony2 is great</source> |
| 115 | + <target>J'aime Symfony2</target> |
| 116 | + </trans-unit> |
| 117 | + <trans-unit id="2"> |
| 118 | + <source>symfony.great</source> |
| 119 | + <target>J'aime Symfony2</target> |
| 120 | + </trans-unit> |
| 121 | + </body> |
| 122 | + </file> |
| 123 | + </xliff> |
| 124 | +
|
| 125 | + .. code-block:: php |
| 126 | +
|
| 127 | + return array( |
| 128 | + 'Symfony2 is great' => 'J\'aime Symfony2', |
| 129 | + 'symfony.great' => 'J\'aime Symfony2', |
| 130 | + ); |
| 131 | +
|
| 132 | +.. note:: |
| 133 | + You can also store translations in a database, or any other storage by |
| 134 | + providing a custom |
| 135 | + :class:`Symfony\\Component\\Translation\\Loader\\LoaderInterface` class. |
| 136 | + See below to learn how to register custom loaders. |
| 137 | + |
| 138 | +Pluralization |
| 139 | +------------- |
| 140 | + |
| 141 | +Message pluralization is a tough topic as the rules can be quite complex. For |
| 142 | +instance, here is the mathematic representation of the Russian pluralization |
| 143 | +rules:: |
| 144 | + |
| 145 | + (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2); |
| 146 | + |
| 147 | +As you can see, in Russian, you can have three different plural forms, based |
| 148 | +on this algorithm. For each form, the plural is different, and so the |
| 149 | +translation is also different. In such a case, you can provide all |
| 150 | +pluralization forms as strings separated by pipes (``|``):: |
| 151 | + |
| 152 | + 'The is one apple|There are {{ count }} apples' |
| 153 | + |
| 154 | +Based on a given number, the translator chooses the right plural form. If |
| 155 | +``count`` is ``1``, the translator will use the first string (``The is one |
| 156 | +apple``) as the translation, if not, it will use ``There are {{ count }} |
| 157 | +apples``. |
| 158 | + |
| 159 | +Here is the French translation:: |
| 160 | + |
| 161 | + 'Il y a {{ count }} pomme|Il y a {{ count }} pommes' |
| 162 | + |
| 163 | +Even if the string looks similar (it is made of two sub-strings separated by a |
| 164 | +pipe), the French rules are different: the first form (no plural) is used when |
| 165 | +``count`` is ``0`` or ``1``. So, the translator will automatically use the |
| 166 | +first string (``Il y a {{ count }} pomme``) when ``count`` is ``0`` or ``1``. |
| 167 | + |
| 168 | +The rules are quite simple for English and French, but for Russian, you'd |
| 169 | +better have a hint to know which rule matches which string. To help |
| 170 | +translators, you can optionally "tag" each string like this:: |
| 171 | + |
| 172 | + 'one: There is one apple|some: There are {{ count }} apples' |
| 173 | + |
| 174 | + 'none_or_one: Il y a {{ count }} pomme|some: Il y a {{ count }} pommes' |
| 175 | + |
| 176 | +The tags are really only hints for translators to help them understand the |
| 177 | +context of the translation (note that the tags do not need to be the same in |
| 178 | +the original message and in the translated one). |
| 179 | + |
| 180 | +.. tip: |
| 181 | + As tags are optional, the translator doesn't use them (the translator will |
| 182 | + only get a string based on its position in the string). |
| 183 | +
|
| 184 | +Sometimes, you want a different translation for specific cases (for ``0``, or |
| 185 | +when the count is large enough, when the count is negative, ...). For such |
| 186 | +cases, you can use explicit math ranges:: |
| 187 | + |
| 188 | + '{0} There is no apples|{1} There is one apple|]1,19] There are {{ count }} apples|[20,Inf] There are many apples' |
| 189 | + |
| 190 | +You can also mix explicit math rules and standard rules. The position for |
| 191 | +standard rules is defined after removing the explicit rules:: |
| 192 | + |
| 193 | + '{0} There is no apples|[20,Inf] There are many apples|There is one apple|a_few: There are {{ count }} apples' |
| 194 | + |
| 195 | +An :class:`Symfony\\Component\\Translator\\Range` can represent a finite set |
| 196 | +of numbers:: |
| 197 | + |
| 198 | + {1,2,3,4} |
| 199 | + |
| 200 | +Or numbers between two other numbers:: |
| 201 | + |
| 202 | + [1, +Inf] |
| 203 | + ]-1,2[ |
| 204 | + |
| 205 | +The left delimiter can be ``[`` (inclusive) or ``]`` (exclusive). The right |
| 206 | +delimiter can be ``[`` (exclusive) or ``]`` (inclusive). Beside numbers, you |
| 207 | +can use ``-Inf`` and ``+Inf`` for the infinite. |
| 208 | + |
| 209 | +The translator |
| 210 | +:method:`Symfony\\Component\\Translation\\Translator::transChoice` method |
| 211 | +knows how to deal with plural:: |
| 212 | + |
| 213 | + $t = $this['translator']->transChoice( |
| 214 | + '{0} There is no apples|{1} There is one apple|]1,Inf] There are {{ count }} apples', |
| 215 | + 10, |
| 216 | + array('{{ count }}' => 10) |
| 217 | + ); |
| 218 | + |
| 219 | +Notice that the second argument is the number to use to determine which plural |
| 220 | +string to use. |
| 221 | + |
| 222 | +Translations in Templates |
| 223 | +------------------------- |
| 224 | + |
| 225 | +Most of the time, translation occurs in templates. Symfony2 provides native |
| 226 | +support for both PHP and Twig templates. |
| 227 | + |
| 228 | +PHP Templates |
| 229 | +~~~~~~~~~~~~~ |
| 230 | + |
| 231 | +The translator service is accessible in PHP templates through the |
| 232 | +``translator`` helper: |
| 233 | + |
| 234 | +.. code-block:: html+php |
| 235 | + |
| 236 | + <?php echo $view['translator']->trans('Symfony2 is great!') ?> |
| 237 | + |
| 238 | + <?php echo $view['translator']->transChoice( |
| 239 | + '{0} There is no apples|{1} There is one apple|]1,Inf] There are {{ count }} apples', |
| 240 | + 10, |
| 241 | + array('{{ count }}' => 10) |
| 242 | + ) ?> |
| 243 | + |
| 244 | +Twig Templates |
| 245 | +~~~~~~~~~~~~~~ |
| 246 | + |
| 247 | +Symfony2 provides specialized Twig tags (``trans`` and ``transChoice``) to |
| 248 | +help with message translation: |
| 249 | + |
| 250 | +.. code-block:: jinja |
| 251 | +
|
| 252 | + {% trans "Symfony2 is great!" %} |
| 253 | +
|
| 254 | + {% trans %} |
| 255 | + Foo {{ name }} |
| 256 | + {% endtrans %} |
| 257 | +
|
| 258 | + {% transchoice count %} |
| 259 | + {0} There is no apples|{1} There is one apple|]1,Inf] There are {{ count }} apples |
| 260 | + {% endtranschoice %} |
| 261 | +
|
| 262 | +The ``transChoice`` tag automatically get the variables from the current |
| 263 | +context and pass them to the translator. This mechanism only works when you |
| 264 | +use placeholder using the ``{{ var }}`` pattern. |
| 265 | + |
| 266 | +You can also specify the message domain: |
| 267 | + |
| 268 | +.. code-block:: jinja |
| 269 | +
|
| 270 | + {% trans "Foo {{ name }}" from app %} |
| 271 | +
|
| 272 | + {% trans from app %} |
| 273 | + Foo {{ name }} |
| 274 | + {% endtrans %} |
| 275 | +
|
| 276 | + {% transchoice count from app %} |
| 277 | + {0} There is no apples|{1} There is one apple|]1,Inf] There are {{ count }} apples |
| 278 | + {% endtranschoice %} |
| 279 | +
|
| 280 | +.. _translation_loader_tag: |
| 281 | + |
| 282 | +Enabling Custom Loaders |
| 283 | +----------------------- |
| 284 | + |
| 285 | +To enable a custom loader, add it as a regular service in one of your |
| 286 | +configuration, tag it with ``translation.loader`` and define an ``alias`` |
| 287 | +attribute (for filesystem based loaders, the alias is the file extension you |
| 288 | +must use to reference the loader): |
| 289 | + |
| 290 | +.. configuration-block:: |
| 291 | + |
| 292 | + .. code-block:: yaml |
| 293 | +
|
| 294 | + services: |
| 295 | + translation.loader.your_helper_name: |
| 296 | + class: Fully\Qualified\Loader\Class\Name |
| 297 | + tags: |
| 298 | + - { name: translation.loader, alias: alias_name } |
| 299 | +
|
| 300 | + .. code-block:: xml |
| 301 | +
|
| 302 | + <service id="translation.loader.your_helper_name" class="Fully\Qualified\Loader\Class\Name"> |
| 303 | + <tag name="translation.loader" alias="alias_name" /> |
| 304 | + </service> |
| 305 | +
|
| 306 | + .. code-block:: php |
| 307 | +
|
| 308 | + $container |
| 309 | + ->register('translation.loader.your_helper_name', 'Fully\Qualified\Loader\Class\Name') |
| 310 | + ->addTag('translation.loader', array('alias' => 'alias_name')) |
| 311 | + ; |
| 312 | +
|
0 commit comments