# I18n Internationalization is always tricky as it involves several layers, from routing to controller logic and view outputs to URL building and redirecting. ## Basics Set up a default language in your configs: ```php 'Config' => [ 'language' => 'de', ], ``` Any `Configure::read('Config.language')` or `I18n::getLocale()` call should return that default language. ## Detecting the user language To detect and switch based on language, you can leverage the Language class. Either you manually use `findMatches()` to sort through, or you use the convenience method `findFirstMatch()`: ```php use Tools\Utility\Language; $language = Language::findFirstMatch(['de', 'en']); ``` It will use the language with the highest weight supported by the user agent data provided. Note: When you have intl installed, you can also try to use the CakePHP core LocaleSelectorMiddleware. ## Session based language switch In your AppController you can now do: ```php /** * @return void */ public function initialize() { parent::initialize(); // First check the session $language = $this->request->getSession()->read('Config.language'); // Then check the browser preference for the whitelisted languages if (!$language) { $language = Language::findFirstMatch(Configure::read('Config.supportedLanguages')); } // Overwrite the system default if ($language) { Configure::write('Config.language', substr($language, 0, 2)); I18n::setLocale($language); } ``` You then just need a switch on the website that allows the other to change the language (by writing it into the session): ```php Configure->read('Config.language') === 'de') { echo $this->Html->image('flag_de.png', ['title' => __('German')]); } else { echo $this->Form->postLink($this->Html->image('flag_de.png'), ['prefix' => false, 'plugin' => 'Tools', 'controller' => 'ShuntRequest', 'action' => 'language', 'de'], ['block' => true, 'escape' => false, 'title' => __('German')]); } ?> Configure->read('Config.language') === 'en') { echo $this->Html->image('flag_en.png', ['title' => __('English')]); } else { echo $this->Form->postLink($this->Html->image('flag_en.png'), ['prefix' => false, 'plugin' => 'Tools', 'controller' => 'ShuntRequest', 'action' => 'language', 'en'], ['block' => true, 'escape' => false, 'title' => __('English')]); }?> ``` Make sure you included the routes for the Tools plugin for this to work or set them up manually in the `src/Config/routes.php` file. This example also uses [Shim.Configure helper](https://github.com/dereuromark/cakephp-shim) to avoid use statements. ## Using language in URLs Instead of the "easy" session-based way you might want to use URLs like `domain.tld/de/controller/action` instead for the other languages of the website. This can however affect also your view layer as you will have to overwrite the URL generation there to always include the new `language` param. Switch out the above session check then with ```php $language = $this->request->getParam('language'); ``` (before CakePHP 3.4+ it would be `param()`) And make sure your routes are all adjusted to accept and parse the language param: ```php Router::scope('/', function (RouteBuilder $routes) { $routes->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']); $routes->connect( '/:language/:controller/:action/*', [], ['language' => 'de'] ); ... } ``` In this case only `/de/...` is a language param, since `/...` (without any) would continue to use the default language (English). You can of course also make the regexp here `'language' => 'de|en'` for all languages to be a param in the URL. Only the root `/` would then be allowed to fallback to the default one. In this case you should have a canonical (pointed to the root) on that action to avoid it being treated as duplicate content. Now you just have to overwrite `Controller::redirect()` for the controller redirects and `HtmlHelper::link()`/`UrlHelper::build()` etc for the view level URLs generated. For a more sophisticated approach on that routing topic you might want to look into [the I18n plugin](https://github.com/ADmad/cakephp-i18n). ## Language vs Locale A locale is a combination of language and region (usually a country). Most would not use locales here for the switch, but languages. The locales are merely used internally for the presentation details. Examples for Germany/German: - `de_DE`/`de_AT` are some of the possible locale codes - `de` is the language (iso code) and internally uses one of those two locales Examples for USE/English: - `en_US`/`en_GB` are some of the possible locale codes - `en` is the language (iso code) and internally uses one of those two locales See [here](http://sandbox.dereuromark.de/export/) for a list of possible languages and their codes.