diff --git a/bootstrap.php b/bootstrap.php
index 73ed0994797a41ab3c5c071ad9eb918d9966765e..986844c41821fec62b6e7b7bbd298504c6da7e54 100644
--- a/bootstrap.php
+++ b/bootstrap.php
@@ -190,6 +190,7 @@ class Bootstrap {
         require_once(INCLUDE_DIR.'class.validator.php'); //Class to help with basic form input validation...please help improve it.
         require(INCLUDE_DIR.'class.mailer.php');
         require_once INCLUDE_DIR.'mysqli.php';
+        require_once INCLUDE_DIR.'class.i18n.php';
     }
 
     function i18n_prep() {
diff --git a/include/class.client.php b/include/class.client.php
index 55195c04627ed7b1695313f87a096a9ce2a34fbf..a4cf0b028b7dfdcfdc3faf5bc99d885cc5248205 100644
--- a/include/class.client.php
+++ b/include/class.client.php
@@ -267,6 +267,19 @@ class  EndUser extends AuthenticatedUser {
         return $this->_account;
     }
 
+    function getLanguage() {
+        static $cached = false;
+        if (!$cached) $cached = &$_SESSION['client:lang'];
+
+        if (!$cached) {
+            if ($acct = $this->getAccount())
+                $cached = $acct->getLanguage();
+            if (!$cached)
+                $cached = Internationalization::getDefaultLanguage();
+        }
+        return $cached;
+    }
+
     private function getStats() {
 
         $sql='SELECT count(open.ticket_id) as open, count(closed.ticket_id) as closed '
diff --git a/include/class.i18n.php b/include/class.i18n.php
index 884dc7cb9863f51e796e5fe812c03eeb67d697ab..ccfdff34460752bb58e212545d592a40f3e49139 100644
--- a/include/class.i18n.php
+++ b/include/class.i18n.php
@@ -284,6 +284,51 @@ class Internationalization {
 
         return $best_match_langcode;
     }
+
+    static function bootstrap($user) {
+        global $cfg;
+
+        #if (!extension_loaded('gettext'))
+            require_once INCLUDE_DIR . 'gettext/gettext.inc';
+
+        if ($user && method_exists($user, 'getLanguage'))
+            $lang = $user->getLanguage();
+        else
+            $lang = Internationalization::getDefaultLanguage();
+
+        $packs = Internationalization::availableLanguages();
+
+        $domain = 'messages';
+
+        // User-specific translations
+        putenv('LC_ALL=' . $lang);
+        setlocale(LC_ALL, $lang);
+        bindtextdomain($domain, $packs[$lang]['path']);
+        textdomain($domain);
+
+        // User-specific translations
+        if (!function_exists('__')) {
+            function __($text) { return _($text); }
+        }
+
+        function _N($msgid, $plural, $count) {
+            return ngettext($msgid, $plural, $count);
+        }
+
+        // System-specific translations
+        function _S($msgid) {
+            global $cfg;
+        }
+        function _SN($msgid, $plural, $count) {
+            global $cfg;
+        }
+
+        // Language-specific translations
+        function _L($msgid, $lang) {
+        }
+        function _LN($msgid, $plural, $count, $lang) {
+        }
+    }
 }
 
 class DataTemplate {
diff --git a/include/class.user.php b/include/class.user.php
index b137d49ee8e0de97d63db576baab32569a92cfbb..d15b5d424d03f4a1d01301ba4640dca2915a20d1 100644
--- a/include/class.user.php
+++ b/include/class.user.php
@@ -834,6 +834,10 @@ class UserAccountModel extends VerySimpleModel {
         $this->user->set('account', $this);
         return $this->user;
     }
+
+    function getLanguage() {
+        return $this->get('lang');
+    }
 }
 
 class UserAccount extends UserAccountModel {
diff --git a/include/gettext-conf.php b/include/gettext-conf.php
deleted file mode 100644
index 3aae5b3880939527fa111e5e60c39bee8d867aa0..0000000000000000000000000000000000000000
--- a/include/gettext-conf.php
+++ /dev/null
@@ -1,125 +0,0 @@
-<?php
-//Multilanguage Support
-//To add additional languages add a folder with your language code to 'include/locale' (for example 'de-de'), create the folder 'LC_MESSAGES' inside it and create your
-//'messages.po' file inside 'LC_MESSAGES'. With the example of de-de the full path to 'messages.po' should look like 'include/locale/de-de/LC_MESSAGES/messages.po'.
-//In case you don't know your language code (or to be more precise: the one your browser prefers), open the php page: 'testlang.php'
-
-//the language detection first checks if a language folder, that has the same name as the preferred broser language exists
-//if that is the case it checks whether it should redirect to another language folder or not
-//if that isn't the case it tries to split the language code to its short version and does the same checks
-//it doesn't check the sanity of the redirect file, so make sure the content is valid
-
-//to redirect a language code to a different one, create a folder inside 'include/locale' that represents the language code you want to redirect and then create a file called 'redirect' in it.
-//the content of 'redirect' has to be the language code it should redirect to.
-//Example:
-//you want to redirect the language code 'de-de' to the code 'de'
-//create a folder inside 'include/locale' called 'de-de'. then create the file 'redirect' in it. the path to this redirect should now look like 'include/locale/de-de/redirect'.
-//now open the redirect file you've created, type de in the first line and save it
-
-
-$use_php_gettext=true; //Set this to false to disable php_gettext
-
-require_once(INCLUDE_DIR.'locale/lang.php');
-
-if($use_php_gettext==true&&function_exists('mb_detect_encoding'))
-{
-	require_once(INCLUDE_DIR.'gettext.inc');
-}
-$language=getDefaultLanguage(); //if you want to use just one static language replace the call to getDefaultLanguage() with your language code (for example 'de-de')
-
-//get the first and second part of the language code
-if(strpos($language,'_')!==false)
-{
-	$language=substr($language,0,strpos($language,'_'));
-	$lang_dialect=substr($language,strpos($language,'_'));
-}
-elseif(strpos($language,'-')!==false)
-{
-	$language=substr($language,0,strpos($language,'-'));
-	$lang_dialect=substr($language,strpos($language,'-'));
-}
-if(!isset($lang_dialect))
-{
-	$lang_dialect=$language;
-}
-
-$tmplangcode=$language.'-'.strtolower($lang_dialect);
-if(!file_exists(INCLUDE_DIR.'locale/'.$tmplangcode)||!is_dir(INCLUDE_DIR.'locale/'.$tmplangcode))
-{
-	$tmplangcode=$language.'_'.strtolower($lang_dialect);
-	if(!file_exists(INCLUDE_DIR.'locale/'.$tmplangcode)||!is_dir(INCLUDE_DIR.'locale/'.$tmplangcode))
-	{
-		$tmplangcode=$language.'_'.strtoupper($lang_dialect);
-		if(!file_exists(INCLUDE_DIR.'locale/'.$tmplangcode)||!is_dir(INCLUDE_DIR.'locale/'.$tmplangcode))
-		{
-			$tmplangcode=$language.'-'.strtoupper($lang_dialect);
-			if(!file_exists(INCLUDE_DIR.'locale/'.$tmplangcode)||!is_dir(INCLUDE_DIR.'locale/'.$tmplangcode))
-			{
-				if(!file_exists(INCLUDE_DIR.'locale/'.$language)||!is_dir(INCLUDE_DIR.'locale/'.$language)) //check short langcode
-				{
-					$language='en'; //set as default
-				}
-			}
-			else
-			{
-				$language=$tmplangcode;
-			}
-		}
-		else
-		{
-			$language=$tmplangcode;
-		}
-	}
-	else
-	{
-		$language=$tmplangcode;
-	}
-}
-else
-{
-	$language=$tmplangcode;
-}
-//check if a redirect file is in there
-if(file_exists(INCLUDE_DIR.'locale/'.$language.'/redirect'))
-{
-	$f = fopen(INCLUDE_DIR.'locale/'.$language.'/redirect','r');
-	if($f!==false)
-	{
-		$line = fgets($f);
-		if(strlen($line)>=2) //safety check
-		{
-			$language=$line; //redirect language
-		}
-		fclose($f);
-	}
-}
-
-// gettext setup
-$domain = 'messages';
-if(extension_loaded('gettext')&&$use_php_gettext==false)
-{
-	putenv('LC_ALL=' . $language);
-	setlocale(LC_ALL, $language);
-	bindtextdomain($domain, INCLUDE_DIR.'locale');
-	textdomain($domain);
-	if(!function_exists('__'))
-	{
-		function __($text){return _($text);}
-	}
-}
-else if($use_php_gettext==true&&function_exists('mb_detect_encoding'))
-{
-	T_setlocale(LC_ALL, $language);
-	// Set the text domain as 'messages'
-	T_bindtextdomain($domain, INCLUDE_DIR.'locale');
-	T_bind_textdomain_codeset($domain, 'UTF-8');
-	T_textdomain($domain);
-}
-else
-{
-	if(!function_exists('__'))
-	{
-		function __($text){return $text;} //fallback definition: in case the gettext extension wasn't loaded osticket should at least work in english
-	}
-}
-?>
\ No newline at end of file
diff --git a/include/gettext.inc b/include/gettext/gettext.inc
similarity index 100%
rename from include/gettext.inc
rename to include/gettext/gettext.inc
diff --git a/include/gettext.php b/include/gettext/gettext.php
similarity index 100%
rename from include/gettext.php
rename to include/gettext/gettext.php
diff --git a/include/streams.php b/include/gettext/streams.php
similarity index 100%
rename from include/streams.php
rename to include/gettext/streams.php
diff --git a/include/locale/lang.php b/include/locale/lang.php
deleted file mode 100644
index 93caf9ef07941f0b1c2739d44adeebb04b55fe4e..0000000000000000000000000000000000000000
--- a/include/locale/lang.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-#########################################################
-# Copyright © 2008 Darrin Yeager                        #
-# http://www.dyeager.org/                               #
-# Licensed under BSD license.                           #
-#   http://www.dyeager.org/downloads/license-bsd.txt    #
-#########################################################
-
-function getDefaultLanguage() {
-   if (isset($_SERVER["HTTP_ACCEPT_LANGUAGE"]))
-      return parseDefaultLanguage($_SERVER["HTTP_ACCEPT_LANGUAGE"]);
-   else
-      return parseDefaultLanguage(NULL);
-   }
-
-function parseDefaultLanguage($http_accept, $deflang = "en") {
-   if(isset($http_accept) && strlen($http_accept) > 1)  {
-      # Split possible languages into array
-      $x = explode(",",$http_accept);
-      foreach ($x as $val) {
-         #check for q-value and create associative array. No q-value means 1 by rule
-         if(preg_match("/(.*);q=([0-1]{0,1}\.\d{0,4})/i",$val,$matches))
-            $lang[$matches[1]] = (float)$matches[2];
-         else
-            $lang[$val] = 1.0;
-      }
-
-      #return default language (highest q-value)
-      $qval = 0.0;
-      foreach ($lang as $key => $value) {
-         if ($value > $qval) {
-            $qval = (float)$value;
-            $deflang = $key;
-         }
-      }
-   }
-   return strtolower($deflang);
-}
-?>
\ No newline at end of file
diff --git a/scp/staff.inc.php b/scp/staff.inc.php
index d7719acc7c528fe2db6d867aa0c437f4861f803e..62dd91855bcd5cecfc557eb4efb7edd562455a90 100644
--- a/scp/staff.inc.php
+++ b/scp/staff.inc.php
@@ -57,6 +57,11 @@ if(!function_exists('staffLoginPage')) { //Ajax interface can pre-declare the fu
 }
 
 $thisstaff = StaffAuthenticationBackend::getUser();
+
+// Bootstrap gettext translations as early as possible, but after attempting
+// to sign on the agent
+Internationalization::bootstrap($thisstaff);
+
 //1) is the user Logged in for real && is staff.
 if (!$thisstaff || !$thisstaff->getId() || !$thisstaff->isValid()) {
     if (isset($_SESSION['_staff']['auth']['msg'])) {