diff --git a/_images/translation/debug_1.png b/_images/translation/debug_1.png deleted file mode 100644 index 8f175f4d7ff..00000000000 Binary files a/_images/translation/debug_1.png and /dev/null differ diff --git a/_images/translation/debug_2.png b/_images/translation/debug_2.png deleted file mode 100644 index 04a57fa41d4..00000000000 Binary files a/_images/translation/debug_2.png and /dev/null differ diff --git a/_images/translation/debug_3.png b/_images/translation/debug_3.png deleted file mode 100644 index 6ed595e097b..00000000000 Binary files a/_images/translation/debug_3.png and /dev/null differ diff --git a/_images/translation/debug_4.png b/_images/translation/debug_4.png deleted file mode 100644 index db642b1773f..00000000000 Binary files a/_images/translation/debug_4.png and /dev/null differ diff --git a/translation.rst b/translation.rst index ab9cc438385..13f1ca54ac2 100644 --- a/translation.rst +++ b/translation.rst @@ -48,24 +48,36 @@ to learn even more. Overall, the process has several steps: .. _translation-configuration: +Installation +------------ + +First, run this command to install the translator before using it: + +.. code-block:: terminal + + $ composer require translator + Configuration ------------- -Translations are handled by a ``translator`` service that uses the user's -locale to lookup and return translated messages. Before using it, enable the -``translator`` in your configuration: +The previous command creates an initial config file where you can define the +default locale of the app and the :ref:`fallback locales ` + that will be used if Symfony can't find some translation: .. configuration-block:: .. code-block:: yaml - # app/config/config.yml + # config/packages/translation.yaml framework: - translator: { fallbacks: [en] } + default_locale: 'en' + translator: + fallbacks: ['en'] + # ... .. code-block:: xml - + - + en + .. code-block:: php - // app/config/config.php + // config/packages/translation.php $container->loadFromExtension('framework', array( + 'default_locale' => 'en', 'translator' => array('fallbacks' => array('en')), + // ... )); -See :ref:`translation-fallback` for details on the ``fallbacks`` key -and what Symfony does when it doesn't find a translation. - The locale used in translations is the one stored on the request. This is typically set via a ``_locale`` attribute on your routes (see :ref:`translation-locale-url`). @@ -108,10 +120,11 @@ for example, that you're translating a simple message from inside a controller:: // ... use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\Translation\Translator; - public function indexAction() + public function index(Translator $translator) { - $translated = $this->get('translator')->trans('Symfony is great'); + $translated = $translator->trans('Symfony is great'); return new Response($translated); } @@ -129,7 +142,7 @@ different formats, XLIFF being the recommended format: .. code-block:: xml - + @@ -144,12 +157,12 @@ different formats, XLIFF being the recommended format: .. code-block:: yaml - # messages.fr.yml + # translations/messages.fr.yml Symfony is great: J'aime Symfony .. code-block:: php - // messages.fr.php + // translations/messages.fr.php return array( 'Symfony is great' => 'J\'aime Symfony', ); @@ -186,10 +199,11 @@ Message Placeholders Sometimes, a message containing a variable needs to be translated:: use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\Translation\Translator; - public function indexAction($name) + public function index(Translator $translator, $name) { - $translated = $this->get('translator')->trans('Hello '.$name); + $translated = $translator->trans('Hello '.$name); return new Response($translated); } @@ -336,14 +350,14 @@ Translation Resource/File Names and Locations Symfony looks for message files (i.e. translations) in the following default locations: -* the ``app/Resources/translations`` directory; +* the ``translations/`` directory; -* the ``app/Resources//translations`` directory; +* the ``src/Resources//translations/`` directory; * the ``Resources/translations/`` directory inside of any bundle. The locations are listed here with the highest priority first. That is, you can -override the translation messages of a bundle in any of the top 2 directories. +override the translation messages of a bundle in any of the top two directories. The override mechanism works at a key level: only the overridden keys need to be listed in a higher priority message file. When a key is not found @@ -359,14 +373,14 @@ must be named according to the following path: ``domain.locale.loader``: * **locale**: The locale that the translations are for (e.g. ``en_GB``, ``en``, etc); * **loader**: How Symfony should load and parse the file (e.g. ``xlf``, - ``php``, ``yml``, etc). + ``php``, ``yaml``, 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. +* ``yaml``: YAML 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. @@ -381,15 +395,15 @@ For more options, see :ref:`component-translator-message-catalogs`. .. code-block:: yaml - # app/config/config.yml + # config/packages/translation.yaml framework: translator: paths: - - '%kernel.project_dir%/translations' + - '%kernel.project_dir%/custom/path/to/translations' .. code-block:: xml - + - %kernel.project_dir%/translations + %kernel.project_dir%/custom/path/to/translations .. code-block:: php - // app/config/config.php + // config/packages/translation.php $container->loadFromExtension('framework', array( 'translator' => array( 'paths' => array( - '%kernel.project_dir%/translations', + '%kernel.project_dir%/custom/path/to/translations', ), ), )); @@ -455,7 +469,7 @@ checks translation resources for several locales: .. note:: - When Symfony doesn't find a translation in the given locale, it will + When Symfony can't find a translation in the given locale, it will add the missing translation to the log file. For details, see :ref:`reference-framework-translator-logging`. @@ -504,9 +518,10 @@ Learn more .. toctree:: :maxdepth: 1 - :glob: - /translation/* + translation/locale + translation/debug + translation/lint .. _`i18n`: https://en.wikipedia.org/wiki/Internationalization_and_localization .. _`ISO 3166-1 alpha-2`: https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes diff --git a/translation/debug.rst b/translation/debug.rst index 5ff68533669..b669a3197f7 100644 --- a/translation/debug.rst +++ b/translation/debug.rst @@ -8,52 +8,50 @@ How to Find Missing or Unused Translation Messages When maintaining an application or bundle, you may add or remove translation messages and forget to update the message catalogues. The ``debug:translation`` -command helps you to find these missing or unused translation messages. +command helps you to find these missing or unused translation messages templates: -Thanks to the messages extractors, the command will detect the translation -tag or filter usages in Twig templates: - -.. code-block:: jinja +.. configuration-block:: - {% trans %}Symfony is great{% endtrans %} + .. code-block:: twig - {{ 'Symfony is great'|trans }} + {# messages can be found when using the trans/transchoice filters and tags #} + {% trans %}Symfony is great{% endtrans %} - {{ 'Symfony is great'|transchoice(1) }} + {{ 'Symfony is great'|trans }} - {% transchoice 1 %}Symfony is great{% endtranschoice %} + {{ 'Symfony is great'|transchoice(1) }} -It will also detect the following translator usages in PHP templates: + {% transchoice 1 %}Symfony is great{% endtranschoice %} -.. code-block:: php + .. code-block:: php - $view['translator']->trans("Symfony is great"); + // messages can be found when using the trans() and transChoice() methods + $view['translator']->trans("Symfony is great"); - $view['translator']->transChoice('Symfony is great', 1); + $view['translator']->transChoice('Symfony is great', 1); .. caution:: - The extractors are not able to inspect the messages translated outside - templates which means that translator usages in form labels or inside - your controllers won't be detected. Dynamic translations involving variables - or expressions are not detected in templates, which means this example - won't be analyzed: + The extractors can't find messages translated outside templates, like form + labels or controllers. Dynamic translations using variables or expressions + in templates are not detected either: - .. code-block:: jinja + .. code-block:: twig + {# this translation uses a Twig variable, so it won't be detected #} {% set message = 'Symfony is great' %} {{ message|trans }} Suppose your application's default_locale is ``fr`` and you have configured ``en`` as the fallback locale (see :ref:`translation-configuration` and :ref:`translation-fallback` for how to configure these). And suppose -you've already setup some translations for the ``fr`` locale inside an AcmeDemoBundle: +you've already setup some translations for the ``fr`` locale: .. configuration-block:: .. code-block:: xml - + @@ -69,12 +67,12 @@ you've already setup some translations for the ``fr`` locale inside an AcmeDemoB .. code-block:: yaml - # src/Acme/AcmeDemoBundle/Resources/translations/messages.fr.yml + # translations/messages.fr.yaml Symfony is great: J'aime Symfony .. code-block:: php - // src/Acme/AcmeDemoBundle/Resources/translations/messages.fr.php + // translations/messages.fr.php return array( 'Symfony is great' => 'J\'aime Symfony', ); @@ -85,7 +83,7 @@ and for the ``en`` locale: .. code-block:: xml - + @@ -100,26 +98,32 @@ and for the ``en`` locale: .. code-block:: yaml - # src/Acme/AcmeDemoBundle/Resources/translations/messages.en.yml + # translations/messages.en.yaml Symfony is great: Symfony is great .. code-block:: php - // src/Acme/AcmeDemoBundle/Resources/translations/messages.en.php + // translations/messages.en.php return array( 'Symfony is great' => 'Symfony is great', ); -To inspect all messages in the ``fr`` locale for the AcmeDemoBundle, run: +To inspect all messages in the ``fr`` locale for the application, run: .. code-block:: terminal - $ php bin/console debug:translation fr AcmeDemoBundle + $ php bin/console debug:translation fr -You will get this output: + +----------+------------------+----------------------+-------------------------------+ + | State(s) | Id | Message Preview (fr) | Fallback Message Preview (en) | + +----------+------------------+----------------------+-------------------------------+ + | o | Symfony is great | J'aime Symfony | Symfony is great | + +----------+------------------+----------------------+-------------------------------+ -.. image:: /_images/translation/debug_1.png - :align: center + Legend: + x Missing message + o Unused message + = Same as the fallback message It shows you a table with the result when translating the message in the ``fr`` locale and the result when the fallback locale ``en`` would be used. On top @@ -131,8 +135,20 @@ because it is translated, but you haven't used it anywhere yet. Now, if you translate the message in one of your templates, you will get this output: -.. image:: /_images/translation/debug_2.png - :align: center +.. code-block:: terminal + + $ php bin/console debug:translation fr + + +----------+------------------+----------------------+-------------------------------+ + | State(s) | Id | Message Preview (fr) | Fallback Message Preview (en) | + +----------+------------------+----------------------+-------------------------------+ + | | Symfony is great | J'aime Symfony | Symfony is great | + +----------+------------------+----------------------+-------------------------------+ + + Legend: + x Missing message + o Unused message + = Same as the fallback message The state is empty which means the message is translated in the ``fr`` locale and used in one or more templates. @@ -140,8 +156,20 @@ and used in one or more templates. If you delete the message ``Symfony is great`` from your translation file for the ``fr`` locale and run the command, you will get: -.. image:: /_images/translation/debug_3.png - :align: center +.. code-block:: terminal + + $ php bin/console debug:translation fr + + +----------+------------------+----------------------+-------------------------------+ + | State(s) | Id | Message Preview (fr) | Fallback Message Preview (en) | + +----------+------------------+----------------------+-------------------------------+ + | x = | Symfony is great | J'aime Symfony | Symfony is great | + +----------+------------------+----------------------+-------------------------------+ + + Legend: + x Missing message + o Unused message + = Same as the fallback message The state indicates the message is missing because it is not translated in the ``fr`` locale but it is still used in the template. Moreover, the message @@ -152,8 +180,20 @@ the ``en`` locale. If you copy the content of the translation file in the ``en`` locale, to the translation file in the ``fr`` locale and run the command, you will get: -.. image:: /_images/translation/debug_4.png - :align: center +.. code-block:: terminal + + $ php bin/console debug:translation fr + + +----------+------------------+----------------------+-------------------------------+ + | State(s) | Id | Message Preview (fr) | Fallback Message Preview (en) | + +----------+------------------+----------------------+-------------------------------+ + | = | Symfony is great | J'aime Symfony | Symfony is great | + +----------+------------------+----------------------+-------------------------------+ + + Legend: + x Missing message + o Unused message + = Same as the fallback message You can see that the translations of the message are identical in the ``fr`` and ``en`` locales which means this message was probably copied from French @@ -164,13 +204,13 @@ domain: .. code-block:: terminal - $ php bin/console debug:translation en AcmeDemoBundle --domain=messages + $ php bin/console debug:translation en --domain=messages -When bundles have a lot of messages, it is useful to display only the unused -or only the missing messages, by using the ``--only-unused`` or ``--only-missing`` -switches: +When the application has a lot of messages, it is useful to display only the +unused or only the missing messages, by using the ``--only-unused`` or +``--only-missing`` options: .. code-block:: terminal - $ php bin/console debug:translation en AcmeDemoBundle --only-unused - $ php bin/console debug:translation en AcmeDemoBundle --only-missing + $ php bin/console debug:translation en --only-unused + $ php bin/console debug:translation en --only-missing diff --git a/translation/lint.rst b/translation/lint.rst index 0c64e4a427e..9fce4198f8a 100644 --- a/translation/lint.rst +++ b/translation/lint.rst @@ -15,21 +15,16 @@ translation file using the ``lint:yaml`` and ``lint:xliff`` commands: .. code-block:: terminal # lint a single file - $ ./bin/console lint:yaml app/Resources/translations/messages.en.yml - $ ./bin/console lint:xliff app/Resources/translations/messages.en.xlf + $ ./bin/console lint:yaml translations/messages.en.yml + $ ./bin/console lint:xliff translations/messages.en.xlf # lint a whole directory - $ ./bin/console lint:yaml app/Resources/translations - $ ./bin/console lint:xliff app/Resources/translations - - # lint a specific bundle - $ ./bin/console lint:yaml @AppBundle - $ ./bin/console lint:xliff @AppBundle + $ ./bin/console lint:yaml translations + $ ./bin/console lint:xliff translations The linter results can be exported to JSON using the ``--format`` option: .. code-block:: terminal - # lint a single file - $ ./bin/console lint:yaml app/Resources/translations --format=json - $ ./bin/console lint:xliff app/Resources/translations --format=json + $ ./bin/console lint:yaml translations/ --format=json + $ ./bin/console lint:xliff translations/ --format=json diff --git a/translation/locale.rst b/translation/locale.rst index d15e441b608..f81a57911e3 100644 --- a/translation/locale.rst +++ b/translation/locale.rst @@ -9,7 +9,7 @@ via the ``Request`` object:: use Symfony\Component\HttpFoundation\Request; - public function indexAction(Request $request) + public function index(Request $request) { $locale = $request->getLocale(); } @@ -61,8 +61,8 @@ by the routing system using the special ``_locale`` parameter: # config/routes.yaml contact: - path: /{_locale}/contact - defaults: { _controller: AppBundle:Contact:index } + path: /{_locale}/contact + controller: App\Controller\ContactContorller::index requirements: _locale: en|fr|de @@ -76,7 +76,7 @@ by the routing system using the special ``_locale`` parameter: http://symfony.com/schema/routing/routing-1.0.xsd"> - AppBundle:Contact:index + App\Controller\ContactContorller::index en|fr|de @@ -86,12 +86,13 @@ by the routing system using the special ``_locale`` parameter: // config/routes.php use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Route; + use App\Controller\ContactContorller; $collection = new RouteCollection(); $collection->add('contact', new Route( '/{_locale}/contact', array( - '_controller' => 'AppBundle:Contact:index', + '_controller' => array(ContactController::class, 'index']), ), array( '_locale' => 'en|fr|de', @@ -130,13 +131,13 @@ the framework: .. code-block:: yaml - # app/config/config.yml + # config/packages/translation.yaml framework: default_locale: en .. code-block:: xml - + loadFromExtension('framework', array( 'default_locale' => 'en', ));