From 2683376fd9962b211e0ac134acb70dd380de4980 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Fri, 27 Dec 2019 16:30:40 +0100 Subject: [PATCH 1/3] Removed Translation component documentation --- _build/redirection_map | 2 + components/form.rst | 2 +- components/translation.rst | 231 -------- components/translation/custom_formats.rst | 125 ----- .../translation/custom_message_formatter.rst | 38 -- components/translation/usage.rst | 500 ------------------ introduction/from_flat_php_to_symfony.rst | 3 +- reference/configuration/framework.rst | 4 - reference/dic_tags.rst | 10 - translation.rst | 386 ++++++++++++-- translation/xliff.rst | 44 ++ 11 files changed, 391 insertions(+), 954 deletions(-) delete mode 100644 components/translation.rst delete mode 100644 components/translation/custom_formats.rst delete mode 100644 components/translation/custom_message_formatter.rst delete mode 100644 components/translation/usage.rst create mode 100644 translation/xliff.rst diff --git a/_build/redirection_map b/_build/redirection_map index 681f00f5b99..21a74195a20 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -369,3 +369,5 @@ /contributing/community/other /contributing/community /setup/composer /setup /components/debug https://github.com/symfony/debug +/components/translation https://github.com/symfony/translation +/components/translation/usage /translation diff --git a/components/form.rst b/components/form.rst index bdc7ba20d21..84b4664d671 100644 --- a/components/form.rst +++ b/components/form.rst @@ -54,7 +54,7 @@ support for very important features: The Symfony Form component relies on other libraries to solve these problems. Most of the time you will use Twig and the Symfony :doc:`HttpFoundation `, -:doc:`Translation ` and :doc:`Validator ` +:doc:`Translation ` and :doc:`Validator ` components, but you can replace any of these with a different library of your choice. The following sections explain how to plug these libraries into the form diff --git a/components/translation.rst b/components/translation.rst deleted file mode 100644 index a578d04c34c..00000000000 --- a/components/translation.rst +++ /dev/null @@ -1,231 +0,0 @@ -.. index:: - single: Translation - single: Components; Translation - -The Translation Component -========================= - - The Translation component provides tools to internationalize your - application. - -Installation ------------- - -.. code-block:: terminal - - $ composer require symfony/translation:^3.4 - -.. include:: /components/require_autoload.rst.inc - -.. seealso:: - - This article explains how to use the Translation features as an independent - component in any PHP application. Read the :doc:`/translation` article to - learn about how to internationalize and manage the user locale in Symfony - applications. - -Constructing the Translator ---------------------------- - -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 -catalogs*). - -Configuration -~~~~~~~~~~~~~ - -The constructor of the ``Translator`` class needs one argument: The locale:: - - use Symfony\Component\Translation\Translator; - - $translator = new Translator('fr_FR'); - -.. note:: - - 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 `ISO 639-1`_ - *language* code, an underscore (``_``), then the `ISO 3166-1 alpha-2`_ - *country* code (e.g. ``fr_FR`` for French/France) is recommended. - -.. _component-translator-message-catalogs: - -Loading Message Catalogs -~~~~~~~~~~~~~~~~~~~~~~~~ - -The messages are stored in message catalogs inside the ``Translator`` -class. A message catalog is like a dictionary of translations for a specific -locale. - -The Translation component uses Loader classes to load catalogs. You can load -multiple resources for the same locale, which will then be combined into one -catalog. - -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 - catalogs from PHP arrays. -* :class:`Symfony\\Component\\Translation\\Loader\\CsvFileLoader` - to load - catalogs from CSV files. -* :class:`Symfony\\Component\\Translation\\Loader\\IcuDatFileLoader` - to load - catalogs from resource bundles. -* :class:`Symfony\\Component\\Translation\\Loader\\IcuResFileLoader` - to load - catalogs from resource bundles. -* :class:`Symfony\\Component\\Translation\\Loader\\IniFileLoader` - to load - catalogs from ini files. -* :class:`Symfony\\Component\\Translation\\Loader\\MoFileLoader` - to load - catalogs from gettext files. -* :class:`Symfony\\Component\\Translation\\Loader\\PhpFileLoader` - to load - catalogs from PHP files. -* :class:`Symfony\\Component\\Translation\\Loader\\PoFileLoader` - to load - catalogs from gettext files. -* :class:`Symfony\\Component\\Translation\\Loader\\QtFileLoader` - to load - catalogs from QT XML files. -* :class:`Symfony\\Component\\Translation\\Loader\\XliffFileLoader` - to load - catalogs from Xliff files. -* :class:`Symfony\\Component\\Translation\\Loader\\JsonFileLoader` - to load - catalogs from JSON files. -* :class:`Symfony\\Component\\Translation\\Loader\\YamlFileLoader` - to load - catalogs from Yaml files (requires the :doc:`Yaml component`). - -All file loaders require the :doc:`Config component `. - -You can also :doc:`create your own Loader `, -in case the format is not already supported by one of the default loaders. - -At first, you should add one or more loaders to the ``Translator``:: - - // ... - $translator->addLoader('array', new ArrayLoader()); - -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 (this was the first argument of the ``addLoader()`` -method), the second is the resource and the third argument is the locale:: - - // ... - $translator->addResource('array', [ - 'Hello World!' => 'Bonjour', - ], 'fr_FR'); - -Loading Messages with the File Loaders -...................................... - -If you use one of the file loaders, you should also use the ``addResource()`` -method. The only difference is that you should put the file name to the resource -file as the second argument, instead of an array:: - - // ... - $translator->addLoader('yaml', new YamlFileLoader()); - $translator->addResource('yaml', 'path/to/messages.fr.yml', 'fr_FR'); - -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 - :ref:`components-fallback-locales` 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` 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). - -.. _components-fallback-locales: - -Fallback Locales -~~~~~~~~~~~~~~~~ - -If the message is not located in the catalog of the specific locale, the -translator will look into the catalog of one or more fallback locales. For -example, assume you're trying to translate into the ``fr_FR`` locale: - -#. First, the translator looks for the translation in the ``fr_FR`` locale; - -#. If it wasn't found, the translator looks for the translation in the ``fr`` - locale; - -#. If the translation still isn't found, the translator uses the one or more - fallback locales set explicitly on the translator. - -For (3), the fallback locales can be set by calling -:method:`Symfony\\Component\\Translation\\Translator::setFallbackLocales`:: - - // ... - $translator->setFallbackLocales(['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 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('xlf', new XliffFileLoader()); - - $translator->addResource('xlf', 'messages.fr.xlf', 'fr_FR'); - $translator->addResource('xlf', 'admin.fr.xlf', 'fr_FR', 'admin'); - $translator->addResource( - 'xlf', - 'navigation.fr.xlf', - '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('Symfony is great', [], 'admin'); - -Symfony will now look for the message in the ``admin`` domain of the -specified locale. - -Usage ------ - -Read how to use the Translation component in :doc:`/components/translation/usage`. - -Learn More ----------- - -.. toctree:: - :maxdepth: 1 - :glob: - - /components/translation/* - /translation - /translation/* - /validation/translations - -.. _`ISO 3166-1 alpha-2`: https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes -.. _`ISO 639-1`: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes diff --git a/components/translation/custom_formats.rst b/components/translation/custom_formats.rst deleted file mode 100644 index 5ebd3fe8599..00000000000 --- a/components/translation/custom_formats.rst +++ /dev/null @@ -1,125 +0,0 @@ -.. index:: - single: Translation; Adding Custom Format Support - -Adding Custom Format Support -============================ - -Sometimes, you need to deal with custom formats for translation files. The -Translation component is flexible enough to support this. Just create a -loader (to load translations) and, optionally, a dumper (to dump translations). - -Imagine that you have a custom format where translation messages are defined -using one line for each translation and parentheses to wrap the key and the -message. A translation file would look like this: - -.. code-block:: text - - (welcome)(accueil) - (goodbye)(au revoir) - (hello)(bonjour) - -.. _components-translation-custom-loader: - -Creating a Custom Loader ------------------------- - -To define a custom loader that is able to read these kinds of files, you must create a -new class that implements the -:class:`Symfony\\Component\\Translation\\Loader\\LoaderInterface`. The -:method:`Symfony\\Component\\Translation\\Loader\\LoaderInterface::load` -method will get a filename and parse it into an array. Then, it will -create the catalog that will be returned:: - - use Symfony\Component\Translation\Loader\LoaderInterface; - use Symfony\Component\Translation\MessageCatalogue; - - class MyFormatLoader implements LoaderInterface - { - public function load($resource, $locale, $domain = 'messages') - { - $messages = []; - $lines = file($resource); - - foreach ($lines as $line) { - if (preg_match('/\(([^\)]+)\)\(([^\)]+)\)/', $line, $matches)) { - $messages[$matches[1]] = $matches[2]; - } - } - - $messageCatalog = new MessageCatalogue($locale); - $messageCatalog->add($messages, $domain); - - return $messageCatalog; - } - - } - -Once created, it can be used as any other loader:: - - use Symfony\Component\Translation\Translator; - - $translator = new Translator('fr_FR'); - $translator->addLoader('my_format', new MyFormatLoader()); - - $translator->addResource('my_format', __DIR__.'/translations/messages.txt', 'fr_FR'); - - var_dump($translator->trans('welcome')); - -It will print *"accueil"*. - -.. _components-translation-custom-dumper: - -Creating a Custom Dumper ------------------------- - -It is also possible to create a custom dumper for your format, which is -useful when using the extraction commands. To do so, a new class -implementing the -:class:`Symfony\\Component\\Translation\\Dumper\\DumperInterface` -must be created. To write the dump contents into a file, extending the -:class:`Symfony\\Component\\Translation\\Dumper\\FileDumper` class -will save a few lines:: - - use Symfony\Component\Translation\Dumper\FileDumper; - use Symfony\Component\Translation\MessageCatalogue; - - class MyFormatDumper extends FileDumper - { - public function formatCatalogue(MessageCatalogue $messages, $domain, array $options = []) - { - $output = ''; - - foreach ($messages->all($domain) as $source => $target) { - $output .= sprintf("(%s)(%s)\n", $source, $target); - } - - return $output; - } - - protected function getExtension() - { - return 'txt'; - } - } - -.. sidebar:: Format a message catalog - - In some cases, you want to send the dump contents as a response instead of - writing them in files. To do this, you can use the ``formatCatalogue`` - method. In this case, you must pass the domain argument, which determines - the list of messages that should be dumped. - -The :method:`Symfony\\Component\\Translation\\Dumper\\FileDumper::formatCatalogue` -method creates the output string, that will be used by the -:method:`Symfony\\Component\\Translation\\Dumper\\FileDumper::dump` method -of the FileDumper class to create the file. The dumper can be used like any other -built-in dumper. In the following example, the translation messages defined in the -YAML file are dumped into a text file with the custom format:: - - use Symfony\Component\Translation\Loader\YamlFileLoader; - - $loader = new YamlFileLoader(); - $translations = $loader->load(__DIR__ . '/translations/messages.fr_FR.yml' , 'fr_FR'); - - $dumper = new MyFormatDumper(); - $dumper->dump($translations, ['path' => __DIR__.'/dumps']); diff --git a/components/translation/custom_message_formatter.rst b/components/translation/custom_message_formatter.rst deleted file mode 100644 index 81e40a4c544..00000000000 --- a/components/translation/custom_message_formatter.rst +++ /dev/null @@ -1,38 +0,0 @@ -.. index:: - single: Translation; Create Custom Message formatter - -Create a Custom Message Formatter -================================= - -The default message formatter provided by Symfony solves the most common needs -when translating messages, such as using variables and pluralization. However, -if your needs are different, you can create your own message formatter. - -Message formatters are PHP classes that implement the -:class:`Symfony\\Component\\Translation\\Formatter\\MessageFormatterInterface`:: - - - use Symfony\Component\Translation\Formatter\MessageFormatterInterface; - - class MyCustomMessageFormatter implements MessageFormatterInterface - { - public function format($message, $locale, array $parameters = []) - { - // ... format the message according to your needs - - return $message; - } - } - -Now, pass an instance of this formatter as the second argument of the translator -to use it when translating messages:: - - use Symfony\Component\Translation\Translator; - - $translator = new Translator('fr_FR', new IntlMessageFormatter()); - $message = $translator->trans($originalMessage, $translationParameters); - -If you want to use this formatter to translate all messages in your Symfony -application, define a service for the formatter and use the -:ref:`translator.formatter ` option -to set that service as the default formatter. diff --git a/components/translation/usage.rst b/components/translation/usage.rst deleted file mode 100644 index 65f3442c75d..00000000000 --- a/components/translation/usage.rst +++ /dev/null @@ -1,500 +0,0 @@ -.. index:: - single: Translation; Usage - -Using the Translator -==================== - -Imagine you want to translate the string *"Symfony is great"* into French:: - - use Symfony\Component\Translation\Loader\ArrayLoader; - use Symfony\Component\Translation\Translator; - - $translator = new Translator('fr_FR'); - $translator->addLoader('array', new ArrayLoader()); - $translator->addResource('array', [ - 'Symfony is great!' => 'Symfony est super !', - ], 'fr_FR'); - - var_dump($translator->trans('Symfony is great!')); - -In this example, the message *"Symfony is great!"* will be translated into -the locale set in the constructor (``fr_FR``) if the message exists in one of -the message catalogs. - -.. _component-translation-placeholders: - -Message Placeholders --------------------- - -Sometimes, a message containing a variable needs to be translated:: - - // ... - $translated = $translator->trans('Hello '.$name); - - var_dump($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%', - ['%name%' => $name] - ); - - var_dump($translated); - -Symfony 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:: yaml - - 'Hello %name%': Bonjour %name% - - .. code-block:: php - - return [ - 'Hello %name%' => 'Bonjour %name%', - ]; - -.. note:: - - The placeholders can take on any form as the full message is reconstructed - using the PHP :phpfunction:`strtr function`. But the ``%...%`` form - is recommended, to avoid problems when using Twig. - -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 catalogs 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. -``symfony.great`` - see the sidebar below). - -Translation files can be created in several formats, XLIFF being the -recommended format. These files are parsed by one of the loader classes. - -.. configuration-block:: - - .. code-block:: xml - - - - - - - Symfony is great - J'aime Symfony - - - symfony.great - J'aime Symfony - - - - - - .. code-block:: yaml - - Symfony is great: J'aime Symfony - symfony.great: J'aime Symfony - - .. code-block:: php - - return [ - 'Symfony is great' => 'J\'aime Symfony', - 'symfony.great' => 'J\'aime Symfony', - ]; - -.. _translation-real-vs-keyword-messages: - -.. sidebar:: Using Real or Keyword Messages - - This example illustrates the two different philosophies when creating - messages to be translated:: - - $translator->trans('Symfony is great'); - - $translator->trans('symfony.great'); - - In the first method, messages are written in the language of the default - locale (English in this case). That message is then used as the "id" - when creating translations. - - In the second method, messages are actually "keywords" that convey the - idea of the message. The keyword message is then used as the "id" for - any translations. In this case, translations must be made for the default - locale (i.e. to translate ``symfony.great`` to ``Symfony is great``). - - The second method is handy because the message key won't need to be changed - in every translation file if you decide that the message should actually - read "Symfony is really great" in the default locale. - - The choice of which method to use is entirely up to you, but the "keyword" - format is often recommended for multi-language applications, whereas for - shared bundles that contain translation resources we recommend the real - message, so your application can choose to disable the translator layer - and you will see a readable message. - - 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 - - symfony: - is: - great: Symfony is great - amazing: Symfony is amazing - has: - bundles: Symfony has bundles - user: - login: Login - - .. code-block:: php - - [ - 'symfony' => [ - 'is' => [ - 'great' => 'Symfony is great', - 'amazing' => 'Symfony is amazing', - ], - 'has' => [ - 'bundles' => 'Symfony has bundles', - ], - ], - 'user' => [ - '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 - - symfony.is.great: Symfony is great - symfony.is.amazing: Symfony is amazing - symfony.has.bundles: Symfony has bundles - user.login: Login - - .. code-block:: php - - return [ - 'symfony.is.great' => 'Symfony is great', - 'symfony.is.amazing' => 'Symfony is amazing', - 'symfony.has.bundles' => 'Symfony has bundles', - 'user.login' => 'Login', - ]; - -.. _component-translation-pluralization: - -Pluralization -------------- - -Message pluralization is a tough topic as the rules can be quite complex. For -instance, here is the mathematical 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:: - - // the %count% placeholder is assigned to the second argument... - $translator->transChoice( - 'There is one apple|There are %count% apples', - 10 - ); - - // ...but you can define more placeholders if needed - $translator->transChoice( - 'Hurry up %name%! There is one apple left.|There are %count% apples left.', - 10, - // no need to include %count% here; Symfony does that for you - ['%name%' => $user->getName()] - ); - -The second argument (``10`` in this example) is the *number* of objects being -described and is used to determine which translation to use and also to populate -the ``%count%`` placeholder. - -.. versionadded:: 3.2 - - Before Symfony 3.2, the placeholder used to select the plural (``%count%`` - in this example) must be included in the third optional argument of the - ``transChoice()`` method:: - - $translator->transChoice( - 'There is one apple|There are %count% apples', - 10, - ['%count%' => 10] - ); - - Starting from Symfony 3.2, when the only placeholder is ``%count%``, you - don't have to pass this third argument. - -Based on the given number, the translator chooses the right plural form. -In English, most words have a singular form when there is exactly one object -and a plural form for all other numbers (0, 2, 3...). So, if ``count`` is -``1``, the translator will use the first string (``There is one apple``) -as the translation. Otherwise it will use ``There are %count% apples``. - -Here is the French translation: - -.. 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 -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( - 'Symfony is great', - [], - 'messages', - 'fr_FR' - ); - - $translator->transChoice( - '{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples', - 10, - [], - 'messages', - 'fr_FR' - ); - -.. note:: - - Starting from Symfony 3.2, the third argument of ``transChoice()`` is - optional when the only placeholder in use is ``%count%``. In previous - Symfony versions you needed to always define it:: - - $translator->transChoice( - '{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples', - 10, - ['%count%' => 10], - 'messages', - 'fr_FR' - ); - -.. _retrieving-the-message-catalogue: - -Retrieving the Message Catalog ------------------------------- - -In case you want to use the same translation catalog outside your application -(e.g. use translation on the client side), it's possible to fetch raw translation -messages. Just specify the required locale:: - - $catalog = $translator->getCatalogue('fr_FR'); - $messages = $catalog->all(); - while ($catalog = $catalog->getFallbackCatalogue()) { - $messages = array_replace_recursive($catalog->all(), $messages); - } - -The ``$messages`` variable will have the following structure:: - - [ - 'messages' => [ - 'Hello world' => 'Bonjour tout le monde', - ], - 'validators' => [ - 'Value should not be empty' => 'Valeur ne doit pas ĂȘtre vide', - 'Value is too long' => 'Valeur est trop long', - ], - ]; - -Adding Notes to Translation Contents ------------------------------------- - -.. versionadded:: 3.4 - - The feature to load and dump translation notes was introduced in Symfony 3.4. - -Sometimes translators need additional context to better decide how to translate -some content. This context can be provided with notes, which are a collection of -comments used to store end user readable information. The only format that -supports loading and dumping notes is XLIFF version 2.0. - -If the XLIFF 2.0 document contains ```` nodes, they are automatically -loaded/dumped when using this component inside a Symfony application: - -.. code-block:: xml - - - - - - - new - true - user login - - - original-content - translated-content - - - - - -When using the standalone Translation component, call the ``setMetadata()`` -method of the catalog and pass the notes as arrays. This is for example the -code needed to generate the previous XLIFF file:: - - use Symfony\Component\Translation\Dumper\XliffFileDumper; - use Symfony\Component\Translation\MessageCatalogue; - - $catalog = new MessageCatalogue('en_US'); - $catalog->add([ - 'original-content' => 'translated-content', - ]); - $catalog->setMetadata('original-content', ['notes' => [ - ['category' => 'state', 'content' => 'new'], - ['category' => 'approved', 'content' => 'true'], - ['category' => 'section', 'content' => 'user login', 'priority' => '1'], - ]]); - - $dumper = new XliffFileDumper(); - $dumper->formatCatalogue($catalog, 'messages', [ - 'default_locale' => 'fr_FR', - 'xliff_version' => '2.0' - ]); - -.. _`L10n`: https://en.wikipedia.org/wiki/Internationalization_and_localization -.. _`ISO 31-11`: https://en.wikipedia.org/wiki/Interval_(mathematics)#Notations_for_intervals diff --git a/introduction/from_flat_php_to_symfony.rst b/introduction/from_flat_php_to_symfony.rst index 661d84767c4..13b61f7eb79 100644 --- a/introduction/from_flat_php_to_symfony.rst +++ b/introduction/from_flat_php_to_symfony.rst @@ -736,7 +736,7 @@ migrating the blog from flat PHP to Symfony has improved your life: :doc:`Templating `, :doc:`Security `, :doc:`Form `, `Validator`_ and - :doc:`Translation ` components (to name + `Translation`_ components (to name a few); * The application now enjoys **fully-flexible URLs** thanks to the Routing @@ -753,6 +753,7 @@ A good selection of `Symfony community tools`_ can be found on GitHub. .. _`Model-View-Controller`: https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller .. _`Doctrine`: http://www.doctrine-project.org +.. _Translation: https://github.com/symfony/translation .. _`Composer`: https://getcomposer.org .. _`download Composer`: https://getcomposer.org/download/ .. _`Validator`: https://github.com/symfony/validator diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index b421a6c5d3a..a0003f08062 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -1726,10 +1726,6 @@ formatter The ID of the service used to format translation messages. The service class must implement the :class:`Symfony\\Component\\Translation\\Formatter\\MessageFormatterInterface`. -.. seealso:: - - For more details, see :doc:`/components/translation/custom_message_formatter`. - .. _reference-translator-paths: paths diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 35a44883808..0ff2dbf9c1b 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -1045,11 +1045,6 @@ translation.loader By default, translations are loaded from the filesystem in a variety of different formats (YAML, XLIFF, PHP, etc). -.. seealso:: - - Learn how to :ref:`load custom formats ` - in the components section. - Now, register your loader as a service and tag it with ``translation.loader``: .. configuration-block:: @@ -1235,11 +1230,6 @@ This is the name that's used to determine which dumper should be used. $container->register(JsonFileDumper::class) ->addTag('translation.dumper', ['alias' => 'json']); -.. seealso:: - - Learn how to :ref:`dump to custom formats ` - in the components section. - .. _reference-dic-tags-twig-extension: twig.extension diff --git a/translation.rst b/translation.rst index 95ed03e51bb..66b6b58a1b2 100644 --- a/translation.rst +++ b/translation.rst @@ -28,10 +28,7 @@ into the language of the user:: *language* code, an underscore (``_``), then the `ISO 3166-1 alpha-2`_ *country* code (e.g. ``fr_FR`` for French/France) is recommended. -In this article, you'll learn how to use the Translation component in the -Symfony Framework. You can read the -:doc:`Translation component documentation ` -to learn even more. Overall, the process has several steps: +The translation process has several steps: #. :ref:`Enable and configure ` Symfony's translation service; @@ -122,10 +119,15 @@ When this code is executed, Symfony will attempt to translate the message you need to tell Symfony how to translate the message via a "translation resource", which is usually a file that contains a collection of translations for a given locale. This "dictionary" of translations can be created in several -different formats, XLIFF being the recommended format: +different formats: .. configuration-block:: + .. code-block:: yaml + + # messages.fr.yml + Symfony is great: J'aime Symfony + .. code-block:: xml @@ -141,11 +143,6 @@ different formats, XLIFF being the recommended format: - .. code-block:: yaml - - # messages.fr.yml - Symfony is great: J'aime Symfony - .. code-block:: php // messages.fr.php @@ -160,27 +157,121 @@ Now, if the language of the user's locale is French (e.g. ``fr_FR`` or ``fr_BE`` the message will be translated into ``J'aime Symfony``. You can also translate the message inside your :ref:`templates `. +.. _translation-real-vs-keyword-messages: + +.. sidebar:: Using Real or Keyword Messages + + This example illustrates the two different philosophies when creating + messages to be translated:: + + $translator->trans('Symfony is great'); + + $translator->trans('symfony.great'); + + In the first method, messages are written in the language of the default + locale (English in this case). That message is then used as the "id" + when creating translations. + + In the second method, messages are actually "keywords" that convey the + idea of the message. The keyword message is then used as the "id" for + any translations. In this case, translations must be made for the default + locale (i.e. to translate ``symfony.great`` to ``Symfony is great``). + + The second method is handy because the message key won't need to be changed + in every translation file if you decide that the message should actually + read "Symfony is really great" in the default locale. + + The choice of which method to use is entirely up to you, but the "keyword" + format is often recommended for multi-language applications, whereas for + shared bundles that contain translation resources we recommend the real + message, so your application can choose to disable the translator layer + and you will see a readable message. + + 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 + + symfony: + is: + # id is symfony.is.great + great: Symfony is great + # id is symfony.is.amazing + amazing: Symfony is amazing + has: + # id is symfony.has.bundles + bundles: Symfony has bundles + user: + # id is user.login + login: Login + + .. code-block:: php + + [ + 'symfony' => [ + 'is' => [ + // id is symfony.is.great + 'great' => 'Symfony is great', + // id is symfony.is.amazing + 'amazing' => 'Symfony is amazing', + ], + 'has' => [ + // id is symfony.has.bundles + 'bundles' => 'Symfony has bundles', + ], + ], + 'user' => [ + // id is user.login + 'login' => 'Login', + ], + ]; + The Translation Process ~~~~~~~~~~~~~~~~~~~~~~~ To actually translate the message, Symfony uses a simple process: -* The ``locale`` of the current user, which is stored on the request is determined; +#. The ``locale`` of the current user, which is stored on the request is determined; -* A catalog (e.g. big collection) of translated messages is loaded from translation - resources defined for the ``locale`` (e.g. ``fr_FR``). Messages from the - :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. +#. A catalog (i.e. big collection) of translated messages is loaded from translation + resources defined 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. +#. If the message is located in the catalog, the translation is returned. If + not, the translator returns the original message. When using the ``trans()`` method, Symfony looks for the exact string inside the appropriate message catalog and returns it (if it exists). +.. _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 the filename without the extension. The default domain is +``messages``. Translations can for example be split into three different +domains: ``messages``, ``admin`` and ``navigation``. The French translation +would be organized in these files: + +* ``app/Resources/translations/messages.fr.yml`` +* ``app/Resources/translations/admin.fr.yml`` +* ``app/Resources/translations/navigation.fr.yml`` + +When translating strings that are not in the default domain (``messages``), +you must specify the domain as the third argument of ``trans()``:: + + $translator->trans('Symfony is great', [], 'admin'); + Message Placeholders --------------------- +~~~~~~~~~~~~~~~~~~~~ Sometimes, a message containing a variable needs to be translated:: @@ -193,10 +284,65 @@ Sometimes, a message containing a variable needs to be 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"*). +(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":: + + // ... + + public function indexAction($name) + { + $translated = $this->get('translator')->trans('Hello %name%'); + + // ... + } + +Symfony 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:: yaml + + 'Hello %name%': Bonjour %name% + + .. code-block:: xml + + + + + + + Hello %name% + Bonjour %name% + + + + + + .. code-block:: php + + return [ + 'Hello %name%' => 'Bonjour %name%', + ]; -For details on how to handle this situation, see :ref:`component-translation-placeholders` -in the components documentation. For how to do this in templates, see :ref:`translation-tags`. +.. note:: + + The placeholders can take on any form as the full message is reconstructed + using the PHP :phpfunction:`strtr function`. But the ``%...%`` form + is recommended, to avoid problems when using Twig. + +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 catalogs that define the translations +for any number of different locales. Pluralization ------------- @@ -209,11 +355,133 @@ plural, based on some variable: There is one apple. There are 5 apples. -To handle this, use the :method:`Symfony\\Component\\Translation\\Translator::transChoice` +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 these messages, use the +:method:`Symfony\\Component\\Translation\\Translator::transChoice` method or the ``transchoice`` tag/filter in your :ref:`template `. +To translate pluralized messages, use the +:method:`Symfony\\Component\\Translation\\Translator::transChoice` method:: + + // the %count% placeholder is assigned to the second argument... + $this->get('translator')->transChoice( + 'There is one apple|There are %count% apples', + 10 + ); + + // ...but you can define more placeholders if needed + $this->get('translator')->transChoice( + 'Hurry up %name%! There is one apple left.|There are %count% apples left.', + 10, + // no need to include %count% here; Symfony does that for you + ['%name%' => $user->getName()] + ); + +The second argument (``10`` in this example) is the *number* of objects being +described and is used to determine which translation to use and also to populate +the ``%count%`` placeholder. + +.. versionadded:: 3.2 + + Before Symfony 3.2, the placeholder used to select the plural (``%count%`` + in this example) must be included in the third optional argument of the + ``transChoice()`` method:: + + $translator->transChoice( + 'There is one apple|There are %count% apples', + 10, + ['%count%' => 10] + ); + + Starting from Symfony 3.2, when the only placeholder is ``%count%``, you + don't have to pass this third argument. + +Based on the given number, the translator chooses the right plural form. +In English, most words have a singular form when there is exactly one object +and a plural form for all other numbers (0, 2, 3...). So, if ``count`` is +``1``, the translator will use the first string (``There is one apple``) +as the translation. Otherwise it will use ``There are %count% apples``. + +Here is the French translation:: + + '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``. -For much more information, see :ref:`component-translation-pluralization` -in the Translation component documentation. +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 +can use ``-Inf`` and ``+Inf`` for the infinite. Translations in Templates ------------------------- @@ -326,6 +594,28 @@ The translator service is accessible in PHP templates through the ['%count%' => 10] ) ?> +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( + 'Symfony is great', + [], + 'messages', + 'fr_FR' + ); + + $translator->transChoice( + '{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples', + 10, + [], + 'messages', + 'fr_FR' + ); + Extracting Translation Contents and Updating Catalogs Automatically ------------------------------------------------------------------- @@ -388,15 +678,32 @@ must be named according to the following path: ``domain.locale.loader``: ``php``, ``yml``, etc). The loader can be the name of any registered loader. By default, Symfony -provides many loaders, including: - -* ``xlf``: XLIFF file; -* ``php``: PHP file; -* ``yml``: YAML file. +provides many loaders: + +* ``.yaml``: YAML file +* ``.xlf``: XLIFF file; +* ``.php``: Returning a PHP array; +* ``.csv``: CSV file; +* ``.json``: JSON file; +* ``.ini``: INI file; +* ``.dat``, ``.res``: ICU resource bundle; +* ``.mo``: Machine object format; +* ``.po``: Portable object format; +* ``.qt``: QT Translations XML file; The choice of which loader to use is entirely up to you and is a matter of -taste. The recommended option is to use ``xlf`` for translations. -For more options, see :ref:`component-translator-message-catalogs`. +taste. The recommended option is to use YAML for simple projects and use XLIFF +if you're generating translations with specialized programs or teams. + +.. caution:: + + Each time you create a *new* message catalog (or install a bundle + that includes a translation catalog), be sure to clear your cache so + that Symfony can discover the new translation resources: + + .. code-block:: terminal + + $ php bin/console cache:clear .. note:: @@ -451,16 +758,6 @@ For more options, see :ref:`component-translator-message-catalogs`. :class:`Symfony\\Component\\Translation\\Loader\\LoaderInterface` interface. See the :ref:`dic-tags-translation-loader` tag for more information. -.. caution:: - - Each time you create a *new* translation resource (or install a bundle - that includes a translation resource), be sure to clear your cache so - that Symfony can discover the new translation resources: - - .. code-block:: terminal - - $ php bin/console cache:clear - .. _translation-fallback: Fallback Translation Locales @@ -515,8 +812,7 @@ 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 - (learn about this in :doc:`/components/translation/usage`); + :method:`Symfony\\Component\\Translation\\Translator::transChoice` methods; * Translate each message into multiple locales by creating translation message files. Symfony discovers and processes each file because its name follows @@ -533,9 +829,11 @@ Learn more :glob: /translation/* + /validation/translations .. _`i18n`: https://en.wikipedia.org/wiki/Internationalization_and_localization .. _`ISO 3166-1 alpha-2`: https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes +.. _`ISO 31-11`: https://en.wikipedia.org/wiki/Interval_(mathematics)#Notations_for_intervals .. _`ISO 639-1`: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes .. _`Translatable Extension`: http://atlantic18.github.io/DoctrineExtensions/doc/translatable.html .. _`Translatable Behavior`: https://github.com/KnpLabs/DoctrineBehaviors diff --git a/translation/xliff.rst b/translation/xliff.rst new file mode 100644 index 00000000000..c81620929b8 --- /dev/null +++ b/translation/xliff.rst @@ -0,0 +1,44 @@ +The XLIFF format +================ + +Most professional translation tools support XLIFF_. These files use the XML +format and are supported by Symfony by default. Besides supporting +:doc:`all Symfony translation features `, the XLIFF format also +has some specific features. + +Adding Notes to Translation Contents +------------------------------------ + +.. versionadded:: 3.4 + + The feature to load and dump translation notes was introduced in Symfony 3.4. + +Sometimes translators need additional context to better decide how to translate +some content. This context can be provided with notes, which are a collection of +comments used to store end user readable information. The only format that +supports loading and dumping notes is XLIFF version 2. + +If the XLIFF 2.0 document contains ```` nodes, they are automatically +loaded/dumped inside a Symfony application: + +.. code-block:: xml + + + + + + + new + true + user login + + + original-content + translated-content + + + + + +.. _XLIFF: http://docs.oasis-open.org/xliff/xliff-core/v2.1/xliff-core-v2.1.html From e3f46467c851b4218d5a150d7970ac993e1b212e Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Wed, 22 Jan 2020 11:24:31 +0100 Subject: [PATCH 2/3] Move sidebar into a real section --- translation.rst | 109 ++++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/translation.rst b/translation.rst index 66b6b58a1b2..2ea8a7e7248 100644 --- a/translation.rst +++ b/translation.rst @@ -159,75 +159,76 @@ the message inside your :ref:`templates `. .. _translation-real-vs-keyword-messages: -.. sidebar:: Using Real or Keyword Messages +Using Real or Keyword Messages +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - This example illustrates the two different philosophies when creating - messages to be translated:: +This example illustrates the two different philosophies when creating +messages to be translated:: - $translator->trans('Symfony is great'); + $translator->trans('Symfony is great'); - $translator->trans('symfony.great'); + $translator->trans('symfony.great'); - In the first method, messages are written in the language of the default - locale (English in this case). That message is then used as the "id" - when creating translations. +In the first method, messages are written in the language of the default +locale (English in this case). That message is then used as the "id" +when creating translations. - In the second method, messages are actually "keywords" that convey the - idea of the message. The keyword message is then used as the "id" for - any translations. In this case, translations must be made for the default - locale (i.e. to translate ``symfony.great`` to ``Symfony is great``). +In the second method, messages are actually "keywords" that convey the +idea of the message. The keyword message is then used as the "id" for +any translations. In this case, translations must be made for the default +locale (i.e. to translate ``symfony.great`` to ``Symfony is great``). - The second method is handy because the message key won't need to be changed - in every translation file if you decide that the message should actually - read "Symfony is really great" in the default locale. +The second method is handy because the message key won't need to be changed +in every translation file if you decide that the message should actually +read "Symfony is really great" in the default locale. - The choice of which method to use is entirely up to you, but the "keyword" - format is often recommended for multi-language applications, whereas for - shared bundles that contain translation resources we recommend the real - message, so your application can choose to disable the translator layer - and you will see a readable message. +The choice of which method to use is entirely up to you, but the "keyword" +format is often recommended for multi-language applications, whereas for +shared bundles that contain translation resources we recommend the real +message, so your application can choose to disable the translator layer +and you will see a readable message. - 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: +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:: +.. configuration-block:: - .. code-block:: yaml + .. code-block:: yaml - symfony: - is: - # id is symfony.is.great - great: Symfony is great - # id is symfony.is.amazing - amazing: Symfony is amazing - has: - # id is symfony.has.bundles - bundles: Symfony has bundles - user: - # id is user.login - login: Login + symfony: + is: + # id is symfony.is.great + great: Symfony is great + # id is symfony.is.amazing + amazing: Symfony is amazing + has: + # id is symfony.has.bundles + bundles: Symfony has bundles + user: + # id is user.login + login: Login - .. code-block:: php + .. code-block:: php - [ - 'symfony' => [ - 'is' => [ - // id is symfony.is.great - 'great' => 'Symfony is great', - // id is symfony.is.amazing - 'amazing' => 'Symfony is amazing', - ], - 'has' => [ - // id is symfony.has.bundles - 'bundles' => 'Symfony has bundles', - ], + [ + 'symfony' => [ + 'is' => [ + // id is symfony.is.great + 'great' => 'Symfony is great', + // id is symfony.is.amazing + 'amazing' => 'Symfony is amazing', ], - 'user' => [ - // id is user.login - 'login' => 'Login', + 'has' => [ + // id is symfony.has.bundles + 'bundles' => 'Symfony has bundles', ], - ]; + ], + 'user' => [ + // id is user.login + 'login' => 'Login', + ], + ]; The Translation Process ~~~~~~~~~~~~~~~~~~~~~~~ From eb7158bd92088f016db8349c0e98ed9246374236 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Wed, 22 Jan 2020 11:25:08 +0100 Subject: [PATCH 3/3] Promote usage of a single translation domain --- bundles/override.rst | 2 +- translation.rst | 38 ++++++++++++------------------------- validation/translations.rst | 2 +- 3 files changed, 14 insertions(+), 28 deletions(-) diff --git a/bundles/override.rst b/bundles/override.rst index a66c277fe2f..e90db9b9315 100644 --- a/bundles/override.rst +++ b/bundles/override.rst @@ -135,6 +135,6 @@ Translations Translations are not related to bundles, but to domains. That means that you can override the translations from any translation file, as long as it is in -:ref:`the correct domain `. +:ref:`the correct domain `. .. _`the Doctrine documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/inheritance-mapping.html#overrides diff --git a/translation.rst b/translation.rst index 2ea8a7e7248..5c3ff5553ad 100644 --- a/translation.rst +++ b/translation.rst @@ -249,27 +249,12 @@ To actually translate the message, Symfony uses a simple process: When using the ``trans()`` method, Symfony looks for the exact string inside the appropriate message catalog and returns it (if it exists). -.. _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 the filename without the extension. The default domain is -``messages``. Translations can for example be split into three different -domains: ``messages``, ``admin`` and ``navigation``. The French translation -would be organized in these files: - -* ``app/Resources/translations/messages.fr.yml`` -* ``app/Resources/translations/admin.fr.yml`` -* ``app/Resources/translations/navigation.fr.yml`` +.. tip:: -When translating strings that are not in the default domain (``messages``), -you must specify the domain as the third argument of ``trans()``:: + When translating strings that are not in the default domain (``messages``), + you must specify the domain as the third argument of ``trans()``:: - $translator->trans('Symfony is great', [], 'admin'); + $translator->trans('Symfony is great', [], 'admin'); Message Placeholders ~~~~~~~~~~~~~~~~~~~~ @@ -520,18 +505,18 @@ works when you use a placeholder following the ``%var%`` pattern. If you need to use the percent character (``%``) in a string, escape it by doubling it: ``{% trans %}Percent: %percent%%%{% endtrans %}`` -You can also specify the message domain and pass some additional variables: +You can also pass some additional variables and specify the message domain: .. code-block:: twig - {% trans with {'%name%': 'Fabien'} from 'app' %}Hello %name%{% endtrans %} - - {% trans with {'%name%': 'Fabien'} from 'app' into 'fr' %}Hello %name%{% endtrans %} + {% trans with {'%name%': 'Fabien'} into 'fr' %}Hello %name%{% endtrans %} - {% transchoice count with {'%name%': 'Fabien'} from 'app' %} + {% transchoice count with {'%name%': 'Fabien'} %} {0} %name%, there are no apples|{1} %name%, there is one apple|]1,Inf[ %name%, there are %count% apples {% endtranschoice %} + {% trans with {'%name%': 'Fabien'} from 'custom_domain' %}Hello %name%{% endtrans %} + .. _translation-filters: The ``trans`` and ``transchoice`` filters can be used to translate *variable @@ -670,8 +655,9 @@ priority message files. The filename of the translation files is also important: each message file must be named according to the following path: ``domain.locale.loader``: -* **domain**: An optional way to organize messages into groups (e.g. ``admin``, - ``navigation`` or the default ``messages``) - see :ref:`using-message-domains`; +* **domain**: An optional way to organize messages into groups. Unless + parts of the application are explicitly separated from each other, it is + recommended to only use default ``messages`` domain; * **locale**: The locale that the translations are for (e.g. ``en_GB``, ``en``, etc); diff --git a/validation/translations.rst b/validation/translations.rst index a7f40a75515..74689ba135d 100644 --- a/validation/translations.rst +++ b/validation/translations.rst @@ -6,7 +6,7 @@ How to Translate Validation Constraint Messages If you're using validation constraints with the Form component, you can translate the error messages by creating a translation resource for the -``validators`` :ref:`domain `. +``validators`` :ref:`domain `. To start, suppose you've created a plain-old-PHP object that you need to use somewhere in your application::