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:: pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy