{"id":8719,"date":"2023-06-16T16:22:00","date_gmt":"2023-06-16T14:22:00","guid":{"rendered":"https:\/\/phraseapp.com\/blog\/?p=4475"},"modified":"2023-08-10T11:43:22","modified_gmt":"2023-08-10T09:43:22","slug":"symfony-4-i18n","status":"publish","type":"post","link":"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/","title":{"rendered":"Symfony Internationalization: A Step-by-Step Guide"},"content":{"rendered":"\n<div id=\"acf\/text-block_2f86e2d18a499e7cd2fe5f103f86eeb6\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Symfony is one of the oldest and most mature PHP Web Frameworks, with a solid user base and extensive documentation. Symfony also has first-class i18n (internationalization) support built right in. However, Symfony i18n can a bit daunting in the beginning. No worries, though. We\u2019re here to help.<\/p>\n<p>In this guide we will build a demo app and localize it using the official <a href=\"https:\/\/symfony.com\/doc\/current\/translation.html\">Symfony Translations<\/a> module. Additionally, we will walk through translating your own data in the database layer using the <a href=\"https:\/\/github.com\/KnpLabs\/DoctrineBehaviors\">Doctrine Behaviors<\/a> bundle. By the end of this guide you will become a Symfony 6 i18n sensei. Let\u2019s get started.<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_69_1 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Overview<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#bundles-used-in-this-guide\" title=\"Bundles used in this guide\">Bundles used in this guide<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#our-demo-app\" title=\"Our demo app\">Our demo app<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#setting-up-the-blog\" title=\"Setting up the blog\">Setting up the blog<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#creating-our-home-page-controller\" title=\"Creating our home page controller\">Creating our home page controller<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#how-do-i-set-up-the-symfony-translations-bundle\" title=\"How do I set up the Symfony Translations bundle?\">How do I set up the Symfony Translations bundle?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#configuration-active-locale\" title=\"Configuration: Active locale\">Configuration: Active locale<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#how-do-i-work-with-basic-translations\" title=\"How do I work with basic translations?\">How do I work with basic translations?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#how-do-i-use-the-translatorinterface\" title=\"How do I use the TranslatorInterface?\">How do I use the TranslatorInterface?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#using-keywords\" title=\"Using keywords\">Using keywords<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#using-variables\" title=\"Using variables\">Using variables<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#forcing-a-locale\" title=\"Forcing a locale\">Forcing a locale<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#how-do-i-work-with-translation-files\" title=\"How do I work with translation files?\">How do I work with translation files?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#extracting-translations-from-code\" title=\"Extracting translations from code\">Extracting translations from code<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-14\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#how-do-i-work-with-icu-messages\" title=\"How do I work with ICU messages?\">How do I work with ICU messages?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-15\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#how-do-i-add-dynamic-values-to-translation-messages\" title=\"How do I add dynamic values to translation messages?\">How do I add dynamic values to translation messages?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-16\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#how-do-i-work-with-plural-messages\" title=\"How do I work with plural messages?\">How do I work with plural messages?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-17\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#complex-plurals\" title=\"Complex plurals\">Complex plurals<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-18\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#how-do-i-localize-dates\" title=\"How do I localize dates?\">How do I localize dates?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-19\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#how-do-i-localize-numbers\" title=\"How do I localize numbers?\">How do I localize numbers?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-20\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#how-do-i-get-or-set-the-active-locale\" title=\"How do I get or set the active locale?\">How do I get or set the active locale?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-21\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#how-do-i-configure-fallback-locales\" title=\"How do I configure fallback locales?\">How do I configure fallback locales?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-22\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#how-do-i-support-right-to-left-languages\" title=\"How do I support right-to-left languages?\">How do I support right-to-left languages?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-23\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#how-do-i-create-localized-routes\" title=\"How do I create localized routes?\">How do I create localized routes?<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-24\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#what-is-a-localized-route\" title=\"What is a localized route?\">What is a localized route?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-25\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#using-a-locale-prefix\" title=\"Using a locale prefix\">Using a locale prefix<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-26\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#using-a-different-path-per-locale\" title=\"Using a different path per locale\">Using a different path per locale<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-27\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#forcing-a-locale-in-a-url\" title=\"Forcing a locale in a URL\">Forcing a locale in a URL<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-28\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#forcing-a-localized-home-route\" title=\"Forcing a localized home route\">Forcing a localized home route<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-29\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#how-do-i-automatically-determine-the-users-locale\" title=\"How do I automatically determine the user\u2019s locale?\">How do I automatically determine the user\u2019s locale?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-30\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#how-do-i-create-a-language-switcher\" title=\"How do I create a language switcher?\">How do I create a language switcher?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-31\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#how-do-i-localize-my-data\" title=\"How do I localize my data?\">How do I localize my data?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-32\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#updating-translatable-models\" title=\"Updating translatable models\">Updating translatable models<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-33\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#how-do-i-accept-form-data-for-a-localized-model\" title=\"How do I accept form data for a localized model?\">How do I accept form data for a localized model?<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-34\" href=\"https:\/\/phrase.com\/blog\/posts\/symfony-4-i18n\/#take-symfony-localization-to-the-next-level\" title=\"Take Symfony localization to the next level\">Take Symfony localization to the next level<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"bundles-used-in-this-guide\"><\/span>Bundles used in this guide<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We\u2019ll use the following bundles to build our demo (versions in parentheses):<\/p>\n<ul>\n<li><code>symfony\/translation<\/code> (v6.2) \u2014 Adds support for Translation messages in Symfony.<\/li>\n<li><code>annotations<\/code> (v2.0) \u2014\u00a0Allows you to use PHP comment annotations in code when declaring routes and paths instead of instead of editing yaml files. We\u2019ll use this for localized routing.<\/li>\n<li>We will be using twig for view templates, and we need a couple of twig add-on bundles to handle i18n filters and functions:\n<ul>\n<li><code>twig<\/code> (v3.0)<\/li>\n<li><code>twig\/string-extra<\/code> (v3.5)<\/li>\n<li><code>twig\/intl-extra<\/code> (v3.5)<\/li>\n<\/ul>\n<\/li>\n<li><code>symfony\/webpack-encore-bundle<\/code> (v1.16) \u2014\u00a0For adding Tailwind CSS to our project for styling the website.<\/li>\n<li><code>knplabs\/doctrine-behaviors<\/code> (v2.6) \u2014\u00a0This is needed for database translations.<\/li>\n<\/ul>\n<p>\ud83d\udd17 Resource \u00bb The code for this guide will be <a href=\"https:\/\/github.com\/PhraseApp-Blog\/symfony-6-i18n\/tree\/main\">available on GitHub<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"our-demo-app\"><\/span>Our demo app<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We are building an Indie Games blog that hosts reviews of popular Independent video games. Here is a sneak peak of the final design:<\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-54990\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/indie-game-blog-final-design-screen-1-1.jpg\" alt=\"Indie game blog final design screen | Phrase\" width=\"2560\" height=\"1361\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/indie-game-blog-final-design-screen-1-1.jpg 2560w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/indie-game-blog-final-design-screen-1-1-300x159.jpg 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/indie-game-blog-final-design-screen-1-1-1024x544.jpg 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/indie-game-blog-final-design-screen-1-1-768x408.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/indie-game-blog-final-design-screen-1-1-1536x817.jpg 1536w\" sizes=\"(max-width: 2560px) 100vw, 2560px\" \/><\/p>\n<p>Using the <a href=\"https:\/\/symfony.com\/download#step-1-install-symfony-cli\">Symfony-CLI<\/a>, let\u2019s create the site using the following command:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">$ symfony new indiegamesblog --version=<span class=\"hljs-string\">\"6.3.*@dev\"<\/span> --webapp<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_ddb69016117bcfe6ebba255872da4fb2\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<h2><span class=\"ez-toc-section\" id=\"setting-up-the-blog\"><\/span>Setting up the blog<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We\u2019ll use <a href=\"https:\/\/getcomposer.org\/\">Composer<\/a> to install the base bundles that we need for now. We will be installing the rest as we go along:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">$ composer require twig symfony\/webpack-encore-bundle annotations<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_bc13da60ba0c0440a5aa65bb8394d9b8\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><span role=\"img\" aria-label=\"\ud83d\uddd2\ufe0f\">\ud83d\uddd2\ufe0f<\/span> <span class=\"notion-enable-hover\" data-token-index=\"1\">Note \u00bb <\/span>If you\u2019re coding along with us, you might want to <a class=\"notion-link-token notion-focusable-token notion-enable-hover\" tabindex=\"0\" href=\"https:\/\/tailwindcss.com\/docs\/guides\/symfony\" rel=\"noopener noreferrer\" data-token-index=\"3\"><span class=\"link-annotation-unknown-block-id-1183083479\">install and configure Tailwind CSS<\/span><\/a> at this point. Again, this is only for styling, and it\u2019s an optional step.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"creating-our-home-page-controller\"><\/span>Creating our home page controller<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Let\u2019s create our home page. Create a <code>HomeController.php<\/code> file inside the <code>\/src\/Controller<\/code> folder, and add the following code to it.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-meta\">&lt;?php<\/span>\n\n<span class=\"hljs-comment\">\/\/ src\/Controller\/HomeController.php<\/span>\n\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Controller<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Component<\/span>\\<span class=\"hljs-title\">HttpFoundation<\/span>\\<span class=\"hljs-title\">Response<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Component<\/span>\\<span class=\"hljs-title\">Routing<\/span>\\<span class=\"hljs-title\">Annotation<\/span>\\<span class=\"hljs-title\">Route<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Bundle<\/span>\\<span class=\"hljs-title\">FrameworkBundle<\/span>\\<span class=\"hljs-title\">Controller<\/span>\\<span class=\"hljs-title\">AbstractController<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HomeController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">AbstractController<\/span>\n<\/span>{\n    <span class=\"hljs-comment\">#&#91;Route('\/', name: 'home')]<\/span>\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">index<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">Response<\/span>\n    <\/span>{         \n        $messages = &#91;\n            <span class=\"hljs-string\">'title'<\/span> =&gt; <span class=\"hljs-string\">'Welcome to Indie Games Blog'<\/span>,\n\t\t\t\t\t\t<span class=\"hljs-string\">'subtitle'<\/span> =&gt; <span class=\"hljs-string\">'Honest Indie game reviews since 2010'<\/span>,\n            <span class=\"hljs-string\">'heading'<\/span> =&gt; <span class=\"hljs-string\">'Recent Game Reviews'<\/span>,\n            <span class=\"hljs-string\">'visitors'<\/span> =&gt; <span class=\"hljs-string\">'There are 156 visitors online'<\/span>,\n        ];\n        \n\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">$this<\/span>-&gt;render(<span class=\"hljs-string\">'home.html.twig'<\/span>, &#91;\n            <span class=\"hljs-string\">'messages'<\/span> =&gt; $messages,\n        ]);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_88afe622e2933db5ebf86ca6f32741d6\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>We will also need a home template to render the page. You can create one inside the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">\/templates<\/span> folder. We will hard-code some mock game reviews until we get the database sorted a bit later.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">{# templates\/home.html.twig #}\n\n{% extends \"base.html.twig\" %}\n\n{% block body %}\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"container mx-sm-auto\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">section<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>{{ messages&#91;'title'] }}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>{{ messages&#91;'subtitle'] }}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">section<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span>&gt;<\/span>{{ messages&#91;'heading'] }}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>\n            <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"background-image: url('https:\/\/media.indiedb.com\/images\/members\/5\/4686\/4685285\/profile\/Logo.png'); background-position: center center; background-blend-mode: multiply; background-size: cover;\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n              <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>Strategy<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span>&gt;<\/span>Candy Shop Rush<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n              A candy shop assistant simulation game, where your goal \n              is to fulfill all the customers' requests as quickly as\n              possible!\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">rel<\/span>=<span class=\"hljs-string\">\"noopener noreferrer\"<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"#\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>Read more<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n              <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>by Theo Despoudis<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n              <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n              <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>3 min read<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ul<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n{% endblock %}\n\n{% block footer %}\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">li<\/span>&gt;<\/span>{{ messages&#91;'visitors'] |trans }}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ul<\/span>&gt;<\/span>\n{% endblock %}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_339f790fb2b46164853894825096dded\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\ud83d\uddd2\ufe0f Note \u00bb We\u2019ve removed most CSS styles from our code listings for brevity. Get the entire demo code, including CSS, from <a href=\"https:\/\/github.com\/PhraseApp-Blog\/symfony-6-i18n\">our GitHub repo<\/a>.<\/p>\n<p>Let\u2019s also modify the <code>base.html.twig<\/code> to include header and footer sections:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">{# templates\/base.html.twig #}\n\n <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n   {% include \"Layout\/header.html.twig\" %}\n   <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n     {% block body %}{% endblock %}\n   <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n   <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">footer<\/span>&gt;<\/span>\n     {% block footer %}{% endblock %}\n   <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">footer<\/span>&gt;<\/span>\n <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">body<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_6c73f3137163679c58a7d98b6ab115d0\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Our <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">header<\/span> is just a simple nav bar for now:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">{# templates\/Layout\/header.html.twig #}\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">header<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"bg-black\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">nav<\/span> <span class=\"hljs-attr\">aria-label<\/span>=<span class=\"hljs-string\">\"Global\"<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"#\"<\/span>&gt;<\/span>Games<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"#\"<\/span>&gt;<\/span>Reviews<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"#\"<\/span>&gt;<\/span>About<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> &gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">a<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"#\"<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"{{ path('home') }}\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"sr-only\"<\/span>&gt;<\/span>Indie Games Blog<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"{{ asset('build\/images\/console-controller.png') }}\"<\/span> <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">\"...\"<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">nav<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">header<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_4d76e4b7d068711a628f296edc34abc3\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>You should now be able to start the server and see the page rendering as expected:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-60380 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Indie-game-blog-final-design-screen-scaled.jpg\" alt=\"Indie game blog final design screen | Phrase\" width=\"2560\" height=\"1360\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Indie-game-blog-final-design-screen-scaled.jpg 2560w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Indie-game-blog-final-design-screen-300x159.jpg 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Indie-game-blog-final-design-screen-1024x544.jpg 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Indie-game-blog-final-design-screen-768x408.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Indie-game-blog-final-design-screen-1536x816.jpg 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Indie-game-blog-final-design-screen-2048x1088.jpg 2048w\" sizes=\"(max-width: 2560px) 100vw, 2560px\" \/><\/p>\n<p>Now let\u2019s start setting up our locales and translations.<\/p>\n<p>&nbsp;<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-set-up-the-symfony-translations-bundle\"><\/span>How do I set up the Symfony Translations bundle?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>First, we install the bundle via composer:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">$ composer <span class=\"hljs-keyword\">require<\/span> symfony\/translation<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_4914dc65faa9021337e7f41f997076be\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<h3><span class=\"ez-toc-section\" id=\"configuration-active-locale\"><\/span>Configuration: Active locale<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>The <code>symfony\/translations<\/code> bundle allows us to specify the active locale on load: when a visitor first navigates to a page, they\u2019ll see the page translated in <em>this<\/em> language. This is configured in <code>config\/packages\/translation.yml<\/code>.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\"><span class=\"hljs-comment\"># config\/packages\/translation.yaml<\/span>\n\nframework:\n    default_locale: en\n    translator:\n        default_path: <span class=\"hljs-string\">'%kernel.project_dir%\/translations'<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_b5f2fb627d73014a6deafb6f99e659c5\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>The <code>default_path<\/code> parameter here is used to configure the location of the translation messages. The framework will expand the variable <code>%kernel.project_dir%<\/code> (which is defined as the root project path) and look inside the <code>\/translations<\/code> path for any defined translations. You can also specify multiple paths to look for using a <code>paths<\/code> field that accepts a list of path values.<\/p>\n<p>&nbsp;<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-work-with-basic-translations\"><\/span>How do I work with basic translations?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Now that we have the locales configured, we can start marking our strings for translation. We use the translator service to wrap all the strings with the <code>trans<\/code> method.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ src\/Controller\/HomeController.php<\/span>\n\n<span class=\"hljs-comment\">\/\/ \ud83d\udc47 This service is used to make the strings discoverable <\/span>\n<span class=\"hljs-comment\">\/\/    and translatable inside templates.<\/span>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Contracts<\/span>\\<span class=\"hljs-title\">Translation<\/span>\\<span class=\"hljs-title\">TranslatorInterface<\/span>;\n\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n\n<span class=\"hljs-comment\">\/\/ We inject the service interface as a parameter \ud83d\udc47<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">index<\/span><span class=\"hljs-params\">(TranslatorInterface $translator)<\/span>\n<\/span>{\n    $messages = &#91;\n        <span class=\"hljs-string\">'title'<\/span> =&gt; $translator-&gt;trans(<span class=\"hljs-string\">'Welcome to Indie Games Blog'<\/span>),\n        <span class=\"hljs-string\">'subtitle'<\/span> =&gt; $translator-&gt;trans(<span class=\"hljs-string\">'Honest Indie game reviews since 2010'<\/span>),\n        <span class=\"hljs-comment\">\/\/ ...<\/span>\n    ];\n\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">$this<\/span>-&gt;render(<span class=\"hljs-string\">'home.html.twig'<\/span>, &#91;\n        <span class=\"hljs-string\">'messages'<\/span> =&gt; $messages,\n    ]);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_bfb9b0a4bdb420ee91912298d96fe8cc\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>We then add the twig <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">trans<\/span> filter after each message string. Let\u2019s update top section of the blog page as an example:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">{# \/templates\/home.html.twig #}\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">section<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\t\t    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>{{ messages&#91;'title'] | trans }}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n\t\t    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>{{ messages&#91;'subtitle'] |trans }}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">section<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_f81086273ffe679ddd4a185656685404\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>You shouldn\u2019t see any difference in the site now, but underneath the hood we\u2019ve just internationalized the blog\u2019s front-end. We can provide a translation file (with messages) for each locale, and see these translations at runtime. We\u2019ll do this shortly.<\/p>\n<p>First, let\u2019s see the different ways to use the <code>TranslatorInterface<\/code> we just saw.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-use-the-translatorinterface\"><\/span>How do I use the TranslatorInterface?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Symfony\u2019s <code>TranslatorInteface<\/code> gives us a few handy ways to define translation strings, and allows to force a locale for a given message.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"using-keywords\"><\/span>Using keywords<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>We can use any kinds of string as message keys. For best practices it\u2019s better if you use dot (.) separated strings.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ In the controller:<\/span>\n&#91;\n    <span class=\"hljs-string\">'title'<\/span> =&gt; $translator-&gt;trans(<span class=\"hljs-string\">'home.title'<\/span>),\n    <span class=\"hljs-string\">'heading'<\/span> =&gt; $translator-&gt;trans(<span class=\"hljs-string\">'home.heading'<\/span>),\n]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_47f78f505573a0753ed7432db358230e\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>The dot (.) syntax allows you to declare messages with nested IDs to avoid repeating yourself:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># In the translation message file:<\/span>\n<span class=\"hljs-attr\">home:<\/span> \n\t<span class=\"hljs-attr\">title:<\/span> <span class=\"hljs-string\">Welcome<\/span> <span class=\"hljs-string\">to<\/span> <span class=\"hljs-string\">Indie<\/span> <span class=\"hljs-string\">Games<\/span> <span class=\"hljs-string\">Blog<\/span>\n  <span class=\"hljs-attr\">heading:<\/span> <span class=\"hljs-string\">Honest<\/span> <span class=\"hljs-string\">Indie<\/span> <span class=\"hljs-string\">game<\/span> <span class=\"hljs-string\">reviews<\/span> <span class=\"hljs-string\">since<\/span> <span class=\"hljs-number\">2010<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_cf4cbee8d9bd33ea73d1012b2c90a3ce\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><span role=\"img\" aria-label=\"\ud83d\uddd2\ufe0f\">\ud83d\uddd2\ufe0f<\/span><span class=\"notion-enable-hover\" data-token-index=\"1\"> Note \u00bb We\u2019ll look at translation message files in more detail in the next section.<\/span><\/p>\n<h3><span class=\"ez-toc-section\" id=\"using-variables\"><\/span>Using variables<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>You can construct a string that mixes string literals and variables:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ In the controller:<\/span>\n$visitorsCount = <span class=\"hljs-number\">12382<\/span>;\n\n&#91;\n    <span class=\"hljs-string\">'visitors'<\/span> =&gt; $translator-&gt;trans($visitorsCount . <span class=\"hljs-string\">' people online'<\/span>),\n]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_880dd5d6ae88d094fba38b5972108ff7\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>When using concatenated strings like the example above, the translator will only pick up the part of the string without the variable and use it as a key. It will then prepend a double underscore (_ _) in the value part to signify that this message needs a proper translation.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"># In the translation message file:\n<span class=\"hljs-string\">' people online'<\/span>: <span class=\"hljs-string\">'__ people online'<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_24e1d680f6c1ac3a8635e73bd2065048\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\u270b Heads up \u00bb Defining translations messages with concatenated strings like this is not recommended. Symfony can\u2019t detect these messages in code properly, so it will be harder to translate properly. Also, languages don\u2019t all share the same grammar; a variable like <code>$visitorCount<\/code> isn\u2019t always placed in the same position in a translation. So hard-coded interpolation is just bad practice.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ In the controller<\/span>\n&#91;\n    <span class=\"hljs-string\">'visitors'<\/span> =&gt; $translator-&gt;trans(\n        <span class=\"hljs-string\">'There are %visitors% visitors online'<\/span>, &#91;<span class=\"hljs-string\">'%visitors%'<\/span> =&gt; <span class=\"hljs-number\">156<\/span>]),\n]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_13a73959a6596af576173a5e8a5086c6\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># In the translation message file:<\/span>\n<span class=\"hljs-string\">'There are %visitors% visitors online'<\/span><span class=\"hljs-string\">:<\/span> <span class=\"hljs-string\">'There are %visitors% visitors online'<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_fad64d4f860d45c2f16cec4e06c5a322\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\ud83d\uddd2\ufe0f Note \u00bb We cover interpolation in detail after looking at ICU messages later.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"forcing-a-locale\"><\/span>Forcing a locale<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>You can specify a locale directly as the second parameter to <code>trans<\/code>. This is useful if you want to pin a specific translation for a string but have the rest of the messages render in the current locale:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">&#91;\n    <span class=\"hljs-string\">'heading'<\/span> =&gt; $translator-&gt;trans(\n        <span class=\"hljs-string\">'Welcome to Indie Games Blog'<\/span>, locale: <span class=\"hljs-string\">'de'<\/span>);\n]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_4c8882f075c4027906bc950fa5aeaa05\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>With this newfound knowledge, let\u2019s start translating our site to German.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-work-with-translation-files\"><\/span>How do I work with translation files?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Symfony provides two formats to setup translation files: normal messages and ICU messages. We\u2019ll deal with ICU messages a bit later. Let\u2019s look at normal messages for now.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"extracting-translations-from-code\"><\/span>Extracting translations from code<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>You can use the following CLI commands to generate translation message files:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">$ php bin\/console translation:extract --force en\n$ php bin\/console translation:extract --force de<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_c701a096fba2a30a502afc268db1de46\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>This will detect all the usages of the <code>TranslatorInterface<\/code> in your code and place them into the associated files:<\/p>\n<ul>\n<li><code>\/translations\/messages.en.yml<\/code><\/li>\n<li><code>\/translations\/messages.de.yml<\/code><\/li>\n<\/ul>\n<p>The files will contain key-value pairs, where both key and value contain the string we passed to <code>trans()<\/code>. For example, we have code like this in our home controller:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">$translator-&gt;trans(<span class=\"hljs-string\">'Indie Games Blog'<\/span>);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_3c113fc0876bb90b39d471d00ce1140f\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>When you run the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">translation:extract<\/span> command, it will detect the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\">trans()<\/span> call in our code, generating this liine in each of our translation files:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-attr\">'Indie Games Blog':<\/span> <span class=\"hljs-string\">'Indie Games Blog'<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_a92babcc0f88b055029e734493e428d0\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><span class=\"discussion-id-8033f692-e185-4ab4-b978-577e2be731b3 notion-enable-hover\" data-token-index=\"0\">Variables are surrounded by percentage symbols (%)<\/span>.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-string\">'app.visitors'<\/span>: <span class=\"hljs-string\">'There are %visitors% visitors online'<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_6177e47946e9f49ea21d437f0ca284c9\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>To create such a message string, you need to add a translation key and then edit the message in the file with the appropriate translation:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\"><span class=\"hljs-variable\">$translator<\/span>-&gt;trans(<span class=\"hljs-string\">'app.visitors'<\/span>);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_13a73959a6596af576173a5e8a5086c6\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-string\">\/translations\/messages.en.yml<\/span>\n<span class=\"hljs-attr\">'app.visitors':<\/span> <span class=\"hljs-string\">'__app.visitors'<\/span>\n\n<span class=\"hljs-attr\">'app.visitors':<\/span> <span class=\"hljs-string\">'There are %visitors% visitors online'<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_cefb439b61ce6bcd893ebbad1541f52a\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\ud83d\udd17 Resource \u00bb\u00a0The Symfony translation bundle covers many formats outside of YAML, including CSV and JSON. See the entire <a href=\"https:\/\/symfony.com\/doc\/current\/translation.html#translation-resource-file-names-and-locations\">list of supported translation file formats<\/a> in the official docs.<\/p>\n<p>Go ahead and provide all the required translation strings for each locale.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># \/translations\/messages.en.yml<\/span>\n\n<span class=\"hljs-attr\">brand_name:<\/span> <span class=\"hljs-string\">'Indie Games Blog'<\/span>\n<span class=\"hljs-attr\">home.title:<\/span> <span class=\"hljs-string\">'Welcome to Indie Games Blog'<\/span>\n<span class=\"hljs-comment\"># ...<\/span>\n<span class=\"hljs-attr\">app.visitors:<\/span> <span class=\"hljs-string\">'There are %visitors% visitors online'<\/span>\n<span class=\"hljs-comment\"># ...<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_13a73959a6596af576173a5e8a5086c6\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-25\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># \/translations\/messages.de.yml<\/span>\n\n<span class=\"hljs-attr\">brand_name:<\/span> <span class=\"hljs-string\">'Indie Games Blog'<\/span>\n<span class=\"hljs-attr\">home.title:<\/span> <span class=\"hljs-string\">'Willkommen beim Indie-Games-Blog'<\/span>\n<span class=\"hljs-comment\"># ...<\/span>\n<span class=\"hljs-attr\">app.visitors:<\/span> <span class=\"hljs-string\">'Es sind %visitors% Besucher online'<\/span>\n<span class=\"hljs-comment\"># ...<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-25\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_aecc9fbd5a43d45b08794eb14afe406e\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Now let\u2019s check our German translations by setting up the default locale as <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">de<\/span>:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-26\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\"># config\/packages\/translation.yaml<\/span>\n\nframework:\n    default_locale: de\n    translator:\n        default_path: <span class=\"hljs-string\">'%kernel.project_dir%\/translations'<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-26\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_5fbb41a0e61eb718014a4c1547b9520c\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Now try to load the site and navigate to the following path<\/p>\n<p>localhost:8000<\/p>\n<p>You should be able to see the translated to German:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-55051\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.16.44-scaled.jpg\" alt=\"Site in Deutsch | Phrase\" width=\"2560\" height=\"938\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.16.44-scaled.jpg 2560w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.16.44-300x110.jpg 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.16.44-1024x375.jpg 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.16.44-768x281.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.16.44-1536x563.jpg 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.16.44-2048x751.jpg 2048w\" sizes=\"(max-width: 2560px) 100vw, 2560px\" \/><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-27\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">\n{# Simple example #} \n{% trans %}Welcome to Indie Games Blog{% endtrans %}\n\n{# Example with variables #} \n{% trans with {'%visitors%': 156} %}There are %visitors% visitors online{% endtrans %}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-27\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_8a418d292775bcf60ba73059ccfc9e25\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>You can also force the locale for a given message:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-28\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">{# Force German Locale #} \n{% trans with {'%visitors%': 156} into 'de' %}There are %visitors% visitors online{% endtrans %}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-28\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_3aa32bfdf2a120a3db2785a3c082d19c\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\u270b Heads up \u00bb Using the Twig {% trans %} is more verbose and does not escape the variables in templates by default. You need to manually escape the translated output using the <code>escape<\/code> filter otherwise this can expose your visitors to XSS (Cross-site Scripting) attacks.<\/p>\n<p>\ud83d\udd17 <em>Resource \u00bb\u00a0You can find all information about escaping rules and filters in the relevant <a href=\"https:\/\/twig.symfony.com\/doc\/2.x\/api.html#escaper-extension\">Twig documentation<\/a>.<\/em><\/p>\n<p>Manually escaping html content works by using the <code>raw<\/code> filter. This is useful when your translation message contains HTML tags:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-29\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">{# \/templates\/home.html.twig #}\n\n{% set copyright = '<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>\u00a92023 Build in Planet Earth<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>' %}\n...\n\n{% block footer %}\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">li<\/span>&gt;<\/span>{{ messages&#91;'visitors'] |trans }}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">li<\/span>&gt;<\/span>{{ copyright|trans|raw }}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">li<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ul<\/span>&gt;<\/span>\n{% endblock %}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-29\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_f9d1ec88fb3e9a8c32fc6a5265ffbaab\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\u270b Heads up \u00bb Again, be careful with the <code>raw<\/code> filter: Make sure that you trust the HTML in the incoming translation message or you expose your visitors to XSS attacks.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-work-with-icu-messages\"><\/span>How do I work with ICU messages?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>ICU (International Components for Unicode) is a set of widely used message formats in translation software and i18n libraries. It provides services and standards to format localized messages. This includes basic messages, pluralization, numbers, dates, and much, much more.<\/p>\n<p>\ud83e\udd3f Go Deeper \u00bb Our <a href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/\">Missing Guide to the ICU Message Format<\/a> covers the ins and outs.<\/p>\n<p>To include ICU MessageFormat translations in our blog we need to make a couple of updates to our project. First you need to change the file extension of the message files to have a <code>+intl-icu<\/code> suffix:<\/p>\n<ul>\n<li><code>messages.en.yaml<\/code> \u2192 <code>messages+intl-icu.en.yaml<\/code><\/li>\n<li><code>messages.de.yaml<\/code> \u2192 <code>messages+intl-icu.de.yaml<\/code><\/li>\n<\/ul>\n<p>Then you need to change all the variables to be surrounded by brackets (<code>{<\/code>) instead of percentage symbols (<code>%<\/code>):<\/p>\n<p><code>There are %visitors% visitors online<\/code> \u2192 <code>There are {visitors} visitors online<\/code><\/p>\n<p>\ud83d\uddd2\ufe0f Note \u00bb Once you have configured the filenames to use ICU, the <code>translation:extract<\/code> command will automatically extract string into ICU messages.<\/p>\n<p>That\u2019s about it for refactoring. Now let\u2019s explore the power ICU provides.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-add-dynamic-values-to-translation-messages\"><\/span>How do I add dynamic values to translation messages?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>With ICU, we can inject dynamic values at runtime using the <code>{variable}<\/code> syntax in our message strings. We will have to provide a matching value using the second parameter to <code>trans()<\/code>:<\/p>\n<p>Let\u2019s define these messages in English:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-30\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># \/translations\/messages+intl-icu.en.yaml<\/span>\n\n<span class=\"hljs-string\">'Hello {username}'<\/span><span class=\"hljs-string\">:<\/span> <span class=\"hljs-string\">'Hello {username}'<\/span>\n<span class=\"hljs-attr\">app.visitors:<\/span> <span class=\"hljs-string\">There<\/span> <span class=\"hljs-string\">are<\/span> <span class=\"hljs-string\">{visitors}<\/span> <span class=\"hljs-string\">visitors<\/span> <span class=\"hljs-string\">online.<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-30\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_4235d0111c9011a214a389795c9131f4\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>And in German:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-31\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># \/translations\/messages+intl-icu.de.yaml<\/span>\n\n<span class=\"hljs-string\">'Hello {username}'<\/span><span class=\"hljs-string\">:<\/span> <span class=\"hljs-string\">'Hallo {username}'<\/span>\n<span class=\"hljs-attr\">app.visitors:<\/span> <span class=\"hljs-string\">Es<\/span> <span class=\"hljs-string\">sind<\/span> <span class=\"hljs-string\">{visitors}<\/span> <span class=\"hljs-string\">Besucher<\/span> <span class=\"hljs-string\">online.<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-31\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_db402af92c2ac3565dea9f54c5671bff\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Then, using the translator interface, pass the variables as a key-value parameters.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-32\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">$translator-&gt;trans(<span class=\"hljs-string\">'Hello {username}'<\/span>, &#91;<span class=\"hljs-string\">'username'<\/span> =&gt; <span class=\"hljs-string\">'Alex'<\/span>]); \n$translator-&gt;trans(<span class=\"hljs-string\">'app.visitors'<\/span>, &#91;<span class=\"hljs-string\">'visitors'<\/span> =&gt; <span class=\"hljs-number\">156<\/span>]);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-32\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_526bfb52366e210c17de91696d86d26a\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Then the output in English will be:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-33\" data-shcb-language-name=\"plaintext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">\/\/ en\nHello Alex\nThere are 156 visitors online<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-33\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">plaintext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_c71b40da47f64f3cfa6f681863423669\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>And German:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-34\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ de<\/span>\nHallo Alex\nEs sind <span class=\"hljs-number\">156<\/span> Besucher online.<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-34\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_0dabaa7b87f8ac6dcdc8434c04ed798d\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>You can also use dynamic values in twig templates. <span class=\"discussion-id-daa7dc53-865d-47e8-848c-7d2c06484b20 notion-enable-hover\" data-token-index=\"1\">You can\u2019t use the trans tag, however.<\/span> You will need to use the full form of the interpolated message with the brackets:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-35\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">&lt;li&gt;{{ messages&#91;<span class=\"hljs-string\">'visitors'<\/span>] | trans({<span class=\"hljs-string\">'{visitors}'<\/span>: <span class=\"hljs-number\">100<\/span>}) }}&lt;\/li&gt; <\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-35\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_26d04f9f97e80c828acacc32dcbee4b7\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\u270b Heads up \u00bb If you use variable interpolations in both twig templates and in the <code>TranslatorInterface<\/code> the latter will always override the former whether you use ICU or normal message formats.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-work-with-plural-messages\"><\/span>How do I work with plural messages?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>ICU is excellent at handling plurals. Let\u2019s add a German plural message to show number of visitors in our site:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-36\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># \/translations\/messages+intl-icu.de.yaml<\/span>\n\n<span class=\"hljs-attr\">app.visitors:<\/span> <span class=\"hljs-string\">&gt;-\n    {visitors, plural,\n        =0 {Es sind keine anderen Besucher hier.}\n        one {Sie sind der einzige Besucher hier.}\n        other {Hier sind # Besucher hier.}\n    }<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-36\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_e027e5d9a5fb95f109a9a95e042c324a\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Notice that we define the count variable, <code>visitors<\/code>, followed by the <code>plural<\/code> directive. We then specify each of our locale\u2019s plural forms. <a href=\"https:\/\/cldr.unicode.org\/index\/cldr-spec\/plural-rules\">German has two<\/a>: <code>one<\/code> and <code>other<\/code>. It\u2019s like having a switch statement to match <code>visitors<\/code> with use cases.<\/p>\n<p>We can also use <code>=&lt;number&gt;<\/code> lines to provide a message for explicit counts. We\u2019re using <code>=0<\/code> to define a message when <code>visitors<\/code> has a value of zero. This will override the regular <code>other<\/code> form in this case.<\/p>\n<p>\u270b Heads up \u00bb The <code>other<\/code> plural form is required. If you do not provide one, you will get an error like the following:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-55090\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-11-at-10.50.14.png\" alt=\"Symfony error message | Phrase\" width=\"2366\" height=\"352\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-11-at-10.50.14.png 2366w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-11-at-10.50.14-300x45.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-11-at-10.50.14-1024x152.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-11-at-10.50.14-768x114.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-11-at-10.50.14-1536x229.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-11-at-10.50.14-2048x305.png 2048w\" sizes=\"(max-width: 2366px) 100vw, 2366px\" \/><\/p>\n<p>\ud83d\udd17 Resource \u00bb\u00a0You can find all plural form keywords, like <code>one<\/code> and <code>other<\/code>, for your supported locales in the <a href=\"https:\/\/unicode-org.github.io\/cldr-staging\/charts\/latest\/supplemental\/language_plural_rules.html\">official Unicode CLDR Language Plural Rules chart<\/a>.<\/p>\n<p>With the message above, we get a different plural form output depending on the value of <code>visitors<\/code>:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-37\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">\n<span class=\"hljs-comment\">\/\/ Assuming German (de) is the active locale:<\/span>\n\n$translator-&gt;trans(<span class=\"hljs-string\">'app.visitors'<\/span>, &#91;<span class=\"hljs-string\">'visitors'<\/span> =&gt; <span class=\"hljs-number\">0<\/span>]);\n<span class=\"hljs-comment\">\/\/ =&gt; Es sind keine anderen Besucher hier.<\/span>\n\n$translator-&gt;trans(<span class=\"hljs-string\">'app.visitors'<\/span>, &#91;<span class=\"hljs-string\">'visitors'<\/span> =&gt; <span class=\"hljs-number\">1<\/span>]);\n<span class=\"hljs-comment\">\/\/ =&gt; Sie sind der einzige Besucher hier.<\/span>\n\n$translator-&gt;trans(<span class=\"hljs-string\">'app.visitors'<\/span>, &#91;<span class=\"hljs-string\">'visitors'<\/span> =&gt; <span class=\"hljs-number\">100<\/span>]);\n<span class=\"hljs-comment\">\/\/ =&gt; Hier sind 100 Besucher hier.<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-37\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_514a82156d210ddc0dc35bc9f050f30c\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>You can also have variables <span class=\"notion-enable-hover\" data-token-index=\"1\">within<\/span> plural forms:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-38\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># \/translations\/messages+intl-icu.de.yaml<\/span>\n\n<span class=\"hljs-attr\">app.visitors:<\/span> <span class=\"hljs-string\">&gt;-\n    {visitors, plural,\n        =0 {Hallo {username}. Es sind keine anderen Besucher hier.}\n        one {Hallo {username}. Sie sind der einzige Besucher hier.}\n        other {Hallo {username}. Hier sind # Besucher.}\n    }<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-38\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_13a73959a6596af576173a5e8a5086c6\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-39\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ Assuming German (de) is the active locale:<\/span>\n\n$translator-&gt;trans(<span class=\"hljs-string\">'app.visitors'<\/span>, &#91;<span class=\"hljs-string\">'visitors'<\/span> =&gt; <span class=\"hljs-number\">0<\/span>, <span class=\"hljs-string\">'username'<\/span> =&gt; <span class=\"hljs-string\">'Theo'<\/span>]);\n<span class=\"hljs-comment\">\/\/ =&gt; Hallo Theo. Es sind keine anderen Besucher hier.<\/span>\n\n$translator-&gt;trans(<span class=\"hljs-string\">'app.visitors'<\/span>, &#91;<span class=\"hljs-string\">'visitors'<\/span> =&gt; <span class=\"hljs-number\">1<\/span>, <span class=\"hljs-string\">'username'<\/span> =&gt; <span class=\"hljs-string\">'Theo'<\/span>]);\n<span class=\"hljs-comment\">\/\/ =&gt; Hallo Theo. Sie sind der einzige Besucher hier.<\/span>\n\n$translator-&gt;trans(<span class=\"hljs-string\">'app.visitors'<\/span>, &#91;<span class=\"hljs-string\">'visitors'<\/span> =&gt; <span class=\"hljs-number\">100<\/span>, <span class=\"hljs-string\">'username'<\/span> =&gt; <span class=\"hljs-string\">'Theo'<\/span>]);\n<span class=\"hljs-comment\">\/\/ =&gt; Hallo Theo. Hier sind 100 Besucher hier.<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-39\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_3b71d284012d48cc1e5d3ac58dd3ca5b\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><span role=\"img\" aria-label=\"\ud83d\uddd2\ufe0f\">\ud83d\uddd2\ufe0f<\/span> <span class=\"notion-enable-hover\" data-token-index=\"1\">Note \u00bb In addition to the plural directive there is a <\/span><span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"2\">select<\/span><span class=\"notion-enable-hover\" data-token-index=\"3\"> directive. <\/span><span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"4\">select<\/span><span class=\"notion-enable-hover\" data-token-index=\"5\"> is suitable for gender rules and any other selection based on non-numeric choices. We cover this in detail in <\/span><a class=\"notion-link-token notion-focusable-token notion-enable-hover\" tabindex=\"0\" href=\"https:\/\/phrase.com\/blog\/posts\/guide-to-the-icu-message-format\/\" rel=\"noopener noreferrer\" data-token-index=\"6\"><span class=\"link-annotation-unknown-block-id-579201912\">our ICU guide<\/span><\/a><span class=\"notion-enable-hover\" data-token-index=\"7\">.<\/span><span class=\"notion-enable-hover\" data-token-index=\"7\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-55102 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-11-at-10.53.23.png\" alt=\"One visitor in German screenshot | Phrase\" width=\"628\" height=\"148\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-11-at-10.53.23.png 628w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-11-at-10.53.23-300x71.png 300w\" sizes=\"(max-width: 628px) 100vw, 628px\" \/><\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-55112 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-11-at-10.55.10.png\" alt=\"Multiple visitors in German screenshot | Phrase\" width=\"528\" height=\"130\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-11-at-10.55.10.png 528w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-11-at-10.55.10-300x74.png 300w\" sizes=\"(max-width: 528px) 100vw, 528px\" \/><\/p>\n<h3><span class=\"ez-toc-section\" id=\"complex-plurals\"><\/span>Complex plurals<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>German and English each have two plural forms, <code>one<\/code> (\u201dWe\u2019ve had 1 visitor\u201d) and <code>other<\/code> (\u201dWe\u2019ve had 12 visitors\u201d). Other languages have more forms. Russian has four, Arabic six. The good news is that ICU messages can handle complex plural forms with ease. Let\u2019s look at an example in Arabic:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-40\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"> <span class=\"hljs-attr\">app.visitors:<\/span> <span class=\"hljs-string\">&gt;-\n    {visitors, plural,\n        zero {\u0644\u0645 \u064a\u0632\u0631\u0646\u0627 \u0623\u062d\u062f}\n        one {\u0632\u0627\u0631\u0646\u0627 \u0634\u062e\u0635 \u0648\u0627\u062d\u062f}\n        two {\u0632\u0627\u0631\u0646\u0627 \u0634\u062e\u0635\u0627\u0646}\n        few {\u0632\u0627\u0631\u0646\u0627 # \u0623\u0634\u062e\u0627\u0635}\n        many {\u0632\u0627\u0631\u0646\u0627 # \u0634\u062e\u0635\u064b\u0627}\n        other {\u0632\u0627\u0631\u0646\u0627 # \u0634\u062e\u0635}\n    }<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-40\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_13a73959a6596af576173a5e8a5086c6\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-41\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">$translator-&gt;trans(<span class=\"hljs-string\">'app.visitors'<\/span>, &#91;<span class=\"hljs-string\">'visitors'<\/span> =&gt; <span class=\"hljs-number\">0<\/span>]);\n<span class=\"hljs-comment\">\/\/ =&gt; \u0644\u0645 \u064a\u0632\u0631\u0646\u0627 \u0623\u062d\u062f<\/span>\n\n$translator-&gt;trans(<span class=\"hljs-string\">'app.visitors'<\/span>, &#91;<span class=\"hljs-string\">'visitors'<\/span> =&gt; <span class=\"hljs-number\">1<\/span>]);\n<span class=\"hljs-comment\">\/\/ =&gt; \u0632\u0627\u0631\u0646\u0627 \u0634\u062e\u0635 \u0648\u0627\u062d\u062f<\/span>\n\n$translator-&gt;trans(<span class=\"hljs-string\">'app.visitors'<\/span>, &#91;<span class=\"hljs-string\">'visitors'<\/span> =&gt; <span class=\"hljs-number\">2<\/span>]);\n<span class=\"hljs-comment\">\/\/ =&gt; \u0632\u0627\u0631\u0646\u0627 \u0634\u062e\u0635\u0627\u0646<\/span>\n\n<span class=\"hljs-comment\">\/\/ The `few` form in Arabic can include 3-10<\/span>\n$translator-&gt;trans(<span class=\"hljs-string\">'app.visitors'<\/span>, &#91;<span class=\"hljs-string\">'visitors'<\/span> =&gt; <span class=\"hljs-number\">5<\/span>]);\n<span class=\"hljs-comment\">\/\/ =&gt; \u0632\u0627\u0631\u0646\u0627 5 \u0623\u0634\u062e\u0627\u0635<\/span>\n\n<span class=\"hljs-comment\">\/\/ The `many` form in Arabic can include 11-26<\/span>\n$translator-&gt;trans(<span class=\"hljs-string\">'app.visitors'<\/span>, &#91;<span class=\"hljs-string\">'visitors'<\/span> =&gt; <span class=\"hljs-number\">13<\/span>]);\n<span class=\"hljs-comment\">\/\/ =&gt; \u0632\u0627\u0631\u0646\u0627 13 \u0634\u062e\u0635\u064b\u0627<\/span>\n\n<span class=\"hljs-comment\">\/\/ The `other` form in Arabic can include 100-102<\/span>\n$translator-&gt;trans(<span class=\"hljs-string\">'app.visitors'<\/span>, &#91;<span class=\"hljs-string\">'visitors'<\/span> =&gt; <span class=\"hljs-number\">101<\/span>]);\n<span class=\"hljs-comment\">\/\/ =&gt; \u0632\u0627\u0631\u0646\u0627 101 \u0634\u062e\u0635<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-41\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_598ed234f552aa9d27c5a9f7b65cd729\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<h2><\/h2>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-localize-dates\"><\/span>How do I localize dates?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>The ICU message format can localize dates using a variety of options:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-42\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># \/translations\/messages+intl-icu.en.yaml<\/span>\n\n<span class=\"hljs-attr\">today:<\/span> <span class=\"hljs-string\">'Today is: {today, date, long}'<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-42\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_13a73959a6596af576173a5e8a5086c6\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-43\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># \/translations\/messages+intl-icu.de.yaml<\/span>\n\n<span class=\"hljs-attr\">today:<\/span> <span class=\"hljs-string\">'Heute ist: {today, date, long}'<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-43\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_75b989324cfe63306be975613eb34524\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>The format for rendering date format for a country is defined within brackets. The <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">today<\/span> parameter specifies the name, the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\">date<\/span> specifies that it is a date type and the long specifies that it should be printed in full form: <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"5\">January 12, 1952 3:30:32pm<\/span>.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-44\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ In our controllers:<\/span>\n\n<span class=\"hljs-comment\">\/\/ Assuming German is active<\/span>\n$translator-&gt;trans(<span class=\"hljs-string\">'today'<\/span>, &#91;<span class=\"hljs-string\">'today'<\/span> =&gt; <span class=\"hljs-keyword\">new<\/span> \\DateTime(<span class=\"hljs-string\">'2023-01-25 11:00:00'<\/span>)]);\n<span class=\"hljs-comment\">\/\/ =&gt; Heute ist: 25. Januar 2023<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-44\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_734dbfa5c2c10d405fefc4b1074f0159\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Twig also comes with special <span class=\"discussion-id-4004a84a-fe55-460d-83be-166fca948181 notion-enable-hover\" data-token-index=\"1\">filters<\/span> that format localized dates. To make full use of them you need to install some extra packages first:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-45\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">$ composer require twig\/intl-extra twig\/string-extra<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-45\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_13a73959a6596af576173a5e8a5086c6\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-46\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">\/\/ Assuming the active locale is German\n\n{{ '2023-01-25 11:00:00'| format_datetime('none', 'short') }}\n{# =&gt; 11:00 #}\n\n{{ '2023-01-25 11:00:00'| format_datetime('full', 'full') }}\n{# =&gt; Mittwoch, 25. Januar 2023 um 11:00:00 Koordinierte Weltzeit #}\n\n{{ '2023-01-25 11:00:00'| format_datetime(pattern=\"hh 'oclock' a, zzzz\") }}\n{# =&gt; 11 oclock AM, Koordinierte Weltzeit #}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-46\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_9aa6c1fd5468da3488f2d4831b599cd4\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>The <code>format_datetime<\/code> accepts different kinds of parameters. The first one is the format of the date part and the second one is the format of the time part. Each of the parameters can be <code>none<\/code>, <code>short<\/code>, <code>full<\/code> or a custom dateFormat string. The last example showcases the usage of a custom <code>pattern<\/code> parameter for the whole datetime string instead of its date and time parts.<\/p>\n<p>\ud83d\udd17 Resource \u00bb\u00a0 Learn more about the <a href=\"https:\/\/twig.symfony.com\/doc\/2.x\/filters\/format_datetime.html\">configuration parameters of format_datetime<\/a> in the official docs.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-localize-numbers\"><\/span>How do I localize numbers?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Localized numbers and currency formatting is performed via the <code>number<\/code> directive in ICU:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-47\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># \/translations\/messages+intl-icu.en.yaml<\/span>\n\n<span class=\"hljs-attr\">game.price:<\/span> <span class=\"hljs-string\">'Price: {price, number, ::currency\/USD}'<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-47\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_13a73959a6596af576173a5e8a5086c6\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-48\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># \/translations\/messages+intl-icu.de.yaml<\/span>\n\n<span class=\"hljs-attr\">game.price:<\/span> <span class=\"hljs-string\">'Preis: {price, number, ::currency\/EUR}'<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-48\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_13a73959a6596af576173a5e8a5086c6\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-49\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ English active:<\/span>\n$translator-&gt;trans(<span class=\"hljs-string\">'game.price'<\/span>, &#91;<span class=\"hljs-string\">'price'<\/span> =&gt; <span class=\"hljs-number\">50<\/span>]);\n<span class=\"hljs-comment\">\/\/ =&gt; Price: $50.00<\/span>\n\n<span class=\"hljs-comment\">\/\/ German active:<\/span>\n$translator-&gt;trans(<span class=\"hljs-string\">'game.price'<\/span>, &#91;<span class=\"hljs-string\">'price'<\/span> =&gt; <span class=\"hljs-number\">60<\/span>]);\n<span class=\"hljs-comment\">\/\/ =&gt; Preis: 60,00 \u20ac<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-49\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_3d0b96b89d8bb089fb5ed0e465808c93\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>The syntax for this is similar to plurals. You use brackets to declare a formatted number parameter. In this case the <code>price<\/code> is the parameter name, the <code>number<\/code> declares that it should be formated as a number and the <code>::currency\/USD<\/code> tag declares that is should be formated as a USD currency.<\/p>\n<p>Using Twig templates, you can use <code>format_number<\/code> filter that allows formating of numbers. By default, the filter uses the current locale if you do not provide a locale parameter:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-50\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">\n{{ '600'|format_duration_number(locale=\"en\") }}\n{# =&gt; 10:00 #}\n\n{{ '60'|format_spellout_number(locale=\"de\") }}\n{# =&gt; sechzig #}\n\n{{ '80'|format_percentage_number(locale=\"de\") }}\n{# =&gt; achzig #}\n\n{{ '100'|format_percent_number(locale=\"de\") }}\n{# =&gt; 10.000 #}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-50\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_6a7c9cba29c99813342bff1805e6effa\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\ud83d\udd17 Resource \u00bb\u00a0Learn more about the different configuration parameters of <code>format_number<\/code> in <a href=\"https:\/\/twig.symfony.com\/doc\/2.x\/filters\/format_number.html\">the official docs<\/a>.<\/p>\n<p>For currency, Twig offers a special filter, <code>format_currency<\/code>:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-51\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">{{ '50'|format_currency('EUR', locale='de') }\n{# =&gt; 50,00 \u20ac #}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-51\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_7872bf2966718e5dbb5e031ac772a06b\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\ud83d\udd17 Resource \u00bb\u00a0Learn more about the configuration parameters of <code>format_currency<\/code> in <a href=\"https:\/\/twig.symfony.com\/doc\/3.x\/filters\/format_currency.html\">the official docs<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-get-or-set-the-active-locale\"><\/span>How do I get or set the active locale?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We can get or set the locale inside a controller or a service by injecting and using Symfony\u2019s <code>LocaleSwitcher<\/code>:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-52\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ src\/Controller\/HomeController.php<\/span>\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Component<\/span>\\<span class=\"hljs-title\">Translation<\/span>\\<span class=\"hljs-title\">LocaleSwitcher<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HomeController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">AbstractController<\/span>\n<\/span>{\n    <span class=\"hljs-comment\">#&#91;Route('\/', name: 'home')]<\/span>\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">index<\/span><span class=\"hljs-params\">(\n        TranslatorInterface $translator,\n        LocaleSwitcher $localeSwitcher)<\/span>: <span class=\"hljs-title\">Response<\/span>\n    <\/span>{   \n        <span class=\"hljs-comment\">\/\/ Set the active locale to German<\/span>\n        $localeSwitcher-&gt;setLocale(<span class=\"hljs-string\">'de'<\/span>); \n\n        <span class=\"hljs-comment\">\/\/ Retrieve the active locale<\/span>\n        $localeSwitcher-&gt;getLocale(); <span class=\"hljs-comment\">\/\/ =&gt; 'de'<\/span>\n\t\t}\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-52\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_26a6e5bdc894cc4c5d6482cf7b12632f\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<h2><span class=\"ez-toc-section\" id=\"how-do-i-configure-fallback-locales\"><\/span>How do I configure fallback locales?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Fallback locales are a list of locales that the server will try to use if a requested locale is not supported, or if translations in the active locale are missing. Let\u2019s add fallbacks in our config file:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-53\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># config\/packages\/translation.yaml<\/span>\n\n<span class=\"hljs-attr\">framework:<\/span>\n    <span class=\"hljs-attr\">default_locale:<\/span> <span class=\"hljs-string\">en<\/span>\n    <span class=\"hljs-attr\">translator:<\/span>\n        <span class=\"hljs-attr\">default_path:<\/span> <span class=\"hljs-string\">'%kernel.project_dir%\/translations'<\/span>\n        <span class=\"hljs-attr\">fallbacks:<\/span> \n            <span class=\"hljs-bullet\">-<\/span> <span class=\"hljs-string\">en<\/span>\n            <span class=\"hljs-bullet\">-<\/span> <span class=\"hljs-string\">de<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-53\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_e812510b5df3dc08ce2563ab2e6b4ac7\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Here we added two lines for <code>fallbacks<\/code> corresponding to the English (<code>en<\/code>) and German (<code>de<\/code>) language tags. Let\u2019s explore how this works in action.<\/p>\n<p>Say we removed the <code>home.subtitle<\/code> message from the German translations. You will see the following screen when you visit the Home page with active locale as German:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-55137\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-18-at-12.25.28.png\" alt=\"Homepage screenshot in German | Phrase\" width=\"3582\" height=\"692\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-18-at-12.25.28.png 3582w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-18-at-12.25.28-300x58.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-18-at-12.25.28-1024x198.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-18-at-12.25.28-768x148.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-18-at-12.25.28-1536x297.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-04-18-at-12.25.28-2048x396.png 2048w\" sizes=\"(max-width: 3582px) 100vw, 3582px\" \/><\/p>\n<p>Here the <code>home.subtitle<\/code> will fallback to the English translation since it\u2019s the one available but the rest of the messages will translate fine.<\/p>\n<p>Symfony\u2019s translation bundle will also automatically resolve locale fallback based on the specificity of the requested locale. Say we\u2019re detecting the locale from the visitor\u2019s browser (more on that later). Let\u2019s assume our visitor has set her preferred browser locale to German (Germany) ie. <code>de-DE<\/code>. With the above configuration, Symfony will fall back to the <em>parent<\/em> locale of <code>de-DE<\/code>, which is <code>de<\/code>.<\/p>\n<p>\ud83d\uddd2\ufe0f Note \u00bb At the end of the fallback cascade, if Symfony can\u2019t find a suitable locale, it always falls back to our default locale (<code>en<\/code> in our case).<\/p>\n<p>\ud83d\udd17 Resource \u00bb\u00a0We\u2019re specifying our locales using their ISO 639-1 codes (<code>en<\/code>, <code>de<\/code>). You can find <a href=\"https:\/\/en.wikipedia.org\/wiki\/List_of_ISO_639-1_codes\">a list of all these codes on Wikipedia<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-support-right-to-left-languages\"><\/span>How do I support right-to-left languages?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>It\u2019s important to remember to support right-to-left (RTL) languages like Arabic and Hebrew. We can leverage browser features to make a web page render RTL content. The first step is to add\u00a0<code>dir=\"rtl\"<\/code> to the <code>&lt;html&gt;<\/code> tag for RTL locales. We can modify the <code>base.html.twig<\/code> template to add this value whenever we switch to that locale:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-54\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ templates\/base.html.twig<\/span>\n\n{% set locale = app.request.attributes.get(<span class=\"hljs-string\">'_locale'<\/span>) %}\n\n&lt;html dir=<span class=\"hljs-string\">\"{% if locale == 'ar' %}rtl{% else %}ltr{% endif %}\"<\/span> lang=<span class=\"hljs-string\">\"{{locale}}\"<\/span>&gt;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-54\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_6ae02c119b4cb12c4cab2d0e1c06bf6b\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>We get the active locale through <code>app.request.attributes.get('_locale')<\/code>. The code above will product <code>&lt;html dir='rtl'&gt;<\/code> when the active locale is Arabic. For every other locale, we\u2019ll get <code>&lt;html dir='ltr'&gt;<\/code>.<\/p>\n<p>For brevity, we won\u2019t go through the steps of adding Arabic to our app here. Feel free to follow the previous configuration, extraction, and translation steps to do so. Assuming all that\u2019s done, setting our <code>default_locale<\/code> to <code>ar<\/code> would yield the home page rendered right-to-left:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-55152\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-11.58.27-scaled.jpg\" alt=\"Homepage screenshot in Arabic | Phrase\" width=\"2560\" height=\"949\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-11.58.27-scaled.jpg 2560w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-11.58.27-300x111.jpg 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-11.58.27-1024x380.jpg 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-11.58.27-768x285.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-11.58.27-1536x569.jpg 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-11.58.27-2048x759.jpg 2048w\" sizes=\"(max-width: 2560px) 100vw, 2560px\" \/><\/p>\n<p><span role=\"img\" aria-label=\"\ud83e\udd3f\">\ud83e\udd3f<\/span> <span class=\"notion-enable-hover\" data-token-index=\"1\">Go Deeper \u00bb <\/span><span class=\"discussion-id-119be5dd-b331-462f-ac7b-a403b6a70d12 notion-enable-hover\" data-token-index=\"2\">The above <\/span><span class=\"discussion-id-119be5dd-b331-462f-ac7b-a403b6a70d12 notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\">if<\/span><span class=\"discussion-id-119be5dd-b331-462f-ac7b-a403b6a70d12 notion-enable-hover\" data-token-index=\"4\"> statement inside the twig template <\/span><span class=\"notion-enable-hover\" data-token-index=\"5\">does not scale well. If you have multiple RTL languages in your app, you might want to <\/span><a class=\"notion-link-token notion-focusable-token notion-enable-hover\" tabindex=\"0\" href=\"https:\/\/twig.symfony.com\/doc\/1.x\/advanced.html#creating-an-extension\" rel=\"noopener noreferrer\" data-token-index=\"6\"><span class=\"link-annotation-unknown-block-id--697269731\">create a custom Twig extension<\/span><\/a><span class=\"notion-enable-hover\" data-token-index=\"7\"> that encapsulates your conditional logic.<\/span><\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-create-localized-routes\"><\/span>How do I create localized routes?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Until now we\u2019ve been viewing a particular locale by setting our <code>default_locale<\/code> setting to that language tag.<\/p>\n<p>We would like visitors to be able to navigate to a page and automatically detect the user locale based on their browser language preferences or the current url parameters.<\/p>\n<p>We have a variety of options to customise the routing behaviour based on the selected locale. We take a deeper look at each one of them.<\/p>\n<p>First, let\u2019s look at basic localized routing in Symfony.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"what-is-a-localized-route\"><\/span>What is a localized route?<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>A localized route is a route that defines a different URL per each translation locale. For example we can associate a route with a particular locale:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-55\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># config\/routes.yaml<\/span>\n\n<span class=\"hljs-attr\">about:<\/span>\n    <span class=\"hljs-attr\">path:<\/span>\n        <span class=\"hljs-attr\">en:<\/span> <span class=\"hljs-string\">\/about<\/span>\n        <span class=\"hljs-attr\">de:<\/span> <span class=\"hljs-string\">\/\u00dcber-uns<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-55\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_256a0f361a343eae8add8720b201f4c9\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>You can generate a localized route in twig using the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">path<\/span> function passing the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\">_locale<\/span> as a parameter:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-56\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">{{ path('about', {_locale: 'en'}) }} {# \/about #}\n{{ path('about', {_locale: 'de'}) }} {# \/\u00dcber-uns #}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-56\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_1f121ce15478af3862b187bef9d6fbb0\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<h3><span class=\"ez-toc-section\" id=\"using-a-locale-prefix\"><\/span>Using a locale prefix<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>A common way to generate localized routes is to prefix each route with a locale code. We can use the special <code>_locale<\/code> parameter in our route annotations to accomplish this. During runtime, the framework will automatically set the active locale to the value of <code>_locale<\/code>:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-57\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ReviewsController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">AbstractController<\/span>\n<\/span>{\n    <span class=\"hljs-comment\">#&#91;Route('\/{_locale}\/reviews')]<\/span>\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">index<\/span><span class=\"hljs-params\">(TranslatorInterface $translator)<\/span>: <span class=\"hljs-title\">Response<\/span>\n    <\/span>{\n        <span class=\"hljs-comment\">\/\/ ...<\/span>\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-57\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_838c3e22dfbfc64fe02f95ce4d0437cb\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>For example:<\/p>\n<p><code>http:\/\/localhost:8000\/en\/reviews<\/code> \u2192 switches locale to English. <code>http:\/\/localhost:8000\/de\/reviews<\/code> \u2192 switches locale to German.<\/p>\n<p>\ud83d\uddd2\ufe0f Note \u00bb It is recommended to follow a consistent scheme for locale paths so that the users can bookmark links for future navigation. Having for example the <code>_locale<\/code> parameter in a different place, e.g. <code>\/blog\/{_locale}\/<\/code>, is possible but not consistent.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"using-a-different-path-per-locale\"><\/span>Using a different path per locale<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>You can provide an array of paths when using the <code>Route<\/code> annotation, allowing you to set a different path for each locale, all resolving to the same controller action:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-58\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ src\/Controller\/ReviewsController.php<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ReviewsController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">AbstractController<\/span>\n<\/span>{\n    <span class=\"hljs-comment\">#&#91;Route(&#91;<\/span>\n        <span class=\"hljs-string\">'en'<\/span> =&gt; <span class=\"hljs-string\">'\/reviews'<\/span>,\n        <span class=\"hljs-string\">'de'<\/span> =&gt; <span class=\"hljs-string\">'\/rezensionen'<\/span>\n    ], name: <span class=\"hljs-string\">'reviews'<\/span>)]\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">index<\/span><span class=\"hljs-params\">(TranslatorInterface $translator)<\/span>: <span class=\"hljs-title\">Response<\/span>\n    <\/span>{\n        <span class=\"hljs-comment\">\/\/ ...<\/span>\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-58\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_ec33e7003bf9019cb269cfad0a90af68\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Here if the active locale is German, the navigation will point to <code>\/de\/rezensionen<\/code>. If it\u2019s English, it will point to <code>\/en\/reviews<\/code>.<\/p>\n<p>\ud83d\uddd2\ufe0f Note \u00bb You might be wondering how the active locale is determined here. We\u2019ll tackle this in the locale determination section a bit later.<\/p>\n<p>If you want to leave the default locale out of the url, just use an empty string:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-59\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ src\/Controller\/ReviewsController.php<\/span>\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ReviewsController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">AbstractController<\/span>\n<\/span>{\n    <span class=\"hljs-comment\">#&#91;Route(&#91;<\/span>\n        <span class=\"hljs-string\">''<\/span> =&gt; <span class=\"hljs-string\">'\/reviews'<\/span>,\n        <span class=\"hljs-string\">'de'<\/span> =&gt; <span class=\"hljs-string\">'\/rezensionen'<\/span>\n    ], name: <span class=\"hljs-string\">'reviews'<\/span>)]\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">index<\/span><span class=\"hljs-params\">(TranslatorInterface $translator)<\/span>: <span class=\"hljs-title\">Response<\/span>\n    <\/span>{\n        <span class=\"hljs-comment\">\/\/ ...<\/span>\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-59\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_51cb34343226187e49a2b9c2a1071a6f\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Here the path to <code>\/reviews<\/code> will use the default locale (English) and the path to <code>de<\/code> will use the German locale.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"forcing-a-locale-in-a-url\"><\/span>Forcing a locale in a URL<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>You can generate url for a fixed locale by passing along the <code>_locale<\/code> parameter:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-60\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-keyword\">$this<\/span>-&gt;generateUrl(<span class=\"hljs-string\">'reviews'<\/span>, &#91;<span class=\"hljs-string\">'_locale'<\/span> =&gt; <span class=\"hljs-string\">'de'<\/span>]);\n<span class=\"hljs-comment\">\/\/ =&gt; \/rezensionen<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-60\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_85acbf281f6d9ddad2b977824920af5a\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>In twig templates, when you use the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">path<\/span> function, it will automatically pick up the active locale from <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\">app.request<\/span>:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-61\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">{<span class=\"hljs-comment\"># Assuming the active locale is German: #}<\/span>\n\n&lt;a href=<span class=\"hljs-string\">\"{{ path('reviews') }}\"<\/span>&gt;{{ <span class=\"hljs-string\">'Reviews'<\/span> | trans }}&lt;\/a&gt;\n{<span class=\"hljs-comment\"># =&gt; &lt;a href=\"\/rezensionen\"&gt;Rezensionen&lt;\/a&gt; #}<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-61\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_ec3d8c12897851694b3ee7c62365e599\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<h3><span class=\"ez-toc-section\" id=\"forcing-a-localized-home-route\"><\/span>Forcing a localized home route<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>We can create a redirect route that ensures that navigating to <code>\/<\/code> always redirects to <code>\/en<\/code>:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-62\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ src\/Controller\/HomeController.php<\/span>\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HomeController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">AbstractController<\/span> <\/span>{\n\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n\n    <span class=\"hljs-comment\">#&#91;Route('\/')]<\/span>\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">indexNoLocale<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">Response<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">$this<\/span>-&gt;redirectToRoute(<span class=\"hljs-string\">'home'<\/span>, &#91;<span class=\"hljs-string\">'_locale'<\/span> =&gt; <span class=\"hljs-string\">'en'<\/span>]);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-62\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_1b0f68801a5a62ee8cf938f80cbc55e9\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\ud83e\udd3f Go deeper \u00bb There&#8217;s a lot more you can do with localized routes, including <a href=\"https:\/\/symfony.com\/doc\/current\/the-fast-track\/en\/28-intl.html#internationalizing-urls\">limiting the locales that a given route supports<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-automatically-determine-the-users-locale\"><\/span>How do I automatically determine the user\u2019s locale?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We can instruct the Symfony framework to choose the active locale based on the user\u2019s browser language preferences and our app\u2019s supported locales. With each request, the browser will provide an <code>Accept-Language<\/code> header, matching a weighted set of locale user preferences. Symfony will use the header and try to match the most suitable locale.<\/p>\n<p>Symfony includes this feature by default. You just need to enable it by adding the following lines in your <code>framework.yaml<\/code> config:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-63\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># config\/packages\/framework.yaml<\/span>\n\n<span class=\"hljs-attr\">framework:<\/span>\n    <span class=\"hljs-attr\">set_locale_from_accept_language:<\/span> <span class=\"hljs-literal\">true<\/span>\n    <span class=\"hljs-attr\">set_content_language_from_locale:<\/span> <span class=\"hljs-literal\">true<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-63\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_71b2541e516a1438e3e51a9e33b0d4d2\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\u270b Heads up \u00bb If using the Language negotiation feature, make sure that you do not annotate the routes with the <code>{_locale}<\/code> parameter. The <code>{_locale}<\/code> parameter will always override an <code>Accept-Language<\/code> header. If you want to support both features you will need to implement a custom <a href=\"https:\/\/symfony.com\/doc\/current\/event_dispatcher.html\">Event Listener<\/a>.<\/p>\n<p>When <code>set_locale_from_accept_language<\/code> is <code>true<\/code> the request locale is automatically set based on the <code>Accept-Language<\/code> value and based on the <code>enabled_locales<\/code> list.<\/p>\n<p>The <code>set_content_language_from_locale<\/code> sets the value of the <code>Content-Language<\/code> HTTP response header based on the request locale. You should have both <code>set_locale_from_accept_language<\/code> and <code>set_content_language_from_locale<\/code> options enabled if using this feature so that the framework will send the correct headers to clients.<\/p>\n<p>To test automatic locale detection, you need to configure your browser\u2019s preferred language list. Make sure the language you want your app to resolve to is at the top of the list. Here is my Chrome configuration, for example, which can be found at <em>Settings \u2192 Languages<\/em>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-55313\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-13.32.07.png\" alt=\"Chrome configuration screenshot found at settings -&gt; languages | Phrase\" width=\"2678\" height=\"1012\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-13.32.07.png 2678w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-13.32.07-300x113.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-13.32.07-1024x387.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-13.32.07-768x290.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-13.32.07-1536x580.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-13.32.07-2048x774.png 2048w\" sizes=\"(max-width: 2678px) 100vw, 2678px\" \/><\/p>\n<p>Then you need a route with no <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">{_locale}<\/span> parameter to allow Symfony to automatically resolve the locale:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-64\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ src\/Controller\/HomeController.php<\/span>\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HomeController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">AbstractController<\/span>\n<\/span>{\n    <span class=\"hljs-comment\">#&#91;Route('\/', name: 'home')]<\/span>\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">index<\/span><span class=\"hljs-params\">(TranslatorInterface $translator)<\/span>: <span class=\"hljs-title\">Response<\/span>\n    <\/span>{\n        <span class=\"hljs-comment\">\/\/ ...<\/span>\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-64\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_36f0b5643b0c1c774f4ef95755b2c13f\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>When you navigate to <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">\/<\/span>, Symfony will render the page in German, since the locale is preferred by the user per the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\">Accept-Language<\/span> header:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-55323\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-13.38.24.png\" alt=\"Homepage screenshot rendered in German | Phrase\" width=\"2966\" height=\"532\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-13.38.24.png 2966w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-13.38.24-300x54.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-13.38.24-1024x184.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-13.38.24-768x138.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-13.38.24-1536x276.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-29-at-13.38.24-2048x367.png 2048w\" sizes=\"(max-width: 2966px) 100vw, 2966px\" \/><\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-create-a-language-switcher\"><\/span>How do I create a language switcher?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We often want a way for users to manually switch the active locale. This is surprisingly easy to do in Symfony. Adding the following HTML code inside the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">header.html.twig<\/span> will show the current language and a list of available locales for the user to choose from:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-65\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ templates\/Layout\/header.html.twig<\/span>\n\n&lt;!-- ... --&gt;\n\n    &lt;div&gt;\n      &lt;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span>=\"<span class=\"hljs-title\">relative<\/span> <span class=\"hljs-title\">group<\/span>\"&gt;\n        &lt;<span class=\"hljs-title\">div<\/span> <span class=\"hljs-title\">data<\/span>-<span class=\"hljs-title\">bs<\/span>-<span class=\"hljs-title\">toggle<\/span>=\"<span class=\"hljs-title\">dropdown<\/span>\" <span class=\"hljs-title\">aria<\/span>-<span class=\"hljs-title\">expanded<\/span>=\"<span class=\"hljs-title\">false<\/span>\"&gt;\n\t\t\t\t\t\t&lt;<span class=\"hljs-title\">img<\/span> \n               <span class=\"hljs-title\">src<\/span>=\"<span class=\"hljs-title\">https<\/span>:\/\/<span class=\"hljs-title\">flagcdn<\/span>.<span class=\"hljs-title\">com<\/span>\/<\/span>{{app.request.locale == <span class=\"hljs-string\">'en'<\/span> ? <span class=\"hljs-string\">'gb'<\/span> : app.request.locale}}.svg<span class=\"hljs-string\">\"&gt;\n             &lt;\/img&gt;\n\t\t\t\t&lt;\/div&gt;\n        \n      &lt;div&gt;\n        &lt;a href=\"<\/span>{{ path(<span class=\"hljs-string\">'home'<\/span>, {_locale: <span class=\"hljs-string\">'en'<\/span>}) }}<span class=\"hljs-string\">\"&gt;\n\t\t\t\t  &lt;img\n            src=\"<\/span>https:<span class=\"hljs-comment\">\/\/flagcdn.com\/gb.svg\"<\/span>\n            alt=<span class=\"hljs-string\">\"Flag {{ 'locale.name.en'|trans(domain = 'locale') }}\"<\/span>\n          &gt;\n\t\t\t\t\t{{ <span class=\"hljs-string\">'English'<\/span> | trans }}\n        &lt;\/a&gt;\n        &lt;a href=<span class=\"hljs-string\">\"{{ path('home', {_locale: 'de'}) }}\"<\/span>&gt;\n\t\t\t\t  &lt;img \n            src=<span class=\"hljs-string\">\"https:\/\/flagcdn.com\/de.svg\"<\/span>\n            alt=<span class=\"hljs-string\">\"Flag {{ 'locale.name.de'|trans(domain = 'locale') }}\"<\/span>\n          &gt;\n\t\t\t\t\t{{ <span class=\"hljs-string\">'German'<\/span> | trans }}\n        &lt;\/a&gt;\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n\n&lt;!-- ... --&gt;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-65\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_63c35b49e60aeb560ee8cd6f0b6dba7b\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-55332\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.33.11.png\" alt=\"Language drop down screenshot | Phrase\" width=\"2680\" height=\"562\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.33.11.png 2680w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.33.11-300x63.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.33.11-1024x215.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.33.11-768x161.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.33.11-1536x322.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.33.11-2048x429.png 2048w\" sizes=\"(max-width: 2680px) 100vw, 2680px\" \/><\/p>\n<p>That\u2019s it! Now our visitors can use the drop-down to select the language of their choice.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-localize-my-data\"><\/span>How do I localize my data?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>The last important step left in our blog translations is the actual reviews content. We would like to have an Admin or Publisher write game reviews using a dashboard. Of course, we\u2019ll want to store this content in a database. So how do we allow for different translations of this content?<\/p>\n<p>Let\u2019s quickly setup a database . You will need to spin up a PostgreSQL server so that you can apply database migrations on it. Luckily for us, the project folder has a <code>docker-compose.yaml<\/code> which we can run with <a href=\"https:\/\/docs.docker.com\/compose\/\">Docker Compose<\/a>:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-66\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">$ docker-compose up -d<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-66\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_8642bd9ed0a3f39c2d054b259738513b\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>This will create a PostgreSQL container with the following configuration:<\/p>\n<ul>\n<li>host: localhost<\/li>\n<li>port: 5432<\/li>\n<li>database: app<\/li>\n<li>password: !ChangeMe!<\/li>\n<\/ul>\n<p>Next use the <code>make:entity<\/code> console command to generate some boilerplate code for the <code>Review<\/code> entity:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-67\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-string\">$<\/span> <span class=\"hljs-string\">symfony<\/span> <span class=\"hljs-string\">console<\/span> <span class=\"hljs-string\">make:entity<\/span>\n\n<span class=\"hljs-string\">Class<\/span> <span class=\"hljs-string\">name<\/span> <span class=\"hljs-string\">of<\/span> <span class=\"hljs-string\">the<\/span> <span class=\"hljs-string\">entity<\/span> <span class=\"hljs-string\">to<\/span> <span class=\"hljs-string\">create<\/span> <span class=\"hljs-string\">or<\/span> <span class=\"hljs-string\">update<\/span> <span class=\"hljs-string\">(e.g.<\/span> <span class=\"hljs-string\">GrumpyElephant):<\/span>\n\n<span class=\"hljs-string\">&gt;<\/span> <span class=\"hljs-string\">Review<\/span>\n<span class=\"hljs-string\">&gt;<\/span> \n\n<span class=\"hljs-attr\">created:<\/span> <span class=\"hljs-string\">src\/Entity\/Review.php<\/span>\n<span class=\"hljs-attr\">created:<\/span> <span class=\"hljs-string\">src\/Repository\/ReviewRepository.php<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-67\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_b97154de002b6504b79c7cc1c6509b73\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>This will create the following <code>Review<\/code> and <code>ReviewRepository<\/code> files:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-68\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ src\/Entity\/Review.php<\/span>\n\n<span class=\"hljs-meta\">&lt;?php<\/span>\n\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Entity<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Repository<\/span>\\<span class=\"hljs-title\">ReviewRepository<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Doctrine<\/span>\\<span class=\"hljs-title\">ORM<\/span>\\<span class=\"hljs-title\">Mapping<\/span> <span class=\"hljs-title\">as<\/span> <span class=\"hljs-title\">ORM<\/span>;\n\n<span class=\"hljs-comment\">#&#91;ORM\\Entity(repositoryClass: ReviewRepository::class)]<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Review<\/span>\n<\/span>{\n    <span class=\"hljs-comment\">#&#91;ORM\\Id]<\/span>\n    <span class=\"hljs-comment\">#&#91;ORM\\GeneratedValue]<\/span>\n    <span class=\"hljs-comment\">#&#91;ORM\\Column]<\/span>\n    <span class=\"hljs-keyword\">private<\/span> ?int $id = <span class=\"hljs-keyword\">null<\/span>;\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getId<\/span><span class=\"hljs-params\">()<\/span>: ?<span class=\"hljs-title\">int<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">$this<\/span>-&gt;id;\n    }<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-68\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_13a73959a6596af576173a5e8a5086c6\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-69\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ src\/Repository\/ReviewRepository.php<\/span>\n<span class=\"hljs-meta\">&lt;?php<\/span>\n\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Repository<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Entity<\/span>\\<span class=\"hljs-title\">Review<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Doctrine<\/span>\\<span class=\"hljs-title\">Bundle<\/span>\\<span class=\"hljs-title\">DoctrineBundle<\/span>\\<span class=\"hljs-title\">Repository<\/span>\\<span class=\"hljs-title\">ServiceEntityRepository<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Doctrine<\/span>\\<span class=\"hljs-title\">Persistence<\/span>\\<span class=\"hljs-title\">ManagerRegistry<\/span>;\n\n<span class=\"hljs-comment\">\/**\n * <span class=\"hljs-doctag\">@extends<\/span> ServiceEntityRepository&lt;Review&gt;\n *\n * <span class=\"hljs-doctag\">@method<\/span> Review|null find($id, $lockMode = null, $lockVersion = null)\n * <span class=\"hljs-doctag\">@method<\/span> Review|null findOneBy(array $criteria, array $orderBy = null)\n * <span class=\"hljs-doctag\">@method<\/span> Review&#91;]    findAll()\n * <span class=\"hljs-doctag\">@method<\/span> Review&#91;]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)\n *\/<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ReviewRepository<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">ServiceEntityRepository<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">__construct<\/span><span class=\"hljs-params\">(ManagerRegistry $registry)<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">parent<\/span>::__construct($registry, Review::class);\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">save<\/span><span class=\"hljs-params\">(Review $entity, bool $flush = false)<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;getEntityManager()-&gt;persist($entity);\n\n        <span class=\"hljs-keyword\">if<\/span> ($flush) {\n            <span class=\"hljs-keyword\">$this<\/span>-&gt;getEntityManager()-&gt;flush();\n        }\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">remove<\/span><span class=\"hljs-params\">(Review $entity, bool $flush = false)<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;getEntityManager()-&gt;remove($entity);\n\n        <span class=\"hljs-keyword\">if<\/span> ($flush) {\n            <span class=\"hljs-keyword\">$this<\/span>-&gt;getEntityManager()-&gt;flush();\n        }\n    }<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-69\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_c698d047849e6be603863bc363277673\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>At this point you can add extra fields in the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">Review<\/span> Entity. We want to have two extra fields for <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\">updated<\/span> and <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"5\">created<\/span> datetime events so go ahead and add those now:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-70\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ src\/Entity\/Review.php<\/span>\n\n<span class=\"hljs-meta\">&lt;?php<\/span> \n   \n  <span class=\"hljs-comment\">\/\/ inside class Review<\/span>\n\t<span class=\"hljs-comment\">#&#91;ORM\\Column(type: 'datetime')]<\/span>\n\t<span class=\"hljs-keyword\">protected<\/span> $created;\n\n\t<span class=\"hljs-comment\">#&#91;ORM\\Column(type: 'datetime', nullable: true)]<\/span>\n\t<span class=\"hljs-keyword\">protected<\/span> $updated;\n\n  <span class=\"hljs-comment\">#&#91;ORM\\PrePersist]<\/span>\n  <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">onPrePersist<\/span><span class=\"hljs-params\">()<\/span>\n  <\/span>{\n      <span class=\"hljs-keyword\">$this<\/span>-&gt;created = <span class=\"hljs-keyword\">new<\/span> \\DateTime(<span class=\"hljs-string\">\"now\"<\/span>);\n  }\n    \n  <span class=\"hljs-comment\">#&#91;ORM\\PreUpdate]<\/span>\n  <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">onPreUpdate<\/span><span class=\"hljs-params\">()<\/span>\n  <\/span>{\n      <span class=\"hljs-keyword\">$this<\/span>-&gt;updated = <span class=\"hljs-keyword\">new<\/span> \\DateTime(<span class=\"hljs-string\">\"now\"<\/span>);\n  }\n}<span class=\"hljs-comment\">\/\/end class Review<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-70\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_5e87e30c57a42fe4dd4636a9f876f98f\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Next we\u2019ll use a\u00a0well known\u00a0bundle called\u00a0<a href=\"https:\/\/github.com\/KnpLabs\/DoctrineBehaviors\">DoctrineBehaviors<\/a>\u00a0from KnpLabs that adds a few interesting traits to our entities; one of these is the\u00a0<code>TranslatableTrait<\/code>.\u00a0This trait allows our models to create easily associate translations with an entity. Let&#8217;s see how we can use it for our example project.<\/p>\n<p>First things first, you need to install it:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-71\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">$ composer require knplabs\/doctrine-behaviors<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-71\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_acaeab74840fe5f813186c00cad1c71a\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\ud83d\udd17 Resource \u00bb\u00a0Review the different options to configure the Translatable trait <a href=\"https:\/\/github.com\/KnpLabs\/DoctrineBehaviors\/blob\/master\/docs\/translatable.md\">in the docs<\/a>.<\/p>\n<p>Next, we need to make the <code>Review<\/code> entity implement Doctrine <code>TranslatableInterface<\/code> and use the <code>TranslatableTrait<\/code> from the bundle:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-72\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ src\/Entity\/Review.php<\/span>\n\n<span class=\"hljs-meta\">&lt;?php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Entity<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Doctrine<\/span>\\<span class=\"hljs-title\">ORM<\/span>\\<span class=\"hljs-title\">Mapping<\/span> <span class=\"hljs-title\">as<\/span> <span class=\"hljs-title\">ORM<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Knp<\/span>\\<span class=\"hljs-title\">DoctrineBehaviors<\/span>\\<span class=\"hljs-title\">Contract<\/span>\\<span class=\"hljs-title\">Entity<\/span>\\<span class=\"hljs-title\">TranslatableInterface<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Knp<\/span>\\<span class=\"hljs-title\">DoctrineBehaviors<\/span>\\<span class=\"hljs-title\">Model<\/span>\\<span class=\"hljs-title\">Translatable<\/span>\\<span class=\"hljs-title\">TranslatableTrait<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Repository<\/span>\\<span class=\"hljs-title\">ReviewRepository<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Doctrine<\/span>\\<span class=\"hljs-title\">ORM<\/span>\\<span class=\"hljs-title\">Mapping<\/span> <span class=\"hljs-title\">as<\/span> <span class=\"hljs-title\">ORM<\/span>;\n\n<span class=\"hljs-comment\">#&#91;ORM\\Entity(repositoryClass: Review::class)]<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Review<\/span> <span class=\"hljs-keyword\">implements<\/span> <span class=\"hljs-title\">TranslatableInterface<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">TranslatableTrait<\/span>;\n     \n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-72\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_1408ed26811728f94c37281aeb2d5754\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Then you will need to create another Entity that provides the Translatable fields. This entity should implement the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">TranslationInterface<\/span> and use the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"4\">TranslationTrait<\/span> (<span role=\"img\" aria-label=\"\u26a0\ufe0f\">\u26a0\ufe0f<\/span>\u00a0<span class=\"notion-enable-hover\" data-token-index=\"6\">not<\/span> <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"8\">TranslatableTrait<\/span>). Follow the same process by using the <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"10\">make:entity<\/span> command to generate the boilerplate files for you:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-73\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">$ symfony console make:entity\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">Class<\/span> <span class=\"hljs-title\">name<\/span> <span class=\"hljs-title\">of<\/span> <span class=\"hljs-title\">the<\/span> <span class=\"hljs-title\">entity<\/span> <span class=\"hljs-title\">to<\/span> <span class=\"hljs-title\">create<\/span> <span class=\"hljs-title\">or<\/span> <span class=\"hljs-title\">update<\/span> (<span class=\"hljs-title\">e<\/span>.<span class=\"hljs-title\">g<\/span>. <span class=\"hljs-title\">GrumpyElephant<\/span>):\n\n&gt; <span class=\"hljs-title\">ReviewTranslation<\/span>\n&gt; \n\n<span class=\"hljs-title\">created<\/span>: <span class=\"hljs-title\">src<\/span>\/<span class=\"hljs-title\">Entity<\/span>\/<span class=\"hljs-title\">ReviewTranslation<\/span>.<span class=\"hljs-title\">php<\/span>\n<span class=\"hljs-title\">created<\/span>: <span class=\"hljs-title\">src<\/span>\/<span class=\"hljs-title\">Repository<\/span>\/<span class=\"hljs-title\">ReviewTranslationRepository<\/span>.<span class=\"hljs-title\">php<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-73\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_f445512e0d0098368023eff3e98463e0\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Delete the <code>ReviewTranslationRepository.php<\/code> since we are not going to use it.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-74\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">$ rm src\/Repository\/ReviewTranslationRepository.php<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-74\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_8add75a23dca8737acf1f37e54700b8b\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Then add the <code>TranslationInterface<\/code> and the translatable fields in the <code>ReviewTranslation<\/code> entity. We need two fields: <code>title<\/code> of type string and <code>content<\/code> of type text.<\/p>\n<p>This should look like this:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-75\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ src\/Entity\/ReviewTranslation.php<\/span>\n\n<span class=\"hljs-meta\">&lt;?php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Entity<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Doctrine<\/span>\\<span class=\"hljs-title\">ORM<\/span>\\<span class=\"hljs-title\">Mapping<\/span> <span class=\"hljs-title\">as<\/span> <span class=\"hljs-title\">ORM<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Knp<\/span>\\<span class=\"hljs-title\">DoctrineBehaviors<\/span>\\<span class=\"hljs-title\">Contract<\/span>\\<span class=\"hljs-title\">Entity<\/span>\\<span class=\"hljs-title\">TranslationInterface<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Knp<\/span>\\<span class=\"hljs-title\">DoctrineBehaviors<\/span>\\<span class=\"hljs-title\">Model<\/span>\\<span class=\"hljs-title\">Translatable<\/span>\\<span class=\"hljs-title\">TranslationTrait<\/span>;\n\n<span class=\"hljs-comment\">\/**\n * <span class=\"hljs-doctag\">@ORM<\/span>\\Entity\n *\/<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ReviewTranslation<\/span> <span class=\"hljs-keyword\">implements<\/span> <span class=\"hljs-title\">TranslationInterface<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">TranslationTrait<\/span>;\n\n    <span class=\"hljs-comment\">#&#91;ORM\\Id]<\/span>\n    <span class=\"hljs-comment\">#&#91;ORM\\GeneratedValue]<\/span>\n    <span class=\"hljs-comment\">#&#91;ORM\\Column]<\/span>\n    <span class=\"hljs-keyword\">protected<\/span> $id;\n\n\t\t<span class=\"hljs-comment\">#&#91;ORM\\Column(type: 'string', unique: true, length: 255)]<\/span>\n    <span class=\"hljs-keyword\">protected<\/span> $title;\n\n    <span class=\"hljs-comment\">#&#91;ORM\\Column(type: 'text')]<\/span>\n    <span class=\"hljs-keyword\">protected<\/span> ?string $content = <span class=\"hljs-keyword\">null<\/span>;\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getTitle<\/span><span class=\"hljs-params\">()<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">$this<\/span>-&gt;title;\n    }\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">setTitle<\/span><span class=\"hljs-params\">($title)<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;title = $title;\n    }\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getContent<\/span><span class=\"hljs-params\">()<\/span>: ?<span class=\"hljs-title\">string<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">$this<\/span>-&gt;content;\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">setContent<\/span><span class=\"hljs-params\">(string $content)<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;content = $content;\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-75\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_75abe9cc99e2eb5c4d2293b196b2ca46\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Note that by default there is no real association between the <code>ReviewTranslation<\/code> and <code>Review<\/code> entities. Instead the bundle relies on merging new translations to the specified model by using the <code>mergeNewTranslations<\/code> method on that model. When we add a locale translation to the <code>Review<\/code> entity we need to subsequently call <code>mergeNewTranslations<\/code> so that it will create entries in the <code>ReviewTranslation<\/code> with the primary key of <code>Review<\/code>.<\/p>\n<p>Run the following from the command line to create database tables:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-76\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">$ php bin\/console doctrine:database:create<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-76\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_7704e3612684637ddaabc77da80abb38\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Then run the following command to create all the necessary migrations for the table schemas.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-77\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">$ bin\/<span class=\"hljs-built_in\">console<\/span> make:migration<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-77\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_cc483b15b365fe0035271e31fdec054f\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Now run the migrations to apply them in the database.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-78\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">$ php bin\/<span class=\"hljs-built_in\">console<\/span> doctrine:migrations:migrate<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-78\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_40ff94c46f32fe3768349221340d4d92\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Check your database. You should be able to see two tables created, one for the\u00a0<span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">Review<\/span>\u00a0and one for the\u00a0<span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\">ReviewTranslation<\/span>:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-79\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql\"><span class=\"hljs-keyword\">CREATE<\/span> <span class=\"hljs-keyword\">TABLE<\/span> review\n(\n    <span class=\"hljs-keyword\">id<\/span> <span class=\"hljs-built_in\">integer<\/span> <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span>,\n    created <span class=\"hljs-built_in\">timestamp<\/span>(<span class=\"hljs-number\">0<\/span>) <span class=\"hljs-keyword\">without<\/span> <span class=\"hljs-built_in\">time<\/span> zone <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span>,\n    <span class=\"hljs-keyword\">updated<\/span> <span class=\"hljs-built_in\">timestamp<\/span>(<span class=\"hljs-number\">0<\/span>) <span class=\"hljs-keyword\">without<\/span> <span class=\"hljs-built_in\">time<\/span> zone <span class=\"hljs-literal\">NULL<\/span>\n\t\tprice <span class=\"hljs-built_in\">integer<\/span> <span class=\"hljs-literal\">NULL<\/span>,\n);\n\n<span class=\"hljs-keyword\">CREATE<\/span> <span class=\"hljs-keyword\">TABLE<\/span> review_translation\n(\n    <span class=\"hljs-keyword\">id<\/span> <span class=\"hljs-built_in\">integer<\/span> <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span>,\n    translatable_id <span class=\"hljs-built_in\">integer<\/span> <span class=\"hljs-literal\">NULL<\/span>,\n    title <span class=\"hljs-built_in\">character<\/span> <span class=\"hljs-built_in\">varying<\/span>(<span class=\"hljs-number\">255<\/span>) <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span>,\n    <span class=\"hljs-keyword\">content<\/span> <span class=\"hljs-built_in\">text<\/span> <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span>,\n    locale <span class=\"hljs-built_in\">character<\/span> <span class=\"hljs-built_in\">varying<\/span>(<span class=\"hljs-number\">5<\/span>) <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span>\n);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-79\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_16aba693e1bbec996f8680fa6c2e78be\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<h2><span class=\"ez-toc-section\" id=\"updating-translatable-models\"><\/span>Updating translatable models<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>When creating or updating the <code>Review<\/code> entity, you need to use the <code>translate<\/code> method provided by DoctrineBehavior\u2019s <code>TranslatableInterface<\/code>. Here is an example interaction for saving a model in the database in a Controller form Action handler:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-80\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-meta\">&lt;?php<\/span>\n\n<span class=\"hljs-comment\">\/\/ src\/Controller\/HomeController.php<\/span>\n\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Controller<\/span>;\n\n<span class=\"hljs-comment\">\/\/ Import declarations<\/span>\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HomeController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">AbstractController<\/span>\n<\/span>{\n\n\t\t<span class=\"hljs-comment\">#&#91;Route('\/review\/new']<\/span>\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">createReview<\/span><span class=\"hljs-params\">(Request $request)<\/span>: <span class=\"hljs-title\">Response<\/span>\n    <\/span>{\n        $review = <span class=\"hljs-keyword\">new<\/span> Review();\n        <span class=\"hljs-comment\">\/\/ Code to create a form<\/span>\n\t\t\t\t$form = <span class=\"hljs-keyword\">$this<\/span>-&gt;createForm(<span class=\"hljs-keyword\">new<\/span> ReviewType(), $review);\n        $form-&gt;handleRequest($request);\n\n        <span class=\"hljs-keyword\">if<\/span> ($form-&gt;isValid())\n        {\n\t\t\t\t\t\t$data = $form-&gt;getData();\n            $em = <span class=\"hljs-keyword\">$this<\/span>-&gt;getDoctrine()-&gt;getManager();\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t  <span class=\"hljs-comment\">\/\/ Translate content based on specified locale specified in the form<\/span>\n            $review-&gt;translate($data&#91;<span class=\"hljs-string\">'locale'<\/span>])-&gt;setContent($data&#91;<span class=\"hljs-string\">'content'<\/span>])\n\t\t\t\t\t  $review-&gt;translate($data&#91;<span class=\"hljs-string\">'locale'<\/span>])-&gt;setTitle($data&#91;<span class=\"hljs-string\">'title'<\/span>])\n\t\t\t\t\n            $em-&gt;persist($review);\n\n\t\t\t\t\t  <span class=\"hljs-comment\">\/\/ \u2757\ufe0fImportant\u2757\ufe0f Call this method to assotiate translations<\/span>\n            <span class=\"hljs-comment\">\/\/ with the saved Entity<\/span>\n\t\t\t\t\t\t$review-&gt;mergeNewTranslations();\n\n            $em-&gt;flush(); <span class=\"hljs-comment\">\/\/ done!<\/span>\n\n            <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">$this<\/span>-&gt;redirect(<span class=\"hljs-keyword\">$this<\/span>-&gt;generateUrl(<span class=\"hljs-string\">'your_next_url'<\/span>));\n         }\n\n         <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">$this<\/span>-&gt;render(<span class=\"hljs-string\">'review\/create.html.twig'<\/span>, &#91;\n\t          <span class=\"hljs-string\">'form'<\/span> =&gt; $form-&gt;createView(),\n\t\t     ]);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-80\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_174185dbd370a989d7329b756113ea8f\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Here we are using a <code>ReviewType<\/code> class which contains the form data for the Review Entity. We will show how it looks like in a moment.<\/p>\n<p>Now check your database. You should be able to see both a new row in the <code>review<\/code> table and associated translation rows in <code>review_translation<\/code>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-55407\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-30-at-14.21.29.png\" alt=\"Screenshot of the review table | Phrase\" width=\"2466\" height=\"532\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-30-at-14.21.29.png 2466w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-30-at-14.21.29-300x65.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-30-at-14.21.29-1024x221.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-30-at-14.21.29-768x166.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-30-at-14.21.29-1536x331.png 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-30-at-14.21.29-2048x442.png 2048w\" sizes=\"(max-width: 2466px) 100vw, 2466px\" \/><\/p>\n<p>Using the above flow you should be able to translate the rest of the site content with the Game Reviews for all supported locales.<\/p>\n<p>Finally we can populate the list of reviews when switching to the selected locale:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-81\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-meta\">&lt;?php<\/span>\n\n<span class=\"hljs-comment\">\/\/ src\/Controller\/HomeController.php<\/span>\n\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Controller<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Component<\/span>\\<span class=\"hljs-title\">HttpFoundation<\/span>\\<span class=\"hljs-title\">Response<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Component<\/span>\\<span class=\"hljs-title\">Routing<\/span>\\<span class=\"hljs-title\">Annotation<\/span>\\<span class=\"hljs-title\">Route<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Entity<\/span>\\<span class=\"hljs-title\">Review<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Doctrine<\/span>\\<span class=\"hljs-title\">ORM<\/span>\\<span class=\"hljs-title\">EntityManagerInterface<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Bundle<\/span>\\<span class=\"hljs-title\">FrameworkBundle<\/span>\\<span class=\"hljs-title\">Controller<\/span>\\<span class=\"hljs-title\">AbstractController<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Contracts<\/span>\\<span class=\"hljs-title\">Translation<\/span>\\<span class=\"hljs-title\">TranslatorInterface<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Model<\/span>\\<span class=\"hljs-title\">NotificationBanner<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HomeController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">AbstractController<\/span>\n<\/span>{\n    <span class=\"hljs-comment\">#&#91;Route('\/{_locale}\/', name: 'home')]<\/span>\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">index<\/span><span class=\"hljs-params\">(\n        TranslatorInterface $translator,\n        EntityManagerInterface $entityManager\n    )<\/span>: <span class=\"hljs-title\">Response<\/span>\n    <\/span>{   \n        $reviews = $entityManager-&gt;getRepository(Review::class)-&gt;findAll();\n\t\t\t\t\n        <span class=\"hljs-comment\">\/\/ ...<\/span>\n\n\t\t\t\t<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">$this<\/span>-&gt;render(<span class=\"hljs-string\">'home.html.twig'<\/span>, &#91;\n            <span class=\"hljs-string\">'messages'<\/span> =&gt; $messages,\n            <span class=\"hljs-string\">'reviews'<\/span> =&gt; $reviews\n        ]);\n    }<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-81\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_b6972a546b8382e9d40e4cc3161ed23e\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Then update the associated Twig template:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-82\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">{<span class=\"hljs-comment\"># templates\/home.html.twig #}<\/span>\n\n{% set locale = app.request.getLocale() %}\n\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n\n&lt;ul&gt;\n\t\t{% <span class=\"hljs-keyword\">if<\/span> reviews|length &gt; <span class=\"hljs-number\">0<\/span> %}\n\t\t\t{% <span class=\"hljs-keyword\">for<\/span> review in reviews %}\n\t\t\t\t&lt;li&gt;\n\t\t\t\t  &lt;h1&gt;{{review.translate(locale).getTitle()}}&lt;\/h1&gt;\n\t\t\t\t\t&lt;p&gt;{{review.translate(locale).getContent()}}&lt;\/p&gt;\n\t      &lt;\/li&gt;\n\t\t\t{% <span class=\"hljs-keyword\">endfor<\/span> %}\n    {% <span class=\"hljs-keyword\">endif<\/span> %}\n&lt;\/ul&gt;\n...<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-82\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_432aac6113a7c73f5219f1e3164af199\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-55413\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.59.54-scaled.jpg\" alt=\"The review data translated to German | Phrase\" width=\"2560\" height=\"1290\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.59.54-scaled.jpg 2560w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.59.54-300x151.jpg 300w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.59.54-1024x516.jpg 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.59.54-768x387.jpg 768w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.59.54-1536x774.jpg 1536w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/06\/Screenshot-2023-03-31-at-11.59.54-2048x1032.jpg 2048w\" sizes=\"(max-width: 2560px) 100vw, 2560px\" \/><\/p>\n<h2><span class=\"ez-toc-section\" id=\"how-do-i-accept-form-data-for-a-localized-model\"><\/span>How do I accept form data for a localized model?<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>To create the form type for <code>Review<\/code> that accepts a specific locale translation data, you need to create a custom form named <code>ReviewType<\/code>. This is how it will look like in code:<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-83\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-meta\">&lt;?php<\/span>\n\n<span class=\"hljs-comment\">\/\/ src\/Form\/ReviewType.php<\/span>\n\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Form<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Entity<\/span>\\<span class=\"hljs-title\">Review<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Component<\/span>\\<span class=\"hljs-title\">Form<\/span>\\<span class=\"hljs-title\">AbstractType<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Component<\/span>\\<span class=\"hljs-title\">Form<\/span>\\<span class=\"hljs-title\">Extension<\/span>\\<span class=\"hljs-title\">Core<\/span>\\<span class=\"hljs-title\">Type<\/span>\\<span class=\"hljs-title\">TextType<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Component<\/span>\\<span class=\"hljs-title\">Form<\/span>\\<span class=\"hljs-title\">Extension<\/span>\\<span class=\"hljs-title\">Core<\/span>\\<span class=\"hljs-title\">Type<\/span>\\<span class=\"hljs-title\">ChoiceType<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Component<\/span>\\<span class=\"hljs-title\">Form<\/span>\\<span class=\"hljs-title\">Extension<\/span>\\<span class=\"hljs-title\">Core<\/span>\\<span class=\"hljs-title\">Type<\/span>\\<span class=\"hljs-title\">TextareaType<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Component<\/span>\\<span class=\"hljs-title\">Form<\/span>\\<span class=\"hljs-title\">FormBuilderInterface<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Symfony<\/span>\\<span class=\"hljs-title\">Component<\/span>\\<span class=\"hljs-title\">OptionsResolver<\/span>\\<span class=\"hljs-title\">OptionsResolver<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ReviewType<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">AbstractType<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">buildForm<\/span><span class=\"hljs-params\">(FormBuilderInterface $builder, array $options)<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        $builder\n            -&gt;add(<span class=\"hljs-string\">'title'<\/span>, TextType::class, &#91;\n                <span class=\"hljs-string\">'required'<\/span> =&gt; <span class=\"hljs-keyword\">true<\/span>,\n                <span class=\"hljs-string\">'mapped'<\/span> =&gt; <span class=\"hljs-keyword\">false<\/span>\n            ])\n            -&gt;add(<span class=\"hljs-string\">'locale'<\/span>, ChoiceType::class, &#91;\n                <span class=\"hljs-string\">'required'<\/span> =&gt; <span class=\"hljs-keyword\">true<\/span>,\n                <span class=\"hljs-string\">'choices'<\/span> =&gt; &#91;<span class=\"hljs-string\">'en'<\/span> =&gt; <span class=\"hljs-string\">'English'<\/span>, <span class=\"hljs-string\">'de'<\/span> =&gt; <span class=\"hljs-string\">'German'<\/span>],\n                <span class=\"hljs-string\">'mapped'<\/span> =&gt; <span class=\"hljs-keyword\">false<\/span>\n            ])\n            -&gt;add(<span class=\"hljs-string\">'content'<\/span>, TextareaType::class, &#91;<span class=\"hljs-string\">'required'<\/span> =&gt; <span class=\"hljs-keyword\">true<\/span>, <span class=\"hljs-string\">'mapped'<\/span> =&gt; <span class=\"hljs-keyword\">false<\/span>]);\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">configureOptions<\/span><span class=\"hljs-params\">(OptionsResolver $resolver)<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        $resolver-&gt;setDefaults(&#91;\n            <span class=\"hljs-string\">'data_class'<\/span> =&gt; Review::class,\n            <span class=\"hljs-string\">'translation_domain'<\/span> =&gt; <span class=\"hljs-string\">'forms'<\/span>,\n        ]);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-83\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_81846be909f4db913624feabb8731b69\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Here we create a custom form with the required Review Entity fields (<code>title<\/code> and <code>content<\/code>) and a special <code>locale<\/code> field that allows the user to choose a language.<\/p>\n<p>Then the user will have to fill the form select the locale to provide translations and submit the POST request.<\/p>\n<p>The controller will take the form data and save the model translations using the previous guide on how to update the Translatable Models.<\/p>\n<p>\ud83e\udd3f <em>Go Deeper \u00bb<\/em> <em>Learn how to create advanced <a href=\"https:\/\/symfony.com\/doc\/current\/forms.html#form-types\">Form Types<\/a> and <a href=\"https:\/\/symfony.com\/doc\/current\/forms.html#processing-forms\">how to process forms<\/a>.<\/em><\/p>\n<p>Similar forms for the <code>update<\/code> actions should be provided for any other entity that you want to provide database translations.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"take-symfony-localization-to-the-next-level\"><\/span>Take Symfony localization to the next level<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We hope you enjoyed building this Symfony i18n project with us. If you\u2019re ready to level up your app translation process, check out the Phrase Localization Platform.<\/p>\n<p>With its dedicated software localization solution, <a href=\"https:\/\/phrase.com\/platform\/strings\/\">Phrase Strings<\/a>, you can manage translation strings for your web or mobile app more easily and efficiently than ever.<\/p>\n<p>With a robust API to automate translation workflows and integrations with GitHub, GitLab, Bitbucket, and other popular software platforms, Phrase Strings can do the heavy lifting for you to stay focused on your code.<\/p>\n<p>Phrase Strings comes with a full-featured strings editor where translators can pick up the content for translation you had pushed. As soon as they\u2019re done with translation, you can pull the translated content back into your project automatically.<\/p>\n<p>As your software grows and you want to scale, our integrated suite lets you connect Phrase Strings with a cutting-edge translation management system (TMS) to fully leverage traditional CAT tools and AI-powered machine translation capabilities.<\/p>\n<p>Check out all\u00a0<a href=\"https:\/\/phrase.com\/roles\/developers\/\">Phrase features for developers<\/a>\u00a0and see for yourself how they can streamline your software localization workflows from the get-go.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n\n<div id=\"acf\/blog-cta-block_be6f3436abbf1ce9870e9a83b3916621\" class=\"pxblock pxblock--blog-cta bg--green image--orientation-landscape\">\n\t<div class=\"block-container\">\n\t\t\t\t\t<div class=\"image image--align-middle\">\n\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"1260\" height=\"992\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/String_Hero.png\" class=\"attachment-original size-original\" alt=\"String Management UI visual | Phrase\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/String_Hero.png 1260w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/String_Hero-300x236.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/String_Hero-1024x806.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/String_Hero-768x605.png 768w\" sizes=\"(max-width: 1260px) 100vw, 1260px\" \/>\t\t\t<\/div>\n\t\t\t\t<div class=\"content\">\n\t\t\t<p class=\"subhead\">Phrase Strings<\/p>\n<p class=\"secondary h6\">Take your web or mobile app global without any hassle<\/p>\n<div class=\"text--copy\">\n<p class=\"small\">Adapt your software, website, or video game for global audiences with the leanest and most realiable software localization platform.<\/p>\n<p><a class=\"btn btn--outline\" href=\"https:\/\/phrase.com\/platform\/strings\/\">Explore Phrase Strings<\/a><\/p>\n<\/div>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Discover the power of Symfony, one of the trendiest PHP frameworks, and unlock its potential to expand your app&#8217;s global user base by implementing robust multilingual support.<\/p>\n","protected":false},"author":61,"featured_media":2612,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_stopmodifiedupdate":true,"_modified_date":"","_searchwp_excluded":"","footnotes":""},"categories":[40],"class_list":["post-8719","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-software-localization"],"acf":[],"_links":{"self":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/8719"}],"collection":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/users\/61"}],"replies":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/comments?post=8719"}],"version-history":[{"count":23,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/8719\/revisions"}],"predecessor-version":[{"id":90956,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/8719\/revisions\/90956"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/media\/2612"}],"wp:attachment":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/media?parent=8719"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/categories?post=8719"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}