diff --git a/includes/core.php b/includes/core.php
index 195679c..51fddab 100644
--- a/includes/core.php
+++ b/includes/core.php
@@ -1,366 +1,449 @@
 <?php
 
 /*
  * Zed
  * (c) 2010, Dereckson, some rights reserved
  * Released under BSD license
  *
  * Core
  */
 
 ////////////////////////////////////////////////////////////////////////////////
 ///                                                                          ///
 /// Configures PHP and loads site-wide used libraries                        ///
 ///                                                                          ///
 ////////////////////////////////////////////////////////////////////////////////
 
 //No register globals
 ini_set('register_globals', 'off');
 error_reporting(E_ALL & ~E_NOTICE);
 
 //Load libraries
 include_once("config.php");               //Site config
 include_once("error.php");               //Error management
 include_once("mysql.php");              //MySQL layer
 include_once("sessions.php");          //Sessions handler
 
 ////////////////////////////////////////////////////////////////////////////////
 ///                                                                          ///
 /// Information helper methods                                               ///
 ///                                                                          ///
 ////////////////////////////////////////////////////////////////////////////////
 
 //Gets username from specified user_id
 function get_name ($id) {
 	global $db;
 	$sql = 'SELECT perso_nickname FROM '. TABLE_PERSOS . " WHERE perso_id = '$id'";
 	if (!$result = $db->sql_query($sql)) message_die(SQL_ERROR, "Can't query persos table.", '', __LINE__, __FILE__, $sql);
 	$row = $db->sql_fetchrow($result);
 	return $row['perso_nickname'];
 }
 
 //Gets user_id from specified username
 function get_userid ($username) {
 	global $db;
 	$username = $db->sql_escape($username);
 	$sql = 'SELECT user_id FROM '. TABLE_USERS . " WHERE username LIKE '$username'";
 	if (!$result = $db->sql_query($sql)) message_die(SQL_ERROR, "Can't query users table.", '', __LINE__, __FILE__, $sql);
 	$row = $db->sql_fetchrow($result);
 	return $row['user_id'];
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 ///                                                                          ///
 /// Misc helper methods                                                      ///
 ///                                                                          ///
 ////////////////////////////////////////////////////////////////////////////////
 
 /*
  * Generates a random string
  * @author Pierre Habart <p.habart@ifrance.com>
  *
  * @param string $format The format e.g. AAA111
  * @return string a random string
  */
 function genereString ($format) {
     mt_srand((double)microtime()*1000000);
     $str_to_return="";
 
     $t_alphabet=explode(",","A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z");
     $t_number=explode(",","1,2,3,4,5,6,7,8,9,0");
 
     for ($i=0;$i<strlen($format);$i++)
     {
         if (ereg("^[a-zA-Z]",$format[$i]))
         {
             $add=$t_alphabet[mt_rand() % sizeof($t_alphabet)];
             if (ereg("^[a-z]",$format[$i]))
                 $add=strtolower($add);
         }
         elseif(ereg("^[0-9]",$format[$i]))
             $add=$t_number[mt_rand() % sizeof($t_number)];
         else $add="?";
 
         $str_to_return.=$add;
     }
     return $str_to_return;
 }
 
 function generer_hexa($longueur) {
         mt_srand((double)microtime()*1000000);
         $str_to_return="";
         $t_number=explode(",","1,2,3,4,5,6,7,8,9,0,A,B,C,D,E,F");
         for ($i = 0 ; $i < $longueur ; $i++) {
                 $str_to_return .= $t_number[mt_rand() % sizeof($t_number)];
         }
     return $str_to_return;
 }
 
 //Plural management
 
 function s ($amount) {
 	if ($amount > 1) return "s";
 }
 
 function x ($amount) {
 	if ($amount > 1) return "x";
 }
 
 //Debug
 
 function dprint_r ($mixed) {
 	echo "<pre>", print_r($mixed, true), "</pre>";
 }
 
 //GUID
 
 function new_guid() {
 	$characters = explode(",","a,b,c,d,e,f,0,1,2,3,4,5,6,7,8,9");
 	$guid = "";
 	for ($i = 0 ; $i < 36 ; $i++) {
 		if ($i == 8 || $i == 13 || $i == 18 || $i == 23) {
 			$guid .= "-";
 		} else {
 			$guid .= $characters[mt_rand() % sizeof($characters)];
 		}
 	}
 	return $guid;
 }
 
 
 function is_guid ($expression) {
     //We avoid regexp to speed up the check
     //A guid is a 36 characters string
     if (strlen($expression) != 36) return false;
     
     $expression = strtolower($expression);
 	for ($i = 0 ; $i < 36 ; $i++) {
 		if ($i == 8 || $i == 13 || $i == 18 || $i == 23) {
 			//with dashes
 			if ($expression[$i] != "-") return false;
 		} else {
 		    //and numbers
 			if (!is_numeric($expression[$i]) && $expression[$i] != 'a' && $expression[$i] != 'b' && $expression[$i] != 'c' && $expression[$i] != 'd' && $expression[$i] != 'e' && $expression[$i] != 'f' ) return false;
 		}
 	}
     return true;
 }
 
 //Gets file extension
 function get_extension ($file) {
     $dotPosition = strrpos($file, ".");
     return substr($file, $dotPosition + 1);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 ///                                                                          ///
 /// Localization (l10n)                                                      ///
 ///                                                                          ///
 ////////////////////////////////////////////////////////////////////////////////
 
+/*
+ * Defines LANG constant to lang to print
+ */
+function initialize_lang () {
+    //If $_SESSION['lang'] doesn't exist yet, find a common language
+    if (!array_key_exists('lang', $_SESSION)) {
+        $lang = find_lang();
+        $_SESSION['lang'] = $lang ? $lang : '-';
+    }
+    
+    if ($_SESSION['lang'] != '-')
+        define('LANG', $_SESSION['lang']);
+}
+
+/*
+ * Gets a common lang spoken by the site and the user
+ * @return string the language
+ */    
+function find_lang () {
+    if (file_exists('lang') && is_dir('lang')) {
+        //Gets lang/ subdirectories: this is the list of available languages
+        $handle = opendir('lang');
+        while ($file = readdir($handle)) {
+            if ($file != '.' && $file != '..' && is_dir("lang/$file")) {
+                $langs[] = $file;
+            }
+        }
+
+        //The array $langs contains now the language available.
+        //Gets the langs the user should want:
+        if (!$userlangs = get_http_accept_languages())
+            return;
+        
+        //Gets the intersection between the both languages arrays
+        //If it matches, returns first result
+        $intersect = array_intersect($userlangs, $langs);
+        if (count($intersect)) {
+            return $intersect[0];
+        }
+        
+        //Now it's okay with Opera and Firefox but Internet Explorer
+        //will return en-US and not en or fr-BE and not fr, so second pass
+        foreach ($userlangs as $userlang) {
+            $lang = explode('-', $userlang);
+            if (count($lang) > 1)
+                $userlangs2[] = $lang[0];
+        }
+        $intersect = array_intersect($userlangs2, $langs);
+        if (count($intersect)) {
+            return $intersect[0];
+        }
+    }
+}
+
+/*
+ * Returns the languages accepted by the browser, by order of priority
+ * @return Array a array of languages string 
+ */
+
+function get_http_accept_languages () {
+    //What language to print is sent by browser in HTTP_ACCEPT_LANGUAGE var.
+    //This will be something like en,fr;q=0.8,fr-fr;q=0.5,en-us;q=0.3
+    
+    if (!array_key_exists('HTTP_ACCEPT_LANGUAGE', $_SERVER)) {
+        return null;
+    }
+    
+    $http_accept_language = explode(',', $_SERVER["HTTP_ACCEPT_LANGUAGE"]);
+    foreach ($http_accept_language as $language) {
+        $userlang = explode(';q=', $language);
+        if (count($userlang) == 1) {
+            $userlangs[] = array(1, $language);
+        } else {
+            $userlangs[] = array($userlang[1], $userlang[0]);
+        }
+    }
+    rsort($userlangs);
+    foreach ($userlangs as $userlang) {
+        $result[] = $userlang[1];
+    }
+    return $result;
+}
+
 /*
  * Loads specified language Smarty configuration file
  *
  * @param string $file the file to load
  * @param mixed $sections array of section names, single section or null
  */
 function lang_load ($file, $sections = null) {
     global $smarty;
     
     //Loads English file as fallback if some parameters are missing
     if (file_exists("lang/en/$file"))
         $smarty->config_load("lang/en/$file", $sections);
     
-    //Loads wanted file
-    if (LANG != 'en' && file_exists('lang/' . LANG . '/' . $file))
+    //Loads wanted file (if it exists and a language have been defined)
+    if (defined('LANG') && LANG != 'en' && file_exists('lang/' . LANG . '/' . $file))
         $smarty->config_load('lang/' . LANG . '/' . $file, $sections);
 }
 
 /*
  * Gets a specified language expression defined in configuration file
  *
  * @param string $key the configuration key matching the value to get
  * @return string The value in the configuration file
  */
 function lang_get ($key) {
     global $smarty;
     
     $smartyConfValue = $smarty->config_vars[$key];
     return $smartyConfValue ? $smartyConfValue : "#$key#";
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 ///                                                                          ///
 /// Zed date and time helper methods                                         ///
 ///                                                                          ///
 ////////////////////////////////////////////////////////////////////////////////
 
 /*
  * Converts a YYYYMMDD or YYYY-MM-DD timestamp to unixtime
  */
 function to_unixtime ($timestamp) {
 	switch (strlen($timestamp)) {
         case 8:
         //YYYYMMDD
         return mktime(0, 0, 0, substr($timestamp, 4, 2), substr($timestamp, 6, 2), substr($timestamp, 0, 4));
     
         case 10:
         //YYYY-MM-DD
         return mktime(0, 0, 0, substr($timestamp, 5, 2), substr($timestamp, 8, 2), substr($timestamp, 0, 4));
     
         default:
         throw new Exception("timestamp is not a valid YYYYMMDD or YYYY-MM-DD timestamp: $timestamp");
     }
 }
 
 /*
  * Converts a unixtime to the YYYYMMDD or YYYY-MM-DD timestamp format
  *
  * @param int $unixtime the time to convert
  * @param int $format 8 or 10. If 8 (default), will output YYYYMMDD. If 10, YYYY-MM-DD.
  */
 function to_timestamp ($unixtime = null, $format = 8) {   
 	//If no parameter is specified (or null, or false), current time is used
     //==== allows to_timestamp(0) to return correct 1970-1-1 value.
     if ($unixtime === null || $unixtime === false) $unixtime = time();
     
 	switch ($format) {
         case 8:
         //YYYYMMDD
         return date('Ymd', $unixtime);
     
         case 10:
         //YYYY-MM-DD
         return date('Y-m-d', $unixtime);
     
         default:
         throw new Exception("format must be 8 (YYYYMMDD) or 10 (YYYY-MM-DD) and not $format.");
     }
 }
 
 /*
  * Converts a unixtime to the Hypership time format.
  */
 function get_hypership_time ($unixtime = null) {
     //If unixtime is not specified, it's now
     if ($unixtime === null) $unixtime = time();
     
     //Hypership time is a count of days since launch @ 2010-01-25 00:00:00
     //Followed by a fraction of the current day /1000, like the internet time
     //but in UTC timezone and not Switzerland CET/CEST.
     //We don't need to use floor(), as we output the result at int, truncating
     //automatically decimal values instead of round it (like in C).
     $seconds = $unixtime - 1264377600;
     $days = $seconds / 86400;
     $fraction = ($seconds % 86400) / 86.4;
     return sprintf("%d.%03d", $days, $fraction);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 ///                                                                          ///
 /// URL helpers functions                                                    ///
 ///                                                                          ///
 ////////////////////////////////////////////////////////////////////////////////
 
 /*
  * Gets URL
  * @return string URL
  */
 function get_url () {
     global $Config;
     if (func_num_args() > 0) {
         $pieces = func_get_args();
         return $Config['BaseURL'] . '/' . implode('/', $pieces);
     } elseif ($Config['BaseURL'] == "" || $Config['BaseURL'] == "/index.php") {
         return "/";
     } else {
         return $Config['BaseURL'];
     }
 }
 
 /*
  * Gets page URL
  * @return string URL
  */
 function get_page_url () {
     $url = $_SERVER['SCRIPT_NAME'] . $_SERVER['PATH_INFO'];
     if (substr($url, -10) == "/index.php") {
         return substr($url, 0, -9);
     }
     return $url;
 }
 
 /*
  * Gets server URL
  * @todo find a way to detect https:// on non standard port
  * @return string the server URL
  */
 function get_server_url () {
 	switch ($port = $_SERVER['SERVER_PORT']) {
 		case '80':
             return "http://$_SERVER[SERVER_NAME]";
         
         case '443':
             return "https://$_SERVER[SERVER_NAME]";
         
         default:
             return "http://$_SERVER[SERVER_NAME]:$_SERVER[SERVER_PORT]";
 	}
 }
 
 /*
  * Gets $_SERVER['PATH_INFO'] or computes the equivalent if not defined.
  * @return string the relevant URL part
  */
 function get_current_url () {
     global $Config;
             
     //Gets relevant URL part from relevant $_SERVER variables
     if (array_key_exists('PATH_INFO', $_SERVER)) {
         //Without mod_rewrite, and url like /index.php/controller
         //we use PATH_INFO. It's the easiest case.
         return $_SERVER["PATH_INFO"];
     }
     
     //In other cases, we'll need to get the relevant part of the URL
     $current_url = get_server_url() . $_SERVER['REQUEST_URI'];
     
     //Relevant URL part starts after the site URL
     $len = strlen($Config['SiteURL']);
     
     //We need to assert it's the correct site
     if (substr($current_url, 0, $len) != $Config['SiteURL']) {
         dieprint_r(GENERAL_ERROR, "Edit includes/config.php and specify the correct site URL<br /><strong>Current value:</strong> $Config[SiteURL]<br /><strong>Expected value:</strong> a string starting by " . get_server_url(), "Setup");
     }
     
     if (array_key_exists('REDIRECT_URL', $_SERVER)) {
         //With mod_rewrite, we can use REDIRECT_URL
         //We takes the end of the URL, ie *FROM* $len position
         return substr(get_server_url() . $_SERVER["REDIRECT_URL"], $len);
     }
     
     //Last possibility: use REQUEST_URI, but remove QUERY_STRING
     //If you need to edit here, use $_SERVER['REQUEST_URI']
     //but you need to discard $_SERVER['QUERY_STRING']
        
     //We takes the end of the URL, ie *FROM* $len position
     $url = substr(get_server_url() . $_SERVER["REQUEST_URI"], $len);
     
     //But if there are a query string (?action=... we need to discard it)	
     if ($_SERVER['QUERY_STRING']) {
         return substr($url, 0, strlen($url) - strlen($_SERVER['QUERY_STRING']) - 1);
     }
     
     return $url;
 }
 
 /*
  * Gets an array of url fragments to be processed by controller
  */
 function get_current_url_fragments () {
     $url_source = get_current_url();
     if ($url_source == '/index.php') return array();
     return explode('/', substr($url_source, 1));
 }
 
 ?>
\ No newline at end of file
diff --git a/index.php b/index.php
index c880f2b..0d1b30c 100644
--- a/index.php
+++ b/index.php
@@ -1,194 +1,194 @@
 <?php
 
 /*
  * Zed
  * (c) 2010, Dereckson, some rights reserved
  * Released under BSD license
  *
  * Application entry point
  */
 
 ////////////////////////////////////////////////////////////////////////////////
 ///
 /// Initialization
 ///
 
 //Pluton library
 include('includes/core.php');
 
 //Session
 $IP = encode_ip($_SERVER["REMOTE_ADDR"]);
 require_once('includes/story/story.php'); //this class can be stored in session
 session_start();
 $_SESSION[ID] = session_id();
 session_update(); //updates or creates the session
 
 include("includes/login.php"); //login/logout
 $CurrentUser = get_logged_user(); //Gets current user infos
 
 //Gets current perso
 require_once('includes/objects/perso.php');
 if ($perso_id = $CurrentUser->session['perso_id']) {
     $CurrentPerso = new Perso($perso_id);
 }
 
 //Skin and accent to load
 define('THEME', $CurrentUser->session['Skin']);
 define('ACCENT', $CurrentUser->session['Skin_accent']);
 
 //Loads Smarty
 require('includes/Smarty/Smarty.class.php');
 $smarty = new Smarty();
 $current_dir = dirname(__FILE__);
 $smarty->template_dir = $current_dir . '/skins/' . THEME;
 
 $smarty->compile_dir = $current_dir . '/cache/compiled';
 $smarty->cache_dir = $current_dir . '/cache';
 $smarty->config_dir = $current_dir;
 
 $smarty->config_vars['StaticContentURL'] = $Config['StaticContentURL'];
 
 //Loads language files
-define('LANG', 'fr');
+initialize_lang();
 lang_load('core.conf');
 
 if ($CurrentUser->id < 1000) {   
     //Anonymous user, proceed to login
     if (array_key_exists('LastUsername', $_COOKIE))
         $smarty->assign('username', $_COOKIE['LastUsername']);
     if (array_key_exists('LastOpenID', $_COOKIE))
         $smarty->assign('OpenID', $_COOKIE['LastOpenID']);
     $smarty->assign('LoginError', $LoginError);
     $smarty->display('login.tpl');
     exit;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 ///
 /// Perso selector
 ///
 
 //Handles form
 if ($_POST['form'] == 'perso.create') {
     $perso = new Perso();
     $perso->load_from_form();
     $perso->user_id = $CurrentUser->id;
     
     //Validates forms
     if (!$perso->name) $errors[] = lang_get("NoFullnameSpecified");
     if (!$perso->race) {
         $errors[] = lang_get("NoRaceSpecified");
         $perso->race = "being";
     }
     if (!$perso->sex) $errors[] = lang_get("NoSexSpecified");
     if (!$perso->nickname) {
         $errors[] = lang_get("NoNicknameSpecified");
     } else if (!Perso::is_available_nickname($perso->nickname)) {
         $errors[] = lang_get("UnavailableNickname");
     }
     
     //Save or prints again forms
     if (!$errors) {
         $perso->save_to_database();
         $smarty->assign('NOTIFY', lang_get('NewCharacterCreated'));
         $CurrentPerso = $perso;
         set_info('perso_id', $perso->id);
         $CurrentPerso->setflag("site.lastlogin", $_SERVER['REQUEST_TIME']);
     } else {
         $smarty->assign('WAP', join("<br />", $errors));
         $smarty->assign('perso', $perso);
     }    
 }
 
 if ($_GET['action'] == 'perso.logout') {
     //User wants to change perso
     $CurrentPerso = null;
     set_info('perso_id', null);
     clean_session();
 } elseif ($_GET['action'] == 'perso.select') {
     //User have selected a perso
     $CurrentPerso = new Perso($_GET['perso_id']);
     if ($CurrentPerso->user_id != $CurrentUser->id) {
         //Hack
         message_die(HACK_ERROR, "This isn't your perso.");
     }
     set_info('perso_id', $CurrentPerso->id);
     $CurrentPerso->setflag("site.lastlogin", $_SERVER['REQUEST_TIME']);
 }
 
 if (!$CurrentPerso) {   
     switch ($count = Perso::get_persos_count($CurrentUser->id)) {
         case 0:
             //Create a perso
             $smarty->display("perso_create.tpl");
             exit;
         
         case 1:
             //Autoselect
             $CurrentPerso = Perso::get_first_perso($CurrentUser->id);
             set_info('perso_id', $CurrentPerso->id);
             $CurrentPerso->setflag("site.lastlogin", $_SERVER['REQUEST_TIME']);
             break;
             
         default:
             //Pick a perso
             $persos = Perso::get_persos($CurrentUser->id);
             $smarty->assign("PERSOS", $persos);
             $smarty->display("perso_select.tpl");
             $_SESSION['UserWithSeveralPersos'] = true;
             exit;
     }
 }
 
 //Assigns current perso object as Smarty variable
 $smarty->assign('CurrentPerso', $CurrentPerso);
 
 ////////////////////////////////////////////////////////////////////////////////
 ///
 /// Tasks to execute before calling the URL controller:
 ///     - assert the perso is somewhere
 ///     - executes the smartline
 ///
 
 //If the perso location is unknown, ejects it to an asteroid
 if (!$CurrentPerso->location_global) {
     require_once('includes/geo/place.php');
     $smarty->assign('NOTIFY', lang_get('NewLocationNotify'));
     $CurrentPerso->move_to(GeoPlace::get_start_location());
 }
 
 //SmartLine
 include("includes/SmartLine/ZedSmartLine.php");
 
 ////////////////////////////////////////////////////////////////////////////////
 ///
 /// Calls the specific controller to serve the requested page
 ///
 
 $url = get_current_url_fragments();
     
 switch ($controller = $url[0]) {
     case '':
         include('controllers/home.php');
         break;
 
     case 'request':
     case 'page':
     case 'explore':
         include("controllers/$controller.php");
         break;
     
     case 'who':
         include('controllers/profile.php'); //Azhàr controller
         break;
     
     case 'push':
         include('controllers/motd.php'); //Azhàr controller
         break;
 
     default:
         //TODO: returns a 404 error
         dieprint_r($url, 'Unknown URL');
 }
 
 ?>
\ No newline at end of file