From 70a304635e72000e5a3ab489c3ed04fedb711909 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 29 Nov 2012 23:06:53 +0100 Subject: [PATCH 01/13] Bootstrapped the bootstrap Translation documentation --- book/translation.rst | 2 + components/index.rst | 1 + components/map.rst.inc | 4 ++ components/translation.rst | 137 +++++++++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+) create mode 100644 components/translation.rst diff --git a/book/translation.rst b/book/translation.rst index b99720f5169..6bee3c30d5e 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -86,6 +86,8 @@ The locale used in translations is the one stored in the user session. .. index:: single: Translations; Basic translation +.. _basic-translation: + Basic Translation ----------------- diff --git a/components/index.rst b/components/index.rst index 14e73dddf1a..d3437cb5db3 100644 --- a/components/index.rst +++ b/components/index.rst @@ -21,6 +21,7 @@ The Components security/index serializer templating + translation yaml/index .. include:: /components/map.rst.inc diff --git a/components/map.rst.inc b/components/map.rst.inc index e8427932fbe..ac68cfef1a2 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -84,6 +84,10 @@ * :doc:`/components/templating` +* **Translation** + + * :doc:`/components/translation` + * :doc:`/components/yaml/index` * :doc:`/components/yaml/introduction` diff --git a/components/translation.rst b/components/translation.rst new file mode 100644 index 00000000000..6d183a46325 --- /dev/null +++ b/components/translation.rst @@ -0,0 +1,137 @@ +.. index:: + single: Translation + single: Components; Translation + +The Translation Component +========================= + + The Translation component provides tools to internationalize your + application. + +Installation +------------ + +You can install the component in many different ways: + +* Use the official Git repository (https://github.com/symfony/Translation); +* :doc:`Install it via Composer` (``symfony/translation`` on `Packagist`_). + +Usage +----- + +The :class:`Symfony\\Component\\Translation\\Translator` class is the main +entry point of the Translation component. + +.. code-block:: php + + use Symfony\Component\Translation\Translator; + use Symfony\Component\Translation\MessageSelector; + use Symfony\Component\Translation\Loader\ArrayLoader; + + $translator = new Translator('fr_FR', new MessageSelector()); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array( + 'Hello World!' => 'Bonjour', + ), 'fr_FR'); + + echo $translator->trans('Hello World!'); + +Message Catalogues +------------------ + +The messages are stored in message catalogues inside the ``Translator`` +class. A Message Catalogue is like a dictionary of translations for a specific +locale. + +Loading catalogues +~~~~~~~~~~~~~~~~~~ + +The Translation component uses Loader classes to load catalogues. You can load +multiple resources for the same locale, it will be combined into one +catalogue. + +The component comes with some default Loaders and you can create your own +Loader too. The default loaders are: + +* :class:`Symfony\\Component\\Translation\\Loader\\ArrayLoader` - to load + catalogues from PHP arrays. +* :class:`Symfony\\Component\\Translation\\Loader\\CsvFileLoader` - to load + catalogues from Csv files. +* :class:`Symfony\\Component\\Translation\\Loader\\PhpFileLoader` - to load + catalogues from Php files. +* :class:`Symfony\\Component\\Translation\\Loader\\XliffFileLoader` - to load + catalogues from Xliff files. +* :class:`Symfony\\Component\\Translation\\Loader\\YamlFileLoader` - to load + catalogues from Yaml files (requires the :doc:`Yaml component`). + +All loaders, except the ``ArrayLoader``, requires the +:doc:`Config component`. + +At first, you should add a loader to the ``Translator``:: + + // ... + $translator->addLoader('array', new ArrayLoader()); + +The first argument is the key to which we can refer the loader in the translator +and the second argument is an instance of the loader itself. After this, you +can add your resources using the correct loader. + +Loading Messages with the ``ArrayLoader`` +......................................... + +Loading messages can be done by calling +:method:`Symfony\\Component\\Translation\\Translator::addResource`. The first +argument is the loader name (the first argument of the ``addLoader`` +method), the second is the resource and the third argument is the locale:: + + // ... + $translator->addResource('array', array( + 'Hello World!' => 'Bonjour', + ), 'fr_FR'); + +Loading Messages with the File Loaders +...................................... + +If you use one of the file loaders, you also use the ``addResource`` method. +The only difference is that you put the file name as the second argument, +instead of an array:: + + // ... + $translator->addLoader('yaml', new YamlFileLoader()); + $translator->addResource('yaml', 'path/to/messages.fr.yml', 'fr_FR'); + +Translate Strings +----------------- + +After you have loaded your Message Catalogues, you can begin to translate your +strings. This is done with the +:method:`Symfony\\Component\\Translation\\Translator::trans` method:: + + // ... + $translator->addResource('array', array( + 'Hello World!' => 'Bonjour', + ), 'fr_FR'); + $translator->addResource('array', array( + 'Hello World!' => 'Hello World', + ), 'en_GB'); + + echo $translator->trans('Hello World!'); + // >> 'Bonjour' + +By default, the ``trans`` method uses the locale that is set in the +constructor of the ``Translator``. If you want to translate another locale, +you can change that by setting the fourth argument to the locale:: + + // ... + echo $translator->trans('Hello World!', array(), 'messages', 'en_GB'); + // >> 'Hello World!' + +Learn More +---------- + +The Translation component can do a lot more things. Read more about the usage +of this component in :ref:`the Translation book article `. +That article is specific about the Translation component in the Symfony2 +Framework, but most of the article is framework independent. + +.. _Packagist: https://packagist.org/packages/symfony/translation From e6b2170c5b260b360477e598559ace835153a7c3 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 27 Apr 2013 13:33:58 +0200 Subject: [PATCH 02/13] Added 'constructing the translator' --- components/translation.rst | 65 +++++++++++++++----------------------- 1 file changed, 25 insertions(+), 40 deletions(-) diff --git a/components/translation.rst b/components/translation.rst index 6d183a46325..09fb01298e8 100644 --- a/components/translation.rst +++ b/components/translation.rst @@ -36,16 +36,35 @@ entry point of the Translation component. echo $translator->trans('Hello World!'); -Message Catalogues ------------------- +Constructing the Translator +--------------------------- + +Before you can use the Translator, you need to configure it and load the +message catalogues. + +Configuration +~~~~~~~~~~~~~ + +The constructor of the ``Translator`` class needs to arguments: The locale and +a :class:`Symfony\\Component\\Translation\\MessageSelector` to use when using +pluralization (more about that later):: + + use Symfony\Component\Translation\Translator; + use Symfony\Component\Translation\MessageSelector; + $translator = new Translator('fr_FR', new MessageSelector()); + +.. note:: + + The locale set here is the default locale to use. You can override this + locale when translating strings. + +Loading Message Catalogues +~~~~~~~~~~~~~~~~~~~~~~~~~~ The messages are stored in message catalogues inside the ``Translator`` -class. A Message Catalogue is like a dictionary of translations for a specific +class. A message catalogue is like a dictionary of translations for a specific locale. -Loading catalogues -~~~~~~~~~~~~~~~~~~ - The Translation component uses Loader classes to load catalogues. You can load multiple resources for the same locale, it will be combined into one catalogue. @@ -100,38 +119,4 @@ instead of an array:: $translator->addLoader('yaml', new YamlFileLoader()); $translator->addResource('yaml', 'path/to/messages.fr.yml', 'fr_FR'); -Translate Strings ------------------ - -After you have loaded your Message Catalogues, you can begin to translate your -strings. This is done with the -:method:`Symfony\\Component\\Translation\\Translator::trans` method:: - - // ... - $translator->addResource('array', array( - 'Hello World!' => 'Bonjour', - ), 'fr_FR'); - $translator->addResource('array', array( - 'Hello World!' => 'Hello World', - ), 'en_GB'); - - echo $translator->trans('Hello World!'); - // >> 'Bonjour' - -By default, the ``trans`` method uses the locale that is set in the -constructor of the ``Translator``. If you want to translate another locale, -you can change that by setting the fourth argument to the locale:: - - // ... - echo $translator->trans('Hello World!', array(), 'messages', 'en_GB'); - // >> 'Hello World!' - -Learn More ----------- - -The Translation component can do a lot more things. Read more about the usage -of this component in :ref:`the Translation book article `. -That article is specific about the Translation component in the Symfony2 -Framework, but most of the article is framework independent. - .. _Packagist: https://packagist.org/packages/symfony/translation From 5fd8de19e3e6ab9c25530850daba0bf6bb82c765 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 27 Apr 2013 13:44:46 +0200 Subject: [PATCH 03/13] Moved framework indepent stuff to component docs --- book/translation.rst | 545 +------------------------------------ components/translation.rst | 497 +++++++++++++++++++++++++++++++++ 2 files changed, 503 insertions(+), 539 deletions(-) diff --git a/book/translation.rst b/book/translation.rst index 6bee3c30d5e..e9809c05123 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -83,206 +83,6 @@ not exist in the user's locale. The locale used in translations is the one stored in the user session. -.. index:: - single: Translations; Basic translation - -.. _basic-translation: - -Basic Translation ------------------ - -Translation of text is done through the ``translator`` service -(:class:`Symfony\\Component\\Translation\\Translator`). To translate a block -of text (called a *message*), use the -:method:`Symfony\\Component\\Translation\\Translator::trans` method. Suppose, -for example, that you're translating a simple message from inside a controller:: - - // ... - use Symfony\Component\HttpFoundation\Response; - - public function indexAction() - { - $t = $this->get('translator')->trans('Symfony2 is great'); - - return new Response($t); - } - -When this code is executed, Symfony2 will attempt to translate the message -"Symfony2 is great" based on the ``locale`` of the user. For this to work, -you need to tell Symfony2 how to translate the message via a "translation -resource", which is a collection of message translations for a given locale. -This "dictionary" of translations can be created in several different formats, -XLIFF being the recommended format: - -.. configuration-block:: - - .. code-block:: xml - - - - - - - - Symfony2 is great - J'aime Symfony2 - - - - - - .. code-block:: php - - // messages.fr.php - return array( - 'Symfony2 is great' => 'J\'aime Symfony2', - ); - - .. code-block:: yaml - - # messages.fr.yml - Symfony2 is great: J'aime Symfony2 - -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 Symfony2``. - -The Translation Process -~~~~~~~~~~~~~~~~~~~~~~~ - -To actually translate the message, Symfony2 uses a simple process: - -* The ``locale`` of the current user, which is stored in the session, is determined; - -* A catalog 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. See `Message Catalogues`_ - for more details; - -* 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, Symfony2 looks for the exact string inside -the appropriate message catalog and returns it (if it exists). - -.. index:: - single: Translations; Message placeholders - -Message Placeholders -~~~~~~~~~~~~~~~~~~~~ - -Sometimes, a message containing a variable needs to be translated:: - - // ... - use Symfony\Component\HttpFoundation\Response; - - public function indexAction($name) - { - $t = $this->get('translator')->trans('Hello '.$name); - - return new Response($t); - } - -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":: - - // ... - use Symfony\Component\HttpFoundation\Response; - - public function indexAction($name) - { - $t = $this->get('translator')->trans( - 'Hello %name%', - array('%name%' => $name) - ); - - return new Response($t); - } - -Symfony2 will now look for a translation of the raw message (``Hello %name%``) -and *then* replace the placeholders with their values. Creating a translation -is done just as before: - -.. configuration-block:: - - .. code-block:: xml - - - - - - - - Hello %name% - Bonjour %name% - - - - - - .. code-block:: php - - // messages.fr.php - return array( - 'Hello %name%' => 'Bonjour %name%', - ); - - .. code-block:: yaml - - # messages.fr.yml - 'Hello %name%': Bonjour %name% - -.. note:: - - The placeholders can take on any form as the full message is reconstructed - using the PHP `strtr function`_. However, the ``%var%`` notation is - required when translating in Twig templates, and is overall a sensible - convention to follow. - -As you've seen, creating a translation is a two-step process: - -#. Abstract the message that needs to be translated by processing it through - the ``Translator``. - -#. Create a translation for the message in each locale that you choose to - support. - -The second step is done by creating message catalogues that define the translations -for any number of different locales. - -.. index:: - single: Translations; Message catalogues - -Message Catalogues ------------------- - -When a message is translated, Symfony2 compiles a message catalogue for the -user's locale and looks in it for a translation of the message. A message -catalogue is like a dictionary of translations for a specific locale. For -example, the catalogue for the ``fr_FR`` locale might contain the following -translation: - -.. code-block:: text - - Symfony2 is Great => J'aime Symfony2 - -It's the responsibility of the developer (or translator) of an internationalized -application to create these translations. Translations are stored on the -filesystem and discovered by Symfony, thanks to some conventions. - -.. tip:: - - 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 resource: - - .. code-block:: bash - - $ php app/console cache:clear - .. index:: single: Translations; Translation resource locations @@ -325,164 +125,15 @@ taste. providing a custom class implementing the :class:`Symfony\\Component\\Translation\\Loader\\LoaderInterface` interface. -.. index:: - single: Translations; Creating translation resources - -Creating Translations -~~~~~~~~~~~~~~~~~~~~~ - -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. -"symfony2.great" - see the sidebar below): - -.. configuration-block:: - - .. code-block:: xml - - - - - - - - Symfony2 is great - J'aime Symfony2 - - - symfony2.great - J'aime Symfony2 - - - - - - .. code-block:: php - - // src/Acme/DemoBundle/Resources/translations/messages.fr.php - return array( - 'Symfony2 is great' => 'J\'aime Symfony2', - 'symfony2.great' => 'J\'aime Symfony2', - ); - - .. code-block:: yaml - - # src/Acme/DemoBundle/Resources/translations/messages.fr.yml - Symfony2 is great: J'aime Symfony2 - symfony2.great: J'aime Symfony2 - -Symfony2 will discover these files and use them when translating either -"Symfony2 is great" or "symfony2.great" into a French language locale (e.g. -``fr_FR`` or ``fr_BE``). - -.. sidebar:: Using Real or Keyword Messages - - This example illustrates the two different philosophies when creating - messages to be translated:: - - $t = $translator->trans('Symfony2 is great'); - - $t = $translator->trans('symfony2.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 ``symfony2.great`` to ``Symfony2 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 "Symfony2 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: - - .. configuration-block:: - - .. code-block:: yaml - - symfony2: - is: - great: Symfony2 is great - amazing: Symfony2 is amazing - has: - bundles: Symfony2 has bundles - user: - login: Login - - .. code-block:: php - - return array( - 'symfony2' => array( - 'is' => array( - 'great' => 'Symfony2 is great', - 'amazing' => 'Symfony2 is amazing', - ), - 'has' => array( - 'bundles' => 'Symfony2 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: - - .. configuration-block:: - - .. code-block:: yaml - - symfony2.is.great: Symfony2 is great - symfony2.is.amazing: Symfony2 is amazing - symfony2.has.bundles: Symfony2 has bundles - user.login: Login - - .. code-block:: php - - return array( - 'symfony2.is.great' => 'Symfony2 is great', - 'symfony2.is.amazing' => 'Symfony2 is amazing', - 'symfony2.has.bundles' => 'Symfony2 has bundles', - 'user.login' => 'Login', - ); - -.. index:: - single: Translations; Message domains - -Using Message Domains ---------------------- - -As you've seen, message files are organized into the different locales that -they translate. The message files can also be organized further into "domains". -When creating message files, the domain is the first portion of the filename. -The default domain is ``messages``. For example, suppose that, for organization, -translations were split into three different domains: ``messages``, ``admin`` -and ``navigation``. The French translation would have the following message -files: - -* ``messages.fr.xliff`` -* ``admin.fr.xliff`` -* ``navigation.fr.xliff`` +.. tip:: -When translating strings that are not in the default domain (``messages``), -you must specify the domain as the third argument of ``trans()``:: + 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 resource: - $this->get('translator')->trans('Symfony2 is great', array(), 'admin'); + .. code-block:: bash -Symfony2 will now look for the message in the ``admin`` domain of the user's -locale. + $ php app/console cache:clear .. index:: single: Translations; User's locale @@ -497,41 +148,6 @@ via the ``session`` service:: $this->get('session')->setLocale('en_US'); -.. index:: - single: Translations; Fallback and default locale - -Fallback and Default Locale -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If the locale hasn't been set explicitly in the session, the ``fallback_locale`` -configuration parameter will be used by the ``Translator``. The parameter -defaults to ``en`` (see `Configuration`_). - -Alternatively, you can guarantee that a locale is set on the user's session -by defining a ``default_locale`` for the session service: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - framework: - session: { default_locale: en } - - .. code-block:: xml - - - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('framework', array( - 'session' => array('default_locale' => 'en'), - )); - .. _book-translation-locale-url: The Locale and the URL @@ -589,122 +205,6 @@ as the locale for the user's session. You can now use the user's locale to create routes to other translated pages in your application. -.. index:: - single: Translations; Pluralization - -Pluralization -------------- - -Message pluralization is a tough topic as the rules can be quite complex. For -instance, here is the mathematic representation of the Russian pluralization -rules:: - - (($number % 10 == 1) && ($number % 100 != 11)) - ? 0 - : ((($number % 10 >= 2) - && ($number % 10 <= 4) - && (($number % 100 < 10) - || ($number % 100 >= 20))) - ? 1 - : 2 - ); - -As you can see, in Russian, you can have three different plural forms, each -given an index of 0, 1 or 2. For each form, the plural is different, and -so the translation is also different. - -When a translation has different forms due to pluralization, you can provide -all the forms as a string separated by a pipe (``|``):: - - 'There is one apple|There are %count% apples' - -To translate pluralized messages, use the -:method:`Symfony\\Component\\Translation\\Translator::transChoice` method:: - - $t = $this->get('translator')->transChoice( - 'There is one apple|There are %count% apples', - 10, - array('%count%' => 10) - ); - -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. - -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:: - - '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:: - - '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). - -Explicit Interval Pluralization -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The easiest way to pluralize a message is to let Symfony2 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:: - - '{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:: - - '{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. - -An :class:`Symfony\\Component\\Translation\\Interval` can represent a finite set -of numbers:: - - {1,2,3,4} - -Or numbers between two other numbers:: - - [1, +Inf[ - ]-1,2[ - -The left delimiter can be ``[`` (inclusive) or ``]`` (exclusive). The right -delimiter can be ``[`` (exclusive) or ``]`` (inclusive). Beside numbers, you -can use ``-Inf`` and ``+Inf`` for the infinite. - .. index:: single: Translations; In templates @@ -801,35 +301,6 @@ The translator service is accessible in PHP templates through the array('%count%' => 10) ) ?> -Forcing the Translator Locale ------------------------------ - -When translating a message, Symfony2 uses the locale from the user's session -or the ``fallback`` locale if necessary. You can also manually specify the -locale to use for translation:: - - $this->get('translator')->trans( - 'Symfony2 is great', - array(), - 'messages', - 'fr_FR' - ); - - $this->get('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' - ); - -Translating Database Content ----------------------------- - -The translation of database content should be handled by Doctrine through -the `Translatable Extension`_. For more information, see the documentation -for that library. - .. _book-translation-constraint-messages: Translating Constraint Messages @@ -960,9 +431,5 @@ steps: * Manage the user's locale, which is stored in the session. .. _`i18n`: http://en.wikipedia.org/wiki/Internationalization_and_localization -.. _`L10n`: http://en.wikipedia.org/wiki/Internationalization_and_localization -.. _`strtr function`: http://www.php.net/manual/en/function.strtr.php -.. _`ISO 31-11`: http://en.wikipedia.org/wiki/Interval_(mathematics)#Notations_for_intervals -.. _`Translatable Extension`: https://github.com/l3pp4rd/DoctrineExtensions .. _`ISO3166 Alpha-2`: http://en.wikipedia.org/wiki/ISO_3166-1#Current_codes .. _`ISO639-1`: http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes diff --git a/components/translation.rst b/components/translation.rst index 09fb01298e8..c8d40706be1 100644 --- a/components/translation.rst +++ b/components/translation.rst @@ -119,4 +119,501 @@ instead of an array:: $translator->addLoader('yaml', new YamlFileLoader()); $translator->addResource('yaml', 'path/to/messages.fr.yml', 'fr_FR'); +.. _basic-translation: + +Basic Translation +----------------- + +Translation of text is done through the ``translator`` service +(:class:`Symfony\\Component\\Translation\\Translator`). To translate a block +of text (called a *message*), use the +:method:`Symfony\\Component\\Translation\\Translator::trans` method. Suppose, +for example, that you're translating a simple message from inside a controller:: + + // ... + use Symfony\Component\HttpFoundation\Response; + + public function indexAction() + { + $t = $this->get('translator')->trans('Symfony2 is great'); + + return new Response($t); + } + +When this code is executed, Symfony2 will attempt to translate the message +"Symfony2 is great" based on the ``locale`` of the user. For this to work, +you need to tell Symfony2 how to translate the message via a "translation +resource", which is a collection of message translations for a given locale. +This "dictionary" of translations can be created in several different formats, +XLIFF being the recommended format: + +.. configuration-block:: + + .. code-block:: xml + + + + + + + + Symfony2 is great + J'aime Symfony2 + + + + + + .. code-block:: php + + // messages.fr.php + return array( + 'Symfony2 is great' => 'J\'aime Symfony2', + ); + + .. code-block:: yaml + + # messages.fr.yml + Symfony2 is great: J'aime Symfony2 + +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 Symfony2``. + +The Translation Process +~~~~~~~~~~~~~~~~~~~~~~~ + +To actually translate the message, Symfony2 uses a simple process: + +* The ``locale`` of the current user, which is stored in the session, is determined; + +* A catalog 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. See `Message Catalogues`_ + for more details; + +* 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, Symfony2 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:: + + // ... + use Symfony\Component\HttpFoundation\Response; + + public function indexAction($name) + { + $t = $this->get('translator')->trans('Hello '.$name); + + return new Response($t); + } + +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":: + + // ... + use Symfony\Component\HttpFoundation\Response; + + public function indexAction($name) + { + $t = $this->get('translator')->trans( + 'Hello %name%', + array('%name%' => $name) + ); + + return new Response($t); + } + +Symfony2 will now look for a translation of the raw message (``Hello %name%``) +and *then* replace the placeholders with their values. Creating a translation +is done just as before: + +.. configuration-block:: + + .. code-block:: xml + + + + + + + + Hello %name% + Bonjour %name% + + + + + + .. code-block:: php + + // messages.fr.php + return array( + 'Hello %name%' => 'Bonjour %name%', + ); + + .. code-block:: yaml + + # messages.fr.yml + 'Hello %name%': Bonjour %name% + +.. note:: + + The placeholders can take on any form as the full message is reconstructed + using the PHP :phpfunction:`strtr function`. However, the ``%var%`` notation is + required when translating in Twig templates, and is overall a sensible + convention to follow. + +As you've seen, creating a translation is a two-step process: + +#. Abstract the message that needs to be translated by processing it through + the ``Translator``. + +#. Create a translation for the message in each locale that you choose to + support. + +The second step is done by creating message catalogues that define the translations +for any number of different locales. + +Creating Translations +~~~~~~~~~~~~~~~~~~~~~ + +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. +"symfony2.great" - see the sidebar below): + +.. configuration-block:: + + .. code-block:: xml + + + + + + + + Symfony2 is great + J'aime Symfony2 + + + symfony2.great + J'aime Symfony2 + + + + + + .. code-block:: php + + // src/Acme/DemoBundle/Resources/translations/messages.fr.php + return array( + 'Symfony2 is great' => 'J\'aime Symfony2', + 'symfony2.great' => 'J\'aime Symfony2', + ); + + .. code-block:: yaml + + # src/Acme/DemoBundle/Resources/translations/messages.fr.yml + Symfony2 is great: J'aime Symfony2 + symfony2.great: J'aime Symfony2 + +Symfony2 will discover these files and use them when translating either +"Symfony2 is great" or "symfony2.great" into a French language locale (e.g. +``fr_FR`` or ``fr_BE``). + +.. sidebar:: Using Real or Keyword Messages + + This example illustrates the two different philosophies when creating + messages to be translated:: + + $t = $translator->trans('Symfony2 is great'); + + $t = $translator->trans('symfony2.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 ``symfony2.great`` to ``Symfony2 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 "Symfony2 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: + + .. configuration-block:: + + .. code-block:: yaml + + symfony2: + is: + great: Symfony2 is great + amazing: Symfony2 is amazing + has: + bundles: Symfony2 has bundles + user: + login: Login + + .. code-block:: php + + return array( + 'symfony2' => array( + 'is' => array( + 'great' => 'Symfony2 is great', + 'amazing' => 'Symfony2 is amazing', + ), + 'has' => array( + 'bundles' => 'Symfony2 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: + + .. configuration-block:: + + .. code-block:: yaml + + symfony2.is.great: Symfony2 is great + symfony2.is.amazing: Symfony2 is amazing + symfony2.has.bundles: Symfony2 has bundles + user.login: Login + + .. code-block:: php + + return array( + 'symfony2.is.great' => 'Symfony2 is great', + 'symfony2.is.amazing' => 'Symfony2 is amazing', + 'symfony2.has.bundles' => 'Symfony2 has bundles', + 'user.login' => 'Login', + ); + +.. index:: + single: Translations; Message domains + +Using Message Domains +--------------------- + +As you've seen, message files are organized into the different locales that +they translate. The message files can also be organized further into "domains". +When creating message files, the domain is the first portion of the filename. +The default domain is ``messages``. For example, suppose that, for organization, +translations were split into three different domains: ``messages``, ``admin`` +and ``navigation``. The French translation would have the following message +files: + +* ``messages.fr.xliff`` +* ``admin.fr.xliff`` +* ``navigation.fr.xliff`` + +When translating strings that are not in the default domain (``messages``), +you must specify the domain as the third argument of ``trans()``:: + + $this->get('translator')->trans('Symfony2 is great', array(), 'admin'); + +Symfony2 will now look for the message in the ``admin`` domain of the user's +locale. + +Fallback and Default Locale +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If the locale hasn't been set explicitly in the session, the ``fallback_locale`` +configuration parameter will be used by the ``Translator``. The parameter +defaults to ``en`` (see `Configuration`_). + +Alternatively, you can guarantee that a locale is set on the user's session +by defining a ``default_locale`` for the session service: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + framework: + session: { default_locale: en } + + .. code-block:: xml + + + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('framework', array( + 'session' => array('default_locale' => 'en'), + )); + +Pluralization +------------- + +Message pluralization is a tough topic as the rules can be quite complex. For +instance, here is the mathematic representation of the Russian pluralization +rules:: + + (($number % 10 == 1) && ($number % 100 != 11)) + ? 0 + : ((($number % 10 >= 2) + && ($number % 10 <= 4) + && (($number % 100 < 10) + || ($number % 100 >= 20))) + ? 1 + : 2 + ); + +As you can see, in Russian, you can have three different plural forms, each +given an index of 0, 1 or 2. For each form, the plural is different, and +so the translation is also different. + +When a translation has different forms due to pluralization, you can provide +all the forms as a string separated by a pipe (``|``):: + + 'There is one apple|There are %count% apples' + +To translate pluralized messages, use the +:method:`Symfony\\Component\\Translation\\Translator::transChoice` method:: + + $t = $this->get('translator')->transChoice( + 'There is one apple|There are %count% apples', + 10, + array('%count%' => 10) + ); + +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. + +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:: + + '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:: + + '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). + +Explicit Interval Pluralization +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The easiest way to pluralize a message is to let Symfony2 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:: + + '{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:: + + '{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. + +An :class:`Symfony\\Component\\Translation\\Interval` can represent a finite set +of numbers:: + + {1,2,3,4} + +Or numbers between two other numbers:: + + [1, +Inf[ + ]-1,2[ + +The left delimiter can be ``[`` (inclusive) or ``]`` (exclusive). The right +delimiter can be ``[`` (exclusive) or ``]`` (inclusive). Beside numbers, you +can use ``-Inf`` and ``+Inf`` for the infinite. + +Forcing the Translator Locale +----------------------------- + +When translating a message, Symfony2 uses the locale from the user's session +or the ``fallback`` locale if necessary. You can also manually specify the +locale to use for translation:: + + $this->get('translator')->trans( + 'Symfony2 is great', + array(), + 'messages', + 'fr_FR' + ); + + $this->get('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' + ); + +Translating Database Content +---------------------------- + +The translation of database content should be handled by Doctrine through +the `Translatable Extension`_. For more information, see the documentation +for that library. + .. _Packagist: https://packagist.org/packages/symfony/translation +.. _`L10n`: http://en.wikipedia.org/wiki/Internationalization_and_localization +.. _`ISO 31-11`: http://en.wikipedia.org/wiki/Interval_(mathematics)#Notations_for_intervals +.. _`Translatable Extension`: https://github.com/l3pp4rd/DoctrineExtensions From 1e34f1fc91b8654c29c1349936f6c66041538b1c Mon Sep 17 00:00:00 2001 From: WouterJ Date: Mon, 29 Apr 2013 11:54:27 +0200 Subject: [PATCH 04/13] Rewrote component article --- book/translation.rst | 45 ++++++ components/translation.rst | 299 ++++++++++++++----------------------- 2 files changed, 158 insertions(+), 186 deletions(-) diff --git a/book/translation.rst b/book/translation.rst index e9809c05123..02ca771d56d 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -83,6 +83,38 @@ not exist in the user's locale. The locale used in translations is the one stored in the user session. +Fallback and Default Locale +--------------------------- + +If the locale hasn't been set explicitly in the session, the ``fallback_locale`` +configuration parameter will be used by the ``Translator``. The parameter +defaults to ``en`` (see `Configuration`_). + +Alternatively, you can guarantee that a locale is set on the user's session +by defining a ``default_locale`` for the session service: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + framework: + session: { default_locale: en } + + .. code-block:: xml + + + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('framework', array( + 'session' => array('default_locale' => 'en'), + )); + .. index:: single: Translations; Translation resource locations @@ -219,6 +251,11 @@ support for both Twig and PHP templates. Twig Templates ~~~~~~~~~~~~~~ +.. + However, the ``%var%`` + notation is required when translating in Twig templates, and is overall a + sensible convention to follow. + Symfony2 provides specialized Twig tags (``trans`` and ``transchoice``) to help with message translation of *static blocks of text*: @@ -413,6 +450,13 @@ Create a translation file under the ``validators`` catalog for the constraint me # validators.en.yml author.name.not_blank: Please enter an author name. +Translating Database Content +---------------------------- + +The translation of database content should be handled by Doctrine through +the `Translatable Extension`_. For more information, see the documentation +for that library. + Summary ------- @@ -433,3 +477,4 @@ steps: .. _`i18n`: http://en.wikipedia.org/wiki/Internationalization_and_localization .. _`ISO3166 Alpha-2`: http://en.wikipedia.org/wiki/ISO_3166-1#Current_codes .. _`ISO639-1`: http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes +.. _`Translatable Extension`: https://github.com/l3pp4rd/DoctrineExtensions diff --git a/components/translation.rst b/components/translation.rst index c8d40706be1..74dc386c320 100644 --- a/components/translation.rst +++ b/components/translation.rst @@ -36,6 +36,8 @@ entry point of the Translation component. echo $translator->trans('Hello World!'); +.. document the fallback locale + Constructing the Translator --------------------------- @@ -51,6 +53,7 @@ pluralization (more about that later):: use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\MessageSelector; + $translator = new Translator('fr_FR', new MessageSelector()); .. note:: @@ -119,84 +122,55 @@ instead of an array:: $translator->addLoader('yaml', new YamlFileLoader()); $translator->addResource('yaml', 'path/to/messages.fr.yml', 'fr_FR'); -.. _basic-translation: - -Basic Translation ------------------ - -Translation of text is done through the ``translator`` service -(:class:`Symfony\\Component\\Translation\\Translator`). To translate a block -of text (called a *message*), use the -:method:`Symfony\\Component\\Translation\\Translator::trans` method. Suppose, -for example, that you're translating a simple message from inside a controller:: - - // ... - use Symfony\Component\HttpFoundation\Response; - - public function indexAction() - { - $t = $this->get('translator')->trans('Symfony2 is great'); - - return new Response($t); - } - -When this code is executed, Symfony2 will attempt to translate the message -"Symfony2 is great" based on the ``locale`` of the user. For this to work, -you need to tell Symfony2 how to translate the message via a "translation -resource", which is a collection of message translations for a given locale. -This "dictionary" of translations can be created in several different formats, -XLIFF being the recommended format: - -.. configuration-block:: - - .. code-block:: xml +The Translation Process +----------------------- - - - - - - - Symfony2 is great - J'aime Symfony2 - - - - +To actually translate the message, the Translator uses a simple process: - .. code-block:: php +* A catalog 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; - // messages.fr.php - return array( - 'Symfony2 is great' => 'J\'aime Symfony2', - ); +* If the message is located in the catalog, the translation is returned. If + not, the translator returns the original message. - .. code-block:: yaml +You start this process by calling +:method:`Symfony\\Component\\Translation\\Translator::trans`. Then, the +Translator looks for the exact string inside the appropriate message catalog +and returns it (if it exists). - # messages.fr.yml - Symfony2 is great: J'aime Symfony2 +Fallback Locale +~~~~~~~~~~~~~~~ -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 Symfony2``. +If the message is not located in the catalogue of the specific locale, the +translator will look into the catalogue of the fallback locale. You can set +this fallback locale by calling +:method:`Symfony\\Component\\Translation\\Translator::setFallbackLocale`:: -The Translation Process -~~~~~~~~~~~~~~~~~~~~~~~ + // ... + $translator->setFallbackLocale('en_EN'); -To actually translate the message, Symfony2 uses a simple process: +Basic Translation +----------------- -* The ``locale`` of the current user, which is stored in the session, is determined; +Imagine you want to translate the string *"Symfony2 is great"* into french:: -* A catalog 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. See `Message Catalogues`_ - for more details; + use Symfony\Component\Translation\Translator; + use Symfony\Component\Translation\MessageSelector; + use Symfony\Component\Translation\Loader\ArrayLoader; -* If the message is located in the catalog, the translation is returned. If - not, the translator returns the original message. + $translator = new Translator('fr_FR', new MessageSelector()); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array( + 'Symfony2 is great!' => 'J'aime Symfony2!', + ), 'fr_FR'); + + echo $translator->trans('Symfony2 is great!'); -When using the ``trans()`` method, Symfony2 looks for the exact string inside -the appropriate message catalog and returns it (if it exists). +In this example, the message *"Symfony2 is great!"* will be translated into +the locale set in the constructor (``fr_FR``) if the message exists in one of +the message catalogues. Message Placeholders ~~~~~~~~~~~~~~~~~~~~ @@ -204,33 +178,23 @@ Message Placeholders Sometimes, a message containing a variable needs to be translated:: // ... - use Symfony\Component\HttpFoundation\Response; + $translated = $translator->trans('Hello '.$name); - public function indexAction($name) - { - $t = $this->get('translator')->trans('Hello '.$name); - - return new Response($t); - } + echo $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"). Instead of writing a translation +(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":: // ... - use Symfony\Component\HttpFoundation\Response; - - public function indexAction($name) - { - $t = $this->get('translator')->trans( - 'Hello %name%', - array('%name%' => $name) - ); + $translated = $translator->trans( + 'Hello %name%', + array('%name%' => $name) + ); - return new Response($t); - } + echo $translated; Symfony2 will now look for a translation of the raw message (``Hello %name%``) and *then* replace the placeholders with their values. Creating a translation @@ -240,7 +204,6 @@ is done just as before: .. code-block:: xml - @@ -255,22 +218,18 @@ is done just as before: .. code-block:: php - // messages.fr.php return array( 'Hello %name%' => 'Bonjour %name%', ); .. code-block:: yaml - # messages.fr.yml 'Hello %name%': Bonjour %name% .. note:: The placeholders can take on any form as the full message is reconstructed - using the PHP :phpfunction:`strtr function`. However, the ``%var%`` notation is - required when translating in Twig templates, and is overall a sensible - convention to follow. + using the PHP :phpfunction:`strtr function`. As you've seen, creating a translation is a two-step process: @@ -284,20 +243,22 @@ The second step is done by creating message catalogues that define the translati for any number of different locales. Creating Translations -~~~~~~~~~~~~~~~~~~~~~ +--------------------- 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. -"symfony2.great" - see the sidebar below): +*"Symfony is great"*) of your application or a unique identifier (e.g. +``symfony2.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. .. configuration-block:: .. code-block:: xml - @@ -316,7 +277,6 @@ for the individual translation, and can be the message in the main locale (e.g. .. code-block:: php - // src/Acme/DemoBundle/Resources/translations/messages.fr.php return array( 'Symfony2 is great' => 'J\'aime Symfony2', 'symfony2.great' => 'J\'aime Symfony2', @@ -324,22 +284,17 @@ for the individual translation, and can be the message in the main locale (e.g. .. code-block:: yaml - # src/Acme/DemoBundle/Resources/translations/messages.fr.yml Symfony2 is great: J'aime Symfony2 symfony2.great: J'aime Symfony2 -Symfony2 will discover these files and use them when translating either -"Symfony2 is great" or "symfony2.great" into a French language locale (e.g. -``fr_FR`` or ``fr_BE``). - .. sidebar:: Using Real or Keyword Messages This example illustrates the two different philosophies when creating messages to be translated:: - $t = $translator->trans('Symfony2 is great'); + $translator->trans('Symfony2 is great'); - $t = $translator->trans('symfony2.great'); + $translator->trans('symfony2.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" @@ -376,7 +331,7 @@ Symfony2 will discover these files and use them when translating either .. code-block:: php - return array( + array( 'symfony2' => array( 'is' => array( 'great' => 'Symfony2 is great', @@ -392,7 +347,7 @@ Symfony2 will discover these files and use them when translating either ); The multiple levels are flattened into single id/translation pairs by - adding a dot (.) between every level, therefore the above examples are + adding a dot (``.``) between every level, therefore the above examples are equivalent to the following: .. configuration-block:: @@ -413,66 +368,8 @@ Symfony2 will discover these files and use them when translating either 'user.login' => 'Login', ); -.. index:: - single: Translations; Message domains - -Using Message Domains ---------------------- - -As you've seen, message files are organized into the different locales that -they translate. The message files can also be organized further into "domains". -When creating message files, the domain is the first portion of the filename. -The default domain is ``messages``. For example, suppose that, for organization, -translations were split into three different domains: ``messages``, ``admin`` -and ``navigation``. The French translation would have the following message -files: - -* ``messages.fr.xliff`` -* ``admin.fr.xliff`` -* ``navigation.fr.xliff`` - -When translating strings that are not in the default domain (``messages``), -you must specify the domain as the third argument of ``trans()``:: - - $this->get('translator')->trans('Symfony2 is great', array(), 'admin'); - -Symfony2 will now look for the message in the ``admin`` domain of the user's -locale. - -Fallback and Default Locale -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If the locale hasn't been set explicitly in the session, the ``fallback_locale`` -configuration parameter will be used by the ``Translator``. The parameter -defaults to ``en`` (see `Configuration`_). - -Alternatively, you can guarantee that a locale is set on the user's session -by defining a ``default_locale`` for the session service: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - framework: - session: { default_locale: en } - - .. code-block:: xml - - - - - - - .. code-block:: php - - // app/config/config.php - $container->loadFromExtension('framework', array( - 'session' => array('default_locale' => 'en'), - )); - Pluralization -------------- +~~~~~~~~~~~~~ Message pluralization is a tough topic as the rules can be quite complex. For instance, here is the mathematic representation of the Russian pluralization @@ -500,7 +397,7 @@ all the forms as a string separated by a pipe (``|``):: To translate pluralized messages, use the :method:`Symfony\\Component\\Translation\\Translator::transChoice` method:: - $t = $this->get('translator')->transChoice( + $translator->transChoice( 'There is one apple|There are %count% apples', 10, array('%count%' => 10) @@ -516,7 +413,9 @@ 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:: +Here is the French translation: + +.. code-block:: text 'Il y a %count% pomme|Il y a %count% pommes' @@ -529,7 +428,9 @@ 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:: +you can optionally "tag" each string: + +.. code-block:: text 'one: There is one apple|some: There are %count% apples' @@ -546,13 +447,15 @@ same in the original message as in the translated one. only get a string based on its position in the string). Explicit Interval Pluralization -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +............................... -The easiest way to pluralize a message is to let Symfony2 use internal logic -to choose which string to use based on a given number. Sometimes, you'll +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:: +use explicit math intervals: + +.. code-block:: text '{0} There are no apples|{1} There is one apple|]1,19] There are %count% apples|[20,Inf] There are many apples' @@ -562,7 +465,9 @@ 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:: +effect after removing the explicit rules: + +.. code-block:: text '{0} There are no apples|[20,Inf] There are many apples|There is one apple|a_few: There are %count% apples' @@ -571,34 +476,64 @@ be used. For ``2-19`` apples, the second standard rule ``There are %count% apples`` will be selected. An :class:`Symfony\\Component\\Translation\\Interval` can represent a finite set -of numbers:: +of numbers: + +.. code-block:: text {1,2,3,4} -Or numbers between two other numbers:: +Or numbers between two other numbers: + +.. code-block:: text [1, +Inf[ ]-1,2[ The left delimiter can be ``[`` (inclusive) or ``]`` (exclusive). The right delimiter can be ``[`` (exclusive) or ``]`` (inclusive). Beside numbers, you + +Using Message Domains +--------------------- + +As you've seen, message files are organized into the different locales that +they translate. The message files can also be organized further into "domains". + +The domain is specific in the fourth argument of the ``addResource()`` method. +The default domain is ``messages``. For example, suppose that, for organization, +translations were split into three different domains: ``messages``, ``admin`` +and ``navigation``. The French translation would be loaded like this:: + + // ... + $translator->addLoader('xliff', new XliffLoader()); + + $translator->addResource('xliff', 'messages.fr.xliff', 'fr_FR'); + $translator->addResource('xliff', 'admin.fr.xliff', 'fr_FR', 'admin'); + $translator->addResource('xliff', 'navigation.fr.xliff', 'fr_FR', 'navigation'); + +When translating strings that are not in the default domain (``messages``), +you must specify the domain as the third argument of ``trans()``:: + + $translator->trans('Symfony2 is great', array(), 'admin'); + +Symfony2 will now look for the message in the ``admin`` domain of the +specified locale. can use ``-Inf`` and ``+Inf`` for the infinite. Forcing the Translator Locale ----------------------------- -When translating a message, Symfony2 uses the locale from the user's session -or the ``fallback`` locale if necessary. You can also manually specify the -locale to use for translation:: +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:: - $this->get('translator')->trans( + $translator->trans( 'Symfony2 is great', array(), 'messages', 'fr_FR' ); - $this->get('translator')->transChoice( + $translator->transChoice( '{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples', 10, array('%count%' => 10), @@ -606,14 +541,6 @@ locale to use for translation:: 'fr_FR' ); -Translating Database Content ----------------------------- - -The translation of database content should be handled by Doctrine through -the `Translatable Extension`_. For more information, see the documentation -for that library. - .. _Packagist: https://packagist.org/packages/symfony/translation .. _`L10n`: http://en.wikipedia.org/wiki/Internationalization_and_localization .. _`ISO 31-11`: http://en.wikipedia.org/wiki/Interval_(mathematics)#Notations_for_intervals -.. _`Translatable Extension`: https://github.com/l3pp4rd/DoctrineExtensions From 6c70350620afe5274e4632811f88b6df83f0852d Mon Sep 17 00:00:00 2001 From: WouterJ Date: Mon, 29 Apr 2013 12:28:14 +0200 Subject: [PATCH 05/13] Rewrote book article --- book/translation.rst | 77 +++++++++++++++++++++----------------- components/translation.rst | 9 +++++ 2 files changed, 51 insertions(+), 35 deletions(-) diff --git a/book/translation.rst b/book/translation.rst index 02ca771d56d..b1fe553de3b 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -26,11 +26,12 @@ the user:: `ISO639-1`_ *language* code, an underscore (``_``), then the `ISO3166 Alpha-2`_ *country* code (e.g. ``fr_FR`` for French/France) is recommended. -In this chapter, you'll learn how to prepare an application to support multiple -locales and then how to create translations for multiple locales. Overall, -the process has several common steps: +In this chapter, you'll learn how to use the Translation component in the +Symfony2 framework. Read the +:doc:`components documentation ` to learn how to use +the Translator. Overall, the process has several common steps: -#. Enable and configure Symfony's ``Translation`` component; +#. Enable and configure Symfony's Translation component; #. Abstract strings (i.e. "messages") by wrapping them in calls to the ``Translator``; @@ -39,13 +40,10 @@ the process has several common steps: #. Determine, set and manage the user's locale in the session. -.. index:: - single: Translations; Configuration - Configuration ------------- -Translations are handled by a ``Translator`` :term:`service` that uses the +Translations are handled by a ``translator`` :term:`service` that uses the user's locale to lookup and return translated messages. Before using it, enable the ``Translator`` in your configuration: @@ -74,17 +72,10 @@ enable the ``Translator`` in your configuration: The ``fallback`` option defines the fallback locale when a translation does not exist in the user's locale. -.. tip:: - - When a translation does not exist for a locale, the translator first tries - to find the translation for the language (``fr`` if the locale is - ``fr_FR`` for instance). If this also fails, it looks for a translation - using the fallback locale. - The locale used in translations is the one stored in the user session. Fallback and Default Locale ---------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~ If the locale hasn't been set explicitly in the session, the ``fallback_locale`` configuration parameter will be used by the ``Translator``. The parameter @@ -115,11 +106,31 @@ by defining a ``default_locale`` for the session service: 'session' => array('default_locale' => 'en'), )); -.. index:: - single: Translations; Translation resource locations +Using the Translation inside Controllers +---------------------------------------- + +When you want to use translation inside controllers, you need to get the +``translator`` service and use ``trans`` or ``transChoice``:: + + // src/Acme/DemoBundle/Controller/DemoController.php + namespace Amce\DemoBundle\Controller; + + // ... + + class DemoController extends Controller + { + public function indexAction() + { + $translator = $this->get('translator'); + + $translated = $translator->trans('Symfony2 is great!'); + + return new Response($translated); + } + } Translation Locations and Naming Conventions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-------------------------------------------- Symfony2 looks for message files (i.e. translations) in two locations: @@ -134,7 +145,7 @@ to determine details about the translations. Each message file must be named according to the following pattern: ``domain.locale.loader``: * **domain**: An optional way to organize messages into groups (e.g. ``admin``, - ``navigation`` or the default ``messages``) - see `Using Message Domains`_; + ``navigation`` or the default ``messages``) - see ":ref:`using-message-domains`"; * **locale**: The locale that the translations are for (e.g. ``en_GB``, ``en``, etc); @@ -145,8 +156,8 @@ The loader can be the name of any registered loader. By default, Symfony provides the following loaders: * ``xliff``: XLIFF file; -* ``php``: PHP file; -* ``yml``: YAML 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. @@ -157,7 +168,7 @@ taste. providing a custom class implementing the :class:`Symfony\\Component\\Translation\\Loader\\LoaderInterface` interface. -.. tip:: +.. 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 @@ -167,9 +178,6 @@ taste. $ php app/console cache:clear -.. index:: - single: Translations; User's locale - Handling the User's Locale -------------------------- @@ -237,9 +245,6 @@ as the locale for the user's session. You can now use the user's locale to create routes to other translated pages in your application. -.. index:: - single: Translations; In templates - Translations in Templates ------------------------- @@ -251,11 +256,6 @@ support for both Twig and PHP templates. Twig Templates ~~~~~~~~~~~~~~ -.. - However, the ``%var%`` - notation is required when translating in Twig templates, and is overall a - sensible convention to follow. - Symfony2 provides specialized Twig tags (``trans`` and ``transchoice``) to help with message translation of *static blocks of text*: @@ -271,6 +271,11 @@ 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. + .. tip:: If you need to use the percent character (``%``) in a string, escape it by @@ -419,7 +424,9 @@ empty, add the following: } } -Create a translation file under the ``validators`` catalog for the constraint messages, typically in the ``Resources/translations/`` directory of the bundle. See `Message Catalogues`_ for more details. +Create a translation file under the ``validators`` catalog for the constraint +messages, typically in the ``Resources/translations/`` directory of the +bundle. .. configuration-block:: diff --git a/components/translation.rst b/components/translation.rst index 74dc386c320..cdbc2a8986f 100644 --- a/components/translation.rst +++ b/components/translation.rst @@ -140,6 +140,13 @@ You start this process by calling Translator looks for the exact string inside the appropriate message catalog and returns it (if it exists). +.. tip:: + + When a translation does not exist for a locale, the translator first tries + to find the translation for the language (``fr`` if the locale is + ``fr_FR`` for instance). If this also fails, it looks for a translation + using the fallback locale. + Fallback Locale ~~~~~~~~~~~~~~~ @@ -492,6 +499,8 @@ Or numbers between two other numbers: The left delimiter can be ``[`` (inclusive) or ``]`` (exclusive). The right delimiter can be ``[`` (exclusive) or ``]`` (inclusive). Beside numbers, you +.. _using-message-domains: + Using Message Domains --------------------- From 43db1a6f42f5a451d20fe80b5b49948b7fc15b87 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 12 May 2013 17:06:42 +0200 Subject: [PATCH 06/13] Moved cookbook article --- components/map.rst.inc | 4 ++-- components/translation/index.rst | 7 +++++++ .../{translation.rst => translation/introduction.rst} | 0 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 components/translation/index.rst rename components/{translation.rst => translation/introduction.rst} (100%) diff --git a/components/map.rst.inc b/components/map.rst.inc index ac68cfef1a2..059f0427ad0 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -84,9 +84,9 @@ * :doc:`/components/templating` -* **Translation** +* :doc:`/components/translation/index` - * :doc:`/components/translation` + * :doc:`/components/translation/introduction` * :doc:`/components/yaml/index` diff --git a/components/translation/index.rst b/components/translation/index.rst new file mode 100644 index 00000000000..05fd1ac5904 --- /dev/null +++ b/components/translation/index.rst @@ -0,0 +1,7 @@ +Translation +=========== + +.. toctree:: + :maxdepth: 2 + + introduction diff --git a/components/translation.rst b/components/translation/introduction.rst similarity index 100% rename from components/translation.rst rename to components/translation/introduction.rst From 09e1a5f73ee662a9e4afc3007b0e8b19a2cfeb06 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 12 May 2013 17:33:02 +0200 Subject: [PATCH 07/13] Moved basic usage to its own article --- components/translation/introduction.rst | 417 +----------------------- components/translation/usage.rst | 394 ++++++++++++++++++++++ 2 files changed, 397 insertions(+), 414 deletions(-) create mode 100644 components/translation/usage.rst diff --git a/components/translation/introduction.rst b/components/translation/introduction.rst index cdbc2a8986f..8e334a2e586 100644 --- a/components/translation/introduction.rst +++ b/components/translation/introduction.rst @@ -16,28 +16,6 @@ You can install the component in many different ways: * Use the official Git repository (https://github.com/symfony/Translation); * :doc:`Install it via Composer` (``symfony/translation`` on `Packagist`_). -Usage ------ - -The :class:`Symfony\\Component\\Translation\\Translator` class is the main -entry point of the Translation component. - -.. code-block:: php - - use Symfony\Component\Translation\Translator; - use Symfony\Component\Translation\MessageSelector; - use Symfony\Component\Translation\Loader\ArrayLoader; - - $translator = new Translator('fr_FR', new MessageSelector()); - $translator->addLoader('array', new ArrayLoader()); - $translator->addResource('array', array( - 'Hello World!' => 'Bonjour', - ), 'fr_FR'); - - echo $translator->trans('Hello World!'); - -.. document the fallback locale - Constructing the Translator --------------------------- @@ -158,398 +136,9 @@ this fallback locale by calling // ... $translator->setFallbackLocale('en_EN'); -Basic Translation ------------------ - -Imagine you want to translate the string *"Symfony2 is great"* into french:: - - use Symfony\Component\Translation\Translator; - use Symfony\Component\Translation\MessageSelector; - use Symfony\Component\Translation\Loader\ArrayLoader; - - $translator = new Translator('fr_FR', new MessageSelector()); - $translator->addLoader('array', new ArrayLoader()); - $translator->addResource('array', array( - 'Symfony2 is great!' => 'J'aime Symfony2!', - ), 'fr_FR'); - - echo $translator->trans('Symfony2 is great!'); - -In this example, the message *"Symfony2 is great!"* will be translated into -the locale set in the constructor (``fr_FR``) if the message exists in one of -the message catalogues. - -Message Placeholders -~~~~~~~~~~~~~~~~~~~~ - -Sometimes, a message containing a variable needs to be translated:: - - // ... - $translated = $translator->trans('Hello '.$name); - - echo $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"*). Instead of writing a translation -for every possible iteration of the ``$name`` variable, you can replace the -variable with a "placeholder":: - - // ... - $translated = $translator->trans( - 'Hello %name%', - array('%name%' => $name) - ); - - echo $translated; - -Symfony2 will now look for a translation of the raw message (``Hello %name%``) -and *then* replace the placeholders with their values. Creating a translation -is done just as before: - -.. configuration-block:: - - .. code-block:: xml - - - - - - - Hello %name% - Bonjour %name% - - - - - - .. code-block:: php - - return array( - 'Hello %name%' => 'Bonjour %name%', - ); - - .. code-block:: yaml - - 'Hello %name%': Bonjour %name% - -.. note:: - - The placeholders can take on any form as the full message is reconstructed - using the PHP :phpfunction:`strtr function`. - -As you've seen, creating a translation is a two-step process: - -#. Abstract the message that needs to be translated by processing it through - the ``Translator``. - -#. Create a translation for the message in each locale that you choose to - support. - -The second step is done by creating message catalogues that define the translations -for any number of different locales. - -Creating Translations ---------------------- - -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. -``symfony2.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. - -.. configuration-block:: - - .. code-block:: xml - - - - - - - Symfony2 is great - J'aime Symfony2 - - - symfony2.great - J'aime Symfony2 - - - - - - .. code-block:: php - - return array( - 'Symfony2 is great' => 'J\'aime Symfony2', - 'symfony2.great' => 'J\'aime Symfony2', - ); - - .. code-block:: yaml - - Symfony2 is great: J'aime Symfony2 - symfony2.great: J'aime Symfony2 - -.. sidebar:: Using Real or Keyword Messages - - This example illustrates the two different philosophies when creating - messages to be translated:: - - $translator->trans('Symfony2 is great'); - - $translator->trans('symfony2.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 ``symfony2.great`` to ``Symfony2 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 "Symfony2 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: - - .. configuration-block:: - - .. code-block:: yaml - - symfony2: - is: - great: Symfony2 is great - amazing: Symfony2 is amazing - has: - bundles: Symfony2 has bundles - user: - login: Login - - .. code-block:: php - - array( - 'symfony2' => array( - 'is' => array( - 'great' => 'Symfony2 is great', - 'amazing' => 'Symfony2 is amazing', - ), - 'has' => array( - 'bundles' => 'Symfony2 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: - - .. configuration-block:: - - .. code-block:: yaml - - symfony2.is.great: Symfony2 is great - symfony2.is.amazing: Symfony2 is amazing - symfony2.has.bundles: Symfony2 has bundles - user.login: Login - - .. code-block:: php - - return array( - 'symfony2.is.great' => 'Symfony2 is great', - 'symfony2.is.amazing' => 'Symfony2 is amazing', - 'symfony2.has.bundles' => 'Symfony2 has bundles', - 'user.login' => 'Login', - ); - -Pluralization -~~~~~~~~~~~~~ - -Message pluralization is a tough topic as the rules can be quite complex. For -instance, here is the mathematic representation of the Russian pluralization -rules:: - - (($number % 10 == 1) && ($number % 100 != 11)) - ? 0 - : ((($number % 10 >= 2) - && ($number % 10 <= 4) - && (($number % 100 < 10) - || ($number % 100 >= 20))) - ? 1 - : 2 - ); - -As you can see, in Russian, you can have three different plural forms, each -given an index of 0, 1 or 2. For each form, the plural is different, and -so the translation is also different. - -When a translation has different forms due to pluralization, you can provide -all the forms as a string separated by a pipe (``|``):: - - 'There is one apple|There are %count% apples' - -To translate pluralized messages, use the -:method:`Symfony\\Component\\Translation\\Translator::transChoice` method:: - - $translator->transChoice( - 'There is one apple|There are %count% apples', - 10, - array('%count%' => 10) - ); - -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. - -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: - -.. code-block:: text - - '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: - -.. code-block:: text - - '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). - -Explicit Interval Pluralization -............................... - -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: - -.. code-block:: text - - '{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: - -.. code-block:: text - - '{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. - -An :class:`Symfony\\Component\\Translation\\Interval` can represent a finite set -of numbers: - -.. code-block:: text - - {1,2,3,4} - -Or numbers between two other numbers: - -.. code-block:: text - - [1, +Inf[ - ]-1,2[ - -The left delimiter can be ``[`` (inclusive) or ``]`` (exclusive). The right -delimiter can be ``[`` (exclusive) or ``]`` (inclusive). Beside numbers, you - -.. _using-message-domains: - -Using Message Domains ---------------------- - -As you've seen, message files are organized into the different locales that -they translate. The message files can also be organized further into "domains". - -The domain is specific in the fourth argument of the ``addResource()`` method. -The default domain is ``messages``. For example, suppose that, for organization, -translations were split into three different domains: ``messages``, ``admin`` -and ``navigation``. The French translation would be loaded like this:: - - // ... - $translator->addLoader('xliff', new XliffLoader()); - - $translator->addResource('xliff', 'messages.fr.xliff', 'fr_FR'); - $translator->addResource('xliff', 'admin.fr.xliff', 'fr_FR', 'admin'); - $translator->addResource('xliff', 'navigation.fr.xliff', 'fr_FR', 'navigation'); - -When translating strings that are not in the default domain (``messages``), -you must specify the domain as the third argument of ``trans()``:: - - $translator->trans('Symfony2 is great', array(), 'admin'); - -Symfony2 will now look for the message in the ``admin`` domain of the -specified locale. -can use ``-Inf`` and ``+Inf`` for the infinite. - -Forcing the Translator Locale ------------------------------ - -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:: - - $translator->trans( - 'Symfony2 is great', - array(), - 'messages', - 'fr_FR' - ); +Usage +----- - $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' - ); +Read how to use the Translation components in ":doc:`/components/translation/usage`" .. _Packagist: https://packagist.org/packages/symfony/translation -.. _`L10n`: http://en.wikipedia.org/wiki/Internationalization_and_localization -.. _`ISO 31-11`: http://en.wikipedia.org/wiki/Interval_(mathematics)#Notations_for_intervals diff --git a/components/translation/usage.rst b/components/translation/usage.rst new file mode 100644 index 00000000000..7571f4eabf2 --- /dev/null +++ b/components/translation/usage.rst @@ -0,0 +1,394 @@ +Usage +----- + +Imagine you want to translate the string *"Symfony2 is great"* into french:: + + use Symfony\Component\Translation\Translator; + use Symfony\Component\Translation\MessageSelector; + use Symfony\Component\Translation\Loader\ArrayLoader; + + $translator = new Translator('fr_FR', new MessageSelector()); + $translator->addLoader('array', new ArrayLoader()); + $translator->addResource('array', array( + 'Symfony2 is great!' => 'J'aime Symfony2!', + ), 'fr_FR'); + + echo $translator->trans('Symfony2 is great!'); + +In this example, the message *"Symfony2 is great!"* will be translated into +the locale set in the constructor (``fr_FR``) if the message exists in one of +the message catalogues. + +Message Placeholders +~~~~~~~~~~~~~~~~~~~~ + +Sometimes, a message containing a variable needs to be translated:: + + // ... + $translated = $translator->trans('Hello '.$name); + + echo $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"*). Instead of writing a translation +for every possible iteration of the ``$name`` variable, you can replace the +variable with a "placeholder":: + + // ... + $translated = $translator->trans( + 'Hello %name%', + array('%name%' => $name) + ); + + echo $translated; + +Symfony2 will now look for a translation of the raw message (``Hello %name%``) +and *then* replace the placeholders with their values. Creating a translation +is done just as before: + +.. configuration-block:: + + .. code-block:: xml + + + + + + + Hello %name% + Bonjour %name% + + + + + + .. code-block:: php + + return array( + 'Hello %name%' => 'Bonjour %name%', + ); + + .. code-block:: yaml + + 'Hello %name%': Bonjour %name% + +.. note:: + + The placeholders can take on any form as the full message is reconstructed + using the PHP :phpfunction:`strtr function`. + +As you've seen, creating a translation is a two-step process: + +#. Abstract the message that needs to be translated by processing it through + the ``Translator``. + +#. Create a translation for the message in each locale that you choose to + support. + +The second step is done by creating message catalogues that define the translations +for any number of different locales. + +Creating Translations +--------------------- + +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. +``symfony2.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. + +.. configuration-block:: + + .. code-block:: xml + + + + + + + Symfony2 is great + J'aime Symfony2 + + + symfony2.great + J'aime Symfony2 + + + + + + .. code-block:: php + + return array( + 'Symfony2 is great' => 'J\'aime Symfony2', + 'symfony2.great' => 'J\'aime Symfony2', + ); + + .. code-block:: yaml + + Symfony2 is great: J'aime Symfony2 + symfony2.great: J'aime Symfony2 + +.. sidebar:: Using Real or Keyword Messages + + This example illustrates the two different philosophies when creating + messages to be translated:: + + $translator->trans('Symfony2 is great'); + + $translator->trans('symfony2.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 ``symfony2.great`` to ``Symfony2 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 "Symfony2 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: + + .. configuration-block:: + + .. code-block:: yaml + + symfony2: + is: + great: Symfony2 is great + amazing: Symfony2 is amazing + has: + bundles: Symfony2 has bundles + user: + login: Login + + .. code-block:: php + + array( + 'symfony2' => array( + 'is' => array( + 'great' => 'Symfony2 is great', + 'amazing' => 'Symfony2 is amazing', + ), + 'has' => array( + 'bundles' => 'Symfony2 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: + + .. configuration-block:: + + .. code-block:: yaml + + symfony2.is.great: Symfony2 is great + symfony2.is.amazing: Symfony2 is amazing + symfony2.has.bundles: Symfony2 has bundles + user.login: Login + + .. code-block:: php + + return array( + 'symfony2.is.great' => 'Symfony2 is great', + 'symfony2.is.amazing' => 'Symfony2 is amazing', + 'symfony2.has.bundles' => 'Symfony2 has bundles', + 'user.login' => 'Login', + ); + +Pluralization +~~~~~~~~~~~~~ + +Message pluralization is a tough topic as the rules can be quite complex. For +instance, here is the mathematic representation of the Russian pluralization +rules:: + + (($number % 10 == 1) && ($number % 100 != 11)) + ? 0 + : ((($number % 10 >= 2) + && ($number % 10 <= 4) + && (($number % 100 < 10) + || ($number % 100 >= 20))) + ? 1 + : 2 + ); + +As you can see, in Russian, you can have three different plural forms, each +given an index of 0, 1 or 2. For each form, the plural is different, and +so the translation is also different. + +When a translation has different forms due to pluralization, you can provide +all the forms as a string separated by a pipe (``|``):: + + 'There is one apple|There are %count% apples' + +To translate pluralized messages, use the +:method:`Symfony\\Component\\Translation\\Translator::transChoice` method:: + + $translator->transChoice( + 'There is one apple|There are %count% apples', + 10, + array('%count%' => 10) + ); + +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. + +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: + +.. code-block:: text + + '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: + +.. code-block:: text + + '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). + +Explicit Interval Pluralization +............................... + +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: + +.. code-block:: text + + '{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: + +.. code-block:: text + + '{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. + +An :class:`Symfony\\Component\\Translation\\Interval` can represent a finite set +of numbers: + +.. code-block:: text + + {1,2,3,4} + +Or numbers between two other numbers: + +.. code-block:: text + + [1, +Inf[ + ]-1,2[ + +The left delimiter can be ``[`` (inclusive) or ``]`` (exclusive). The right +delimiter can be ``[`` (exclusive) or ``]`` (inclusive). Beside numbers, you + +.. _using-message-domains: + +Using Message Domains +--------------------- + +As you've seen, message files are organized into the different locales that +they translate. The message files can also be organized further into "domains". + +The domain is specific in the fourth argument of the ``addResource()`` method. +The default domain is ``messages``. For example, suppose that, for organization, +translations were split into three different domains: ``messages``, ``admin`` +and ``navigation``. The French translation would be loaded like this:: + + // ... + $translator->addLoader('xliff', new XliffLoader()); + + $translator->addResource('xliff', 'messages.fr.xliff', 'fr_FR'); + $translator->addResource('xliff', 'admin.fr.xliff', 'fr_FR', 'admin'); + $translator->addResource('xliff', 'navigation.fr.xliff', 'fr_FR', 'navigation'); + +When translating strings that are not in the default domain (``messages``), +you must specify the domain as the third argument of ``trans()``:: + + $translator->trans('Symfony2 is great', array(), 'admin'); + +Symfony2 will now look for the message in the ``admin`` domain of the +specified locale. +can use ``-Inf`` and ``+Inf`` for the infinite. + +Forcing the Translator Locale +----------------------------- + +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:: + + $translator->trans( + 'Symfony2 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('%count%' => 10), + 'messages', + 'fr_FR' + ); + +.. _`L10n`: http://en.wikipedia.org/wiki/Internationalization_and_localization +.. _`ISO 31-11`: http://en.wikipedia.org/wiki/Interval_(mathematics)#Notations_for_intervals From 394221135721ab4d42ddfff3341577775cc260d5 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 12 May 2013 17:36:25 +0200 Subject: [PATCH 08/13] updated new article --- components/map.rst.inc | 1 + components/translation/index.rst | 1 + components/translation/usage.rst | 14 +++++++------- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/components/map.rst.inc b/components/map.rst.inc index 059f0427ad0..1375612b63f 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -87,6 +87,7 @@ * :doc:`/components/translation/index` * :doc:`/components/translation/introduction` + * :doc:`/components/translation/usage` * :doc:`/components/yaml/index` diff --git a/components/translation/index.rst b/components/translation/index.rst index 05fd1ac5904..3f87cbc1425 100644 --- a/components/translation/index.rst +++ b/components/translation/index.rst @@ -5,3 +5,4 @@ Translation :maxdepth: 2 introduction + usage diff --git a/components/translation/usage.rst b/components/translation/usage.rst index 7571f4eabf2..16778e1026b 100644 --- a/components/translation/usage.rst +++ b/components/translation/usage.rst @@ -1,5 +1,5 @@ Usage ------ +===== Imagine you want to translate the string *"Symfony2 is great"* into french:: @@ -20,7 +20,7 @@ the locale set in the constructor (``fr_FR``) if the message exists in one of the message catalogues. Message Placeholders -~~~~~~~~~~~~~~~~~~~~ +-------------------- Sometimes, a message containing a variable needs to be translated:: @@ -90,7 +90,7 @@ The second step is done by creating message catalogues that define the translati for any number of different locales. Creating Translations ---------------------- +===================== The act of creating translation files is an important part of "localization" (often abbreviated `L10n`_). Translation files consist of a series of @@ -216,7 +216,7 @@ recommended format. These files are parsed by one of the loader classes. ); Pluralization -~~~~~~~~~~~~~ +------------- Message pluralization is a tough topic as the rules can be quite complex. For instance, here is the mathematic representation of the Russian pluralization @@ -294,7 +294,7 @@ same in the original message as in the translated one. only get a string based on its position in the string). Explicit Interval Pluralization -............................... +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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 @@ -342,7 +342,7 @@ delimiter can be ``[`` (exclusive) or ``]`` (inclusive). Beside numbers, you .. _using-message-domains: Using Message Domains ---------------------- +===================== As you've seen, message files are organized into the different locales that they translate. The message files can also be organized further into "domains". @@ -369,7 +369,7 @@ specified locale. can use ``-Inf`` and ``+Inf`` for the infinite. Forcing the Translator Locale ------------------------------ +============================= When translating a message, the Translator uses the specified locale or the ``fallback`` locale if necessary. You can also manually specify the locale to From a6ca1718bec2887412ba174203eb981cbbdfb642 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 12 May 2013 17:37:15 +0200 Subject: [PATCH 09/13] Added index directive --- components/translation/usage.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/translation/usage.rst b/components/translation/usage.rst index 16778e1026b..cfd7bd161e1 100644 --- a/components/translation/usage.rst +++ b/components/translation/usage.rst @@ -1,3 +1,6 @@ +.. index:: + single: Translation; Usage + Usage ===== From 59560d73205cb9630d8d651a6a99b8cb8e264bc2 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 19 May 2013 17:59:06 +0200 Subject: [PATCH 10/13] Reread component docs --- components/translation/introduction.rst | 42 ++++++++++++++----------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/components/translation/introduction.rst b/components/translation/introduction.rst index 8e334a2e586..73907ccb04c 100644 --- a/components/translation/introduction.rst +++ b/components/translation/introduction.rst @@ -19,15 +19,17 @@ You can install the component in many different ways: Constructing the Translator --------------------------- -Before you can use the Translator, you need to configure it and load the -message catalogues. +The main access point of the Translation Component is +:class:`Symfony\\Component\\Translation\\Translator`. Before you can use it, +you need to configure it and load the messages to translate (called *message +catalogues*). Configuration ~~~~~~~~~~~~~ -The constructor of the ``Translator`` class needs to arguments: The locale and -a :class:`Symfony\\Component\\Translation\\MessageSelector` to use when using -pluralization (more about that later):: +The constructor of the ``Translator`` class needs two arguments: The locale +and the :class:`Symfony\\Component\\Translation\\MessageSelector` to use when +using pluralization (more about that later):: use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\MessageSelector; @@ -56,9 +58,9 @@ Loader too. The default loaders are: * :class:`Symfony\\Component\\Translation\\Loader\\ArrayLoader` - to load catalogues from PHP arrays. * :class:`Symfony\\Component\\Translation\\Loader\\CsvFileLoader` - to load - catalogues from Csv files. + catalogues from CSV files. * :class:`Symfony\\Component\\Translation\\Loader\\PhpFileLoader` - to load - catalogues from Php files. + catalogues from PHP files. * :class:`Symfony\\Component\\Translation\\Loader\\XliffFileLoader` - to load catalogues from Xliff files. * :class:`Symfony\\Component\\Translation\\Loader\\YamlFileLoader` - to load @@ -67,21 +69,21 @@ Loader too. The default loaders are: All loaders, except the ``ArrayLoader``, requires the :doc:`Config component`. -At first, you should add a loader to the ``Translator``:: +At first, you should add one or more loaders to the ``Translator``:: // ... $translator->addLoader('array', new ArrayLoader()); -The first argument is the key to which we can refer the loader in the translator -and the second argument is an instance of the loader itself. After this, you -can add your resources using the correct loader. +The first argument is the name to which you can refer the loader in the +translator and the second argument is an instance of the loader itself. After +this, you can add your resources using the correct loader. Loading Messages with the ``ArrayLoader`` ......................................... Loading messages can be done by calling :method:`Symfony\\Component\\Translation\\Translator::addResource`. The first -argument is the loader name (the first argument of the ``addLoader`` +argument is the loader name (this was the first argument of the ``addLoader`` method), the second is the resource and the third argument is the locale:: // ... @@ -106,24 +108,26 @@ The Translation Process To actually translate the message, the Translator uses a simple process: * A catalog 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; + for the ``locale`` (e.g. ``fr_FR``). Messages from the + :ref:`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. You start this process by calling -:method:`Symfony\\Component\\Translation\\Translator::trans`. Then, the +:method:`Symfony\\Component\\Translation\\Translator::trans` or +:method:`Symfony\\Component\\Translation\\Translator::transChoice`. Then, the Translator looks for the exact string inside the appropriate message catalog and returns it (if it exists). .. tip:: When a translation does not exist for a locale, the translator first tries - to find the translation for the language (``fr`` if the locale is - ``fr_FR`` for instance). If this also fails, it looks for a translation - using the fallback locale. + to find the translation for the language (e.g. ``fr`` if the locale is + ``fr_FR``). If this also fails, it looks for a translation using the + fallback locale. Fallback Locale ~~~~~~~~~~~~~~~ From b102e9b99927fc7de693034bae26932635cbede4 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 19 May 2013 18:05:30 +0200 Subject: [PATCH 11/13] Reread usage docs --- components/translation/introduction.rst | 31 ++++++++++++++++++++- components/translation/usage.rst | 36 +++---------------------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/components/translation/introduction.rst b/components/translation/introduction.rst index 73907ccb04c..c498c7fc5eb 100644 --- a/components/translation/introduction.rst +++ b/components/translation/introduction.rst @@ -140,9 +140,38 @@ this fallback locale by calling // ... $translator->setFallbackLocale('en_EN'); +.. _using-message-domains: + +Using Message Domains +--------------------- + +As you've seen, message files are organized into the different locales that +they translate. The message files can also be organized further into "domains". + +The domain is specific in the fourth argument of the ``addResource()`` method. +The default domain is ``messages``. For example, suppose that, for organization, +translations were split into three different domains: ``messages``, ``admin`` +and ``navigation``. The French translation would be loaded like this:: + + // ... + $translator->addLoader('xliff', new XliffLoader()); + + $translator->addResource('xliff', 'messages.fr.xliff', 'fr_FR'); + $translator->addResource('xliff', 'admin.fr.xliff', 'fr_FR', 'admin'); + $translator->addResource('xliff', 'navigation.fr.xliff', 'fr_FR', 'navigation'); + +When translating strings that are not in the default domain (``messages``), +you must specify the domain as the third argument of ``trans()``:: + + $translator->trans('Symfony2 is great', array(), 'admin'); + +Symfony2 will now look for the message in the ``admin`` domain of the +specified locale. +can use ``-Inf`` and ``+Inf`` for the infinite. + Usage ----- -Read how to use the Translation components in ":doc:`/components/translation/usage`" +Read how to use the Translation components in ":doc:`/components/translation/usage`". .. _Packagist: https://packagist.org/packages/symfony/translation diff --git a/components/translation/usage.rst b/components/translation/usage.rst index cfd7bd161e1..287ea09add2 100644 --- a/components/translation/usage.rst +++ b/components/translation/usage.rst @@ -4,7 +4,7 @@ Usage ===== -Imagine you want to translate the string *"Symfony2 is great"* into french:: +Imagine you want to translate the string *"Symfony2 is great"* into French:: use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\MessageSelector; @@ -79,7 +79,8 @@ is done just as before: .. note:: The placeholders can take on any form as the full message is reconstructed - using the PHP :phpfunction:`strtr function`. + using the PHP :phpfunction:`strtr function`. But the ``%...%`` form + is recommend, to avoid problems when using Twig. As you've seen, creating a translation is a two-step process: @@ -342,37 +343,8 @@ Or numbers between two other numbers: The left delimiter can be ``[`` (inclusive) or ``]`` (exclusive). The right delimiter can be ``[`` (exclusive) or ``]`` (inclusive). Beside numbers, you -.. _using-message-domains: - -Using Message Domains -===================== - -As you've seen, message files are organized into the different locales that -they translate. The message files can also be organized further into "domains". - -The domain is specific in the fourth argument of the ``addResource()`` method. -The default domain is ``messages``. For example, suppose that, for organization, -translations were split into three different domains: ``messages``, ``admin`` -and ``navigation``. The French translation would be loaded like this:: - - // ... - $translator->addLoader('xliff', new XliffLoader()); - - $translator->addResource('xliff', 'messages.fr.xliff', 'fr_FR'); - $translator->addResource('xliff', 'admin.fr.xliff', 'fr_FR', 'admin'); - $translator->addResource('xliff', 'navigation.fr.xliff', 'fr_FR', 'navigation'); - -When translating strings that are not in the default domain (``messages``), -you must specify the domain as the third argument of ``trans()``:: - - $translator->trans('Symfony2 is great', array(), 'admin'); - -Symfony2 will now look for the message in the ``admin`` domain of the -specified locale. -can use ``-Inf`` and ``+Inf`` for the infinite. - Forcing the Translator Locale -============================= +----------------------------- When translating a message, the Translator uses the specified locale or the ``fallback`` locale if necessary. You can also manually specify the locale to From 81c124c8cb32711e57725f6cdf6248c7bc6963de Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 19 May 2013 18:12:47 +0200 Subject: [PATCH 12/13] Reread book article --- book/translation.rst | 31 ++++++++++++++----------- components/translation/introduction.rst | 10 ++++++++ components/translation/usage.rst | 4 ++-- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/book/translation.rst b/book/translation.rst index b1fe553de3b..2f4ff9afe60 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -4,12 +4,12 @@ Translations ============ -The term "internationalization" (often abbreviated `i18n`_) refers to the process -of abstracting strings and other locale-specific pieces out of your application -and 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:: +The term "internationalization" (often abbreviated `i18n`_) refers to the +process of abstracting strings and other locale-specific pieces out of your +application and 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:: // text will *always* print out in English echo 'Hello World'; @@ -21,10 +21,10 @@ the user:: .. 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 - `ISO639-1`_ *language* code, an underscore (``_``), then the `ISO3166 Alpha-2`_ *country* - code (e.g. ``fr_FR`` for French/France) is recommended. + can be any string that your application uses to manage translations and + other format differences (e.g. currency format). The `ISO639-1`_ + *language* code, an underscore (``_``), then the `ISO3166 Alpha-2`_ + *country* code (e.g. ``fr_FR`` for French/France) is recommended. In this chapter, you'll learn how to use the Translation component in the Symfony2 framework. Read the @@ -33,7 +33,8 @@ the Translator. Overall, the process has several common steps: #. Enable and configure Symfony's Translation component; -#. Abstract strings (i.e. "messages") by wrapping them in calls to the ``Translator``; +#. Abstract strings (i.e. "messages") by wrapping them in calls to the + ``Translator`` (learn about this in ":doc:`/components/translation/usage`"); #. Create translation resources for each supported locale that translate each message in the application; @@ -110,13 +111,14 @@ Using the Translation inside Controllers ---------------------------------------- When you want to use translation inside controllers, you need to get the -``translator`` service and use ``trans`` or ``transChoice``:: +``translator`` service and use +:method:`Symfony\\Component\\Translation\\Translator::trans` or +:method:`Symfony\\Component\\Translation\\Translator::transChoice`:: // src/Acme/DemoBundle/Controller/DemoController.php namespace Amce\DemoBundle\Controller; // ... - class DemoController extends Controller { public function indexAction() @@ -473,7 +475,8 @@ steps: * Abstract messages in your application by wrapping each in either the :method:`Symfony\\Component\\Translation\\Translator::trans` or - :method:`Symfony\\Component\\Translation\\Translator::transChoice` methods; + :method:`Symfony\\Component\\Translation\\Translator::transChoice` methods + (learn about this in ":doc:`/components/translation/usage`"); * Translate each message into multiple locales by creating translation message files. Symfony2 discovers and processes each file because its name follows diff --git a/components/translation/introduction.rst b/components/translation/introduction.rst index c498c7fc5eb..b0101376f7a 100644 --- a/components/translation/introduction.rst +++ b/components/translation/introduction.rst @@ -41,6 +41,14 @@ using pluralization (more about that later):: The locale set here is the default locale to use. You can override this locale when translating strings. +.. 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 `ISO639-1`_ + *language* code, an underscore (``_``), then the `ISO3166 Alpha-2`_ + *country* code (e.g. ``fr_FR`` for French/France) is recommended. + Loading Message Catalogues ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -175,3 +183,5 @@ Usage Read how to use the Translation components in ":doc:`/components/translation/usage`". .. _Packagist: https://packagist.org/packages/symfony/translation +.. _`ISO3166 Alpha-2`: http://en.wikipedia.org/wiki/ISO_3166-1#Current_codes +.. _`ISO639-1`: http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes diff --git a/components/translation/usage.rst b/components/translation/usage.rst index 287ea09add2..5ac78b2c1ce 100644 --- a/components/translation/usage.rst +++ b/components/translation/usage.rst @@ -1,8 +1,8 @@ .. index:: single: Translation; Usage -Usage -===== +Using the Translator +==================== Imagine you want to translate the string *"Symfony2 is great"* into French:: From 12af0e880b5df0bb15b7ac4e5ccf16652b8beea5 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 6 Jun 2013 19:17:44 +0200 Subject: [PATCH 13/13] Fixed things mentioned by the great @xabbuh --- book/translation.rst | 8 ++++---- components/translation/introduction.rst | 14 +++++++------- components/translation/usage.rst | 1 + 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/book/translation.rst b/book/translation.rst index 2f4ff9afe60..aea1fc6e3d3 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -78,9 +78,9 @@ The locale used in translations is the one stored in the user session. Fallback and Default Locale ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If the locale hasn't been set explicitly in the session, the ``fallback_locale`` -configuration parameter will be used by the ``Translator``. The parameter -defaults to ``en`` (see `Configuration`_). +If the locale hasn't been set, the ``fallback`` configuration parameter will +be used by the ``Translator``. The parameter defaults to ``en`` (see +`Configuration`_). Alternatively, you can guarantee that a locale is set on the user's session by defining a ``default_locale`` for the session service: @@ -276,7 +276,7 @@ works when you use a placeholder following the ``%var%`` pattern. .. caution:: The ``%var%`` notation of placeholders is required when translating in - Twig templates. + Twig templates using the tag. .. tip:: diff --git a/components/translation/introduction.rst b/components/translation/introduction.rst index b0101376f7a..f4469cd475b 100644 --- a/components/translation/introduction.rst +++ b/components/translation/introduction.rst @@ -11,7 +11,7 @@ The Translation Component Installation ------------ -You can install the component in many different ways: +You can install the component in 2 different ways: * Use the official Git repository (https://github.com/symfony/Translation); * :doc:`Install it via Composer` (``symfony/translation`` on `Packagist`_). @@ -156,10 +156,11 @@ Using Message Domains As you've seen, message files are organized into the different locales that they translate. The message files can also be organized further into "domains". -The domain is specific in the fourth argument of the ``addResource()`` method. -The default domain is ``messages``. For example, suppose that, for organization, -translations were split into three different domains: ``messages``, ``admin`` -and ``navigation``. The French translation would be loaded like this:: +The domain is specified in the fourth argument of the ``addResource()`` +method. The default domain is ``messages``. For example, suppose that, for +organization, translations were split into three different domains: +``messages``, ``admin`` and ``navigation``. The French translation would be +loaded like this:: // ... $translator->addLoader('xliff', new XliffLoader()); @@ -175,12 +176,11 @@ you must specify the domain as the third argument of ``trans()``:: Symfony2 will now look for the message in the ``admin`` domain of the specified locale. -can use ``-Inf`` and ``+Inf`` for the infinite. Usage ----- -Read how to use the Translation components in ":doc:`/components/translation/usage`". +Read how to use the Translation component in ":doc:`/components/translation/usage`". .. _Packagist: https://packagist.org/packages/symfony/translation .. _`ISO3166 Alpha-2`: http://en.wikipedia.org/wiki/ISO_3166-1#Current_codes diff --git a/components/translation/usage.rst b/components/translation/usage.rst index 5ac78b2c1ce..f5ff77a2c98 100644 --- a/components/translation/usage.rst +++ b/components/translation/usage.rst @@ -342,6 +342,7 @@ Or numbers between two other numbers: The left delimiter can be ``[`` (inclusive) or ``]`` (exclusive). The right delimiter can be ``[`` (exclusive) or ``]`` (inclusive). Beside numbers, you +can use ``-Inf`` and ``+Inf`` for the infinite. Forcing the Translator Locale -----------------------------