PageRenderTime 48ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/components/translation/usage.rst

https://github.com/richsage/symfony-docs
ReStructuredText | 437 lines | 322 code | 115 blank | 0 comment | 0 complexity | b5c8e811a9aa746775ff38f6fd42de3a MD5 | raw file
  1. .. index::
  2. single: Translation; Usage
  3. Using the Translator
  4. ====================
  5. Imagine you want to translate the string *"Symfony is great"* into French::
  6. use Symfony\Component\Translation\Translator;
  7. use Symfony\Component\Translation\Loader\ArrayLoader;
  8. $translator = new Translator('fr_FR');
  9. $translator->addLoader('array', new ArrayLoader());
  10. $translator->addResource('array', array(
  11. 'Symfony is great!' => 'J\'aime Symfony!',
  12. ), 'fr_FR');
  13. var_dump($translator->trans('Symfony is great!'));
  14. In this example, the message *"Symfony is great!"* will be translated into
  15. the locale set in the constructor (``fr_FR``) if the message exists in one of
  16. the message catalogs.
  17. .. _component-translation-placeholders:
  18. Message Placeholders
  19. --------------------
  20. Sometimes, a message containing a variable needs to be translated::
  21. // ...
  22. $translated = $translator->trans('Hello '.$name);
  23. var_dump($translated);
  24. However, creating a translation for this string is impossible since the translator
  25. will try to look up the exact message, including the variable portions
  26. (e.g. *"Hello Ryan"* or *"Hello Fabien"*). Instead of writing a translation
  27. for every possible iteration of the ``$name`` variable, you can replace the
  28. variable with a "placeholder"::
  29. // ...
  30. $translated = $translator->trans(
  31. 'Hello %name%',
  32. array('%name%' => $name)
  33. );
  34. var_dump($translated);
  35. Symfony will now look for a translation of the raw message (``Hello %name%``)
  36. and *then* replace the placeholders with their values. Creating a translation
  37. is done just as before:
  38. .. configuration-block::
  39. .. code-block:: xml
  40. <?xml version="1.0"?>
  41. <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
  42. <file source-language="en" datatype="plaintext" original="file.ext">
  43. <body>
  44. <trans-unit id="1">
  45. <source>Hello %name%</source>
  46. <target>Bonjour %name%</target>
  47. </trans-unit>
  48. </body>
  49. </file>
  50. </xliff>
  51. .. code-block:: php
  52. return array(
  53. 'Hello %name%' => 'Bonjour %name%',
  54. );
  55. .. code-block:: yaml
  56. 'Hello %name%': Bonjour %name%
  57. .. note::
  58. The placeholders can take on any form as the full message is reconstructed
  59. using the PHP :phpfunction:`strtr function<strtr>`. But the ``%...%`` form
  60. is recommended, to avoid problems when using Twig.
  61. As you've seen, creating a translation is a two-step process:
  62. #. Abstract the message that needs to be translated by processing it through
  63. the ``Translator``.
  64. #. Create a translation for the message in each locale that you choose to
  65. support.
  66. The second step is done by creating message catalogs that define the translations
  67. for any number of different locales.
  68. Creating Translations
  69. ---------------------
  70. The act of creating translation files is an important part of "localization"
  71. (often abbreviated `L10n`_). Translation files consist of a series of
  72. id-translation pairs for the given domain and locale. The source is the identifier
  73. for the individual translation, and can be the message in the main locale (e.g.
  74. *"Symfony is great"*) of your application or a unique identifier (e.g.
  75. ``symfony.great`` - see the sidebar below).
  76. Translation files can be created in several different formats, XLIFF being the
  77. recommended format. These files are parsed by one of the loader classes.
  78. .. configuration-block::
  79. .. code-block:: xml
  80. <?xml version="1.0"?>
  81. <xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
  82. <file source-language="en" datatype="plaintext" original="file.ext">
  83. <body>
  84. <trans-unit id="symfony_is_great">
  85. <source>Symfony is great</source>
  86. <target>J'aime Symfony</target>
  87. </trans-unit>
  88. <trans-unit id="symfony.great">
  89. <source>symfony.great</source>
  90. <target>J'aime Symfony</target>
  91. </trans-unit>
  92. </body>
  93. </file>
  94. </xliff>
  95. .. code-block:: yaml
  96. Symfony is great: J'aime Symfony
  97. symfony.great: J'aime Symfony
  98. .. code-block:: php
  99. return array(
  100. 'Symfony is great' => 'J\'aime Symfony',
  101. 'symfony.great' => 'J\'aime Symfony',
  102. );
  103. .. _translation-real-vs-keyword-messages:
  104. .. sidebar:: Using Real or Keyword Messages
  105. This example illustrates the two different philosophies when creating
  106. messages to be translated::
  107. $translator->trans('Symfony is great');
  108. $translator->trans('symfony.great');
  109. In the first method, messages are written in the language of the default
  110. locale (English in this case). That message is then used as the "id"
  111. when creating translations.
  112. In the second method, messages are actually "keywords" that convey the
  113. idea of the message. The keyword message is then used as the "id" for
  114. any translations. In this case, translations must be made for the default
  115. locale (i.e. to translate ``symfony.great`` to ``Symfony is great``).
  116. The second method is handy because the message key won't need to be changed
  117. in every translation file if you decide that the message should actually
  118. read "Symfony is really great" in the default locale.
  119. The choice of which method to use is entirely up to you, but the "keyword"
  120. format is often recommended.
  121. Additionally, the ``php`` and ``yaml`` file formats support nested ids to
  122. avoid repeating yourself if you use keywords instead of real text for your
  123. ids:
  124. .. configuration-block::
  125. .. code-block:: yaml
  126. symfony:
  127. is:
  128. great: Symfony is great
  129. amazing: Symfony is amazing
  130. has:
  131. bundles: Symfony has bundles
  132. user:
  133. login: Login
  134. .. code-block:: php
  135. array(
  136. 'symfony' => array(
  137. 'is' => array(
  138. 'great' => 'Symfony is great',
  139. 'amazing' => 'Symfony is amazing',
  140. ),
  141. 'has' => array(
  142. 'bundles' => 'Symfony has bundles',
  143. ),
  144. ),
  145. 'user' => array(
  146. 'login' => 'Login',
  147. ),
  148. );
  149. The multiple levels are flattened into single id/translation pairs by
  150. adding a dot (``.``) between every level, therefore the above examples are
  151. equivalent to the following:
  152. .. configuration-block::
  153. .. code-block:: yaml
  154. symfony.is.great: Symfony is great
  155. symfony.is.amazing: Symfony is amazing
  156. symfony.has.bundles: Symfony has bundles
  157. user.login: Login
  158. .. code-block:: php
  159. return array(
  160. 'symfony.is.great' => 'Symfony is great',
  161. 'symfony.is.amazing' => 'Symfony is amazing',
  162. 'symfony.has.bundles' => 'Symfony has bundles',
  163. 'user.login' => 'Login',
  164. );
  165. .. _component-translation-pluralization:
  166. Pluralization
  167. -------------
  168. Message pluralization is a tough topic as the rules can be quite complex. For
  169. instance, here is the mathematical representation of the Russian pluralization
  170. rules::
  171. (($number % 10 == 1) && ($number % 100 != 11))
  172. ? 0
  173. : ((($number % 10 >= 2)
  174. && ($number % 10 <= 4)
  175. && (($number % 100 < 10)
  176. || ($number % 100 >= 20)))
  177. ? 1
  178. : 2
  179. );
  180. As you can see, in Russian, you can have three different plural forms, each
  181. given an index of 0, 1 or 2. For each form, the plural is different, and
  182. so the translation is also different.
  183. When a translation has different forms due to pluralization, you can provide
  184. all the forms as a string separated by a pipe (``|``)::
  185. 'There is one apple|There are %count% apples'
  186. To translate pluralized messages, use the
  187. :method:`Symfony\\Component\\Translation\\Translator::transChoice` method::
  188. // the %count% placeholder is assigned to the second argument...
  189. $translator->transChoice(
  190. 'There is one apple|There are %count% apples',
  191. 10
  192. );
  193. // ...but you can define more placeholders if needed
  194. $translator->transChoice(
  195. 'Hurry up %name%! There is one apple left.|There are %count% apples left.',
  196. 10,
  197. // no need to include %count% here; Symfony does that for you
  198. array('%name%' => $user->getName())
  199. );
  200. The second argument (``10`` in this example) is the *number* of objects being
  201. described and is used to determine which translation to use and also to populate
  202. the ``%count%`` placeholder.
  203. .. versionadded:: 3.2
  204. Before Symfony 3.2, the placeholder used to select the plural (``%count%``
  205. in this example) must be included in the third optional argument of the
  206. ``transChoice()`` method::
  207. $translator->transChoice(
  208. 'There is one apple|There are %count% apples',
  209. 10,
  210. array('%count%' => 10)
  211. );
  212. Starting from Symfony 3.2, when the only placeholder is ``%count%``, you
  213. don't have to pass this third argument.
  214. Based on the given number, the translator chooses the right plural form.
  215. In English, most words have a singular form when there is exactly one object
  216. and a plural form for all other numbers (0, 2, 3...). So, if ``count`` is
  217. ``1``, the translator will use the first string (``There is one apple``)
  218. as the translation. Otherwise it will use ``There are %count% apples``.
  219. Here is the French translation:
  220. .. code-block:: text
  221. 'Il y a %count% pomme|Il y a %count% pommes'
  222. Even if the string looks similar (it is made of two sub-strings separated by a
  223. pipe), the French rules are different: the first form (no plural) is used when
  224. ``count`` is ``0`` or ``1``. So, the translator will automatically use the
  225. first string (``Il y a %count% pomme``) when ``count`` is ``0`` or ``1``.
  226. Each locale has its own set of rules, with some having as many as six different
  227. plural forms with complex rules behind which numbers map to which plural form.
  228. The rules are quite simple for English and French, but for Russian, you'd
  229. may want a hint to know which rule matches which string. To help translators,
  230. you can optionally "tag" each string:
  231. .. code-block:: text
  232. 'one: There is one apple|some: There are %count% apples'
  233. 'none_or_one: Il y a %count% pomme|some: Il y a %count% pommes'
  234. The tags are really only hints for translators and don't affect the logic
  235. used to determine which plural form to use. The tags can be any descriptive
  236. string that ends with a colon (``:``). The tags also do not need to be the
  237. same in the original message as in the translated one.
  238. .. tip::
  239. As tags are optional, the translator doesn't use them (the translator will
  240. only get a string based on its position in the string).
  241. Explicit Interval Pluralization
  242. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  243. The easiest way to pluralize a message is to let the Translator use internal
  244. logic to choose which string to use based on a given number. Sometimes, you'll
  245. need more control or want a different translation for specific cases (for
  246. ``0``, or when the count is negative, for example). For such cases, you can
  247. use explicit math intervals:
  248. .. code-block:: text
  249. '{0} There are no apples|{1} There is one apple|]1,19] There are %count% apples|[20,Inf[ There are many apples'
  250. The intervals follow the `ISO 31-11`_ notation. The above string specifies
  251. four different intervals: exactly ``0``, exactly ``1``, ``2-19``, and ``20``
  252. and higher.
  253. You can also mix explicit math rules and standard rules. In this case, if
  254. the count is not matched by a specific interval, the standard rules take
  255. effect after removing the explicit rules:
  256. .. code-block:: text
  257. '{0} There are no apples|[20,Inf[ There are many apples|There is one apple|a_few: There are %count% apples'
  258. For example, for ``1`` apple, the standard rule ``There is one apple`` will
  259. be used. For ``2-19`` apples, the second standard rule ``There are %count%
  260. apples`` will be selected.
  261. An :class:`Symfony\\Component\\Translation\\Interval` can represent a finite set
  262. of numbers:
  263. .. code-block:: text
  264. {1,2,3,4}
  265. Or numbers between two other numbers:
  266. .. code-block:: text
  267. [1, +Inf[
  268. ]-1,2[
  269. The left delimiter can be ``[`` (inclusive) or ``]`` (exclusive). The right
  270. delimiter can be ``[`` (exclusive) or ``]`` (inclusive). Beside numbers, you
  271. can use ``-Inf`` and ``+Inf`` for the infinite.
  272. Forcing the Translator Locale
  273. -----------------------------
  274. When translating a message, the Translator uses the specified locale or the
  275. ``fallback`` locale if necessary. You can also manually specify the locale to
  276. use for translation::
  277. $translator->trans(
  278. 'Symfony is great',
  279. array(),
  280. 'messages',
  281. 'fr_FR'
  282. );
  283. $translator->transChoice(
  284. '{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
  285. 10,
  286. array(),
  287. 'messages',
  288. 'fr_FR'
  289. );
  290. .. note::
  291. Starting from Symfony 3.2, the third argument of ``transChoice()`` is
  292. optional when the only placeholder in use is ``%count%``. In previous
  293. Symfony versions you needed to always define it::
  294. $translator->transChoice(
  295. '{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples',
  296. 10,
  297. array('%count%' => 10),
  298. 'messages',
  299. 'fr_FR'
  300. );
  301. Retrieving the Message Catalogue
  302. --------------------------------
  303. In case you want to use the same translation catalogue outside your application
  304. (e.g. use translation on the client side), it's possible to fetch raw translation
  305. messages. Just specify the required locale::
  306. $catalogue = $translator->getCatalogue('fr_FR');
  307. $messages = $catalogue->all();
  308. while ($catalogue = $catalogue->getFallbackCatalogue()) {
  309. $messages = array_replace_recursive($catalogue->all(), $messages);
  310. }
  311. The ``$messages`` variable will have the following structure::
  312. array(
  313. 'messages' => array(
  314. 'Hello world' => 'Bonjour tout le monde',
  315. ),
  316. 'validators' => array(
  317. 'Value should not be empty' => 'Valeur ne doit pas ĂȘtre vide',
  318. 'Value is too long' => 'Valeur est trop long',
  319. ),
  320. );
  321. .. _`L10n`: https://en.wikipedia.org/wiki/Internationalization_and_localization
  322. .. _`ISO 31-11`: https://en.wikipedia.org/wiki/Interval_(mathematics)#Notations_for_intervals