From a6dabc1d8fd0ca9444f04eab09cf579ccf28edb5 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Sun, 28 Apr 2019 00:39:07 +0200 Subject: [PATCH 1/6] Some general fixes/improvements for translation guide --- translation.rst | 142 ++++++-------------------------------- translation/templates.rst | 111 +++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 121 deletions(-) create mode 100644 translation/templates.rst diff --git a/translation.rst b/translation.rst index ac859742c55..0670c2d07a1 100644 --- a/translation.rst +++ b/translation.rst @@ -12,13 +12,11 @@ wrapping each with a function capable of translating the text (or "message") into the language of the user:: // text will *always* print out in English - dump('Hello World'); - die(); + echo 'Hello World'; // text can be translated into the end-user's language or // default to English - dump($translator->trans('Hello World')); - die(); + echo $translator->trans('Hello World'); .. note:: @@ -163,7 +161,7 @@ different formats, XLIFF being the recommended format: // translations/messages.fr.php return [ - 'Symfony is great' => 'J\'aime Symfony', + 'Symfony is great' => "J'aime Symfony", ]; For information on where these files should be located, see @@ -171,7 +169,7 @@ For information on where these files should be located, see 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 `. +the message inside your `templates `. The Translation Process ~~~~~~~~~~~~~~~~~~~~~~~ @@ -184,7 +182,8 @@ To actually translate the message, Symfony uses the following process: 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. + "dictionary" of translations. This catalog is cached in production, to + minimize performance impact. * If the message is located in the catalog, the translation is returned. If not, the translator returns the original message. @@ -240,112 +239,14 @@ Translations in Templates ------------------------- Most of the time, translation occurs in templates. Symfony provides native -support for both Twig and PHP templates. +support for both Twig and PHP templates: -.. _translation-tags: +.. code-block:: html+twig -Twig Templates -~~~~~~~~~~~~~~ +

{% trans %}Symfony is great!{% endtrans %}

