diff --git a/README.md b/README.md index 1a2d4a1..53f8813 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,26 @@ -# Kohana Multilang Module +# Kohana Multilang Module 2.0 + +!!! NEW VERSION 2.0 !!! Multilingual module for Kohana PHP Framework, version 3.1 -Partly based on this module https://github.com/GeertDD/kohana-lang - ## Features -* Language segment in uri +* Language segment in uri. Can be optional for the default language * Works with normal routes -* Custom routes for each language (localization or parameters) -* Auto language detection or cookie -* Language selection menu +* Custom routes for each language +* Auto language detection for the homepage (headers then cookie) +* Language selector menu ## Usage ### Configuration return array( - 'default' => 'en', // The default language code - 'cookie' => 'lang', // The cookie name + 'default' => 'en', // The default language code + 'cookie' => 'lang', // The cookie name + 'hide_default' => FALSE, // Hide the language code for the default language + 'auto_detect' => TRUE, // Auto detect the user language on the homepage /** * The allowed languages * For each language, you need to give a code (2-5 chars) for the key, @@ -47,23 +49,26 @@ Partly based on this module https://github.com/GeertDD/kohana-lang ### Example -If you try to access `http://www.domain.tld/`, the module will redirect it to `http://www.domain.tld/en/` for example. +If you try to access `http://www.domain.tld/`, the module will redirect it to `http://www.domain.tld/en/` if the hide_default option is set to FALSE. Let's say we have a product page, with kohana 3 we'd have something like : - Route::set('product.details', 'products/-', array( - 'product_id' => '[0-9]+', - 'product_slug' => '.+', - ))->defaults(array( - 'controller' => 'product', - 'action' => 'details', - 'product_id' => NULL, - 'product_slug' => '', - )); +Route::set('product.details', 'products/-', array( + 'product_id' => '[0-9]+', + 'product_slug' => '.+', +))->defaults(array( + 'controller' => 'product', + 'action' => 'details', + 'product_id' => NULL, + 'product_slug' => '', +)); + -If you try to access `http://www.domain.tld/products/12-my-product`, it will redirect to `http://www.domain.tld/en/products/12-my-product`. -Now, I'm on the same page in french `http://www.domain.tld/fr/products/12-my-product`, but I'd like to translate it and set `produits` instead of `products`. You can use the `Routes` object (notice the S at the end) to set multiple routes for each language. +You can access `http://www.domain.tld/products/12-my-product` with this one. +Now, I want this url in french too, like that: `http://www.domain.tld/fr/products/12-my-product`. +You can use the `Routes` object (notice the S at the end) to set multiple routes for each language. +Let's take a look: Routes::set('product.details', array( 'en' => 'products/-', @@ -78,27 +83,29 @@ Now, I'm on the same page in french `http://www.domain.tld/fr/products/12-my-pro 'product_slug' => '', )); -This creates 2 routes named `en.products.details` and `fr.products.details`. The default language (english here) is required. If we have a third language like `de`, it will use `en`. The thing is, both url `http://www.domain.tld/fr/products/12-my-product` and `http://www.domain.tld/en/produits/12-my-product` will still work. To make sure this is not an issue, you should use reverse routing everywhere. With `Route::get('product/details')->uri(array('product_id' => 12, 'product_slug' => 'my_product'))`, you'll get the complete uri with the current user language code. To get another language, just pass a second parameter to `Route::get('product/details', 'en')`. +This creates 2 routes named `en.products.details` and `fr.products.details`. The default language (english here) is required. If we have a third language like german with `de`, it will use the default uri, english here. +It is highly recommanded to use reverse routing! With `Route::get('product/details')->uri(array('product_id' => 12, 'product_slug' => 'my_product'))`, you'll get the complete uri with the current user language code. To get another language, just pass a second parameter to `Route::get('product/details', 'en')`.For the routes than dont need any language code, you can keep the normal kohana syntax. +If you wanna create a route for only one language, you're gonna have to pass a 4th parameter: -Now, I got a controller that serves CSS files, I obviously don't need any language specified. To prevent the normal behaviour, just pass `FALSE` as the third parameter to `Route::set` like this : - - Route::set('file.css', 'static/css/.css', array( - 'action' => '[^/.]+', - ), FALSE)->defaults(array( - 'controller' => 'css', - 'action' => NULL, + Route::set('product.made-in-france', 'produits/fabrique-en-france', NULL, 'fr') + ->defaults(array( + 'controller' => 'product', + 'action' => 'made_in_france', )); - -If you access `http://www.domain.tld/static/css/custom_page.css`, it will not redirect and `Route::get('file.css.custom')->uri(array('action' => 'categories'))` will not return the uri with a language code. + +And then to get it `Route::get('fr.product.made-in-france')` or `Route::get('product.made-in-france', 'fr)`. + +The default route is particular and its declaration is set in init.php. Since it doesnt need any translations and has a different behaviour (we wanna keep the trailing slash), we create a custom route that alows all the languages. + ### Language selector menu -`Multilang::selector($current)` returns a menu to select the language. It will keep the same page. The `current` parameter adds the current language in the menu or not. +`Multilang::selector($current)` returns a menu to select the language. It will keep the same page if routes are available for the other languages. The `current` parameter adds the current language in the menu. You can change the view file `multilang/selector.php`. ### Misc -To access the current language, you can use `Request::$lang`. +To get the current language, you can use `Request::$lang`. ### Input @@ -107,24 +114,6 @@ If you have any suggestions, found a bug or anything, feel free to share. ### How it works -See https://github.com/GeertDD/kohana-lang - -#### The URI does not contain a language code - -If somebody visits `http://www.domain.tld/page`, without a language, the best default language will be found and the user will be redirected to the same URL *with* that language prepended. To find the best language, the following elements are taken into account (in this order): - -1. a language cookie (set during a previous visit); -2. the HTTP Accept-Language header; -3. a hard-coded default language. - -#### The URI contains a language code - -1. The language code is chopped off before the request and stored in `Request::$lang`. -2. `I18n::$lang` is set to the correct target language (from config). -3. The correct locale is set (from config). -4. A cookie with the language code is set. -5. Normal request processing continues. - -It is important to be aware that the *language part is completely chopped off* of the URI. When normal request processing continues it, it does so with a URI without language. This means that **your routes must not contain a language code. Also, you can create HMVC subrequests without having to worry about adding the current language to the URI. - -The one thing we still need to take care of then, is that any generated URLs should contain the language. An extension of `URL::site` is created for this. A third argument, `$lang`, is added to `URL::site`. By default, the current language is used (`Request::$lang`). You can also provide another language key as a string, or set the argument to `FALSE` to generate a URL without language. +We change the Request to force the detection of a language on the site root. Then if a route is found, we get the language from it with its 'lang' parameter and we initialize. +Each route created with a language code gets another parameter: `lang`. A uri like `products/details` will become `/products/details`. +But since every multilingual route is unique (except default), the regex part allows only one language code. So we have `array('lang' => 'en')` instead of having something like `array('lang' => '(en|fr|de')`. We could have directly the language code in the uri like `en/products/details` but the lang parameter allows us to easily retrieve the route language and works better with the default route. \ No newline at end of file diff --git a/classes/multilang/core.php b/classes/multilang/core.php index 6cd7ac4..bdf1260 100644 --- a/classes/multilang/core.php +++ b/classes/multilang/core.php @@ -119,12 +119,13 @@ class Multilang_Core { { // We juste need to change the language parameter $route = Request::initial()->route(); - + $params['lang'] = NULL; if(!Kohana::config('multilang.hide_default') || Kohana::config('multilang.default') !== $lang) { $params['lang'] = $lang; } + } else { diff --git a/classes/multilang/request.php b/classes/multilang/request.php index 298aa90..2de144f 100644 --- a/classes/multilang/request.php +++ b/classes/multilang/request.php @@ -31,23 +31,25 @@ class Multilang_Request extends Kohana_Request { if(!Kohana::$is_cli) { // If we don't hide the default language, we must look for a language code for the root uri - if(Kohana::config('multilang.auto_detect') && $uri === TRUE && Request::detect_uri() === '') + if(Request::detect_uri() === '' && Kohana::config('multilang.auto_detect') && $uri === TRUE) { - $lang = Multilang::find_user_language(); - - // Use the default server protocol - $protocol = (isset($_SERVER['SERVER_PROTOCOL'])) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1'; + $lang = Multilang::find_user_language(); + if(!Kohana::config('multilang.hide_default') || $lang != Kohana::config('multilang.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; + // Redirect to the root URI, but with language prepended + header($protocol.' 302 Found'); + header('Location: '.URL::base(TRUE, TRUE).$lang.'/'); + exit; + } } } $request = parent::factory($uri, $cache, $injected_routes); - // If the default language is hidden, we manually set it + // If the default language is hidden, we manually set it if(Kohana::config('multilang.hide_default') && $request->param('lang') === NULL) { Request::$lang = Kohana::config('multilang.default'); @@ -55,8 +57,8 @@ class Multilang_Request extends Kohana_Request { else { Request::$lang = $request->param('lang'); - } - + } + Multilang::init(); return $request; } diff --git a/config/multilang.php b/config/multilang.php index ae53415..1318885 100644 --- a/config/multilang.php +++ b/config/multilang.php @@ -17,6 +17,7 @@ return array( * For each language, you need to give a code (2-5 chars) for the key, * the 5 letters i18n language code, the locale and the label for the auto generated language selector menu. */ + /* 'languages' => array( 'en' => array( 'i18n' => 'en_US', @@ -34,4 +35,5 @@ return array( 'label' => 'deutsch', ), ), + */ );