diff --git a/classes/Multilang.php b/classes/Multilang.php
new file mode 100644
index 0000000..d15e454
--- /dev/null
+++ b/classes/Multilang.php
@@ -0,0 +1,9 @@
+load('multilang');
+
+ if($config->auto_detect)
+ {
+ // Get the list of supported languages
+ $languages = $config->languages;
+ $cookie = $config->cookie;
+
+ // Look for language cookie first
+ if($lang = Cookie::get($cookie))
+ {
+ // Valid language found in cookie
+ if(isset($languages[$lang]))
+ {
+ return $lang;
+ }
+
+ // Delete cookie with unset language
+ Cookie::delete($cookie);
+ }
+
+ // Parse HTTP Accept-Language headers
+ foreach(Request::accept_lang() as $lang => $quality)
+ {
+ // Return the first language found (the language with the highest quality)
+ if(isset($languages[$lang]))
+ {
+ return $lang;
+ }
+ }
+ }
+ // Return the hard-coded default language as final fallback
+ return $config->default;
+ }
+
+ /**
+ * Initialize the config and cookies
+ */
+ static public function init()
+ {
+ $config = Kohana::$config->load('multilang');
+
+ // Get the list of supported languages
+ $langs = $config->languages;
+
+ // Set the language in I18n
+ I18n::lang($langs[Request::$lang]['i18n']);
+
+ // Set locale
+ setlocale(LC_ALL, $langs[Request::$lang]['locale']);
+
+ $cookie = $config->cookie;
+ // Update language cookie if needed
+ if(Cookie::get($cookie) !== Request::$lang)
+ {
+ Cookie::set($cookie, Request::$lang);
+ }
+ }
+
+ /**
+ * Return a language selector menu
+ * @param boolean $current Display the current language or not
+ * @return View
+ */
+ static public function selector($current = TRUE)
+ {
+ $config = Kohana::$config->load('multilang');
+
+ $languages = $config->languages;
+
+ // Get the current route name
+ $current_route = Route::name(Request::initial()->route());
+
+
+ $params = Request::initial()->param();
+
+ if($current_route !== 'default' && strpos($current_route, '.') !== FALSE)
+ {
+ // Split the route path
+ list($lang, $name) = explode('.', $current_route, 2);
+ }
+ else
+ {
+ $name = $current_route;
+ }
+
+ // Create uris for each language
+ foreach($languages as $lang => &$language)
+ {
+ // If it's the current language
+ if($lang === Request::$lang)
+ {
+ // We only display it when required
+ if($current)
+ {
+ $selectors[$lang] = ''.$languages[$lang]['label'].'';
+ }
+ }
+ else
+ {
+ $route = NULL;
+
+ // If it's the default route, it's unique and special (like you <3)
+ if($current_route === 'default')
+ {
+ // We juste need to change the language parameter
+ $route = Request::initial()->route();
+ $params['lang'] = NULL;
+ if(!$config->hide_default || $config->default !== $lang)
+ {
+ $params['lang'] = $lang;
+ }
+
+
+ }
+ else
+ {
+ if(Arr::get(Route::all(), $lang.'.'.$name))
+ {
+ $route = Route::get($name, $lang);
+ }
+ }
+
+ if($route !== NULL)
+ {
+ $selectors[$lang] = HTML::anchor($route->uri($params), $languages[$lang]['label'], array('class' => 'multilang-selectable multilang-'.$lang, 'title' => $languages[$lang]['label']));
+ }
+ }
+ }
+
+ // We display the menu only if we can select another language for this page
+ if(count($selectors) > 1)
+ {
+ return View::factory('multilang/selector')
+ ->bind('selectors', $selectors);
+ }
+ return '';
+ }
+}
\ No newline at end of file
diff --git a/classes/Multilang/Request.php b/classes/Multilang/Request.php
new file mode 100644
index 0000000..b85f29c
--- /dev/null
+++ b/classes/Multilang/Request.php
@@ -0,0 +1,199 @@
+load('multilang');
+
+ // If we don't hide the default language, we must look for a language code for the root uri
+ if(Request::detect_uri() === '' AND $config->auto_detect AND $uri === TRUE)
+ {
+ $lang = Multilang::find_user_language();
+ if(!$config->hide_default OR $lang != $config->default)
+ {
+ // Use the default server protocol
+ $protocol = (isset($_SERVER['SERVER_PROTOCOL'])) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';
+
+ // Redirect to the root URI, but with language prepended
+ header($protocol.' 302 Found');
+ header('Location: '.URL::base(TRUE, TRUE).$lang.'/');
+ exit;
+ }
+ }
+
+ return parent::factory($uri, $client_params, $allow_external, $injected_routes);
+ }
+
+ /**
+ * We don't want to remove the trailing slash from the uri
+ */
+ public function __construct($uri, $client_params = array(), $allow_external = TRUE, $injected_routes = array())
+ {
+ $client_params = is_array($client_params) ? $client_params : array();
+
+ // Initialise the header
+ $this->_header = new HTTP_Header(array());
+
+ // Assign injected routes
+ $this->_routes = $injected_routes;
+
+ // Cleanse query parameters from URI (faster that parse_url())
+ $split_uri = explode('?', $uri);
+ $uri = array_shift($split_uri);
+
+ // Initial request has global $_GET already applied
+ if (Request::$initial !== NULL)
+ {
+ if ($split_uri)
+ {
+ parse_str($split_uri[0], $this->_get);
+ }
+ }
+
+ // Detect protocol (if present)
+ // $allow_external = FALSE prevents the default index.php from
+ // being able to proxy external pages.
+ if ( ! $allow_external OR strpos($uri, '://') === FALSE)
+ {
+ // Remove trailing slashes from the URI (We don't want that)
+ //$this->_uri = trim($uri, '/');
+ $this->_uri = ltrim($uri, '/');
+ //$this->_route = new Route($uri);
+ // Apply the client
+ $this->_client = new Request_Client_Internal($client_params);
+ }
+ else
+ {
+ // Create a route
+ $this->_route = new Route($uri);
+
+ // Store the URI
+ $this->_uri = $uri;
+
+ // Set the security setting if required
+ if (strpos($uri, 'https://') === 0)
+ {
+ $this->secure(TRUE);
+ }
+
+ // Set external state
+ $this->_external = TRUE;
+
+ // Setup the client
+ $this->_client = Request_Client_External::factory($client_params);
+ }
+ }
+
+ /**
+ * Altered to detect the language in the uri
+ *
+ * @return Response
+ * @throws Request_Exception
+ * @throws HTTP_Exception_404
+ * @uses [Kohana::$profiling]
+ * @uses [Profiler]
+ */
+ public function execute()
+ {
+ if ( ! $this->_external)
+ {
+ $processed = Request::process($this, $this->_routes);
+
+ if ($processed)
+ {
+ // Store the matching route
+ $this->_route = $processed['route'];
+ $params = $processed['params'];
+
+ // Is this route external?
+ $this->_external = $this->_route->is_external();
+
+ if (isset($params['directory']))
+ {
+ // Controllers are in a sub-directory
+ $this->_directory = $params['directory'];
+ }
+
+ // Store the controller
+ $this->_controller = $params['controller'];
+
+ // Store the action
+ $this->_action = (isset($params['action']))
+ ? $params['action']
+ : Route::$default_action;
+
+ // These are accessible as public vars and can be overloaded
+ unset($params['controller'], $params['action'], $params['directory']);
+
+ // Params cannot be changed once matched
+ $this->_params = $params;
+ }
+ }
+
+ if ( ! $this->_route instanceof Route)
+ {
+ return HTTP_Exception::factory(404, 'Unable to find a route to match the URI: :uri', array(
+ ':uri' => $this->_uri,
+ ))->request($this)
+ ->get_response();
+ }
+
+ if ( ! $this->_client instanceof Request_Client)
+ {
+ throw new Request_Exception('Unable to execute :uri without a Kohana_Request_Client', array(
+ ':uri' => $this->_uri,
+ ));
+ }
+
+ // Multilang part
+ if(Request::$lang === NULL)
+ {
+ Request::$lang = $this->_route->lang;
+ }
+
+ $config = Kohana::$config->load('multilang');
+ if($config->hide_default AND $this->param('lang') === NULL OR $this->_route->lang === NULL AND $this->param('lang') === NULL)
+ {
+ Request::$lang = $config->default;
+ }
+ else
+ {
+ Request::$lang = $this->param('lang');
+ }
+ Multilang::init();
+
+ return $this->_client->execute($this);
+ }
+
+
+}
\ No newline at end of file
diff --git a/classes/Multilang/Route.php b/classes/Multilang/Route.php
new file mode 100644
index 0000000..d94f483
--- /dev/null
+++ b/classes/Multilang/Route.php
@@ -0,0 +1,346 @@
+load('multilang');
+
+ if(!$config->hide_default || $config->default != $lang)
+ {
+ if($lang !== NULL)
+ {
+ $uri_callback = '/'.$uri_callback;
+ $regex['lang'] = $lang;
+ }
+ }
+
+ if($lang !== NULL)
+ {
+ $name = $lang.'.'.$name;
+ }
+
+ return Route::$_routes[$name] = new Route($uri_callback, $regex, $lang);
+ }
+
+
+ /**
+ * Retrieves a named route.
+ *
+ * $route = Route::get('default');
+ *
+ * @param string route name
+ * @return Route
+ * @throws Kohana_Exception
+ */
+ static public function get($name, $lang = NULL)
+ {
+ // We use the current language if none given
+ if($lang === NULL)
+ {
+ $lang = Request::$lang;
+ }
+
+ // We first look for a "given_language.name" route.
+ if(isset(Route::$_routes[$lang.'.'.$name]))
+ {
+ $name = $lang.'.'.$name;
+
+ } // then the default language
+ elseif(isset(Route::$_routes[Kohana::$config->load('multilang')->default.'.'.$name])) {
+ $name = $config->default.'.'.$name;
+ }
+ // And if we don't have any for this language, it means that route is neither defined nor multilingual
+ return parent::get($name);
+ }
+
+ /**
+ * Altered constructor to handle multilingual routes
+ *
+ * Creates a new route. Sets the URI and regular expressions for keys.
+ * Routes should always be created with [Route::set] or they will not
+ * be properly stored.
+ *
+ * $route = new Route($uri, $regex);
+ *
+ * The $uri parameter can either be a string for basic regex matching or it
+ * can be a valid callback or anonymous function (php 5.3+). If you use a
+ * callback or anonymous function, your method should return an array
+ * containing the proper keys for the route. If you want the route to be
+ * "reversable", you need to return a 'uri' key in the standard syntax.
+ *
+ * $route = new Route(function($uri)
+ * {
+ * if (list($controller, $action, $param) = explode('/', $uri) AND $controller == 'foo' AND $action == 'bar')
+ * {
+ * return array(
+ * 'controller' => 'foobar',
+ * 'action' => $action,
+ * 'id' => $param,
+ * 'uri' => 'foo/bar/.html
+ * );
+ * }
+ * });
+ *
+ * @param mixed route URI pattern or lambda/callback function
+ * @param array key patterns
+ * @param
+ * @return void
+ * @uses Route::_compile
+ */
+ public function __construct($uri = NULL, array $regex = NULL, $lang = NULL)
+ {
+ $this->lang = $lang;
+ return parent::__construct($uri, $regex);
+ }
+
+
+ /**
+ * Generates a URI for the current route based on the parameters given.
+ *
+ * // Using the "default" route: "users/profile/10"
+ * $route->uri(array(
+ * 'controller' => 'users',
+ * 'action' => 'profile',
+ * 'id' => '10'
+ * ));
+ *
+ * @param array $params URI parameters
+ * @return string
+ * @throws Kohana_Exception
+ * @uses Route::REGEX_Key
+ */
+ public function uri(array $params = NULL, $lang = NULL)
+ {
+ // We define the language if required
+ if($this->lang !== NULL)
+ {
+ $params['lang'] = ($lang === NULL ? $this->lang : $lang);
+ }
+
+ // Start with the routed URI
+ $uri = $this->_uri;
+
+ if (strpos($uri, '<') === FALSE AND strpos($uri, '(') === FALSE)
+ {
+ // This is a static route, no need to replace anything
+
+ if ( ! $this->is_external())
+ return $uri;
+
+ // If the localhost setting does not have a protocol
+ if (strpos($this->_defaults['host'], '://') === FALSE)
+ {
+ // Use the default defined protocol
+ $params['host'] = Route::$default_protocol.$this->_defaults['host'];
+ }
+ else
+ {
+ // Use the supplied host with protocol
+ $params['host'] = $this->_defaults['host'];
+ }
+
+ // Compile the final uri and return it
+ return rtrim($params['host'], '/').'/'.$uri;
+ }
+
+ // Keep track of whether an optional param was replaced
+ $provided_optional = FALSE;
+
+ while (preg_match('#\([^()]++\)#', $uri, $match))
+ {
+
+ // Search for the matched value
+ $search = $match[0];
+
+ // Remove the parenthesis from the match as the replace
+ $replace = substr($match[0], 1, -1);
+
+ while (preg_match('#'.Route::REGEX_KEY.'#', $replace, $match))
+ {
+ list($key, $param) = $match;
+
+ if (isset($params[$param]) AND $params[$param] !== Arr::get($this->_defaults, $param))
+ {
+ // Future optional params should be required
+ $provided_optional = TRUE;
+
+ // Replace the key with the parameter value
+ $replace = str_replace($key, $params[$param], $replace);
+ }
+ elseif ($provided_optional)
+ {
+ // Look for a default
+ if (isset($this->_defaults[$param]))
+ {
+ $replace = str_replace($key, $this->_defaults[$param], $replace);
+ }
+ else
+ {
+ // Ungrouped parameters are required
+ throw new Kohana_Exception('Required route parameter not passed: :param', array(
+ ':param' => $param,
+ ));
+ }
+ }
+ else
+ {
+ // This group has missing parameters
+ $replace = '';
+ break;
+ }
+ }
+
+ // Replace the group in the URI
+ $uri = str_replace($search, $replace, $uri);
+ }
+
+ while (preg_match('#'.Route::REGEX_KEY.'#', $uri, $match))
+ {
+ list($key, $param) = $match;
+
+ if ( ! isset($params[$param]))
+ {
+ // Look for a default
+ if (isset($this->_defaults[$param]))
+ {
+ $params[$param] = $this->_defaults[$param];
+ }
+ else
+ {
+ // Ungrouped parameters are required
+ throw new Kohana_Exception('Required route parameter not passed: :param', array(
+ ':param' => $param,
+ ));
+ }
+ }
+
+ $uri = str_replace($key, $params[$param], $uri);
+ }
+
+ // Trim all extra slashes from the URI
+ //$uri = preg_replace('#//+#', '/', rtrim($uri, '/'));
+ $uri = preg_replace('#//+#', '/', $uri);
+
+ if ($this->is_external())
+ {
+ // Need to add the host to the URI
+ $host = $this->_defaults['host'];
+
+ if (strpos($host, '://') === FALSE)
+ {
+ // Use the default defined protocol
+ $host = Route::$default_protocol.$host;
+ }
+
+ // Clean up the host and prepend it to the URI
+ $uri = rtrim($host, '/').'/'.$uri;
+ }
+
+ return $uri;
+ }
+
+ /**
+ * Altered method to handle multilingual parameter
+ *
+ * Create a URL from a route name. This is a shortcut for:
+ *
+ * echo URL::site(Route::get($name)->uri($params), $protocol);
+ *
+ * @param string route name
+ * @param array URI parameters
+ * @param mixed protocol string or boolean, adds protocol and domain
+ * @return string
+ * @since 3.0.7
+ * @uses URL::site
+ */
+ static public function url($name, array $params = NULL, $protocol = NULL, $lang = NULL)
+ {
+ // Create a URI with the route and convert it to a URL
+ return URL::site(Route::get($name, $lang)->uri($params), $protocol);
+ }
+
+ /**
+ * We don't want to remove the trailing slash.
+ */
+ public function matches(Request $request)
+ {
+ // Get the URI from the Request
+ //$uri = trim($request->uri(), '/');
+ $uri = ltrim($request->uri(), '/');
+
+ if ( ! preg_match($this->_route_regex, $uri, $matches))
+ return FALSE;
+
+ $params = array();
+ foreach ($matches as $key => $value)
+ {
+ if (is_int($key))
+ {
+ // Skip all unnamed keys
+ continue;
+ }
+
+ // Set the value for all matched keys
+ $params[$key] = $value;
+ }
+
+ foreach ($this->_defaults as $key => $value)
+ {
+ if ( ! isset($params[$key]) OR $params[$key] === '')
+ {
+ // Set default values for any key that was not matched
+ $params[$key] = $value;
+ }
+ }
+
+ if ( ! empty($params['controller']))
+ {
+ // PSR-0: Replace underscores with spaces, run ucwords, then replace underscore
+ $params['controller'] = str_replace(' ', '_', ucwords(str_replace('_', ' ', $params['controller'])));
+ }
+
+ if ( ! empty($params['directory']))
+ {
+ // PSR-0: Replace underscores with spaces, run ucwords, then replace underscore
+ $params['directory'] = str_replace(' ', '_', ucwords(str_replace('_', ' ', $params['directory'])));
+ }
+
+ if ($this->_filters)
+ {
+ foreach ($this->_filters as $callback)
+ {
+ // Execute the filter giving it the route, params, and request
+ $return = call_user_func($callback, $this, $params, $request);
+
+ if ($return === FALSE)
+ {
+ // Filter has aborted the match
+ return FALSE;
+ }
+ elseif (is_array($return))
+ {
+ // Filter has modified the parameters
+ $params = $return;
+ }
+ }
+ }
+
+ return $params;
+ }
+
+}
\ No newline at end of file
diff --git a/classes/Multilang/Routes.php b/classes/Multilang/Routes.php
new file mode 100644
index 0000000..4be4498
--- /dev/null
+++ b/classes/Multilang/Routes.php
@@ -0,0 +1,77 @@
+ 'home',
+ * 'fr' => 'accueil',
+ * ))->defaults(array(
+ * 'controller' => 'homepage',
+ * 'action' => 'index',
+ * ));
+ *
+ * @param string route name
+ * @param array URI patterns (array of "language code" => "uri")
+ * @param array regex patterns for route keys
+ * @return Routes
+ */
+ static public function set($name, $uris = array(), $regex = NULL)
+ {
+ $config = Kohana::$config->load('multilang');
+ $routes = new Routes();
+
+ // We add the routes for each language and set their names to lang.name (en.homepage for example).
+ // The segment is also added on the uri if it's not hidden
+
+ $default_lang = $config->default;
+ $languages = $config->languages;
+
+ // We first look for the default language uri which is obviously compulsory
+ $default_uri = Arr::get($uris, $default_lang);
+ if($default_uri === NULL)
+ {
+ throw new Kohana_Exception('The default language route uri is required for the route: :route', array(':route' => $name));
+ }
+ else
+ {
+ $routes->_routes[$default_lang.'.'.$name] = Route::set($name, $default_uri, $regex, $default_lang);
+ }
+ unset($languages[$default_lang]);
+
+ // Then we add the routes for all the other languages
+ foreach($languages as $lang => $settings)
+ {
+ $uri = (Arr::get($uris, $lang) ? $uris[$lang] : $uris[$default_lang]);
+
+ // For the uri, we use the one given or the default one
+ $routes->_routes[$lang.'.'.$name] = Route::set($name, $uri, $regex, $lang);
+ }
+ return $routes;
+ }
+
+
+ /**
+ * Set the defaults values for each route
+ * @param array $defaults
+ * @return Multilang_Routes
+ */
+ public function defaults(array $defaults = NULL)
+ {
+ foreach($this->_routes as $route)
+ {
+ $route->defaults($defaults);
+ }
+ return $this;
+ }
+
+
+}
\ No newline at end of file
diff --git a/classes/Multilang/URL.php b/classes/Multilang/URL.php
new file mode 100644
index 0000000..570f754
--- /dev/null
+++ b/classes/Multilang/URL.php
@@ -0,0 +1,22 @@
+