-Symfony provides specialized Twig tags (``trans`` and ``transchoice``) to -help with message translation of *static blocks of text*: - -.. code-block:: twig - - {% trans %}Hello %name%{% endtrans %} - - {% transchoice count %} - {0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples - {% endtranschoice %} - -The ``transchoice`` tag automatically gets the ``%count%`` variable from -the current context and passes it to the translator. This mechanism only -works when you use a placeholder following the ``%var%`` pattern. - -.. caution:: - - The ``%var%`` notation of placeholders is required when translating in - Twig templates using the tag. - -.. tip:: - - 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: - -.. code-block:: twig - - {% trans with {'%name%': 'Fabien'} from 'app' %}Hello %name%{% endtrans %} - - {% trans with {'%name%': 'Fabien'} from 'app' into 'fr' %}Hello %name%{% endtrans %} - - {% transchoice count with {'%name%': 'Fabien'} from 'app' %} - {0} %name%, there are no apples|{1} %name%, there is one apple|]1,Inf[ %name%, there are %count% apples - {% endtranschoice %} - -.. _translation-filters: - -The ``trans`` and ``transchoice`` filters can be used to translate *variable -texts* and complex expressions: - -.. code-block:: twig - - {{ message|trans }} - - {{ message|transchoice(5) }} - - {{ message|trans({'%name%': 'Fabien'}, 'app') }} - - {{ message|transchoice(5, {'%name%': 'Fabien'}, 'app') }} - -.. tip:: - - Using the translation tags or filters have the same effect, but with - one subtle difference: automatic output escaping is only applied to - translations using a filter. In other words, if you need to be sure - that your translated message is *not* output escaped, you must apply - the ``raw`` filter after the translation filter: - - .. code-block:: html+twig - - {# text translated between tags is never escaped #} - {% trans %} -

foo

- {% endtrans %} - - {% set message = '

foo

' %} - - {# strings and variables translated via a filter are escaped by default #} - {{ message|trans|raw }} - {{ '

bar

'|trans|raw }} - -.. tip:: - - You can set the translation domain for an entire Twig template with a single tag: - - .. code-block:: twig - - {% trans_default_domain 'app' %} - - Note that this only influences the current template, not any "included" - template (in order to avoid side effects). - -PHP Templates -~~~~~~~~~~~~~ - -The translator service is accessible in PHP templates through the -``translator`` helper: - -.. code-block:: html+php - - trans('Symfony is great') ?> - - transChoice( - '{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples', - 10, - ['%count%' => 10] - ) ?> +Read :doc:`/translation/templates` for more information about the Twig tags and +filters for translation. Extracting Translation Contents and Updating Catalogs Automatically ------------------------------------------------------------------- @@ -381,11 +282,9 @@ Translation Resource/File Names and Locations Symfony looks for message files (i.e. translations) in the following default locations: -* the ``translations/`` directory (at the root of the project); - -* the ``src/Resources//translations/`` directory; - -* the ``Resources/translations/`` directory inside of any bundle. +#. the ``translations/`` directory (at the root of the project); +#. the ``src/Resources//translations/`` directory; +#. the ``Resources/translations/`` directory inside of any bundle. .. deprecated:: 4.2 @@ -486,6 +385,12 @@ For more options, see :ref:`component-translator-message-catalogs`. $ php bin/console cache:clear +Handling the User's Locale +-------------------------- + +Translating happens based on the user's locale. Read :doc:`/translation/locale` +to learn more about how to handle it. + .. _translation-fallback: Fallback Translation Locales @@ -510,12 +415,6 @@ checks translation resources for several locales: add the missing translation to the log file. For details, see :ref:`reference-framework-translator-logging`. -Handling the User's Locale --------------------------- - -Translating happens based on the user's locale. Read :doc:`/translation/locale` -to learn more about how to handle it. - Translating Database Content ---------------------------- @@ -555,6 +454,7 @@ Learn more .. toctree:: :maxdepth: 1 + translation/templates translation/locale translation/debug translation/lint diff --git a/translation/templates.rst b/translation/templates.rst new file mode 100644 index 00000000000..172932e1667 --- /dev/null +++ b/translation/templates.rst @@ -0,0 +1,111 @@ +Using Translation in Templates +============================== + +Twig Templates +-------------- + +.. _translation-tags: + +Using Twig Tags +~~~~~~~~~~~~~~~ + +Symfony provides specialized Twig tags (``trans`` and ``transchoice``) to +help with message translation of *static blocks of text*: + +.. code-block:: twig + + {% trans %}Hello %name%{% endtrans %} + + {% transchoice count %} + {0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples + {% endtranschoice %} + +The ``transchoice`` tag automatically gets the ``%count%`` variable from +the current context and passes it to the translator. This mechanism only +works when you use a placeholder following the ``%var%`` pattern. + +.. caution:: + + The ``%var%`` notation of placeholders is required when translating in + Twig templates using the tag. + +.. tip:: + + 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: + +.. code-block:: twig + + {% trans with {'%name%': 'Fabien'} from 'app' %}Hello %name%{% endtrans %} + + {% trans with {'%name%': 'Fabien'} from 'app' into 'fr' %}Hello %name%{% endtrans %} + + {% transchoice count with {'%name%': 'Fabien'} from 'app' %} + {0} %name%, there are no apples|{1} %name%, there is one apple|]1,Inf[ %name%, there are %count% apples + {% endtranschoice %} + +.. _translation-filters: + +Using Twig Filters +~~~~~~~~~~~~~~~~~~ + +The ``trans`` and ``transchoice`` filters can be used to translate *variable +texts* and complex expressions: + +.. code-block:: twig + + {{ message|trans }} + + {{ message|transchoice(5) }} + + {{ message|trans({'%name%': 'Fabien'}, 'app') }} + + {{ message|transchoice(5, {'%name%': 'Fabien'}, 'app') }} + +.. tip:: + + Using the translation tags or filters have the same effect, but with + one subtle difference: automatic output escaping is only applied to + translations using a filter. In other words, if you need to be sure + that your translated message is *not* output escaped, you must apply + the ``raw`` filter after the translation filter: + + .. code-block:: html+twig + + {# text translated between tags is never escaped #} + {% trans %} +

foo

+ {% endtrans %} + + {% set message = '

foo

' %} + + {# strings and variables translated via a filter are escaped by default #} + {{ message|trans|raw }} + {{ '

bar

'|trans|raw }} + +.. tip:: + + You can set the translation domain for an entire Twig template with a single tag: + + .. code-block:: twig + + {% trans_default_domain 'app' %} + + Note that this only influences the current template, not any "included" + template (in order to avoid side effects). + +PHP Templates +------------- + +The translator service is accessible in PHP templates through the +``translator`` helper:: + + trans('Symfony is great') ?> + + transChoice( + '{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples', + 10, + ['%count%' => 10] + ) ?> From 0d29e3a61ecb4dd69351cbf70116978d129f5a85 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Sun, 28 Apr 2019 00:39:59 +0200 Subject: [PATCH 2/6] Added documentation on the ICU MessageFormat --- components/translation/usage.rst | 208 --------------- translation.rst | 55 ++-- translation/message_format.rst | 436 +++++++++++++++++++++++++++++++ 3 files changed, 457 insertions(+), 242 deletions(-) create mode 100644 translation/message_format.rst diff --git a/components/translation/usage.rst b/components/translation/usage.rst index b1dfa9a1f34..656e84083b5 100644 --- a/components/translation/usage.rst +++ b/components/translation/usage.rst @@ -21,79 +21,6 @@ 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 --------------------- @@ -222,141 +149,6 @@ recommended format. These files are parsed by one of the loader classes. '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. - -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 ----------------------------- diff --git a/translation.rst b/translation.rst index 0670c2d07a1..cbb0b212cad 100644 --- a/translation.rst +++ b/translation.rst @@ -174,7 +174,8 @@ the message inside your `templates `. The Translation Process ~~~~~~~~~~~~~~~~~~~~~~~ -To actually translate the message, Symfony uses the following process: +To actually translate the message, Symfony uses the following process when +using the ``trans()`` method: * The ``locale`` of the current user, which is stored on the request is determined; @@ -188,33 +189,21 @@ To actually translate the message, Symfony uses the following process: * 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). +.. _message-placeholders: +.. _pluralization: -Message Placeholders --------------------- +Message Format +-------------- Sometimes, a message containing a variable needs to be translated:: - use Symfony\Contracts\Translation\TranslatorInterface; - - public function index(TranslatorInterface $translator, $name) - { - $translated = $translator->trans('Hello '.$name); - - // ... - } + // ... + $translated = $translator->trans('Hello '.$name); -However, creating a translation for this string is impossible since the translator -will try to look up the exact message, including the variable portions +However, creating a translation for this string is impossible since the +translator will try to look up the message including the variable portions (e.g. *"Hello Ryan"* or *"Hello Fabien"*). -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`. - -Pluralization -------------- - Another complication is when you have translations that may or may not be plural, based on some variable: @@ -223,17 +212,15 @@ plural, based on some variable: There is one apple. There are 5 apples. -To handle this, use the :method:`Symfony\\Component\\Translation\\Translator::transChoice` -method or the ``transchoice`` tag/filter in your :ref:`template `. - -For much more information, see :ref:`component-translation-pluralization` -in the Translation component documentation. +To manage these situations, Symfony follows the `ICU MessageFormat`_ syntax by +using PHP's :phpclass:`MessageFormatter` class. Read more about this in +:doc:`/translation/message_format`. -.. deprecated:: 4.2 +.. versionadded:: 4.2 - In Symfony 4.2 the ``Translator::transChoice()`` method was deprecated in - favor of using ``Translator::trans()`` with ``%count%`` as the parameter - driving plurals. + Support for ICU MessageFormat was introduced in Symfony 4.2. Prior to this, + pluralization was managed by the + :method:`Symfony\\Component\\Translation\\Translator::transChoice` method. Translations in Templates ------------------------- @@ -436,10 +423,8 @@ Summary With the Symfony Translation component, creating an internationalized application no longer needs to be a painful process and boils down to these 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`); +* Abstract messages in your application by wrapping each in the + :method:`Symfony\\Component\\Translation\\Translator::trans` method; * Translate each message into multiple locales by creating translation message files. Symfony discovers and processes each file because its name follows @@ -454,12 +439,14 @@ Learn more .. toctree:: :maxdepth: 1 + translation/message_format translation/templates translation/locale translation/debug translation/lint .. _`i18n`: https://en.wikipedia.org/wiki/Internationalization_and_localization +.. _`ICU MessageFormat`: http://userguide.icu-project.org/formatparse/messages .. _`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 .. _`Translatable Extension`: http://atlantic18.github.io/DoctrineExtensions/doc/translatable.html diff --git a/translation/message_format.rst b/translation/message_format.rst new file mode 100644 index 00000000000..ba69b6f3f6c --- /dev/null +++ b/translation/message_format.rst @@ -0,0 +1,436 @@ +.. index:: + single: Translation; Message Format + +How to translate messages using the ICU MessageFormat +===================================================== + +.. versionadded:: 4.2 + + Support for ICU MessageFormat was introduced in Symfony 4.2. + +Messages (i.e. strings) in applications are almost never completely static. +They contain variables or other complexer logic like pluralization. In order to +handle this, the Translator component supports the `ICU MessageFormat`_ syntax. + +Using the ICU Message Format +---------------------------- + +In order to use the ICU Message Format, the :ref:`message domain +` has to be suffixed with ``intl-icu``: + +====================== =============================== +Normal file name ICU Message Format filename +====================== =============================== +``messages.en.yml`` ``messages+intl-icu.en.yml`` +``messages.fr_FR.xlf`` ``messages+intl-icu.fr_FR.xlf`` +``admin.en.yml`` ``admin+intl-icu.en.yml`` +====================== =============================== + +All messages in this file will now be processed by the +:phpclass:`MessageFormatter` during translation. + +.. _component-translation-placeholders: + +Message Placeholders +-------------------- + +The basic usage of the MessageFormat allows you to use placeholder (called +*arguments* in ICU MessageFormat) in your messages: + +.. configuration-block:: + + .. code-block:: yaml + + # translations/messages.en.yaml + say_hello: 'Hello {name}!' + + .. code-block:: xml + + + + + + + + say_hello + Hello {name}! + + + + + + .. code-block:: php + + // translations/messages.en.php + return [ + 'say_hello' => "Hello {name}!", + ]; + +Everything within the curly braces (``{...}``) is processed by the formatter +and replaced by it's placeholder:: + + // ... + echo $translator->trans('say_hello', ['name' => 'Fabien']); // Hello Fabien! + echo $translator->trans('say_hello', ['name' => 'Symfony']); // Hello Symfony! + +Selecting Different Messages Based on a Condition +------------------------------------------------- + +The curly brace syntax allows to "modify" the output of the variable. One of +these functions is the ``switch`` function. It acts like PHP's `switch statement`_ +and allows to use different strings based on the value of the variable. A +typical usage of this is gender: + +.. configuration-block:: + + .. code-block:: yaml + + # translations/messages.en.yaml + invitation_title: > + {organizer_gender, select, + female {{organizer_name} has invited you for her party!} + male {{organizer_name} has invited you for his party!} + other {{organizer_name} have invited you for their party!} + } + + .. code-block:: xml + + + + + + + + invitation_title + {organizer_gender, select, female {{organizer_name} has invited you for her party!} male {{organizer_name} has invited you for his party!} other {{organizer_name} have invited you for their party!}} + + + + + + .. code-block:: php + + // translations/messages.en.php + return [ + 'invitation_title' => '{organizer_gender, select, + female {{organizer_name} has invited you for her party!} + male {{organizer_name} has invited you for his party!} + other {{organizer_name} have invited you for their party!} + }', + ]; + +This might look very complex. The basic syntax for all functions is +``{variable_name, function_name, function_statement}``. In this case, the +function name is ``select`` and its statement contains the "cases" of this +select. This function is applied over the ``organizer_gender`` variable:: + + // ... + + // prints "Ryan has invited you for his party!" + echo $translator->trans('invition_title', [ + 'organizer_name' => 'Ryan', + 'organizer_gender' => 'male', + ]); + + // prints "John & Jane have invited you for their party!" + echo $translator->trans('invition_title', [ + 'organizer_name' => 'John & Jane', + 'organizer_gender' => 'not_applicable', + ]); + +The ``{...}`` syntax alternates between "literal" and "code" mode. This allows +you to use literal text in the select statements: + +#. The first ``{organizer_gender, select, ...}`` block starts the "code" mode, + which means ``organizer_gender`` is processed as a variable. +#. The inner ``{... has invited you for her party!}`` block brings you back in + "literal" mode, meaning the text is not processed. +#. Inside this block, ``{organizer_name}`` starts "code" mode again, allowing + ``organizer_name`` to be processed as variable. + +.. tip:: + + While it might seem more logical to only put ``her``, ``his`` or ``their`` + in the switch statement, it is better to use "complex arguments" at the + outermost structure of the message. The strings are in this way better + readable for translators and, as you can see in the ``other`` case, other + parts of the sentence mgith be influenced by the variable. + +Pluralization +------------- + +Another interesting function is ``plural``. It allows you to +handle pluralization in your messages (e.g. ``There are 3 apples`` vs +``There is one apple``). The function looks very similair to the ``select`` function: + +.. configuration-block:: + + .. code-block:: yaml + + # translations/messages.en.yaml + nr_of_apples: > + {apples, plural, + =0 {There are no apples :(} + one {There is one apple...} + other {There are # apples!} + } + + .. code-block:: xml + + + + + + + + nr_of_apples + {apples, plural, =0 {There are no apples :(} one {There is one apple...} other {There are # apples!}} + + + + + + .. code-block:: php + + // translations/messages.en.php + return [ + 'nr_of_apples' => '{apples, plural, + =0 {There are no apples :(} + one {There is one apple...} + other {There are # apples!} + }', + ]; + +Pluralization rules are actually quite complex and differ for each language. +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 + ); + +In order to facilitate this, the possible cases in the ``plural`` function are +also different for each language. For instance, Russian has ``one``, ``few``, +``many`` and ``other``, while English has only ``one`` and ``other``. The full +list of possible cases can be found in Unicode's `Language Plural Rules`_ +document. By prefixing with ``=``, you can match exact values (like ``0`` in +the above example). + +Usage of this string is the same as with variables and select:: + + // ... + + // prints "There is one apple..." + echo $translator->trans('nr_of_apples', ['apples' => 1]); + + // prints "There are 23 apples!" + echo $translator->trans('nr_of_apples', ['apples' => 23]); + +.. note:: + + You can also set an ``offset`` variable to determine whether the + pluralization should be offset (e.g. in sentences like ``You and # other people`` + / ``You and # other person``). + +.. tip:: + + When combining the ``select`` and ``plural`` functions, try to still have + ``select`` as outermost function: + + .. code-block:: text + + {gender_of_host, select, + female { + {num_guests, plural, offset:1 + =0 {{host} does not give a party.} + =1 {{host} invites {guest} to her party.} + =2 {{host} invites {guest} and one other person to her party.} + other {{host} invites {guest} and # other people to her party.}} + } + male { + {num_guests, plural, offset:1 + =0 {{host} does not give a party.} + =1 {{host} invites {guest} to his party.} + =2 {{host} invites {guest} and one other person to his party.} + other {{host} invites {guest} and # other people to his party.}} + } + other { + {num_guests, plural, offset:1 + =0 {{host} does not give a party.} + =1 {{host} invites {guest} to their party.} + =2 {{host} invites {guest} and one other person to their party.} + other {{host} invites {guest} and # other people to their party.}} + } + } + +Other Placeholder Functions +--------------------------- + +Besides these, the MessageFormat comes with a couple other interesting functions. + +Ordinal +~~~~~~~ + +Similair to ``plural``, ``selectordinal`` allows you to use numbers as ordinal scale: + +.. configuration-block:: + + .. code-block:: yaml + + # translations/messages.en.yaml + finish_place: > + You finished {place, selectordinal, + one {#st} + two {#nd} + few {#rd} + other {#th} + }! + + .. code-block:: xml + + + + + + + + finish_place + {apples, plural, =0 {There are no apples :(} one {There is one apple...} other {There are # apples!}} + + + + + + .. code-block:: php + + // translations/messages.en.php + return [ + 'finish_place' => 'You finished {place, selectordinal, + one {#st} + two {#nd} + few {#rd} + other {#th} + }!', + ]; + +.. code-block:: php + + // ... + + // prints "You finished 1st!" + echo $translator->trans('finish_place', ['place' => 1]); + + // prints "You finished 9th!" + echo $translator->trans('finish_place', ['place' => 9]); + + // prints "You finished 23rd!" + echo $translator->trans('finish_place', ['place' => 23]); + +The possible cases for this are also shown in Unicode's `Language Plural Rules`_ document. + +Date and Time +~~~~~~~~~~~~~ + +The date and time function allows you to format dates in the target locale +using the :phpclass:`IntlDateFormatter`: + +.. configuration-block:: + + .. code-block:: yaml + + # translations/messages.en.yaml + published_at: 'Published at {time, date} - {time, time, short}' + + .. code-block:: xml + + + + + + + + published_at + Published at {time, date} - {time, time, short} + + + + + + .. code-block:: php + + // translations/messages.en.php + return [ + 'published_at' => 'Published at {time, date} - {time, time, short}', + ]; + +.. code-block:: php + + // ... + + // prints "Published at Jan 25, 2019 - 11:30 AM" + echo $translator->trans('published_at', ['time' => new \DateTime('2019-01-25 11:30:00')]); + +Numbers +~~~~~~~ + +The ``number`` formatter allows you to format numbers using Intl's :phpclass:`NumberFormatter`: + +.. configuration-block:: + + .. code-block:: yaml + + # translations/messages.en.yaml + progress: '{progress, number, percent} of the work is done' + value_of_object: 'This artifact is worth {value, number, currency}' + + .. code-block:: xml + + + + + + + + progress + {progress, number, percent} of the work is done + + + + value_of_object + This artifact is worth {value, number, currency} + + + + + + .. code-block:: php + + // translations/messages.en.php + return [ + 'progress' => '{progress, number, percent} of the work is done', + 'value_of_object' => 'This artifact is worth {value, number, currency}', + ]; + +.. code-block:: php + + // ... + + // prints "82% of the work is done" + echo $translator->trans('progress', ['progress' => 0.82]); + // prints "100% of the work is done" + echo $translator->trans('progress', ['progress' => 1]); + + // prints "This artifact is worth $9,988,776.65" + // if we would translate this to i.e. French, the value would be shown as + // "9 988 776,65 €" + echo $translator->trans('value_of_object', ['value' => 9988776.65]); + +.. _`ICU MessageFormat`: http://userguide.icu-project.org/formatparse/messages +.. _`switch statement`: https://php.net/control-structures.switch +.. _`Language Plural Rules`: http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html From 2bd7f86bd1e89e08f58f8b5714c44ee28d4e2b87 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Sat, 4 May 2019 18:34:53 +0200 Subject: [PATCH 3/6] Removed further usage of transchoice in the docs --- reference/twig_reference.rst | 12 ++++++++++++ translation/debug.rst | 6 +----- translation/templates.rst | 12 ++++++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 27c208dc34c..083583727e7 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -359,6 +359,12 @@ Translates the text into the current language. More information in transchoice ~~~~~~~~~~~ +.. deprecated:: 4.2 + + The ``transchoice`` filter is deprecated since Symfony 4.2 and will be + removed in 5.0. Use the :doc:`ICU MessageFormat ` with + the ``trans`` filter instead. + .. code-block:: twig {{ message|transchoice(count, arguments = [], domain = null, locale = null) }} @@ -588,6 +594,12 @@ Renders the translation of the content. More information in :ref:`translation-ta transchoice ~~~~~~~~~~~ +.. deprecated:: 4.2 + + The ``transchoice`` tag is deprecated since Symfony 4.2 and will be + removed in 5.0. Use the :doc:`ICU MessageFormat ` with + the ``trans`` tag instead. + .. code-block:: twig {% transchoice count with vars from domain into locale %}{% endtranschoice %} diff --git a/translation/debug.rst b/translation/debug.rst index cec010c9901..126c048def9 100644 --- a/translation/debug.rst +++ b/translation/debug.rst @@ -12,15 +12,11 @@ command helps you to find these missing or unused translation messages templates .. code-block:: twig - {# messages can be found when using the trans/transchoice filters and tags #} + {# messages can be found when using the trans filter and tag #} {% trans %}Symfony is great{% endtrans %} {{ 'Symfony is great'|trans }} - {{ 'Symfony is great'|transchoice(1) }} - - {% transchoice 1 %}Symfony is great{% endtranschoice %} - .. caution:: The extractors can't find messages translated outside templates, like form diff --git a/translation/templates.rst b/translation/templates.rst index 172932e1667..670b840f748 100644 --- a/translation/templates.rst +++ b/translation/templates.rst @@ -24,6 +24,12 @@ The ``transchoice`` tag automatically gets the ``%count%`` variable from the current context and passes it to the translator. This mechanism only works when you use a placeholder following the ``%var%`` pattern. +.. deprecated:: 4.2 + + The ``transchoice`` tag is deprecated since Symfony 4.2 and will be + removed in 5.0. Use the :doc:`ICU MessageFormat ` with + the ``trans`` tag instead. + .. caution:: The ``%var%`` notation of placeholders is required when translating in @@ -64,6 +70,12 @@ texts* and complex expressions: {{ message|transchoice(5, {'%name%': 'Fabien'}, 'app') }} +.. deprecated:: 4.2 + + The ``transchoice`` filter is deprecated since Symfony 4.2 and will be + removed in 5.0. Use the :doc:`ICU MessageFormat ` with + the ``trans`` filter instead. + .. tip:: Using the translation tags or filters have the same effect, but with From 6ec443f6838ae71d15319aaffac57974744cac1a Mon Sep 17 00:00:00 2001 From: Wouter J Date: Sat, 11 May 2019 20:02:29 +0200 Subject: [PATCH 4/6] Fixes after thorough reviews! --- translation/message_format.rst | 128 +++++++++++++++++---------------- translation/templates.rst | 2 +- 2 files changed, 66 insertions(+), 64 deletions(-) diff --git a/translation/message_format.rst b/translation/message_format.rst index ba69b6f3f6c..563a45fabcf 100644 --- a/translation/message_format.rst +++ b/translation/message_format.rst @@ -1,7 +1,7 @@ .. index:: single: Translation; Message Format -How to translate messages using the ICU MessageFormat +How to Translate Messages using the ICU MessageFormat ===================================================== .. versionadded:: 4.2 @@ -9,7 +9,7 @@ How to translate messages using the ICU MessageFormat Support for ICU MessageFormat was introduced in Symfony 4.2. Messages (i.e. strings) in applications are almost never completely static. -They contain variables or other complexer logic like pluralization. In order to +They contain variables or other complex logic like pluralization. In order to handle this, the Translator component supports the `ICU MessageFormat`_ syntax. Using the ICU Message Format @@ -34,7 +34,7 @@ All messages in this file will now be processed by the Message Placeholders -------------------- -The basic usage of the MessageFormat allows you to use placeholder (called +The basic usage of the MessageFormat allows you to use placeholders (called *arguments* in ICU MessageFormat) in your messages: .. configuration-block:: @@ -67,17 +67,19 @@ The basic usage of the MessageFormat allows you to use placeholder (called ]; Everything within the curly braces (``{...}``) is processed by the formatter -and replaced by it's placeholder:: +and replaced by its placeholder:: - // ... - echo $translator->trans('say_hello', ['name' => 'Fabien']); // Hello Fabien! - echo $translator->trans('say_hello', ['name' => 'Symfony']); // Hello Symfony! + // prints "Hello Fabien!" + echo $translator->trans('say_hello', ['name' => 'Fabien']); + + // prints "Hello Symfony!" + echo $translator->trans('say_hello', ['name' => 'Symfony']); Selecting Different Messages Based on a Condition ------------------------------------------------- The curly brace syntax allows to "modify" the output of the variable. One of -these functions is the ``switch`` function. It acts like PHP's `switch statement`_ +these functions is the ``select`` function. It acts like PHP's `switch statement`_ and allows to use different strings based on the value of the variable. A typical usage of this is gender: @@ -102,7 +104,7 @@ typical usage of this is gender: invitation_title - {organizer_gender, select, female {{organizer_name} has invited you for her party!} male {{organizer_name} has invited you for his party!} other {{organizer_name} have invited you for their party!}} + {organizer_gender, select, female {{organizer_name} has invited you for her party!} male {{organizer_name} has invited you for his party!} other {{organizer_name} have invited you for their party!}} @@ -120,12 +122,11 @@ typical usage of this is gender: ]; This might look very complex. The basic syntax for all functions is -``{variable_name, function_name, function_statement}``. In this case, the -function name is ``select`` and its statement contains the "cases" of this +``{variable_name, function_name, function_statement}`` (where, as you see +later, ``function_statement`` is optional for some functions). In this case, +the function name is ``select`` and its statement contains the "cases" of this select. This function is applied over the ``organizer_gender`` variable:: - // ... - // prints "Ryan has invited you for his party!" echo $translator->trans('invition_title', [ 'organizer_name' => 'Ryan', @@ -154,23 +155,23 @@ you to use literal text in the select statements: in the switch statement, it is better to use "complex arguments" at the outermost structure of the message. The strings are in this way better readable for translators and, as you can see in the ``other`` case, other - parts of the sentence mgith be influenced by the variable. + parts of the sentence might be influenced by the variables. Pluralization ------------- Another interesting function is ``plural``. It allows you to handle pluralization in your messages (e.g. ``There are 3 apples`` vs -``There is one apple``). The function looks very similair to the ``select`` function: +``There is one apple``). The function looks very similar to the ``select`` function: .. configuration-block:: .. code-block:: yaml # translations/messages.en.yaml - nr_of_apples: > + num_of_apples: > {apples, plural, - =0 {There are no apples :(} + =0 {There are no apples} one {There is one apple...} other {There are # apples!} } @@ -182,9 +183,9 @@ handle pluralization in your messages (e.g. ``There are 3 apples`` vs - - nr_of_apples - {apples, plural, =0 {There are no apples :(} one {There is one apple...} other {There are # apples!}} + + num_of_apples + {apples, plural, =0 {There are no apples} one {There is one apple...} other {There are # apples!}} @@ -194,43 +195,32 @@ handle pluralization in your messages (e.g. ``There are 3 apples`` vs // translations/messages.en.php return [ - 'nr_of_apples' => '{apples, plural, - =0 {There are no apples :(} + 'num_of_apples' => '{apples, plural, + =0 {There are no apples} one {There is one apple...} other {There are # apples!} }', ]; Pluralization rules are actually quite complex and differ for each language. -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 - ); - -In order to facilitate this, the possible cases in the ``plural`` function are -also different for each language. For instance, Russian has ``one``, ``few``, -``many`` and ``other``, while English has only ``one`` and ``other``. The full -list of possible cases can be found in Unicode's `Language Plural Rules`_ -document. By prefixing with ``=``, you can match exact values (like ``0`` in -the above example). +For instance, Russian uses different plural forms for numbers ending with 1; +numbers ending with 2, 3 or 4; numbers ending with 5, 6, 7, 8 or 9; and even +some exceptions of this! -Usage of this string is the same as with variables and select:: +In order to properly translate this, the possible cases in the ``plural`` +function are also different for each language. For instance, Russian has +``one``, ``few``, ``many`` and ``other``, while English has only ``one`` and +``other``. The full list of possible cases can be found in Unicode's +`Language Plural Rules`_ document. By prefixing with ``=``, you can match exact +values (like ``0`` in the above example). - // ... +Usage of this string is the same as with variables and select:: // prints "There is one apple..." - echo $translator->trans('nr_of_apples', ['apples' => 1]); + echo $translator->trans('num_of_apples', ['apples' => 1]); // prints "There are 23 apples!" - echo $translator->trans('nr_of_apples', ['apples' => 23]); + echo $translator->trans('num_of_apples', ['apples' => 23]); .. note:: @@ -269,15 +259,15 @@ Usage of this string is the same as with variables and select:: } } -Other Placeholder Functions ---------------------------- +Additional Placeholder Functions +-------------------------------- -Besides these, the MessageFormat comes with a couple other interesting functions. +Besides these, the ICU MessageFormat comes with a couple other interesting functions. Ordinal ~~~~~~~ -Similair to ``plural``, ``selectordinal`` allows you to use numbers as ordinal scale: +Similar to ``plural``, ``selectordinal`` allows you to use numbers as ordinal scale: .. configuration-block:: @@ -286,12 +276,16 @@ Similair to ``plural``, ``selectordinal`` allows you to use numbers as ordinal s # translations/messages.en.yaml finish_place: > You finished {place, selectordinal, - one {#st} - two {#nd} - few {#rd} + one {#st} + two {#nd} + few {#rd} other {#th} }! + # when only formatting the number as ordinal (like above), you can also + # use the `ordinal` function: + finish_place: You finished {place, ordinal}! + .. code-block:: xml @@ -301,7 +295,14 @@ Similair to ``plural``, ``selectordinal`` allows you to use numbers as ordinal s finish_place - {apples, plural, =0 {There are no apples :(} one {There is one apple...} other {There are # apples!}} + You finished {place, selectordinal, one {#st} two {#nd} few {#rd} other {#th}}! + + + + + finish_place + You finished {place, ordinal}! @@ -317,12 +318,14 @@ Similair to ``plural``, ``selectordinal`` allows you to use numbers as ordinal s few {#rd} other {#th} }!', + + // when only formatting the number as ordinal (like above), you can + // also use the `ordinal` function: + 'finish_place' => 'You finished {place, ordinal}!', ]; .. code-block:: php - // ... - // prints "You finished 1st!" echo $translator->trans('finish_place', ['place' => 1]); @@ -345,7 +348,7 @@ using the :phpclass:`IntlDateFormatter`: .. code-block:: yaml # translations/messages.en.yaml - published_at: 'Published at {time, date} - {time, time, short}' + published_at: 'Published at {publication_date, date} - {publication_date, time, short}' .. code-block:: xml @@ -356,7 +359,7 @@ using the :phpclass:`IntlDateFormatter`: published_at - Published at {time, date} - {time, time, short} + Published at {publication_date, date} - {publication_date, time, short} @@ -366,15 +369,16 @@ using the :phpclass:`IntlDateFormatter`: // translations/messages.en.php return [ - 'published_at' => 'Published at {time, date} - {time, time, short}', + 'published_at' => 'Published at {publication_date, date} - {publication_date, time, short}', ]; -.. code-block:: php +The "function statement" for the ``time`` and ``date`` functions can be one of +short, medium, long or full, as documented on PHP.net. - // ... +.. code-block:: php // prints "Published at Jan 25, 2019 - 11:30 AM" - echo $translator->trans('published_at', ['time' => new \DateTime('2019-01-25 11:30:00')]); + echo $translator->trans('published_at', ['publication_date' => new \DateTime('2019-01-25 11:30:00')]); Numbers ~~~~~~~ @@ -419,8 +423,6 @@ The ``number`` formatter allows you to format numbers using Intl's :phpclass:`Nu .. code-block:: php - // ... - // prints "82% of the work is done" echo $translator->trans('progress', ['progress' => 0.82]); // prints "100% of the work is done" diff --git a/translation/templates.rst b/translation/templates.rst index 670b840f748..903f1934d92 100644 --- a/translation/templates.rst +++ b/translation/templates.rst @@ -103,7 +103,7 @@ texts* and complex expressions: .. code-block:: twig - {% trans_default_domain 'app' %} + {% trans_default_domain 'app' %} Note that this only influences the current template, not any "included" template (in order to avoid side effects). From eed64a82dd4fe70e44634b82203a8b62ae203b32 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Sat, 11 May 2019 20:04:47 +0200 Subject: [PATCH 5/6] Add link to usefull online editor --- translation/message_format.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/translation/message_format.rst b/translation/message_format.rst index 563a45fabcf..9e06b3083ef 100644 --- a/translation/message_format.rst +++ b/translation/message_format.rst @@ -12,6 +12,10 @@ Messages (i.e. strings) in applications are almost never completely static. They contain variables or other complex logic like pluralization. In order to handle this, the Translator component supports the `ICU MessageFormat`_ syntax. +.. tip:: + + You can test out examples of the ICU MessageFormatter in this `online editor`_. + Using the ICU Message Format ---------------------------- @@ -433,6 +437,7 @@ The ``number`` formatter allows you to format numbers using Intl's :phpclass:`Nu // "9 988 776,65 €" echo $translator->trans('value_of_object', ['value' => 9988776.65]); +.. _`online editor`: http://format-message.github.io/icu-message-format-for-translators/ .. _`ICU MessageFormat`: http://userguide.icu-project.org/formatparse/messages .. _`switch statement`: https://php.net/control-structures.switch .. _`Language Plural Rules`: http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html From d2736bc48c02a9b8cfe5b3e5cf5509fafad478cc Mon Sep 17 00:00:00 2001 From: Wouter J Date: Tue, 28 May 2019 23:13:08 +0200 Subject: [PATCH 6/6] Yet another great review! --- translation.rst | 2 +- translation/message_format.rst | 38 +++++++++++++++++----------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/translation.rst b/translation.rst index cbb0b212cad..2e1bee7d8a0 100644 --- a/translation.rst +++ b/translation.rst @@ -183,7 +183,7 @@ using the ``trans()`` method: 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. This catalog is cached in production, to + "dictionary" of translations. This catalog is cached in production to minimize performance impact. * If the message is located in the catalog, the translation is returned. If diff --git a/translation/message_format.rst b/translation/message_format.rst index 9e06b3083ef..9879be895a9 100644 --- a/translation/message_format.rst +++ b/translation/message_format.rst @@ -20,7 +20,7 @@ Using the ICU Message Format ---------------------------- In order to use the ICU Message Format, the :ref:`message domain -` has to be suffixed with ``intl-icu``: +` has to be suffixed with ``+intl-icu``: ====================== =============================== Normal file name ICU Message Format filename @@ -45,12 +45,12 @@ The basic usage of the MessageFormat allows you to use placeholders (called .. code-block:: yaml - # translations/messages.en.yaml + # translations/messages+intl-icu.en.yaml say_hello: 'Hello {name}!' .. code-block:: xml - + @@ -65,7 +65,7 @@ The basic usage of the MessageFormat allows you to use placeholders (called .. code-block:: php - // translations/messages.en.php + // translations/messages+intl-icu.en.php return [ 'say_hello' => "Hello {name}!", ]; @@ -91,7 +91,7 @@ typical usage of this is gender: .. code-block:: yaml - # translations/messages.en.yaml + # translations/messages+intl-icu.en.yaml invitation_title: > {organizer_gender, select, female {{organizer_name} has invited you for her party!} @@ -101,7 +101,7 @@ typical usage of this is gender: .. code-block:: xml - + @@ -116,7 +116,7 @@ typical usage of this is gender: .. code-block:: php - // translations/messages.en.php + // translations/messages+intl-icu.en.php return [ 'invitation_title' => '{organizer_gender, select, female {{organizer_name} has invited you for her party!} @@ -172,7 +172,7 @@ handle pluralization in your messages (e.g. ``There are 3 apples`` vs .. code-block:: yaml - # translations/messages.en.yaml + # translations/messages+intl-icu.en.yaml num_of_apples: > {apples, plural, =0 {There are no apples} @@ -182,7 +182,7 @@ handle pluralization in your messages (e.g. ``There are 3 apples`` vs .. code-block:: xml - + @@ -197,7 +197,7 @@ handle pluralization in your messages (e.g. ``There are 3 apples`` vs .. code-block:: php - // translations/messages.en.php + // translations/messages+intl-icu.en.php return [ 'num_of_apples' => '{apples, plural, =0 {There are no apples} @@ -277,7 +277,7 @@ Similar to ``plural``, ``selectordinal`` allows you to use numbers as ordinal sc .. code-block:: yaml - # translations/messages.en.yaml + # translations/messages+intl-icu.en.yaml finish_place: > You finished {place, selectordinal, one {#st} @@ -292,7 +292,7 @@ Similar to ``plural``, ``selectordinal`` allows you to use numbers as ordinal sc .. code-block:: xml - + @@ -314,7 +314,7 @@ Similar to ``plural``, ``selectordinal`` allows you to use numbers as ordinal sc .. code-block:: php - // translations/messages.en.php + // translations/messages+intl-icu.en.php return [ 'finish_place' => 'You finished {place, selectordinal, one {#st} @@ -351,12 +351,12 @@ using the :phpclass:`IntlDateFormatter`: .. code-block:: yaml - # translations/messages.en.yaml + # translations/messages+intl-icu.en.yaml published_at: 'Published at {publication_date, date} - {publication_date, time, short}' .. code-block:: xml - + @@ -371,7 +371,7 @@ using the :phpclass:`IntlDateFormatter`: .. code-block:: php - // translations/messages.en.php + // translations/messages+intl-icu.en.php return [ 'published_at' => 'Published at {publication_date, date} - {publication_date, time, short}', ]; @@ -393,13 +393,13 @@ The ``number`` formatter allows you to format numbers using Intl's :phpclass:`Nu .. code-block:: yaml - # translations/messages.en.yaml + # translations/messages+intl-icu.en.yaml progress: '{progress, number, percent} of the work is done' value_of_object: 'This artifact is worth {value, number, currency}' .. code-block:: xml - + @@ -419,7 +419,7 @@ The ``number`` formatter allows you to format numbers using Intl's :phpclass:`Nu .. code-block:: php - // translations/messages.en.php + // translations/messages+intl-icu.en.php return [ 'progress' => '{progress, number, percent} of the work is done', 'value_of_object' => 'This artifact is worth {value, number, currency}',