CakeDC Blog


i18n routes with CakePHP 1.3

Internationalizing a CakePHP application can be tricky when it comes to deal with i18n urls. We will see in this article how the Custom route classes introduced by CakePHP 1.3 could be used to add the current language to your urls in a few lines of code.

EDIT: This proof of concept has now been improved and a better version of the code below can be found in CakeDC's I18n plugin on Github


This article will not go too deep in internationalizing an application as many resources already exist about it. We suppose the following:

  • Your application defines the current language on given the language code passed in the url
  • The available languages are configured via Configure::write('Config.languages', array('eng', 'fre', 'deu'));
  • You use the CakePHP array syntax for defining urls:
    • $this->Html->link('link', array('controller' => 'posts', 'action' => 'view', $post['Post']['id']));
    • $this->redirect(array('controller' => 'posts', 'action' => 'index'));
    • Router::url(array('controller' => 'posts', 'action' => 'index'), true);

Custom routes were already introduced by Mark Story on his blog, so we will not do it again here... before continuing be sure you have read "Using custom Route classes in CakePHP"

Show me some code!


As I said (or not), routes are probably the best place for customizing your urls and add information in them... much more better at least than overriding the Helper::url() method in an AppHelper class!

Custom routes introduced a way to customize how routes are processed in a very easy and powerful way (i.e ~20 lines of code). It is a bit like wrapping the Router class in CakePHP 1.2, a good example of this was the CroogoRouter.

First, we are going to create an I18nRoute class extending CakeRoute in the "/libs/routes/i18n_route.php" file. Here is its code:

class I18nRoute extends CakeRoute {
 * Constructor for a Route
 * Add a regex condition on the lang param to be sure it matches the available langs
 * @param string $template Template string with parameter placeholders
 * @param array $defaults Array of defaults for the route.
 * @param string $params Array of parameters and additional options for the Route
 * @return void
 * @access public
	public function __construct($template, $defaults = array(), $options = array()) {
		$options = array_merge((array)$options, array(
			'lang' => join('|', Configure::read('Config.languages'))
		parent::__construct($template, $defaults, $options);

 * Attempt to match a url array.  If the url matches the route parameters + settings, then
 * return a generated string url.  If the url doesn't match the route parameters false will be returned.
 * This method handles the reverse routing or conversion of url arrays into string urls.
 * @param array $url An array of parameters to check matching with.
 * @return mixed Either a string url for the parameters if they match or false.
 * @access public
	public function match($url) {
		if (empty($url['lang'])) {
			$url['lang'] = Configure::read('Config.language');
		return parent::match($url);


The most important part of the code is in the "match()" method. We just add the current language to the url "lang" named param if it was not set. The constructor was also overriden to add a regex pattern for the "lang" param. Thus, only lang prefixes defined in your list of available languages will be parsed by the route.

Define your routes

It is now time to use this custom route in your application. Here is how the default route for pages could be defined in "/config/routes.php":

App::import('Lib', 'routes/I18nRoute');
Router::connect('/:lang/pages/*', array('controller' => 'pages', 'action' => 'display'), array('routeClass' => 'I18nRoute'));
  1. import the library file containing the custom route
  2. add a ":lang" param in where you want the language code appear in the url
  3. tell the Router you want to use this custom class (third param)

Link from everywhere!

Now you won't have to worry about the language code transmitted in your urls... every generated link will contain the current language code. If you want to switch the language (for instance switching to the French version of your application), you will just have to add the "lang" param to the url array.

Here are some examples of urls which would be generated on the "/eng/posts/index" page:

$this->Html->link(__('French', true), array_merge($this->passedArgs, array('lang' => 'fre'))); // /fre/posts/index
$this->Html->link('link', array('controller' => 'posts', 'action' => 'view', $post['Post']['id'])); // /eng/posts/view/2


This code is experimental and the article shows you how to use CustomRoutes to implement this basic feature. Many improvements could be added to fit your needs (no language code for the default application lang, short languages code...)

Even if the tests we made were successful, we have not used this code in production yet so there may be "real word" use cases that are not handled correctly with this solution... if you find one, please tell us in the comments!

Latest articles

Create Google app for web oauth2 login step by step

Here's a step by step tutorial about how to create a web oauth2 app in Google dashboard.

  • Add some cool name for your new Google app project and click "Create"
  • Under "Library" section, create a new Google+ API project
  • Click "Enable" in the dashboard tab
  • Under "Credentials" menu, click "Oauth consent screen" tab and enter some cool name to be displayed to users when requesting their access to your application. Then click "Save".
  • Under "Credentials" menu, click "Create credentials" and select "Oauth client ID".
  • Now click "Web application" radio, and type your domain name and oauth callback
    • Under "Authorized Javascript origins", add your domain name:
    • Under "Authorized redirect URIs", add all the allowed callback url's to your application. For example if you are using CakeDC/Users Plugin, you'll need to add
  • Then click "Save"
  • Copy the Iauth client and secret id's into your application configuration
  • Be careful, some browsers will append blank spaces to the codes, remove any extra blank space (trim)
  • Ensure the API is enabled, you can test your application now and check there is "Traffic" displayed
  You have now a Google app configured to provide Oauth2 login to your web application. Enjoy!                  

Login with Google Oauth2 in CakePHP using CakeDC/Users Plugin

This article is inspired by this question in Stack Overflow and belongs to a series of articles describing the step by step tutorial to configure CakeDC Users Plugin with the most commonly used Oauth2 providers, in this case we'll configure Google login. We'll assume you have a working CakePHP application with no Auth configured yet.


Use composer to install the CakeDC Users Plugin and the required oauth2 providers To be able to configure the callbacks in Google dashboard, you'll need to create a virtual host for you application. You don't need a working domain name, you could use something like "" but Google requires a domain name (no localhost). composer require cakedc/users:@stable composer require league/oauth2-google:@stable Load it from your bootstrap.php file Plugin::load('CakeDC/Users', ['routes' => true, 'bootstrap' => true]); Run migrations to add 2 new tables: 'users' and 'social_accounts' bin/cake migrations migrate -p CakeDC/Users


Load the Component in your src/Controller/AppController.php public function initialize() { parent::initialize(); // // ... // $this->loadComponent('CakeDC/Users.UsersAuth'); }

Create a new Google application

<?php // /config/users.php file contents $config = [ 'Users.Social.login' => true, '' => 'CLIENT_ID_HERE', '' => 'SECRET_HERE', ]; return $config;
  • Modify your bootstrap.php file to ensure the config file is loaded this way
Configure::write('Users.config', ['users']); //add this line before Plugin::load('CakeDC/Users... Plugin::load('CakeDC/Users', ['routes' => true, 'bootstrap' => true]); This file will override any configuration key present in the Plugin, you can check the configuration options here Configuration. Now you are ready to go to your login page and click "Sign up with Google". Upon successful login, a new user will be created in your users table and related oauth2 tokens will be saved in the social_accounts table. The new user created will have the "user" role (by default, but customizable). And based on your Auth rules, this user will be able to access your site. You are done!

Read more about CakeDC Users Plugin

Giving back to the community

This Plugin's development has been sponsored by the Cake Development Corporation. Contact us if you are interested in: We hope you've enjoyed this short tutorial covering the Google login, stay tunned for new CakePHP + Users Plugin tutorials coming soon...

Towards Data Integrity: Validations and Behaviors in CakePHP 3.0

Let us consider “validation” in a little more detail to see how it has been implemented and optimized in CakePHP 3.0. In addition to what we discussed in the earlier sections, validation now incorporates two complementary conceptions or areas. These include 1) data type and format validation and 2) Application rules. 1. Data Type and Format Validation This part of the validation deals structural aspects such as data type, format validation, and basic types. Unlike in previous versions, validation is applied before ORM entities are created. This is a very useful feature that ensures everything is totally in sync and set in a way that preserves data integrity and the overall stability of the entire application. Moreover, it markedly reduces application errors and inconsistencies throughout the system. It is therefore a significant enhancement over previous versions. 2. Application Rules Application rules are the second component of validation in CakePHP 3.0 implementation. They play a key role in quality control to ensure that all application rules and workflows are operating in an orderly and systematic fashion. This is implemented through buildRules() method in tables. Here is a code example that uses buildRules() method for articles table. // In src/Model/Table/ArticlesTable.php namespace App\Model\Table; use Cake\ORM\Table; use Cake\ORM\RulesChecker; class Articles extends Table {     public function buildRules(RulesChecker $rules)     {         $rules->add($rules->existsIn('user_id', 'Users'));         $rules->add(             function ($article, $options) {                 return ($article->published && empty($article->reviewer));             },             'isReviewed', [                 'errorField' => 'published',                 'message' => 'Articles must be reviewed before publishing.'             ]         );         return $rules;     } } Identifier Quoting Identifier quoting is another CakePHP feature or process that has changed in CakePHP 3.0. In the new release, quoted identifiers, which were expensive and involved a notoriously error-prone process of parsing SQL snippets has been disabled by default - thereby removing a major source of frustration for developers. The only time you may want to enable identifier quoting is when working with column names or table names with special characters or reserved words. Here is how to enable identifier quoting when configuring a connection. // In config/app.php 'Datasources' => [     'default' => [         'className' => 'Cake\Database\Driver\Mysql',         'username' => 'root',         'password' => 'super_secret',         'host' => 'localhost',         'database' => 'cakephp',         'quoteIdentifiers' => true     ] ],
Note: Identifiers in QueryExpression objects require manual quoting or IdentifierExpression objects.   Updating Behaviors Let us now turn to behaviors. As with most features that has to do with ORM, the way behaviors are setup and configured has evolved for smooth integration with the new framework. Among other things, behaviors now attach to table instances. Here are some other significant differences in the way behaviors are handled in CakePHP as compared to earlier versions. 1. Each table that uses a behavior will have its own instance. No storing of “name space” setting in a behavior is required. 2. Method signature for mixin, callback, and base class for behaviors have all changed 3. Finder methods can now be added easily by behaviors.   The above, in a nutshell, summarizes the main changes and enhancements in the new ORM and CakePHP 3.0 in general. Like all major releases or upgrades, the new release supplants many processes and functions in previous versions while at the same time adding many brand new features. But as you go through the initial learning curve, please remember that you, the developer, have been the primary driving force behind the changes and enhancements. Your feedback and critiques over the years was the invaluable source that inspired CakePHP team to produce this groundbreaking and cutting-edge release that you are reviewing.

We Bake with CakePHP